diff options
Diffstat (limited to 'drivers/clk')
109 files changed, 6916 insertions, 873 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 42f7120ca9ce..57316528e924 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -14,6 +14,7 @@ config COMMON_CLK select HAVE_CLK_PREPARE select CLKDEV_LOOKUP select SRCU + select RATIONAL ---help--- The common clock framework is a single definition of struct clk, useful across many platforms, as well as an @@ -68,6 +69,16 @@ config COMMON_CLK_SI5351 This driver supports Silicon Labs 5351A/B/C programmable clock generators. +config COMMON_CLK_SI514 + tristate "Clock driver for SiLabs 514 devices" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help--- + This driver supports the Silicon Labs 514 programmable clock + generator. + config COMMON_CLK_SI570 tristate "Clock driver for SiLabs 570 and compatible devices" depends on I2C @@ -113,7 +124,7 @@ config CLK_TWL6040 config COMMON_CLK_AXI_CLKGEN tristate "AXI clkgen driver" - depends on ARCH_ZYNQ || MICROBLAZE + depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST help ---help--- Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx @@ -121,7 +132,7 @@ config COMMON_CLK_AXI_CLKGEN config CLK_QORIQ bool "Clock driver for Freescale QorIQ platforms" - depends on (PPC_E500MC || ARM) && OF + depends on (PPC_E500MC || ARM || COMPILE_TEST) && OF ---help--- This adds the clock driver support for Freescale QorIQ platforms using common clock framework. @@ -129,13 +140,13 @@ config CLK_QORIQ config COMMON_CLK_XGENE bool "Clock driver for APM XGene SoC" default y - depends on ARM64 + depends on ARM64 || COMPILE_TEST ---help--- Sypport for the APM X-Gene SoC reference, PLL, and device clocks. config COMMON_CLK_KEYSTONE tristate "Clock drivers for Keystone based SOCs" - depends on ARCH_KEYSTONE && OF + depends on (ARCH_KEYSTONE || COMPILE_TEST) && OF ---help--- Supports clock drivers for Keystone based SOCs. These SOCs have local a power sleep control module that gate the clock to the IPs and PLLs. diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 6124bd31e557..d3e1910eebab 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-divider.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o +obj-$(CONFIG_COMMON_CLK) += clk-multiplier.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o @@ -19,7 +20,6 @@ endif obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o -obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o @@ -37,6 +37,7 @@ obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o +obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 89a48a7bd5df..13e67bd35cff 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o +obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c new file mode 100644 index 000000000000..abc80949e1dd --- /dev/null +++ b/drivers/clk/at91/clk-generated.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2015 Atmel Corporation, + * Nicolas Ferre <nicolas.ferre@atmel.com> + * + * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON. + * + * 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/clkdev.h> +#include <linux/clk/at91_pmc.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/io.h> + +#include "pmc.h" + +#define PERIPHERAL_MAX 64 +#define PERIPHERAL_ID_MIN 2 + +#define GENERATED_SOURCE_MAX 6 +#define GENERATED_MAX_DIV 255 + +struct clk_generated { + struct clk_hw hw; + struct at91_pmc *pmc; + struct clk_range range; + u32 id; + u32 gckdiv; + u8 parent_id; +}; + +#define to_clk_generated(hw) \ + container_of(hw, struct clk_generated, hw) + +static int clk_generated_enable(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + struct at91_pmc *pmc = gck->pmc; + u32 tmp; + + pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n", + __func__, gck->gckdiv, gck->parent_id); + + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK)); + tmp = pmc_read(pmc, AT91_PMC_PCR) & + ~(AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK); + pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_GCKCSS(gck->parent_id) + | AT91_PMC_PCR_CMD + | AT91_PMC_PCR_GCKDIV(gck->gckdiv) + | AT91_PMC_PCR_GCKEN); + pmc_unlock(pmc); + return 0; +} + +static void clk_generated_disable(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + struct at91_pmc *pmc = gck->pmc; + u32 tmp; + + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK)); + tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_GCKEN; + pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_CMD); + pmc_unlock(pmc); +} + +static int clk_generated_is_enabled(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + struct at91_pmc *pmc = gck->pmc; + int ret; + + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK)); + ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_GCKEN); + pmc_unlock(pmc); + + return ret; +} + +static unsigned long +clk_generated_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_generated *gck = to_clk_generated(hw); + + return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1); +} + +static int clk_generated_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_generated *gck = to_clk_generated(hw); + struct clk_hw *parent = NULL; + long best_rate = -EINVAL; + unsigned long tmp_rate, min_rate; + int best_diff = -1; + int tmp_diff; + int i; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + u32 div; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1); + if (!parent_rate || + (gck->range.max && min_rate > gck->range.max)) + continue; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); + tmp_diff = abs(req->rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + best_rate = tmp_rate; + best_diff = tmp_diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (!best_diff || tmp_rate < req->rate) + break; + } + + if (!best_diff) + break; + } + + pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", + __func__, best_rate, + __clk_get_name((req->best_parent_hw)->clk), + req->best_parent_rate); + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + return 0; +} + +/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */ +static int clk_generated_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_generated *gck = to_clk_generated(hw); + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + gck->parent_id = index; + return 0; +} + +static u8 clk_generated_get_parent(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + + return gck->parent_id; +} + +/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */ +static int clk_generated_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_generated *gck = to_clk_generated(hw); + u32 div; + + if (!rate) + return -EINVAL; + + if (gck->range.max && rate > gck->range.max) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > GENERATED_MAX_DIV + 1 || !div) + return -EINVAL; + + gck->gckdiv = div - 1; + return 0; +} + +static const struct clk_ops generated_ops = { + .enable = clk_generated_enable, + .disable = clk_generated_disable, + .is_enabled = clk_generated_is_enabled, + .recalc_rate = clk_generated_recalc_rate, + .determine_rate = clk_generated_determine_rate, + .get_parent = clk_generated_get_parent, + .set_parent = clk_generated_set_parent, + .set_rate = clk_generated_set_rate, +}; + +/** + * clk_generated_startup - Initialize a given clock to its default parent and + * divisor parameter. + * + * @gck: Generated clock to set the startup parameters for. + * + * Take parameters from the hardware and update local clock configuration + * accordingly. + */ +static void clk_generated_startup(struct clk_generated *gck) +{ + struct at91_pmc *pmc = gck->pmc; + u32 tmp; + + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK)); + tmp = pmc_read(pmc, AT91_PMC_PCR); + pmc_unlock(pmc); + + gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK) + >> AT91_PMC_PCR_GCKCSS_OFFSET; + gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) + >> AT91_PMC_PCR_GCKDIV_OFFSET; +} + +static struct clk * __init +at91_clk_register_generated(struct at91_pmc *pmc, 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; + + gck = kzalloc(sizeof(*gck), GFP_KERNEL); + if (!gck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &generated_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + gck->id = id; + gck->hw.init = &init; + gck->pmc = pmc; + gck->range = *range; + + clk = clk_register(NULL, &gck->hw); + if (IS_ERR(clk)) + kfree(gck); + else + clk_generated_startup(gck); + + return clk; +} + +void __init of_sama5d2_clk_generated_setup(struct device_node *np, + struct at91_pmc *pmc) +{ + int num; + u32 id; + const char *name; + struct clk *clk; + int num_parents; + const char *parent_names[GENERATED_SOURCE_MAX]; + struct device_node *gcknp; + struct clk_range range = CLK_RANGE(0, 0); + + num_parents = of_clk_get_parent_count(np); + if (num_parents <= 0 || num_parents > GENERATED_SOURCE_MAX) + return; + + of_clk_parent_fill(np, parent_names, num_parents); + + num = of_get_child_count(np); + if (!num || num > PERIPHERAL_MAX) + return; + + for_each_child_of_node(np, gcknp) { + if (of_property_read_u32(gcknp, "reg", &id)) + continue; + + if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = gcknp->name; + + of_at91_get_clk_range(gcknp, "atmel,clk-output-range", + &range); + + clk = at91_clk_register_generated(pmc, name, parent_names, + num_parents, id, &range); + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(gcknp, of_clk_src_simple_get, clk); + } +} diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c index e4d7b574f1ea..58f3b568e9cb 100644 --- a/drivers/clk/at91/clk-peripheral.c +++ b/drivers/clk/at91/clk-peripheral.c @@ -161,14 +161,18 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) { struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); struct at91_pmc *pmc = periph->pmc; + u32 tmp; if (periph->id < PERIPHERAL_ID_MIN) return 0; - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) | - AT91_PMC_PCR_CMD | - AT91_PMC_PCR_DIV(periph->div) | - AT91_PMC_PCR_EN); + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK)); + tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_DIV_MASK; + pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_DIV(periph->div) + | AT91_PMC_PCR_CMD + | AT91_PMC_PCR_EN); + pmc_unlock(pmc); return 0; } @@ -176,12 +180,16 @@ static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) { struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); struct at91_pmc *pmc = periph->pmc; + u32 tmp; if (periph->id < PERIPHERAL_ID_MIN) return; - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) | - AT91_PMC_PCR_CMD); + pmc_lock(pmc); + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK)); + tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_EN; + pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_CMD); + pmc_unlock(pmc); } static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) @@ -194,7 +202,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) return 1; pmc_lock(pmc); - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID)); + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK)); ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN); pmc_unlock(pmc); @@ -213,7 +221,7 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, return parent_rate; pmc_lock(pmc); - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID)); + pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK)); tmp = pmc_read(pmc, AT91_PMC_PCR); pmc_unlock(pmc); diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 58008b3e8bc1..3f5314344286 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -138,7 +138,8 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name, clk = clk_register(NULL, &sys->hw); if (IS_ERR(clk)) { - free_irq(sys->irq, sys); + if (irq) + free_irq(sys->irq, sys); kfree(sys); } diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index 30dd697b1668..ca561e90a60f 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -47,7 +47,7 @@ static int clk_utmi_prepare(struct clk_hw *hw) { struct clk_utmi *utmi = to_clk_utmi(hw); struct at91_pmc *pmc = utmi->pmc; - u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) | AT91_PMC_UPLLEN | + u32 tmp = pmc_read(pmc, AT91_CKGR_UCKR) | AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; pmc_write(pmc, AT91_CKGR_UCKR, tmp); @@ -73,7 +73,7 @@ static void clk_utmi_unprepare(struct clk_hw *hw) { struct clk_utmi *utmi = to_clk_utmi(hw); struct at91_pmc *pmc = utmi->pmc; - u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN; + u32 tmp = pmc_read(pmc, AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN; pmc_write(pmc, AT91_CKGR_UCKR, tmp); } diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index d1844f1f3729..8476b570779b 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -206,6 +206,14 @@ static const struct at91_pmc_caps at91sam9x5_caps = { AT91_PMC_MOSCRCS | AT91_PMC_CFDEV, }; +static const struct at91_pmc_caps sama5d2_caps = { + .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | + AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | + AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY | + AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS | + AT91_PMC_CFDEV | AT91_PMC_GCKRDY, +}; + static const struct at91_pmc_caps sama5d3_caps = { .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | @@ -369,6 +377,12 @@ static const struct of_device_id pmc_clk_ids[] __initconst = { .data = of_sama5d4_clk_h32mx_setup, }, #endif +#if defined(CONFIG_HAVE_AT91_GENERATED_CLK) + { + .compatible = "atmel,sama5d2-clk-generated", + .data = of_sama5d2_clk_generated_setup, + }, +#endif { /*sentinel*/ } }; @@ -436,6 +450,13 @@ static void __init of_at91sam9x5_pmc_setup(struct device_node *np) CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc", of_at91sam9x5_pmc_setup); +static void __init of_sama5d2_pmc_setup(struct device_node *np) +{ + of_at91_pmc_setup(np, &sama5d2_caps); +} +CLK_OF_DECLARE(sama5d2_clk_pmc, "atmel,sama5d2-pmc", + of_sama5d2_pmc_setup); + static void __init of_sama5d3_pmc_setup(struct device_node *np) { of_at91_pmc_setup(np, &sama5d3_caps); diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 8b87771c69b2..f65739272779 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -118,4 +118,7 @@ void of_at91sam9x5_clk_smd_setup(struct device_node *np, void of_sama5d4_clk_h32mx_setup(struct device_node *np, struct at91_pmc *pmc); +void of_sama5d2_clk_generated_setup(struct device_node *np, + struct at91_pmc *pmc); + #endif /* __PMC_H_ */ diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 46ee475a1d76..85260fb96b36 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -1,6 +1,6 @@ config CLK_BCM_KONA bool "Broadcom Kona CCU clock support" - depends on ARCH_BCM_MOBILE + depends on ARCH_BCM_MOBILE || COMPILE_TEST depends on COMMON_CLK default y help diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 2d1cbc5aac41..3fc95060d875 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o 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_COMMON_CLK_IPROC) += clk-ns2.o obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c new file mode 100644 index 000000000000..39bf5820297e --- /dev/null +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -0,0 +1,1575 @@ +/* + * Copyright (C) 2010,2015 Broadcom + * Copyright (C) 2012 Stephen Warren + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * DOC: BCM2835 CPRMAN (clock manager for the "audio" domain) + * + * The clock tree on the 2835 has several levels. There's a root + * oscillator running at 19.2Mhz. After the oscillator there are 5 + * PLLs, roughly divided as "camera", "ARM", "core", "DSI displays", + * and "HDMI displays". Those 5 PLLs each can divide their output to + * produce up to 4 channels. Finally, there is the level of clocks to + * be consumed by other hardware components (like "H264" or "HDMI + * state machine"), which divide off of some subset of the PLL + * channels. + * + * All of the clocks in the tree are exposed in the DT, because the DT + * may want to make assignments of the final layer of clocks to the + * PLL channels, and some components of the hardware will actually + * skip layers of the tree (for example, the pixel clock comes + * directly from the PLLH PIX channel without using a CM_*CTL clock + * generator). + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/bcm2835.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <dt-bindings/clock/bcm2835.h> + +#define CM_PASSWORD 0x5a000000 + +#define CM_GNRICCTL 0x000 +#define CM_GNRICDIV 0x004 +# define CM_DIV_FRAC_BITS 12 + +#define CM_VPUCTL 0x008 +#define CM_VPUDIV 0x00c +#define CM_SYSCTL 0x010 +#define CM_SYSDIV 0x014 +#define CM_PERIACTL 0x018 +#define CM_PERIADIV 0x01c +#define CM_PERIICTL 0x020 +#define CM_PERIIDIV 0x024 +#define CM_H264CTL 0x028 +#define CM_H264DIV 0x02c +#define CM_ISPCTL 0x030 +#define CM_ISPDIV 0x034 +#define CM_V3DCTL 0x038 +#define CM_V3DDIV 0x03c +#define CM_CAM0CTL 0x040 +#define CM_CAM0DIV 0x044 +#define CM_CAM1CTL 0x048 +#define CM_CAM1DIV 0x04c +#define CM_CCP2CTL 0x050 +#define CM_CCP2DIV 0x054 +#define CM_DSI0ECTL 0x058 +#define CM_DSI0EDIV 0x05c +#define CM_DSI0PCTL 0x060 +#define CM_DSI0PDIV 0x064 +#define CM_DPICTL 0x068 +#define CM_DPIDIV 0x06c +#define CM_GP0CTL 0x070 +#define CM_GP0DIV 0x074 +#define CM_GP1CTL 0x078 +#define CM_GP1DIV 0x07c +#define CM_GP2CTL 0x080 +#define CM_GP2DIV 0x084 +#define CM_HSMCTL 0x088 +#define CM_HSMDIV 0x08c +#define CM_OTPCTL 0x090 +#define CM_OTPDIV 0x094 +#define CM_PWMCTL 0x0a0 +#define CM_PWMDIV 0x0a4 +#define CM_SMICTL 0x0b0 +#define CM_SMIDIV 0x0b4 +#define CM_TSENSCTL 0x0e0 +#define CM_TSENSDIV 0x0e4 +#define CM_TIMERCTL 0x0e8 +#define CM_TIMERDIV 0x0ec +#define CM_UARTCTL 0x0f0 +#define CM_UARTDIV 0x0f4 +#define CM_VECCTL 0x0f8 +#define CM_VECDIV 0x0fc +#define CM_PULSECTL 0x190 +#define CM_PULSEDIV 0x194 +#define CM_SDCCTL 0x1a8 +#define CM_SDCDIV 0x1ac +#define CM_ARMCTL 0x1b0 +#define CM_EMMCCTL 0x1c0 +#define CM_EMMCDIV 0x1c4 + +/* General bits for the CM_*CTL regs */ +# define CM_ENABLE BIT(4) +# define CM_KILL BIT(5) +# define CM_GATE_BIT 6 +# define CM_GATE BIT(CM_GATE_BIT) +# define CM_BUSY BIT(7) +# define CM_BUSYD BIT(8) +# define CM_SRC_SHIFT 0 +# define CM_SRC_BITS 4 +# define CM_SRC_MASK 0xf +# define CM_SRC_GND 0 +# define CM_SRC_OSC 1 +# define CM_SRC_TESTDEBUG0 2 +# define CM_SRC_TESTDEBUG1 3 +# define CM_SRC_PLLA_CORE 4 +# define CM_SRC_PLLA_PER 4 +# define CM_SRC_PLLC_CORE0 5 +# define CM_SRC_PLLC_PER 5 +# define CM_SRC_PLLC_CORE1 8 +# define CM_SRC_PLLD_CORE 6 +# define CM_SRC_PLLD_PER 6 +# define CM_SRC_PLLH_AUX 7 +# define CM_SRC_PLLC_CORE1 8 +# define CM_SRC_PLLC_CORE2 9 + +#define CM_OSCCOUNT 0x100 + +#define CM_PLLA 0x104 +# define CM_PLL_ANARST BIT(8) +# define CM_PLLA_HOLDPER BIT(7) +# define CM_PLLA_LOADPER BIT(6) +# define CM_PLLA_HOLDCORE BIT(5) +# define CM_PLLA_LOADCORE BIT(4) +# define CM_PLLA_HOLDCCP2 BIT(3) +# define CM_PLLA_LOADCCP2 BIT(2) +# define CM_PLLA_HOLDDSI0 BIT(1) +# define CM_PLLA_LOADDSI0 BIT(0) + +#define CM_PLLC 0x108 +# define CM_PLLC_HOLDPER BIT(7) +# define CM_PLLC_LOADPER BIT(6) +# define CM_PLLC_HOLDCORE2 BIT(5) +# define CM_PLLC_LOADCORE2 BIT(4) +# define CM_PLLC_HOLDCORE1 BIT(3) +# define CM_PLLC_LOADCORE1 BIT(2) +# define CM_PLLC_HOLDCORE0 BIT(1) +# define CM_PLLC_LOADCORE0 BIT(0) + +#define CM_PLLD 0x10c +# define CM_PLLD_HOLDPER BIT(7) +# define CM_PLLD_LOADPER BIT(6) +# define CM_PLLD_HOLDCORE BIT(5) +# define CM_PLLD_LOADCORE BIT(4) +# define CM_PLLD_HOLDDSI1 BIT(3) +# define CM_PLLD_LOADDSI1 BIT(2) +# define CM_PLLD_HOLDDSI0 BIT(1) +# define CM_PLLD_LOADDSI0 BIT(0) + +#define CM_PLLH 0x110 +# define CM_PLLH_LOADRCAL BIT(2) +# define CM_PLLH_LOADAUX BIT(1) +# define CM_PLLH_LOADPIX BIT(0) + +#define CM_LOCK 0x114 +# define CM_LOCK_FLOCKH BIT(12) +# define CM_LOCK_FLOCKD BIT(11) +# define CM_LOCK_FLOCKC BIT(10) +# define CM_LOCK_FLOCKB BIT(9) +# define CM_LOCK_FLOCKA BIT(8) + +#define CM_EVENT 0x118 +#define CM_DSI1ECTL 0x158 +#define CM_DSI1EDIV 0x15c +#define CM_DSI1PCTL 0x160 +#define CM_DSI1PDIV 0x164 +#define CM_DFTCTL 0x168 +#define CM_DFTDIV 0x16c + +#define CM_PLLB 0x170 +# define CM_PLLB_HOLDARM BIT(1) +# define CM_PLLB_LOADARM BIT(0) + +#define A2W_PLLA_CTRL 0x1100 +#define A2W_PLLC_CTRL 0x1120 +#define A2W_PLLD_CTRL 0x1140 +#define A2W_PLLH_CTRL 0x1160 +#define A2W_PLLB_CTRL 0x11e0 +# define A2W_PLL_CTRL_PRST_DISABLE BIT(17) +# define A2W_PLL_CTRL_PWRDN BIT(16) +# define A2W_PLL_CTRL_PDIV_MASK 0x000007000 +# define A2W_PLL_CTRL_PDIV_SHIFT 12 +# define A2W_PLL_CTRL_NDIV_MASK 0x0000003ff +# define A2W_PLL_CTRL_NDIV_SHIFT 0 + +#define A2W_PLLA_ANA0 0x1010 +#define A2W_PLLC_ANA0 0x1030 +#define A2W_PLLD_ANA0 0x1050 +#define A2W_PLLH_ANA0 0x1070 +#define A2W_PLLB_ANA0 0x10f0 + +#define A2W_PLL_KA_SHIFT 7 +#define A2W_PLL_KA_MASK GENMASK(9, 7) +#define A2W_PLL_KI_SHIFT 19 +#define A2W_PLL_KI_MASK GENMASK(21, 19) +#define A2W_PLL_KP_SHIFT 15 +#define A2W_PLL_KP_MASK GENMASK(18, 15) + +#define A2W_PLLH_KA_SHIFT 19 +#define A2W_PLLH_KA_MASK GENMASK(21, 19) +#define A2W_PLLH_KI_LOW_SHIFT 22 +#define A2W_PLLH_KI_LOW_MASK GENMASK(23, 22) +#define A2W_PLLH_KI_HIGH_SHIFT 0 +#define A2W_PLLH_KI_HIGH_MASK GENMASK(0, 0) +#define A2W_PLLH_KP_SHIFT 1 +#define A2W_PLLH_KP_MASK GENMASK(4, 1) + +#define A2W_XOSC_CTRL 0x1190 +# define A2W_XOSC_CTRL_PLLB_ENABLE BIT(7) +# define A2W_XOSC_CTRL_PLLA_ENABLE BIT(6) +# define A2W_XOSC_CTRL_PLLD_ENABLE BIT(5) +# define A2W_XOSC_CTRL_DDR_ENABLE BIT(4) +# define A2W_XOSC_CTRL_CPR1_ENABLE BIT(3) +# define A2W_XOSC_CTRL_USB_ENABLE BIT(2) +# define A2W_XOSC_CTRL_HDMI_ENABLE BIT(1) +# define A2W_XOSC_CTRL_PLLC_ENABLE BIT(0) + +#define A2W_PLLA_FRAC 0x1200 +#define A2W_PLLC_FRAC 0x1220 +#define A2W_PLLD_FRAC 0x1240 +#define A2W_PLLH_FRAC 0x1260 +#define A2W_PLLB_FRAC 0x12e0 +# define A2W_PLL_FRAC_MASK ((1 << A2W_PLL_FRAC_BITS) - 1) +# define A2W_PLL_FRAC_BITS 20 + +#define A2W_PLL_CHANNEL_DISABLE BIT(8) +#define A2W_PLL_DIV_BITS 8 +#define A2W_PLL_DIV_SHIFT 0 + +#define A2W_PLLA_DSI0 0x1300 +#define A2W_PLLA_CORE 0x1400 +#define A2W_PLLA_PER 0x1500 +#define A2W_PLLA_CCP2 0x1600 + +#define A2W_PLLC_CORE2 0x1320 +#define A2W_PLLC_CORE1 0x1420 +#define A2W_PLLC_PER 0x1520 +#define A2W_PLLC_CORE0 0x1620 + +#define A2W_PLLD_DSI0 0x1340 +#define A2W_PLLD_CORE 0x1440 +#define A2W_PLLD_PER 0x1540 +#define A2W_PLLD_DSI1 0x1640 + +#define A2W_PLLH_AUX 0x1360 +#define A2W_PLLH_RCAL 0x1460 +#define A2W_PLLH_PIX 0x1560 +#define A2W_PLLH_STS 0x1660 + +#define A2W_PLLH_CTRLR 0x1960 +#define A2W_PLLH_FRACR 0x1a60 +#define A2W_PLLH_AUXR 0x1b60 +#define A2W_PLLH_RCALR 0x1c60 +#define A2W_PLLH_PIXR 0x1d60 +#define A2W_PLLH_STSR 0x1e60 + +#define A2W_PLLB_ARM 0x13e0 +#define A2W_PLLB_SP0 0x14e0 +#define A2W_PLLB_SP1 0x15e0 +#define A2W_PLLB_SP2 0x16e0 + +#define LOCK_TIMEOUT_NS 100000000 +#define BCM2835_MAX_FB_RATE 1750000000u + +struct bcm2835_cprman { + struct device *dev; + void __iomem *regs; + spinlock_t regs_lock; + const char *osc_name; + + struct clk_onecell_data onecell; + struct clk *clks[BCM2835_CLOCK_COUNT]; +}; + +static inline void cprman_write(struct bcm2835_cprman *cprman, u32 reg, u32 val) +{ + writel(CM_PASSWORD | val, cprman->regs + reg); +} + +static inline u32 cprman_read(struct bcm2835_cprman *cprman, u32 reg) +{ + return readl(cprman->regs + reg); +} + +/* + * These are fixed clocks. They're probably not all root clocks and it may + * be possible to turn them on and off but until this is mapped out better + * it's the only way they can be used. + */ +void __init bcm2835_init_clocks(void) +{ + struct clk *clk; + int ret; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, + 126000000); + if (IS_ERR(clk)) + pr_err("apb_pclk not registered\n"); + + clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, CLK_IS_ROOT, + 3000000); + if (IS_ERR(clk)) + pr_err("uart0_pclk not registered\n"); + ret = clk_register_clkdev(clk, NULL, "20201000.uart"); + if (ret) + pr_err("uart0_pclk alias not registered\n"); + + clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, CLK_IS_ROOT, + 125000000); + if (IS_ERR(clk)) + pr_err("uart1_pclk not registered\n"); + ret = clk_register_clkdev(clk, NULL, "20215000.uart"); + if (ret) + pr_err("uart1_pclk alias not registered\n"); +} + +struct bcm2835_pll_data { + const char *name; + u32 cm_ctrl_reg; + u32 a2w_ctrl_reg; + u32 frac_reg; + u32 ana_reg_base; + u32 reference_enable_mask; + /* Bit in CM_LOCK to indicate when the PLL has locked. */ + u32 lock_mask; + + const struct bcm2835_pll_ana_bits *ana; + + unsigned long min_rate; + unsigned long max_rate; + /* + * Highest rate for the VCO before we have to use the + * pre-divide-by-2. + */ + unsigned long max_fb_rate; +}; + +struct bcm2835_pll_ana_bits { + u32 mask0; + u32 set0; + u32 mask1; + u32 set1; + u32 mask3; + u32 set3; + u32 fb_prediv_mask; +}; + +static const struct bcm2835_pll_ana_bits bcm2835_ana_default = { + .mask0 = 0, + .set0 = 0, + .mask1 = ~(A2W_PLL_KI_MASK | A2W_PLL_KP_MASK), + .set1 = (2 << A2W_PLL_KI_SHIFT) | (8 << A2W_PLL_KP_SHIFT), + .mask3 = ~A2W_PLL_KA_MASK, + .set3 = (2 << A2W_PLL_KA_SHIFT), + .fb_prediv_mask = BIT(14), +}; + +static const struct bcm2835_pll_ana_bits bcm2835_ana_pllh = { + .mask0 = ~(A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK), + .set0 = (2 << A2W_PLLH_KA_SHIFT) | (2 << A2W_PLLH_KI_LOW_SHIFT), + .mask1 = ~(A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK), + .set1 = (6 << A2W_PLLH_KP_SHIFT), + .mask3 = 0, + .set3 = 0, + .fb_prediv_mask = BIT(11), +}; + +/* + * PLLA is the auxiliary PLL, used to drive the CCP2 (Compact Camera + * Port 2) transmitter clock. + * + * It is in the PX LDO power domain, which is on when the AUDIO domain + * is on. + */ +static const struct bcm2835_pll_data bcm2835_plla_data = { + .name = "plla", + .cm_ctrl_reg = CM_PLLA, + .a2w_ctrl_reg = A2W_PLLA_CTRL, + .frac_reg = A2W_PLLA_FRAC, + .ana_reg_base = A2W_PLLA_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLA_ENABLE, + .lock_mask = CM_LOCK_FLOCKA, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 2400000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* PLLB is used for the ARM's clock. */ +static const struct bcm2835_pll_data bcm2835_pllb_data = { + .name = "pllb", + .cm_ctrl_reg = CM_PLLB, + .a2w_ctrl_reg = A2W_PLLB_CTRL, + .frac_reg = A2W_PLLB_FRAC, + .ana_reg_base = A2W_PLLB_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE, + .lock_mask = CM_LOCK_FLOCKB, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* + * PLLC is the core PLL, used to drive the core VPU clock. + * + * It is in the PX LDO power domain, which is on when the AUDIO domain + * is on. +*/ +static const struct bcm2835_pll_data bcm2835_pllc_data = { + .name = "pllc", + .cm_ctrl_reg = CM_PLLC, + .a2w_ctrl_reg = A2W_PLLC_CTRL, + .frac_reg = A2W_PLLC_FRAC, + .ana_reg_base = A2W_PLLC_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, + .lock_mask = CM_LOCK_FLOCKC, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* + * PLLD is the display PLL, used to drive DSI display panels. + * + * It is in the PX LDO power domain, which is on when the AUDIO domain + * is on. + */ +static const struct bcm2835_pll_data bcm2835_plld_data = { + .name = "plld", + .cm_ctrl_reg = CM_PLLD, + .a2w_ctrl_reg = A2W_PLLD_CTRL, + .frac_reg = A2W_PLLD_FRAC, + .ana_reg_base = A2W_PLLD_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_DDR_ENABLE, + .lock_mask = CM_LOCK_FLOCKD, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 2400000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* + * PLLH is used to supply the pixel clock or the AUX clock for the TV + * encoder. + * + * It is in the HDMI power domain. + */ +static const struct bcm2835_pll_data bcm2835_pllh_data = { + "pllh", + .cm_ctrl_reg = CM_PLLH, + .a2w_ctrl_reg = A2W_PLLH_CTRL, + .frac_reg = A2W_PLLH_FRAC, + .ana_reg_base = A2W_PLLH_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, + .lock_mask = CM_LOCK_FLOCKH, + + .ana = &bcm2835_ana_pllh, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +struct bcm2835_pll_divider_data { + const char *name; + const struct bcm2835_pll_data *source_pll; + u32 cm_reg; + u32 a2w_reg; + + u32 load_mask; + u32 hold_mask; + u32 fixed_divider; +}; + +static const struct bcm2835_pll_divider_data bcm2835_plla_core_data = { + .name = "plla_core", + .source_pll = &bcm2835_plla_data, + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_CORE, + .load_mask = CM_PLLA_LOADCORE, + .hold_mask = CM_PLLA_HOLDCORE, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_plla_per_data = { + .name = "plla_per", + .source_pll = &bcm2835_plla_data, + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_PER, + .load_mask = CM_PLLA_LOADPER, + .hold_mask = CM_PLLA_HOLDPER, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllb_arm_data = { + .name = "pllb_arm", + .source_pll = &bcm2835_pllb_data, + .cm_reg = CM_PLLB, + .a2w_reg = A2W_PLLB_ARM, + .load_mask = CM_PLLB_LOADARM, + .hold_mask = CM_PLLB_HOLDARM, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_core0_data = { + .name = "pllc_core0", + .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE0, + .load_mask = CM_PLLC_LOADCORE0, + .hold_mask = CM_PLLC_HOLDCORE0, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_core1_data = { + .name = "pllc_core1", .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, A2W_PLLC_CORE1, + .load_mask = CM_PLLC_LOADCORE1, + .hold_mask = CM_PLLC_HOLDCORE1, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_core2_data = { + .name = "pllc_core2", + .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE2, + .load_mask = CM_PLLC_LOADCORE2, + .hold_mask = CM_PLLC_HOLDCORE2, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_per_data = { + .name = "pllc_per", + .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_PER, + .load_mask = CM_PLLC_LOADPER, + .hold_mask = CM_PLLC_HOLDPER, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_plld_core_data = { + .name = "plld_core", + .source_pll = &bcm2835_plld_data, + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_CORE, + .load_mask = CM_PLLD_LOADCORE, + .hold_mask = CM_PLLD_HOLDCORE, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_plld_per_data = { + .name = "plld_per", + .source_pll = &bcm2835_plld_data, + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_PER, + .load_mask = CM_PLLD_LOADPER, + .hold_mask = CM_PLLD_HOLDPER, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllh_rcal_data = { + .name = "pllh_rcal", + .source_pll = &bcm2835_pllh_data, + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_RCAL, + .load_mask = CM_PLLH_LOADRCAL, + .hold_mask = 0, + .fixed_divider = 10, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllh_aux_data = { + .name = "pllh_aux", + .source_pll = &bcm2835_pllh_data, + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_AUX, + .load_mask = CM_PLLH_LOADAUX, + .hold_mask = 0, + .fixed_divider = 10, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllh_pix_data = { + .name = "pllh_pix", + .source_pll = &bcm2835_pllh_data, + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_PIX, + .load_mask = CM_PLLH_LOADPIX, + .hold_mask = 0, + .fixed_divider = 10, +}; + +struct bcm2835_clock_data { + const char *name; + + const char *const *parents; + int num_mux_parents; + + u32 ctl_reg; + u32 div_reg; + + /* Number of integer bits in the divider */ + u32 int_bits; + /* Number of fractional bits in the divider */ + u32 frac_bits; + + bool is_vpu_clock; +}; + +static const char *const bcm2835_clock_per_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "plla_per", + "pllc_per", + "plld_per", + "pllh_aux", +}; + +static const char *const bcm2835_clock_vpu_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "plla_core", + "pllc_core0", + "plld_core", + "pllh_aux", + "pllc_core1", + "pllc_core2", +}; + +static const char *const bcm2835_clock_osc_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1" +}; + +/* + * Used for a 1Mhz clock for the system clocksource, and also used by + * the watchdog timer and the camera pulse generator. + */ +static const struct bcm2835_clock_data bcm2835_clock_timer_data = { + .name = "timer", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), + .parents = bcm2835_clock_osc_parents, + .ctl_reg = CM_TIMERCTL, + .div_reg = CM_TIMERDIV, + .int_bits = 6, + .frac_bits = 12, +}; + +/* One Time Programmable Memory clock. Maximum 10Mhz. */ +static const struct bcm2835_clock_data bcm2835_clock_otp_data = { + .name = "otp", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), + .parents = bcm2835_clock_osc_parents, + .ctl_reg = CM_OTPCTL, + .div_reg = CM_OTPDIV, + .int_bits = 4, + .frac_bits = 0, +}; + +/* + * VPU clock. This doesn't have an enable bit, since it drives the + * bus for everything else, and is special so it doesn't need to be + * gated for rate changes. It is also known as "clk_audio" in various + * hardware documentation. + */ +static const struct bcm2835_clock_data bcm2835_clock_vpu_data = { + .name = "vpu", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_VPUCTL, + .div_reg = CM_VPUDIV, + .int_bits = 12, + .frac_bits = 8, + .is_vpu_clock = true, +}; + +static const struct bcm2835_clock_data bcm2835_clock_v3d_data = { + .name = "v3d", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_V3DCTL, + .div_reg = CM_V3DDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +static const struct bcm2835_clock_data bcm2835_clock_isp_data = { + .name = "isp", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_ISPCTL, + .div_reg = CM_ISPDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +static const struct bcm2835_clock_data bcm2835_clock_h264_data = { + .name = "h264", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_H264CTL, + .div_reg = CM_H264DIV, + .int_bits = 4, + .frac_bits = 8, +}; + +/* TV encoder clock. Only operating frequency is 108Mhz. */ +static const struct bcm2835_clock_data bcm2835_clock_vec_data = { + .name = "vec", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_VECCTL, + .div_reg = CM_VECDIV, + .int_bits = 4, + .frac_bits = 0, +}; + +static const struct bcm2835_clock_data bcm2835_clock_uart_data = { + .name = "uart", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_UARTCTL, + .div_reg = CM_UARTDIV, + .int_bits = 10, + .frac_bits = 12, +}; + +/* HDMI state machine */ +static const struct bcm2835_clock_data bcm2835_clock_hsm_data = { + .name = "hsm", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_HSMCTL, + .div_reg = CM_HSMDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +/* + * Secondary SDRAM clock. Used for low-voltage modes when the PLL in + * the SDRAM controller can't be used. + */ +static const struct bcm2835_clock_data bcm2835_clock_sdram_data = { + .name = "sdram", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_SDCCTL, + .div_reg = CM_SDCDIV, + .int_bits = 6, + .frac_bits = 0, +}; + +/* Clock for the temperature sensor. Generally run at 2Mhz, max 5Mhz. */ +static const struct bcm2835_clock_data bcm2835_clock_tsens_data = { + .name = "tsens", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), + .parents = bcm2835_clock_osc_parents, + .ctl_reg = CM_TSENSCTL, + .div_reg = CM_TSENSDIV, + .int_bits = 5, + .frac_bits = 0, +}; + +/* Arasan EMMC clock */ +static const struct bcm2835_clock_data bcm2835_clock_emmc_data = { + .name = "emmc", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_EMMCCTL, + .div_reg = CM_EMMCDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +struct bcm2835_pll { + struct clk_hw hw; + struct bcm2835_cprman *cprman; + const struct bcm2835_pll_data *data; +}; + +static int bcm2835_pll_is_on(struct clk_hw *hw) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + + return cprman_read(cprman, data->a2w_ctrl_reg) & + A2W_PLL_CTRL_PRST_DISABLE; +} + +static void bcm2835_pll_choose_ndiv_and_fdiv(unsigned long rate, + unsigned long parent_rate, + u32 *ndiv, u32 *fdiv) +{ + u64 div; + + div = (u64)rate << A2W_PLL_FRAC_BITS; + do_div(div, parent_rate); + + *ndiv = div >> A2W_PLL_FRAC_BITS; + *fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1); +} + +static long bcm2835_pll_rate_from_divisors(unsigned long parent_rate, + u32 ndiv, u32 fdiv, u32 pdiv) +{ + u64 rate; + + if (pdiv == 0) + return 0; + + rate = (u64)parent_rate * ((ndiv << A2W_PLL_FRAC_BITS) + fdiv); + do_div(rate, pdiv); + return rate >> A2W_PLL_FRAC_BITS; +} + +static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 ndiv, fdiv; + + bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv); + + return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1); +} + +static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + u32 a2wctrl = cprman_read(cprman, data->a2w_ctrl_reg); + u32 ndiv, pdiv, fdiv; + bool using_prediv; + + if (parent_rate == 0) + return 0; + + fdiv = cprman_read(cprman, data->frac_reg) & A2W_PLL_FRAC_MASK; + ndiv = (a2wctrl & A2W_PLL_CTRL_NDIV_MASK) >> A2W_PLL_CTRL_NDIV_SHIFT; + pdiv = (a2wctrl & A2W_PLL_CTRL_PDIV_MASK) >> A2W_PLL_CTRL_PDIV_SHIFT; + using_prediv = cprman_read(cprman, data->ana_reg_base + 4) & + data->ana->fb_prediv_mask; + + if (using_prediv) + ndiv *= 2; + + return bcm2835_pll_rate_from_divisors(parent_rate, ndiv, fdiv, pdiv); +} + +static void bcm2835_pll_off(struct clk_hw *hw) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + + cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST); + cprman_write(cprman, data->a2w_ctrl_reg, A2W_PLL_CTRL_PWRDN); +} + +static int bcm2835_pll_on(struct clk_hw *hw) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + ktime_t timeout; + + /* Take the PLL out of reset. */ + cprman_write(cprman, data->cm_ctrl_reg, + cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST); + + /* Wait for the PLL to lock. */ + timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask)) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(cprman->dev, "%s: couldn't lock PLL\n", + clk_hw_get_name(hw)); + return -ETIMEDOUT; + } + + cpu_relax(); + } + + return 0; +} + +static void +bcm2835_pll_write_ana(struct bcm2835_cprman *cprman, u32 ana_reg_base, u32 *ana) +{ + int i; + + /* + * ANA register setup is done as a series of writes to + * ANA3-ANA0, in that order. This lets us write all 4 + * registers as a single cycle of the serdes interface (taking + * 100 xosc clocks), whereas if we were to update ana0, 1, and + * 3 individually through their partial-write registers, each + * would be their own serdes cycle. + */ + for (i = 3; i >= 0; i--) + cprman_write(cprman, ana_reg_base + i * 4, ana[i]); +} + +static int bcm2835_pll_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + bool was_using_prediv, use_fb_prediv, do_ana_setup_first; + u32 ndiv, fdiv, a2w_ctl; + u32 ana[4]; + int i; + + if (rate < data->min_rate || rate > data->max_rate) { + dev_err(cprman->dev, "%s: rate out of spec: %lu vs (%lu, %lu)\n", + clk_hw_get_name(hw), rate, + data->min_rate, data->max_rate); + return -EINVAL; + } + + if (rate > data->max_fb_rate) { + use_fb_prediv = true; + rate /= 2; + } else { + use_fb_prediv = false; + } + + bcm2835_pll_choose_ndiv_and_fdiv(rate, parent_rate, &ndiv, &fdiv); + + for (i = 3; i >= 0; i--) + ana[i] = cprman_read(cprman, data->ana_reg_base + i * 4); + + was_using_prediv = ana[1] & data->ana->fb_prediv_mask; + + ana[0] &= ~data->ana->mask0; + ana[0] |= data->ana->set0; + ana[1] &= ~data->ana->mask1; + ana[1] |= data->ana->set1; + ana[3] &= ~data->ana->mask3; + ana[3] |= data->ana->set3; + + if (was_using_prediv && !use_fb_prediv) { + ana[1] &= ~data->ana->fb_prediv_mask; + do_ana_setup_first = true; + } else if (!was_using_prediv && use_fb_prediv) { + ana[1] |= data->ana->fb_prediv_mask; + do_ana_setup_first = false; + } else { + do_ana_setup_first = true; + } + + /* Unmask the reference clock from the oscillator. */ + cprman_write(cprman, A2W_XOSC_CTRL, + cprman_read(cprman, A2W_XOSC_CTRL) | + data->reference_enable_mask); + + if (do_ana_setup_first) + bcm2835_pll_write_ana(cprman, data->ana_reg_base, ana); + + /* Set the PLL multiplier from the oscillator. */ + cprman_write(cprman, data->frac_reg, fdiv); + + a2w_ctl = cprman_read(cprman, data->a2w_ctrl_reg); + a2w_ctl &= ~A2W_PLL_CTRL_NDIV_MASK; + a2w_ctl |= ndiv << A2W_PLL_CTRL_NDIV_SHIFT; + a2w_ctl &= ~A2W_PLL_CTRL_PDIV_MASK; + a2w_ctl |= 1 << A2W_PLL_CTRL_PDIV_SHIFT; + cprman_write(cprman, data->a2w_ctrl_reg, a2w_ctl); + + if (!do_ana_setup_first) + bcm2835_pll_write_ana(cprman, data->ana_reg_base, ana); + + return 0; +} + +static const struct clk_ops bcm2835_pll_clk_ops = { + .is_prepared = bcm2835_pll_is_on, + .prepare = bcm2835_pll_on, + .unprepare = bcm2835_pll_off, + .recalc_rate = bcm2835_pll_get_rate, + .set_rate = bcm2835_pll_set_rate, + .round_rate = bcm2835_pll_round_rate, +}; + +struct bcm2835_pll_divider { + struct clk_divider div; + struct bcm2835_cprman *cprman; + const struct bcm2835_pll_divider_data *data; +}; + +static struct bcm2835_pll_divider * +bcm2835_pll_divider_from_hw(struct clk_hw *hw) +{ + return container_of(hw, struct bcm2835_pll_divider, div.hw); +} + +static int bcm2835_pll_divider_is_on(struct clk_hw *hw) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + + return !(cprman_read(cprman, data->a2w_reg) & A2W_PLL_CHANNEL_DISABLE); +} + +static long bcm2835_pll_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + return clk_divider_ops.round_rate(hw, rate, parent_rate); +} + +static unsigned long bcm2835_pll_divider_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + u32 div = cprman_read(cprman, data->a2w_reg); + + div &= (1 << A2W_PLL_DIV_BITS) - 1; + if (div == 0) + div = 256; + + return parent_rate / div; +} + +static void bcm2835_pll_divider_off(struct clk_hw *hw) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + + cprman_write(cprman, data->cm_reg, + (cprman_read(cprman, data->cm_reg) & + ~data->load_mask) | data->hold_mask); + cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); +} + +static int bcm2835_pll_divider_on(struct clk_hw *hw) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + + cprman_write(cprman, data->a2w_reg, + cprman_read(cprman, data->a2w_reg) & + ~A2W_PLL_CHANNEL_DISABLE); + + cprman_write(cprman, data->cm_reg, + cprman_read(cprman, data->cm_reg) & ~data->hold_mask); + + return 0; +} + +static int bcm2835_pll_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + u32 cm; + int ret; + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); + if (ret) + return ret; + + cm = cprman_read(cprman, data->cm_reg); + cprman_write(cprman, data->cm_reg, cm | data->load_mask); + cprman_write(cprman, data->cm_reg, cm & ~data->load_mask); + + return 0; +} + +static const struct clk_ops bcm2835_pll_divider_clk_ops = { + .is_prepared = bcm2835_pll_divider_is_on, + .prepare = bcm2835_pll_divider_on, + .unprepare = bcm2835_pll_divider_off, + .recalc_rate = bcm2835_pll_divider_get_rate, + .set_rate = bcm2835_pll_divider_set_rate, + .round_rate = bcm2835_pll_divider_round_rate, +}; + +/* + * The CM dividers do fixed-point division, so we can't use the + * generic integer divider code like the PLL dividers do (and we can't + * fake it by having some fixed shifts preceding it in the clock tree, + * because we'd run out of bits in a 32-bit unsigned long). + */ +struct bcm2835_clock { + struct clk_hw hw; + struct bcm2835_cprman *cprman; + const struct bcm2835_clock_data *data; +}; + +static struct bcm2835_clock *bcm2835_clock_from_hw(struct clk_hw *hw) +{ + return container_of(hw, struct bcm2835_clock, hw); +} + +static int bcm2835_clock_is_on(struct clk_hw *hw) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + return (cprman_read(cprman, data->ctl_reg) & CM_ENABLE) != 0; +} + +static u32 bcm2835_clock_choose_div(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + const struct bcm2835_clock_data *data = clock->data; + u32 unused_frac_mask = GENMASK(CM_DIV_FRAC_BITS - data->frac_bits, 0); + u64 temp = (u64)parent_rate << CM_DIV_FRAC_BITS; + u32 div; + + do_div(temp, rate); + div = temp; + + /* Round and mask off the unused bits */ + if (unused_frac_mask != 0) { + div += unused_frac_mask >> 1; + div &= ~unused_frac_mask; + } + + /* Clamp to the limits. */ + div = max(div, unused_frac_mask + 1); + div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, + CM_DIV_FRAC_BITS - data->frac_bits)); + + return div; +} + +static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, + unsigned long parent_rate, + u32 div) +{ + const struct bcm2835_clock_data *data = clock->data; + u64 temp; + + /* + * The divisor is a 12.12 fixed point field, but only some of + * the bits are populated in any given clock. + */ + div >>= CM_DIV_FRAC_BITS - data->frac_bits; + div &= (1 << (data->int_bits + data->frac_bits)) - 1; + + if (div == 0) + return 0; + + temp = (u64)parent_rate << data->frac_bits; + + do_div(temp, div); + + return temp; +} + +static long bcm2835_clock_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + u32 div = bcm2835_clock_choose_div(hw, rate, *parent_rate); + + return bcm2835_clock_rate_from_divisor(clock, *parent_rate, div); +} + +static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + u32 div = cprman_read(cprman, data->div_reg); + + return bcm2835_clock_rate_from_divisor(clock, parent_rate, div); +} + +static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock) +{ + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + ktime_t timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + + while (cprman_read(cprman, data->ctl_reg) & CM_BUSY) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(cprman->dev, "%s: couldn't lock PLL\n", + clk_hw_get_name(&clock->hw)); + return; + } + cpu_relax(); + } +} + +static void bcm2835_clock_off(struct clk_hw *hw) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->ctl_reg, + cprman_read(cprman, data->ctl_reg) & ~CM_ENABLE); + spin_unlock(&cprman->regs_lock); + + /* BUSY will remain high until the divider completes its cycle. */ + bcm2835_clock_wait_busy(clock); +} + +static int bcm2835_clock_on(struct clk_hw *hw) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->ctl_reg, + cprman_read(cprman, data->ctl_reg) | + CM_ENABLE | + CM_GATE); + spin_unlock(&cprman->regs_lock); + + return 0; +} + +static int bcm2835_clock_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + u32 div = bcm2835_clock_choose_div(hw, rate, parent_rate); + + cprman_write(cprman, data->div_reg, div); + + return 0; +} + +static const struct clk_ops bcm2835_clock_clk_ops = { + .is_prepared = bcm2835_clock_is_on, + .prepare = bcm2835_clock_on, + .unprepare = bcm2835_clock_off, + .recalc_rate = bcm2835_clock_get_rate, + .set_rate = bcm2835_clock_set_rate, + .round_rate = bcm2835_clock_round_rate, +}; + +static int bcm2835_vpu_clock_is_on(struct clk_hw *hw) +{ + return true; +} + +/* + * The VPU clock can never be disabled (it doesn't have an ENABLE + * bit), so it gets its own set of clock ops. + */ +static const struct clk_ops bcm2835_vpu_clock_clk_ops = { + .is_prepared = bcm2835_vpu_clock_is_on, + .recalc_rate = bcm2835_clock_get_rate, + .set_rate = bcm2835_clock_set_rate, + .round_rate = bcm2835_clock_round_rate, +}; + +static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman, + const struct bcm2835_pll_data *data) +{ + struct bcm2835_pll *pll; + struct clk_init_data init; + + memset(&init, 0, sizeof(init)); + + /* All of the PLLs derive from the external oscillator. */ + init.parent_names = &cprman->osc_name; + init.num_parents = 1; + init.name = data->name; + init.ops = &bcm2835_pll_clk_ops; + init.flags = CLK_IGNORE_UNUSED; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return NULL; + + pll->cprman = cprman; + pll->data = data; + pll->hw.init = &init; + + return devm_clk_register(cprman->dev, &pll->hw); +} + +static struct clk * +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; + + if (data->fixed_divider != 1) { + divider_name = devm_kasprintf(cprman->dev, GFP_KERNEL, + "%s_prediv", data->name); + if (!divider_name) + return NULL; + } else { + divider_name = data->name; + } + + memset(&init, 0, sizeof(init)); + + init.parent_names = &data->source_pll->name; + init.num_parents = 1; + init.name = divider_name; + init.ops = &bcm2835_pll_divider_clk_ops; + init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED; + + divider = devm_kzalloc(cprman->dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return NULL; + + divider->div.reg = cprman->regs + data->a2w_reg; + divider->div.shift = A2W_PLL_DIV_SHIFT; + divider->div.width = A2W_PLL_DIV_BITS; + divider->div.flags = 0; + divider->div.lock = &cprman->regs_lock; + divider->div.hw.init = &init; + divider->div.table = NULL; + + divider->cprman = cprman; + divider->data = data; + + clk = devm_clk_register(cprman->dev, ÷r->div.hw); + if (IS_ERR(clk)) + return clk; + + /* + * 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; +} + +static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, + const struct bcm2835_clock_data *data) +{ + struct bcm2835_clock *clock; + struct clk_init_data init; + const char *parent; + + /* + * Most of the clock generators have a mux field, so we + * instantiate a generic mux as our parent to handle it. + */ + if (data->num_mux_parents) { + const char *parents[1 << CM_SRC_BITS]; + int i; + + parent = devm_kasprintf(cprman->dev, GFP_KERNEL, + "mux_%s", data->name); + if (!parent) + return NULL; + + /* + * Replace our "xosc" references with the oscillator's + * actual name. + */ + for (i = 0; i < data->num_mux_parents; i++) { + if (strcmp(data->parents[i], "xosc") == 0) + parents[i] = cprman->osc_name; + else + parents[i] = data->parents[i]; + } + + clk_register_mux(cprman->dev, parent, + parents, data->num_mux_parents, + CLK_SET_RATE_PARENT, + cprman->regs + data->ctl_reg, + CM_SRC_SHIFT, CM_SRC_BITS, + 0, &cprman->regs_lock); + } else { + parent = data->parents[0]; + } + + memset(&init, 0, sizeof(init)); + init.parent_names = &parent; + init.num_parents = 1; + init.name = data->name; + init.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; + } + + clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL); + if (!clock) + return NULL; + + clock->cprman = cprman; + clock->data = data; + clock->hw.init = &init; + + return devm_clk_register(cprman->dev, &clock->hw); +} + +static int bcm2835_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk **clks; + struct bcm2835_cprman *cprman; + struct resource *res; + + cprman = devm_kzalloc(dev, sizeof(*cprman), GFP_KERNEL); + if (!cprman) + return -ENOMEM; + + spin_lock_init(&cprman->regs_lock); + cprman->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cprman->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(cprman->regs)) + return PTR_ERR(cprman->regs); + + cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0); + if (!cprman->osc_name) + return -ENODEV; + + platform_set_drvdata(pdev, cprman); + + cprman->onecell.clk_num = BCM2835_CLOCK_COUNT; + cprman->onecell.clks = cprman->clks; + clks = cprman->clks; + + clks[BCM2835_PLLA] = bcm2835_register_pll(cprman, &bcm2835_plla_data); + clks[BCM2835_PLLB] = bcm2835_register_pll(cprman, &bcm2835_pllb_data); + clks[BCM2835_PLLC] = bcm2835_register_pll(cprman, &bcm2835_pllc_data); + clks[BCM2835_PLLD] = bcm2835_register_pll(cprman, &bcm2835_plld_data); + clks[BCM2835_PLLH] = bcm2835_register_pll(cprman, &bcm2835_pllh_data); + + clks[BCM2835_PLLA_CORE] = + bcm2835_register_pll_divider(cprman, &bcm2835_plla_core_data); + clks[BCM2835_PLLA_PER] = + bcm2835_register_pll_divider(cprman, &bcm2835_plla_per_data); + clks[BCM2835_PLLC_CORE0] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core0_data); + clks[BCM2835_PLLC_CORE1] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core1_data); + clks[BCM2835_PLLC_CORE2] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core2_data); + clks[BCM2835_PLLC_PER] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_per_data); + clks[BCM2835_PLLD_CORE] = + bcm2835_register_pll_divider(cprman, &bcm2835_plld_core_data); + clks[BCM2835_PLLD_PER] = + bcm2835_register_pll_divider(cprman, &bcm2835_plld_per_data); + clks[BCM2835_PLLH_RCAL] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllh_rcal_data); + clks[BCM2835_PLLH_AUX] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllh_aux_data); + clks[BCM2835_PLLH_PIX] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllh_pix_data); + + clks[BCM2835_CLOCK_TIMER] = + bcm2835_register_clock(cprman, &bcm2835_clock_timer_data); + clks[BCM2835_CLOCK_OTP] = + bcm2835_register_clock(cprman, &bcm2835_clock_otp_data); + clks[BCM2835_CLOCK_TSENS] = + bcm2835_register_clock(cprman, &bcm2835_clock_tsens_data); + clks[BCM2835_CLOCK_VPU] = + bcm2835_register_clock(cprman, &bcm2835_clock_vpu_data); + clks[BCM2835_CLOCK_V3D] = + bcm2835_register_clock(cprman, &bcm2835_clock_v3d_data); + clks[BCM2835_CLOCK_ISP] = + bcm2835_register_clock(cprman, &bcm2835_clock_isp_data); + clks[BCM2835_CLOCK_H264] = + bcm2835_register_clock(cprman, &bcm2835_clock_h264_data); + clks[BCM2835_CLOCK_V3D] = + bcm2835_register_clock(cprman, &bcm2835_clock_v3d_data); + clks[BCM2835_CLOCK_SDRAM] = + bcm2835_register_clock(cprman, &bcm2835_clock_sdram_data); + clks[BCM2835_CLOCK_UART] = + bcm2835_register_clock(cprman, &bcm2835_clock_uart_data); + clks[BCM2835_CLOCK_VEC] = + bcm2835_register_clock(cprman, &bcm2835_clock_vec_data); + clks[BCM2835_CLOCK_HSM] = + bcm2835_register_clock(cprman, &bcm2835_clock_hsm_data); + clks[BCM2835_CLOCK_EMMC] = + bcm2835_register_clock(cprman, &bcm2835_clock_emmc_data); + + /* + * CM_PERIICTL (and CM_PERIACTL, CM_SYSCTL and CM_VPUCTL if + * you have the debug bit set in the power manager, which we + * don't bother exposing) are individual gates off of the + * non-stop vpu clock. + */ + clks[BCM2835_CLOCK_PERI_IMAGE] = + clk_register_gate(dev, "peri_image", "vpu", + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, + cprman->regs + CM_PERIICTL, CM_GATE_BIT, + 0, &cprman->regs_lock); + + return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + &cprman->onecell); +} + +static const struct of_device_id bcm2835_clk_of_match[] = { + { .compatible = "brcm,bcm2835-cprman", }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm2835_clk_of_match); + +static struct platform_driver bcm2835_clk_driver = { + .driver = { + .name = "bcm2835-clk", + .of_match_table = bcm2835_clk_of_match, + }, + .probe = bcm2835_clk_probe, +}; + +builtin_platform_driver(bcm2835_clk_driver); + +MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); +MODULE_DESCRIPTION("BCM2835 clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c index 7f7da79f4adc..afd5891ac9e6 100644 --- a/drivers/clk/bcm/clk-iproc-pll.c +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -366,8 +366,8 @@ static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, struct iproc_pll *pll = clk->pll; const struct iproc_pll_ctrl *ctrl = pll->ctrl; u32 val; - u64 ndiv; - unsigned int ndiv_int, ndiv_frac, pdiv; + u64 ndiv, ndiv_int, ndiv_frac; + unsigned int pdiv; if (parent_rate == 0) return 0; @@ -387,22 +387,19 @@ static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, val = readl(pll->control_base + ctrl->ndiv_int.offset); ndiv_int = (val >> ctrl->ndiv_int.shift) & bit_mask(ctrl->ndiv_int.width); - ndiv = (u64)ndiv_int << ctrl->ndiv_int.shift; + ndiv = ndiv_int << 20; if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { val = readl(pll->control_base + ctrl->ndiv_frac.offset); ndiv_frac = (val >> ctrl->ndiv_frac.shift) & bit_mask(ctrl->ndiv_frac.width); - - if (ndiv_frac != 0) - ndiv = ((u64)ndiv_int << ctrl->ndiv_int.shift) | - ndiv_frac; + ndiv += ndiv_frac; } val = readl(pll->control_base + ctrl->pdiv.offset); pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); - clk->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift; + clk->rate = (ndiv * parent_rate) >> 20; if (pdiv == 0) clk->rate *= 2; diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c index 73153fc45ee9..23e0e3be6c37 100644 --- a/drivers/clk/berlin/bg2.c +++ b/drivers/clk/berlin/bg2.c @@ -490,8 +490,8 @@ static const struct berlin2_gate_data bg2_gates[] __initconst = { { "usb0", "perif", 11 }, { "usb1", "perif", 12 }, { "pbridge", "perif", 13, CLK_IGNORE_UNUSED }, - { "sdio0", "perif", 14, CLK_IGNORE_UNUSED }, - { "sdio1", "perif", 15, CLK_IGNORE_UNUSED }, + { "sdio0", "perif", 14 }, + { "sdio1", "perif", 15 }, { "nfc", "perif", 17 }, { "smemc", "perif", 19 }, { "audiohd", "audiohd_pll", 26 }, diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c index 221f40c2b850..243f421abcb4 100644 --- a/drivers/clk/berlin/bg2q.c +++ b/drivers/clk/berlin/bg2q.c @@ -283,7 +283,7 @@ static const struct berlin2_gate_data bg2q_gates[] __initconst = { { "usb2", "perif", 13 }, { "usb3", "perif", 14 }, { "pbridge", "perif", 15, CLK_IGNORE_UNUSED }, - { "sdio", "perif", 16, CLK_IGNORE_UNUSED }, + { "sdio", "perif", 16 }, { "nfc", "perif", 18 }, { "pcie", "perif", 22 }, }; diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c deleted file mode 100644 index dd295e498309..000000000000 --- a/drivers/clk/clk-bcm2835.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010 Broadcom - * Copyright (C) 2012 Stephen Warren - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/clk-provider.h> -#include <linux/clkdev.h> -#include <linux/clk/bcm2835.h> -#include <linux/of.h> - -/* - * These are fixed clocks. They're probably not all root clocks and it may - * be possible to turn them on and off but until this is mapped out better - * it's the only way they can be used. - */ -void __init bcm2835_init_clocks(void) -{ - struct clk *clk; - int ret; - - clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, - 126000000); - if (IS_ERR(clk)) - pr_err("apb_pclk not registered\n"); - - clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, CLK_IS_ROOT, - 3000000); - if (IS_ERR(clk)) - pr_err("uart0_pclk not registered\n"); - ret = clk_register_clkdev(clk, NULL, "20201000.uart"); - if (ret) - pr_err("uart0_pclk alias not registered\n"); - - clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, CLK_IS_ROOT, - 125000000); - if (IS_ERR(clk)) - pr_err("uart1_pclk not registered\n"); - ret = clk_register_clkdev(clk, NULL, "20215000.uart"); - if (ret) - pr_err("uart1_pclk alias not registered\n"); -} diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index f24d0a19ae70..3ace102a2a0a 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -24,7 +24,7 @@ * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared * enable - clk_enable only ensures that parents are enabled - * rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor) + * rate - rate is adjustable. clk->rate = ceiling(parent->rate / divisor) * parent - fixed parent. No clk_set_parent support */ @@ -132,7 +132,7 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, return parent_rate; } - return DIV_ROUND_UP(parent_rate, div); + return DIV_ROUND_UP_ULL((u64)parent_rate, div); } EXPORT_SYMBOL_GPL(divider_recalc_rate); @@ -210,7 +210,7 @@ static int _div_round_up(const struct clk_div_table *table, unsigned long parent_rate, unsigned long rate, unsigned long flags) { - int div = DIV_ROUND_UP(parent_rate, rate); + int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); if (flags & CLK_DIVIDER_POWER_OF_TWO) div = __roundup_pow_of_two(div); @@ -227,7 +227,7 @@ static int _div_round_closest(const struct clk_div_table *table, int up, down; unsigned long up_rate, down_rate; - up = DIV_ROUND_UP(parent_rate, rate); + up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); down = parent_rate / rate; if (flags & CLK_DIVIDER_POWER_OF_TWO) { @@ -238,8 +238,8 @@ static int _div_round_closest(const struct clk_div_table *table, down = _round_down_table(table, down); } - up_rate = DIV_ROUND_UP(parent_rate, up); - down_rate = DIV_ROUND_UP(parent_rate, down); + up_rate = DIV_ROUND_UP_ULL((u64)parent_rate, up); + down_rate = DIV_ROUND_UP_ULL((u64)parent_rate, down); return (rate - up_rate) <= (down_rate - rate) ? up : down; } @@ -318,7 +318,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * i); - now = DIV_ROUND_UP(parent_rate, i); + now = DIV_ROUND_UP_ULL((u64)parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv = i; best = now; @@ -342,7 +342,7 @@ long divider_round_rate(struct clk_hw *hw, unsigned long rate, div = clk_divider_bestdiv(hw, rate, prate, table, width, flags); - return DIV_ROUND_UP(*prate, div); + return DIV_ROUND_UP_ULL((u64)*prate, div); } EXPORT_SYMBOL_GPL(divider_round_rate); @@ -358,7 +358,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, bestdiv &= div_mask(divider->width); bestdiv = _get_div(divider->table, bestdiv, divider->flags, divider->width); - return DIV_ROUND_UP(*prate, bestdiv); + return DIV_ROUND_UP_ULL((u64)*prate, bestdiv); } return divider_round_rate(hw, rate, prate, divider->table, @@ -371,7 +371,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, { unsigned int div, value; - div = DIV_ROUND_UP(parent_rate, rate); + div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); if (!_is_valid_div(table, div, flags)) return -EINVAL; diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index e85f856b8592..5c4955e33f7a 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -7,13 +7,14 @@ * * Adjustable fractional divider clock implementation. * Output rate = (m / n) * parent_rate. + * Uses rational best approximation algorithm. */ #include <linux/clk-provider.h> #include <linux/module.h> #include <linux/device.h> #include <linux/slab.h> -#include <linux/gcd.h> +#include <linux/rational.h> #define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw) @@ -22,7 +23,8 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, { struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long flags = 0; - u32 val, m, n; + unsigned long m, n; + u32 val; u64 ret; if (fd->lock) @@ -50,23 +52,33 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, } static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) + unsigned long *parent_rate) { struct clk_fractional_divider *fd = to_clk_fd(hw); - unsigned maxn = (fd->nmask >> fd->nshift) + 1; - unsigned div; + unsigned long scale; + unsigned long m, n; + u64 ret; - if (!rate || rate >= *prate) - return *prate; + if (!rate || rate >= *parent_rate) + return *parent_rate; - div = gcd(*prate, rate); + /* + * Get rate closer to *parent_rate to guarantee there is no overflow + * for m and n. In the result it will be the nearest rate left shifted + * by (scale - fd->nwidth) bits. + */ + scale = fls_long(*parent_rate / rate - 1); + if (scale > fd->nwidth) + rate <<= scale - fd->nwidth; - while ((*prate / div) > maxn) { - div <<= 1; - rate <<= 1; - } + rational_best_approximation(rate, *parent_rate, + GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), + &m, &n); - return rate; + ret = (u64)*parent_rate * m; + do_div(ret, n); + + return ret; } static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, @@ -74,13 +86,12 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long flags = 0; - unsigned long div; - unsigned n, m; + unsigned long m, n; u32 val; - div = gcd(parent_rate, rate); - m = rate / div; - n = parent_rate / div; + rational_best_approximation(rate, parent_rate, + GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), + &m, &n); if (fd->lock) spin_lock_irqsave(fd->lock, flags); @@ -128,9 +139,11 @@ struct clk *clk_register_fractional_divider(struct device *dev, fd->reg = reg; fd->mshift = mshift; - fd->mmask = (BIT(mwidth) - 1) << mshift; + fd->mwidth = mwidth; + fd->mmask = GENMASK(mwidth - 1, 0) << mshift; fd->nshift = nshift; - fd->nmask = (BIT(nwidth) - 1) << nshift; + fd->nwidth = nwidth; + fd->nmask = GENMASK(nwidth - 1, 0) << nshift; fd->flags = clk_divider_flags; fd->lock = lock; fd->hw.init = &init; diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c index 74c49b93a6eb..4a89f7979ba0 100644 --- a/drivers/clk/clk-max77802.c +++ b/drivers/clk/clk-max77802.c @@ -94,5 +94,5 @@ static struct platform_driver max77802_clk_driver = { module_platform_driver(max77802_clk_driver); MODULE_DESCRIPTION("MAXIM 77802 Clock Driver"); -MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>"); +MODULE_AUTHOR("Javier Martinez Canillas <javier@osg.samsung.com"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-multiplier.c b/drivers/clk/clk-multiplier.c new file mode 100644 index 000000000000..43ec269fcbff --- /dev/null +++ b/drivers/clk/clk-multiplier.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 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 version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/slab.h> + +#define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, hw) + +static unsigned long __get_mult(struct clk_multiplier *mult, + unsigned long rate, + unsigned long parent_rate) +{ + if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST) + return DIV_ROUND_CLOSEST(rate, parent_rate); + + return rate / parent_rate; +} + +static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_multiplier *mult = to_clk_multiplier(hw); + unsigned long val; + + val = clk_readl(mult->reg) >> mult->shift; + val &= GENMASK(mult->width - 1, 0); + + if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS) + val = 1; + + return parent_rate * val; +} + +static bool __is_best_rate(unsigned long rate, unsigned long new, + unsigned long best, unsigned long flags) +{ + if (flags & CLK_MULTIPLIER_ROUND_CLOSEST) + return abs(rate - new) < abs(rate - best); + + return new >= rate && new < best; +} + +static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + u8 width, unsigned long flags) +{ + unsigned long orig_parent_rate = *best_parent_rate; + unsigned long parent_rate, current_rate, best_rate = ~0; + unsigned int i, bestmult = 0; + + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) + return rate / *best_parent_rate; + + for (i = 1; i < ((1 << width) - 1); i++) { + if (rate == orig_parent_rate * i) { + /* + * This is the best case for us if we have a + * perfect match without changing the parent + * rate. + */ + *best_parent_rate = orig_parent_rate; + return i; + } + + parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), + rate / i); + current_rate = parent_rate * i; + + if (__is_best_rate(rate, current_rate, best_rate, flags)) { + bestmult = i; + best_rate = current_rate; + *best_parent_rate = parent_rate; + } + } + + return bestmult; +} + +static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_multiplier *mult = to_clk_multiplier(hw); + unsigned long factor = __bestmult(hw, rate, parent_rate, + mult->width, mult->flags); + + return *parent_rate * factor; +} + +static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_multiplier *mult = to_clk_multiplier(hw); + unsigned long factor = __get_mult(mult, rate, parent_rate); + unsigned long flags = 0; + unsigned long val; + + if (mult->lock) + spin_lock_irqsave(mult->lock, flags); + else + __acquire(mult->lock); + + val = clk_readl(mult->reg); + val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift); + val |= factor << mult->shift; + clk_writel(val, mult->reg); + + if (mult->lock) + spin_unlock_irqrestore(mult->lock, flags); + else + __release(mult->lock); + + return 0; +} + +const struct clk_ops clk_multiplier_ops = { + .recalc_rate = clk_multiplier_recalc_rate, + .round_rate = clk_multiplier_round_rate, + .set_rate = clk_multiplier_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_multiplier_ops); + +struct clk *clk_register_multiplier(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mult_flags, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_multiplier *mult; + struct clk *clk; + + mult = kmalloc(sizeof(*mult), GFP_KERNEL); + if (!mult) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_multiplier_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + mult->reg = reg; + mult->shift = shift; + mult->width = width; + mult->flags = clk_mult_flags; + mult->lock = lock; + mult->hw.init = &init; + + clk = clk_register(dev, &mult->hw); + if (IS_ERR(clk)) + kfree(mult); + + return clk; +} +EXPORT_SYMBOL_GPL(clk_register_multiplier); + +void clk_unregister_multiplier(struct clk *clk) +{ + struct clk_multiplier *mult; + struct clk_hw *hw; + + hw = __clk_get_hw(clk); + if (!hw) + return; + + mult = to_clk_multiplier(hw); + + clk_unregister(clk); + kfree(mult); +} +EXPORT_SYMBOL_GPL(clk_unregister_multiplier); diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c new file mode 100644 index 000000000000..6af7dce54241 --- /dev/null +++ b/drivers/clk/clk-si514.c @@ -0,0 +1,379 @@ +/* + * Driver for Silicon Labs Si514 Programmable Oscillator + * + * Copyright (C) 2015 Topic Embedded Products + * + * Author: Mike Looijmans <mike.looijmans@topic.nl> + * + * 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/delay.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/* I2C registers */ +#define SI514_REG_LP 0 +#define SI514_REG_M_FRAC1 5 +#define SI514_REG_M_FRAC2 6 +#define SI514_REG_M_FRAC3 7 +#define SI514_REG_M_INT_FRAC 8 +#define SI514_REG_M_INT 9 +#define SI514_REG_HS_DIV 10 +#define SI514_REG_LS_HS_DIV 11 +#define SI514_REG_OE_STATE 14 +#define SI514_REG_RESET 128 +#define SI514_REG_CONTROL 132 + +/* Register values */ +#define SI514_RESET_RST BIT(7) + +#define SI514_CONTROL_FCAL BIT(0) +#define SI514_CONTROL_OE BIT(2) + +#define SI514_MIN_FREQ 100000U +#define SI514_MAX_FREQ 250000000U + +#define FXO 31980000U + +#define FVCO_MIN 2080000000U +#define FVCO_MAX 2500000000U + +#define HS_DIV_MAX 1022 + +struct clk_si514 { + struct clk_hw hw; + struct regmap *regmap; + struct i2c_client *i2c_client; +}; +#define to_clk_si514(_hw) container_of(_hw, struct clk_si514, hw) + +/* Multiplier/divider settings */ +struct clk_si514_muldiv { + u32 m_frac; /* 29-bit Fractional part of multiplier M */ + u8 m_int; /* Integer part of multiplier M, 65..78 */ + u8 ls_div_bits; /* 2nd divider, as 2^x */ + u16 hs_div; /* 1st divider, must be even and 10<=x<=1022 */ +}; + +/* Enables or disables the output driver */ +static int si514_enable_output(struct clk_si514 *data, bool enable) +{ + return regmap_update_bits(data->regmap, SI514_REG_CONTROL, + SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0); +} + +/* Retrieve clock multiplier and dividers from hardware */ +static int si514_get_muldiv(struct clk_si514 *data, + struct clk_si514_muldiv *settings) +{ + int err; + u8 reg[7]; + + err = regmap_bulk_read(data->regmap, SI514_REG_M_FRAC1, + reg, ARRAY_SIZE(reg)); + if (err) + return err; + + settings->m_frac = reg[0] | reg[1] << 8 | reg[2] << 16 | + (reg[3] & 0x1F) << 24; + settings->m_int = (reg[4] & 0x3f) << 3 | reg[3] >> 5; + settings->ls_div_bits = (reg[6] >> 4) & 0x07; + settings->hs_div = (reg[6] & 0x03) << 8 | reg[5]; + return 0; +} + +static int si514_set_muldiv(struct clk_si514 *data, + struct clk_si514_muldiv *settings) +{ + u8 lp; + u8 reg[7]; + int err; + + /* Calculate LP1/LP2 according to table 13 in the datasheet */ + /* 65.259980246 */ + if (settings->m_int < 65 || + (settings->m_int == 65 && settings->m_frac <= 139575831)) + lp = 0x22; + /* 67.859763463 */ + else if (settings->m_int < 67 || + (settings->m_int == 67 && settings->m_frac <= 461581994)) + lp = 0x23; + /* 72.937624981 */ + else if (settings->m_int < 72 || + (settings->m_int == 72 && settings->m_frac <= 503383578)) + lp = 0x33; + /* 75.843265046 */ + else if (settings->m_int < 75 || + (settings->m_int == 75 && settings->m_frac <= 452724474)) + lp = 0x34; + else + lp = 0x44; + + err = regmap_write(data->regmap, SI514_REG_LP, lp); + if (err < 0) + return err; + + reg[0] = settings->m_frac; + reg[1] = settings->m_frac >> 8; + reg[2] = settings->m_frac >> 16; + reg[3] = settings->m_frac >> 24 | settings->m_int << 5; + reg[4] = settings->m_int >> 3; + reg[5] = settings->hs_div; + reg[6] = (settings->hs_div >> 8) | (settings->ls_div_bits << 4); + + err = regmap_bulk_write(data->regmap, SI514_REG_HS_DIV, reg + 5, 2); + if (err < 0) + return err; + /* + * Writing to SI514_REG_M_INT_FRAC triggers the clock change, so that + * must be written last + */ + return regmap_bulk_write(data->regmap, SI514_REG_M_FRAC1, reg, 5); +} + +/* Calculate divider settings for a given frequency */ +static int si514_calc_muldiv(struct clk_si514_muldiv *settings, + unsigned long frequency) +{ + u64 m; + u32 ls_freq; + u32 tmp; + u8 res; + + if ((frequency < SI514_MIN_FREQ) || (frequency > SI514_MAX_FREQ)) + return -EINVAL; + + /* Determine the minimum value of LS_DIV and resulting target freq. */ + ls_freq = frequency; + if (frequency >= (FVCO_MIN / HS_DIV_MAX)) + settings->ls_div_bits = 0; + else { + res = 1; + tmp = 2 * HS_DIV_MAX; + while (tmp <= (HS_DIV_MAX * 32)) { + if ((frequency * tmp) >= FVCO_MIN) + break; + ++res; + tmp <<= 1; + } + settings->ls_div_bits = res; + ls_freq = frequency << res; + } + + /* Determine minimum HS_DIV, round up to even number */ + settings->hs_div = DIV_ROUND_UP(FVCO_MIN >> 1, ls_freq) << 1; + + /* M = LS_DIV x HS_DIV x frequency / F_XO (in fixed-point) */ + m = ((u64)(ls_freq * settings->hs_div) << 29) + (FXO / 2); + do_div(m, FXO); + settings->m_frac = (u32)m & (BIT(29) - 1); + settings->m_int = (u32)(m >> 29); + + return 0; +} + +/* Calculate resulting frequency given the register settings */ +static unsigned long si514_calc_rate(struct clk_si514_muldiv *settings) +{ + u64 m = settings->m_frac | ((u64)settings->m_int << 29); + u32 d = settings->hs_div * BIT(settings->ls_div_bits); + + return ((u32)(((m * FXO) + (FXO / 2)) >> 29)) / d; +} + +static unsigned long si514_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_si514 *data = to_clk_si514(hw); + struct clk_si514_muldiv settings; + int err; + + err = si514_get_muldiv(data, &settings); + if (err) { + dev_err(&data->i2c_client->dev, "unable to retrieve settings\n"); + return 0; + } + + return si514_calc_rate(&settings); +} + +static long si514_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_si514_muldiv settings; + int err; + + if (!rate) + return 0; + + err = si514_calc_muldiv(&settings, rate); + if (err) + return err; + + return si514_calc_rate(&settings); +} + +/* + * Update output frequency for big frequency changes (> 1000 ppm). + * The chip supports <1000ppm changes "on the fly", we haven't implemented + * that here. + */ +static int si514_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_si514 *data = to_clk_si514(hw); + struct clk_si514_muldiv settings; + int err; + + err = si514_calc_muldiv(&settings, rate); + if (err) + return err; + + si514_enable_output(data, false); + + err = si514_set_muldiv(data, &settings); + if (err < 0) + return err; /* Undefined state now, best to leave disabled */ + + /* Trigger calibration */ + err = regmap_write(data->regmap, SI514_REG_CONTROL, SI514_CONTROL_FCAL); + if (err < 0) + return err; + + /* Applying a new frequency can take up to 10ms */ + usleep_range(10000, 12000); + + si514_enable_output(data, true); + + return err; +} + +static const struct clk_ops si514_clk_ops = { + .recalc_rate = si514_recalc_rate, + .round_rate = si514_round_rate, + .set_rate = si514_set_rate, +}; + +static bool si514_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SI514_REG_CONTROL: + case SI514_REG_RESET: + return true; + default: + return false; + } +} + +static bool si514_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SI514_REG_LP: + case SI514_REG_M_FRAC1 ... SI514_REG_LS_HS_DIV: + case SI514_REG_OE_STATE: + case SI514_REG_RESET: + case SI514_REG_CONTROL: + return true; + default: + return false; + } +} + +static const struct regmap_config si514_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = SI514_REG_CONTROL, + .writeable_reg = si514_regmap_is_writeable, + .volatile_reg = si514_regmap_is_volatile, +}; + +static int si514_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clk_si514 *data; + struct clk_init_data init; + struct clk *clk; + int err; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + init.ops = &si514_clk_ops; + init.flags = CLK_IS_ROOT; + init.num_parents = 0; + data->hw.init = &init; + data->i2c_client = client; + + if (of_property_read_string(client->dev.of_node, "clock-output-names", + &init.name)) + init.name = client->dev.of_node->name; + + data->regmap = devm_regmap_init_i2c(client, &si514_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + i2c_set_clientdata(client, data); + + clk = devm_clk_register(&client->dev, &data->hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration failed\n"); + return PTR_ERR(clk); + } + err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get, + clk); + if (err) { + dev_err(&client->dev, "unable to add clk provider\n"); + return err; + } + + return 0; +} + +static int si514_remove(struct i2c_client *client) +{ + of_clk_del_provider(client->dev.of_node); + return 0; +} + +static const struct i2c_device_id si514_id[] = { + { "si514", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si514_id); + +static const struct of_device_id clk_si514_of_match[] = { + { .compatible = "silabs,si514" }, + { }, +}; +MODULE_DEVICE_TABLE(of, clk_si514_of_match); + +static struct i2c_driver si514_driver = { + .driver = { + .name = "si514", + .of_match_table = clk_si514_of_match, + }, + .probe = si514_probe, + .remove = si514_remove, + .id_table = si514_id, +}; +module_i2c_driver(si514_driver); + +MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); +MODULE_DESCRIPTION("Si514 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 5596c0aac22f..e346b223199d 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1183,13 +1183,13 @@ static int si5351_dt_parse(struct i2c_client *client, if (of_property_read_u32(child, "reg", &num)) { dev_err(&client->dev, "missing reg property of %s\n", child->name); - return -EINVAL; + goto put_child; } if (num >= 8 || (variant == SI5351_VARIANT_A3 && num >= 3)) { dev_err(&client->dev, "invalid clkout %d\n", num); - return -EINVAL; + goto put_child; } if (!of_property_read_u32(child, "silabs,multisynth-source", @@ -1207,7 +1207,7 @@ static int si5351_dt_parse(struct i2c_client *client, dev_err(&client->dev, "invalid parent %d for multisynth %d\n", val, num); - return -EINVAL; + goto put_child; } } @@ -1230,7 +1230,7 @@ static int si5351_dt_parse(struct i2c_client *client, dev_err(&client->dev, "invalid parent %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; } pdata->clkout[num].clkout_src = SI5351_CLKOUT_SRC_CLKIN; @@ -1239,7 +1239,7 @@ static int si5351_dt_parse(struct i2c_client *client, dev_err(&client->dev, "invalid parent %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; } } @@ -1256,7 +1256,7 @@ static int si5351_dt_parse(struct i2c_client *client, dev_err(&client->dev, "invalid drive strength %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; } } @@ -1283,7 +1283,7 @@ static int si5351_dt_parse(struct i2c_client *client, dev_err(&client->dev, "invalid disable state %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; } } @@ -1296,6 +1296,9 @@ static int si5351_dt_parse(struct i2c_client *client, client->dev.platform_data = pdata; return 0; +put_child: + of_node_put(child); + return -EINVAL; } #else static int si5351_dt_parse(struct i2c_client *client, enum si5351_variant variant) diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 96a6190acac2..27c0da29eca3 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -27,7 +27,6 @@ #include <linux/clkdev.h> #include <linux/clk-provider.h> #include <linux/of_address.h> -#include <asm/setup.h> /* Register SCU_PCPPLL bit fields */ #define N_DIV_RD(src) (((src) & 0x000001ff)) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 43e2c3ad6c31..2eae76f21d6f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -272,7 +272,7 @@ late_initcall_sync(clk_disable_unused); /*** helper functions ***/ -const char *__clk_get_name(struct clk *clk) +const char *__clk_get_name(const struct clk *clk) { return !clk ? NULL : clk->core->name; } @@ -1685,7 +1685,7 @@ static struct clk_core *__clk_init_parent(struct clk_core *core) "%s: multi-parent clocks must implement .get_parent\n", __func__); goto out; - }; + } /* * Do our best to cache parent clocks in core->parents. This prevents @@ -2437,7 +2437,8 @@ static int __clk_init(struct device *dev, struct clk *clk_user) hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { if (orphan->num_parents && orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); - if (!strcmp(core->name, orphan->parent_names[i])) + if (i >= 0 && i < orphan->num_parents && + !strcmp(core->name, orphan->parent_names[i])) clk_core_reparent(orphan, core); continue; } @@ -2931,7 +2932,7 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) unsigned int idx = clkspec->args[0]; if (idx >= clk_data->clk_num) { - pr_err("%s: invalid clock index %d\n", __func__, idx); + pr_err("%s: invalid clock index %u\n", __func__, idx); return ERR_PTR(-EINVAL); } @@ -3054,6 +3055,7 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) u32 pv; int rc; int count; + struct clk *clk; if (index < 0) return NULL; @@ -3079,8 +3081,25 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) if (of_property_read_string_index(clkspec.np, "clock-output-names", index, - &clk_name) < 0) - clk_name = clkspec.np->name; + &clk_name) < 0) { + /* + * Best effort to get the name if the clock has been + * registered with the framework. If the clock isn't + * registered, we return the node name as the name of + * the clock as long as #clock-cells = 0. + */ + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + if (clkspec.args_count == 0) + clk_name = clkspec.np->name; + else + clk_name = NULL; + } else { + clk_name = __clk_get_name(clk); + clk_put(clk); + } + } + of_node_put(clkspec.np); return clk_name; @@ -3178,13 +3197,15 @@ void __init of_clk_init(const struct of_device_id *matches) list_for_each_entry_safe(clk_provider, next, &clk_provider_list, node) { list_del(&clk_provider->node); + of_node_put(clk_provider->np); kfree(clk_provider); } + of_node_put(np); return; } parent->clk_init_cb = match->data; - parent->np = np; + parent->np = of_node_get(np); list_add_tail(&parent->node, &clk_provider_list); } @@ -3198,6 +3219,7 @@ void __init of_clk_init(const struct of_device_id *matches) of_clk_set_defaults(clk_provider->np, true); list_del(&clk_provider->node); + of_node_put(clk_provider->np); kfree(clk_provider); is_init_done = true; } diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c index 2a38eb4a2552..6cf38dc1c929 100644 --- a/drivers/clk/h8300/clk-h8s2678.c +++ b/drivers/clk/h8300/clk-h8s2678.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/device.h> #include <linux/of_address.h> +#include <linux/slab.h> static DEFINE_SPINLOCK(clklock); diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index 2c16807341dc..e43485448612 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -1,6 +1,12 @@ config COMMON_CLK_HI6220 bool "Hi6220 Clock Driver" - depends on (ARCH_HISI || COMPILE_TEST) && MAILBOX + depends on ARCH_HISI || COMPILE_TEST default ARCH_HISI help Build the Hisilicon Hi6220 clock driver based on the common clock framework. + +config STUB_CLK_HI6220 + bool "Hi6220 Stub Clock Driver" + depends on COMMON_CLK_HI6220 && MAILBOX + help + Build the Hisilicon Hi6220 stub clock driver. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 4a1001a11f04..74dba31590f9 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -7,4 +7,5 @@ obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o -obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o clk-hi6220-stub.o +obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o +obj-$(CONFIG_STUB_CLK_HI6220) += clk-hi6220-stub.o diff --git a/drivers/clk/hisilicon/clk-hi6220-stub.c b/drivers/clk/hisilicon/clk-hi6220-stub.c index 2c4add11c1ca..8afb40ef40ce 100644 --- a/drivers/clk/hisilicon/clk-hi6220-stub.c +++ b/drivers/clk/hisilicon/clk-hi6220-stub.c @@ -230,7 +230,7 @@ static int hi6220_stub_clk_probe(struct platform_device *pdev) if (IS_ERR(stub_clk->mbox)) { dev_err(dev, "failed get mailbox channel\n"); return PTR_ERR(stub_clk->mbox); - }; + } init.name = "acpu0"; init.ops = &hi6220_stub_clk_ops; diff --git a/drivers/clk/imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c index ec1a4c1dacf1..c4c141cab444 100644 --- a/drivers/clk/imx/clk-imx25.c +++ b/drivers/clk/imx/clk-imx25.c @@ -86,6 +86,16 @@ enum mx25_clks { static struct clk *clk[clk_max]; +static struct clk ** const uart_clks[] __initconst = { + &clk[uart_ipg_per], + &clk[uart1_ipg], + &clk[uart2_ipg], + &clk[uart3_ipg], + &clk[uart4_ipg], + &clk[uart5_ipg], + NULL +}; + static int __init __mx25_clocks_init(unsigned long osc_rate, void __iomem *ccm_base) { @@ -233,6 +243,8 @@ static int __init __mx25_clocks_init(unsigned long osc_rate, */ clk_set_parent(clk[cko_sel], clk[ipg]); + imx_register_uart_clocks(uart_clks); + return 0; } diff --git a/drivers/clk/imx/clk-imx27.c b/drivers/clk/imx/clk-imx27.c index d9d50d54ef2a..cf5cf75a4848 100644 --- a/drivers/clk/imx/clk-imx27.c +++ b/drivers/clk/imx/clk-imx27.c @@ -47,6 +47,17 @@ static const char *ssi_sel_clks[] = { "spll_gate", "mpll", }; static struct clk *clk[IMX27_CLK_MAX]; static struct clk_onecell_data clk_data; +static struct clk ** const uart_clks[] __initconst = { + &clk[IMX27_CLK_PER1_GATE], + &clk[IMX27_CLK_UART1_IPG_GATE], + &clk[IMX27_CLK_UART2_IPG_GATE], + &clk[IMX27_CLK_UART3_IPG_GATE], + &clk[IMX27_CLK_UART4_IPG_GATE], + &clk[IMX27_CLK_UART5_IPG_GATE], + &clk[IMX27_CLK_UART6_IPG_GATE], + NULL +}; + static void __init _mx27_clocks_init(unsigned long fref) { BUG_ON(!ccm); @@ -163,6 +174,8 @@ static void __init _mx27_clocks_init(unsigned long fref) clk_prepare_enable(clk[IMX27_CLK_EMI_AHB_GATE]); + imx_register_uart_clocks(uart_clks); + imx_print_silicon_rev("i.MX27", mx27_revision()); } @@ -248,8 +261,10 @@ static void __init mx27_clocks_init_dt(struct device_node *np) if (!of_device_is_compatible(refnp, "fsl,imx-osc26m")) continue; - if (!of_property_read_u32(refnp, "clock-frequency", &fref)) + if (!of_property_read_u32(refnp, "clock-frequency", &fref)) { + of_node_put(refnp); break; + } } ccm = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c index 1f8383475bb3..6a964144a5b5 100644 --- a/drivers/clk/imx/clk-imx31.c +++ b/drivers/clk/imx/clk-imx31.c @@ -62,7 +62,17 @@ enum mx31_clks { static struct clk *clk[clk_max]; static struct clk_onecell_data clk_data; -int __init mx31_clocks_init(unsigned long fref) +static struct clk ** const uart_clks[] __initconst = { + &clk[ipg], + &clk[uart1_gate], + &clk[uart2_gate], + &clk[uart3_gate], + &clk[uart4_gate], + &clk[uart5_gate], + NULL +}; + +static void __init _mx31_clocks_init(unsigned long fref) { void __iomem *base; struct device_node *np; @@ -132,6 +142,12 @@ int __init mx31_clocks_init(unsigned long fref) imx_check_clocks(clk, ARRAY_SIZE(clk)); + clk_set_parent(clk[csi], clk[upll]); + clk_prepare_enable(clk[emi_gate]); + clk_prepare_enable(clk[iim_gate]); + mx31_revision(); + clk_disable_unprepare(clk[iim_gate]); + np = of_find_compatible_node(NULL, NULL, "fsl,imx31-ccm"); if (np) { @@ -139,6 +155,13 @@ int __init mx31_clocks_init(unsigned long fref) clk_data.clk_num = ARRAY_SIZE(clk); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } +} + +int __init mx31_clocks_init(void) +{ + u32 fref = 26000000; /* default */ + + _mx31_clocks_init(fref); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); clk_register_clkdev(clk[ipg], "ipg", "imx-gpt.0"); @@ -194,12 +217,8 @@ int __init mx31_clocks_init(unsigned long fref) clk_register_clkdev(clk[sdma_gate], NULL, "imx31-sdma"); clk_register_clkdev(clk[iim_gate], "iim", NULL); - clk_set_parent(clk[csi], clk[upll]); - clk_prepare_enable(clk[emi_gate]); - clk_prepare_enable(clk[iim_gate]); - mx31_revision(); - clk_disable_unprepare(clk[iim_gate]); + imx_register_uart_clocks(uart_clks); mxc_timer_init(MX31_GPT1_BASE_ADDR, MX31_INT_GPT, GPT_TYPE_IMX31); return 0; @@ -214,9 +233,13 @@ int __init mx31_clocks_init_dt(void) if (!of_device_is_compatible(np, "fsl,imx-osc26m")) continue; - if (!of_property_read_u32(np, "clock-frequency", &fref)) + if (!of_property_read_u32(np, "clock-frequency", &fref)) { + of_node_put(np); break; + } } - return mx31_clocks_init(fref); + _mx31_clocks_init(fref); + + return 0; } diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index 8623cd4e49fd..a71d24cb4c06 100644 --- a/drivers/clk/imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c @@ -84,7 +84,15 @@ enum mx35_clks { static struct clk *clk[clk_max]; -int __init mx35_clocks_init(void) +static struct clk ** const uart_clks[] __initconst = { + &clk[ipg], + &clk[uart1_gate], + &clk[uart2_gate], + &clk[uart3_gate], + NULL +}; + +static void __init _mx35_clocks_init(void) { void __iomem *base; u32 pdr0, consumer_sel, hsp_sel; @@ -220,6 +228,32 @@ int __init mx35_clocks_init(void) imx_check_clocks(clk, ARRAY_SIZE(clk)); + clk_prepare_enable(clk[spba_gate]); + clk_prepare_enable(clk[gpio1_gate]); + clk_prepare_enable(clk[gpio2_gate]); + clk_prepare_enable(clk[gpio3_gate]); + clk_prepare_enable(clk[iim_gate]); + clk_prepare_enable(clk[emi_gate]); + clk_prepare_enable(clk[max_gate]); + clk_prepare_enable(clk[iomuxc_gate]); + + /* + * SCC is needed to boot via mmc after a watchdog reset. The clock code + * before conversion to common clk also enabled UART1 (which isn't + * handled here and not needed for mmc) and IIM (which is enabled + * unconditionally above). + */ + clk_prepare_enable(clk[scc_gate]); + + imx_register_uart_clocks(uart_clks); + + imx_print_silicon_rev("i.MX35", mx35_revision()); +} + +int __init mx35_clocks_init(void) +{ + _mx35_clocks_init(); + clk_register_clkdev(clk[pata_gate], NULL, "pata_imx"); clk_register_clkdev(clk[can1_gate], NULL, "flexcan.0"); clk_register_clkdev(clk[can2_gate], NULL, "flexcan.1"); @@ -279,25 +313,6 @@ int __init mx35_clocks_init(void) clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0"); clk_register_clkdev(clk[admux_gate], "audmux", NULL); - clk_prepare_enable(clk[spba_gate]); - clk_prepare_enable(clk[gpio1_gate]); - clk_prepare_enable(clk[gpio2_gate]); - clk_prepare_enable(clk[gpio3_gate]); - clk_prepare_enable(clk[iim_gate]); - clk_prepare_enable(clk[emi_gate]); - clk_prepare_enable(clk[max_gate]); - clk_prepare_enable(clk[iomuxc_gate]); - - /* - * SCC is needed to boot via mmc after a watchdog reset. The clock code - * before conversion to common clk also enabled UART1 (which isn't - * handled here and not needed for mmc) and IIM (which is enabled - * unconditionally above). - */ - clk_prepare_enable(clk[scc_gate]); - - imx_print_silicon_rev("i.MX35", mx35_revision()); - mxc_timer_init(MX35_GPT1_BASE_ADDR, MX35_INT_GPT, GPT_TYPE_IMX31); return 0; @@ -305,10 +320,10 @@ int __init mx35_clocks_init(void) static void __init mx35_clocks_init_dt(struct device_node *ccm_node) { + _mx35_clocks_init(); + clk_data.clks = clk; clk_data.clk_num = ARRAY_SIZE(clk); of_clk_add_provider(ccm_node, of_clk_src_onecell_get, &clk_data); - - mx35_clocks_init(); } CLK_OF_DECLARE(imx35, "fsl,imx35-ccm", mx35_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c index a7e4f394be0d..c6770348d2ab 100644 --- a/drivers/clk/imx/clk-imx51-imx53.c +++ b/drivers/clk/imx/clk-imx51-imx53.c @@ -130,6 +130,20 @@ static const char *cpu_podf_sels[] = { "pll1_sw", "step_sel" }; static struct clk *clk[IMX5_CLK_END]; static struct clk_onecell_data clk_data; +static struct clk ** const uart_clks[] __initconst = { + &clk[IMX5_CLK_UART1_IPG_GATE], + &clk[IMX5_CLK_UART1_PER_GATE], + &clk[IMX5_CLK_UART2_IPG_GATE], + &clk[IMX5_CLK_UART2_PER_GATE], + &clk[IMX5_CLK_UART3_IPG_GATE], + &clk[IMX5_CLK_UART3_PER_GATE], + &clk[IMX5_CLK_UART4_IPG_GATE], + &clk[IMX5_CLK_UART4_PER_GATE], + &clk[IMX5_CLK_UART5_IPG_GATE], + &clk[IMX5_CLK_UART5_PER_GATE], + NULL +}; + static void __init mx5_clocks_common_init(void __iomem *ccm_base) { clk[IMX5_CLK_DUMMY] = imx_clk_fixed("dummy", 0); @@ -310,6 +324,8 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base) clk_prepare_enable(clk[IMX5_CLK_TMAX1]); clk_prepare_enable(clk[IMX5_CLK_TMAX2]); /* esdhc2, fec */ clk_prepare_enable(clk[IMX5_CLK_TMAX3]); /* esdhc1, esdhc4 */ + + imx_register_uart_clocks(uart_clks); } static void __init mx50_clocks_init(struct device_node *np) diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index b2c1c047dc94..c1935081d34a 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -119,6 +119,7 @@ static unsigned int share_count_ssi1; static unsigned int share_count_ssi2; static unsigned int share_count_ssi3; static unsigned int share_count_mipi_core_cfg; +static unsigned int share_count_spdif; static inline int clk_on_imx6q(void) { @@ -130,6 +131,12 @@ static inline int clk_on_imx6dl(void) return of_machine_is_compatible("fsl,imx6dl"); } +static struct clk ** const uart_clks[] __initconst = { + &clk[IMX6QDL_CLK_UART_IPG], + &clk[IMX6QDL_CLK_UART_SERIAL], + NULL +}; + static void __init imx6q_clocks_init(struct device_node *ccm_node) { struct device_node *np; @@ -456,7 +463,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_SATA] = imx_clk_gate2("sata", "ahb", base + 0x7c, 4); clk[IMX6QDL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); clk[IMX6QDL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); - clk[IMX6QDL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif_podf", base + 0x7c, 14); + clk[IMX6QDL_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_spdif); + clk[IMX6QDL_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_spdif); clk[IMX6QDL_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); clk[IMX6QDL_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); clk[IMX6QDL_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); @@ -541,5 +549,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) /* All existing boards with PCIe use LVDS1 */ if (IS_ENABLED(CONFIG_PCI_IMX6)) clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]); + + imx_register_uart_clocks(uart_clks); } CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c index a0d4cf26cfa9..1be6230a07af 100644 --- a/drivers/clk/imx/clk-imx6sl.c +++ b/drivers/clk/imx/clk-imx6sl.c @@ -97,6 +97,7 @@ static struct clk_div_table video_div_table[] = { static unsigned int share_count_ssi1; static unsigned int share_count_ssi2; static unsigned int share_count_ssi3; +static unsigned int share_count_spdif; static struct clk *clks[IMX6SL_CLK_END]; static struct clk_onecell_data clk_data; @@ -184,6 +185,12 @@ void imx6sl_set_wait_clk(bool enter) imx6sl_enable_pll_arm(false); } +static struct clk ** const uart_clks[] __initconst = { + &clks[IMX6SL_CLK_UART], + &clks[IMX6SL_CLK_UART_SERIAL], + NULL +}; + static void __init imx6sl_clocks_init(struct device_node *ccm_node) { struct device_node *np; @@ -391,7 +398,8 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) clks[IMX6SL_CLK_PWM4] = imx_clk_gate2("pwm4", "perclk", base + 0x78, 22); clks[IMX6SL_CLK_SDMA] = imx_clk_gate2("sdma", "ipg", base + 0x7c, 6); clks[IMX6SL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); - clks[IMX6SL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif0_podf", base + 0x7c, 14); + clks[IMX6SL_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif0_podf", base + 0x7c, 14, &share_count_spdif); + clks[IMX6SL_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_spdif); clks[IMX6SL_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); clks[IMX6SL_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); clks[IMX6SL_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); @@ -439,5 +447,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) clk_set_parent(clks[IMX6SL_CLK_LCDIF_AXI_SEL], clks[IMX6SL_CLK_PLL2_PFD2]); + + imx_register_uart_clocks(uart_clks); } CLK_OF_DECLARE(imx6sl, "fsl,imx6sl-ccm", imx6sl_clocks_init); diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index 5b95c2c2bf52..fea125eb4330 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -135,6 +135,12 @@ static u32 share_count_ssi1; static u32 share_count_ssi2; static u32 share_count_ssi3; +static struct clk ** const uart_clks[] __initconst = { + &clks[IMX6SX_CLK_UART_IPG], + &clks[IMX6SX_CLK_UART_SERIAL], + NULL +}; + static void __init imx6sx_clocks_init(struct device_node *ccm_node) { struct device_node *np; @@ -454,6 +460,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); clks[IMX6SX_CLK_AUDIO] = imx_clk_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio); clks[IMX6SX_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio); + clks[IMX6SX_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_audio); clks[IMX6SX_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); clks[IMX6SX_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); clks[IMX6SX_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); @@ -557,5 +564,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clk_set_parent(clks[IMX6SX_CLK_QSPI1_SEL], clks[IMX6SX_CLK_PLL2_BUS]); clk_set_parent(clks[IMX6SX_CLK_QSPI2_SEL], clks[IMX6SX_CLK_PLL2_BUS]); + + imx_register_uart_clocks(uart_clks); } CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init); diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index aaa36650695f..01718d05e952 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -407,6 +407,24 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + /* + * Lower the AHB clock rate before changing the parent clock source, + * as AHB clock rate can NOT be higher than 133MHz, but its parent + * will be switched from 396MHz PFD to 528MHz PLL in order to increase + * AXI clock rate, so we need to lower AHB rate first to make sure at + * any time, AHB rate is <= 133MHz. + */ + clk_set_rate(clks[IMX6UL_CLK_AHB], 99000000); + + /* Change periph_pre clock to pll2_bus to adjust AXI rate to 264MHz */ + clk_set_parent(clks[IMX6UL_CLK_PERIPH_CLK2_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]); + clk_set_parent(clks[IMX6UL_CLK_PERIPH], clks[IMX6UL_CLK_PERIPH_CLK2]); + clk_set_parent(clks[IMX6UL_CLK_PERIPH_PRE], clks[IMX6UL_CLK_PLL2_BUS]); + clk_set_parent(clks[IMX6UL_CLK_PERIPH], clks[IMX6UL_CLK_PERIPH_PRE]); + + /* Make sure AHB rate is 132MHz */ + clk_set_rate(clks[IMX6UL_CLK_AHB], 132000000); + /* set perclk to from OSC */ clk_set_parent(clks[IMX6UL_CLK_PERCLK_SEL], clks[IMX6UL_CLK_OSC]); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 71f3a94b472c..448ef321948b 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -363,6 +363,17 @@ static const char *pll_video_bypass_sel[] = { "pll_video_main", "pll_video_main_ static struct clk_onecell_data clk_data; +static struct clk ** const uart_clks[] __initconst = { + &clks[IMX7D_UART1_ROOT_CLK], + &clks[IMX7D_UART2_ROOT_CLK], + &clks[IMX7D_UART3_ROOT_CLK], + &clks[IMX7D_UART4_ROOT_CLK], + &clks[IMX7D_UART5_ROOT_CLK], + &clks[IMX7D_UART6_ROOT_CLK], + &clks[IMX7D_UART7_ROOT_CLK], + NULL +}; + static void __init imx7d_clocks_init(struct device_node *ccm_node) { struct device_node *np; @@ -818,6 +829,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_CSI_MCLK_ROOT_CLK] = imx_clk_gate2("csi_mclk_root_clk", "csi_mclk_post_div", base + 0x4490, 0); clks[IMX7D_AUDIO_MCLK_ROOT_CLK] = imx_clk_gate2("audio_mclk_root_clk", "audio_mclk_post_div", base + 0x4790, 0); clks[IMX7D_WRCLK_ROOT_CLK] = imx_clk_gate2("wrclk_root_clk", "wrclk_post_div", base + 0x47a0, 0); + clks[IMX7D_ADC_ROOT_CLK] = imx_clk_gate2("adc_root_clk", "ipg_root_clk", base + 0x4200, 0); clks[IMX7D_GPT_3M_CLK] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); @@ -856,5 +868,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) /* 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]); + imx_register_uart_clocks(uart_clks); + } CLK_OF_DECLARE(imx7d, "fsl,imx7d-ccm", imx7d_clocks_init); diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c index 20889d59b44d..b18f875eac6a 100644 --- a/drivers/clk/imx/clk-pllv2.c +++ b/drivers/clk/imx/clk-pllv2.c @@ -77,7 +77,7 @@ struct clk_pllv2 { static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate, u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn) { - long mfi, mfn, mfd, pdf, ref_clk, mfn_abs; + long mfi, mfn, mfd, pdf, ref_clk; unsigned long dbl; s64 temp; @@ -87,19 +87,15 @@ static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate, mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET; mfi = (mfi <= 5) ? 5 : mfi; mfd = dp_mfd & MXC_PLL_DP_MFD_MASK; - mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK; - /* Sign extend to 32-bits */ - if (mfn >= 0x04000000) { - mfn |= 0xFC000000; - mfn_abs = -mfn; - } + mfn = dp_mfn & MXC_PLL_DP_MFN_MASK; + mfn = sign_extend32(mfn, 26); ref_clk = 2 * parent_rate; if (dbl != 0) ref_clk *= 2; ref_clk /= (pdf + 1); - temp = (u64) ref_clk * mfn_abs; + temp = (u64) ref_clk * abs(mfn); do_div(temp, mfd + 1); if (mfn < 0) temp = -temp; diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index bff45ead7389..d1b1c95177bb 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -387,6 +387,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_SNVS] = imx_clk_gate2("snvs-rtc", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(7)); clk[VF610_CLK_DAP] = imx_clk_gate("dap", "platform_bus", CCM_CCSR, 24); + clk[VF610_CLK_OCOTP] = imx_clk_gate("ocotp", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(5)); imx_check_clocks(clk, ARRAY_SIZE(clk)); diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c index df12b5307175..a634b1185be3 100644 --- a/drivers/clk/imx/clk.c +++ b/drivers/clk/imx/clk.c @@ -73,3 +73,41 @@ void imx_cscmr1_fixup(u32 *val) *val ^= CSCMR1_FIXUP; return; } + +static int imx_keep_uart_clocks __initdata; +static struct clk ** const *imx_uart_clocks __initdata; + +static int __init imx_keep_uart_clocks_param(char *str) +{ + imx_keep_uart_clocks = 1; + + return 0; +} +__setup_param("earlycon", imx_keep_uart_earlycon, + imx_keep_uart_clocks_param, 0); +__setup_param("earlyprintk", imx_keep_uart_earlyprintk, + imx_keep_uart_clocks_param, 0); + +void __init imx_register_uart_clocks(struct clk ** const clks[]) +{ + if (imx_keep_uart_clocks) { + int i; + + imx_uart_clocks = clks; + for (i = 0; imx_uart_clocks[i]; i++) + clk_prepare_enable(*imx_uart_clocks[i]); + } +} + +static int __init imx_clk_disable_uart(void) +{ + if (imx_keep_uart_clocks && imx_uart_clocks) { + int i; + + for (i = 0; imx_uart_clocks[i]; i++) + clk_disable_unprepare(*imx_uart_clocks[i]); + } + + return 0; +} +late_initcall_sync(imx_clk_disable_uart); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 1049b0c7d818..c94ac5c26226 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -7,6 +7,7 @@ extern spinlock_t imx_ccm_lock; void imx_check_clocks(struct clk *clks[], unsigned int count); +void imx_register_uart_clocks(struct clk ** const clks[]); extern void imx_cscmr1_fixup(u32 *val); diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c index 3f553d0ae0b5..a26ba2184454 100644 --- a/drivers/clk/keystone/pll.c +++ b/drivers/clk/keystone/pll.c @@ -157,7 +157,7 @@ out: * _of_clk_init - PLL initialisation via DT * @node: device tree node for this clock * @pllctrl: If true, lower 6 bits of multiplier is in pllm register of - * pll controller, else it is in the control regsiter0(bit 11-6) + * pll controller, else it is in the control register0(bit 11-6) */ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) { diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 8e4b2a4635b9..95fdfacb2ebf 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 +obj-y += 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 diff --git a/drivers/clk/mediatek/clk-apmixed.c b/drivers/clk/mediatek/clk-apmixed.c new file mode 100644 index 000000000000..5303c5980867 --- /dev/null +++ b/drivers/clk/mediatek/clk-apmixed.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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/delay.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +#include "clk-mtk.h" + +#define REF2USB_TX_EN BIT(0) +#define REF2USB_TX_LPF_EN BIT(1) +#define REF2USB_TX_OUT_EN BIT(2) +#define REF2USB_EN_MASK (REF2USB_TX_EN | REF2USB_TX_LPF_EN | \ + REF2USB_TX_OUT_EN) + +struct mtk_ref2usb_tx { + struct clk_hw hw; + void __iomem *base_addr; +}; + +static inline struct mtk_ref2usb_tx *to_mtk_ref2usb_tx(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_ref2usb_tx, hw); +} + +static int mtk_ref2usb_tx_is_prepared(struct clk_hw *hw) +{ + struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw); + + return (readl(tx->base_addr) & REF2USB_EN_MASK) == REF2USB_EN_MASK; +} + +static int mtk_ref2usb_tx_prepare(struct clk_hw *hw) +{ + struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw); + u32 val; + + val = readl(tx->base_addr); + + val |= REF2USB_TX_EN; + writel(val, tx->base_addr); + udelay(100); + + val |= REF2USB_TX_LPF_EN; + writel(val, tx->base_addr); + + val |= REF2USB_TX_OUT_EN; + writel(val, tx->base_addr); + + return 0; +} + +static void mtk_ref2usb_tx_unprepare(struct clk_hw *hw) +{ + struct mtk_ref2usb_tx *tx = to_mtk_ref2usb_tx(hw); + u32 val; + + val = readl(tx->base_addr); + val &= ~REF2USB_EN_MASK; + writel(val, tx->base_addr); +} + +static const struct clk_ops mtk_ref2usb_tx_ops = { + .is_prepared = mtk_ref2usb_tx_is_prepared, + .prepare = mtk_ref2usb_tx_prepare, + .unprepare = mtk_ref2usb_tx_unprepare, +}; + +struct clk * __init mtk_clk_register_ref2usb_tx(const char *name, + const char *parent_name, void __iomem *reg) +{ + struct mtk_ref2usb_tx *tx; + struct clk_init_data init = {}; + struct clk *clk; + + tx = kzalloc(sizeof(*tx), GFP_KERNEL); + if (!tx) + return ERR_PTR(-ENOMEM); + + tx->base_addr = reg; + tx->hw.init = &init; + + init.name = name; + init.ops = &mtk_ref2usb_tx_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &tx->hw); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", name, PTR_ERR(clk)); + kfree(tx); + } + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c index 57020368a693..576bdb7c98b8 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 *mtk_clk_register_gate( +struct clk * __init 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 90eff85f4285..227e356403d9 100644 --- a/drivers/clk/mediatek/clk-mt8173.c +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -15,21 +15,28 @@ #include <linux/clk.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/slab.h> -#include <linux/mfd/syscon.h> #include "clk-mtk.h" #include "clk-gate.h" #include <dt-bindings/clock/mt8173-clk.h> +/* + * For some clocks, we don't care what their actual rates are. And these + * clocks may change their rate on different products or different scenarios. + * So we model these clocks' rate as 0, to denote it's not an actual rate. + */ +#define DUMMY_RATE 0 + static DEFINE_SPINLOCK(mt8173_clk_lock); -static const struct mtk_fixed_factor root_clk_alias[] __initconst = { - FACTOR(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk_null", 1, 1), - FACTOR(CLK_TOP_DPI, "dpi_ck", "clk_null", 1, 1), - FACTOR(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk_null", 1, 1), - FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "clk_null", 1, 1), +static const struct mtk_fixed_clk fixed_clks[] __initconst = { + FIXED_CLK(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk26m", DUMMY_RATE), + FIXED_CLK(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk26m", 125 * MHZ), + FIXED_CLK(CLK_TOP_DSI0_DIG, "dsi0_dig", "clk26m", DUMMY_RATE), + FIXED_CLK(CLK_TOP_DSI1_DIG, "dsi1_dig", "clk26m", DUMMY_RATE), + FIXED_CLK(CLK_TOP_LVDS_PXL, "lvds_pxl", "lvdspll", DUMMY_RATE), + FIXED_CLK(CLK_TOP_LVDS_CTS, "lvds_cts", "lvdspll", DUMMY_RATE), }; static const struct mtk_fixed_factor top_divs[] __initconst = { @@ -54,6 +61,7 @@ static const struct mtk_fixed_factor top_divs[] __initconst = { FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1), + FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "tvdpll_445p5m", 1, 3), FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), @@ -590,7 +598,7 @@ static const struct mtk_composite top_muxes[] __initconst = { MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1), }; -static const struct mtk_gate_regs infra_cg_regs = { +static const struct mtk_gate_regs infra_cg_regs __initconst = { .set_ofs = 0x0040, .clr_ofs = 0x0044, .sta_ofs = 0x0048, @@ -612,20 +620,24 @@ static const struct mtk_gate infra_clks[] __initconst = { GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6), GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7), GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8), - GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "clk_null", 15), + GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "cpum_ck", 15), GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16), GATE_ICG(CLK_INFRA_CEC, "infra_cec", "clk26m", 18), GATE_ICG(CLK_INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22), GATE_ICG(CLK_INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23), }; -static const struct mtk_gate_regs peri0_cg_regs = { +static const struct mtk_fixed_factor infra_divs[] __initconst = { + FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2), +}; + +static const struct mtk_gate_regs peri0_cg_regs __initconst = { .set_ofs = 0x0008, .clr_ofs = 0x0010, .sta_ofs = 0x0018, }; -static const struct mtk_gate_regs peri1_cg_regs = { +static const struct mtk_gate_regs peri1_cg_regs __initconst = { .set_ofs = 0x000c, .clr_ofs = 0x0014, .sta_ofs = 0x001c, @@ -701,6 +713,183 @@ static const struct mtk_composite peri_clks[] __initconst = { MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), }; +static const struct mtk_gate_regs cg_regs_4_8_0 __initconst = { + .set_ofs = 0x0004, + .clr_ofs = 0x0008, + .sta_ofs = 0x0000, +}; + +#define GATE_IMG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &cg_regs_4_8_0, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate img_clks[] __initconst = { + GATE_IMG(CLK_IMG_LARB2_SMI, "img_larb2_smi", "mm_sel", 0), + GATE_IMG(CLK_IMG_CAM_SMI, "img_cam_smi", "mm_sel", 5), + GATE_IMG(CLK_IMG_CAM_CAM, "img_cam_cam", "mm_sel", 6), + GATE_IMG(CLK_IMG_SEN_TG, "img_sen_tg", "camtg_sel", 7), + GATE_IMG(CLK_IMG_SEN_CAM, "img_sen_cam", "mm_sel", 8), + GATE_IMG(CLK_IMG_CAM_SV, "img_cam_sv", "mm_sel", 9), + GATE_IMG(CLK_IMG_FD, "img_fd", "mm_sel", 11), +}; + +static const struct mtk_gate_regs mm0_cg_regs __initconst = { + .set_ofs = 0x0104, + .clr_ofs = 0x0108, + .sta_ofs = 0x0100, +}; + +static const struct mtk_gate_regs mm1_cg_regs __initconst = { + .set_ofs = 0x0114, + .clr_ofs = 0x0118, + .sta_ofs = 0x0110, +}; + +#define GATE_MM0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &mm0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_MM1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &mm1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate mm_clks[] __initconst = { + /* MM0 */ + GATE_MM0(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 0), + GATE_MM0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1), + GATE_MM0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 2), + GATE_MM0(CLK_MM_MDP_RDMA0, "mm_mdp_rdma0", "mm_sel", 3), + GATE_MM0(CLK_MM_MDP_RDMA1, "mm_mdp_rdma1", "mm_sel", 4), + GATE_MM0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 5), + GATE_MM0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 6), + GATE_MM0(CLK_MM_MDP_RSZ2, "mm_mdp_rsz2", "mm_sel", 7), + GATE_MM0(CLK_MM_MDP_TDSHP0, "mm_mdp_tdshp0", "mm_sel", 8), + GATE_MM0(CLK_MM_MDP_TDSHP1, "mm_mdp_tdshp1", "mm_sel", 9), + GATE_MM0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11), + GATE_MM0(CLK_MM_MDP_WROT0, "mm_mdp_wrot0", "mm_sel", 12), + GATE_MM0(CLK_MM_MDP_WROT1, "mm_mdp_wrot1", "mm_sel", 13), + GATE_MM0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 14), + GATE_MM0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "rtc_sel", 15), + GATE_MM0(CLK_MM_DISP_OVL0, "mm_disp_ovl0", "mm_sel", 16), + GATE_MM0(CLK_MM_DISP_OVL1, "mm_disp_ovl1", "mm_sel", 17), + GATE_MM0(CLK_MM_DISP_RDMA0, "mm_disp_rdma0", "mm_sel", 18), + GATE_MM0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19), + GATE_MM0(CLK_MM_DISP_RDMA2, "mm_disp_rdma2", "mm_sel", 20), + GATE_MM0(CLK_MM_DISP_WDMA0, "mm_disp_wdma0", "mm_sel", 21), + GATE_MM0(CLK_MM_DISP_WDMA1, "mm_disp_wdma1", "mm_sel", 22), + GATE_MM0(CLK_MM_DISP_COLOR0, "mm_disp_color0", "mm_sel", 23), + GATE_MM0(CLK_MM_DISP_COLOR1, "mm_disp_color1", "mm_sel", 24), + GATE_MM0(CLK_MM_DISP_AAL, "mm_disp_aal", "mm_sel", 25), + GATE_MM0(CLK_MM_DISP_GAMMA, "mm_disp_gamma", "mm_sel", 26), + GATE_MM0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 27), + GATE_MM0(CLK_MM_DISP_SPLIT0, "mm_disp_split0", "mm_sel", 28), + GATE_MM0(CLK_MM_DISP_SPLIT1, "mm_disp_split1", "mm_sel", 29), + GATE_MM0(CLK_MM_DISP_MERGE, "mm_disp_merge", "mm_sel", 30), + GATE_MM0(CLK_MM_DISP_OD, "mm_disp_od", "mm_sel", 31), + /* MM1 */ + GATE_MM1(CLK_MM_DISP_PWM0MM, "mm_disp_pwm0mm", "mm_sel", 0), + GATE_MM1(CLK_MM_DISP_PWM026M, "mm_disp_pwm026m", "pwm_sel", 1), + GATE_MM1(CLK_MM_DISP_PWM1MM, "mm_disp_pwm1mm", "mm_sel", 2), + GATE_MM1(CLK_MM_DISP_PWM126M, "mm_disp_pwm126m", "pwm_sel", 3), + GATE_MM1(CLK_MM_DSI0_ENGINE, "mm_dsi0_engine", "mm_sel", 4), + GATE_MM1(CLK_MM_DSI0_DIGITAL, "mm_dsi0_digital", "dsi0_dig", 5), + GATE_MM1(CLK_MM_DSI1_ENGINE, "mm_dsi1_engine", "mm_sel", 6), + GATE_MM1(CLK_MM_DSI1_DIGITAL, "mm_dsi1_digital", "dsi1_dig", 7), + GATE_MM1(CLK_MM_DPI_PIXEL, "mm_dpi_pixel", "dpi0_sel", 8), + GATE_MM1(CLK_MM_DPI_ENGINE, "mm_dpi_engine", "mm_sel", 9), + GATE_MM1(CLK_MM_DPI1_PIXEL, "mm_dpi1_pixel", "lvds_pxl", 10), + GATE_MM1(CLK_MM_DPI1_ENGINE, "mm_dpi1_engine", "mm_sel", 11), + GATE_MM1(CLK_MM_HDMI_PIXEL, "mm_hdmi_pixel", "dpi0_sel", 12), + GATE_MM1(CLK_MM_HDMI_PLLCK, "mm_hdmi_pllck", "hdmi_sel", 13), + GATE_MM1(CLK_MM_HDMI_AUDIO, "mm_hdmi_audio", "apll1", 14), + GATE_MM1(CLK_MM_HDMI_SPDIF, "mm_hdmi_spdif", "apll2", 15), + GATE_MM1(CLK_MM_LVDS_PIXEL, "mm_lvds_pixel", "lvds_pxl", 16), + GATE_MM1(CLK_MM_LVDS_CTS, "mm_lvds_cts", "lvds_cts", 17), + GATE_MM1(CLK_MM_SMI_LARB4, "mm_smi_larb4", "mm_sel", 18), + GATE_MM1(CLK_MM_HDMI_HDCP, "mm_hdmi_hdcp", "hdcp_sel", 19), + GATE_MM1(CLK_MM_HDMI_HDCP24M, "mm_hdmi_hdcp24m", "hdcp_24m_sel", 20), +}; + +static const struct mtk_gate_regs vdec0_cg_regs __initconst = { + .set_ofs = 0x0000, + .clr_ofs = 0x0004, + .sta_ofs = 0x0000, +}; + +static const struct mtk_gate_regs vdec1_cg_regs __initconst = { + .set_ofs = 0x0008, + .clr_ofs = 0x000c, + .sta_ofs = 0x0008, +}; + +#define GATE_VDEC0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &vdec0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +#define GATE_VDEC1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &vdec1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate vdec_clks[] __initconst = { + GATE_VDEC0(CLK_VDEC_CKEN, "vdec_cken", "vdec_sel", 0), + GATE_VDEC1(CLK_VDEC_LARB_CKEN, "vdec_larb_cken", "mm_sel", 0), +}; + +#define GATE_VENC(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &cg_regs_4_8_0, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate venc_clks[] __initconst = { + GATE_VENC(CLK_VENC_CKE0, "venc_cke0", "mm_sel", 0), + GATE_VENC(CLK_VENC_CKE1, "venc_cke1", "venc_sel", 4), + GATE_VENC(CLK_VENC_CKE2, "venc_cke2", "venc_sel", 8), + GATE_VENC(CLK_VENC_CKE3, "venc_cke3", "venc_sel", 12), +}; + +#define GATE_VENCLT(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &cg_regs_4_8_0, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr_inv, \ + } + +static const struct mtk_gate venclt_clks[] __initconst = { + GATE_VENCLT(CLK_VENCLT_CKE0, "venclt_cke0", "mm_sel", 0), + GATE_VENCLT(CLK_VENCLT_CKE1, "venclt_cke1", "venclt_sel", 4), +}; + static struct clk_onecell_data *mt8173_top_clk_data __initdata; static struct clk_onecell_data *mt8173_pll_clk_data __initdata; @@ -731,7 +920,7 @@ static void __init mtk_topckgen_init(struct device_node *node) mt8173_top_clk_data = clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); - mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_fixed_clks(fixed_clks, ARRAY_SIZE(fixed_clks), clk_data); mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, &mt8173_clk_lock, clk_data); @@ -754,6 +943,7 @@ static void __init mtk_infrasys_init(struct device_node *node) mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), clk_data); + mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data); r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); if (r) @@ -792,6 +982,24 @@ static void __init mtk_pericfg_init(struct device_node *node) } CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init); +struct mtk_clk_usb { + int id; + const char *name; + const char *parent; + u32 reg_ofs; +}; + +#define APMIXED_USB(_id, _name, _parent, _reg_ofs) { \ + .id = _id, \ + .name = _name, \ + .parent = _parent, \ + .reg_ofs = _reg_ofs, \ + } + +static const struct mtk_clk_usb apmixed_usb[] __initconst = { + APMIXED_USB(CLK_APMIXED_REF2USB_TX, "ref2usb_tx", "clk26m", 0x8), +}; + #define MT8173_PLL_FMAX (3000UL * MHZ) #define CON0_MT8173_RST_BAR BIT(24) @@ -852,6 +1060,15 @@ static const struct mtk_pll_data plls[] = { static void __init mtk_apmixedsys_init(struct device_node *node) { struct clk_onecell_data *clk_data; + void __iomem *base; + struct clk *clk; + int r, i; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } mt8173_pll_clk_data = clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); if (!clk_data) @@ -859,7 +1076,113 @@ static void __init mtk_apmixedsys_init(struct device_node *node) mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); + for (i = 0; i < ARRAY_SIZE(apmixed_usb); i++) { + const struct mtk_clk_usb *cku = &apmixed_usb[i]; + + clk = mtk_clk_register_ref2usb_tx(cku->name, cku->parent, + base + cku->reg_ofs); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", cku->name, + PTR_ERR(clk)); + continue; + } + + clk_data->clks[cku->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + mtk_clk_enable_critical(); } CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys", mtk_apmixedsys_init); + +static void __init mtk_imgsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK); + + mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_imgsys, "mediatek,mt8173-imgsys", mtk_imgsys_init); + +static void __init mtk_mmsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK); + + mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_mmsys, "mediatek,mt8173-mmsys", mtk_mmsys_init); + +static void __init mtk_vdecsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_VDEC_NR_CLK); + + mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_vdecsys, "mediatek,mt8173-vdecsys", mtk_vdecsys_init); + +static void __init mtk_vencsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK); + + mtk_clk_register_gates(node, venc_clks, ARRAY_SIZE(venc_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_vencsys, "mediatek,mt8173-vencsys", mtk_vencsys_init); + +static void __init mtk_vencltsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_VENCLT_NR_CLK); + + mtk_clk_register_gates(node, venclt_clks, ARRAY_SIZE(venclt_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_vencltsys, "mediatek,mt8173-vencltsys", mtk_vencltsys_init); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 18444aea63c9..cf08db6c130c 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 *mtk_alloc_clk_data(unsigned int clk_num) +struct clk_onecell_data * __init mtk_alloc_clk_data(unsigned int clk_num) { int i; struct clk_onecell_data *clk_data; @@ -49,8 +49,31 @@ err_out: return NULL; } -void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, - struct clk_onecell_data *clk_data) +void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_clk *rc = &clks[i]; + + clk = clk_register_fixed_rate(NULL, rc->name, rc->parent, + rc->parent ? 0 : CLK_IS_ROOT, rc->rate); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + rc->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[rc->id] = clk; + } +} + +void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data) { int i; struct clk *clk; @@ -72,7 +95,8 @@ void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, } } -int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, +int __init mtk_clk_register_gates(struct device_node *node, + const struct mtk_gate *clks, int num, struct clk_onecell_data *clk_data) { int i; @@ -111,7 +135,7 @@ int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks return 0; } -struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, +struct clk * __init mtk_clk_register_composite(const struct mtk_composite *mc, void __iomem *base, spinlock_t *lock) { struct clk *clk; @@ -196,7 +220,7 @@ err_out: return ERR_PTR(ret); } -void mtk_clk_register_composites(const struct mtk_composite *mcs, +void __init 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-mtk.h b/drivers/clk/mediatek/clk-mtk.h index c5cbecb3d218..32d2e455eb3f 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -26,6 +26,23 @@ struct clk; #define MHZ (1000 * 1000) +struct mtk_fixed_clk { + int id; + const char *name; + const char *parent; + unsigned long rate; +}; + +#define FIXED_CLK(_id, _name, _parent, _rate) { \ + .id = _id, \ + .name = _name, \ + .parent = _parent, \ + .rate = _rate, \ + } + +void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, + int num, struct clk_onecell_data *clk_data); + struct mtk_fixed_factor { int id; const char *name; @@ -42,7 +59,7 @@ struct mtk_fixed_factor { .div = _div, \ } -extern void 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); struct mtk_composite { @@ -159,10 +176,13 @@ struct mtk_pll_data { const struct mtk_pll_div_table *div_table; }; -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); +struct clk *mtk_clk_register_ref2usb_tx(const char *name, + const char *parent_name, void __iomem *reg); + #ifdef CONFIG_RESET_CONTROLLER void mtk_register_reset_controller(struct device_node *np, unsigned int num_regs, int regofs); diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index 622e7b6c62b4..966cab1348da 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -317,7 +317,7 @@ void __init 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; - int r, i; + int i; struct clk *clk; base = of_iomap(node, 0); @@ -339,9 +339,4 @@ void __init mtk_clk_register_plls(struct device_node *node, clk_data->clks[pll->id] = clk; } - - r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); - if (r) - pr_err("%s(): could not register clock provider: %d\n", - __func__, r); } diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 4a22429cd7a2..28aac67e7b92 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -165,7 +165,7 @@ void __init mvebu_coreclk_setup(struct device_node *np, clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, cpuclk_name, 0, mult, div); WARN_ON(IS_ERR(clk_data.clks[2+n])); - }; + } /* Register optional refclk */ if (desc->get_refclk_freq) { diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c index 73f0240569ac..f8dd10f6df3d 100644 --- a/drivers/clk/mxs/clk-frac.c +++ b/drivers/clk/mxs/clk-frac.c @@ -41,11 +41,13 @@ static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, { struct clk_frac *frac = to_clk_frac(hw); u32 div; + u64 tmp_rate; div = readl_relaxed(frac->reg) >> frac->shift; div &= (1 << frac->width) - 1; - return (parent_rate >> frac->width) * div; + tmp_rate = (u64)parent_rate * div; + return tmp_rate >> frac->width; } static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate, @@ -54,7 +56,7 @@ static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate, struct clk_frac *frac = to_clk_frac(hw); unsigned long parent_rate = *prate; u32 div; - u64 tmp; + u64 tmp, tmp_rate, result; if (rate > parent_rate) return -EINVAL; @@ -67,7 +69,11 @@ static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate, if (!div) return -EINVAL; - return (parent_rate >> frac->width) * div; + tmp_rate = (u64)parent_rate * div; + result = tmp_rate >> frac->width; + if ((result << frac->width) < tmp_rate) + result += 1; + return result; } static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 59d16668bdf5..ee4c83aab4f4 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -1,3 +1,7 @@ +config QCOM_GDSC + bool + select PM_GENERIC_DOMAINS if PM + config COMMON_CLK_QCOM tristate "Support for Qualcomm's clock controllers" depends on OF @@ -7,6 +11,7 @@ config COMMON_CLK_QCOM config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on apq8084 devices. @@ -16,6 +21,7 @@ config APQ_GCC_8084 config APQ_MMCC_8084 tristate "APQ8084 Multimedia Clock Controller" select APQ_GCC_8084 + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the multimedia clock controller on apq8084 devices. @@ -49,6 +55,7 @@ config MSM_GCC_8660 config MSM_GCC_8916 tristate "MSM8916 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on msm8916 devices. @@ -83,6 +90,7 @@ config MSM_MMCC_8960 config MSM_GCC_8974 tristate "MSM8974 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on msm8974 devices. @@ -92,6 +100,7 @@ config MSM_GCC_8974 config MSM_MMCC_8974 tristate "MSM8974 Multimedia Clock Controller" select MSM_GCC_8974 + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the multimedia clock controller on msm8974 devices. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 50b337a24a87..fe6252349e55 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -9,6 +9,7 @@ clk-qcom-y += clk-branch.o clk-qcom-y += clk-regmap-divider.o clk-qcom-y += clk-regmap-mux.o clk-qcom-y += reset.o +clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c index bccedc4b5756..bfbb28f450c2 100644 --- a/drivers/clk/qcom/clk-rcg.c +++ b/drivers/clk/qcom/clk-rcg.c @@ -542,6 +542,200 @@ static int clk_rcg_bypass_set_rate(struct clk_hw *hw, unsigned long rate, return __clk_rcg_set_rate(rcg, rcg->freq_tbl); } +static int clk_rcg_bypass2_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *p; + + p = req->best_parent_hw; + req->best_parent_rate = clk_hw_round_rate(p, req->rate); + req->rate = req->best_parent_rate; + + return 0; +} + +static int clk_rcg_bypass2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg *rcg = to_clk_rcg(hw); + struct freq_tbl f = { 0 }; + u32 ns, src; + int i, ret, num_parents = clk_hw_get_num_parents(hw); + + ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); + if (ret) + return ret; + + src = ns_to_src(&rcg->s, ns); + f.pre_div = ns_to_pre_div(&rcg->p, ns) + 1; + + for (i = 0; i < num_parents; i++) { + if (src == rcg->s.parent_map[i].cfg) { + f.src = rcg->s.parent_map[i].src; + return __clk_rcg_set_rate(rcg, &f); + } + } + + return -EINVAL; +} + +static int clk_rcg_bypass2_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + /* Read the hardware to determine parent during set_rate */ + return clk_rcg_bypass2_set_rate(hw, rate, parent_rate); +} + +struct frac_entry { + int num; + int den; +}; + +static const struct frac_entry pixel_table[] = { + { 1, 2 }, + { 1, 3 }, + { 3, 16 }, + { } +}; + +static int clk_rcg_pixel_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int delta = 100000; + const struct frac_entry *frac = pixel_table; + unsigned long request, src_rate; + + for (; frac->num; frac++) { + request = (req->rate * frac->den) / frac->num; + + src_rate = clk_hw_round_rate(req->best_parent_hw, request); + + if ((src_rate < (request - delta)) || + (src_rate > (request + delta))) + continue; + + req->best_parent_rate = src_rate; + req->rate = (src_rate * frac->num) / frac->den; + return 0; + } + + return -EINVAL; +} + +static int clk_rcg_pixel_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg *rcg = to_clk_rcg(hw); + int delta = 100000; + const struct frac_entry *frac = pixel_table; + unsigned long request; + struct freq_tbl f = { 0 }; + u32 ns, src; + int i, ret, num_parents = clk_hw_get_num_parents(hw); + + ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); + if (ret) + return ret; + + src = ns_to_src(&rcg->s, ns); + f.pre_div = ns_to_pre_div(&rcg->p, ns) + 1; + + for (i = 0; i < num_parents; i++) { + if (src == rcg->s.parent_map[i].cfg) { + f.src = rcg->s.parent_map[i].src; + break; + } + } + + /* let us find appropriate m/n values for this */ + for (; frac->num; frac++) { + request = (rate * frac->den) / frac->num; + + if ((parent_rate < (request - delta)) || + (parent_rate > (request + delta))) + continue; + + f.m = frac->num; + f.n = frac->den; + + return __clk_rcg_set_rate(rcg, &f); + } + + return -EINVAL; +} + +static int clk_rcg_pixel_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + return clk_rcg_pixel_set_rate(hw, rate, parent_rate); +} + +static int clk_rcg_esc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg *rcg = to_clk_rcg(hw); + int pre_div_max = BIT(rcg->p.pre_div_width); + int div; + unsigned long src_rate; + + if (req->rate == 0) + return -EINVAL; + + src_rate = clk_hw_get_rate(req->best_parent_hw); + + div = src_rate / req->rate; + + if (div >= 1 && div <= pre_div_max) { + req->best_parent_rate = src_rate; + req->rate = src_rate / div; + return 0; + } + + return -EINVAL; +} + +static int clk_rcg_esc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg *rcg = to_clk_rcg(hw); + struct freq_tbl f = { 0 }; + int pre_div_max = BIT(rcg->p.pre_div_width); + int div; + u32 ns; + int i, ret, num_parents = clk_hw_get_num_parents(hw); + + if (rate == 0) + return -EINVAL; + + ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); + if (ret) + return ret; + + ns = ns_to_src(&rcg->s, ns); + + for (i = 0; i < num_parents; i++) { + if (ns == rcg->s.parent_map[i].cfg) { + f.src = rcg->s.parent_map[i].src; + break; + } + } + + div = parent_rate / rate; + + if (div >= 1 && div <= pre_div_max) { + f.pre_div = div; + return __clk_rcg_set_rate(rcg, &f); + } + + return -EINVAL; +} + +static int clk_rcg_esc_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + return clk_rcg_esc_set_rate(hw, rate, parent_rate); +} + /* * This type of clock has a glitch-free mux that switches between the output of * the M/N counter and an always on clock source (XO). When clk_set_rate() is @@ -639,6 +833,42 @@ const struct clk_ops clk_rcg_bypass_ops = { }; EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops); +const struct clk_ops clk_rcg_bypass2_ops = { + .enable = clk_enable_regmap, + .disable = clk_disable_regmap, + .get_parent = clk_rcg_get_parent, + .set_parent = clk_rcg_set_parent, + .recalc_rate = clk_rcg_recalc_rate, + .determine_rate = clk_rcg_bypass2_determine_rate, + .set_rate = clk_rcg_bypass2_set_rate, + .set_rate_and_parent = clk_rcg_bypass2_set_rate_and_parent, +}; +EXPORT_SYMBOL_GPL(clk_rcg_bypass2_ops); + +const struct clk_ops clk_rcg_pixel_ops = { + .enable = clk_enable_regmap, + .disable = clk_disable_regmap, + .get_parent = clk_rcg_get_parent, + .set_parent = clk_rcg_set_parent, + .recalc_rate = clk_rcg_recalc_rate, + .determine_rate = clk_rcg_pixel_determine_rate, + .set_rate = clk_rcg_pixel_set_rate, + .set_rate_and_parent = clk_rcg_pixel_set_rate_and_parent, +}; +EXPORT_SYMBOL_GPL(clk_rcg_pixel_ops); + +const struct clk_ops clk_rcg_esc_ops = { + .enable = clk_enable_regmap, + .disable = clk_disable_regmap, + .get_parent = clk_rcg_get_parent, + .set_parent = clk_rcg_set_parent, + .recalc_rate = clk_rcg_recalc_rate, + .determine_rate = clk_rcg_esc_determine_rate, + .set_rate = clk_rcg_esc_set_rate, + .set_rate_and_parent = clk_rcg_esc_set_rate_and_parent, +}; +EXPORT_SYMBOL_GPL(clk_rcg_esc_ops); + const struct clk_ops clk_rcg_lcc_ops = { .enable = clk_rcg_lcc_enable, .disable = clk_rcg_lcc_disable, diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 56028bb31d87..4b1e94bdf29e 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -106,6 +106,9 @@ struct clk_rcg { extern const struct clk_ops clk_rcg_ops; extern const struct clk_ops clk_rcg_bypass_ops; +extern const struct clk_ops clk_rcg_bypass2_ops; +extern const struct clk_ops clk_rcg_pixel_ops; +extern const struct clk_ops clk_rcg_esc_ops; extern const struct clk_ops clk_rcg_lcc_ops; #define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr) @@ -153,8 +156,8 @@ extern const struct clk_ops clk_dyn_rcg_ops; * @hid_width: number of bits in half integer divider * @parent_map: map from software's parent index to hardware's src_sel field * @freq_tbl: frequency table + * @current_freq: last cached frequency when using branches with shared RCGs * @clkr: regmap clock handle - * @lock: register lock * */ struct clk_rcg2 { @@ -163,14 +166,17 @@ struct clk_rcg2 { u8 hid_width; const struct parent_map *parent_map; const struct freq_tbl *freq_tbl; + unsigned long current_freq; struct clk_regmap clkr; }; #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) extern const struct clk_ops clk_rcg2_ops; +extern const struct clk_ops clk_rcg2_shared_ops; extern const struct clk_ops clk_edp_pixel_ops; extern const struct clk_ops clk_byte_ops; +extern const struct clk_ops clk_byte2_ops; extern const struct clk_ops clk_pixel_ops; #endif diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 9aec1761fd29..b544bb302f79 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -300,6 +300,85 @@ const struct clk_ops clk_rcg2_ops = { }; EXPORT_SYMBOL_GPL(clk_rcg2_ops); +static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const char *name = clk_hw_get_name(hw); + int ret, count; + + /* force enable RCG */ + ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, + CMD_ROOT_EN, CMD_ROOT_EN); + if (ret) + return ret; + + /* wait for RCG to turn ON */ + for (count = 500; count > 0; count--) { + ret = clk_rcg2_is_enabled(hw); + if (ret) + break; + udelay(1); + } + if (!count) + pr_err("%s: RCG did not turn on\n", name); + + /* set clock rate */ + ret = __clk_rcg2_set_rate(hw, rate); + if (ret) + return ret; + + /* clear force enable RCG */ + return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, + CMD_ROOT_EN, 0); +} + +static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + /* cache the rate */ + rcg->current_freq = rate; + + if (!__clk_is_enabled(hw->clk)) + return 0; + + return clk_rcg2_shared_force_enable(hw, rcg->current_freq); +} + +static unsigned long +clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + return rcg->current_freq = clk_rcg2_recalc_rate(hw, parent_rate); +} + +static int clk_rcg2_shared_enable(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + return clk_rcg2_shared_force_enable(hw, rcg->current_freq); +} + +static void clk_rcg2_shared_disable(struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + /* switch to XO, which is the lowest entry in the freq table */ + clk_rcg2_shared_set_rate(hw, rcg->freq_tbl[0].freq, 0); +} + +const struct clk_ops clk_rcg2_shared_ops = { + .enable = clk_rcg2_shared_enable, + .disable = clk_rcg2_shared_disable, + .get_parent = clk_rcg2_get_parent, + .recalc_rate = clk_rcg2_shared_recalc_rate, + .determine_rate = clk_rcg2_determine_rate, + .set_rate = clk_rcg2_shared_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); + struct frac_entry { int num; int den; @@ -485,6 +564,76 @@ const struct clk_ops clk_byte_ops = { }; EXPORT_SYMBOL_GPL(clk_byte_ops); +static int clk_byte2_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + unsigned long parent_rate, div; + u32 mask = BIT(rcg->hid_width) - 1; + struct clk_hw *p; + unsigned long rate = req->rate; + + if (rate == 0) + return -EINVAL; + + p = req->best_parent_hw; + req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate); + + div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; + div = min_t(u32, div, mask); + + req->rate = calc_rate(parent_rate, 0, 0, 0, div); + + return 0; +} + +static int clk_byte2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + struct freq_tbl f = { 0 }; + unsigned long div; + int i, num_parents = clk_hw_get_num_parents(hw); + u32 mask = BIT(rcg->hid_width) - 1; + u32 cfg; + + div = DIV_ROUND_UP((2 * parent_rate), rate) - 1; + div = min_t(u32, div, mask); + + f.pre_div = div; + + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + cfg &= CFG_SRC_SEL_MASK; + cfg >>= CFG_SRC_SEL_SHIFT; + + for (i = 0; i < num_parents; i++) { + if (cfg == rcg->parent_map[i].cfg) { + f.src = rcg->parent_map[i].src; + return clk_rcg2_configure(rcg, &f); + } + } + + return -EINVAL; +} + +static int clk_byte2_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + /* Read the hardware to determine parent during set_rate */ + return clk_byte2_set_rate(hw, rate, parent_rate); +} + +const struct clk_ops clk_byte2_ops = { + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .set_rate = clk_byte2_set_rate, + .set_rate_and_parent = clk_byte2_set_rate_and_parent, + .determine_rate = clk_byte2_determine_rate, +}; +EXPORT_SYMBOL_GPL(clk_byte2_ops); + static const struct frac_entry frac_table_pixel[] = { { 3, 8 }, { 2, 9 }, @@ -496,14 +645,9 @@ static const struct frac_entry frac_table_pixel[] = { static int clk_pixel_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct clk_rcg2 *rcg = to_clk_rcg2(hw); unsigned long request, src_rate; int delta = 100000; - const struct freq_tbl *f = rcg->freq_tbl; const struct frac_entry *frac = frac_table_pixel; - int index = qcom_find_src_index(hw, rcg->parent_map, f->src); - - req->best_parent_hw = clk_hw_get_parent_by_index(hw, index); for (; frac->num; frac++) { request = (req->rate * frac->den) / frac->num; @@ -525,12 +669,23 @@ static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); - struct freq_tbl f = *rcg->freq_tbl; + struct freq_tbl f = { 0 }; const struct frac_entry *frac = frac_table_pixel; unsigned long request; int delta = 100000; u32 mask = BIT(rcg->hid_width) - 1; - u32 hid_div; + u32 hid_div, cfg; + int i, num_parents = clk_hw_get_num_parents(hw); + + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + cfg &= CFG_SRC_SEL_MASK; + cfg >>= CFG_SRC_SEL_SHIFT; + + for (i = 0; i < num_parents; i++) + if (cfg == rcg->parent_map[i].cfg) { + f.src = rcg->parent_map[i].src; + break; + } for (; frac->num; frac++) { request = (rate * frac->den) / frac->num; @@ -555,7 +710,6 @@ static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate, static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index) { - /* Parent index is set statically in frequency table */ return clk_pixel_set_rate(hw, rate, parent_rate); } diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 2dedceefd21d..8fa477293ae0 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -22,6 +22,7 @@ #include "clk-rcg.h" #include "clk-regmap.h" #include "reset.h" +#include "gdsc.h" struct qcom_cc { struct qcom_reset_controller reset; @@ -72,6 +73,21 @@ qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc) } EXPORT_SYMBOL_GPL(qcom_cc_map); +static void qcom_cc_del_clk_provider(void *data) +{ + of_clk_del_provider(data); +} + +static void qcom_cc_reset_unregister(void *data) +{ + reset_controller_unregister(data); +} + +static void qcom_cc_gdsc_unregister(void *data) +{ + gdsc_unregister(data); +} + int qcom_cc_really_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc, struct regmap *regmap) { @@ -110,6 +126,8 @@ int qcom_cc_really_probe(struct platform_device *pdev, if (ret) return ret; + devm_add_action(dev, qcom_cc_del_clk_provider, pdev->dev.of_node); + reset = &cc->reset; reset->rcdev.of_node = dev->of_node; reset->rcdev.ops = &qcom_reset_ops; @@ -117,13 +135,24 @@ int qcom_cc_really_probe(struct platform_device *pdev, reset->rcdev.nr_resets = desc->num_resets; reset->regmap = regmap; reset->reset_map = desc->resets; - platform_set_drvdata(pdev, &reset->rcdev); ret = reset_controller_register(&reset->rcdev); if (ret) - of_clk_del_provider(dev->of_node); + return ret; + + devm_add_action(dev, qcom_cc_reset_unregister, &reset->rcdev); + + if (desc->gdscs && desc->num_gdscs) { + ret = gdsc_register(dev, desc->gdscs, desc->num_gdscs, + &reset->rcdev, regmap); + if (ret) + return ret; + } + + devm_add_action(dev, qcom_cc_gdsc_unregister, dev); - return ret; + + return 0; } EXPORT_SYMBOL_GPL(qcom_cc_really_probe); @@ -139,11 +168,4 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) } EXPORT_SYMBOL_GPL(qcom_cc_probe); -void qcom_cc_remove(struct platform_device *pdev) -{ - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); -} -EXPORT_SYMBOL_GPL(qcom_cc_remove); - MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 7a0e73713063..7c1fba3ebc03 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -28,6 +28,8 @@ struct qcom_cc_desc { size_t num_clks; const struct qcom_reset_map *resets; size_t num_resets; + struct gdsc **gdscs; + size_t num_gdscs; }; extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, @@ -43,6 +45,4 @@ extern int qcom_cc_really_probe(struct platform_device *pdev, extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); -extern void qcom_cc_remove(struct platform_device *pdev); - #endif diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c index 3563019b8e3c..1567c3a79534 100644 --- a/drivers/clk/qcom/gcc-apq8084.c +++ b/drivers/clk/qcom/gcc-apq8084.c @@ -31,6 +31,7 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "gdsc.h" enum { P_XO, @@ -3254,6 +3255,38 @@ static struct clk_branch gcc_usb_hsic_system_clk = { }, }; +static struct gdsc usb_hs_hsic_gdsc = { + .gdscr = 0x404, + .pd = { + .name = "usb_hs_hsic", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc pcie0_gdsc = { + .gdscr = 0x1ac4, + .pd = { + .name = "pcie0", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc pcie1_gdsc = { + .gdscr = 0x1b44, + .pd = { + .name = "pcie1", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc usb30_gdsc = { + .gdscr = 0x1e84, + .pd = { + .name = "usb30", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct clk_regmap *gcc_apq8084_clocks[] = { [GPLL0] = &gpll0.clkr, [GPLL0_VOTE] = &gpll0_vote, @@ -3447,6 +3480,13 @@ static struct clk_regmap *gcc_apq8084_clocks[] = { [GCC_USB_HSIC_SYSTEM_CLK] = &gcc_usb_hsic_system_clk.clkr, }; +static struct gdsc *gcc_apq8084_gdscs[] = { + [USB_HS_HSIC_GDSC] = &usb_hs_hsic_gdsc, + [PCIE0_GDSC] = &pcie0_gdsc, + [PCIE1_GDSC] = &pcie1_gdsc, + [USB30_GDSC] = &usb30_gdsc, +}; + static const struct qcom_reset_map gcc_apq8084_resets[] = { [GCC_SYSTEM_NOC_BCR] = { 0x0100 }, [GCC_CONFIG_NOC_BCR] = { 0x0140 }, @@ -3555,6 +3595,8 @@ static const struct qcom_cc_desc gcc_apq8084_desc = { .num_clks = ARRAY_SIZE(gcc_apq8084_clocks), .resets = gcc_apq8084_resets, .num_resets = ARRAY_SIZE(gcc_apq8084_resets), + .gdscs = gcc_apq8084_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_apq8084_gdscs), }; static const struct of_device_id gcc_apq8084_match_table[] = { @@ -3581,15 +3623,8 @@ static int gcc_apq8084_probe(struct platform_device *pdev) return qcom_cc_probe(pdev, &gcc_apq8084_desc); } -static int gcc_apq8084_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver gcc_apq8084_driver = { .probe = gcc_apq8084_probe, - .remove = gcc_apq8084_remove, .driver = { .name = "gcc-apq8084", .of_match_table = gcc_apq8084_match_table, diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index 40e480220cd3..16fc64c082a5 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -3058,15 +3058,8 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) return 0; } -static int gcc_ipq806x_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver gcc_ipq806x_driver = { .probe = gcc_ipq806x_probe, - .remove = gcc_ipq806x_remove, .driver = { .name = "gcc-ipq806x", .of_match_table = gcc_ipq806x_match_table, diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c index b02826ed770a..f110bb5a1df3 100644 --- a/drivers/clk/qcom/gcc-msm8660.c +++ b/drivers/clk/qcom/gcc-msm8660.c @@ -2735,15 +2735,8 @@ static int gcc_msm8660_probe(struct platform_device *pdev) return qcom_cc_probe(pdev, &gcc_msm8660_desc); } -static int gcc_msm8660_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver gcc_msm8660_driver = { .probe = gcc_msm8660_probe, - .remove = gcc_msm8660_remove, .driver = { .name = "gcc-msm8660", .of_match_table = gcc_msm8660_match_table, diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 22a4e1e732c0..d0a0313d6bef 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -31,6 +31,7 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "gdsc.h" enum { P_XO, @@ -44,6 +45,9 @@ enum { P_SLEEP_CLK, P_DSI0_PHYPLL_BYTE, P_DSI0_PHYPLL_DSI, + P_EXT_PRI_I2S, + P_EXT_SEC_I2S, + P_EXT_MCLK, }; static const struct parent_map gcc_xo_gpll0_map[] = { @@ -190,6 +194,76 @@ static const char * const gcc_xo_gpll0a_gpll1_gpll2[] = { "gpll2_vote", }; +static const struct parent_map gcc_xo_gpll0_gpll1_sleep_map[] = { + { P_XO, 0 }, + { P_GPLL0, 1 }, + { P_GPLL1, 2 }, + { P_SLEEP_CLK, 6 } +}; + +static const char * const gcc_xo_gpll0_gpll1_sleep[] = { + "xo", + "gpll0_vote", + "gpll1_vote", + "sleep_clk", +}; + +static const struct parent_map gcc_xo_gpll1_epi2s_emclk_sleep_map[] = { + { P_XO, 0 }, + { P_GPLL1, 1 }, + { P_EXT_PRI_I2S, 2 }, + { P_EXT_MCLK, 3 }, + { P_SLEEP_CLK, 6 } +}; + +static const char * const gcc_xo_gpll1_epi2s_emclk_sleep[] = { + "xo", + "gpll1_vote", + "ext_pri_i2s", + "ext_mclk", + "sleep_clk", +}; + +static const struct parent_map gcc_xo_gpll1_esi2s_emclk_sleep_map[] = { + { P_XO, 0 }, + { P_GPLL1, 1 }, + { P_EXT_SEC_I2S, 2 }, + { P_EXT_MCLK, 3 }, + { P_SLEEP_CLK, 6 } +}; + +static const char * const gcc_xo_gpll1_esi2s_emclk_sleep[] = { + "xo", + "gpll1_vote", + "ext_sec_i2s", + "ext_mclk", + "sleep_clk", +}; + +static const struct parent_map gcc_xo_sleep_map[] = { + { P_XO, 0 }, + { P_SLEEP_CLK, 6 } +}; + +static const char * const gcc_xo_sleep[] = { + "xo", + "sleep_clk", +}; + +static const struct parent_map gcc_xo_gpll1_emclk_sleep_map[] = { + { P_XO, 0 }, + { P_GPLL1, 1 }, + { P_EXT_MCLK, 2 }, + { P_SLEEP_CLK, 6 } +}; + +static const char * const gcc_xo_gpll1_emclk_sleep[] = { + "xo", + "gpll1_vote", + "ext_mclk", + "sleep_clk", +}; + #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } static struct clk_pll gpll0 = { @@ -906,21 +980,15 @@ static struct clk_rcg2 gp3_clk_src = { }, }; -static struct freq_tbl ftbl_gcc_mdss_byte0_clk[] = { - { .src = P_DSI0_PHYPLL_BYTE }, - { } -}; - static struct clk_rcg2 byte0_clk_src = { .cmd_rcgr = 0x4d044, .hid_width = 5, .parent_map = gcc_xo_gpll0a_dsibyte_map, - .freq_tbl = ftbl_gcc_mdss_byte0_clk, .clkr.hw.init = &(struct clk_init_data){ .name = "byte0_clk_src", .parent_names = gcc_xo_gpll0a_dsibyte, .num_parents = 3, - .ops = &clk_byte_ops, + .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, }; @@ -968,17 +1036,11 @@ static struct clk_rcg2 mdp_clk_src = { }, }; -static struct freq_tbl ftbl_gcc_mdss_pclk[] = { - { .src = P_DSI0_PHYPLL_DSI }, - { } -}; - static struct clk_rcg2 pclk0_clk_src = { .cmd_rcgr = 0x4d000, .mnd_width = 8, .hid_width = 5, .parent_map = gcc_xo_gpll0a_dsiphy_map, - .freq_tbl = ftbl_gcc_mdss_pclk, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk0_clk_src", .parent_names = gcc_xo_gpll0a_dsiphy, @@ -1094,6 +1156,30 @@ static struct clk_rcg2 apss_tcu_clk_src = { }, }; +static const struct freq_tbl ftbl_gcc_bimc_gpu_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 8, 0, 0), + F(200000000, P_GPLL0, 4, 0, 0), + F(266500000, P_BIMC, 4, 0, 0), + F(400000000, P_GPLL0, 2, 0, 0), + F(533000000, P_BIMC, 2, 0, 0), + { } +}; + +static struct clk_rcg2 bimc_gpu_clk_src = { + .cmd_rcgr = 0x31028, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_bimc_map, + .freq_tbl = ftbl_gcc_bimc_gpu_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "bimc_gpu_clk_src", + .parent_names = gcc_xo_gpll0_bimc, + .num_parents = 3, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_shared_ops, + }, +}; + static const struct freq_tbl ftbl_gcc_usb_hs_system_clk[] = { F(80000000, P_GPLL0, 10, 0, 0), { } @@ -1112,6 +1198,305 @@ static struct clk_rcg2 usb_hs_system_clk_src = { }, }; +static const struct freq_tbl ftbl_gcc_ultaudio_ahb_clk[] = { + F(3200000, P_XO, 6, 0, 0), + F(6400000, P_XO, 3, 0, 0), + F(9600000, P_XO, 2, 0, 0), + F(19200000, P_XO, 1, 0, 0), + F(40000000, P_GPLL0, 10, 1, 2), + F(66670000, P_GPLL0, 12, 0, 0), + F(80000000, P_GPLL0, 10, 0, 0), + F(100000000, P_GPLL0, 8, 0, 0), + { } +}; + +static struct clk_rcg2 ultaudio_ahbfabric_clk_src = { + .cmd_rcgr = 0x1c010, + .hid_width = 5, + .mnd_width = 8, + .parent_map = gcc_xo_gpll0_gpll1_sleep_map, + .freq_tbl = ftbl_gcc_ultaudio_ahb_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ultaudio_ahbfabric_clk_src", + .parent_names = gcc_xo_gpll0_gpll1_sleep, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_ultaudio_ahbfabric_ixfabric_clk = { + .halt_reg = 0x1c028, + .clkr = { + .enable_reg = 0x1c028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_ahbfabric_ixfabric_clk", + .parent_names = (const char *[]){ + "ultaudio_ahbfabric_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ultaudio_ahbfabric_ixfabric_lpm_clk = { + .halt_reg = 0x1c024, + .clkr = { + .enable_reg = 0x1c024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_ahbfabric_ixfabric_lpm_clk", + .parent_names = (const char *[]){ + "ultaudio_ahbfabric_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_ultaudio_lpaif_i2s_clk[] = { + F(256000, P_XO, 5, 1, 15), + F(512000, P_XO, 5, 2, 15), + F(705600, P_GPLL1, 16, 1, 80), + F(768000, P_XO, 5, 1, 5), + F(800000, P_XO, 5, 5, 24), + F(1024000, P_GPLL1, 14, 1, 63), + F(1152000, P_XO, 1, 3, 50), + F(1411200, P_GPLL1, 16, 1, 40), + F(1536000, P_XO, 1, 2, 25), + F(1600000, P_XO, 12, 0, 0), + F(2048000, P_GPLL1, 9, 1, 49), + F(2400000, P_XO, 8, 0, 0), + F(2822400, P_GPLL1, 16, 1, 20), + F(3072000, P_GPLL1, 14, 1, 21), + F(4096000, P_GPLL1, 9, 2, 49), + F(4800000, P_XO, 4, 0, 0), + F(5644800, P_GPLL1, 16, 1, 10), + F(6144000, P_GPLL1, 7, 1, 21), + F(8192000, P_GPLL1, 9, 4, 49), + F(9600000, P_XO, 2, 0, 0), + F(11289600, P_GPLL1, 16, 1, 5), + F(12288000, P_GPLL1, 7, 2, 21), + { } +}; + +static struct clk_rcg2 ultaudio_lpaif_pri_i2s_clk_src = { + .cmd_rcgr = 0x1c054, + .hid_width = 5, + .mnd_width = 8, + .parent_map = gcc_xo_gpll1_epi2s_emclk_sleep_map, + .freq_tbl = ftbl_gcc_ultaudio_lpaif_i2s_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ultaudio_lpaif_pri_i2s_clk_src", + .parent_names = gcc_xo_gpll1_epi2s_emclk_sleep, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_ultaudio_lpaif_pri_i2s_clk = { + .halt_reg = 0x1c068, + .clkr = { + .enable_reg = 0x1c068, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_lpaif_pri_i2s_clk", + .parent_names = (const char *[]){ + "ultaudio_lpaif_pri_i2s_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_rcg2 ultaudio_lpaif_sec_i2s_clk_src = { + .cmd_rcgr = 0x1c06c, + .hid_width = 5, + .mnd_width = 8, + .parent_map = gcc_xo_gpll1_esi2s_emclk_sleep_map, + .freq_tbl = ftbl_gcc_ultaudio_lpaif_i2s_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ultaudio_lpaif_sec_i2s_clk_src", + .parent_names = gcc_xo_gpll1_esi2s_emclk_sleep, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_ultaudio_lpaif_sec_i2s_clk = { + .halt_reg = 0x1c080, + .clkr = { + .enable_reg = 0x1c080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_lpaif_sec_i2s_clk", + .parent_names = (const char *[]){ + "ultaudio_lpaif_sec_i2s_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_rcg2 ultaudio_lpaif_aux_i2s_clk_src = { + .cmd_rcgr = 0x1c084, + .hid_width = 5, + .mnd_width = 8, + .parent_map = gcc_xo_gpll1_emclk_sleep_map, + .freq_tbl = ftbl_gcc_ultaudio_lpaif_i2s_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ultaudio_lpaif_aux_i2s_clk_src", + .parent_names = gcc_xo_gpll1_esi2s_emclk_sleep, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_ultaudio_lpaif_aux_i2s_clk = { + .halt_reg = 0x1c098, + .clkr = { + .enable_reg = 0x1c098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_lpaif_aux_i2s_clk", + .parent_names = (const char *[]){ + "ultaudio_lpaif_aux_i2s_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_ultaudio_xo_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 ultaudio_xo_clk_src = { + .cmd_rcgr = 0x1c034, + .hid_width = 5, + .parent_map = gcc_xo_sleep_map, + .freq_tbl = ftbl_gcc_ultaudio_xo_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ultaudio_xo_clk_src", + .parent_names = gcc_xo_sleep, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_ultaudio_avsync_xo_clk = { + .halt_reg = 0x1c04c, + .clkr = { + .enable_reg = 0x1c04c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_avsync_xo_clk", + .parent_names = (const char *[]){ + "ultaudio_xo_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ultaudio_stc_xo_clk = { + .halt_reg = 0x1c050, + .clkr = { + .enable_reg = 0x1c050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_stc_xo_clk", + .parent_names = (const char *[]){ + "ultaudio_xo_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_codec_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(11289600, P_EXT_MCLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 codec_digcodec_clk_src = { + .cmd_rcgr = 0x1c09c, + .hid_width = 5, + .parent_map = gcc_xo_gpll1_emclk_sleep_map, + .freq_tbl = ftbl_codec_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "codec_digcodec_clk_src", + .parent_names = gcc_xo_gpll1_emclk_sleep, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_codec_digcodec_clk = { + .halt_reg = 0x1c0b0, + .clkr = { + .enable_reg = 0x1c0b0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_codec_digcodec_clk", + .parent_names = (const char *[]){ + "codec_digcodec_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ultaudio_pcnoc_mport_clk = { + .halt_reg = 0x1c000, + .clkr = { + .enable_reg = 0x1c000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_pcnoc_mport_clk", + .parent_names = (const char *[]){ + "pcnoc_bfdcd_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ultaudio_pcnoc_sway_clk = { + .halt_reg = 0x1c004, + .clkr = { + .enable_reg = 0x1c004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ultaudio_pcnoc_sway_clk", + .parent_names = (const char *[]){ + "pcnoc_bfdcd_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + static const struct freq_tbl ftbl_gcc_venus0_vcodec0_clk[] = { F(100000000, P_GPLL0, 8, 0, 0), F(160000000, P_GPLL0, 5, 0, 0), @@ -2358,6 +2743,51 @@ static struct clk_branch gcc_sdcc2_apps_clk = { }, }; +static struct clk_rcg2 bimc_ddr_clk_src = { + .cmd_rcgr = 0x32004, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_bimc_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "bimc_ddr_clk_src", + .parent_names = gcc_xo_gpll0_bimc, + .num_parents = 3, + .ops = &clk_rcg2_ops, + .flags = CLK_GET_RATE_NOCACHE, + }, +}; + +static struct clk_branch gcc_apss_tcu_clk = { + .halt_reg = 0x12018, + .clkr = { + .enable_reg = 0x4500c, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_apss_tcu_clk", + .parent_names = (const char *[]){ + "bimc_ddr_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gfx_tcu_clk = { + .halt_reg = 0x12020, + .clkr = { + .enable_reg = 0x4500c, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gfx_tcu_clk", + .parent_names = (const char *[]){ + "bimc_ddr_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_gtcu_ahb_clk = { .halt_reg = 0x12044, .clkr = { @@ -2375,6 +2805,40 @@ static struct clk_branch gcc_gtcu_ahb_clk = { }, }; +static struct clk_branch gcc_bimc_gfx_clk = { + .halt_reg = 0x31024, + .clkr = { + .enable_reg = 0x31024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_bimc_gfx_clk", + .parent_names = (const char *[]){ + "bimc_gpu_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_bimc_gpu_clk = { + .halt_reg = 0x31040, + .clkr = { + .enable_reg = 0x31040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_bimc_gpu_clk", + .parent_names = (const char *[]){ + "bimc_gpu_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_jpeg_tbu_clk = { .halt_reg = 0x12034, .clkr = { @@ -2562,6 +3026,46 @@ static struct clk_branch gcc_venus0_vcodec0_clk = { }, }; +static struct gdsc venus_gdsc = { + .gdscr = 0x4c018, + .pd = { + .name = "venus", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x4d078, + .pd = { + .name = "mdss", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc jpeg_gdsc = { + .gdscr = 0x5701c, + .pd = { + .name = "jpeg", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc vfe_gdsc = { + .gdscr = 0x58034, + .pd = { + .name = "vfe", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc oxili_gdsc = { + .gdscr = 0x5901c, + .pd = { + .name = "oxili", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct clk_regmap *gcc_msm8916_clocks[] = { [GPLL0] = &gpll0.clkr, [GPLL0_VOTE] = &gpll0_vote, @@ -2701,6 +3205,36 @@ static struct clk_regmap *gcc_msm8916_clocks[] = { [GCC_VENUS0_AHB_CLK] = &gcc_venus0_ahb_clk.clkr, [GCC_VENUS0_AXI_CLK] = &gcc_venus0_axi_clk.clkr, [GCC_VENUS0_VCODEC0_CLK] = &gcc_venus0_vcodec0_clk.clkr, + [BIMC_DDR_CLK_SRC] = &bimc_ddr_clk_src.clkr, + [GCC_APSS_TCU_CLK] = &gcc_apss_tcu_clk.clkr, + [GCC_GFX_TCU_CLK] = &gcc_gfx_tcu_clk.clkr, + [BIMC_GPU_CLK_SRC] = &bimc_gpu_clk_src.clkr, + [GCC_BIMC_GFX_CLK] = &gcc_bimc_gfx_clk.clkr, + [GCC_BIMC_GPU_CLK] = &gcc_bimc_gpu_clk.clkr, + [ULTAUDIO_AHBFABRIC_CLK_SRC] = &ultaudio_ahbfabric_clk_src.clkr, + [ULTAUDIO_LPAIF_PRI_I2S_CLK_SRC] = &ultaudio_lpaif_pri_i2s_clk_src.clkr, + [ULTAUDIO_LPAIF_SEC_I2S_CLK_SRC] = &ultaudio_lpaif_sec_i2s_clk_src.clkr, + [ULTAUDIO_LPAIF_AUX_I2S_CLK_SRC] = &ultaudio_lpaif_aux_i2s_clk_src.clkr, + [ULTAUDIO_XO_CLK_SRC] = &ultaudio_xo_clk_src.clkr, + [CODEC_DIGCODEC_CLK_SRC] = &codec_digcodec_clk_src.clkr, + [GCC_ULTAUDIO_PCNOC_MPORT_CLK] = &gcc_ultaudio_pcnoc_mport_clk.clkr, + [GCC_ULTAUDIO_PCNOC_SWAY_CLK] = &gcc_ultaudio_pcnoc_sway_clk.clkr, + [GCC_ULTAUDIO_AVSYNC_XO_CLK] = &gcc_ultaudio_avsync_xo_clk.clkr, + [GCC_ULTAUDIO_STC_XO_CLK] = &gcc_ultaudio_stc_xo_clk.clkr, + [GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_CLK] = &gcc_ultaudio_ahbfabric_ixfabric_clk.clkr, + [GCC_ULTAUDIO_AHBFABRIC_IXFABRIC_LPM_CLK] = &gcc_ultaudio_ahbfabric_ixfabric_lpm_clk.clkr, + [GCC_ULTAUDIO_LPAIF_PRI_I2S_CLK] = &gcc_ultaudio_lpaif_pri_i2s_clk.clkr, + [GCC_ULTAUDIO_LPAIF_SEC_I2S_CLK] = &gcc_ultaudio_lpaif_sec_i2s_clk.clkr, + [GCC_ULTAUDIO_LPAIF_AUX_I2S_CLK] = &gcc_ultaudio_lpaif_aux_i2s_clk.clkr, + [GCC_CODEC_DIGCODEC_CLK] = &gcc_codec_digcodec_clk.clkr, +}; + +static struct gdsc *gcc_msm8916_gdscs[] = { + [VENUS_GDSC] = &venus_gdsc, + [MDSS_GDSC] = &mdss_gdsc, + [JPEG_GDSC] = &jpeg_gdsc, + [VFE_GDSC] = &vfe_gdsc, + [OXILI_GDSC] = &oxili_gdsc, }; static const struct qcom_reset_map gcc_msm8916_resets[] = { @@ -2810,6 +3344,8 @@ static const struct qcom_cc_desc gcc_msm8916_desc = { .num_clks = ARRAY_SIZE(gcc_msm8916_clocks), .resets = gcc_msm8916_resets, .num_resets = ARRAY_SIZE(gcc_msm8916_resets), + .gdscs = gcc_msm8916_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_msm8916_gdscs), }; static const struct of_device_id gcc_msm8916_match_table[] = { @@ -2836,15 +3372,8 @@ static int gcc_msm8916_probe(struct platform_device *pdev) return qcom_cc_probe(pdev, &gcc_msm8916_desc); } -static int gcc_msm8916_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver gcc_msm8916_driver = { .probe = gcc_msm8916_probe, - .remove = gcc_msm8916_remove, .driver = { .name = "gcc-msm8916", .of_match_table = gcc_msm8916_match_table, diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index aa294b1bad34..66c18bc97857 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -3506,6 +3506,8 @@ static int gcc_msm8960_probe(struct platform_device *pdev) struct clk *clk; struct device *dev = &pdev->dev; const struct of_device_id *match; + struct platform_device *tsens; + int ret; match = of_match_device(gcc_msm8960_match_table, &pdev->dev); if (!match) @@ -3520,12 +3522,26 @@ static int gcc_msm8960_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - return qcom_cc_probe(pdev, match->data); + ret = qcom_cc_probe(pdev, match->data); + if (ret) + return ret; + + tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1, + NULL, 0); + if (IS_ERR(tsens)) + return PTR_ERR(tsens); + + platform_set_drvdata(pdev, tsens); + + return 0; } static int gcc_msm8960_remove(struct platform_device *pdev) { - qcom_cc_remove(pdev); + struct platform_device *tsens = platform_get_drvdata(pdev); + + platform_device_unregister(tsens); + return 0; } diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c index 2bcf87538f9d..28abb8f8f293 100644 --- a/drivers/clk/qcom/gcc-msm8974.c +++ b/drivers/clk/qcom/gcc-msm8974.c @@ -31,6 +31,7 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "gdsc.h" enum { P_XO, @@ -2432,6 +2433,14 @@ static struct clk_branch gcc_usb_hsic_system_clk = { }, }; +static struct gdsc usb_hs_hsic_gdsc = { + .gdscr = 0x404, + .pd = { + .name = "usb_hs_hsic", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct clk_regmap *gcc_msm8974_clocks[] = { [GPLL0] = &gpll0.clkr, [GPLL0_VOTE] = &gpll0_vote, @@ -2661,6 +2670,10 @@ static const struct qcom_reset_map gcc_msm8974_resets[] = { [GCC_VENUS_RESTART] = { 0x1740 }, }; +static struct gdsc *gcc_msm8974_gdscs[] = { + [USB_HS_HSIC_GDSC] = &usb_hs_hsic_gdsc, +}; + static const struct regmap_config gcc_msm8974_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2675,6 +2688,8 @@ static const struct qcom_cc_desc gcc_msm8974_desc = { .num_clks = ARRAY_SIZE(gcc_msm8974_clocks), .resets = gcc_msm8974_resets, .num_resets = ARRAY_SIZE(gcc_msm8974_resets), + .gdscs = gcc_msm8974_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_msm8974_gdscs), }; static const struct of_device_id gcc_msm8974_match_table[] = { @@ -2729,15 +2744,8 @@ static int gcc_msm8974_probe(struct platform_device *pdev) return qcom_cc_probe(pdev, &gcc_msm8974_desc); } -static int gcc_msm8974_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver gcc_msm8974_driver = { .probe = gcc_msm8974_probe, - .remove = gcc_msm8974_remove, .driver = { .name = "gcc-msm8974", .of_match_table = gcc_msm8974_match_table, diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c new file mode 100644 index 000000000000..da9fad8b642b --- /dev/null +++ b/drivers/clk/qcom/gdsc.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/pm_domain.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> +#include "gdsc.h" + +#define PWR_ON_MASK BIT(31) +#define EN_REST_WAIT_MASK GENMASK_ULL(23, 20) +#define EN_FEW_WAIT_MASK GENMASK_ULL(19, 16) +#define CLK_DIS_WAIT_MASK GENMASK_ULL(15, 12) +#define SW_OVERRIDE_MASK BIT(2) +#define HW_CONTROL_MASK BIT(1) +#define SW_COLLAPSE_MASK BIT(0) + +/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */ +#define EN_REST_WAIT_VAL (0x2 << 20) +#define EN_FEW_WAIT_VAL (0x8 << 16) +#define CLK_DIS_WAIT_VAL (0x2 << 12) + +#define RETAIN_MEM BIT(14) +#define RETAIN_PERIPH BIT(13) + +#define TIMEOUT_US 100 + +#define domain_to_gdsc(domain) container_of(domain, struct gdsc, pd) + +static int gdsc_is_enabled(struct gdsc *sc) +{ + u32 val; + int ret; + + ret = regmap_read(sc->regmap, sc->gdscr, &val); + if (ret) + return ret; + + return !!(val & PWR_ON_MASK); +} + +static int gdsc_toggle_logic(struct gdsc *sc, bool en) +{ + int ret; + u32 val = en ? 0 : SW_COLLAPSE_MASK; + u32 check = en ? PWR_ON_MASK : 0; + unsigned long timeout; + + ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val); + if (ret) + return ret; + + timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); + do { + ret = regmap_read(sc->regmap, sc->gdscr, &val); + if (ret) + return ret; + + if ((val & PWR_ON_MASK) == check) + return 0; + } while (time_before(jiffies, timeout)); + + ret = regmap_read(sc->regmap, sc->gdscr, &val); + if (ret) + return ret; + + if ((val & PWR_ON_MASK) == check) + return 0; + + return -ETIMEDOUT; +} + +static inline int gdsc_deassert_reset(struct gdsc *sc) +{ + int i; + + for (i = 0; i < sc->reset_count; i++) + sc->rcdev->ops->deassert(sc->rcdev, sc->resets[i]); + return 0; +} + +static inline int gdsc_assert_reset(struct gdsc *sc) +{ + int i; + + for (i = 0; i < sc->reset_count; i++) + sc->rcdev->ops->assert(sc->rcdev, sc->resets[i]); + return 0; +} + +static inline void gdsc_force_mem_on(struct gdsc *sc) +{ + int i; + u32 mask = RETAIN_MEM | RETAIN_PERIPH; + + for (i = 0; i < sc->cxc_count; i++) + regmap_update_bits(sc->regmap, sc->cxcs[i], mask, mask); +} + +static inline void gdsc_clear_mem_on(struct gdsc *sc) +{ + int i; + u32 mask = RETAIN_MEM | RETAIN_PERIPH; + + for (i = 0; i < sc->cxc_count; i++) + regmap_update_bits(sc->regmap, sc->cxcs[i], mask, 0); +} + +static int gdsc_enable(struct generic_pm_domain *domain) +{ + struct gdsc *sc = domain_to_gdsc(domain); + int ret; + + if (sc->pwrsts == PWRSTS_ON) + return gdsc_deassert_reset(sc); + + ret = gdsc_toggle_logic(sc, true); + if (ret) + return ret; + + if (sc->pwrsts & PWRSTS_OFF) + gdsc_force_mem_on(sc); + + /* + * If clocks to this power domain were already on, they will take an + * additional 4 clock cycles to re-enable after the power domain is + * enabled. Delay to account for this. A delay is also needed to ensure + * clocks are not enabled within 400ns of enabling power to the + * memories. + */ + udelay(1); + + return 0; +} + +static int gdsc_disable(struct generic_pm_domain *domain) +{ + struct gdsc *sc = domain_to_gdsc(domain); + + if (sc->pwrsts == PWRSTS_ON) + return gdsc_assert_reset(sc); + + if (sc->pwrsts & PWRSTS_OFF) + gdsc_clear_mem_on(sc); + + return gdsc_toggle_logic(sc, false); +} + +static int gdsc_init(struct gdsc *sc) +{ + u32 mask, val; + int on, ret; + + /* + * Disable HW trigger: collapse/restore occur based on registers writes. + * Disable SW override: Use hardware state-machine for sequencing. + * Configure wait time between states. + */ + mask = HW_CONTROL_MASK | SW_OVERRIDE_MASK | + EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK; + val = EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL; + ret = regmap_update_bits(sc->regmap, sc->gdscr, mask, val); + if (ret) + return ret; + + /* Force gdsc ON if only ON state is supported */ + if (sc->pwrsts == PWRSTS_ON) { + ret = gdsc_toggle_logic(sc, true); + if (ret) + return ret; + } + + on = gdsc_is_enabled(sc); + if (on < 0) + return on; + + if (on || (sc->pwrsts & PWRSTS_RET)) + gdsc_force_mem_on(sc); + else + gdsc_clear_mem_on(sc); + + sc->pd.power_off = gdsc_disable; + sc->pd.power_on = gdsc_enable; + pm_genpd_init(&sc->pd, NULL, !on); + + return 0; +} + +int gdsc_register(struct device *dev, struct gdsc **scs, size_t num, + struct reset_controller_dev *rcdev, struct regmap *regmap) +{ + int i, ret; + struct genpd_onecell_data *data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domains = devm_kcalloc(dev, num, sizeof(*data->domains), + GFP_KERNEL); + if (!data->domains) + return -ENOMEM; + + data->num_domains = num; + for (i = 0; i < num; i++) { + if (!scs[i]) + continue; + scs[i]->regmap = regmap; + scs[i]->rcdev = rcdev; + ret = gdsc_init(scs[i]); + if (ret) + return ret; + data->domains[i] = &scs[i]->pd; + } + + return of_genpd_add_provider_onecell(dev->of_node, data); +} + +void gdsc_unregister(struct device *dev) +{ + of_genpd_del_provider(dev->of_node); +} diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h new file mode 100644 index 000000000000..5ded26884f08 --- /dev/null +++ b/drivers/clk/qcom/gdsc.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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. + */ + +#ifndef __QCOM_GDSC_H__ +#define __QCOM_GDSC_H__ + +#include <linux/err.h> +#include <linux/pm_domain.h> + +struct regmap; +struct reset_controller_dev; + +/* Powerdomain allowable state bitfields */ +#define PWRSTS_OFF BIT(0) +#define PWRSTS_RET BIT(1) +#define PWRSTS_ON BIT(2) +#define PWRSTS_OFF_ON (PWRSTS_OFF | PWRSTS_ON) +#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON) + +/** + * struct gdsc - Globally Distributed Switch Controller + * @pd: generic power domain + * @regmap: regmap for MMIO accesses + * @gdscr: gsdc control register + * @cxcs: offsets of branch registers to toggle mem/periph bits in + * @cxc_count: number of @cxcs + * @pwrsts: Possible powerdomain power states + * @resets: ids of resets associated with this gdsc + * @reset_count: number of @resets + * @rcdev: reset controller + */ +struct gdsc { + struct generic_pm_domain pd; + struct regmap *regmap; + unsigned int gdscr; + unsigned int *cxcs; + unsigned int cxc_count; + const u8 pwrsts; + struct reset_controller_dev *rcdev; + unsigned int *resets; + unsigned int reset_count; +}; + +#ifdef CONFIG_QCOM_GDSC +int gdsc_register(struct device *, struct gdsc **, size_t n, + struct reset_controller_dev *, struct regmap *); +void gdsc_unregister(struct device *); +#else +static inline int gdsc_register(struct device *d, struct gdsc **g, size_t n, + struct reset_controller_dev *rcdev, + struct regmap *r) +{ + return -ENOSYS; +} + +static inline void gdsc_unregister(struct device *d) {}; +#endif /* CONFIG_QCOM_GDSC */ +#endif /* __QCOM_GDSC_H__ */ diff --git a/drivers/clk/qcom/lcc-ipq806x.c b/drivers/clk/qcom/lcc-ipq806x.c index 93ad42b14366..db3998e5e2d8 100644 --- a/drivers/clk/qcom/lcc-ipq806x.c +++ b/drivers/clk/qcom/lcc-ipq806x.c @@ -452,15 +452,8 @@ static int lcc_ipq806x_probe(struct platform_device *pdev) return qcom_cc_really_probe(pdev, &lcc_ipq806x_desc, regmap); } -static int lcc_ipq806x_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver lcc_ipq806x_driver = { .probe = lcc_ipq806x_probe, - .remove = lcc_ipq806x_remove, .driver = { .name = "lcc-ipq806x", .of_match_table = lcc_ipq806x_match_table, diff --git a/drivers/clk/qcom/lcc-msm8960.c b/drivers/clk/qcom/lcc-msm8960.c index ecb96c284675..4fcf9d1d233c 100644 --- a/drivers/clk/qcom/lcc-msm8960.c +++ b/drivers/clk/qcom/lcc-msm8960.c @@ -565,15 +565,8 @@ static int lcc_msm8960_probe(struct platform_device *pdev) return qcom_cc_really_probe(pdev, &lcc_msm8960_desc, regmap); } -static int lcc_msm8960_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver lcc_msm8960_driver = { .probe = lcc_msm8960_probe, - .remove = lcc_msm8960_remove, .driver = { .name = "lcc-msm8960", .of_match_table = lcc_msm8960_match_table, diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index f0ee6bde11af..30777f9f1a43 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, The Linux Foundation. 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 @@ -26,6 +26,7 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "gdsc.h" enum { P_XO, @@ -571,17 +572,11 @@ static struct clk_rcg2 jpeg2_clk_src = { }, }; -static struct freq_tbl pixel_freq_tbl[] = { - { .src = P_DSI0PLL }, - { } -}; - static struct clk_rcg2 pclk0_clk_src = { .cmd_rcgr = 0x2000, .mnd_width = 8, .hid_width = 5, .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, - .freq_tbl = pixel_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk0_clk_src", .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, @@ -596,7 +591,6 @@ static struct clk_rcg2 pclk1_clk_src = { .mnd_width = 8, .hid_width = 5, .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, - .freq_tbl = pixel_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk1_clk_src", .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, @@ -844,21 +838,15 @@ static struct clk_rcg2 cpp_clk_src = { }, }; -static struct freq_tbl byte_freq_tbl[] = { - { .src = P_DSI0PLL_BYTE }, - { } -}; - static struct clk_rcg2 byte0_clk_src = { .cmd_rcgr = 0x2120, .hid_width = 5, .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, - .freq_tbl = byte_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "byte0_clk_src", .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, .num_parents = 6, - .ops = &clk_byte_ops, + .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, }; @@ -867,12 +855,11 @@ static struct clk_rcg2 byte1_clk_src = { .cmd_rcgr = 0x2140, .hid_width = 5, .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, - .freq_tbl = byte_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "byte1_clk_src", .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, .num_parents = 6, - .ops = &clk_byte_ops, + .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, }; @@ -3077,6 +3064,76 @@ static const struct pll_config mmpll3_config = { .aux_output_mask = BIT(1), }; +static struct gdsc venus0_gdsc = { + .gdscr = 0x1024, + .pd = { + .name = "venus0", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc venus0_core0_gdsc = { + .gdscr = 0x1040, + .pd = { + .name = "venus0_core0", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc venus0_core1_gdsc = { + .gdscr = 0x1044, + .pd = { + .name = "venus0_core1", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x2304, + .cxcs = (unsigned int []){ 0x231c, 0x2320 }, + .cxc_count = 2, + .pd = { + .name = "mdss", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc camss_jpeg_gdsc = { + .gdscr = 0x35a4, + .pd = { + .name = "camss_jpeg", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc camss_vfe_gdsc = { + .gdscr = 0x36a4, + .cxcs = (unsigned int []){ 0x36a8, 0x36ac, 0x36b0 }, + .cxc_count = 3, + .pd = { + .name = "camss_vfe", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc oxili_gdsc = { + .gdscr = 0x4024, + .cxcs = (unsigned int []){ 0x4028 }, + .cxc_count = 1, + .pd = { + .name = "oxili", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc oxilicx_gdsc = { + .gdscr = 0x4034, + .pd = { + .name = "oxilicx", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct clk_regmap *mmcc_apq8084_clocks[] = { [MMSS_AHB_CLK_SRC] = &mmss_ahb_clk_src.clkr, [MMSS_AXI_CLK_SRC] = &mmss_axi_clk_src.clkr, @@ -3294,6 +3351,17 @@ static const struct qcom_reset_map mmcc_apq8084_resets[] = { [MMSSNOCAXI_RESET] = { 0x5060 }, }; +static struct gdsc *mmcc_apq8084_gdscs[] = { + [VENUS0_GDSC] = &venus0_gdsc, + [VENUS0_CORE0_GDSC] = &venus0_core0_gdsc, + [VENUS0_CORE1_GDSC] = &venus0_core1_gdsc, + [MDSS_GDSC] = &mdss_gdsc, + [CAMSS_JPEG_GDSC] = &camss_jpeg_gdsc, + [CAMSS_VFE_GDSC] = &camss_vfe_gdsc, + [OXILI_GDSC] = &oxili_gdsc, + [OXILICX_GDSC] = &oxilicx_gdsc, +}; + static const struct regmap_config mmcc_apq8084_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -3308,6 +3376,8 @@ static const struct qcom_cc_desc mmcc_apq8084_desc = { .num_clks = ARRAY_SIZE(mmcc_apq8084_clocks), .resets = mmcc_apq8084_resets, .num_resets = ARRAY_SIZE(mmcc_apq8084_resets), + .gdscs = mmcc_apq8084_gdscs, + .num_gdscs = ARRAY_SIZE(mmcc_apq8084_gdscs), }; static const struct of_device_id mmcc_apq8084_match_table[] = { @@ -3332,15 +3402,8 @@ static int mmcc_apq8084_probe(struct platform_device *pdev) return 0; } -static int mmcc_apq8084_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver mmcc_apq8084_driver = { .probe = mmcc_apq8084_probe, - .remove = mmcc_apq8084_remove, .driver = { .name = "mmcc-apq8084", .of_match_table = mmcc_apq8084_match_table, diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index bad02aebf959..397f5df6422a 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -41,6 +41,10 @@ enum { P_PLL3, P_PLL15, P_HDMI_PLL, + P_DSI1_PLL_DSICLK, + P_DSI2_PLL_DSICLK, + P_DSI1_PLL_BYTECLK, + P_DSI2_PLL_BYTECLK, }; #define F_MN(f, s, _m, _n) { .freq = f, .src = s, .m = _m, .n = _n } @@ -85,6 +89,30 @@ static const char * const mmcc_pxo_pll8_pll2_pll3[] = { "pll3", }; +static const struct parent_map mmcc_pxo_dsi2_dsi1_map[] = { + { P_PXO, 0 }, + { P_DSI2_PLL_DSICLK, 1 }, + { P_DSI1_PLL_DSICLK, 3 }, +}; + +static const char * const mmcc_pxo_dsi2_dsi1[] = { + "pxo", + "dsi2pll", + "dsi1pll", +}; + +static const struct parent_map mmcc_pxo_dsi1_dsi2_byte_map[] = { + { P_PXO, 0 }, + { P_DSI1_PLL_BYTECLK, 1 }, + { P_DSI2_PLL_BYTECLK, 2 }, +}; + +static const char * const mmcc_pxo_dsi1_dsi2_byte[] = { + "pxo", + "dsi1pllbyte", + "dsi2pllbyte", +}; + static struct clk_pll pll2 = { .l_reg = 0x320, .m_reg = 0x324, @@ -2042,6 +2070,350 @@ static struct clk_branch dsi2_s_ahb_clk = { }, }; +static struct clk_rcg dsi1_src = { + .ns_reg = 0x0054, + .md_reg = 0x0050, + .mn = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 6, + .n_val_shift = 24, + .m_val_shift = 8, + .width = 8, + }, + .p = { + .pre_div_shift = 14, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi2_dsi1_map, + }, + .clkr = { + .enable_reg = 0x004c, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_src", + .parent_names = mmcc_pxo_dsi2_dsi1, + .num_parents = 3, + .ops = &clk_rcg_bypass2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi1_clk = { + .halt_reg = 0x01d0, + .halt_bit = 1, + .clkr = { + .enable_reg = 0x004c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_clk", + .parent_names = (const char *[]){ "dsi1_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi2_src = { + .ns_reg = 0x012c, + .md_reg = 0x00a8, + .mn = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 6, + .n_val_shift = 24, + .m_val_shift = 8, + .width = 8, + }, + .p = { + .pre_div_shift = 14, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi2_dsi1_map, + }, + .clkr = { + .enable_reg = 0x003c, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_src", + .parent_names = mmcc_pxo_dsi2_dsi1, + .num_parents = 3, + .ops = &clk_rcg_bypass2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi2_clk = { + .halt_reg = 0x01d0, + .halt_bit = 2, + .clkr = { + .enable_reg = 0x003c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_clk", + .parent_names = (const char *[]){ "dsi2_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi1_byte_src = { + .ns_reg = 0x00b0, + .p = { + .pre_div_shift = 12, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi1_dsi2_byte_map, + }, + .clkr = { + .enable_reg = 0x0090, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_byte_src", + .parent_names = mmcc_pxo_dsi1_dsi2_byte, + .num_parents = 3, + .ops = &clk_rcg_bypass2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi1_byte_clk = { + .halt_reg = 0x01cc, + .halt_bit = 21, + .clkr = { + .enable_reg = 0x0090, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_byte_clk", + .parent_names = (const char *[]){ "dsi1_byte_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi2_byte_src = { + .ns_reg = 0x012c, + .p = { + .pre_div_shift = 12, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi1_dsi2_byte_map, + }, + .clkr = { + .enable_reg = 0x0130, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_byte_src", + .parent_names = mmcc_pxo_dsi1_dsi2_byte, + .num_parents = 3, + .ops = &clk_rcg_bypass2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch dsi2_byte_clk = { + .halt_reg = 0x01cc, + .halt_bit = 20, + .clkr = { + .enable_reg = 0x00b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_byte_clk", + .parent_names = (const char *[]){ "dsi2_byte_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi1_esc_src = { + .ns_reg = 0x0011c, + .p = { + .pre_div_shift = 12, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi1_dsi2_byte_map, + }, + .clkr = { + .enable_reg = 0x00cc, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_esc_src", + .parent_names = mmcc_pxo_dsi1_dsi2_byte, + .num_parents = 3, + .ops = &clk_rcg_esc_ops, + }, + }, +}; + +static struct clk_branch dsi1_esc_clk = { + .halt_reg = 0x01e8, + .halt_bit = 1, + .clkr = { + .enable_reg = 0x00cc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_esc_clk", + .parent_names = (const char *[]){ "dsi1_esc_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi2_esc_src = { + .ns_reg = 0x0150, + .p = { + .pre_div_shift = 12, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi1_dsi2_byte_map, + }, + .clkr = { + .enable_reg = 0x013c, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_esc_src", + .parent_names = mmcc_pxo_dsi1_dsi2_byte, + .num_parents = 3, + .ops = &clk_rcg_esc_ops, + }, + }, +}; + +static struct clk_branch dsi2_esc_clk = { + .halt_reg = 0x01e8, + .halt_bit = 3, + .clkr = { + .enable_reg = 0x013c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_esc_clk", + .parent_names = (const char *[]){ "dsi2_esc_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi1_pixel_src = { + .ns_reg = 0x0138, + .md_reg = 0x0134, + .mn = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 6, + .n_val_shift = 16, + .m_val_shift = 8, + .width = 8, + }, + .p = { + .pre_div_shift = 12, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi2_dsi1_map, + }, + .clkr = { + .enable_reg = 0x0130, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi1_pixel_src", + .parent_names = mmcc_pxo_dsi2_dsi1, + .num_parents = 3, + .ops = &clk_rcg_pixel_ops, + }, + }, +}; + +static struct clk_branch dsi1_pixel_clk = { + .halt_reg = 0x01d0, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x0130, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdp_pclk1_clk", + .parent_names = (const char *[]){ "dsi1_pixel_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg dsi2_pixel_src = { + .ns_reg = 0x00e4, + .md_reg = 0x00b8, + .mn = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 6, + .n_val_shift = 16, + .m_val_shift = 8, + .width = 8, + }, + .p = { + .pre_div_shift = 12, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_dsi2_dsi1_map, + }, + .clkr = { + .enable_reg = 0x0094, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "dsi2_pixel_src", + .parent_names = mmcc_pxo_dsi2_dsi1, + .num_parents = 3, + .ops = &clk_rcg_pixel_ops, + }, + }, +}; + +static struct clk_branch dsi2_pixel_clk = { + .halt_reg = 0x01d0, + .halt_bit = 19, + .clkr = { + .enable_reg = 0x0094, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdp_pclk2_clk", + .parent_names = (const char *[]){ "dsi2_pixel_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + static struct clk_branch gfx2d0_ahb_clk = { .hwcg_reg = 0x0038, .hwcg_bit = 28, @@ -2325,6 +2697,8 @@ static struct clk_regmap *mmcc_msm8960_clks[] = { [CSI2_SRC] = &csi2_src.clkr, [CSI2_CLK] = &csi2_clk.clkr, [CSI2_PHY_CLK] = &csi2_phy_clk.clkr, + [DSI_SRC] = &dsi1_src.clkr, + [DSI_CLK] = &dsi1_clk.clkr, [CSI_PIX_CLK] = &csi_pix_clk.clkr, [CSI_RDI_CLK] = &csi_rdi_clk.clkr, [MDP_VSYNC_CLK] = &mdp_vsync_clk.clkr, @@ -2345,6 +2719,18 @@ static struct clk_regmap *mmcc_msm8960_clks[] = { [MDP_SRC] = &mdp_src.clkr, [MDP_CLK] = &mdp_clk.clkr, [MDP_LUT_CLK] = &mdp_lut_clk.clkr, + [DSI2_PIXEL_SRC] = &dsi2_pixel_src.clkr, + [DSI2_PIXEL_CLK] = &dsi2_pixel_clk.clkr, + [DSI2_SRC] = &dsi2_src.clkr, + [DSI2_CLK] = &dsi2_clk.clkr, + [DSI1_BYTE_SRC] = &dsi1_byte_src.clkr, + [DSI1_BYTE_CLK] = &dsi1_byte_clk.clkr, + [DSI2_BYTE_SRC] = &dsi2_byte_src.clkr, + [DSI2_BYTE_CLK] = &dsi2_byte_clk.clkr, + [DSI1_ESC_SRC] = &dsi1_esc_src.clkr, + [DSI1_ESC_CLK] = &dsi1_esc_clk.clkr, + [DSI2_ESC_SRC] = &dsi2_esc_src.clkr, + [DSI2_ESC_CLK] = &dsi2_esc_clk.clkr, [ROT_SRC] = &rot_src.clkr, [ROT_CLK] = &rot_clk.clkr, [TV_ENC_CLK] = &tv_enc_clk.clkr, @@ -2359,6 +2745,8 @@ static struct clk_regmap *mmcc_msm8960_clks[] = { [VFE_CSI_CLK] = &vfe_csi_clk.clkr, [VPE_SRC] = &vpe_src.clkr, [VPE_CLK] = &vpe_clk.clkr, + [DSI_PIXEL_SRC] = &dsi1_pixel_src.clkr, + [DSI_PIXEL_CLK] = &dsi1_pixel_clk.clkr, [CAMCLK0_SRC] = &camclk0_src.clkr, [CAMCLK0_CLK] = &camclk0_clk.clkr, [CAMCLK1_SRC] = &camclk1_src.clkr, @@ -2490,6 +2878,8 @@ static struct clk_regmap *mmcc_apq8064_clks[] = { [CSI2_SRC] = &csi2_src.clkr, [CSI2_CLK] = &csi2_clk.clkr, [CSI2_PHY_CLK] = &csi2_phy_clk.clkr, + [DSI_SRC] = &dsi1_src.clkr, + [DSI_CLK] = &dsi1_clk.clkr, [CSI_PIX_CLK] = &csi_pix_clk.clkr, [CSI_RDI_CLK] = &csi_rdi_clk.clkr, [MDP_VSYNC_CLK] = &mdp_vsync_clk.clkr, @@ -2506,6 +2896,18 @@ static struct clk_regmap *mmcc_apq8064_clks[] = { [MDP_SRC] = &mdp_src.clkr, [MDP_CLK] = &mdp_clk.clkr, [MDP_LUT_CLK] = &mdp_lut_clk.clkr, + [DSI2_PIXEL_SRC] = &dsi2_pixel_src.clkr, + [DSI2_PIXEL_CLK] = &dsi2_pixel_clk.clkr, + [DSI2_SRC] = &dsi2_src.clkr, + [DSI2_CLK] = &dsi2_clk.clkr, + [DSI1_BYTE_SRC] = &dsi1_byte_src.clkr, + [DSI1_BYTE_CLK] = &dsi1_byte_clk.clkr, + [DSI2_BYTE_SRC] = &dsi2_byte_src.clkr, + [DSI2_BYTE_CLK] = &dsi2_byte_clk.clkr, + [DSI1_ESC_SRC] = &dsi1_esc_src.clkr, + [DSI1_ESC_CLK] = &dsi1_esc_clk.clkr, + [DSI2_ESC_SRC] = &dsi2_esc_src.clkr, + [DSI2_ESC_CLK] = &dsi2_esc_clk.clkr, [ROT_SRC] = &rot_src.clkr, [ROT_CLK] = &rot_clk.clkr, [TV_DAC_CLK] = &tv_dac_clk.clkr, @@ -2519,6 +2921,8 @@ static struct clk_regmap *mmcc_apq8064_clks[] = { [VFE_CSI_CLK] = &vfe_csi_clk.clkr, [VPE_SRC] = &vpe_src.clkr, [VPE_CLK] = &vpe_clk.clkr, + [DSI_PIXEL_SRC] = &dsi1_pixel_src.clkr, + [DSI_PIXEL_CLK] = &dsi1_pixel_clk.clkr, [CAMCLK0_SRC] = &camclk0_src.clkr, [CAMCLK0_CLK] = &camclk0_clk.clkr, [CAMCLK1_SRC] = &camclk1_src.clkr, @@ -2686,15 +3090,8 @@ static int mmcc_msm8960_probe(struct platform_device *pdev) return qcom_cc_really_probe(pdev, match->data, regmap); } -static int mmcc_msm8960_remove(struct platform_device *pdev) -{ - qcom_cc_remove(pdev); - return 0; -} - static struct platform_driver mmcc_msm8960_driver = { .probe = mmcc_msm8960_probe, - .remove = mmcc_msm8960_remove, .driver = { .name = "mmcc-msm8960", .of_match_table = mmcc_msm8960_match_table, diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index 0987bf443e1f..9d790bcadf25 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -31,6 +31,7 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "gdsc.h" enum { P_XO, @@ -522,17 +523,11 @@ static struct clk_rcg2 jpeg2_clk_src = { }, }; -static struct freq_tbl pixel_freq_tbl[] = { - { .src = P_DSI0PLL }, - { } -}; - static struct clk_rcg2 pclk0_clk_src = { .cmd_rcgr = 0x2000, .mnd_width = 8, .hid_width = 5, .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, - .freq_tbl = pixel_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk0_clk_src", .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, @@ -547,7 +542,6 @@ static struct clk_rcg2 pclk1_clk_src = { .mnd_width = 8, .hid_width = 5, .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, - .freq_tbl = pixel_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk1_clk_src", .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, @@ -785,7 +779,7 @@ static struct clk_rcg2 byte0_clk_src = { .name = "byte0_clk_src", .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, .num_parents = 6, - .ops = &clk_byte_ops, + .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, }; @@ -799,7 +793,7 @@ static struct clk_rcg2 byte1_clk_src = { .name = "byte1_clk_src", .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, .num_parents = 6, - .ops = &clk_byte_ops, + .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, }; @@ -2349,6 +2343,66 @@ static struct pll_config mmpll3_config = { .aux_output_mask = BIT(1), }; +static struct gdsc venus0_gdsc = { + .gdscr = 0x1024, + .cxcs = (unsigned int []){ 0x1028 }, + .cxc_count = 1, + .resets = (unsigned int []){ VENUS0_RESET }, + .reset_count = 1, + .pd = { + .name = "venus0", + }, + .pwrsts = PWRSTS_ON, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x2304, + .cxcs = (unsigned int []){ 0x231c, 0x2320 }, + .cxc_count = 2, + .pd = { + .name = "mdss", + }, + .pwrsts = PWRSTS_RET_ON, +}; + +static struct gdsc camss_jpeg_gdsc = { + .gdscr = 0x35a4, + .cxcs = (unsigned int []){ 0x35a8, 0x35ac, 0x35b0 }, + .cxc_count = 3, + .pd = { + .name = "camss_jpeg", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc camss_vfe_gdsc = { + .gdscr = 0x36a4, + .cxcs = (unsigned int []){ 0x36a8, 0x36ac, 0x3704, 0x3714, 0x36b0 }, + .cxc_count = 5, + .pd = { + .name = "camss_vfe", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc oxili_gdsc = { + .gdscr = 0x4024, + .cxcs = (unsigned int []){ 0x4028 }, + .cxc_count = 1, + .pd = { + .name = "oxili", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc oxilicx_gdsc = { + .gdscr = 0x4034, + .pd = { + .name = "oxilicx", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + static struct clk_regmap *mmcc_msm8974_clocks[] = { [MMSS_AHB_CLK_SRC] = &mmss_ahb_clk_src.clkr, [MMSS_AXI_CLK_SRC] = &mmss_axi_clk_src.clkr, @@ -2525,6 +2579,15 @@ static const struct qcom_reset_map mmcc_msm8974_resets[] = { [OCMEMNOC_RESET] = { 0x50b0 }, }; +static struct gdsc *mmcc_msm8974_gdscs[] = { + [VENUS0_GDSC] = &venus0_gdsc, + [MDSS_GDSC] = &mdss_gdsc, + [CAMSS_JPEG_GDSC] = &camss_jpeg_gdsc, + [CAMSS_VFE_GDSC] = &camss_vfe_gdsc, + [OXILI_GDSC] = &oxili_gdsc, + [OXILICX_GDSC] = &oxilicx_gdsc, +}; + static const struct regmap_config mmcc_msm8974_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2539,6 +2602,8 @@ static const struct qcom_cc_desc mmcc_msm8974_desc = { .num_clks = ARRAY_SIZE(mmcc_msm8974_clocks), .resets = mmcc_msm8974_resets, .num_resets = ARRAY_SIZE(mmcc_msm8974_resets), + .gdscs = mmcc_msm8974_gdscs, + .num_gdscs = ARRAY_SIZE(mmcc_msm8974_gdscs), }; static const struct of_device_id mmcc_msm8974_match_table[] = { @@ -2550,6 +2615,7 @@ MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table); static int mmcc_msm8974_probe(struct platform_device *pdev) { struct regmap *regmap; + int ret; regmap = qcom_cc_map(pdev, &mmcc_msm8974_desc); if (IS_ERR(regmap)) @@ -2558,12 +2624,16 @@ static int mmcc_msm8974_probe(struct platform_device *pdev) clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); - return qcom_cc_really_probe(pdev, &mmcc_msm8974_desc, regmap); + ret = qcom_cc_really_probe(pdev, &mmcc_msm8974_desc, regmap); + if (ret) + return ret; + + return pm_genpd_add_subdomain(&oxili_gdsc.pd, &oxilicx_gdsc.pd); } static int mmcc_msm8974_remove(struct platform_device *pdev) { - qcom_cc_remove(pdev); + pm_genpd_remove_subdomain(&oxili_gdsc.pd, &oxilicx_gdsc.pd); return 0; } diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index 9b613426e968..1c8162e5c1ce 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -41,6 +41,8 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw, #define ROCKCHIP_MMC_DEGREE_MASK 0x3 #define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 #define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) +#define ROCKCHIP_MMC_INIT_STATE_RESET 0x1 +#define ROCKCHIP_MMC_INIT_STATE_SHIFT 1 #define PSECS_PER_SEC 1000000000000LL @@ -143,6 +145,15 @@ struct clk *rockchip_clk_register_mmc(const char *name, mmc_clock->reg = reg; mmc_clock->shift = shift; + /* + * Assert init_state to soft reset the CLKGEN + * for mmc tuning phase and degree + */ + if (mmc_clock->shift == ROCKCHIP_MMC_INIT_STATE_SHIFT) + writel(HIWORD_UPDATE(ROCKCHIP_MMC_INIT_STATE_RESET, + ROCKCHIP_MMC_INIT_STATE_RESET, + mmc_clock->shift), mmc_clock->reg); + clk = clk_register(NULL, &mmc_clock->hw); if (IS_ERR(clk)) goto err_free; diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 7737a1df1e4b..4881eb8a1576 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -126,11 +126,32 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) #define RK3066_PLLCON3_PWRDOWN (1 << 1) #define RK3066_PLLCON3_BYPASS (1 << 0) +static void rockchip_rk3066_pll_get_params(struct rockchip_clk_pll *pll, + struct rockchip_pll_rate_table *rate) +{ + u32 pllcon; + + pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); + rate->nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT) + & RK3066_PLLCON0_NR_MASK) + 1; + rate->no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT) + & RK3066_PLLCON0_OD_MASK) + 1; + + pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); + rate->nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT) + & RK3066_PLLCON1_NF_MASK) + 1; + + pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2)); + rate->nb = ((pllcon >> RK3066_PLLCON2_NB_SHIFT) + & RK3066_PLLCON2_NB_MASK) + 1; +} + static unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw, unsigned long prate) { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); - u64 nf, nr, no, rate64 = prate; + struct rockchip_pll_rate_table cur; + u64 rate64 = prate; u32 pllcon; pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(3)); @@ -140,53 +161,31 @@ static unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw, return prate; } - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); - nf = (pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK; - - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); - nr = (pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK; - no = (pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK; + rockchip_rk3066_pll_get_params(pll, &cur); - rate64 *= (nf + 1); - do_div(rate64, nr + 1); - do_div(rate64, no + 1); + rate64 *= cur.nf; + do_div(rate64, cur.nr); + do_div(rate64, cur.no); return (unsigned long)rate64; } -static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) +static int rockchip_rk3066_pll_set_params(struct rockchip_clk_pll *pll, + const struct rockchip_pll_rate_table *rate) { - struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); - const struct rockchip_pll_rate_table *rate; - unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); - struct regmap *grf = rockchip_clk_get_grf(); - struct clk_mux *pll_mux = &pll->pll_mux; const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; + struct clk_mux *pll_mux = &pll->pll_mux; + struct rockchip_pll_rate_table cur; int rate_change_remuxed = 0; int cur_parent; int ret; - if (IS_ERR(grf)) { - pr_debug("%s: grf regmap not available, aborting rate change\n", - __func__); - return PTR_ERR(grf); - } - - pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", - __func__, clk_hw_get_name(hw), old_rate, drate, prate); - - /* Get required rate settings from table */ - rate = rockchip_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; - } - pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", __func__, rate->rate, rate->nr, rate->no, rate->nf); + rockchip_rk3066_pll_get_params(pll, &cur); + cur.rate = 0; + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); if (cur_parent == PLL_MODE_NORM) { pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); @@ -219,9 +218,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, /* wait for the pll to lock */ ret = rockchip_pll_wait_lock(pll); if (ret) { - pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", - __func__, old_rate); - rockchip_rk3066_pll_set_rate(hw, old_rate, prate); + pr_warn("%s: pll update unsucessful, trying to restore old params\n", + __func__); + rockchip_rk3066_pll_set_params(pll, &cur); } if (rate_change_remuxed) @@ -230,6 +229,34 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, return ret; } +static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + const struct rockchip_pll_rate_table *rate; + unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); + struct regmap *grf = rockchip_clk_get_grf(); + + if (IS_ERR(grf)) { + pr_debug("%s: grf regmap not available, aborting rate change\n", + __func__); + return PTR_ERR(grf); + } + + pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", + __func__, clk_hw_get_name(hw), old_rate, drate, prate); + + /* Get required rate settings from table */ + rate = rockchip_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; + } + + return rockchip_rk3066_pll_set_params(pll, rate); +} + static int rockchip_rk3066_pll_enable(struct clk_hw *hw) { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); @@ -261,9 +288,8 @@ static void rockchip_rk3066_pll_init(struct clk_hw *hw) { struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); const struct rockchip_pll_rate_table *rate; - unsigned int nf, nr, no, nb; + struct rockchip_pll_rate_table cur; unsigned long drate; - u32 pllcon; if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE)) return; @@ -275,34 +301,21 @@ static void rockchip_rk3066_pll_init(struct clk_hw *hw) if (!rate) return; - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); - nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK) + 1; - no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK) + 1; - - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); - nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK) + 1; - - pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2)); - nb = ((pllcon >> RK3066_PLLCON2_NB_SHIFT) & RK3066_PLLCON2_NB_MASK) + 1; + rockchip_rk3066_pll_get_params(pll, &cur); pr_debug("%s: pll %s@%lu: nr (%d:%d); no (%d:%d); nf(%d:%d), nb(%d:%d)\n", - __func__, clk_hw_get_name(hw), drate, rate->nr, nr, - rate->no, no, rate->nf, nf, rate->nb, nb); - if (rate->nr != nr || rate->no != no || rate->nf != nf - || rate->nb != nb) { - struct clk_hw *parent = clk_hw_get_parent(hw); - unsigned long prate; - - if (!parent) { - pr_warn("%s: parent of %s not available\n", - __func__, clk_hw_get_name(hw)); + __func__, clk_hw_get_name(hw), drate, rate->nr, cur.nr, + rate->no, cur.no, rate->nf, cur.nf, rate->nb, cur.nb); + if (rate->nr != cur.nr || rate->no != cur.no || rate->nf != cur.nf + || rate->nb != cur.nb) { + struct regmap *grf = rockchip_clk_get_grf(); + + if (IS_ERR(grf)) return; - } pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n", __func__, clk_hw_get_name(hw)); - prate = clk_hw_get_rate(parent); - rockchip_rk3066_pll_set_rate(hw, drate, prate); + rockchip_rk3066_pll_set_params(pll, rate); } } diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index ed02bbc7b11f..abb47608713b 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -716,6 +716,8 @@ static const char *const rk3188_critical_clocks[] __initconst = { "aclk_cpu", "aclk_peri", "hclk_peri", + "pclk_cpu", + "pclk_peri", }; static void __init rk3188_common_clk_init(struct device_node *np) @@ -744,8 +746,6 @@ static void __init rk3188_common_clk_init(struct device_node *np) rockchip_clk_register_branches(common_clk_branches, ARRAY_SIZE(common_clk_branches)); - rockchip_clk_protect_critical(rk3188_critical_clocks, - ARRAY_SIZE(rk3188_critical_clocks)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); @@ -765,6 +765,8 @@ static void __init rk3066a_clk_init(struct device_node *np) mux_armclk_p, ARRAY_SIZE(mux_armclk_p), &rk3066_cpuclk_data, rk3066_cpuclk_rates, ARRAY_SIZE(rk3066_cpuclk_rates)); + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); } CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); @@ -801,6 +803,9 @@ static void __init rk3188a_clk_init(struct device_node *np) pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n", __func__); } + + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); } CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3368.c b/drivers/clk/rockchip/clk-rk3368.c index 9c5d61e698ef..7e6b783e6eee 100644 --- a/drivers/clk/rockchip/clk-rk3368.c +++ b/drivers/clk/rockchip/clk-rk3368.c @@ -818,6 +818,10 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { GATE(0, "sclk_timer00", "xin24m", CLK_IGNORE_UNUSED, RK3368_CLKGATE_CON(24), 0, GFLAGS), }; +static const char *const rk3368_critical_clocks[] __initconst = { + "pclk_pd_pmu", +}; + static void __init rk3368_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -862,6 +866,8 @@ static void __init rk3368_clk_init(struct device_node *np) RK3368_GRF_SOC_STATUS0); rockchip_clk_register_branches(rk3368_clk_branches, ARRAY_SIZE(rk3368_clk_branches)); + rockchip_clk_protect_critical(rk3368_critical_clocks, + ARRAY_SIZE(rk3368_critical_clocks)); rockchip_clk_register_armclk(ARMCLKB, "armclkb", mux_armclkb_p, ARRAY_SIZE(mux_armclkb_p), diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 24938815655f..be6c7fd8315d 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -135,9 +135,11 @@ static struct clk *rockchip_clk_register_frac_branch(const char *name, div->flags = div_flags; div->reg = base + muxdiv_offset; div->mshift = 16; - div->mmask = 0xffff0000; + div->mwidth = 16; + div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift; div->nshift = 0; - div->nmask = 0xffff; + div->nwidth = 16; + div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; div->lock = lock; div_ops = &clk_fractional_divider_ops; diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c index 7c1e1f58e2da..2fe37f708dc7 100644 --- a/drivers/clk/samsung/clk-cpu.c +++ b/drivers/clk/samsung/clk-cpu.c @@ -164,7 +164,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, * the values for DIV_COPY and DIV_HPM dividers need not be set. */ div0 = cfg_data->div0; - if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { + if (cpuclk->flags & CLK_CPU_HAS_DIV1) { div1 = cfg_data->div1; if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK) div1 = readl(base + E4210_DIV_CPU1) & @@ -185,7 +185,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; WARN_ON(alt_div >= MAX_DIV); - if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { /* * In Exynos4210, ATB clock parent is also mout_core. So * ATB clock also needs to be mantained at safe speed. @@ -206,7 +206,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, writel(div0, base + E4210_DIV_CPU0); wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL); - if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { + if (cpuclk->flags & CLK_CPU_HAS_DIV1) { writel(div1, base + E4210_DIV_CPU1); wait_until_divider_stable(base + E4210_DIV_STAT_CPU1, DIV_MASK_ALL); @@ -225,7 +225,7 @@ static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, unsigned long mux_reg; /* find out the divider values to use for clock data */ - if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { while ((cfg_data->prate * 1000) != ndata->new_rate) { if (cfg_data->prate == 0) return -EINVAL; @@ -240,7 +240,7 @@ static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU); wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1); - if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); div_mask |= E4210_DIV0_ATB_MASK; } diff --git a/drivers/clk/samsung/clk-exynos7.c b/drivers/clk/samsung/clk-exynos7.c index 8524e667097e..55f8e2e24ab8 100644 --- a/drivers/clk/samsung/clk-exynos7.c +++ b/drivers/clk/samsung/clk-exynos7.c @@ -32,39 +32,41 @@ #define DIV_TOPC0 0x0600 #define DIV_TOPC1 0x0604 #define DIV_TOPC3 0x060C +#define ENABLE_ACLK_TOPC0 0x0800 #define ENABLE_ACLK_TOPC1 0x0804 +#define ENABLE_SCLK_TOPC1 0x0A04 static struct samsung_fixed_factor_clock topc_fixed_factor_clks[] __initdata = { - FFACTOR(0, "ffac_topc_bus0_pll_div2", "mout_bus0_pll_ctrl", 1, 2, 0), + FFACTOR(0, "ffac_topc_bus0_pll_div2", "mout_topc_bus0_pll", 1, 2, 0), FFACTOR(0, "ffac_topc_bus0_pll_div4", "ffac_topc_bus0_pll_div2", 1, 2, 0), - FFACTOR(0, "ffac_topc_bus1_pll_div2", "mout_bus1_pll_ctrl", 1, 2, 0), - FFACTOR(0, "ffac_topc_cc_pll_div2", "mout_cc_pll_ctrl", 1, 2, 0), - FFACTOR(0, "ffac_topc_mfc_pll_div2", "mout_mfc_pll_ctrl", 1, 2, 0), + FFACTOR(0, "ffac_topc_bus1_pll_div2", "mout_topc_bus1_pll", 1, 2, 0), + FFACTOR(0, "ffac_topc_cc_pll_div2", "mout_topc_cc_pll", 1, 2, 0), + FFACTOR(0, "ffac_topc_mfc_pll_div2", "mout_topc_mfc_pll", 1, 2, 0), }; /* List of parent clocks for Muxes in CMU_TOPC */ -PNAME(mout_aud_pll_ctrl_p) = { "fin_pll", "fout_aud_pll" }; -PNAME(mout_bus0_pll_ctrl_p) = { "fin_pll", "fout_bus0_pll" }; -PNAME(mout_bus1_pll_ctrl_p) = { "fin_pll", "fout_bus1_pll" }; -PNAME(mout_cc_pll_ctrl_p) = { "fin_pll", "fout_cc_pll" }; -PNAME(mout_mfc_pll_ctrl_p) = { "fin_pll", "fout_mfc_pll" }; +PNAME(mout_topc_aud_pll_ctrl_p) = { "fin_pll", "fout_aud_pll" }; +PNAME(mout_topc_bus0_pll_ctrl_p) = { "fin_pll", "fout_bus0_pll" }; +PNAME(mout_topc_bus1_pll_ctrl_p) = { "fin_pll", "fout_bus1_pll" }; +PNAME(mout_topc_cc_pll_ctrl_p) = { "fin_pll", "fout_cc_pll" }; +PNAME(mout_topc_mfc_pll_ctrl_p) = { "fin_pll", "fout_mfc_pll" }; -PNAME(mout_topc_group2) = { "mout_sclk_bus0_pll_cmuc", - "mout_sclk_bus1_pll_cmuc", "mout_sclk_cc_pll_cmuc", - "mout_sclk_mfc_pll_cmuc" }; +PNAME(mout_topc_group2) = { "mout_topc_bus0_pll_half", + "mout_topc_bus1_pll_half", "mout_topc_cc_pll_half", + "mout_topc_mfc_pll_half" }; -PNAME(mout_sclk_bus0_pll_cmuc_p) = { "mout_bus0_pll_ctrl", +PNAME(mout_topc_bus0_pll_half_p) = { "mout_topc_bus0_pll", "ffac_topc_bus0_pll_div2", "ffac_topc_bus0_pll_div4"}; -PNAME(mout_sclk_bus1_pll_cmuc_p) = { "mout_bus1_pll_ctrl", +PNAME(mout_topc_bus1_pll_half_p) = { "mout_topc_bus1_pll", "ffac_topc_bus1_pll_div2"}; -PNAME(mout_sclk_cc_pll_cmuc_p) = { "mout_cc_pll_ctrl", +PNAME(mout_topc_cc_pll_half_p) = { "mout_topc_cc_pll", "ffac_topc_cc_pll_div2"}; -PNAME(mout_sclk_mfc_pll_cmuc_p) = { "mout_mfc_pll_ctrl", +PNAME(mout_topc_mfc_pll_half_p) = { "mout_topc_mfc_pll", "ffac_topc_mfc_pll_div2"}; -PNAME(mout_sclk_bus0_pll_out_p) = {"mout_bus0_pll_ctrl", +PNAME(mout_topc_bus0_pll_out_p) = {"mout_topc_bus0_pll", "ffac_topc_bus0_pll_div2"}; static unsigned long topc_clk_regs[] __initdata = { @@ -88,23 +90,27 @@ static unsigned long topc_clk_regs[] __initdata = { }; static struct samsung_mux_clock topc_mux_clks[] __initdata = { - MUX(0, "mout_bus0_pll_ctrl", mout_bus0_pll_ctrl_p, MUX_SEL_TOPC0, 0, 1), - MUX(0, "mout_bus1_pll_ctrl", mout_bus1_pll_ctrl_p, MUX_SEL_TOPC0, 4, 1), - MUX(0, "mout_cc_pll_ctrl", mout_cc_pll_ctrl_p, MUX_SEL_TOPC0, 8, 1), - MUX(0, "mout_mfc_pll_ctrl", mout_mfc_pll_ctrl_p, MUX_SEL_TOPC0, 12, 1), - - MUX(0, "mout_sclk_bus0_pll_cmuc", mout_sclk_bus0_pll_cmuc_p, + MUX(0, "mout_topc_bus0_pll", mout_topc_bus0_pll_ctrl_p, + MUX_SEL_TOPC0, 0, 1), + MUX(0, "mout_topc_bus1_pll", mout_topc_bus1_pll_ctrl_p, + MUX_SEL_TOPC0, 4, 1), + MUX(0, "mout_topc_cc_pll", mout_topc_cc_pll_ctrl_p, + MUX_SEL_TOPC0, 8, 1), + MUX(0, "mout_topc_mfc_pll", mout_topc_mfc_pll_ctrl_p, + MUX_SEL_TOPC0, 12, 1), + MUX(0, "mout_topc_bus0_pll_half", mout_topc_bus0_pll_half_p, MUX_SEL_TOPC0, 16, 2), - MUX(0, "mout_sclk_bus1_pll_cmuc", mout_sclk_bus1_pll_cmuc_p, + MUX(0, "mout_topc_bus1_pll_half", mout_topc_bus1_pll_half_p, MUX_SEL_TOPC0, 20, 1), - MUX(0, "mout_sclk_cc_pll_cmuc", mout_sclk_cc_pll_cmuc_p, + MUX(0, "mout_topc_cc_pll_half", mout_topc_cc_pll_half_p, MUX_SEL_TOPC0, 24, 1), - MUX(0, "mout_sclk_mfc_pll_cmuc", mout_sclk_mfc_pll_cmuc_p, + MUX(0, "mout_topc_mfc_pll_half", mout_topc_mfc_pll_half_p, MUX_SEL_TOPC0, 28, 1), - MUX(0, "mout_sclk_bus0_pll_out", mout_sclk_bus0_pll_out_p, + MUX(0, "mout_topc_aud_pll", mout_topc_aud_pll_ctrl_p, + MUX_SEL_TOPC1, 0, 1), + MUX(0, "mout_topc_bus0_pll_out", mout_topc_bus0_pll_out_p, MUX_SEL_TOPC1, 16, 1), - MUX(0, "mout_aud_pll_ctrl", mout_aud_pll_ctrl_p, MUX_SEL_TOPC1, 0, 1), MUX(0, "mout_aclk_ccore_133", mout_topc_group2, MUX_SEL_TOPC2, 4, 2), @@ -121,16 +127,16 @@ static struct samsung_div_clock topc_div_clks[] __initdata = { DIV(DOUT_ACLK_PERIS, "dout_aclk_peris_66", "mout_aclk_peris_66", DIV_TOPC1, 24, 4), - DIV(DOUT_SCLK_BUS0_PLL, "dout_sclk_bus0_pll", "mout_sclk_bus0_pll_out", - DIV_TOPC3, 0, 3), - DIV(DOUT_SCLK_BUS1_PLL, "dout_sclk_bus1_pll", "mout_bus1_pll_ctrl", - DIV_TOPC3, 8, 3), - DIV(DOUT_SCLK_CC_PLL, "dout_sclk_cc_pll", "mout_cc_pll_ctrl", - DIV_TOPC3, 12, 3), - DIV(DOUT_SCLK_MFC_PLL, "dout_sclk_mfc_pll", "mout_mfc_pll_ctrl", - DIV_TOPC3, 16, 3), - DIV(DOUT_SCLK_AUD_PLL, "dout_sclk_aud_pll", "mout_aud_pll_ctrl", - DIV_TOPC3, 28, 3), + DIV(DOUT_SCLK_BUS0_PLL, "dout_sclk_bus0_pll", "mout_topc_bus0_pll_out", + DIV_TOPC3, 0, 4), + DIV(DOUT_SCLK_BUS1_PLL, "dout_sclk_bus1_pll", "mout_topc_bus1_pll", + DIV_TOPC3, 8, 4), + DIV(DOUT_SCLK_CC_PLL, "dout_sclk_cc_pll", "mout_topc_cc_pll", + DIV_TOPC3, 12, 4), + DIV(DOUT_SCLK_MFC_PLL, "dout_sclk_mfc_pll", "mout_topc_mfc_pll", + DIV_TOPC3, 16, 4), + DIV(DOUT_SCLK_AUD_PLL, "dout_sclk_aud_pll", "mout_topc_aud_pll", + DIV_TOPC3, 28, 4), }; static struct samsung_pll_rate_table pll1460x_24mhz_tbl[] __initdata = { @@ -139,8 +145,33 @@ static struct samsung_pll_rate_table pll1460x_24mhz_tbl[] __initdata = { }; static struct samsung_gate_clock topc_gate_clks[] __initdata = { + GATE(ACLK_CCORE_133, "aclk_ccore_133", "dout_aclk_ccore_133", + ENABLE_ACLK_TOPC0, 4, 0, 0), + GATE(ACLK_MSCL_532, "aclk_mscl_532", "dout_aclk_mscl_532", ENABLE_ACLK_TOPC1, 20, 0, 0), + + GATE(ACLK_PERIS_66, "aclk_peris_66", "dout_aclk_peris_66", + ENABLE_ACLK_TOPC1, 24, 0, 0), + + GATE(SCLK_AUD_PLL, "sclk_aud_pll", "dout_sclk_aud_pll", + ENABLE_SCLK_TOPC1, 20, 0, 0), + GATE(SCLK_MFC_PLL_B, "sclk_mfc_pll_b", "dout_sclk_mfc_pll", + ENABLE_SCLK_TOPC1, 17, 0, 0), + GATE(SCLK_MFC_PLL_A, "sclk_mfc_pll_a", "dout_sclk_mfc_pll", + ENABLE_SCLK_TOPC1, 16, 0, 0), + GATE(SCLK_BUS1_PLL_B, "sclk_bus1_pll_b", "dout_sclk_bus1_pll", + ENABLE_SCLK_TOPC1, 13, 0, 0), + GATE(SCLK_BUS1_PLL_A, "sclk_bus1_pll_a", "dout_sclk_bus1_pll", + ENABLE_SCLK_TOPC1, 12, 0, 0), + GATE(SCLK_BUS0_PLL_B, "sclk_bus0_pll_b", "dout_sclk_bus0_pll", + ENABLE_SCLK_TOPC1, 5, 0, 0), + GATE(SCLK_BUS0_PLL_A, "sclk_bus0_pll_a", "dout_sclk_bus0_pll", + ENABLE_SCLK_TOPC1, 4, 0, 0), + GATE(SCLK_CC_PLL_B, "sclk_cc_pll_b", "dout_sclk_cc_pll", + ENABLE_SCLK_TOPC1, 1, 0, 0), + GATE(SCLK_CC_PLL_A, "sclk_cc_pll_a", "dout_sclk_cc_pll", + ENABLE_SCLK_TOPC1, 0, 0, 0), }; static struct samsung_pll_clock topc_pll_clks[] __initdata = { @@ -193,36 +224,37 @@ CLK_OF_DECLARE(exynos7_clk_topc, "samsung,exynos7-clock-topc", #define DIV_TOP0_PERIC1 0x0634 #define DIV_TOP0_PERIC2 0x0638 #define DIV_TOP0_PERIC3 0x063C +#define ENABLE_ACLK_TOP03 0x080C #define ENABLE_SCLK_TOP0_PERIC0 0x0A30 #define ENABLE_SCLK_TOP0_PERIC1 0x0A34 #define ENABLE_SCLK_TOP0_PERIC2 0x0A38 #define ENABLE_SCLK_TOP0_PERIC3 0x0A3C /* List of parent clocks for Muxes in CMU_TOP0 */ -PNAME(mout_bus0_pll_p) = { "fin_pll", "dout_sclk_bus0_pll" }; -PNAME(mout_bus1_pll_p) = { "fin_pll", "dout_sclk_bus1_pll" }; -PNAME(mout_cc_pll_p) = { "fin_pll", "dout_sclk_cc_pll" }; -PNAME(mout_mfc_pll_p) = { "fin_pll", "dout_sclk_mfc_pll" }; -PNAME(mout_aud_pll_p) = { "fin_pll", "dout_sclk_aud_pll" }; +PNAME(mout_top0_bus0_pll_user_p) = { "fin_pll", "sclk_bus0_pll_a" }; +PNAME(mout_top0_bus1_pll_user_p) = { "fin_pll", "sclk_bus1_pll_a" }; +PNAME(mout_top0_cc_pll_user_p) = { "fin_pll", "sclk_cc_pll_a" }; +PNAME(mout_top0_mfc_pll_user_p) = { "fin_pll", "sclk_mfc_pll_a" }; +PNAME(mout_top0_aud_pll_user_p) = { "fin_pll", "sclk_aud_pll" }; -PNAME(mout_top0_half_bus0_pll_p) = {"mout_top0_bus0_pll", +PNAME(mout_top0_bus0_pll_half_p) = {"mout_top0_bus0_pll_user", "ffac_top0_bus0_pll_div2"}; -PNAME(mout_top0_half_bus1_pll_p) = {"mout_top0_bus1_pll", +PNAME(mout_top0_bus1_pll_half_p) = {"mout_top0_bus1_pll_user", "ffac_top0_bus1_pll_div2"}; -PNAME(mout_top0_half_cc_pll_p) = {"mout_top0_cc_pll", +PNAME(mout_top0_cc_pll_half_p) = {"mout_top0_cc_pll_user", "ffac_top0_cc_pll_div2"}; -PNAME(mout_top0_half_mfc_pll_p) = {"mout_top0_mfc_pll", +PNAME(mout_top0_mfc_pll_half_p) = {"mout_top0_mfc_pll_user", "ffac_top0_mfc_pll_div2"}; -PNAME(mout_top0_group1) = {"mout_top0_half_bus0_pll", - "mout_top0_half_bus1_pll", "mout_top0_half_cc_pll", - "mout_top0_half_mfc_pll"}; +PNAME(mout_top0_group1) = {"mout_top0_bus0_pll_half", + "mout_top0_bus1_pll_half", "mout_top0_cc_pll_half", + "mout_top0_mfc_pll_half"}; PNAME(mout_top0_group3) = {"ioclk_audiocdclk0", "ioclk_audiocdclk1", "ioclk_spdif_extclk", - "mout_top0_aud_pll", "mout_top0_half_bus0_pll", - "mout_top0_half_bus1_pll"}; -PNAME(mout_top0_group4) = {"ioclk_audiocdclk1", "mout_top0_aud_pll", - "mout_top0_half_bus0_pll", "mout_top0_half_bus1_pll"}; + "mout_top0_aud_pll_user", "mout_top0_bus0_pll_half", + "mout_top0_bus1_pll_half"}; +PNAME(mout_top0_group4) = {"ioclk_audiocdclk1", "mout_top0_aud_pll_user", + "mout_top0_bus0_pll_half", "mout_top0_bus1_pll_half"}; static unsigned long top0_clk_regs[] __initdata = { MUX_SEL_TOP00, @@ -244,19 +276,24 @@ static unsigned long top0_clk_regs[] __initdata = { }; static struct samsung_mux_clock top0_mux_clks[] __initdata = { - MUX(0, "mout_top0_aud_pll", mout_aud_pll_p, MUX_SEL_TOP00, 0, 1), - MUX(0, "mout_top0_mfc_pll", mout_mfc_pll_p, MUX_SEL_TOP00, 4, 1), - MUX(0, "mout_top0_cc_pll", mout_cc_pll_p, MUX_SEL_TOP00, 8, 1), - MUX(0, "mout_top0_bus1_pll", mout_bus1_pll_p, MUX_SEL_TOP00, 12, 1), - MUX(0, "mout_top0_bus0_pll", mout_bus0_pll_p, MUX_SEL_TOP00, 16, 1), - - MUX(0, "mout_top0_half_mfc_pll", mout_top0_half_mfc_pll_p, + MUX(0, "mout_top0_aud_pll_user", mout_top0_aud_pll_user_p, + MUX_SEL_TOP00, 0, 1), + MUX(0, "mout_top0_mfc_pll_user", mout_top0_mfc_pll_user_p, + MUX_SEL_TOP00, 4, 1), + MUX(0, "mout_top0_cc_pll_user", mout_top0_cc_pll_user_p, + MUX_SEL_TOP00, 8, 1), + MUX(0, "mout_top0_bus1_pll_user", mout_top0_bus1_pll_user_p, + MUX_SEL_TOP00, 12, 1), + MUX(0, "mout_top0_bus0_pll_user", mout_top0_bus0_pll_user_p, + MUX_SEL_TOP00, 16, 1), + + MUX(0, "mout_top0_mfc_pll_half", mout_top0_mfc_pll_half_p, MUX_SEL_TOP01, 4, 1), - MUX(0, "mout_top0_half_cc_pll", mout_top0_half_cc_pll_p, + MUX(0, "mout_top0_cc_pll_half", mout_top0_cc_pll_half_p, MUX_SEL_TOP01, 8, 1), - MUX(0, "mout_top0_half_bus1_pll", mout_top0_half_bus1_pll_p, + MUX(0, "mout_top0_bus1_pll_half", mout_top0_bus1_pll_half_p, MUX_SEL_TOP01, 12, 1), - MUX(0, "mout_top0_half_bus0_pll", mout_top0_half_bus0_pll_p, + MUX(0, "mout_top0_bus0_pll_half", mout_top0_bus0_pll_half_p, MUX_SEL_TOP01, 16, 1), MUX(0, "mout_aclk_peric1_66", mout_top0_group1, MUX_SEL_TOP03, 12, 2), @@ -302,6 +339,11 @@ static struct samsung_div_clock top0_div_clks[] __initdata = { }; static struct samsung_gate_clock top0_gate_clks[] __initdata = { + GATE(CLK_ACLK_PERIC0_66, "aclk_peric0_66", "dout_aclk_peric0_66", + ENABLE_ACLK_TOP03, 20, CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_PERIC1_66, "aclk_peric1_66", "dout_aclk_peric1_66", + ENABLE_ACLK_TOP03, 12, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_SPDIF, "sclk_spdif", "dout_sclk_spdif", ENABLE_SCLK_TOP0_PERIC0, 4, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_PCM1, "sclk_pcm1", "dout_sclk_pcm1", @@ -331,10 +373,12 @@ static struct samsung_gate_clock top0_gate_clks[] __initdata = { }; static struct samsung_fixed_factor_clock top0_fixed_factor_clks[] __initdata = { - FFACTOR(0, "ffac_top0_bus0_pll_div2", "mout_top0_bus0_pll", 1, 2, 0), - FFACTOR(0, "ffac_top0_bus1_pll_div2", "mout_top0_bus1_pll", 1, 2, 0), - FFACTOR(0, "ffac_top0_cc_pll_div2", "mout_top0_cc_pll", 1, 2, 0), - FFACTOR(0, "ffac_top0_mfc_pll_div2", "mout_top0_mfc_pll", 1, 2, 0), + FFACTOR(0, "ffac_top0_bus0_pll_div2", "mout_top0_bus0_pll_user", + 1, 2, 0), + FFACTOR(0, "ffac_top0_bus1_pll_div2", "mout_top0_bus1_pll_user", + 1, 2, 0), + FFACTOR(0, "ffac_top0_cc_pll_div2", "mout_top0_cc_pll_user", 1, 2, 0), + FFACTOR(0, "ffac_top0_mfc_pll_div2", "mout_top0_mfc_pll_user", 1, 2, 0), }; static struct samsung_cmu_info top0_cmu_info __initdata = { @@ -365,31 +409,34 @@ CLK_OF_DECLARE(exynos7_clk_top0, "samsung,exynos7-clock-top0", #define MUX_SEL_TOP13 0x020C #define MUX_SEL_TOP1_FSYS0 0x0224 #define MUX_SEL_TOP1_FSYS1 0x0228 +#define MUX_SEL_TOP1_FSYS11 0x022C #define DIV_TOP13 0x060C #define DIV_TOP1_FSYS0 0x0624 #define DIV_TOP1_FSYS1 0x0628 +#define DIV_TOP1_FSYS11 0x062C #define ENABLE_ACLK_TOP13 0x080C #define ENABLE_SCLK_TOP1_FSYS0 0x0A24 #define ENABLE_SCLK_TOP1_FSYS1 0x0A28 +#define ENABLE_SCLK_TOP1_FSYS11 0x0A2C /* List of parent clocks for Muxes in CMU_TOP1 */ -PNAME(mout_top1_bus0_pll_p) = { "fin_pll", "dout_sclk_bus0_pll" }; -PNAME(mout_top1_bus1_pll_p) = { "fin_pll", "dout_sclk_bus1_pll_b" }; -PNAME(mout_top1_cc_pll_p) = { "fin_pll", "dout_sclk_cc_pll_b" }; -PNAME(mout_top1_mfc_pll_p) = { "fin_pll", "dout_sclk_mfc_pll_b" }; +PNAME(mout_top1_bus0_pll_user_p) = { "fin_pll", "sclk_bus0_pll_b" }; +PNAME(mout_top1_bus1_pll_user_p) = { "fin_pll", "sclk_bus1_pll_b" }; +PNAME(mout_top1_cc_pll_user_p) = { "fin_pll", "sclk_cc_pll_b" }; +PNAME(mout_top1_mfc_pll_user_p) = { "fin_pll", "sclk_mfc_pll_b" }; -PNAME(mout_top1_half_bus0_pll_p) = {"mout_top1_bus0_pll", +PNAME(mout_top1_bus0_pll_half_p) = {"mout_top1_bus0_pll_user", "ffac_top1_bus0_pll_div2"}; -PNAME(mout_top1_half_bus1_pll_p) = {"mout_top1_bus1_pll", +PNAME(mout_top1_bus1_pll_half_p) = {"mout_top1_bus1_pll_user", "ffac_top1_bus1_pll_div2"}; -PNAME(mout_top1_half_cc_pll_p) = {"mout_top1_cc_pll", +PNAME(mout_top1_cc_pll_half_p) = {"mout_top1_cc_pll_user", "ffac_top1_cc_pll_div2"}; -PNAME(mout_top1_half_mfc_pll_p) = {"mout_top1_mfc_pll", +PNAME(mout_top1_mfc_pll_half_p) = {"mout_top1_mfc_pll_user", "ffac_top1_mfc_pll_div2"}; -PNAME(mout_top1_group1) = {"mout_top1_half_bus0_pll", - "mout_top1_half_bus1_pll", "mout_top1_half_cc_pll", - "mout_top1_half_mfc_pll"}; +PNAME(mout_top1_group1) = {"mout_top1_bus0_pll_half", + "mout_top1_bus1_pll_half", "mout_top1_cc_pll_half", + "mout_top1_mfc_pll_half"}; static unsigned long top1_clk_regs[] __initdata = { MUX_SEL_TOP10, @@ -397,40 +444,54 @@ static unsigned long top1_clk_regs[] __initdata = { MUX_SEL_TOP13, MUX_SEL_TOP1_FSYS0, MUX_SEL_TOP1_FSYS1, + MUX_SEL_TOP1_FSYS11, DIV_TOP13, DIV_TOP1_FSYS0, DIV_TOP1_FSYS1, + DIV_TOP1_FSYS11, ENABLE_ACLK_TOP13, ENABLE_SCLK_TOP1_FSYS0, ENABLE_SCLK_TOP1_FSYS1, + ENABLE_SCLK_TOP1_FSYS11, }; static struct samsung_mux_clock top1_mux_clks[] __initdata = { - MUX(0, "mout_top1_mfc_pll", mout_top1_mfc_pll_p, MUX_SEL_TOP10, 4, 1), - MUX(0, "mout_top1_cc_pll", mout_top1_cc_pll_p, MUX_SEL_TOP10, 8, 1), - MUX(0, "mout_top1_bus1_pll", mout_top1_bus1_pll_p, + MUX(0, "mout_top1_mfc_pll_user", mout_top1_mfc_pll_user_p, + MUX_SEL_TOP10, 4, 1), + MUX(0, "mout_top1_cc_pll_user", mout_top1_cc_pll_user_p, + MUX_SEL_TOP10, 8, 1), + MUX(0, "mout_top1_bus1_pll_user", mout_top1_bus1_pll_user_p, MUX_SEL_TOP10, 12, 1), - MUX(0, "mout_top1_bus0_pll", mout_top1_bus0_pll_p, + MUX(0, "mout_top1_bus0_pll_user", mout_top1_bus0_pll_user_p, MUX_SEL_TOP10, 16, 1), - MUX(0, "mout_top1_half_mfc_pll", mout_top1_half_mfc_pll_p, + MUX(0, "mout_top1_mfc_pll_half", mout_top1_mfc_pll_half_p, MUX_SEL_TOP11, 4, 1), - MUX(0, "mout_top1_half_cc_pll", mout_top1_half_cc_pll_p, + MUX(0, "mout_top1_cc_pll_half", mout_top1_cc_pll_half_p, MUX_SEL_TOP11, 8, 1), - MUX(0, "mout_top1_half_bus1_pll", mout_top1_half_bus1_pll_p, + MUX(0, "mout_top1_bus1_pll_half", mout_top1_bus1_pll_half_p, MUX_SEL_TOP11, 12, 1), - MUX(0, "mout_top1_half_bus0_pll", mout_top1_half_bus0_pll_p, + MUX(0, "mout_top1_bus0_pll_half", mout_top1_bus0_pll_half_p, MUX_SEL_TOP11, 16, 1), MUX(0, "mout_aclk_fsys1_200", mout_top1_group1, MUX_SEL_TOP13, 24, 2), MUX(0, "mout_aclk_fsys0_200", mout_top1_group1, MUX_SEL_TOP13, 28, 2), - MUX(0, "mout_sclk_mmc2", mout_top1_group1, MUX_SEL_TOP1_FSYS0, 24, 2), + MUX(0, "mout_sclk_phy_fsys0_26m", mout_top1_group1, + MUX_SEL_TOP1_FSYS0, 0, 2), + MUX(0, "mout_sclk_mmc2", mout_top1_group1, MUX_SEL_TOP1_FSYS0, 16, 2), MUX(0, "mout_sclk_usbdrd300", mout_top1_group1, MUX_SEL_TOP1_FSYS0, 28, 2), - MUX(0, "mout_sclk_mmc1", mout_top1_group1, MUX_SEL_TOP1_FSYS1, 24, 2), - MUX(0, "mout_sclk_mmc0", mout_top1_group1, MUX_SEL_TOP1_FSYS1, 28, 2), + MUX(0, "mout_sclk_phy_fsys1", mout_top1_group1, + MUX_SEL_TOP1_FSYS1, 0, 2), + MUX(0, "mout_sclk_ufsunipro20", mout_top1_group1, + MUX_SEL_TOP1_FSYS1, 16, 2), + + MUX(0, "mout_sclk_mmc1", mout_top1_group1, MUX_SEL_TOP1_FSYS11, 0, 2), + MUX(0, "mout_sclk_mmc0", mout_top1_group1, MUX_SEL_TOP1_FSYS11, 12, 2), + MUX(0, "mout_sclk_phy_fsys1_26m", mout_top1_group1, + MUX_SEL_TOP1_FSYS11, 24, 2), }; static struct samsung_div_clock top1_div_clks[] __initdata = { @@ -439,34 +500,61 @@ static struct samsung_div_clock top1_div_clks[] __initdata = { DIV(DOUT_ACLK_FSYS0_200, "dout_aclk_fsys0_200", "mout_aclk_fsys0_200", DIV_TOP13, 28, 4), + DIV(DOUT_SCLK_PHY_FSYS1, "dout_sclk_phy_fsys1", + "mout_sclk_phy_fsys1", DIV_TOP1_FSYS1, 0, 6), + + DIV(DOUT_SCLK_UFSUNIPRO20, "dout_sclk_ufsunipro20", + "mout_sclk_ufsunipro20", + DIV_TOP1_FSYS1, 16, 6), + DIV(DOUT_SCLK_MMC2, "dout_sclk_mmc2", "mout_sclk_mmc2", - DIV_TOP1_FSYS0, 24, 4), + DIV_TOP1_FSYS0, 16, 10), DIV(0, "dout_sclk_usbdrd300", "mout_sclk_usbdrd300", DIV_TOP1_FSYS0, 28, 4), DIV(DOUT_SCLK_MMC1, "dout_sclk_mmc1", "mout_sclk_mmc1", - DIV_TOP1_FSYS1, 24, 4), + DIV_TOP1_FSYS11, 0, 10), DIV(DOUT_SCLK_MMC0, "dout_sclk_mmc0", "mout_sclk_mmc0", - DIV_TOP1_FSYS1, 28, 4), + DIV_TOP1_FSYS11, 12, 10), + + DIV(DOUT_SCLK_PHY_FSYS1_26M, "dout_sclk_phy_fsys1_26m", + "mout_sclk_phy_fsys1_26m", DIV_TOP1_FSYS11, 24, 6), }; static struct samsung_gate_clock top1_gate_clks[] __initdata = { GATE(CLK_SCLK_MMC2, "sclk_mmc2", "dout_sclk_mmc2", - ENABLE_SCLK_TOP1_FSYS0, 24, CLK_SET_RATE_PARENT, 0), + ENABLE_SCLK_TOP1_FSYS0, 16, CLK_SET_RATE_PARENT, 0), GATE(0, "sclk_usbdrd300", "dout_sclk_usbdrd300", ENABLE_SCLK_TOP1_FSYS0, 28, 0, 0), + GATE(CLK_SCLK_PHY_FSYS1, "sclk_phy_fsys1", "dout_sclk_phy_fsys1", + ENABLE_SCLK_TOP1_FSYS1, 0, CLK_SET_RATE_PARENT, 0), + + GATE(CLK_SCLK_UFSUNIPRO20, "sclk_ufsunipro20", "dout_sclk_ufsunipro20", + ENABLE_SCLK_TOP1_FSYS1, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MMC1, "sclk_mmc1", "dout_sclk_mmc1", - ENABLE_SCLK_TOP1_FSYS1, 24, CLK_SET_RATE_PARENT, 0), + ENABLE_SCLK_TOP1_FSYS11, 0, CLK_SET_RATE_PARENT, 0), GATE(CLK_SCLK_MMC0, "sclk_mmc0", "dout_sclk_mmc0", - ENABLE_SCLK_TOP1_FSYS1, 28, CLK_SET_RATE_PARENT, 0), + ENABLE_SCLK_TOP1_FSYS11, 12, CLK_SET_RATE_PARENT, 0), + + GATE(CLK_ACLK_FSYS0_200, "aclk_fsys0_200", "dout_aclk_fsys0_200", + ENABLE_ACLK_TOP13, 28, CLK_SET_RATE_PARENT, 0), + GATE(CLK_ACLK_FSYS1_200, "aclk_fsys1_200", "dout_aclk_fsys1_200", + ENABLE_ACLK_TOP13, 24, CLK_SET_RATE_PARENT, 0), + + GATE(CLK_SCLK_PHY_FSYS1_26M, "sclk_phy_fsys1_26m", + "dout_sclk_phy_fsys1_26m", ENABLE_SCLK_TOP1_FSYS11, + 24, CLK_SET_RATE_PARENT, 0), }; static struct samsung_fixed_factor_clock top1_fixed_factor_clks[] __initdata = { - FFACTOR(0, "ffac_top1_bus0_pll_div2", "mout_top1_bus0_pll", 1, 2, 0), - FFACTOR(0, "ffac_top1_bus1_pll_div2", "mout_top1_bus1_pll", 1, 2, 0), - FFACTOR(0, "ffac_top1_cc_pll_div2", "mout_top1_cc_pll", 1, 2, 0), - FFACTOR(0, "ffac_top1_mfc_pll_div2", "mout_top1_mfc_pll", 1, 2, 0), + FFACTOR(0, "ffac_top1_bus0_pll_div2", "mout_top1_bus0_pll_user", + 1, 2, 0), + FFACTOR(0, "ffac_top1_bus1_pll_div2", "mout_top1_bus1_pll_user", + 1, 2, 0), + FFACTOR(0, "ffac_top1_cc_pll_div2", "mout_top1_cc_pll_user", 1, 2, 0), + FFACTOR(0, "ffac_top1_mfc_pll_div2", "mout_top1_mfc_pll_user", 1, 2, 0), }; static struct samsung_cmu_info top1_cmu_info __initdata = { @@ -501,7 +589,7 @@ CLK_OF_DECLARE(exynos7_clk_top1, "samsung,exynos7-clock-top1", /* * List of parent clocks for Muxes in CMU_CCORE */ -PNAME(mout_aclk_ccore_133_p) = { "fin_pll", "dout_aclk_ccore_133" }; +PNAME(mout_aclk_ccore_133_user_p) = { "fin_pll", "aclk_ccore_133" }; static unsigned long ccore_clk_regs[] __initdata = { MUX_SEL_CCORE, @@ -509,7 +597,7 @@ static unsigned long ccore_clk_regs[] __initdata = { }; static struct samsung_mux_clock ccore_mux_clks[] __initdata = { - MUX(0, "mout_aclk_ccore_133_user", mout_aclk_ccore_133_p, + MUX(0, "mout_aclk_ccore_133_user", mout_aclk_ccore_133_user_p, MUX_SEL_CCORE, 1, 1), }; @@ -542,8 +630,8 @@ CLK_OF_DECLARE(exynos7_clk_ccore, "samsung,exynos7-clock-ccore", #define ENABLE_SCLK_PERIC0 0x0A00 /* List of parent clocks for Muxes in CMU_PERIC0 */ -PNAME(mout_aclk_peric0_66_p) = { "fin_pll", "dout_aclk_peric0_66" }; -PNAME(mout_sclk_uart0_p) = { "fin_pll", "sclk_uart0" }; +PNAME(mout_aclk_peric0_66_user_p) = { "fin_pll", "aclk_peric0_66" }; +PNAME(mout_sclk_uart0_user_p) = { "fin_pll", "sclk_uart0" }; static unsigned long peric0_clk_regs[] __initdata = { MUX_SEL_PERIC0, @@ -552,9 +640,9 @@ static unsigned long peric0_clk_regs[] __initdata = { }; static struct samsung_mux_clock peric0_mux_clks[] __initdata = { - MUX(0, "mout_aclk_peric0_66_user", mout_aclk_peric0_66_p, + MUX(0, "mout_aclk_peric0_66_user", mout_aclk_peric0_66_user_p, MUX_SEL_PERIC0, 0, 1), - MUX(0, "mout_sclk_uart0_user", mout_sclk_uart0_p, + MUX(0, "mout_sclk_uart0_user", mout_sclk_uart0_user_p, MUX_SEL_PERIC0, 16, 1), }; @@ -611,15 +699,15 @@ CLK_OF_DECLARE(exynos7_clk_peric0, "samsung,exynos7-clock-peric0", exynos7_clk_peric0_init); /* List of parent clocks for Muxes in CMU_PERIC1 */ -PNAME(mout_aclk_peric1_66_p) = { "fin_pll", "dout_aclk_peric1_66" }; -PNAME(mout_sclk_uart1_p) = { "fin_pll", "sclk_uart1" }; -PNAME(mout_sclk_uart2_p) = { "fin_pll", "sclk_uart2" }; -PNAME(mout_sclk_uart3_p) = { "fin_pll", "sclk_uart3" }; -PNAME(mout_sclk_spi0_p) = { "fin_pll", "sclk_spi0" }; -PNAME(mout_sclk_spi1_p) = { "fin_pll", "sclk_spi1" }; -PNAME(mout_sclk_spi2_p) = { "fin_pll", "sclk_spi2" }; -PNAME(mout_sclk_spi3_p) = { "fin_pll", "sclk_spi3" }; -PNAME(mout_sclk_spi4_p) = { "fin_pll", "sclk_spi4" }; +PNAME(mout_aclk_peric1_66_user_p) = { "fin_pll", "aclk_peric1_66" }; +PNAME(mout_sclk_uart1_user_p) = { "fin_pll", "sclk_uart1" }; +PNAME(mout_sclk_uart2_user_p) = { "fin_pll", "sclk_uart2" }; +PNAME(mout_sclk_uart3_user_p) = { "fin_pll", "sclk_uart3" }; +PNAME(mout_sclk_spi0_user_p) = { "fin_pll", "sclk_spi0" }; +PNAME(mout_sclk_spi1_user_p) = { "fin_pll", "sclk_spi1" }; +PNAME(mout_sclk_spi2_user_p) = { "fin_pll", "sclk_spi2" }; +PNAME(mout_sclk_spi3_user_p) = { "fin_pll", "sclk_spi3" }; +PNAME(mout_sclk_spi4_user_p) = { "fin_pll", "sclk_spi4" }; static unsigned long peric1_clk_regs[] __initdata = { MUX_SEL_PERIC10, @@ -630,24 +718,24 @@ static unsigned long peric1_clk_regs[] __initdata = { }; static struct samsung_mux_clock peric1_mux_clks[] __initdata = { - MUX(0, "mout_aclk_peric1_66_user", mout_aclk_peric1_66_p, + MUX(0, "mout_aclk_peric1_66_user", mout_aclk_peric1_66_user_p, MUX_SEL_PERIC10, 0, 1), - MUX_F(0, "mout_sclk_spi0_user", mout_sclk_spi0_p, + MUX_F(0, "mout_sclk_spi0_user", mout_sclk_spi0_user_p, MUX_SEL_PERIC11, 0, 1, CLK_SET_RATE_PARENT, 0), - MUX_F(0, "mout_sclk_spi1_user", mout_sclk_spi1_p, + MUX_F(0, "mout_sclk_spi1_user", mout_sclk_spi1_user_p, MUX_SEL_PERIC11, 4, 1, CLK_SET_RATE_PARENT, 0), - MUX_F(0, "mout_sclk_spi2_user", mout_sclk_spi2_p, + MUX_F(0, "mout_sclk_spi2_user", mout_sclk_spi2_user_p, MUX_SEL_PERIC11, 8, 1, CLK_SET_RATE_PARENT, 0), - MUX_F(0, "mout_sclk_spi3_user", mout_sclk_spi3_p, + MUX_F(0, "mout_sclk_spi3_user", mout_sclk_spi3_user_p, MUX_SEL_PERIC11, 12, 1, CLK_SET_RATE_PARENT, 0), - MUX_F(0, "mout_sclk_spi4_user", mout_sclk_spi4_p, + MUX_F(0, "mout_sclk_spi4_user", mout_sclk_spi4_user_p, MUX_SEL_PERIC11, 16, 1, CLK_SET_RATE_PARENT, 0), - MUX(0, "mout_sclk_uart1_user", mout_sclk_uart1_p, + MUX(0, "mout_sclk_uart1_user", mout_sclk_uart1_user_p, MUX_SEL_PERIC11, 20, 1), - MUX(0, "mout_sclk_uart2_user", mout_sclk_uart2_p, + MUX(0, "mout_sclk_uart2_user", mout_sclk_uart2_user_p, MUX_SEL_PERIC11, 24, 1), - MUX(0, "mout_sclk_uart3_user", mout_sclk_uart3_p, + MUX(0, "mout_sclk_uart3_user", mout_sclk_uart3_user_p, MUX_SEL_PERIC11, 28, 1), }; @@ -735,7 +823,7 @@ CLK_OF_DECLARE(exynos7_clk_peric1, "samsung,exynos7-clock-peric1", #define ENABLE_SCLK_PERIS_SECURE_CHIPID 0x0A10 /* List of parent clocks for Muxes in CMU_PERIS */ -PNAME(mout_aclk_peris_66_p) = { "fin_pll", "dout_aclk_peris_66" }; +PNAME(mout_aclk_peris_66_user_p) = { "fin_pll", "aclk_peris_66" }; static unsigned long peris_clk_regs[] __initdata = { MUX_SEL_PERIS, @@ -747,7 +835,7 @@ static unsigned long peris_clk_regs[] __initdata = { static struct samsung_mux_clock peris_mux_clks[] __initdata = { MUX(0, "mout_aclk_peris_66_user", - mout_aclk_peris_66_p, MUX_SEL_PERIS, 0, 1), + mout_aclk_peris_66_user_p, MUX_SEL_PERIS, 0, 1), }; static struct samsung_gate_clock peris_gate_clks[] __initdata = { @@ -795,17 +883,17 @@ CLK_OF_DECLARE(exynos7_clk_peris, "samsung,exynos7-clock-peris", /* * List of parent clocks for Muxes in CMU_FSYS0 */ -PNAME(mout_aclk_fsys0_200_p) = { "fin_pll", "dout_aclk_fsys0_200" }; -PNAME(mout_sclk_mmc2_p) = { "fin_pll", "sclk_mmc2" }; +PNAME(mout_aclk_fsys0_200_user_p) = { "fin_pll", "aclk_fsys0_200" }; +PNAME(mout_sclk_mmc2_user_p) = { "fin_pll", "sclk_mmc2" }; -PNAME(mout_sclk_usbdrd300_p) = { "fin_pll", "sclk_usbdrd300" }; -PNAME(mout_phyclk_usbdrd300_udrd30_phyclk_p) = { "fin_pll", +PNAME(mout_sclk_usbdrd300_user_p) = { "fin_pll", "sclk_usbdrd300" }; +PNAME(mout_phyclk_usbdrd300_udrd30_phyclk_user_p) = { "fin_pll", "phyclk_usbdrd300_udrd30_phyclock" }; -PNAME(mout_phyclk_usbdrd300_udrd30_pipe_pclk_p) = { "fin_pll", +PNAME(mout_phyclk_usbdrd300_udrd30_pipe_pclk_user_p) = { "fin_pll", "phyclk_usbdrd300_udrd30_pipe_pclk" }; /* fixed rate clocks used in the FSYS0 block */ -struct samsung_fixed_rate_clock fixed_rate_clks_fsys0[] __initdata = { +static struct samsung_fixed_rate_clock fixed_rate_clks_fsys0[] __initdata = { FRATE(0, "phyclk_usbdrd300_udrd30_phyclock", NULL, CLK_IS_ROOT, 60000000), FRATE(0, "phyclk_usbdrd300_udrd30_pipe_pclk", NULL, @@ -824,29 +912,30 @@ static unsigned long fsys0_clk_regs[] __initdata = { }; static struct samsung_mux_clock fsys0_mux_clks[] __initdata = { - MUX(0, "mout_aclk_fsys0_200_user", mout_aclk_fsys0_200_p, + MUX(0, "mout_aclk_fsys0_200_user", mout_aclk_fsys0_200_user_p, MUX_SEL_FSYS00, 24, 1), - MUX(0, "mout_sclk_mmc2_user", mout_sclk_mmc2_p, MUX_SEL_FSYS01, 24, 1), - MUX(0, "mout_sclk_usbdrd300_user", mout_sclk_usbdrd300_p, + MUX(0, "mout_sclk_mmc2_user", mout_sclk_mmc2_user_p, + MUX_SEL_FSYS01, 24, 1), + MUX(0, "mout_sclk_usbdrd300_user", mout_sclk_usbdrd300_user_p, MUX_SEL_FSYS01, 28, 1), MUX(0, "mout_phyclk_usbdrd300_udrd30_pipe_pclk_user", - mout_phyclk_usbdrd300_udrd30_pipe_pclk_p, + mout_phyclk_usbdrd300_udrd30_pipe_pclk_user_p, MUX_SEL_FSYS02, 24, 1), MUX(0, "mout_phyclk_usbdrd300_udrd30_phyclk_user", - mout_phyclk_usbdrd300_udrd30_phyclk_p, + mout_phyclk_usbdrd300_udrd30_phyclk_user_p, MUX_SEL_FSYS02, 28, 1), }; static struct samsung_gate_clock fsys0_gate_clks[] __initdata = { - GATE(ACLK_AXIUS_USBDRD30X_FSYS0X, "aclk_axius_usbdrd30x_fsys0x", - "mout_aclk_fsys0_200_user", - ENABLE_ACLK_FSYS00, 19, 0, 0), GATE(ACLK_PDMA1, "aclk_pdma1", "mout_aclk_fsys0_200_user", ENABLE_ACLK_FSYS00, 3, 0, 0), GATE(ACLK_PDMA0, "aclk_pdma0", "mout_aclk_fsys0_200_user", ENABLE_ACLK_FSYS00, 4, 0, 0), + GATE(ACLK_AXIUS_USBDRD30X_FSYS0X, "aclk_axius_usbdrd30x_fsys0x", + "mout_aclk_fsys0_200_user", + ENABLE_ACLK_FSYS00, 19, 0, 0), GATE(ACLK_USBDRD300, "aclk_usbdrd300", "mout_aclk_fsys0_200_user", ENABLE_ACLK_FSYS01, 29, 0, 0), @@ -874,11 +963,13 @@ static struct samsung_gate_clock fsys0_gate_clks[] __initdata = { }; static struct samsung_cmu_info fsys0_cmu_info __initdata = { + .fixed_clks = fixed_rate_clks_fsys0, + .nr_fixed_clks = ARRAY_SIZE(fixed_rate_clks_fsys0), .mux_clks = fsys0_mux_clks, .nr_mux_clks = ARRAY_SIZE(fsys0_mux_clks), .gate_clks = fsys0_gate_clks, .nr_gate_clks = ARRAY_SIZE(fsys0_gate_clks), - .nr_clk_ids = TOP1_NR_CLK, + .nr_clk_ids = FSYS0_NR_CLK, .clk_regs = fsys0_clk_regs, .nr_clk_regs = ARRAY_SIZE(fsys0_clk_regs), }; @@ -894,42 +985,122 @@ CLK_OF_DECLARE(exynos7_clk_fsys0, "samsung,exynos7-clock-fsys0", /* Register Offset definitions for CMU_FSYS1 (0x156E0000) */ #define MUX_SEL_FSYS10 0x0200 #define MUX_SEL_FSYS11 0x0204 +#define MUX_SEL_FSYS12 0x0208 +#define DIV_FSYS1 0x0600 #define ENABLE_ACLK_FSYS1 0x0800 +#define ENABLE_PCLK_FSYS1 0x0900 +#define ENABLE_SCLK_FSYS11 0x0A04 +#define ENABLE_SCLK_FSYS12 0x0A08 +#define ENABLE_SCLK_FSYS13 0x0A0C /* * List of parent clocks for Muxes in CMU_FSYS1 */ -PNAME(mout_aclk_fsys1_200_p) = { "fin_pll", "dout_aclk_fsys1_200" }; -PNAME(mout_sclk_mmc0_p) = { "fin_pll", "sclk_mmc0" }; -PNAME(mout_sclk_mmc1_p) = { "fin_pll", "sclk_mmc1" }; +PNAME(mout_aclk_fsys1_200_user_p) = { "fin_pll", "aclk_fsys1_200" }; +PNAME(mout_fsys1_group_p) = { "fin_pll", "fin_pll_26m", + "sclk_phy_fsys1_26m" }; +PNAME(mout_sclk_mmc0_user_p) = { "fin_pll", "sclk_mmc0" }; +PNAME(mout_sclk_mmc1_user_p) = { "fin_pll", "sclk_mmc1" }; +PNAME(mout_sclk_ufsunipro20_user_p) = { "fin_pll", "sclk_ufsunipro20" }; +PNAME(mout_phyclk_ufs20_tx0_user_p) = { "fin_pll", "phyclk_ufs20_tx0_symbol" }; +PNAME(mout_phyclk_ufs20_rx0_user_p) = { "fin_pll", "phyclk_ufs20_rx0_symbol" }; +PNAME(mout_phyclk_ufs20_rx1_user_p) = { "fin_pll", "phyclk_ufs20_rx1_symbol" }; + +/* fixed rate clocks used in the FSYS1 block */ +static struct samsung_fixed_rate_clock fixed_rate_clks_fsys1[] __initdata = { + FRATE(PHYCLK_UFS20_TX0_SYMBOL, "phyclk_ufs20_tx0_symbol", NULL, + CLK_IS_ROOT, 300000000), + FRATE(PHYCLK_UFS20_RX0_SYMBOL, "phyclk_ufs20_rx0_symbol", NULL, + CLK_IS_ROOT, 300000000), + FRATE(PHYCLK_UFS20_RX1_SYMBOL, "phyclk_ufs20_rx1_symbol", NULL, + CLK_IS_ROOT, 300000000), +}; static unsigned long fsys1_clk_regs[] __initdata = { MUX_SEL_FSYS10, MUX_SEL_FSYS11, + MUX_SEL_FSYS12, + DIV_FSYS1, ENABLE_ACLK_FSYS1, + ENABLE_PCLK_FSYS1, + ENABLE_SCLK_FSYS11, + ENABLE_SCLK_FSYS12, + ENABLE_SCLK_FSYS13, }; static struct samsung_mux_clock fsys1_mux_clks[] __initdata = { - MUX(0, "mout_aclk_fsys1_200_user", mout_aclk_fsys1_200_p, + MUX(MOUT_FSYS1_PHYCLK_SEL1, "mout_fsys1_phyclk_sel1", + mout_fsys1_group_p, MUX_SEL_FSYS10, 16, 2), + MUX(0, "mout_fsys1_phyclk_sel0", mout_fsys1_group_p, + MUX_SEL_FSYS10, 20, 2), + MUX(0, "mout_aclk_fsys1_200_user", mout_aclk_fsys1_200_user_p, MUX_SEL_FSYS10, 28, 1), - MUX(0, "mout_sclk_mmc1_user", mout_sclk_mmc1_p, MUX_SEL_FSYS11, 24, 1), - MUX(0, "mout_sclk_mmc0_user", mout_sclk_mmc0_p, MUX_SEL_FSYS11, 28, 1), + MUX(0, "mout_sclk_mmc1_user", mout_sclk_mmc1_user_p, + MUX_SEL_FSYS11, 24, 1), + MUX(0, "mout_sclk_mmc0_user", mout_sclk_mmc0_user_p, + MUX_SEL_FSYS11, 28, 1), + MUX(0, "mout_sclk_ufsunipro20_user", mout_sclk_ufsunipro20_user_p, + MUX_SEL_FSYS11, 20, 1), + + MUX(0, "mout_phyclk_ufs20_rx1_symbol_user", + mout_phyclk_ufs20_rx1_user_p, MUX_SEL_FSYS12, 16, 1), + MUX(0, "mout_phyclk_ufs20_rx0_symbol_user", + mout_phyclk_ufs20_rx0_user_p, MUX_SEL_FSYS12, 24, 1), + MUX(0, "mout_phyclk_ufs20_tx0_symbol_user", + mout_phyclk_ufs20_tx0_user_p, MUX_SEL_FSYS12, 28, 1), +}; + +static struct samsung_div_clock fsys1_div_clks[] __initdata = { + DIV(DOUT_PCLK_FSYS1, "dout_pclk_fsys1", "mout_aclk_fsys1_200_user", + DIV_FSYS1, 0, 2), }; static struct samsung_gate_clock fsys1_gate_clks[] __initdata = { + GATE(SCLK_UFSUNIPRO20_USER, "sclk_ufsunipro20_user", + "mout_sclk_ufsunipro20_user", + ENABLE_SCLK_FSYS11, 20, 0, 0), + GATE(ACLK_MMC1, "aclk_mmc1", "mout_aclk_fsys1_200_user", ENABLE_ACLK_FSYS1, 29, 0, 0), GATE(ACLK_MMC0, "aclk_mmc0", "mout_aclk_fsys1_200_user", ENABLE_ACLK_FSYS1, 30, 0, 0), + + GATE(ACLK_UFS20_LINK, "aclk_ufs20_link", "dout_pclk_fsys1", + ENABLE_ACLK_FSYS1, 31, 0, 0), + GATE(PCLK_GPIO_FSYS1, "pclk_gpio_fsys1", "mout_aclk_fsys1_200_user", + ENABLE_PCLK_FSYS1, 30, 0, 0), + + GATE(PHYCLK_UFS20_RX1_SYMBOL_USER, "phyclk_ufs20_rx1_symbol_user", + "mout_phyclk_ufs20_rx1_symbol_user", + ENABLE_SCLK_FSYS12, 16, 0, 0), + GATE(PHYCLK_UFS20_RX0_SYMBOL_USER, "phyclk_ufs20_rx0_symbol_user", + "mout_phyclk_ufs20_rx0_symbol_user", + ENABLE_SCLK_FSYS12, 24, 0, 0), + GATE(PHYCLK_UFS20_TX0_SYMBOL_USER, "phyclk_ufs20_tx0_symbol_user", + "mout_phyclk_ufs20_tx0_symbol_user", + ENABLE_SCLK_FSYS12, 28, 0, 0), + + GATE(OSCCLK_PHY_CLKOUT_EMBEDDED_COMBO_PHY, + "oscclk_phy_clkout_embedded_combo_phy", + "fin_pll", + ENABLE_SCLK_FSYS12, 4, CLK_IGNORE_UNUSED, 0), + + GATE(SCLK_COMBO_PHY_EMBEDDED_26M, "sclk_combo_phy_embedded_26m", + "mout_fsys1_phyclk_sel1", + ENABLE_SCLK_FSYS13, 24, CLK_IGNORE_UNUSED, 0), }; static struct samsung_cmu_info fsys1_cmu_info __initdata = { + .fixed_clks = fixed_rate_clks_fsys1, + .nr_fixed_clks = ARRAY_SIZE(fixed_rate_clks_fsys1), .mux_clks = fsys1_mux_clks, .nr_mux_clks = ARRAY_SIZE(fsys1_mux_clks), + .div_clks = fsys1_div_clks, + .nr_div_clks = ARRAY_SIZE(fsys1_div_clks), .gate_clks = fsys1_gate_clks, .nr_gate_clks = ARRAY_SIZE(fsys1_gate_clks), - .nr_clk_ids = TOP1_NR_CLK, + .nr_clk_ids = FSYS1_NR_CLK, .clk_regs = fsys1_clk_regs, .nr_clk_regs = ARRAY_SIZE(fsys1_clk_regs), }; diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c index b1df7b2f1e97..4abf21172625 100644 --- a/drivers/clk/shmobile/clk-mstp.c +++ b/drivers/clk/shmobile/clk-mstp.c @@ -214,7 +214,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) break; if (clkidx >= MSTP_MAX_CLOCKS) { - pr_err("%s: invalid clock %s %s index %u)\n", + pr_err("%s: invalid clock %s %s index %u\n", __func__, np->name, name, clkidx); continue; } diff --git a/drivers/clk/shmobile/clk-r8a7778.c b/drivers/clk/shmobile/clk-r8a7778.c index 87c1d2f2fb57..b1741551fff2 100644 --- a/drivers/clk/shmobile/clk-r8a7778.c +++ b/drivers/clk/shmobile/clk-r8a7778.c @@ -20,10 +20,10 @@ struct r8a7778_cpg { }; /* PLL multipliers per bits 11, 12, and 18 of MODEMR */ -struct { +static const struct { unsigned long plla_mult; unsigned long pllb_mult; -} r8a7778_rates[] __initdata = { +} r8a7778_rates[] __initconst = { [0] = { 21, 21 }, [1] = { 24, 24 }, [2] = { 28, 28 }, @@ -34,10 +34,10 @@ struct { }; /* Clock dividers per bits 1 and 2 of MODEMR */ -struct { +static const struct { const char *name; unsigned int div[4]; -} r8a7778_divs[6] __initdata = { +} r8a7778_divs[6] __initconst = { { "b", { 12, 12, 16, 18 } }, { "out", { 12, 12, 16, 18 } }, { "p", { 16, 12, 16, 12 } }, diff --git a/drivers/clk/sirf/clk-atlas7.c b/drivers/clk/sirf/clk-atlas7.c index a98e21fe773a..957aae63e7cc 100644 --- a/drivers/clk/sirf/clk-atlas7.c +++ b/drivers/clk/sirf/clk-atlas7.c @@ -205,18 +205,11 @@ #define SIRFSOC_CLKC_LEAF_CLK_EN7_SET 0x0530 #define SIRFSOC_CLKC_LEAF_CLK_EN8_SET 0x0548 - -static void __iomem *sirfsoc_clk_vbase; -static struct clk_onecell_data clk_data; - -static const struct clk_div_table pll_div_table[] = { - { .val = 0, .div = 1 }, - { .val = 1, .div = 2 }, - { .val = 2, .div = 4 }, - { .val = 3, .div = 8 }, - { .val = 4, .div = 16 }, - { .val = 5, .div = 32 }, -}; +#define SIRFSOC_NOC_CLK_IDLEREQ_SET 0x02D0 +#define SIRFSOC_NOC_CLK_IDLEREQ_CLR 0x02D4 +#define SIRFSOC_NOC_CLK_SLVRDY_SET 0x02E8 +#define SIRFSOC_NOC_CLK_SLVRDY_CLR 0x02EC +#define SIRFSOC_NOC_CLK_IDLE_STATUS 0x02F4 struct clk_pll { struct clk_hw hw; @@ -231,10 +224,18 @@ struct clk_dto { }; #define to_dtoclk(_hw) container_of(_hw, struct clk_dto, hw) +enum clk_unit_type { + CLK_UNIT_NOC_OTHER, + CLK_UNIT_NOC_CLOCK, + CLK_UNIT_NOC_SOCKET, +}; + struct clk_unit { struct clk_hw hw; u16 regofs; u16 bit; + u32 type; + u8 idle_bit; spinlock_t *lock; }; #define to_unitclk(_hw) container_of(_hw, struct clk_unit, hw) @@ -272,6 +273,8 @@ struct atlas7_unit_init_data { unsigned long flags; u32 regofs; u8 bit; + u32 type; + u8 idle_bit; spinlock_t *lock; }; @@ -284,6 +287,18 @@ struct atlas7_reset_desc { spinlock_t *lock; }; +static void __iomem *sirfsoc_clk_vbase; +static struct clk_onecell_data clk_data; + +static const struct clk_div_table pll_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 16 }, + { .val = 5, .div = 32 }, +}; + static DEFINE_SPINLOCK(cpupll_ctrl1_lock); static DEFINE_SPINLOCK(mempll_ctrl1_lock); static DEFINE_SPINLOCK(sys0pll_ctrl1_lock); @@ -1040,148 +1055,148 @@ static struct atlas7_mux_init_data mux_list[] __initdata = { /* new unit should add start from the tail of list */ static struct atlas7_unit_init_data unit_list[] __initdata = { /* unit_name, parent_name, flags, regofs, bit, lock */ - { 0, "audmscm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 0, &root0_gate_lock }, - { 1, "gnssm_gnss", "gnss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 1, &root0_gate_lock }, - { 2, "gpum_gpu", "gpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 2, &root0_gate_lock }, - { 3, "mediam_g2d", "g2d_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 3, &root0_gate_lock }, - { 4, "mediam_jpenc", "jpenc_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 4, &root0_gate_lock }, - { 5, "vdifm_disp0", "disp0_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 5, &root0_gate_lock }, - { 6, "vdifm_disp1", "disp1_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 6, &root0_gate_lock }, - { 7, "audmscm_i2s", "i2s_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 8, &root0_gate_lock }, - { 8, "audmscm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 11, &root0_gate_lock }, - { 9, "vdifm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 12, &root0_gate_lock }, - { 10, "gnssm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 13, &root0_gate_lock }, - { 11, "mediam_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 14, &root0_gate_lock }, - { 12, "btm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 17, &root0_gate_lock }, - { 13, "mediam_sdphy01", "sdphy01_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 18, &root0_gate_lock }, - { 14, "vdifm_sdphy23", "sdphy23_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 19, &root0_gate_lock }, - { 15, "vdifm_sdphy45", "sdphy45_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 20, &root0_gate_lock }, - { 16, "vdifm_sdphy67", "sdphy67_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 21, &root0_gate_lock }, - { 17, "audmscm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 22, &root0_gate_lock }, - { 18, "mediam_nand", "nand_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 27, &root0_gate_lock }, - { 19, "gnssm_sec", "sec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 28, &root0_gate_lock }, - { 20, "cpum_cpu", "cpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 29, &root0_gate_lock }, - { 21, "gnssm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 30, &root0_gate_lock }, - { 22, "vdifm_vip", "vip_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 31, &root0_gate_lock }, - { 23, "btm_btss", "btss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 0, &root1_gate_lock }, - { 24, "mediam_usbphy", "usbphy_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 1, &root1_gate_lock }, - { 25, "rtcm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 2, &root1_gate_lock }, - { 26, "audmscm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 3, &root1_gate_lock }, - { 27, "vdifm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 4, &root1_gate_lock }, - { 28, "gnssm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 5, &root1_gate_lock }, - { 29, "mediam_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 6, &root1_gate_lock }, - { 30, "cpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 8, &root1_gate_lock }, - { 31, "gpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 9, &root1_gate_lock }, - { 32, "audmscm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 11, &root1_gate_lock }, - { 33, "vdifm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 12, &root1_gate_lock }, - { 34, "gnssm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 13, &root1_gate_lock }, - { 35, "mediam_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 14, &root1_gate_lock }, - { 36, "ddrm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 15, &root1_gate_lock }, - { 37, "cpum_tpiu", "tpiu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 16, &root1_gate_lock }, - { 38, "gpum_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 17, &root1_gate_lock }, - { 39, "gnssm_rgmii", "rgmii_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 20, &root1_gate_lock }, - { 40, "mediam_vdec", "vdec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 21, &root1_gate_lock }, - { 41, "gpum_sdr", "sdr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 22, &root1_gate_lock }, - { 42, "vdifm_deint", "deint_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 23, &root1_gate_lock }, - { 43, "gnssm_can", "can_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 26, &root1_gate_lock }, - { 44, "mediam_usb", "usb_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 28, &root1_gate_lock }, - { 45, "gnssm_gmac", "gmac_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 29, &root1_gate_lock }, - { 46, "cvd_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 0, &leaf1_gate_lock }, - { 47, "timer_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 1, &leaf1_gate_lock }, - { 48, "pulse_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 2, &leaf1_gate_lock }, - { 49, "tsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 3, &leaf1_gate_lock }, - { 50, "tsc_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 21, &leaf1_gate_lock }, - { 51, "ioctop_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 4, &leaf1_gate_lock }, - { 52, "rsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 5, &leaf1_gate_lock }, - { 53, "dvm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 6, &leaf1_gate_lock }, - { 54, "lvds_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 7, &leaf1_gate_lock }, - { 55, "kas_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 8, &leaf1_gate_lock }, - { 56, "ac97_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 9, &leaf1_gate_lock }, - { 57, "usp0_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 10, &leaf1_gate_lock }, - { 58, "usp1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 11, &leaf1_gate_lock }, - { 59, "usp2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 12, &leaf1_gate_lock }, - { 60, "dmac2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 13, &leaf1_gate_lock }, - { 61, "dmac3_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 14, &leaf1_gate_lock }, - { 62, "audioif_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 15, &leaf1_gate_lock }, - { 63, "i2s1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 17, &leaf1_gate_lock }, - { 64, "thaudmscm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 22, &leaf1_gate_lock }, - { 65, "analogtest_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 23, &leaf1_gate_lock }, - { 66, "sys2pci_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 0, &leaf2_gate_lock }, - { 67, "pciarb_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 1, &leaf2_gate_lock }, - { 68, "pcicopy_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 2, &leaf2_gate_lock }, - { 69, "rom_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 3, &leaf2_gate_lock }, - { 70, "sdio23_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 4, &leaf2_gate_lock }, - { 71, "sdio45_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 5, &leaf2_gate_lock }, - { 72, "sdio67_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 6, &leaf2_gate_lock }, - { 73, "vip1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 7, &leaf2_gate_lock }, - { 74, "vip1_vip", "vdifm_vip", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 16, &leaf2_gate_lock }, - { 75, "sdio23_sdphy23", "vdifm_sdphy23", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 8, &leaf2_gate_lock }, - { 76, "sdio45_sdphy45", "vdifm_sdphy45", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 9, &leaf2_gate_lock }, - { 77, "sdio67_sdphy67", "vdifm_sdphy67", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 10, &leaf2_gate_lock }, - { 78, "vpp0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 11, &leaf2_gate_lock }, - { 79, "lcd0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 12, &leaf2_gate_lock }, - { 80, "vpp1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 13, &leaf2_gate_lock }, - { 81, "lcd1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 14, &leaf2_gate_lock }, - { 82, "dcu_deint", "vdifm_deint", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 15, &leaf2_gate_lock }, - { 83, "vdifm_dapa_r_nocr", "vdifm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 17, &leaf2_gate_lock }, - { 84, "gpio1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 18, &leaf2_gate_lock }, - { 85, "thvdifm_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 19, &leaf2_gate_lock }, - { 86, "gmac_rgmii", "gnssm_rgmii", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 0, &leaf3_gate_lock }, - { 87, "gmac_gmac", "gnssm_gmac", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 1, &leaf3_gate_lock }, - { 88, "uart1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 2, &leaf3_gate_lock }, - { 89, "dmac0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 3, &leaf3_gate_lock }, - { 90, "uart0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 4, &leaf3_gate_lock }, - { 91, "uart2_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 5, &leaf3_gate_lock }, - { 92, "uart3_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 6, &leaf3_gate_lock }, - { 93, "uart4_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 7, &leaf3_gate_lock }, - { 94, "uart5_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 8, &leaf3_gate_lock }, - { 95, "spi1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 9, &leaf3_gate_lock }, - { 96, "gnss_gnss", "gnssm_gnss", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 10, &leaf3_gate_lock }, - { 97, "canbus1_can", "gnssm_can", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 12, &leaf3_gate_lock }, - { 98, "ccsec_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 15, &leaf3_gate_lock }, - { 99, "ccpub_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 16, &leaf3_gate_lock }, - { 100, "gnssm_dapa_r_nocr", "gnssm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 13, &leaf3_gate_lock }, - { 101, "thgnssm_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 14, &leaf3_gate_lock }, - { 102, "media_vdec", "mediam_vdec", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 0, &leaf4_gate_lock }, - { 103, "media_jpenc", "mediam_jpenc", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 1, &leaf4_gate_lock }, - { 104, "g2d_g2d", "mediam_g2d", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 2, &leaf4_gate_lock }, - { 105, "i2c0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 3, &leaf4_gate_lock }, - { 106, "i2c1_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 4, &leaf4_gate_lock }, - { 107, "gpio0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 5, &leaf4_gate_lock }, - { 108, "nand_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 6, &leaf4_gate_lock }, - { 109, "sdio01_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 7, &leaf4_gate_lock }, - { 110, "sys2pci2_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 8, &leaf4_gate_lock }, - { 111, "sdio01_sdphy01", "mediam_sdphy01", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 9, &leaf4_gate_lock }, - { 112, "nand_nand", "mediam_nand", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 10, &leaf4_gate_lock }, - { 113, "usb0_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 11, &leaf4_gate_lock }, - { 114, "usb1_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 12, &leaf4_gate_lock }, - { 115, "usbphy0_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 13, &leaf4_gate_lock }, - { 116, "usbphy1_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 14, &leaf4_gate_lock }, - { 117, "thmediam_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 15, &leaf4_gate_lock }, - { 118, "memc_mem", "mempll_clk1", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 0, &leaf5_gate_lock }, - { 119, "dapa_mem", "mempll_clk1", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 1, &leaf5_gate_lock }, - { 120, "nocddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 2, &leaf5_gate_lock }, - { 121, "thddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 3, &leaf5_gate_lock }, - { 122, "spram1_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 0, &leaf6_gate_lock }, - { 123, "spram2_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 1, &leaf6_gate_lock }, - { 124, "coresight_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 2, &leaf6_gate_lock }, - { 125, "coresight_tpiu", "cpum_tpiu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 3, &leaf6_gate_lock }, - { 126, "graphic_gpu", "gpum_gpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 0, &leaf7_gate_lock }, - { 127, "vss_sdr", "gpum_sdr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 1, &leaf7_gate_lock }, - { 128, "thgpum_nocr", "gpum_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 2, &leaf7_gate_lock }, - { 129, "a7ca_btss", "btm_btss", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 1, &leaf8_gate_lock }, - { 130, "dmac4_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 2, &leaf8_gate_lock }, - { 131, "uart6_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 3, &leaf8_gate_lock }, - { 132, "usp3_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 4, &leaf8_gate_lock }, - { 133, "a7ca_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 5, &leaf8_gate_lock }, - { 134, "noc_btm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 6, &leaf8_gate_lock }, - { 135, "thbtm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 7, &leaf8_gate_lock }, - { 136, "btslow", "xinw_fixdiv_btslow", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 25, &root1_gate_lock }, - { 137, "a7ca_btslow", "btslow", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 0, &leaf8_gate_lock }, - { 138, "pwm_io", "io_mux", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 0, &leaf0_gate_lock }, - { 139, "pwm_xin", "xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 1, &leaf0_gate_lock }, - { 140, "pwm_xinw", "xinw", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 2, &leaf0_gate_lock }, - { 141, "thcgum_sys", "sys_mux", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 3, &leaf0_gate_lock }, + { 0, "audmscm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 0, 0, 0, &root0_gate_lock }, + { 1, "gnssm_gnss", "gnss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 1, 0, 0, &root0_gate_lock }, + { 2, "gpum_gpu", "gpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 2, 0, 0, &root0_gate_lock }, + { 3, "mediam_g2d", "g2d_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 3, 0, 0, &root0_gate_lock }, + { 4, "mediam_jpenc", "jpenc_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 4, 0, 0, &root0_gate_lock }, + { 5, "vdifm_disp0", "disp0_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 5, 0, 0, &root0_gate_lock }, + { 6, "vdifm_disp1", "disp1_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 6, 0, 0, &root0_gate_lock }, + { 7, "audmscm_i2s", "i2s_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 8, 0, 0, &root0_gate_lock }, + { 8, "audmscm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 11, 0, 0, &root0_gate_lock }, + { 9, "vdifm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 12, 0, 0, &root0_gate_lock }, + { 10, "gnssm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 13, 0, 0, &root0_gate_lock }, + { 11, "mediam_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 14, 0, 0, &root0_gate_lock }, + { 12, "btm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 17, 0, 0, &root0_gate_lock }, + { 13, "mediam_sdphy01", "sdphy01_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 18, 0, 0, &root0_gate_lock }, + { 14, "vdifm_sdphy23", "sdphy23_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 19, 0, 0, &root0_gate_lock }, + { 15, "vdifm_sdphy45", "sdphy45_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 20, 0, 0, &root0_gate_lock }, + { 16, "vdifm_sdphy67", "sdphy67_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 21, 0, 0, &root0_gate_lock }, + { 17, "audmscm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 22, 0, 0, &root0_gate_lock }, + { 18, "mediam_nand", "nand_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 27, 0, 0, &root0_gate_lock }, + { 19, "gnssm_sec", "sec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 28, 0, 0, &root0_gate_lock }, + { 20, "cpum_cpu", "cpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 29, 0, 0, &root0_gate_lock }, + { 21, "gnssm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 30, 0, 0, &root0_gate_lock }, + { 22, "vdifm_vip", "vip_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 31, 0, 0, &root0_gate_lock }, + { 23, "btm_btss", "btss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 0, 0, 0, &root1_gate_lock }, + { 24, "mediam_usbphy", "usbphy_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 1, 0, 0, &root1_gate_lock }, + { 25, "rtcm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 2, 0, 0, &root1_gate_lock }, + { 26, "audmscm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 3, 0, 0, &root1_gate_lock }, + { 27, "vdifm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 4, 0, 0, &root1_gate_lock }, + { 28, "gnssm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 5, 0, 0, &root1_gate_lock }, + { 29, "mediam_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 6, 0, 0, &root1_gate_lock }, + { 30, "cpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 8, 0, 0, &root1_gate_lock }, + { 31, "gpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 9, 0, 0, &root1_gate_lock }, + { 32, "audmscm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 11, 0, 0, &root1_gate_lock }, + { 33, "vdifm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 12, 0, 0, &root1_gate_lock }, + { 34, "gnssm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 13, 0, 0, &root1_gate_lock }, + { 35, "mediam_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 14, 0, 0, &root1_gate_lock }, + { 36, "ddrm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 15, 0, 0, &root1_gate_lock }, + { 37, "cpum_tpiu", "tpiu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 16, 0, 0, &root1_gate_lock }, + { 38, "gpum_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 17, 0, 0, &root1_gate_lock }, + { 39, "gnssm_rgmii", "rgmii_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 20, 0, 0, &root1_gate_lock }, + { 40, "mediam_vdec", "vdec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 21, 0, 0, &root1_gate_lock }, + { 41, "gpum_sdr", "sdr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 22, 0, 0, &root1_gate_lock }, + { 42, "vdifm_deint", "deint_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 23, 0, 0, &root1_gate_lock }, + { 43, "gnssm_can", "can_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 26, 0, 0, &root1_gate_lock }, + { 44, "mediam_usb", "usb_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 28, 0, 0, &root1_gate_lock }, + { 45, "gnssm_gmac", "gmac_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 29, 0, 0, &root1_gate_lock }, + { 46, "cvd_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 0, CLK_UNIT_NOC_CLOCK, 4, &leaf1_gate_lock }, + { 47, "timer_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 1, 0, 0, &leaf1_gate_lock }, + { 48, "pulse_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 2, 0, 0, &leaf1_gate_lock }, + { 49, "tsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 3, 0, 0, &leaf1_gate_lock }, + { 50, "tsc_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 21, 0, 0, &leaf1_gate_lock }, + { 51, "ioctop_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 4, 0, 0, &leaf1_gate_lock }, + { 52, "rsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 5, 0, 0, &leaf1_gate_lock }, + { 53, "dvm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 6, CLK_UNIT_NOC_SOCKET, 7, &leaf1_gate_lock }, + { 54, "lvds_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 7, CLK_UNIT_NOC_SOCKET, 8, &leaf1_gate_lock }, + { 55, "kas_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 8, CLK_UNIT_NOC_CLOCK, 2, &leaf1_gate_lock }, + { 56, "ac97_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 9, 0, 0, &leaf1_gate_lock }, + { 57, "usp0_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 10, CLK_UNIT_NOC_SOCKET, 4, &leaf1_gate_lock }, + { 58, "usp1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 11, CLK_UNIT_NOC_SOCKET, 5, &leaf1_gate_lock }, + { 59, "usp2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 12, CLK_UNIT_NOC_SOCKET, 6, &leaf1_gate_lock }, + { 60, "dmac2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 13, CLK_UNIT_NOC_SOCKET, 1, &leaf1_gate_lock }, + { 61, "dmac3_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 14, CLK_UNIT_NOC_SOCKET, 2, &leaf1_gate_lock }, + { 62, "audioif_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 15, CLK_UNIT_NOC_SOCKET, 0, &leaf1_gate_lock }, + { 63, "i2s1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 17, CLK_UNIT_NOC_CLOCK, 2, &leaf1_gate_lock }, + { 64, "thaudmscm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 22, 0, 0, &leaf1_gate_lock }, + { 65, "analogtest_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 23, 0, 0, &leaf1_gate_lock }, + { 66, "sys2pci_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 0, CLK_UNIT_NOC_CLOCK, 20, &leaf2_gate_lock }, + { 67, "pciarb_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 1, 0, 0, &leaf2_gate_lock }, + { 68, "pcicopy_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 2, 0, 0, &leaf2_gate_lock }, + { 69, "rom_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 3, 0, 0, &leaf2_gate_lock }, + { 70, "sdio23_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 4, 0, 0, &leaf2_gate_lock }, + { 71, "sdio45_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 5, 0, 0, &leaf2_gate_lock }, + { 72, "sdio67_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 6, 0, 0, &leaf2_gate_lock }, + { 73, "vip1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 7, 0, 0, &leaf2_gate_lock }, + { 74, "vip1_vip", "vdifm_vip", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 16, CLK_UNIT_NOC_CLOCK, 21, &leaf2_gate_lock }, + { 75, "sdio23_sdphy23", "vdifm_sdphy23", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 8, 0, 0, &leaf2_gate_lock }, + { 76, "sdio45_sdphy45", "vdifm_sdphy45", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 9, 0, 0, &leaf2_gate_lock }, + { 77, "sdio67_sdphy67", "vdifm_sdphy67", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 10, 0, 0, &leaf2_gate_lock }, + { 78, "vpp0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 11, CLK_UNIT_NOC_CLOCK, 22, &leaf2_gate_lock }, + { 79, "lcd0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 12, CLK_UNIT_NOC_CLOCK, 18, &leaf2_gate_lock }, + { 80, "vpp1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 13, CLK_UNIT_NOC_CLOCK, 23, &leaf2_gate_lock }, + { 81, "lcd1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 14, CLK_UNIT_NOC_CLOCK, 19, &leaf2_gate_lock }, + { 82, "dcu_deint", "vdifm_deint", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 15, CLK_UNIT_NOC_CLOCK, 17, &leaf2_gate_lock }, + { 83, "vdifm_dapa_r_nocr", "vdifm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 17, 0, 0, &leaf2_gate_lock }, + { 84, "gpio1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 18, 0, 0, &leaf2_gate_lock }, + { 85, "thvdifm_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 19, 0, 0, &leaf2_gate_lock }, + { 86, "gmac_rgmii", "gnssm_rgmii", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 0, 0, 0, &leaf3_gate_lock }, + { 87, "gmac_gmac", "gnssm_gmac", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 1, CLK_UNIT_NOC_CLOCK, 10, &leaf3_gate_lock }, + { 88, "uart1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 2, CLK_UNIT_NOC_SOCKET, 14, &leaf3_gate_lock }, + { 89, "dmac0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 3, CLK_UNIT_NOC_SOCKET, 11, &leaf3_gate_lock }, + { 90, "uart0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 4, CLK_UNIT_NOC_SOCKET, 13, &leaf3_gate_lock }, + { 91, "uart2_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 5, CLK_UNIT_NOC_SOCKET, 15, &leaf3_gate_lock }, + { 92, "uart3_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 6, CLK_UNIT_NOC_SOCKET, 16, &leaf3_gate_lock }, + { 93, "uart4_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 7, CLK_UNIT_NOC_SOCKET, 17, &leaf3_gate_lock }, + { 94, "uart5_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 8, CLK_UNIT_NOC_SOCKET, 18, &leaf3_gate_lock }, + { 95, "spi1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 9, CLK_UNIT_NOC_SOCKET, 12, &leaf3_gate_lock }, + { 96, "gnss_gnss", "gnssm_gnss", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 10, 0, 0, &leaf3_gate_lock }, + { 97, "canbus1_can", "gnssm_can", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 12, CLK_UNIT_NOC_CLOCK, 7, &leaf3_gate_lock }, + { 98, "ccsec_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 15, CLK_UNIT_NOC_CLOCK, 9, &leaf3_gate_lock }, + { 99, "ccpub_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 16, CLK_UNIT_NOC_CLOCK, 8, &leaf3_gate_lock }, + { 100, "gnssm_dapa_r_nocr", "gnssm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 13, 0, 0, &leaf3_gate_lock }, + { 101, "thgnssm_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 14, 0, 0, &leaf3_gate_lock }, + { 102, "media_vdec", "mediam_vdec", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 0, CLK_UNIT_NOC_CLOCK, 3, &leaf4_gate_lock }, + { 103, "media_jpenc", "mediam_jpenc", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 1, CLK_UNIT_NOC_CLOCK, 1, &leaf4_gate_lock }, + { 104, "g2d_g2d", "mediam_g2d", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 2, CLK_UNIT_NOC_CLOCK, 12, &leaf4_gate_lock }, + { 105, "i2c0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 3, CLK_UNIT_NOC_SOCKET, 21, &leaf4_gate_lock }, + { 106, "i2c1_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 4, CLK_UNIT_NOC_SOCKET, 20, &leaf4_gate_lock }, + { 107, "gpio0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 5, CLK_UNIT_NOC_SOCKET, 19, &leaf4_gate_lock }, + { 108, "nand_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 6, 0, 0, &leaf4_gate_lock }, + { 109, "sdio01_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 7, 0, 0, &leaf4_gate_lock }, + { 110, "sys2pci2_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 8, CLK_UNIT_NOC_CLOCK, 13, &leaf4_gate_lock }, + { 111, "sdio01_sdphy01", "mediam_sdphy01", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 9, 0, 0, &leaf4_gate_lock }, + { 112, "nand_nand", "mediam_nand", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 10, CLK_UNIT_NOC_CLOCK, 14, &leaf4_gate_lock }, + { 113, "usb0_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 11, CLK_UNIT_NOC_CLOCK, 15, &leaf4_gate_lock }, + { 114, "usb1_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 12, CLK_UNIT_NOC_CLOCK, 16, &leaf4_gate_lock }, + { 115, "usbphy0_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 13, 0, 0, &leaf4_gate_lock }, + { 116, "usbphy1_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 14, 0, 0, &leaf4_gate_lock }, + { 117, "thmediam_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 15, 0, 0, &leaf4_gate_lock }, + { 118, "memc_mem", "mempll_clk1", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 0, 0, 0, &leaf5_gate_lock }, + { 119, "dapa_mem", "mempll_clk1", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 1, 0, 0, &leaf5_gate_lock }, + { 120, "nocddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 2, 0, 0, &leaf5_gate_lock }, + { 121, "thddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 3, 0, 0, &leaf5_gate_lock }, + { 122, "spram1_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 0, CLK_UNIT_NOC_SOCKET, 9, &leaf6_gate_lock }, + { 123, "spram2_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 1, CLK_UNIT_NOC_SOCKET, 10, &leaf6_gate_lock }, + { 124, "coresight_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 2, 0, 0, &leaf6_gate_lock }, + { 125, "coresight_tpiu", "cpum_tpiu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 3, 0, 0, &leaf6_gate_lock }, + { 126, "graphic_gpu", "gpum_gpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 0, CLK_UNIT_NOC_CLOCK, 0, &leaf7_gate_lock }, + { 127, "vss_sdr", "gpum_sdr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 1, CLK_UNIT_NOC_CLOCK, 11, &leaf7_gate_lock }, + { 128, "thgpum_nocr", "gpum_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 2, 0, 0, &leaf7_gate_lock }, + { 129, "a7ca_btss", "btm_btss", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 1, 0, 0, &leaf8_gate_lock }, + { 130, "dmac4_io", "a7ca_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 2, 0, 0, &leaf8_gate_lock }, + { 131, "uart6_io", "dmac4_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 3, 0, 0, &leaf8_gate_lock }, + { 132, "usp3_io", "dmac4_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 4, 0, 0, &leaf8_gate_lock }, + { 133, "a7ca_io", "noc_btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 5, 0, 0, &leaf8_gate_lock }, + { 134, "noc_btm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 6, 0, 0, &leaf8_gate_lock }, + { 135, "thbtm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 7, 0, 0, &leaf8_gate_lock }, + { 136, "btslow", "xinw_fixdiv_btslow", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 25, 0, 0, &root1_gate_lock }, + { 137, "a7ca_btslow", "btslow", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 0, 0, 0, &leaf8_gate_lock }, + { 138, "pwm_io", "io_mux", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 0, 0, 0, &leaf0_gate_lock }, + { 139, "pwm_xin", "xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 1, 0, 0, &leaf0_gate_lock }, + { 140, "pwm_xinw", "xinw", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 2, 0, 0, &leaf0_gate_lock }, + { 141, "thcgum_sys", "sys_mux", 0, SIRFSOC_CLKC_LEAF_CLK_EN0_SET, 3, 0, 0, &leaf0_gate_lock }, }; static struct clk *atlas7_clks[ARRAY_SIZE(unit_list) + ARRAY_SIZE(mux_list)]; @@ -1206,20 +1221,44 @@ static int unit_clk_enable(struct clk_hw *hw) spin_lock_irqsave(clk->lock, flags); clkc_writel(BIT(clk->bit), reg); + if (clk->type == CLK_UNIT_NOC_CLOCK) + clkc_writel(BIT(clk->idle_bit), SIRFSOC_NOC_CLK_IDLEREQ_CLR); + else if (clk->type == CLK_UNIT_NOC_SOCKET) + clkc_writel(BIT(clk->idle_bit), SIRFSOC_NOC_CLK_SLVRDY_SET); + spin_unlock_irqrestore(clk->lock, flags); return 0; } static void unit_clk_disable(struct clk_hw *hw) { - u32 reg; + u32 reg; + u32 i = 0; struct clk_unit *clk = to_unitclk(hw); unsigned long flags; reg = clk->regofs + SIRFSOC_CLKC_ROOT_CLK_EN0_CLR - SIRFSOC_CLKC_ROOT_CLK_EN0_SET; - spin_lock_irqsave(clk->lock, flags); + if (clk->type == CLK_UNIT_NOC_CLOCK) { + clkc_writel(BIT(clk->idle_bit), SIRFSOC_NOC_CLK_IDLEREQ_SET); + while (!(clkc_readl(SIRFSOC_NOC_CLK_IDLE_STATUS) & + BIT(clk->idle_bit)) && (i++ < 100)) { + cpu_relax(); + udelay(10); + } + + if (i == 100) { + pr_err("unit NoC Clock disconnect Error:timeout\n"); + /*once timeout, undo idlereq by CLR*/ + clkc_writel(BIT(clk->idle_bit), SIRFSOC_NOC_CLK_IDLEREQ_CLR); + goto err; + } + + } else if (clk->type == CLK_UNIT_NOC_SOCKET) + clkc_writel(BIT(clk->idle_bit), SIRFSOC_NOC_CLK_SLVRDY_CLR); + clkc_writel(BIT(clk->bit), reg); +err: spin_unlock_irqrestore(clk->lock, flags); } @@ -1232,7 +1271,7 @@ static const struct clk_ops unit_clk_ops = { static struct clk * __init atlas7_unit_clk_register(struct device *dev, const char *name, const char * const parent_name, unsigned long flags, - u32 regofs, u8 bit, spinlock_t *lock) + u32 regofs, u8 bit, u32 type, u8 idle_bit, spinlock_t *lock) { struct clk *clk; struct clk_unit *unit; @@ -1251,6 +1290,9 @@ atlas7_unit_clk_register(struct device *dev, const char *name, unit->hw.init = &init; unit->regofs = regofs; unit->bit = bit; + + unit->type = type; + unit->idle_bit = idle_bit; unit->lock = lock; clk = clk_register(dev, &unit->hw); @@ -1624,7 +1666,7 @@ static void __init atlas7_clk_init(struct device_node *np) for (i = 0; i < ARRAY_SIZE(unit_list); i++) { unit = &unit_list[i]; atlas7_clks[i] = atlas7_unit_clk_register(NULL, unit->unit_name, unit->parent_name, - unit->flags, unit->regofs, unit->bit, unit->lock); + unit->flags, unit->regofs, unit->bit, unit->type, unit->idle_bit, unit->lock); BUG_ON(!atlas7_clks[i]); } diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index bd355ee33766..24d99594c0b3 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -268,6 +268,7 @@ static void __init st_of_flexgen_setup(struct device_node *np) int num_parents, i; spinlock_t *rlock = NULL; unsigned long flex_flags = 0; + int ret; pnode = of_get_parent(np); if (!pnode) @@ -285,13 +286,13 @@ static void __init st_of_flexgen_setup(struct device_node *np) if (!clk_data) goto err; - clk_data->clk_num = of_property_count_strings(np , - "clock-output-names"); - if (clk_data->clk_num <= 0) { + ret = of_property_count_strings(np, "clock-output-names"); + if (ret <= 0) { pr_err("%s: Failed to get number of output clocks (%d)", __func__, clk_data->clk_num); goto err; } + clk_data->clk_num = ret; clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), GFP_KERNEL); diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index 83ccf142ff2a..576cd0354d48 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -307,7 +307,7 @@ static const struct clkgen_quadfs_data st_fs660c32_F_416 = { .get_rate = clk_fs660c32_dig_get_rate, }; -static const struct clkgen_quadfs_data st_fs660c32_C_407 = { +static const struct clkgen_quadfs_data st_fs660c32_C = { .nrst_present = true, .nrst = { CLKGEN_FIELD(0x2f0, 0x1, 0), CLKGEN_FIELD(0x2f0, 0x1, 1), @@ -350,7 +350,7 @@ static const struct clkgen_quadfs_data st_fs660c32_C_407 = { .get_rate = clk_fs660c32_dig_get_rate, }; -static const struct clkgen_quadfs_data st_fs660c32_D_407 = { +static const struct clkgen_quadfs_data st_fs660c32_D = { .nrst_present = true, .nrst = { CLKGEN_FIELD(0x2a0, 0x1, 0), CLKGEN_FIELD(0x2a0, 0x1, 1), @@ -1077,11 +1077,11 @@ static const struct of_device_id quadfs_of_match[] = { }, { .compatible = "st,stih407-quadfs660-C", - .data = &st_fs660c32_C_407 + .data = &st_fs660c32_C }, { .compatible = "st,stih407-quadfs660-D", - .data = &st_fs660c32_D_407 + .data = &st_fs660c32_D }, {} }; diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index 4f7f6c00b219..5dc5ce217960 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -17,6 +17,7 @@ #include <linux/of_address.h> #include <linux/clk.h> #include <linux/clk-provider.h> +#include "clkgen.h" static DEFINE_SPINLOCK(clkgena_divmux_lock); static DEFINE_SPINLOCK(clkgenf_lock); @@ -576,6 +577,7 @@ 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, @@ -586,6 +588,7 @@ static struct clkgen_mux_data stih407_a9_mux_data = { .offset = 0x1a4, .shift = 0, .width = 2, + .lock = &clkgen_a9_lock, }; static const struct of_device_id mux_of_match[] = { diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 47a38a994cac..38f6f3a9098e 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -18,10 +18,12 @@ #include <linux/of_address.h> #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/iopoll.h> #include "clkgen.h" static DEFINE_SPINLOCK(clkgena_c32_odf_lock); +DEFINE_SPINLOCK(clkgen_a9_lock); /* * Common PLL configuration register bits for PLL800 and PLL1600 C65 @@ -38,30 +40,46 @@ static DEFINE_SPINLOCK(clkgena_c32_odf_lock); #define C32_IDF_MASK (0x7) #define C32_ODF_MASK (0x3f) #define C32_LDF_MASK (0x7f) +#define C32_CP_MASK (0x1f) #define C32_MAX_ODFS (4) +/* + * PLL configuration register bits for PLL4600 C28 + */ +#define C28_NDIV_MASK (0xff) +#define C28_IDF_MASK (0x7) +#define C28_ODF_MASK (0x3f) + struct clkgen_pll_data { struct clkgen_field pdn_status; + struct clkgen_field pdn_ctrl; struct clkgen_field locked_status; struct clkgen_field mdiv; struct clkgen_field ndiv; struct clkgen_field pdiv; struct clkgen_field idf; struct clkgen_field ldf; + struct clkgen_field cp; unsigned int num_odfs; struct clkgen_field odf[C32_MAX_ODFS]; struct clkgen_field odf_gate[C32_MAX_ODFS]; + bool switch2pll_en; + struct clkgen_field switch2pll; + spinlock_t *lock; 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), @@ -70,6 +88,7 @@ static const struct clkgen_pll_data st_pll1600c65_ax = { 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), @@ -79,6 +98,7 @@ static const struct clkgen_pll_data st_pll800c65_ax = { 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), @@ -96,6 +116,7 @@ static const struct clkgen_pll_data st_pll3200c32_a1x_0 = { 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), @@ -114,6 +135,7 @@ static const struct clkgen_pll_data st_pll3200c32_a1x_1 = { /* 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), @@ -125,6 +147,7 @@ static const struct clkgen_pll_data st_pll3200c32_a9_415 = { 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), @@ -137,7 +160,8 @@ static const struct clkgen_pll_data st_pll3200c32_ddr_415 = { }; static const struct clkgen_pll_data st_pll1200c32_gpu_415 = { - .pdn_status = CLKGEN_FIELD(0x144, 0x1, 3), + .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), @@ -149,6 +173,7 @@ static const struct clkgen_pll_data st_pll1200c32_gpu_415 = { /* 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), @@ -160,6 +185,7 @@ static const struct clkgen_pll_data st_pll3200c32_a9_416 = { 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), @@ -173,6 +199,7 @@ static const struct clkgen_pll_data st_pll3200c32_ddr_416 = { 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), @@ -184,6 +211,7 @@ static const struct clkgen_pll_data st_pll1200c32_gpu_416 = { static const struct clkgen_pll_data st_pll3200c32_407_a0 = { /* 407 A0 */ .pdn_status = CLKGEN_FIELD(0x2a0, 0x1, 8), + .pdn_ctrl = CLKGEN_FIELD(0x2a0, 0x1, 8), .locked_status = CLKGEN_FIELD(0x2a0, 0x1, 24), .ndiv = CLKGEN_FIELD(0x2a4, C32_NDIV_MASK, 16), .idf = CLKGEN_FIELD(0x2a4, C32_IDF_MASK, 0x0), @@ -193,9 +221,10 @@ static const struct clkgen_pll_data st_pll3200c32_407_a0 = { .ops = &stm_pll3200c32_ops, }; -static const struct clkgen_pll_data st_pll3200c32_407_c0_0 = { +static const struct clkgen_pll_data st_pll3200c32_cx_0 = { /* 407 C0 PLL0 */ .pdn_status = CLKGEN_FIELD(0x2a0, 0x1, 8), + .pdn_ctrl = CLKGEN_FIELD(0x2a0, 0x1, 8), .locked_status = CLKGEN_FIELD(0x2a0, 0x1, 24), .ndiv = CLKGEN_FIELD(0x2a4, C32_NDIV_MASK, 16), .idf = CLKGEN_FIELD(0x2a4, C32_IDF_MASK, 0x0), @@ -205,9 +234,10 @@ static const struct clkgen_pll_data st_pll3200c32_407_c0_0 = { .ops = &stm_pll3200c32_ops, }; -static const struct clkgen_pll_data st_pll3200c32_407_c0_1 = { +static const struct clkgen_pll_data st_pll3200c32_cx_1 = { /* 407 C0 PLL1 */ .pdn_status = CLKGEN_FIELD(0x2c8, 0x1, 8), + .pdn_ctrl = CLKGEN_FIELD(0x2c8, 0x1, 8), .locked_status = CLKGEN_FIELD(0x2c8, 0x1, 24), .ndiv = CLKGEN_FIELD(0x2cc, C32_NDIV_MASK, 16), .idf = CLKGEN_FIELD(0x2cc, C32_IDF_MASK, 0x0), @@ -220,13 +250,34 @@ static const struct clkgen_pll_data st_pll3200c32_407_c0_1 = { static const struct clkgen_pll_data st_pll3200c32_407_a9 = { /* 407 A9 */ .pdn_status = CLKGEN_FIELD(0x1a8, 0x1, 0), + .pdn_ctrl = CLKGEN_FIELD(0x1a8, 0x1, 0), .locked_status = CLKGEN_FIELD(0x87c, 0x1, 0), .ndiv = CLKGEN_FIELD(0x1b0, C32_NDIV_MASK, 0), .idf = CLKGEN_FIELD(0x1a8, C32_IDF_MASK, 25), .num_odfs = 1, .odf = { CLKGEN_FIELD(0x1b0, C32_ODF_MASK, 8) }, .odf_gate = { CLKGEN_FIELD(0x1ac, 0x1, 28) }, - .ops = &stm_pll3200c32_ops, + .switch2pll_en = true, + .cp = CLKGEN_FIELD(0x1a8, C32_CP_MASK, 1), + .switch2pll = CLKGEN_FIELD(0x1a4, 0x1, 1), + .lock = &clkgen_a9_lock, + .ops = &stm_pll3200c32_a9_ops, +}; + +static struct clkgen_pll_data st_pll4600c28_418_a9 = { + /* 418 A9 */ + .pdn_status = CLKGEN_FIELD(0x1a8, 0x1, 0), + .pdn_ctrl = CLKGEN_FIELD(0x1a8, 0x1, 0), + .locked_status = CLKGEN_FIELD(0x87c, 0x1, 0), + .ndiv = CLKGEN_FIELD(0x1b0, C28_NDIV_MASK, 0), + .idf = CLKGEN_FIELD(0x1a8, C28_IDF_MASK, 25), + .num_odfs = 1, + .odf = { CLKGEN_FIELD(0x1b0, C28_ODF_MASK, 8) }, + .odf_gate = { CLKGEN_FIELD(0x1ac, 0x1, 28) }, + .switch2pll_en = true, + .switch2pll = CLKGEN_FIELD(0x1a4, 0x1, 1), + .lock = &clkgen_a9_lock, + .ops = &stm_pll4600c28_ops, }; /** @@ -252,10 +303,26 @@ struct clkgen_pll { struct clk_hw hw; struct clkgen_pll_data *data; void __iomem *regs_base; + spinlock_t *lock; + + u32 ndiv; + u32 idf; + u32 odf; + u32 cp; }; #define to_clkgen_pll(_hw) container_of(_hw, struct clkgen_pll, hw) +struct stm_pll { + unsigned long mdiv; + unsigned long ndiv; + unsigned long pdiv; + unsigned long odf; + unsigned long idf; + unsigned long ldf; + unsigned long cp; +}; + static int clkgen_pll_is_locked(struct clk_hw *hw) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -271,6 +338,78 @@ static int clkgen_pll_is_enabled(struct clk_hw *hw) return !poweroff; } +static int __clkgen_pll_enable(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + void __iomem *base = pll->regs_base; + struct clkgen_field *field = &pll->data->locked_status; + int ret = 0; + u32 reg; + + if (clkgen_pll_is_enabled(hw)) + return 0; + + CLKGEN_WRITE(pll, pdn_ctrl, 0); + + ret = readl_relaxed_poll_timeout(base + field->offset, reg, + !!((reg >> field->shift) & field->mask), 0, 10000); + + if (!ret) { + if (pll->data->switch2pll_en) + CLKGEN_WRITE(pll, switch2pll, 0); + + pr_debug("%s:%s enabled\n", __clk_get_name(hw->clk), __func__); + } + + return ret; +} + +static int clkgen_pll_enable(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long flags = 0; + int ret = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + ret = __clkgen_pll_enable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return ret; +} + +static void __clkgen_pll_disable(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + + if (!clkgen_pll_is_enabled(hw)) + return; + + if (pll->data->switch2pll_en) + CLKGEN_WRITE(pll, switch2pll, 1); + + CLKGEN_WRITE(pll, pdn_ctrl, 1); + + pr_debug("%s:%s disabled\n", __clk_get_name(hw->clk), __func__); +} + +static void clkgen_pll_disable(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + __clkgen_pll_disable(hw); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + static unsigned long recalc_stm_pll800c65(struct clk_hw *hw, unsigned long parent_rate) { @@ -322,6 +461,67 @@ static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, return rate; } +static int clk_pll3200c32_get_params(unsigned long input, unsigned long output, + struct stm_pll *pll) +{ + unsigned long i, n; + unsigned long deviation = ~0; + unsigned long new_freq; + long new_deviation; + /* Charge pump table: highest ndiv value for cp=6 to 25 */ + static const unsigned char cp_table[] = { + 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, + 128, 136, 144, 152, 160, 168, 176, 184, 192 + }; + + /* Output clock range: 800Mhz to 1600Mhz */ + if (output < 800000000 || output > 1600000000) + return -EINVAL; + + input /= 1000; + output /= 1000; + + for (i = 1; i <= 7 && deviation; i++) { + n = i * output / (2 * input); + + /* Checks */ + if (n < 8) + continue; + if (n > 200) + break; + + new_freq = (input * 2 * n) / i; + + new_deviation = abs(new_freq - output); + + if (!new_deviation || new_deviation < deviation) { + pll->idf = i; + pll->ndiv = n; + deviation = new_deviation; + } + } + + if (deviation == ~0) /* No solution found */ + return -EINVAL; + + /* Computing recommended charge pump value */ + for (pll->cp = 6; pll->ndiv > cp_table[pll->cp-6]; (pll->cp)++) + ; + + return 0; +} + +static int clk_pll3200c32_get_rate(unsigned long input, struct stm_pll *pll, + unsigned long *rate) +{ + if (!pll->idf) + pll->idf = 1; + + *rate = ((2 * (input / 1000) * pll->ndiv) / pll->idf) * 1000; + + return 0; +} + static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, unsigned long parent_rate) { @@ -344,6 +544,70 @@ static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, return rate; } +static long round_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct stm_pll params; + + if (!clk_pll3200c32_get_params(*prate, rate, ¶ms)) + clk_pll3200c32_get_rate(*prate, ¶ms, &rate); + else { + pr_debug("%s: %s rate %ld Invalid\n", __func__, + __clk_get_name(hw->clk), rate); + return 0; + } + + pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", + __func__, __clk_get_name(hw->clk), + rate, (unsigned int)params.ndiv, + (unsigned int)params.idf); + + return rate; +} + +static int set_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + struct stm_pll params; + long hwrate = 0; + unsigned long flags = 0; + + if (!rate || !parent_rate) + return -EINVAL; + + if (!clk_pll3200c32_get_params(parent_rate, rate, ¶ms)) + clk_pll3200c32_get_rate(parent_rate, ¶ms, &hwrate); + + pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", + __func__, __clk_get_name(hw->clk), + hwrate, (unsigned int)params.ndiv, + (unsigned int)params.idf); + + if (!hwrate) + return -EINVAL; + + pll->ndiv = params.ndiv; + pll->idf = params.idf; + pll->cp = params.cp; + + __clkgen_pll_disable(hw); + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + CLKGEN_WRITE(pll, ndiv, pll->ndiv); + CLKGEN_WRITE(pll, idf, pll->idf); + CLKGEN_WRITE(pll, cp, pll->cp); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + __clkgen_pll_enable(hw); + + return 0; +} + static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, unsigned long parent_rate) { @@ -371,30 +635,213 @@ static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, return rate; } +/* PLL output structure + * FVCO >> /2 >> FVCOBY2 (no output) + * |> Divider (ODF) >> PHI + * + * FVCOby2 output = (input * 2 * NDIV) / IDF (assuming FRAC_CONTROL==L) + * + * Rules: + * 4Mhz <= INFF input <= 350Mhz + * 4Mhz <= INFIN (INFF / IDF) <= 50Mhz + * 19.05Mhz <= FVCOby2 output (PHI w ODF=1) <= 3000Mhz + * 1 <= i (register/dec value for IDF) <= 7 + * 8 <= n (register/dec value for NDIV) <= 246 + */ + +static int clk_pll4600c28_get_params(unsigned long input, unsigned long output, + struct stm_pll *pll) +{ + + unsigned long i, infin, n; + unsigned long deviation = ~0; + unsigned long new_freq, new_deviation; + + /* Output clock range: 19Mhz to 3000Mhz */ + if (output < 19000000 || output > 3000000000u) + return -EINVAL; + + /* For better jitter, IDF should be smallest and NDIV must be maximum */ + for (i = 1; i <= 7 && deviation; i++) { + /* INFIN checks */ + infin = input / i; + if (infin < 4000000 || infin > 50000000) + continue; /* Invalid case */ + + n = output / (infin * 2); + if (n < 8 || n > 246) + continue; /* Invalid case */ + if (n < 246) + n++; /* To work around 'y' when n=x.y */ + + for (; n >= 8 && deviation; n--) { + new_freq = infin * 2 * n; + if (new_freq < output) + break; /* Optimization: shorting loop */ + + new_deviation = new_freq - output; + if (!new_deviation || new_deviation < deviation) { + pll->idf = i; + pll->ndiv = n; + deviation = new_deviation; + } + } + } + + if (deviation == ~0) /* No solution found */ + return -EINVAL; + + return 0; +} + +static int clk_pll4600c28_get_rate(unsigned long input, struct stm_pll *pll, + unsigned long *rate) +{ + if (!pll->idf) + pll->idf = 1; + + *rate = (input / pll->idf) * 2 * pll->ndiv; + + return 0; +} + +static unsigned long recalc_stm_pll4600c28(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + struct stm_pll params; + unsigned long rate; + + if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) + return 0; + + params.ndiv = CLKGEN_READ(pll, ndiv); + params.idf = CLKGEN_READ(pll, idf); + + clk_pll4600c28_get_rate(parent_rate, ¶ms, &rate); + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; +} + +static long round_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct stm_pll params; + + if (!clk_pll4600c28_get_params(*prate, rate, ¶ms)) { + clk_pll4600c28_get_rate(*prate, ¶ms, &rate); + } else { + pr_debug("%s: %s rate %ld Invalid\n", __func__, + __clk_get_name(hw->clk), rate); + return 0; + } + + pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", + __func__, __clk_get_name(hw->clk), + rate, (unsigned int)params.ndiv, + (unsigned int)params.idf); + + return rate; +} + +static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + struct stm_pll params; + long hwrate; + unsigned long flags = 0; + + if (!rate || !parent_rate) + return -EINVAL; + + if (!clk_pll4600c28_get_params(parent_rate, rate, ¶ms)) { + clk_pll4600c28_get_rate(parent_rate, ¶ms, &hwrate); + } else { + pr_debug("%s: %s rate %ld Invalid\n", __func__, + __clk_get_name(hw->clk), rate); + return -EINVAL; + } + + pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", + __func__, __clk_get_name(hw->clk), + hwrate, (unsigned int)params.ndiv, + (unsigned int)params.idf); + + if (!hwrate) + return -EINVAL; + + pll->ndiv = params.ndiv; + pll->idf = params.idf; + + __clkgen_pll_disable(hw); + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + CLKGEN_WRITE(pll, ndiv, pll->ndiv); + CLKGEN_WRITE(pll, idf, pll->idf); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + __clkgen_pll_enable(hw); + + 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, .is_enabled = clkgen_pll_is_enabled, .recalc_rate = recalc_stm_pll3200c32, }; +static const struct clk_ops stm_pll3200c32_a9_ops = { + .enable = clkgen_pll_enable, + .disable = clkgen_pll_disable, + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll3200c32, + .round_rate = round_rate_stm_pll3200c32, + .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, + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll4600c28, + .round_rate = round_rate_stm_pll4600c28, + .set_rate = set_rate_stm_pll4600c28, +}; + static struct clk * __init clkgen_pll_register(const char *parent_name, struct clkgen_pll_data *pll_data, void __iomem *reg, - const char *clk_name) + const char *clk_name, spinlock_t *lock) { struct clkgen_pll *pll; struct clk *clk; @@ -414,6 +861,7 @@ static struct clk * __init clkgen_pll_register(const char *parent_name, pll->data = pll_data; pll->regs_base = reg; pll->hw.init = &init; + pll->lock = lock; clk = clk_register(NULL, &pll->hw); if (IS_ERR(clk)) { @@ -500,7 +948,7 @@ static void __init clkgena_c65_pll_setup(struct device_node *np) */ clk_data->clks[0] = clkgen_pll_register(parent_name, (struct clkgen_pll_data *) &st_pll1600c65_ax, - reg + CLKGENAx_PLL0_OFFSET, clk_name); + reg + CLKGENAx_PLL0_OFFSET, clk_name, NULL); if (IS_ERR(clk_data->clks[0])) goto err; @@ -529,7 +977,7 @@ static void __init clkgena_c65_pll_setup(struct device_node *np) */ clk_data->clks[2] = clkgen_pll_register(parent_name, (struct clkgen_pll_data *) &st_pll800c65_ax, - reg + CLKGENAx_PLL1_OFFSET, clk_name); + reg + CLKGENAx_PLL1_OFFSET, clk_name, NULL); if (IS_ERR(clk_data->clks[2])) goto err; @@ -556,7 +1004,7 @@ static struct clk * __init clkgen_odf_register(const char *parent_name, struct clk_gate *gate; struct clk_divider *div; - flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT; gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) @@ -624,17 +1072,21 @@ static const struct of_device_id c32_pll_of_match[] = { .data = &st_pll3200c32_407_a0, }, { - .compatible = "st,stih407-plls-c32-c0_0", - .data = &st_pll3200c32_407_c0_0, + .compatible = "st,plls-c32-cx_0", + .data = &st_pll3200c32_cx_0, }, { - .compatible = "st,stih407-plls-c32-c0_1", - .data = &st_pll3200c32_407_c0_1, + .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, + }, {} }; @@ -664,7 +1116,8 @@ static void __init clkgen_c32_pll_setup(struct device_node *np) if (!pll_base) return; - clk = clkgen_pll_register(parent_name, data, pll_base, np->name); + clk = clkgen_pll_register(parent_name, data, pll_base, np->name, + data->lock); if (IS_ERR(clk)) return; @@ -753,7 +1206,7 @@ static void __init clkgengpu_c32_pll_setup(struct device_node *np) /* * PLL 1200MHz output */ - clk = clkgen_pll_register(parent_name, data, reg, clk_name); + clk = clkgen_pll_register(parent_name, data, reg, clk_name, data->lock); if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); diff --git a/drivers/clk/st/clkgen.h b/drivers/clk/st/clkgen.h index 35c863295268..f7ec2d9139d6 100644 --- a/drivers/clk/st/clkgen.h +++ b/drivers/clk/st/clkgen.h @@ -9,6 +9,8 @@ Copyright (C) 2014 STMicroelectronics #ifndef __CLKGEN_INFO_H #define __CLKGEN_INFO_H +extern spinlock_t clkgen_a9_lock; + struct clkgen_field { unsigned int offset; unsigned int mask; diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index f5a35b82cc1a..cb4c299214ce 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -3,7 +3,10 @@ # obj-y += clk-sunxi.o clk-factors.o +obj-y += clk-a10-codec.o obj-y += clk-a10-hosc.o +obj-y += clk-a10-mod1.o +obj-y += clk-a10-pll2.o obj-y += clk-a20-gmac.o obj-y += clk-mod0.o obj-y += clk-simple-gates.o diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c new file mode 100644 index 000000000000..ac321d6a0df5 --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-codec.c @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López <emilio@elopez.com.ar> + * + * 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/of.h> +#include <linux/of_address.h> + +#define SUN4I_CODEC_GATE 31 + +static void __init sun4i_codec_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name, *parent_name; + void __iomem *reg; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, parent_name, + CLK_SET_RATE_PARENT, reg, + SUN4I_CODEC_GATE, 0, NULL); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(sun4i_codec, "allwinner,sun4i-a10-codec-clk", + sun4i_codec_clk_setup); diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c new file mode 100644 index 000000000000..e9d870de165c --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-mod1.c @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López <emilio@elopez.com.ar> + * + * 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/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +static DEFINE_SPINLOCK(mod1_lock); + +#define SUN4I_MOD1_ENABLE 31 +#define SUN4I_MOD1_MUX 16 +#define SUN4I_MOD1_MUX_WIDTH 2 +#define SUN4I_MOD1_MAX_PARENTS 4 + +static void __init sun4i_mod1_clk_setup(struct device_node *node) +{ + struct clk *clk; + struct clk_mux *mux; + struct clk_gate *gate; + const char *parents[4]; + const char *clk_name = node->name; + void __iomem *reg; + int i; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto err_unmap; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto err_free_mux; + + of_property_read_string(node, "clock-output-names", &clk_name); + i = of_clk_parent_fill(node, parents, SUN4I_MOD1_MAX_PARENTS); + + gate->reg = reg; + gate->bit_idx = SUN4I_MOD1_ENABLE; + gate->lock = &mod1_lock; + mux->reg = reg; + mux->shift = SUN4I_MOD1_MUX; + mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1; + mux->lock = &mod1_lock; + + clk = clk_register_composite(NULL, clk_name, parents, i, + &mux->hw, &clk_mux_ops, + NULL, NULL, + &gate->hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) + goto err_free_gate; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return; + +err_free_gate: + kfree(gate); +err_free_mux: + kfree(mux); +err_unmap: + iounmap(reg); +} +CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", + sun4i_mod1_clk_setup); diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c new file mode 100644 index 000000000000..5484c31ec568 --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-pll2.c @@ -0,0 +1,216 @@ +/* + * Copyright 2013 Emilio López + * Emilio López <emilio@elopez.com.ar> + * + * Copyright 2015 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. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/sun4i-a10-pll2.h> + +#define SUN4I_PLL2_ENABLE 31 + +#define SUN4I_PLL2_PRE_DIV_SHIFT 0 +#define SUN4I_PLL2_PRE_DIV_WIDTH 5 +#define SUN4I_PLL2_PRE_DIV_MASK GENMASK(SUN4I_PLL2_PRE_DIV_WIDTH - 1, 0) + +#define SUN4I_PLL2_N_SHIFT 8 +#define SUN4I_PLL2_N_WIDTH 7 +#define SUN4I_PLL2_N_MASK GENMASK(SUN4I_PLL2_N_WIDTH - 1, 0) + +#define SUN4I_PLL2_POST_DIV_SHIFT 26 +#define SUN4I_PLL2_POST_DIV_WIDTH 4 +#define SUN4I_PLL2_POST_DIV_MASK GENMASK(SUN4I_PLL2_POST_DIV_WIDTH - 1, 0) + +#define SUN4I_PLL2_POST_DIV_VALUE 4 + +#define SUN4I_PLL2_OUTPUTS 4 + +struct sun4i_pll2_data { + u32 post_div_offset; + u32 pre_div_flags; +}; + +static DEFINE_SPINLOCK(sun4i_a10_pll2_lock); + +static void __init sun4i_pll2_setup(struct device_node *node, + struct sun4i_pll2_data *data) +{ + const char *clk_name = node->name, *parent; + struct clk **clks, *base_clk, *prediv_clk; + struct clk_onecell_data *clk_data; + struct clk_multiplier *mult; + struct clk_gate *gate; + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err_unmap; + + clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(struct clk *), GFP_KERNEL); + if (!clks) + goto err_free_data; + + parent = of_clk_get_parent_name(node, 0); + prediv_clk = clk_register_divider(NULL, "pll2-prediv", + parent, 0, reg, + SUN4I_PLL2_PRE_DIV_SHIFT, + SUN4I_PLL2_PRE_DIV_WIDTH, + data->pre_div_flags, + &sun4i_a10_pll2_lock); + if (!prediv_clk) { + pr_err("Couldn't register the prediv clock\n"); + goto err_free_array; + } + + /* Setup the gate part of the PLL2 */ + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) + goto err_unregister_prediv; + + gate->reg = reg; + gate->bit_idx = SUN4I_PLL2_ENABLE; + gate->lock = &sun4i_a10_pll2_lock; + + /* Setup the multiplier part of the PLL2 */ + mult = kzalloc(sizeof(struct clk_multiplier), GFP_KERNEL); + if (!mult) + goto err_free_gate; + + mult->reg = reg; + mult->shift = SUN4I_PLL2_N_SHIFT; + mult->width = 7; + mult->flags = CLK_MULTIPLIER_ZERO_BYPASS | + CLK_MULTIPLIER_ROUND_CLOSEST; + mult->lock = &sun4i_a10_pll2_lock; + + parent = __clk_get_name(prediv_clk); + base_clk = clk_register_composite(NULL, "pll2-base", + &parent, 1, + NULL, NULL, + &mult->hw, &clk_multiplier_ops, + &gate->hw, &clk_gate_ops, + CLK_SET_RATE_PARENT); + if (!base_clk) { + pr_err("Couldn't register the base multiplier clock\n"); + goto err_free_multiplier; + } + + parent = __clk_get_name(base_clk); + + /* + * PLL2-1x + * + * This is supposed to have a post divider, but we won't need + * to use it, we just need to initialise it to 4, and use a + * fixed divider. + */ + val = readl(reg); + val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT); + val |= (SUN4I_PLL2_POST_DIV_VALUE - data->post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT; + writel(val, reg); + + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_1X, &clk_name); + clks[SUN4I_A10_PLL2_1X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 1, + SUN4I_PLL2_POST_DIV_VALUE); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_1X])); + + /* + * PLL2-2x + * + * This clock doesn't use the post divider, and really is just + * a fixed divider from the PLL2 base clock. + */ + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_2X, &clk_name); + clks[SUN4I_A10_PLL2_2X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 1, 2); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_2X])); + + /* PLL2-4x */ + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_4X, &clk_name); + clks[SUN4I_A10_PLL2_4X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 1, 1); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_4X])); + + /* PLL2-8x */ + of_property_read_string_index(node, "clock-output-names", + SUN4I_A10_PLL2_8X, &clk_name); + clks[SUN4I_A10_PLL2_8X] = clk_register_fixed_factor(NULL, clk_name, + parent, + CLK_SET_RATE_PARENT, + 2, 1); + WARN_ON(IS_ERR(clks[SUN4I_A10_PLL2_8X])); + + clk_data->clks = clks; + clk_data->clk_num = SUN4I_PLL2_OUTPUTS; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + return; + +err_free_multiplier: + kfree(mult); +err_free_gate: + kfree(gate); +err_unregister_prediv: + clk_unregister_divider(prediv_clk); +err_free_array: + kfree(clks); +err_free_data: + kfree(clk_data); +err_unmap: + iounmap(reg); +} + +static struct sun4i_pll2_data sun4i_a10_pll2_data = { + .pre_div_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, +}; + +static void __init sun4i_a10_pll2_setup(struct device_node *node) +{ + sun4i_pll2_setup(node, &sun4i_a10_pll2_data); +} + +CLK_OF_DECLARE(sun4i_a10_pll2, "allwinner,sun4i-a10-pll2-clk", + sun4i_a10_pll2_setup); + +static struct sun4i_pll2_data sun5i_a13_pll2_data = { + .post_div_offset = 1, +}; + +static void __init sun5i_a13_pll2_setup(struct device_node *node) +{ + sun4i_pll2_setup(node, &sun5i_a13_pll2_data); +} + +CLK_OF_DECLARE(sun5i_a13_pll2, "allwinner,sun5i-a13-pll2-clk", + sun5i_a13_pll2_setup); diff --git a/drivers/clk/sunxi/clk-simple-gates.c b/drivers/clk/sunxi/clk-simple-gates.c index 6ce91180da1b..0214c6548afd 100644 --- a/drivers/clk/sunxi/clk-simple-gates.c +++ b/drivers/clk/sunxi/clk-simple-gates.c @@ -128,6 +128,8 @@ CLK_OF_DECLARE(sun8i_a23_apb1, "allwinner,sun8i-a23-apb1-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun8i_a23_apb2, "allwinner,sun8i-a23-apb2-gates-clk", sunxi_simple_gates_init); +CLK_OF_DECLARE(sun8i_a33_ahb1, "allwinner,sun8i-a33-ahb1-gates-clk", + sunxi_simple_gates_init); CLK_OF_DECLARE(sun9i_a80_ahb0, "allwinner,sun9i-a80-ahb0-gates-clk", sunxi_simple_gates_init); CLK_OF_DECLARE(sun9i_a80_ahb1, "allwinner,sun9i-a80-ahb1-gates-clk", diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index 64f3e46d383c..23d042aabb4f 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -34,6 +34,7 @@ static const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = { { .compatible = "allwinner,sun8i-a23-apb0-gates-clk", .data = &sun8i_a23_apb0_gates }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, sun6i_a31_apb0_gates_clk_dt_ids); static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) { diff --git a/drivers/clk/sunxi/clk-sun6i-apb0.c b/drivers/clk/sunxi/clk-sun6i-apb0.c index 70763600aeae..e703e1895b76 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0.c @@ -61,6 +61,7 @@ static const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = { { .compatible = "allwinner,sun6i-a31-apb0-clk" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, sun6i_a31_apb0_clk_dt_ids); static struct platform_driver sun6i_a31_apb0_clk_driver = { .driver = { diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c index 806fd019c05d..20887686bdbe 100644 --- a/drivers/clk/sunxi/clk-sun6i-ar100.c +++ b/drivers/clk/sunxi/clk-sun6i-ar100.c @@ -219,6 +219,7 @@ static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = { { .compatible = "allwinner,sun6i-a31-ar100-clk" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, sun6i_a31_ar100_clk_dt_ids); static struct platform_driver sun6i_a31_ar100_clk_driver = { .driver = { diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c index 155d0022194f..7ae5d2c2cde1 100644 --- a/drivers/clk/sunxi/clk-sun8i-apb0.c +++ b/drivers/clk/sunxi/clk-sun8i-apb0.c @@ -52,6 +52,7 @@ static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { { .compatible = "allwinner,sun8i-a23-apb0-clk" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, sun8i_a23_apb0_clk_dt_ids); static struct platform_driver sun8i_a23_apb0_clk_driver = { .driver = { diff --git a/drivers/clk/sunxi/clk-sun9i-mmc.c b/drivers/clk/sunxi/clk-sun9i-mmc.c index 3436a948b796..a9b176139aca 100644 --- a/drivers/clk/sunxi/clk-sun9i-mmc.c +++ b/drivers/clk/sunxi/clk-sun9i-mmc.c @@ -204,6 +204,7 @@ static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids[] = { { .compatible = "allwinner,sun9i-a80-mmc-config-clk" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, sun9i_a80_mmc_config_clk_dt_ids); static struct platform_driver sun9i_a80_mmc_config_clk_driver = { .driver = { diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index c2ff859ee0e8..86a307b17eb0 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -469,56 +469,6 @@ static unsigned long dfll_scale_dvco_rate(int scale_bits, } /* - * Monitor control - */ - -/** - * dfll_calc_monitored_rate - convert DFLL_MONITOR_DATA_VAL rate into real freq - * @monitor_data: value read from the DFLL_MONITOR_DATA_VAL bitfield - * @ref_rate: DFLL reference clock rate - * - * Convert @monitor_data from DFLL_MONITOR_DATA_VAL units into cycles - * per second. Returns the converted value. - */ -static u64 dfll_calc_monitored_rate(u32 monitor_data, - unsigned long ref_rate) -{ - return monitor_data * (ref_rate / REF_CLK_CYC_PER_DVCO_SAMPLE); -} - -/** - * dfll_read_monitor_rate - return the DFLL's output rate from internal monitor - * @td: DFLL instance - * - * If the DFLL is enabled, return the last rate reported by the DFLL's - * internal monitoring hardware. This works in both open-loop and - * closed-loop mode, and takes the output scaler setting into account. - * Assumes that the monitor was programmed to monitor frequency before - * the sample period started. If the driver believes that the DFLL is - * currently uninitialized or disabled, it will return 0, since - * otherwise the DFLL monitor data register will return the last - * measured rate from when the DFLL was active. - */ -static u64 dfll_read_monitor_rate(struct tegra_dfll *td) -{ - u32 v, s; - u64 pre_scaler_rate, post_scaler_rate; - - if (!dfll_is_running(td)) - return 0; - - v = dfll_readl(td, DFLL_MONITOR_DATA); - v = (v & DFLL_MONITOR_DATA_VAL_MASK) >> DFLL_MONITOR_DATA_VAL_SHIFT; - pre_scaler_rate = dfll_calc_monitored_rate(v, td->ref_rate); - - s = dfll_readl(td, DFLL_FREQ_REQ); - s = (s & DFLL_FREQ_REQ_SCALE_MASK) >> DFLL_FREQ_REQ_SCALE_SHIFT; - post_scaler_rate = dfll_scale_dvco_rate(s, pre_scaler_rate); - - return post_scaler_rate; -} - -/* * DFLL mode switching */ @@ -682,11 +632,17 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate) struct dev_pm_opp *opp; int i, uv; + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); - if (IS_ERR(opp)) + if (IS_ERR(opp)) { + rcu_read_unlock(); return PTR_ERR(opp); + } uv = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + for (i = 0; i < td->i2c_lut_size; i++) { if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv) return i; @@ -1000,24 +956,25 @@ static unsigned long dfll_clk_recalc_rate(struct clk_hw *hw, return td->last_unrounded_rate; } -static long dfll_clk_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *parent_rate) +/* Must use determine_rate since it allows for rates exceeding 2^31-1 */ +static int dfll_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *clk_req) { struct tegra_dfll *td = clk_hw_to_dfll(hw); struct dfll_rate_req req; int ret; - ret = dfll_calculate_rate_request(td, &req, rate); + ret = dfll_calculate_rate_request(td, &req, clk_req->rate); if (ret) return ret; /* - * Don't return the rounded rate, since it doesn't really matter as + * Don't set the rounded rate, since it doesn't really matter as * the output rate will be voltage controlled anyway, and cpufreq * freaks out if any rounding happens. */ - return rate; + + return 0; } static int dfll_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1033,7 +990,7 @@ static const struct clk_ops dfll_clk_ops = { .enable = dfll_clk_enable, .disable = dfll_clk_disable, .recalc_rate = dfll_clk_recalc_rate, - .round_rate = dfll_clk_round_rate, + .determine_rate = dfll_clk_determine_rate, .set_rate = dfll_clk_set_rate, }; @@ -1095,6 +1052,55 @@ static void dfll_unregister_clk(struct tegra_dfll *td) */ #ifdef CONFIG_DEBUG_FS +/* + * Monitor control + */ + +/** + * dfll_calc_monitored_rate - convert DFLL_MONITOR_DATA_VAL rate into real freq + * @monitor_data: value read from the DFLL_MONITOR_DATA_VAL bitfield + * @ref_rate: DFLL reference clock rate + * + * Convert @monitor_data from DFLL_MONITOR_DATA_VAL units into cycles + * per second. Returns the converted value. + */ +static u64 dfll_calc_monitored_rate(u32 monitor_data, + unsigned long ref_rate) +{ + return monitor_data * (ref_rate / REF_CLK_CYC_PER_DVCO_SAMPLE); +} + +/** + * dfll_read_monitor_rate - return the DFLL's output rate from internal monitor + * @td: DFLL instance + * + * If the DFLL is enabled, return the last rate reported by the DFLL's + * internal monitoring hardware. This works in both open-loop and + * closed-loop mode, and takes the output scaler setting into account. + * Assumes that the monitor was programmed to monitor frequency before + * the sample period started. If the driver believes that the DFLL is + * currently uninitialized or disabled, it will return 0, since + * otherwise the DFLL monitor data register will return the last + * measured rate from when the DFLL was active. + */ +static u64 dfll_read_monitor_rate(struct tegra_dfll *td) +{ + u32 v, s; + u64 pre_scaler_rate, post_scaler_rate; + + if (!dfll_is_running(td)) + return 0; + + v = dfll_readl(td, DFLL_MONITOR_DATA); + v = (v & DFLL_MONITOR_DATA_VAL_MASK) >> DFLL_MONITOR_DATA_VAL_SHIFT; + pre_scaler_rate = dfll_calc_monitored_rate(v, td->ref_rate); + + s = dfll_readl(td, DFLL_FREQ_REQ); + s = (s & DFLL_FREQ_REQ_SCALE_MASK) >> DFLL_FREQ_REQ_SCALE_SHIFT; + post_scaler_rate = dfll_scale_dvco_rate(s, pre_scaler_rate); + + return post_scaler_rate; +} static int attr_enable_get(void *data, u64 *val) { diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 138a94b99b5b..e1fe8f35d45c 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -491,10 +491,8 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, for_each_child_of_node(np, node) { err = of_property_read_u32(node, "nvidia,ram-code", &node_ram_code); - if (err) { - of_node_put(node); + if (err) continue; - } /* * Store timings for all ram codes as we cannot read the diff --git a/drivers/clk/tegra/clk-tegra-audio.c b/drivers/clk/tegra/clk-tegra-audio.c index 11e3ad7ad7a3..e2bfa9b368f6 100644 --- a/drivers/clk/tegra/clk-tegra-audio.c +++ b/drivers/clk/tegra/clk-tegra-audio.c @@ -125,18 +125,29 @@ static struct tegra_audio2x_clk_initdata audio2x_clks[] = { void __init tegra_audio_clk_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, - struct tegra_clk_pll_params *pll_a_params) + struct tegra_audio_clk_info *audio_info, + unsigned int num_plls) { struct clk *clk; struct clk **dt_clk; int i; - /* PLLA */ - dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a, tegra_clks); - if (dt_clk) { - clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base, - pmc_base, 0, pll_a_params, NULL); - *dt_clk = clk; + if (!audio_info || num_plls < 1) { + pr_err("No audio data passed to tegra_audio_clk_init\n"); + WARN_ON(1); + return; + } + + for (i = 0; i < num_plls; i++) { + struct tegra_audio_clk_info *info = &audio_info[i]; + + dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks); + if (dt_clk) { + clk = tegra_clk_register_pll(info->name, info->parent, + clk_base, pmc_base, 0, info->pll_params, + NULL); + *dt_clk = clk; + } } /* PLLA_OUT0 */ diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index db5871519bf5..b7d03e9add97 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -933,6 +933,10 @@ static u32 mux_pllm_pllc2_c_c3_pllp_plla_idx[] = { [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, }; +static struct tegra_audio_clk_info tegra114_audio_plls[] = { + { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" }, +}; + static struct clk **clks; static unsigned long osc_freq; @@ -1481,7 +1485,9 @@ static void __init tegra114_clock_init(struct device_node *np) tegra114_fixed_clk_init(clk_base); tegra114_pll_init(clk_base, pmc_base); tegra114_periph_clk_init(clk_base, pmc_base); - tegra_audio_clk_init(clk_base, pmc_base, tegra114_clks, &pll_a_params); + tegra_audio_clk_init(clk_base, pmc_base, tegra114_clks, + tegra114_audio_plls, + ARRAY_SIZE(tegra114_audio_plls)); tegra_pmc_clk_init(pmc_base, tegra114_clks); tegra_super_clk_gen4_init(clk_base, pmc_base, tegra114_clks, &pll_x_params); diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 824d75883d2b..87975f7adddc 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1417,6 +1417,10 @@ static struct tegra_clk_init_table tegra132_init_table[] __initdata = { {TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0}, }; +static struct tegra_audio_clk_info tegra124_audio_plls[] = { + { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" }, +}; + /** * tegra124_clock_apply_init_table - initialize clocks on Tegra124 SoCs * @@ -1555,7 +1559,9 @@ static void __init tegra124_132_clock_init_pre(struct device_node *np) tegra_fixed_clk_init(tegra124_clks); tegra124_pll_init(clk_base, pmc_base); tegra124_periph_clk_init(clk_base, pmc_base); - tegra_audio_clk_init(clk_base, pmc_base, tegra124_clks, &pll_a_params); + tegra_audio_clk_init(clk_base, pmc_base, tegra124_clks, + tegra124_audio_plls, + ARRAY_SIZE(tegra124_audio_plls)); tegra_pmc_clk_init(pmc_base, tegra124_clks); /* For Tegra124 & Tegra132, PLLD is the only source for DSIA & DSIB */ diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index fad561a5896b..b90db615c29e 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1405,6 +1405,10 @@ static const struct of_device_id pmc_match[] __initconst = { {}, }; +static struct tegra_audio_clk_info tegra30_audio_plls[] = { + { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" }, +}; + static void __init tegra30_clock_init(struct device_node *np) { struct device_node *node; @@ -1442,7 +1446,9 @@ static void __init tegra30_clock_init(struct device_node *np) tegra30_pll_init(); tegra30_super_clk_init(); tegra30_periph_clk_init(); - tegra_audio_clk_init(clk_base, pmc_base, tegra30_clks, &pll_a_params); + tegra_audio_clk_init(clk_base, pmc_base, tegra30_clks, + tegra30_audio_plls, + ARRAY_SIZE(tegra30_audio_plls)); tegra_pmc_clk_init(pmc_base, tegra30_clks); tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX); diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 0621887e06f7..5d2678914160 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -157,7 +157,7 @@ struct div_nmp { }; /** - * struct clk_pll_params - PLL parameters + * struct tegra_clk_pll_params - PLL parameters * * @input_min: Minimum input frequency * @input_max: Maximum input frequency @@ -168,9 +168,45 @@ struct div_nmp { * @base_reg: PLL base reg offset * @misc_reg: PLL misc reg offset * @lock_reg: PLL lock reg offset - * @lock_bit_idx: Bit index for PLL lock status + * @lock_mask: Bitmask for PLL lock status * @lock_enable_bit_idx: Bit index to enable PLL lock + * @iddq_reg: PLL IDDQ register offset + * @iddq_bit_idx: Bit index to enable PLL IDDQ + * @aux_reg: AUX register offset + * @dyn_ramp_reg: Dynamic ramp control register offset + * @ext_misc_reg: Miscellaneous control register offsets + * @pmc_divnm_reg: n, m divider PMC override register offset (PLLM) + * @pmc_divp_reg: p divider PMC override register offset (PLLM) + * @flags: PLL flags + * @stepa_shift: Dynamic ramp step A field shift + * @stepb_shift: Dynamic ramp step B field shift * @lock_delay: Delay in us if PLL lock is not used + * @max_p: maximum value for the p divider + * @pdiv_tohw: mapping of p divider to register values + * @div_nmp: offsets and widths on n, m and p fields + * @freq_table: array of frequencies supported by PLL + * @fixed_rate: PLL rate if it is fixed + * + * Flags: + * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for + * PLL locking. If not set it will use lock_delay value to wait. + * TEGRA_PLL_HAS_CPCON - This flag indicates that CPCON value needs + * to be programmed to change output frequency of the PLL. + * TEGRA_PLL_SET_LFCON - This flag indicates that LFCON value needs + * to be programmed to change output frequency of the PLL. + * TEGRA_PLL_SET_DCCON - This flag indicates that DCCON value needs + * to be programmed to change output frequency of the PLL. + * TEGRA_PLLU - PLLU has inverted post divider. This flags indicated + * that it is PLLU and invert post divider value. + * TEGRA_PLLM - PLLM has additional override settings in PMC. This + * flag indicates that it is PLLM and use override settings. + * TEGRA_PLL_FIXED - We are not supposed to change output frequency + * of some plls. + * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. + * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the + * base register. + * TEGRA_PLL_BYPASS - PLL has bypass bit + * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring */ struct tegra_clk_pll_params { unsigned long input_min; @@ -203,38 +239,26 @@ struct tegra_clk_pll_params { unsigned long fixed_rate; }; +#define TEGRA_PLL_USE_LOCK BIT(0) +#define TEGRA_PLL_HAS_CPCON BIT(1) +#define TEGRA_PLL_SET_LFCON BIT(2) +#define TEGRA_PLL_SET_DCCON BIT(3) +#define TEGRA_PLLU BIT(4) +#define TEGRA_PLLM BIT(5) +#define TEGRA_PLL_FIXED BIT(6) +#define TEGRA_PLLE_CONFIGURE BIT(7) +#define TEGRA_PLL_LOCK_MISC BIT(8) +#define TEGRA_PLL_BYPASS BIT(9) +#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10) + /** * struct tegra_clk_pll - Tegra PLL clock * * @hw: handle between common and hardware-specifix interfaces * @clk_base: address of CAR controller * @pmc: address of PMC, required to read override bits - * @freq_table: array of frequencies supported by PLL - * @params: PLL parameters - * @flags: PLL flags - * @fixed_rate: PLL rate if it is fixed * @lock: register lock - * - * Flags: - * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for - * PLL locking. If not set it will use lock_delay value to wait. - * TEGRA_PLL_HAS_CPCON - This flag indicates that CPCON value needs - * to be programmed to change output frequency of the PLL. - * TEGRA_PLL_SET_LFCON - This flag indicates that LFCON value needs - * to be programmed to change output frequency of the PLL. - * TEGRA_PLL_SET_DCCON - This flag indicates that DCCON value needs - * to be programmed to change output frequency of the PLL. - * TEGRA_PLLU - PLLU has inverted post divider. This flags indicated - * that it is PLLU and invert post divider value. - * TEGRA_PLLM - PLLM has additional override settings in PMC. This - * flag indicates that it is PLLM and use override settings. - * TEGRA_PLL_FIXED - We are not supposed to change output frequency - * of some plls. - * TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling. - * TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the - * base register. - * TEGRA_PLL_BYPASS - PLL has bypass bit - * TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring + * @params: PLL parameters */ struct tegra_clk_pll { struct clk_hw hw; @@ -246,17 +270,20 @@ struct tegra_clk_pll { #define to_clk_pll(_hw) container_of(_hw, struct tegra_clk_pll, hw) -#define TEGRA_PLL_USE_LOCK BIT(0) -#define TEGRA_PLL_HAS_CPCON BIT(1) -#define TEGRA_PLL_SET_LFCON BIT(2) -#define TEGRA_PLL_SET_DCCON BIT(3) -#define TEGRA_PLLU BIT(4) -#define TEGRA_PLLM BIT(5) -#define TEGRA_PLL_FIXED BIT(6) -#define TEGRA_PLLE_CONFIGURE BIT(7) -#define TEGRA_PLL_LOCK_MISC BIT(8) -#define TEGRA_PLL_BYPASS BIT(9) -#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10) +/** + * struct tegra_audio_clk_info - Tegra Audio Clk Information + * + * @name: name for the audio pll + * @pll_params: pll_params for audio pll + * @clk_id: clk_ids for the audio pll + * @parent: name of the parent of the audio pll + */ +struct tegra_audio_clk_info { + char *name; + struct tegra_clk_pll_params *pll_params; + int clk_id; + char *parent; +}; extern const struct clk_ops tegra_clk_pll_ops; extern const struct clk_ops tegra_clk_plle_ops; @@ -610,7 +637,8 @@ void tegra_register_devclks(struct tegra_devclk *dev_clks, int num); void tegra_audio_clk_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, - struct tegra_clk_pll_params *pll_params); + struct tegra_audio_clk_info *audio_info, + unsigned int num_plls); void tegra_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c index 0204e0861134..69c74eec3a4b 100644 --- a/drivers/clk/tegra/cvb.c +++ b/drivers/clk/tegra/cvb.c @@ -78,13 +78,6 @@ static int build_opp_table(const struct cvb_table *d, if (!table->freq || (table->freq > max_freq)) break; - /* - * FIXME after clk_round_rate/clk_determine_rate prototypes - * have been updated - */ - if (table->freq & (1<<31)) - continue; - dfll_mv = get_cvb_voltage( speedo_value, d->speedo_scale, &table->coefficients); dfll_mv = round_cvb_voltage(dfll_mv, d->voltage_scale, align); diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index 676ee8f6d813..8831e1a05367 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -374,7 +374,6 @@ static struct ti_dt_clk omap3xxx_clks[] = { DT_CLK(NULL, "gpio2_ick", "gpio2_ick"), DT_CLK(NULL, "wdt3_ick", "wdt3_ick"), DT_CLK(NULL, "uart3_ick", "uart3_ick"), - DT_CLK(NULL, "uart4_ick", "uart4_ick"), DT_CLK(NULL, "gpt9_ick", "gpt9_ick"), DT_CLK(NULL, "gpt8_ick", "gpt8_ick"), DT_CLK(NULL, "gpt7_ick", "gpt7_ick"), @@ -519,6 +518,7 @@ static struct ti_dt_clk am35xx_clks[] = { static struct ti_dt_clk omap36xx_clks[] = { DT_CLK(NULL, "omap_192m_alwon_fck", "omap_192m_alwon_fck"), DT_CLK(NULL, "uart4_fck", "uart4_fck"), + DT_CLK(NULL, "uart4_ick", "uart4_ick"), { .node_name = NULL }, }; diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c index 9b5b289e6334..a911d7de3377 100644 --- a/drivers/clk/ti/clk-7xx.c +++ b/drivers/clk/ti/clk-7xx.c @@ -18,7 +18,6 @@ #include "clock.h" -#define DRA7_DPLL_ABE_DEFFREQ 180633600 #define DRA7_DPLL_GMAC_DEFFREQ 1000000000 #define DRA7_DPLL_USB_DEFFREQ 960000000 @@ -313,27 +312,12 @@ static struct ti_dt_clk dra7xx_clks[] = { int __init dra7xx_dt_clk_init(void) { int rc; - struct clk *abe_dpll_mux, *sys_clkin2, *dpll_ck, *hdcp_ck; + struct clk *dpll_ck, *hdcp_ck; ti_dt_clocks_register(dra7xx_clks); omap2_clk_disable_autoidle_all(); - abe_dpll_mux = clk_get_sys(NULL, "abe_dpll_sys_clk_mux"); - sys_clkin2 = clk_get_sys(NULL, "sys_clkin2"); - dpll_ck = clk_get_sys(NULL, "dpll_abe_ck"); - - rc = clk_set_parent(abe_dpll_mux, sys_clkin2); - if (!rc) - rc = clk_set_rate(dpll_ck, DRA7_DPLL_ABE_DEFFREQ); - if (rc) - pr_err("%s: failed to configure ABE DPLL!\n", __func__); - - dpll_ck = clk_get_sys(NULL, "dpll_abe_m2x2_ck"); - rc = clk_set_rate(dpll_ck, DRA7_DPLL_ABE_DEFFREQ * 2); - if (rc) - pr_err("%s: failed to configure ABE DPLL m2x2!\n", __func__); - dpll_ck = clk_get_sys(NULL, "dpll_gmac_ck"); rc = clk_set_rate(dpll_ck, DRA7_DPLL_GMAC_DEFFREQ); if (rc) diff --git a/drivers/clk/ti/clkt_dflt.c b/drivers/clk/ti/clkt_dflt.c index 90d7d8a21c49..1ddc288fce4e 100644 --- a/drivers/clk/ti/clkt_dflt.c +++ b/drivers/clk/ti/clkt_dflt.c @@ -222,7 +222,7 @@ int omap2_dflt_clk_enable(struct clk_hw *hw) } } - if (unlikely(!clk->enable_reg)) { + if (unlikely(IS_ERR(clk->enable_reg))) { pr_err("%s: %s missing enable_reg\n", __func__, clk_hw_get_name(hw)); ret = -EINVAL; @@ -264,7 +264,7 @@ void omap2_dflt_clk_disable(struct clk_hw *hw) u32 v; clk = to_clk_hw_omap(hw); - if (!clk->enable_reg) { + if (IS_ERR(clk->enable_reg)) { /* * 'independent' here refers to a clock which is not * controlled by its parent. diff --git a/drivers/clk/versatile/Kconfig b/drivers/clk/versatile/Kconfig index 1530c9352a76..fc50b6264bed 100644 --- a/drivers/clk/versatile/Kconfig +++ b/drivers/clk/versatile/Kconfig @@ -1,6 +1,6 @@ config COMMON_CLK_VERSATILE bool "Clock driver for ARM Reference designs" - depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 + depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 || COMPILE_TEST ---help--- Supports clocking on ARM Reference designs: - Integrator/AP and Integrator/CP |