diff options
Diffstat (limited to 'drivers/clk')
136 files changed, 12641 insertions, 1284 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9897f353bf1a..42f7120ca9ce 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -78,6 +78,23 @@ config COMMON_CLK_SI570 This driver supports Silicon Labs 570/571/598/599 programmable clock generators. +config COMMON_CLK_CDCE925 + tristate "Clock driver for TI CDCE925 devices" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help--- + This driver supports the TI CDCE925 programmable clock synthesizer. + The chip contains two PLLs with spread-spectrum clocking support and + five output dividers. The driver only supports the following setup, + and uses a fixed setting for the output muxes. + Y1 is derived from the input clock + Y2 and Y3 derive from PLL1 + Y4 and Y5 derive from PLL2 + Given a target output frequency, the driver will set the PLL and + divider to best approximate the desired output. + config COMMON_CLK_S2MPS11 tristate "Clock driver for S2MPS1X/S5M8767 MFD" depends on MFD_SEC_CORE @@ -150,11 +167,13 @@ config COMMON_CLK_CDCE706 ---help--- This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. +source "drivers/clk/bcm/Kconfig" +source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/qcom/Kconfig" endmenu -source "drivers/clk/bcm/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/samsung/Kconfig" +source "drivers/clk/tegra/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 8732e4c5bf3c..c4cf075a2320 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -38,6 +38,8 @@ 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_SI570) += clk-si570.o +obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o +obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o @@ -45,19 +47,20 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ -obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ +obj-$(CONFIG_ARCH_BCM) += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ -obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ -obj-$(CONFIG_ARCH_HIP04) += hisilicon/ -obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ +obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_MACH_INGENIC) += ingenic/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ +obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_ARCH_LPC18XX) += nxp/ obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index 59fa3cc96c9e..c2400456a044 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -614,7 +614,7 @@ void __init of_at91sam9x5_clk_main_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > 2) return; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index c1af80bcdf20..f98eafe9b12d 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -224,7 +224,7 @@ of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, const char *name = np->name; struct clk_master_characteristics *characteristics; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 86c8a073dcc3..8c86c0f7847a 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -237,7 +237,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc, const char *name; struct device_node *progclknp; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c index 2f13bd5246b5..98a84a865fe1 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -373,7 +373,7 @@ void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > 2) return; @@ -451,7 +451,7 @@ void __init of_at91sam9260_clk_slow_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents != 2) return; diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c index 144d47ecfe63..3817ea865ca2 100644 --- a/drivers/clk/at91/clk-smd.c +++ b/drivers/clk/at91/clk-smd.c @@ -150,7 +150,7 @@ void __init of_at91sam9x5_clk_smd_setup(struct device_node *np, const char *parent_names[SMD_SOURCE_MAX]; const char *name = np->name; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > SMD_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 0b7c3e8840ba..b0cbd2b1ff59 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -378,7 +378,7 @@ void __init of_at91sam9x5_clk_usb_setup(struct device_node *np, const char *parent_names[USB_SOURCE_MAX]; const char *name = np->name; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > USB_SOURCE_MAX) return; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 3f27d21fb729..39be2be82b0a 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -153,7 +153,7 @@ static int pmc_irq_domain_xlate(struct irq_domain *d, return 0; } -static struct irq_domain_ops pmc_irq_ops = { +static const struct irq_domain_ops pmc_irq_ops = { .map = pmc_irq_map, .xlate = pmc_irq_domain_xlate, }; diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 75506e53075b..88febf53b276 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -7,3 +7,12 @@ config CLK_BCM_KONA Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those in the BCM281xx and BCM21664 families. + +config COMMON_CLK_IPROC + bool "Broadcom iProc clock support" + depends on ARCH_BCM_IPROC + depends on COMMON_CLK + default ARCH_BCM_IPROC + help + Enable common clock framework support for Broadcom SoCs + based on the iProc architecture diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 6297d05a9a10..8a7a477862c7 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o 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_BCM_CYGNUS) += clk-cygnus.o diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c new file mode 100644 index 000000000000..316c60337661 --- /dev/null +++ b/drivers/clk/bcm/clk-cygnus.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clkdev.h> +#include <linux/of_address.h> +#include <linux/delay.h> + +#include <dt-bindings/clock/bcm-cygnus.h> +#include "clk-iproc.h" + +#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, } + +#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \ + .pwr_shift = ps, .iso_shift = is } + +#define sw_ctrl_val(o, s) { .offset = o, .shift = s, } + +#define asiu_div_val(o, es, hs, hw, ls, lw) \ + { .offset = o, .en_shift = es, .high_shift = hs, \ + .high_width = hw, .low_shift = ls, .low_width = lw } + +#define reset_val(o, rs, prs, kis, kiw, kps, kpw, kas, kaw) { .offset = o, \ + .reset_shift = rs, .p_reset_shift = prs, .ki_shift = kis, \ + .ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \ + .ka_width = kaw } + +#define vco_ctrl_val(uo, lo) { .u_offset = uo, .l_offset = lo } + +#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \ + .hold_shift = hs, .bypass_shift = bs } + +#define asiu_gate_val(o, es) { .offset = o, .en_shift = es } + +static void __init cygnus_armpll_init(struct device_node *node) +{ + iproc_armpll_setup(node); +} +CLK_OF_DECLARE(cygnus_armpll, "brcm,cygnus-armpll", cygnus_armpll_init); + +static const struct iproc_pll_ctrl genpll = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_PLL_NEEDS_SW_CFG, + .aon = aon_val(0x0, 2, 1, 0), + .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 3), + .sw_ctrl = sw_ctrl_val(0x10, 31), + .ndiv_int = reg_val(0x10, 20, 10), + .ndiv_frac = reg_val(0x10, 0, 20), + .pdiv = reg_val(0x14, 0, 4), + .vco_ctrl = vco_ctrl_val(0x18, 0x1c), + .status = reg_val(0x28, 12, 1), +}; + +static const struct iproc_clk_ctrl genpll_clk[] = { + [BCM_CYGNUS_GENPLL_AXI21_CLK] = { + .channel = BCM_CYGNUS_GENPLL_AXI21_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 6, 0, 12), + .mdiv = reg_val(0x20, 0, 8), + }, + [BCM_CYGNUS_GENPLL_250MHZ_CLK] = { + .channel = BCM_CYGNUS_GENPLL_250MHZ_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 7, 1, 13), + .mdiv = reg_val(0x20, 10, 8), + }, + [BCM_CYGNUS_GENPLL_IHOST_SYS_CLK] = { + .channel = BCM_CYGNUS_GENPLL_IHOST_SYS_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 8, 2, 14), + .mdiv = reg_val(0x20, 20, 8), + }, + [BCM_CYGNUS_GENPLL_ENET_SW_CLK] = { + .channel = BCM_CYGNUS_GENPLL_ENET_SW_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 9, 3, 15), + .mdiv = reg_val(0x24, 0, 8), + }, + [BCM_CYGNUS_GENPLL_AUDIO_125_CLK] = { + .channel = BCM_CYGNUS_GENPLL_AUDIO_125_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 10, 4, 16), + .mdiv = reg_val(0x24, 10, 8), + }, + [BCM_CYGNUS_GENPLL_CAN_CLK] = { + .channel = BCM_CYGNUS_GENPLL_CAN_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 11, 5, 17), + .mdiv = reg_val(0x24, 20, 8), + }, +}; + +static void __init cygnus_genpll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &genpll, NULL, 0, genpll_clk, + ARRAY_SIZE(genpll_clk)); +} +CLK_OF_DECLARE(cygnus_genpll, "brcm,cygnus-genpll", cygnus_genpll_clk_init); + +static const struct iproc_pll_ctrl lcpll0 = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_NEEDS_SW_CFG, + .aon = aon_val(0x0, 2, 5, 4), + .reset = reset_val(0x0, 31, 30, 27, 3, 23, 4, 19, 4), + .sw_ctrl = sw_ctrl_val(0x4, 31), + .ndiv_int = reg_val(0x4, 16, 10), + .pdiv = reg_val(0x4, 26, 4), + .vco_ctrl = vco_ctrl_val(0x10, 0x14), + .status = reg_val(0x18, 12, 1), +}; + +static const struct iproc_clk_ctrl lcpll0_clk[] = { + [BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 7, 1, 13), + .mdiv = reg_val(0x8, 0, 8), + }, + [BCM_CYGNUS_LCPLL0_DDR_PHY_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_DDR_PHY_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 8, 2, 14), + .mdiv = reg_val(0x8, 10, 8), + }, + [BCM_CYGNUS_LCPLL0_SDIO_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_SDIO_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 9, 3, 15), + .mdiv = reg_val(0x8, 20, 8), + }, + [BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 10, 4, 16), + .mdiv = reg_val(0xc, 0, 8), + }, + [BCM_CYGNUS_LCPLL0_SMART_CARD_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_SMART_CARD_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 11, 5, 17), + .mdiv = reg_val(0xc, 10, 8), + }, + [BCM_CYGNUS_LCPLL0_CH5_UNUSED] = { + .channel = BCM_CYGNUS_LCPLL0_CH5_UNUSED, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 12, 6, 18), + .mdiv = reg_val(0xc, 20, 8), + }, +}; + +static void __init cygnus_lcpll0_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &lcpll0, NULL, 0, lcpll0_clk, + ARRAY_SIZE(lcpll0_clk)); +} +CLK_OF_DECLARE(cygnus_lcpll0, "brcm,cygnus-lcpll0", cygnus_lcpll0_clk_init); + +/* + * MIPI PLL VCO frequency parameter table + */ +static const struct iproc_pll_vco_param mipipll_vco_params[] = { + /* rate (Hz) ndiv_int ndiv_frac pdiv */ + { 750000000UL, 30, 0, 1 }, + { 1000000000UL, 40, 0, 1 }, + { 1350000000ul, 54, 0, 1 }, + { 2000000000UL, 80, 0, 1 }, + { 2100000000UL, 84, 0, 1 }, + { 2250000000UL, 90, 0, 1 }, + { 2500000000UL, 100, 0, 1 }, + { 2700000000UL, 54, 0, 0 }, + { 2975000000UL, 119, 0, 1 }, + { 3100000000UL, 124, 0, 1 }, + { 3150000000UL, 126, 0, 1 }, +}; + +static const struct iproc_pll_ctrl mipipll = { + .flags = IPROC_CLK_PLL_ASIU | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_NEEDS_READ_BACK, + .aon = aon_val(0x0, 4, 17, 16), + .asiu = asiu_gate_val(0x0, 3), + .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 4), + .ndiv_int = reg_val(0x10, 20, 10), + .ndiv_frac = reg_val(0x10, 0, 20), + .pdiv = reg_val(0x14, 0, 4), + .vco_ctrl = vco_ctrl_val(0x18, 0x1c), + .status = reg_val(0x28, 12, 1), +}; + +static const struct iproc_clk_ctrl mipipll_clk[] = { + [BCM_CYGNUS_MIPIPLL_CH0_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH0_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 12, 6, 18), + .mdiv = reg_val(0x20, 0, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH1_LCD] = { + .channel = BCM_CYGNUS_MIPIPLL_CH1_LCD, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 13, 7, 19), + .mdiv = reg_val(0x20, 10, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH2_V3D] = { + .channel = BCM_CYGNUS_MIPIPLL_CH2_V3D, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 14, 8, 20), + .mdiv = reg_val(0x20, 20, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH3_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH3_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 15, 9, 21), + .mdiv = reg_val(0x24, 0, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH4_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH4_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 16, 10, 22), + .mdiv = reg_val(0x24, 10, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH5_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH5_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 17, 11, 23), + .mdiv = reg_val(0x24, 20, 8), + }, +}; + +static void __init cygnus_mipipll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &mipipll, mipipll_vco_params, + ARRAY_SIZE(mipipll_vco_params), mipipll_clk, + ARRAY_SIZE(mipipll_clk)); +} +CLK_OF_DECLARE(cygnus_mipipll, "brcm,cygnus-mipipll", cygnus_mipipll_clk_init); + +static const struct iproc_asiu_div asiu_div[] = { + [BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_div_val(0x0, 31, 16, 10, 0, 10), + [BCM_CYGNUS_ASIU_ADC_CLK] = asiu_div_val(0x4, 31, 16, 10, 0, 10), + [BCM_CYGNUS_ASIU_PWM_CLK] = asiu_div_val(0x8, 31, 16, 10, 0, 10), +}; + +static const struct iproc_asiu_gate asiu_gate[] = { + [BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_gate_val(0x0, 7), + [BCM_CYGNUS_ASIU_ADC_CLK] = asiu_gate_val(0x0, 9), + [BCM_CYGNUS_ASIU_PWM_CLK] = asiu_gate_val(IPROC_CLK_INVALID_OFFSET, 0), +}; + +static void __init cygnus_asiu_init(struct device_node *node) +{ + iproc_asiu_setup(node, asiu_div, asiu_gate, ARRAY_SIZE(asiu_div)); +} +CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init); diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c new file mode 100644 index 000000000000..a196ee28a17a --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-armpll.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clkdev.h> +#include <linux/of_address.h> + +#define IPROC_CLK_MAX_FREQ_POLICY 0x3 +#define IPROC_CLK_POLICY_FREQ_OFFSET 0x008 +#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8 +#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7 + +#define IPROC_CLK_PLLARMA_OFFSET 0xc00 +#define IPROC_CLK_PLLARMA_LOCK_SHIFT 28 +#define IPROC_CLK_PLLARMA_PDIV_SHIFT 24 +#define IPROC_CLK_PLLARMA_PDIV_MASK 0xf +#define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8 +#define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff + +#define IPROC_CLK_PLLARMB_OFFSET 0xc04 +#define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff + +#define IPROC_CLK_PLLARMC_OFFSET 0xc08 +#define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8 +#define IPROC_CLK_PLLARMC_MDIV_MASK 0xff + +#define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20 +#define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff + +#define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24 +#define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29 +#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20 +#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff +#define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff + +#define IPROC_CLK_ARM_DIV_OFFSET 0xe00 +#define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4 +#define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf + +#define IPROC_CLK_POLICY_DBG_OFFSET 0xec0 +#define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12 +#define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7 + +enum iproc_arm_pll_fid { + ARM_PLL_FID_CRYSTAL_CLK = 0, + ARM_PLL_FID_SYS_CLK = 2, + ARM_PLL_FID_CH0_SLOW_CLK = 6, + ARM_PLL_FID_CH1_FAST_CLK = 7 +}; + +struct iproc_arm_pll { + struct clk_hw hw; + void __iomem *base; + unsigned long rate; +}; + +#define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw) + +static unsigned int __get_fid(struct iproc_arm_pll *pll) +{ + u32 val; + unsigned int policy, fid, active_fid; + + val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET); + if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT)) + policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK; + else + policy = 0; + + /* something is seriously wrong */ + BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY); + + val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET); + fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) & + IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK; + + val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET); + active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK & + (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT); + if (fid != active_fid) { + pr_debug("%s: fid override %u->%u\n", __func__, fid, + active_fid); + fid = active_fid; + } + + pr_debug("%s: active fid: %u\n", __func__, fid); + + return fid; +} + +/* + * Determine the mdiv (post divider) based on the frequency ID being used. + * There are 4 sources that can be used to derive the output clock rate: + * - 25 MHz Crystal + * - System clock + * - PLL channel 0 (slow clock) + * - PLL channel 1 (fast clock) + */ +static int __get_mdiv(struct iproc_arm_pll *pll) +{ + unsigned int fid; + int mdiv; + u32 val; + + fid = __get_fid(pll); + + switch (fid) { + case ARM_PLL_FID_CRYSTAL_CLK: + case ARM_PLL_FID_SYS_CLK: + mdiv = 1; + break; + + case ARM_PLL_FID_CH0_SLOW_CLK: + val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET); + mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK; + if (mdiv == 0) + mdiv = 256; + break; + + case ARM_PLL_FID_CH1_FAST_CLK: + val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET); + mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK; + if (mdiv == 0) + mdiv = 256; + break; + + default: + mdiv = -EFAULT; + } + + return mdiv; +} + +static unsigned int __get_ndiv(struct iproc_arm_pll *pll) +{ + u32 val; + unsigned int ndiv_int, ndiv_frac, ndiv; + + val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET); + if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) { + /* + * offset mode is active. Read the ndiv from the PLLARM OFFSET + * register + */ + ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) & + IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK; + if (ndiv_int == 0) + ndiv_int = 256; + + ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK; + } else { + /* offset mode not active */ + val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET); + ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) & + IPROC_CLK_PLLARMA_NDIV_INT_MASK; + if (ndiv_int == 0) + ndiv_int = 1024; + + val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET); + ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK; + } + + ndiv = (ndiv_int << 20) | ndiv_frac; + + return ndiv; +} + +/* + * The output frequency of the ARM PLL is calculated based on the ARM PLL + * divider values: + * pdiv = ARM PLL pre-divider + * ndiv = ARM PLL multiplier + * mdiv = ARM PLL post divider + * + * The frequency is calculated by: + * ((ndiv * parent clock rate) / pdiv) / mdiv + */ +static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_arm_pll *pll = to_iproc_arm_pll(hw); + u32 val; + int mdiv; + u64 ndiv; + unsigned int pdiv; + + /* in bypass mode, use parent rate */ + val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET); + if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) { + pll->rate = parent_rate; + return pll->rate; + } + + /* PLL needs to be locked */ + val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET); + if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) { + pll->rate = 0; + return 0; + } + + pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) & + IPROC_CLK_PLLARMA_PDIV_MASK; + if (pdiv == 0) + pdiv = 16; + + ndiv = __get_ndiv(pll); + mdiv = __get_mdiv(pll); + if (mdiv <= 0) { + pll->rate = 0; + return 0; + } + pll->rate = (ndiv * parent_rate) >> 20; + pll->rate = (pll->rate / pdiv) / mdiv; + + pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__, + pll->rate, parent_rate); + pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__, + (unsigned int)(ndiv >> 20), pdiv, mdiv); + + return pll->rate; +} + +static const struct clk_ops iproc_arm_pll_ops = { + .recalc_rate = iproc_arm_pll_recalc_rate, +}; + +void __init iproc_armpll_setup(struct device_node *node) +{ + int ret; + struct clk *clk; + struct iproc_arm_pll *pll; + struct clk_init_data init; + const char *parent_name; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->base = of_iomap(node, 0); + if (WARN_ON(!pll->base)) + goto err_free_pll; + + init.name = node->name; + init.ops = &iproc_arm_pll_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_iounmap; + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(ret)) + goto err_clk_unregister; + + return; + +err_clk_unregister: + clk_unregister(clk); +err_iounmap: + iounmap(pll->base); +err_free_pll: + kfree(pll); +} diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c new file mode 100644 index 000000000000..e19c09cd9645 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-asiu.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clkdev.h> +#include <linux/of_address.h> +#include <linux/delay.h> + +#include "clk-iproc.h" + +struct iproc_asiu; + +struct iproc_asiu_clk { + struct clk_hw hw; + const char *name; + struct iproc_asiu *asiu; + unsigned long rate; + struct iproc_asiu_div div; + struct iproc_asiu_gate gate; +}; + +struct iproc_asiu { + void __iomem *div_base; + void __iomem *gate_base; + + struct clk_onecell_data clk_data; + struct iproc_asiu_clk *clks; +}; + +#define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw) + +static int iproc_asiu_clk_enable(struct clk_hw *hw) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + + /* some clocks at the ASIU level are always enabled */ + if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET) + return 0; + + val = readl(asiu->gate_base + clk->gate.offset); + val |= (1 << clk->gate.en_shift); + writel(val, asiu->gate_base + clk->gate.offset); + + return 0; +} + +static void iproc_asiu_clk_disable(struct clk_hw *hw) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + + /* some clocks at the ASIU level are always enabled */ + if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET) + return; + + val = readl(asiu->gate_base + clk->gate.offset); + val &= ~(1 << clk->gate.en_shift); + writel(val, asiu->gate_base + clk->gate.offset); +} + +static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + unsigned int div_h, div_l; + + if (parent_rate == 0) { + clk->rate = 0; + return 0; + } + + /* if clock divisor is not enabled, simply return parent rate */ + val = readl(asiu->div_base + clk->div.offset); + if ((val & (1 << clk->div.en_shift)) == 0) { + clk->rate = parent_rate; + return parent_rate; + } + + /* clock rate = parent rate / (high_div + 1) + (low_div + 1) */ + div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width); + div_h++; + div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width); + div_l++; + + clk->rate = parent_rate / (div_h + div_l); + pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n", + __func__, clk->rate, parent_rate, div_h, div_l); + + return clk->rate; +} + +static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div; + + if (rate == 0 || *parent_rate == 0) + return -EINVAL; + + if (rate == *parent_rate) + return *parent_rate; + + div = DIV_ROUND_UP(*parent_rate, rate); + if (div < 2) + return *parent_rate; + + return *parent_rate / div; +} + +static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + unsigned int div, div_h, div_l; + u32 val; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + /* simply disable the divisor if one wants the same rate as parent */ + if (rate == parent_rate) { + val = readl(asiu->div_base + clk->div.offset); + val &= ~(1 << clk->div.en_shift); + writel(val, asiu->div_base + clk->div.offset); + return 0; + } + + div = DIV_ROUND_UP(parent_rate, rate); + if (div < 2) + return -EINVAL; + + div_h = div_l = div >> 1; + div_h--; + div_l--; + + val = readl(asiu->div_base + clk->div.offset); + val |= 1 << clk->div.en_shift; + if (div_h) { + val &= ~(bit_mask(clk->div.high_width) + << clk->div.high_shift); + val |= div_h << clk->div.high_shift; + } else { + val &= ~(bit_mask(clk->div.high_width) + << clk->div.high_shift); + } + if (div_l) { + val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift); + val |= div_l << clk->div.low_shift; + } else { + val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift); + } + writel(val, asiu->div_base + clk->div.offset); + + return 0; +} + +static const struct clk_ops iproc_asiu_ops = { + .enable = iproc_asiu_clk_enable, + .disable = iproc_asiu_clk_disable, + .recalc_rate = iproc_asiu_clk_recalc_rate, + .round_rate = iproc_asiu_clk_round_rate, + .set_rate = iproc_asiu_clk_set_rate, +}; + +void __init iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks) +{ + int i, ret; + struct iproc_asiu *asiu; + + if (WARN_ON(!gate || !div)) + return; + + asiu = kzalloc(sizeof(*asiu), GFP_KERNEL); + if (WARN_ON(!asiu)) + return; + + asiu->clk_data.clk_num = num_clks; + asiu->clk_data.clks = kcalloc(num_clks, sizeof(*asiu->clk_data.clks), + GFP_KERNEL); + if (WARN_ON(!asiu->clk_data.clks)) + goto err_clks; + + asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL); + if (WARN_ON(!asiu->clks)) + goto err_asiu_clks; + + asiu->div_base = of_iomap(node, 0); + if (WARN_ON(!asiu->div_base)) + goto err_iomap_div; + + asiu->gate_base = of_iomap(node, 1); + if (WARN_ON(!asiu->gate_base)) + goto err_iomap_gate; + + for (i = 0; i < num_clks; i++) { + struct clk_init_data init; + struct clk *clk; + const char *parent_name; + struct iproc_asiu_clk *asiu_clk; + const char *clk_name; + + clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL); + if (WARN_ON(!clk_name)) + goto err_clk_register; + + ret = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + if (WARN_ON(ret)) + goto err_clk_register; + + asiu_clk = &asiu->clks[i]; + asiu_clk->name = clk_name; + asiu_clk->asiu = asiu; + asiu_clk->div = div[i]; + asiu_clk->gate = gate[i]; + init.name = clk_name; + init.ops = &iproc_asiu_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + asiu_clk->hw.init = &init; + + clk = clk_register(NULL, &asiu_clk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + asiu->clk_data.clks[i] = clk; + } + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, + &asiu->clk_data); + if (WARN_ON(ret)) + goto err_clk_register; + + return; + +err_clk_register: + for (i = 0; i < num_clks; i++) + kfree(asiu->clks[i].name); + iounmap(asiu->gate_base); + +err_iomap_gate: + iounmap(asiu->div_base); + +err_iomap_div: + kfree(asiu->clks); + +err_asiu_clks: + kfree(asiu->clk_data.clks); + +err_clks: + kfree(asiu); +} diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c new file mode 100644 index 000000000000..46fb84bc2674 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clkdev.h> +#include <linux/of_address.h> +#include <linux/delay.h> + +#include "clk-iproc.h" + +#define PLL_VCO_HIGH_SHIFT 19 +#define PLL_VCO_LOW_SHIFT 30 + +/* number of delay loops waiting for PLL to lock */ +#define LOCK_DELAY 100 + +/* number of VCO frequency bands */ +#define NUM_FREQ_BANDS 8 + +#define NUM_KP_BANDS 3 +enum kp_band { + KP_BAND_MID = 0, + KP_BAND_HIGH, + KP_BAND_HIGH_HIGH +}; + +static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = { + { 5, 6, 6, 7, 7, 8, 9, 10 }, + { 4, 4, 5, 5, 6, 7, 8, 9 }, + { 4, 5, 5, 6, 7, 8, 9, 10 }, +}; + +static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = { + { 10000000, 12500000 }, + { 12500000, 15000000 }, + { 15000000, 20000000 }, + { 20000000, 25000000 }, + { 25000000, 50000000 }, + { 50000000, 75000000 }, + { 75000000, 100000000 }, + { 100000000, 125000000 }, +}; + +enum vco_freq_range { + VCO_LOW = 700000000U, + VCO_MID = 1200000000U, + VCO_HIGH = 2200000000U, + VCO_HIGH_HIGH = 3100000000U, + VCO_MAX = 4000000000U, +}; + +struct iproc_pll; + +struct iproc_clk { + struct clk_hw hw; + const char *name; + struct iproc_pll *pll; + unsigned long rate; + const struct iproc_clk_ctrl *ctrl; +}; + +struct iproc_pll { + void __iomem *pll_base; + void __iomem *pwr_base; + void __iomem *asiu_base; + + const struct iproc_pll_ctrl *ctrl; + const struct iproc_pll_vco_param *vco_param; + unsigned int num_vco_entries; + + struct clk_onecell_data clk_data; + struct iproc_clk *clks; +}; + +#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw) + +/* + * Based on the target frequency, find a match from the VCO frequency parameter + * table and return its index + */ +static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate) +{ + int i; + + for (i = 0; i < pll->num_vco_entries; i++) + if (target_rate == pll->vco_param[i].rate) + break; + + if (i >= pll->num_vco_entries) + return -EINVAL; + + return i; +} + +static int get_kp(unsigned long ref_freq, enum kp_band kp_index) +{ + int i; + + if (ref_freq < ref_freq_table[0][0]) + return -EINVAL; + + for (i = 0; i < NUM_FREQ_BANDS; i++) { + if (ref_freq >= ref_freq_table[i][0] && + ref_freq < ref_freq_table[i][1]) + return kp_table[kp_index][i]; + } + return -EINVAL; +} + +static int pll_wait_for_lock(struct iproc_pll *pll) +{ + int i; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + for (i = 0; i < LOCK_DELAY; i++) { + u32 val = readl(pll->pll_base + ctrl->status.offset); + + if (val & (1 << ctrl->status.shift)) + return 0; + udelay(10); + } + + return -EIO; +} + +static void __pll_disable(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + + if (ctrl->flags & IPROC_CLK_PLL_ASIU) { + val = readl(pll->asiu_base + ctrl->asiu.offset); + val &= ~(1 << ctrl->asiu.en_shift); + writel(val, pll->asiu_base + ctrl->asiu.offset); + } + + /* latch input value so core power can be shut down */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= (1 << ctrl->aon.iso_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); + + /* power down the core */ + val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); +} + +static int __pll_enable(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + + /* power up the PLL and make sure it's not latched */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift; + val &= ~(1 << ctrl->aon.iso_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); + + /* certain PLLs also need to be ungated from the ASIU top level */ + if (ctrl->flags & IPROC_CLK_PLL_ASIU) { + val = readl(pll->asiu_base + ctrl->asiu.offset); + val |= (1 << ctrl->asiu.en_shift); + writel(val, pll->asiu_base + ctrl->asiu.offset); + } + + return 0; +} + +static void __pll_put_in_reset(struct iproc_pll *pll) +{ + u32 val; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + + val = readl(pll->pll_base + reset->offset); + val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift); + writel(val, pll->pll_base + reset->offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + reset->offset); +} + +static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, + unsigned int ka, unsigned int ki) +{ + u32 val; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + + val = readl(pll->pll_base + reset->offset); + val &= ~(bit_mask(reset->ki_width) << reset->ki_shift | + bit_mask(reset->kp_width) << reset->kp_shift | + bit_mask(reset->ka_width) << reset->ka_shift); + val |= ki << reset->ki_shift | kp << reset->kp_shift | + ka << reset->ka_shift; + val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift; + writel(val, pll->pll_base + reset->offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + reset->offset); +} + +static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, + unsigned long parent_rate) +{ + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_vco_param *vco = &pll->vco_param[rate_index]; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + int ka = 0, ki, kp, ret; + unsigned long rate = vco->rate; + u32 val; + enum kp_band kp_index; + unsigned long ref_freq; + + /* + * reference frequency = parent frequency / PDIV + * If PDIV = 0, then it becomes a multiplier (x2) + */ + if (vco->pdiv == 0) + ref_freq = parent_rate * 2; + else + ref_freq = parent_rate / vco->pdiv; + + /* determine Ki and Kp index based on target VCO frequency */ + if (rate >= VCO_LOW && rate < VCO_HIGH) { + ki = 4; + kp_index = KP_BAND_MID; + } else if (rate >= VCO_HIGH && rate && rate < VCO_HIGH_HIGH) { + ki = 3; + kp_index = KP_BAND_HIGH; + } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) { + ki = 3; + kp_index = KP_BAND_HIGH_HIGH; + } else { + pr_err("%s: pll: %s has invalid rate: %lu\n", __func__, + clk->name, rate); + return -EINVAL; + } + + kp = get_kp(ref_freq, kp_index); + if (kp < 0) { + pr_err("%s: pll: %s has invalid kp\n", __func__, clk->name); + return kp; + } + + ret = __pll_enable(pll); + if (ret) { + pr_err("%s: pll: %s fails to enable\n", __func__, clk->name); + return ret; + } + + /* put PLL in reset */ + __pll_put_in_reset(pll); + + writel(0, pll->pll_base + ctrl->vco_ctrl.u_offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->vco_ctrl.u_offset); + val = readl(pll->pll_base + ctrl->vco_ctrl.l_offset); + + if (rate >= VCO_LOW && rate < VCO_MID) + val |= (1 << PLL_VCO_LOW_SHIFT); + + if (rate < VCO_HIGH) + val &= ~(1 << PLL_VCO_HIGH_SHIFT); + else + val |= (1 << PLL_VCO_HIGH_SHIFT); + + writel(val, pll->pll_base + ctrl->vco_ctrl.l_offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->vco_ctrl.l_offset); + + /* program integer part of NDIV */ + val = readl(pll->pll_base + ctrl->ndiv_int.offset); + val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift); + val |= vco->ndiv_int << ctrl->ndiv_int.shift; + writel(val, pll->pll_base + ctrl->ndiv_int.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->ndiv_int.offset); + + /* program fractional part of NDIV */ + if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { + val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + val &= ~(bit_mask(ctrl->ndiv_frac.width) << + ctrl->ndiv_frac.shift); + val |= vco->ndiv_frac << ctrl->ndiv_frac.shift; + writel(val, pll->pll_base + ctrl->ndiv_frac.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->ndiv_frac.offset); + } + + /* program PDIV */ + val = readl(pll->pll_base + ctrl->pdiv.offset); + val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift); + val |= vco->pdiv << ctrl->pdiv.shift; + writel(val, pll->pll_base + ctrl->pdiv.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->pdiv.offset); + + __pll_bring_out_reset(pll, kp, ka, ki); + + ret = pll_wait_for_lock(pll); + if (ret < 0) { + pr_err("%s: pll: %s failed to lock\n", __func__, clk->name); + return ret; + } + + return 0; +} + +static int iproc_pll_enable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + + return __pll_enable(pll); +} + +static void iproc_pll_disable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + if (ctrl->flags & IPROC_CLK_AON) + return; + + __pll_disable(pll); +} + +static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(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; + + if (parent_rate == 0) + return 0; + + /* PLL needs to be locked */ + val = readl(pll->pll_base + ctrl->status.offset); + if ((val & (1 << ctrl->status.shift)) == 0) { + clk->rate = 0; + return 0; + } + + /* + * PLL output frequency = + * + * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv) + */ + val = readl(pll->pll_base + ctrl->ndiv_int.offset); + ndiv_int = (val >> ctrl->ndiv_int.shift) & + bit_mask(ctrl->ndiv_int.width); + ndiv = ndiv_int << ctrl->ndiv_int.shift; + + if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { + val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + ndiv_frac = (val >> ctrl->ndiv_frac.shift) & + bit_mask(ctrl->ndiv_frac.width); + + if (ndiv_frac != 0) + ndiv = (ndiv_int << ctrl->ndiv_int.shift) | ndiv_frac; + } + + val = readl(pll->pll_base + ctrl->pdiv.offset); + pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); + + clk->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift; + + if (pdiv == 0) + clk->rate *= 2; + else + clk->rate /= pdiv; + + return clk->rate; +} + +static long iproc_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned i; + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + + if (rate == 0 || *parent_rate == 0 || !pll->vco_param) + return -EINVAL; + + for (i = 0; i < pll->num_vco_entries; i++) { + if (rate <= pll->vco_param[i].rate) + break; + } + + if (i == pll->num_vco_entries) + i--; + + return pll->vco_param[i].rate; +} + +static int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + int rate_index, ret; + + rate_index = pll_get_rate_index(pll, rate); + if (rate_index < 0) + return rate_index; + + ret = pll_set_rate(clk, rate_index, parent_rate); + return ret; +} + +static const struct clk_ops iproc_pll_ops = { + .enable = iproc_pll_enable, + .disable = iproc_pll_disable, + .recalc_rate = iproc_pll_recalc_rate, + .round_rate = iproc_pll_round_rate, + .set_rate = iproc_pll_set_rate, +}; + +static int iproc_clk_enable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + + /* channel enable is active low */ + val = readl(pll->pll_base + ctrl->enable.offset); + val &= ~(1 << ctrl->enable.enable_shift); + writel(val, pll->pll_base + ctrl->enable.offset); + + /* also make sure channel is not held */ + val = readl(pll->pll_base + ctrl->enable.offset); + val &= ~(1 << ctrl->enable.hold_shift); + writel(val, pll->pll_base + ctrl->enable.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->enable.offset); + + return 0; +} + +static void iproc_clk_disable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + + if (ctrl->flags & IPROC_CLK_AON) + return; + + val = readl(pll->pll_base + ctrl->enable.offset); + val |= 1 << ctrl->enable.enable_shift; + writel(val, pll->pll_base + ctrl->enable.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->enable.offset); +} + +static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + unsigned int mdiv; + + if (parent_rate == 0) + return 0; + + val = readl(pll->pll_base + ctrl->mdiv.offset); + mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width); + if (mdiv == 0) + mdiv = 256; + + clk->rate = parent_rate / mdiv; + + return clk->rate; +} + +static long iproc_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div; + + if (rate == 0 || *parent_rate == 0) + return -EINVAL; + + if (rate == *parent_rate) + return *parent_rate; + + div = DIV_ROUND_UP(*parent_rate, rate); + if (div < 2) + return *parent_rate; + + if (div > 256) + div = 256; + + return *parent_rate / div; +} + +static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + unsigned int div; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + div = DIV_ROUND_UP(parent_rate, rate); + if (div > 256) + return -EINVAL; + + val = readl(pll->pll_base + ctrl->mdiv.offset); + if (div == 256) { + val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); + } else { + val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); + val |= div << ctrl->mdiv.shift; + } + writel(val, pll->pll_base + ctrl->mdiv.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->mdiv.offset); + clk->rate = parent_rate / div; + + return 0; +} + +static const struct clk_ops iproc_clk_ops = { + .enable = iproc_clk_enable, + .disable = iproc_clk_disable, + .recalc_rate = iproc_clk_recalc_rate, + .round_rate = iproc_clk_round_rate, + .set_rate = iproc_clk_set_rate, +}; + +/** + * Some PLLs require the PLL SW override bit to be set before changes can be + * applied to the PLL + */ +static void iproc_pll_sw_cfg(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) { + u32 val; + + val = readl(pll->pll_base + ctrl->sw_ctrl.offset); + val |= BIT(ctrl->sw_ctrl.shift); + writel(val, pll->pll_base + ctrl->sw_ctrl.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->sw_ctrl.offset); + } +} + +void __init iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks) +{ + int i, ret; + struct clk *clk; + struct iproc_pll *pll; + struct iproc_clk *iclk; + struct clk_init_data init; + const char *parent_name; + + if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl)) + return; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->clk_data.clk_num = num_clks; + pll->clk_data.clks = kcalloc(num_clks, sizeof(*pll->clk_data.clks), + GFP_KERNEL); + if (WARN_ON(!pll->clk_data.clks)) + goto err_clk_data; + + pll->clks = kcalloc(num_clks, sizeof(*pll->clks), GFP_KERNEL); + if (WARN_ON(!pll->clks)) + goto err_clks; + + pll->pll_base = of_iomap(node, 0); + if (WARN_ON(!pll->pll_base)) + goto err_pll_iomap; + + pll->pwr_base = of_iomap(node, 1); + if (WARN_ON(!pll->pwr_base)) + goto err_pwr_iomap; + + /* some PLLs require gating control at the top ASIU level */ + if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) { + pll->asiu_base = of_iomap(node, 2); + if (WARN_ON(!pll->asiu_base)) + goto err_asiu_iomap; + } + + /* initialize and register the PLL itself */ + pll->ctrl = pll_ctrl; + + iclk = &pll->clks[0]; + iclk->pll = pll; + iclk->name = node->name; + + init.name = node->name; + init.ops = &iproc_pll_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + iclk->hw.init = &init; + + if (vco) { + pll->num_vco_entries = num_vco_entries; + pll->vco_param = vco; + } + + iproc_pll_sw_cfg(pll); + + clk = clk_register(NULL, &iclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_pll_register; + + pll->clk_data.clks[0] = clk; + + /* now initialize and register all leaf clocks */ + for (i = 1; i < num_clks; i++) { + const char *clk_name; + + memset(&init, 0, sizeof(init)); + parent_name = node->name; + + clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL); + if (WARN_ON(!clk_name)) + goto err_clk_register; + + ret = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + if (WARN_ON(ret)) + goto err_clk_register; + + iclk = &pll->clks[i]; + iclk->name = clk_name; + iclk->pll = pll; + iclk->ctrl = &clk_ctrl[i]; + + init.name = clk_name; + init.ops = &iproc_clk_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + iclk->hw.init = &init; + + clk = clk_register(NULL, &iclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + + pll->clk_data.clks[i] = clk; + } + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data); + if (WARN_ON(ret)) + goto err_clk_register; + + return; + +err_clk_register: + for (i = 0; i < num_clks; i++) { + kfree(pll->clks[i].name); + clk_unregister(pll->clk_data.clks[i]); + } + +err_pll_register: + if (pll->asiu_base) + iounmap(pll->asiu_base); + +err_asiu_iomap: + iounmap(pll->pwr_base); + +err_pwr_iomap: + iounmap(pll->pll_base); + +err_pll_iomap: + kfree(pll->clks); + +err_clks: + kfree(pll->clk_data.clks); + +err_clk_data: + kfree(pll); +} diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h new file mode 100644 index 000000000000..d834b7abd5c6 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CLK_IPROC_H +#define _CLK_IPROC_H + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/clk-provider.h> + +#define IPROC_CLK_NAME_LEN 25 +#define IPROC_CLK_INVALID_OFFSET 0xffffffff +#define bit_mask(width) ((1 << (width)) - 1) + +/* clocks that should not be disabled at runtime */ +#define IPROC_CLK_AON BIT(0) + +/* PLL that requires gating through ASIU */ +#define IPROC_CLK_PLL_ASIU BIT(1) + +/* PLL that has fractional part of the NDIV */ +#define IPROC_CLK_PLL_HAS_NDIV_FRAC BIT(2) + +/* + * Some of the iProc PLL/clocks may have an ASIC bug that requires read back + * of the same register following the write to flush the write transaction into + * the intended register + */ +#define IPROC_CLK_NEEDS_READ_BACK BIT(3) + +/* + * Some PLLs require the PLL SW override bit to be set before changes can be + * applied to the PLL + */ +#define IPROC_CLK_PLL_NEEDS_SW_CFG BIT(4) + +/* + * Parameters for VCO frequency configuration + * + * VCO frequency = + * ((ndiv_int + ndiv_frac / 2^20) * (ref freqeuncy / pdiv) + */ +struct iproc_pll_vco_param { + unsigned long rate; + unsigned int ndiv_int; + unsigned int ndiv_frac; + unsigned int pdiv; +}; + +struct iproc_clk_reg_op { + unsigned int offset; + unsigned int shift; + unsigned int width; +}; + +/* + * Clock gating control at the top ASIU level + */ +struct iproc_asiu_gate { + unsigned int offset; + unsigned int en_shift; +}; + +/* + * Control of powering on/off of a PLL + * + * Before powering off a PLL, input isolation (ISO) needs to be enabled + */ +struct iproc_pll_aon_pwr_ctrl { + unsigned int offset; + unsigned int pwr_width; + unsigned int pwr_shift; + unsigned int iso_shift; +}; + +/* + * Control of the PLL reset, with Ki, Kp, and Ka parameters + */ +struct iproc_pll_reset_ctrl { + unsigned int offset; + unsigned int reset_shift; + unsigned int p_reset_shift; + unsigned int ki_shift; + unsigned int ki_width; + unsigned int kp_shift; + unsigned int kp_width; + unsigned int ka_shift; + unsigned int ka_width; +}; + +/* + * To enable SW control of the PLL + */ +struct iproc_pll_sw_ctrl { + unsigned int offset; + unsigned int shift; +}; + +struct iproc_pll_vco_ctrl { + unsigned int u_offset; + unsigned int l_offset; +}; + +/* + * Main PLL control parameters + */ +struct iproc_pll_ctrl { + unsigned long flags; + struct iproc_pll_aon_pwr_ctrl aon; + struct iproc_asiu_gate asiu; + struct iproc_pll_reset_ctrl reset; + struct iproc_pll_sw_ctrl sw_ctrl; + struct iproc_clk_reg_op ndiv_int; + struct iproc_clk_reg_op ndiv_frac; + struct iproc_clk_reg_op pdiv; + struct iproc_pll_vco_ctrl vco_ctrl; + struct iproc_clk_reg_op status; +}; + +/* + * Controls enabling/disabling a PLL derived clock + */ +struct iproc_clk_enable_ctrl { + unsigned int offset; + unsigned int enable_shift; + unsigned int hold_shift; + unsigned int bypass_shift; +}; + +/* + * Main clock control parameters for clocks derived from the PLLs + */ +struct iproc_clk_ctrl { + unsigned int channel; + unsigned long flags; + struct iproc_clk_enable_ctrl enable; + struct iproc_clk_reg_op mdiv; +}; + +/* + * Divisor of the ASIU clocks + */ +struct iproc_asiu_div { + unsigned int offset; + unsigned int en_shift; + unsigned int high_shift; + unsigned int high_width; + unsigned int low_shift; + unsigned int low_width; +}; + +void __init iproc_armpll_setup(struct device_node *node); +void __init iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks); +void __init iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks); + +#endif /* _CLK_IPROC_H */ diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index e5aededdd322..deaa7f962b84 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -21,8 +21,6 @@ #define selector_clear_exists(sel) ((sel)->width = 0) #define trigger_clear_exists(trig) FLAG_CLEAR(trig, TRIG, EXISTS) -LIST_HEAD(ccu_list); /* The list of set up CCUs */ - /* Validity checking */ static bool ccu_data_offsets_valid(struct ccu_data *ccu) @@ -773,7 +771,6 @@ static void kona_ccu_teardown(struct ccu_data *ccu) of_clk_del_provider(ccu->node); /* safe if never added */ ccu_clks_teardown(ccu); - list_del(&ccu->links); of_node_put(ccu->node); ccu->node = NULL; iounmap(ccu->base); @@ -847,7 +844,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, goto out_err; } ccu->node = of_node_get(node); - list_add_tail(&ccu->links, &ccu_list); /* * Set up each defined kona clock and save the result in diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index a0ef4f75d457..79a98506c433 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1240,7 +1240,7 @@ static bool __kona_clk_init(struct kona_clk *bcm_clk) default: BUG(); } - return -EINVAL; + return false; } /* Set a CCU and all its clocks into their desired initial state */ diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 6849a64baf6d..906576ec97b6 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -480,7 +480,6 @@ struct ccu_data { spinlock_t lock; /* serialization lock */ bool write_enabled; /* write access is currently enabled */ struct ccu_policy policy; - struct list_head links; /* for ccu_list */ struct device_node *node; struct clk_onecell_data clk_data; const char *name; @@ -492,7 +491,6 @@ struct ccu_data { #define KONA_CCU_COMMON(_prefix, _name, _ccuname) \ .name = #_name "_ccu", \ .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ - .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ .clk_data = { \ .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \ } diff --git a/drivers/clk/berlin/berlin2-pll.c b/drivers/clk/berlin/berlin2-pll.c index bdc506b03824..f4b8d324b083 100644 --- a/drivers/clk/berlin/berlin2-pll.c +++ b/drivers/clk/berlin/berlin2-pll.c @@ -25,14 +25,7 @@ #include <asm/div64.h> #include "berlin2-div.h" - -struct berlin2_pll_map { - const u8 vcodiv[16]; - u8 mult; - u8 fbdiv_shift; - u8 rfdiv_shift; - u8 divsel_shift; -}; +#include "berlin2-pll.h" struct berlin2_pll { struct clk_hw hw; diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index 88f4ff6916fe..90897af8d9f7 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -274,7 +274,7 @@ static void __init asm9260_acc_init(struct device_node *np) u32 accuracy = 0; base = of_io_request_and_map(np, 0, np->name); - if (!base) + if (IS_ERR(base)) panic("%s: unable to map resource", np->name); /* register pll */ diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c index 0f6368ceec4c..c7c91a5ecf8b 100644 --- a/drivers/clk/clk-axm5516.c +++ b/drivers/clk/clk-axm5516.c @@ -556,7 +556,7 @@ static int axmclk_probe(struct platform_device *pdev) return PTR_ERR(regmap); num_clks = ARRAY_SIZE(axmclk_clocks); - pr_info("axmclk: supporting %u clocks\n", num_clks); + pr_info("axmclk: supporting %zu clocks\n", num_clks); priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks, GFP_KERNEL); if (!priv) diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index b8e4f8a822e9..f01164fada5d 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -94,7 +94,7 @@ static const char * const cdce706_source_name[] = { "clk_in0", "clk_in1", }; -static const char *cdce706_clkin_name[] = { +static const char * const cdce706_clkin_name[] = { "clk_in", }; @@ -102,7 +102,7 @@ static const char * const cdce706_pll_name[] = { "pll1", "pll2", "pll3", }; -static const char *cdce706_divider_parent_name[] = { +static const char * const cdce706_divider_parent_name[] = { "clk_in", "pll1", "pll2", "pll2", "pll3", }; @@ -666,6 +666,7 @@ static int cdce706_probe(struct i2c_client *client, static int cdce706_remove(struct i2c_client *client) { + of_clk_del_provider(client->dev.of_node); return 0; } diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c new file mode 100644 index 000000000000..85fafb41e6ca --- /dev/null +++ b/drivers/clk/clk-cdce925.c @@ -0,0 +1,749 @@ +/* + * Driver for TI Dual PLL CDCE925 clock synthesizer + * + * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1 + * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve + * basis. Clients can directly request any frequency that the chip can + * deliver using the standard clk framework. In addition, the device can + * be configured and activated via the devicetree. + * + * Copyright (C) 2014, Topic Embedded Products + * Licenced under GPL + */ +#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> +#include <linux/gcd.h> + +/* The chip has 2 PLLs which can be routed through dividers to 5 outputs. + * Model this as 2 PLL clocks which are parents to the outputs. + */ +#define NUMBER_OF_PLLS 2 +#define NUMBER_OF_OUTPUTS 5 + +#define CDCE925_REG_GLOBAL1 0x01 +#define CDCE925_REG_Y1SPIPDIVH 0x02 +#define CDCE925_REG_PDIVL 0x03 +#define CDCE925_REG_XCSEL 0x05 +/* PLL parameters start at 0x10, steps of 0x10 */ +#define CDCE925_OFFSET_PLL 0x10 +/* Add CDCE925_OFFSET_PLL * (pll) to these registers before sending */ +#define CDCE925_PLL_MUX_OUTPUTS 0x14 +#define CDCE925_PLL_MULDIV 0x18 + +#define CDCE925_PLL_FREQUENCY_MIN 80000000ul +#define CDCE925_PLL_FREQUENCY_MAX 230000000ul +struct clk_cdce925_chip; + +struct clk_cdce925_output { + struct clk_hw hw; + struct clk_cdce925_chip *chip; + u8 index; + u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */ +}; +#define to_clk_cdce925_output(_hw) \ + container_of(_hw, struct clk_cdce925_output, hw) + +struct clk_cdce925_pll { + struct clk_hw hw; + struct clk_cdce925_chip *chip; + u8 index; + u16 m; /* 1..511 */ + u16 n; /* 1..4095 */ +}; +#define to_clk_cdce925_pll(_hw) container_of(_hw, struct clk_cdce925_pll, hw) + +struct clk_cdce925_chip { + struct regmap *regmap; + struct i2c_client *i2c_client; + struct clk_cdce925_pll pll[NUMBER_OF_PLLS]; + struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS]; + struct clk *dt_clk[NUMBER_OF_OUTPUTS]; + struct clk_onecell_data onecell; +}; + +/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ + +static unsigned long cdce925_pll_calculate_rate(unsigned long parent_rate, + u16 n, u16 m) +{ + if ((!m || !n) || (m == n)) + return parent_rate; /* In bypass mode runs at same frequency */ + return mult_frac(parent_rate, (unsigned long)n, (unsigned long)m); +} + +static unsigned long cdce925_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* Output frequency of PLL is Fout = (Fin/Pdiv)*(N/M) */ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + + return cdce925_pll_calculate_rate(parent_rate, data->n, data->m); +} + +static void cdce925_pll_find_rate(unsigned long rate, + unsigned long parent_rate, u16 *n, u16 *m) +{ + unsigned long un; + unsigned long um; + unsigned long g; + + if (rate <= parent_rate) { + /* Can always deliver parent_rate in bypass mode */ + rate = parent_rate; + *n = 0; + *m = 0; + } else { + /* In PLL mode, need to apply min/max range */ + if (rate < CDCE925_PLL_FREQUENCY_MIN) + rate = CDCE925_PLL_FREQUENCY_MIN; + else if (rate > CDCE925_PLL_FREQUENCY_MAX) + rate = CDCE925_PLL_FREQUENCY_MAX; + + g = gcd(rate, parent_rate); + um = parent_rate / g; + un = rate / g; + /* When outside hw range, reduce to fit (rounding errors) */ + while ((un > 4095) || (um > 511)) { + un >>= 1; + um >>= 1; + } + if (un == 0) + un = 1; + if (um == 0) + um = 1; + + *n = un; + *m = um; + } +} + +static long cdce925_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u16 n, m; + + cdce925_pll_find_rate(rate, *parent_rate, &n, &m); + return (long)cdce925_pll_calculate_rate(*parent_rate, n, m); +} + +static int cdce925_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + + if (!rate || (rate == parent_rate)) { + data->m = 0; /* Bypass mode */ + data->n = 0; + return 0; + } + + if ((rate < CDCE925_PLL_FREQUENCY_MIN) || + (rate > CDCE925_PLL_FREQUENCY_MAX)) { + pr_debug("%s: rate %lu outside PLL range.\n", __func__, rate); + return -EINVAL; + } + + if (rate < parent_rate) { + pr_debug("%s: rate %lu less than parent rate %lu.\n", __func__, + rate, parent_rate); + return -EINVAL; + } + + cdce925_pll_find_rate(rate, parent_rate, &data->n, &data->m); + return 0; +} + + +/* calculate p = max(0, 4 - int(log2 (n/m))) */ +static u8 cdce925_pll_calc_p(u16 n, u16 m) +{ + u8 p; + u16 r = n / m; + + if (r >= 16) + return 0; + p = 4; + while (r > 1) { + r >>= 1; + --p; + } + return p; +} + +/* Returns VCO range bits for VCO1_0_RANGE */ +static u8 cdce925_pll_calc_range_bits(struct clk_hw *hw, u16 n, u16 m) +{ + struct clk *parent = clk_get_parent(hw->clk); + unsigned long rate = clk_get_rate(parent); + + rate = mult_frac(rate, (unsigned long)n, (unsigned long)m); + if (rate >= 175000000) + return 0x3; + if (rate >= 150000000) + return 0x02; + if (rate >= 125000000) + return 0x01; + return 0x00; +} + +/* I2C clock, hence everything must happen in (un)prepare because this + * may sleep */ +static int cdce925_pll_prepare(struct clk_hw *hw) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + u16 n = data->n; + u16 m = data->m; + u16 r; + u8 q; + u8 p; + u16 nn; + u8 pll[4]; /* Bits are spread out over 4 byte registers */ + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; + unsigned i; + + if ((!m || !n) || (m == n)) { + /* Set PLL mux to bypass mode, leave the rest as is */ + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); + } else { + /* According to data sheet: */ + /* p = max(0, 4 - int(log2 (n/m))) */ + p = cdce925_pll_calc_p(n, m); + /* nn = n * 2^p */ + nn = n * BIT(p); + /* q = int(nn/m) */ + q = nn / m; + if ((q < 16) || (1 > 64)) { + pr_debug("%s invalid q=%d\n", __func__, q); + return -EINVAL; + } + r = nn - (m*q); + if (r > 511) { + pr_debug("%s invalid r=%d\n", __func__, r); + return -EINVAL; + } + pr_debug("%s n=%d m=%d p=%d q=%d r=%d\n", __func__, + n, m, p, q, r); + /* encode into register bits */ + pll[0] = n >> 4; + pll[1] = ((n & 0x0F) << 4) | ((r >> 5) & 0x0F); + pll[2] = ((r & 0x1F) << 3) | ((q >> 3) & 0x07); + pll[3] = ((q & 0x07) << 5) | (p << 2) | + cdce925_pll_calc_range_bits(hw, n, m); + /* Write to registers */ + for (i = 0; i < ARRAY_SIZE(pll); ++i) + regmap_write(data->chip->regmap, + reg_ofs + CDCE925_PLL_MULDIV + i, pll[i]); + /* Enable PLL */ + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x00); + } + + return 0; +} + +static void cdce925_pll_unprepare(struct clk_hw *hw) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; + + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); +} + +static const struct clk_ops cdce925_pll_ops = { + .prepare = cdce925_pll_prepare, + .unprepare = cdce925_pll_unprepare, + .recalc_rate = cdce925_pll_recalc_rate, + .round_rate = cdce925_pll_round_rate, + .set_rate = cdce925_pll_set_rate, +}; + + +static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv) +{ + switch (data->index) { + case 0: + regmap_update_bits(data->chip->regmap, + CDCE925_REG_Y1SPIPDIVH, + 0x03, (pdiv >> 8) & 0x03); + regmap_write(data->chip->regmap, 0x03, pdiv & 0xFF); + break; + case 1: + regmap_update_bits(data->chip->regmap, 0x16, 0x7F, pdiv); + break; + case 2: + regmap_update_bits(data->chip->regmap, 0x17, 0x7F, pdiv); + break; + case 3: + regmap_update_bits(data->chip->regmap, 0x26, 0x7F, pdiv); + break; + case 4: + regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv); + break; + } +} + +static void cdce925_clk_activate(struct clk_cdce925_output *data) +{ + switch (data->index) { + case 0: + regmap_update_bits(data->chip->regmap, + CDCE925_REG_Y1SPIPDIVH, 0x0c, 0x0c); + break; + case 1: + case 2: + regmap_update_bits(data->chip->regmap, 0x14, 0x03, 0x03); + break; + case 3: + case 4: + regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03); + break; + } +} + +static int cdce925_clk_prepare(struct clk_hw *hw) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + cdce925_clk_set_pdiv(data, data->pdiv); + cdce925_clk_activate(data); + return 0; +} + +static void cdce925_clk_unprepare(struct clk_hw *hw) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + /* Disable clock by setting divider to "0" */ + cdce925_clk_set_pdiv(data, 0); +} + +static unsigned long cdce925_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + if (data->pdiv) + return parent_rate / data->pdiv; + return 0; +} + +static u16 cdce925_calc_divider(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long divider; + + if (!rate) + return 0; + if (rate >= parent_rate) + return 1; + + divider = DIV_ROUND_CLOSEST(parent_rate, rate); + if (divider > 0x7F) + divider = 0x7F; + + return (u16)divider; +} + +static unsigned long cdce925_clk_best_parent_rate( + struct clk_hw *hw, unsigned long rate) +{ + struct clk *pll = clk_get_parent(hw->clk); + struct clk *root = clk_get_parent(pll); + unsigned long root_rate = clk_get_rate(root); + unsigned long best_rate_error = rate; + u16 pdiv_min; + u16 pdiv_max; + u16 pdiv_best; + u16 pdiv_now; + + if (root_rate % rate == 0) + return root_rate; /* Don't need the PLL, use bypass */ + + pdiv_min = (u16)max(1ul, DIV_ROUND_UP(CDCE925_PLL_FREQUENCY_MIN, rate)); + pdiv_max = (u16)min(127ul, CDCE925_PLL_FREQUENCY_MAX / rate); + + if (pdiv_min > pdiv_max) + return 0; /* No can do? */ + + pdiv_best = pdiv_min; + for (pdiv_now = pdiv_min; pdiv_now < pdiv_max; ++pdiv_now) { + unsigned long target_rate = rate * pdiv_now; + long pll_rate = clk_round_rate(pll, target_rate); + unsigned long actual_rate; + unsigned long rate_error; + + if (pll_rate <= 0) + continue; + actual_rate = pll_rate / pdiv_now; + rate_error = abs((long)actual_rate - (long)rate); + if (rate_error < best_rate_error) { + pdiv_best = pdiv_now; + best_rate_error = rate_error; + } + /* TODO: Consider PLL frequency based on smaller n/m values + * and pick the better one if the error is equal */ + } + + return rate * pdiv_best; +} + +static long cdce925_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long l_parent_rate = *parent_rate; + u16 divider = cdce925_calc_divider(rate, l_parent_rate); + + if (l_parent_rate / divider != rate) { + l_parent_rate = cdce925_clk_best_parent_rate(hw, rate); + divider = cdce925_calc_divider(rate, l_parent_rate); + *parent_rate = l_parent_rate; + } + + if (divider) + return (long)(l_parent_rate / divider); + return 0; +} + +static int cdce925_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + data->pdiv = cdce925_calc_divider(rate, parent_rate); + + return 0; +} + +static const struct clk_ops cdce925_clk_ops = { + .prepare = cdce925_clk_prepare, + .unprepare = cdce925_clk_unprepare, + .recalc_rate = cdce925_clk_recalc_rate, + .round_rate = cdce925_clk_round_rate, + .set_rate = cdce925_clk_set_rate, +}; + + +static u16 cdce925_y1_calc_divider(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long divider; + + if (!rate) + return 0; + if (rate >= parent_rate) + return 1; + + divider = DIV_ROUND_CLOSEST(parent_rate, rate); + if (divider > 0x3FF) /* Y1 has 10-bit divider */ + divider = 0x3FF; + + return (u16)divider; +} + +static long cdce925_clk_y1_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long l_parent_rate = *parent_rate; + u16 divider = cdce925_y1_calc_divider(rate, l_parent_rate); + + if (divider) + return (long)(l_parent_rate / divider); + return 0; +} + +static int cdce925_clk_y1_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + data->pdiv = cdce925_y1_calc_divider(rate, parent_rate); + + return 0; +} + +static const struct clk_ops cdce925_clk_y1_ops = { + .prepare = cdce925_clk_prepare, + .unprepare = cdce925_clk_unprepare, + .recalc_rate = cdce925_clk_recalc_rate, + .round_rate = cdce925_clk_y1_round_rate, + .set_rate = cdce925_clk_y1_set_rate, +}; + + +static struct regmap_config cdce925_regmap_config = { + .name = "configuration0", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0x2F, +}; + +#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00 +#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80 + +static int cdce925_regmap_i2c_write( + void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + u8 reg_data[2]; + + if (count != 2) + return -ENOTSUPP; + + /* First byte is command code */ + reg_data[0] = CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)data)[0]; + reg_data[1] = ((u8 *)data)[1]; + + dev_dbg(&i2c->dev, "%s(%zu) %#x %#x\n", __func__, count, + reg_data[0], reg_data[1]); + + ret = i2c_master_send(i2c, reg_data, count); + if (likely(ret == count)) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int cdce925_regmap_i2c_read(void *context, + const void *reg, size_t reg_size, void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg xfer[2]; + int ret; + u8 reg_data[2]; + + if (reg_size != 1) + return -ENOTSUPP; + + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].buf = reg_data; + if (val_size == 1) { + reg_data[0] = + CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)reg)[0]; + xfer[0].len = 1; + } else { + reg_data[0] = + CDCE925_I2C_COMMAND_BLOCK_TRANSFER | ((u8 *)reg)[0]; + reg_data[1] = val_size; + xfer[0].len = 2; + } + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = val_size; + xfer[1].buf = val; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (likely(ret == 2)) { + dev_dbg(&i2c->dev, "%s(%zu, %zu) %#x %#x\n", __func__, + reg_size, val_size, reg_data[0], *((u8 *)val)); + return 0; + } else if (ret < 0) + return ret; + else + return -EIO; +} + +/* The CDCE925 uses a funky way to read/write registers. Bulk mode is + * just weird, so just use the single byte mode exclusively. */ +static struct regmap_bus regmap_cdce925_bus = { + .write = cdce925_regmap_i2c_write, + .read = cdce925_regmap_i2c_read, +}; + +static int cdce925_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clk_cdce925_chip *data; + struct device_node *node = client->dev.of_node; + const char *parent_name; + const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,}; + struct clk_init_data init; + struct clk *clk; + u32 value; + int i; + int err; + struct device_node *np_output; + char child_name[6]; + + dev_dbg(&client->dev, "%s\n", __func__); + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->i2c_client = client; + data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus, + &client->dev, &cdce925_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); + + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + dev_err(&client->dev, "missing parent clock\n"); + return -ENODEV; + } + dev_dbg(&client->dev, "parent is: %s\n", parent_name); + + if (of_property_read_u32(node, "xtal-load-pf", &value) == 0) + regmap_write(data->regmap, + CDCE925_REG_XCSEL, (value << 3) & 0xF8); + /* PWDN bit */ + regmap_update_bits(data->regmap, CDCE925_REG_GLOBAL1, BIT(4), 0); + + /* Set input source for Y1 to be the XTAL */ + regmap_update_bits(data->regmap, 0x02, BIT(7), 0); + + init.ops = &cdce925_pll_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = parent_name ? 1 : 0; + + /* Register PLL clocks */ + for (i = 0; i < NUMBER_OF_PLLS; ++i) { + pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d", + client->dev.of_node->name, i); + init.name = pll_clk_name[i]; + data->pll[i].chip = data; + data->pll[i].hw.init = &init; + data->pll[i].index = i; + clk = devm_clk_register(&client->dev, &data->pll[i].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "Failed register PLL %d\n", i); + err = PTR_ERR(clk); + goto error; + } + sprintf(child_name, "PLL%d", i+1); + np_output = of_get_child_by_name(node, child_name); + if (!np_output) + continue; + if (!of_property_read_u32(np_output, + "clock-frequency", &value)) { + err = clk_set_rate(clk, value); + if (err) + dev_err(&client->dev, + "unable to set PLL frequency %ud\n", + value); + } + if (!of_property_read_u32(np_output, + "spread-spectrum", &value)) { + u8 flag = of_property_read_bool(np_output, + "spread-spectrum-center") ? 0x80 : 0x00; + regmap_update_bits(data->regmap, + 0x16 + (i*CDCE925_OFFSET_PLL), + 0x80, flag); + regmap_update_bits(data->regmap, + 0x12 + (i*CDCE925_OFFSET_PLL), + 0x07, value & 0x07); + } + } + + /* Register output clock Y1 */ + init.ops = &cdce925_clk_y1_ops; + init.flags = 0; + init.num_parents = 1; + init.parent_names = &parent_name; /* Mux Y1 to input */ + init.name = kasprintf(GFP_KERNEL, "%s.Y1", client->dev.of_node->name); + data->clk[0].chip = data; + data->clk[0].hw.init = &init; + data->clk[0].index = 0; + data->clk[0].pdiv = 1; + clk = devm_clk_register(&client->dev, &data->clk[0].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration Y1 failed\n"); + err = PTR_ERR(clk); + goto error; + } + data->dt_clk[0] = clk; + + /* Register output clocks Y2 .. Y5*/ + init.ops = &cdce925_clk_ops; + init.flags = CLK_SET_RATE_PARENT; + init.num_parents = 1; + for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) { + init.name = kasprintf(GFP_KERNEL, "%s.Y%d", + client->dev.of_node->name, i+1); + data->clk[i].chip = data; + data->clk[i].hw.init = &init; + data->clk[i].index = i; + data->clk[i].pdiv = 1; + switch (i) { + case 1: + case 2: + /* Mux Y2/3 to PLL1 */ + init.parent_names = &pll_clk_name[0]; + break; + case 3: + case 4: + /* Mux Y4/5 to PLL2 */ + init.parent_names = &pll_clk_name[1]; + break; + } + clk = devm_clk_register(&client->dev, &data->clk[i].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration failed\n"); + err = PTR_ERR(clk); + goto error; + } + data->dt_clk[i] = clk; + } + + /* Register the output clocks */ + data->onecell.clk_num = NUMBER_OF_OUTPUTS; + data->onecell.clks = data->dt_clk; + err = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, + &data->onecell); + if (err) + dev_err(&client->dev, "unable to add OF clock provider\n"); + + err = 0; + +error: + for (i = 0; i < NUMBER_OF_PLLS; ++i) + /* clock framework made a copy of the name */ + kfree(pll_clk_name[i]); + + return err; +} + +static const struct i2c_device_id cdce925_id[] = { + { "cdce925", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cdce925_id); + +static const struct of_device_id clk_cdce925_of_match[] = { + { .compatible = "ti,cdce925" }, + { }, +}; +MODULE_DEVICE_TABLE(of, clk_cdce925_of_match); + +static struct i2c_driver cdce925_driver = { + .driver = { + .name = "cdce925", + .of_match_table = of_match_ptr(clk_cdce925_of_match), + }, + .probe = cdce925_probe, + .id_table = cdce925_id, +}; +module_i2c_driver(cdce925_driver); + +MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); +MODULE_DESCRIPTION("cdce925 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 956b7e54fa1c..616f5aef3c26 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -188,7 +188,7 @@ static void clk_composite_disable(struct clk_hw *hw) } struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, @@ -200,10 +200,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk_ops *clk_composite_ops; composite = kzalloc(sizeof(*composite), GFP_KERNEL); - if (!composite) { - pr_err("%s: could not allocate composite clk\n", __func__); + if (!composite) return ERR_PTR(-ENOMEM); - } init.name = name; init.flags = flags | CLK_IS_BASIC; diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 48a65b2b4027..43a218f35b19 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -106,8 +106,9 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) rc = clk_set_rate(clk, rate); if (rc < 0) - pr_err("clk: couldn't set %s clock rate: %d\n", - __clk_get_name(clk), rc); + pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n", + __clk_get_name(clk), rate, rc, + clk_get_rate(clk)); clk_put(clk); } index++; @@ -124,7 +125,7 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) * and sets any specified clock parents and rates. The @clk_supplier argument * should be set to true if @node may be also a clock supplier of any clock * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. - * If @clk_supplier is false the function exits returnning 0 as soon as it + * If @clk_supplier is false the function exits returning 0 as soon as it * determines the @node is also a supplier of any of the clocks. */ int of_clk_set_defaults(struct device_node *node, bool clk_supplier) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 25006a8bb8e6..706b5783c360 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -430,11 +430,9 @@ static struct clk *_register_divider(struct device *dev, const char *name, } /* allocate the divider */ - div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); - if (!div) { - pr_err("%s: could not allocate divider clk\n", __func__); + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_divider_ops; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index d9e3f671c2ea..fccabe497f6e 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -55,10 +55,16 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { + /* + * We must report success but we can do so unconditionally because + * clk_factor_round_rate returns values that ensure this call is a + * nop. + */ + return 0; } -struct clk_ops clk_fixed_factor_ops = { +const struct clk_ops clk_fixed_factor_ops = { .round_rate = clk_factor_round_rate, .set_rate = clk_factor_set_rate, .recalc_rate = clk_factor_recalc_rate, @@ -74,10 +80,8 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, struct clk *clk; fix = kmalloc(sizeof(*fix), GFP_KERNEL); - if (!fix) { - pr_err("%s: could not allocate fixed factor clk\n", __func__); + if (!fix) return ERR_PTR(-ENOMEM); - } /* struct clk_fixed_factor assignments */ fix->mult = mult; diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 0fc56ab6e844..f85ec8d1711f 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -65,11 +65,9 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, struct clk_init_data init; /* allocate fixed-rate clock */ - fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); - if (!fixed) { - pr_err("%s: could not allocate fixed clk\n", __func__); + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_fixed_rate_ops; diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 6aa72d9d79ba..140eb5844dc4 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -109,10 +109,8 @@ struct clk *clk_register_fractional_divider(struct device *dev, struct clk *clk; fd = kzalloc(sizeof(*fd), GFP_KERNEL); - if (!fd) { - dev_err(dev, "could not allocate fractional divider clk\n"); + if (!fd) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_fractional_divider_ops; diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 3f0e4200cb5d..551dd0672794 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -135,11 +135,9 @@ struct clk *clk_register_gate(struct device *dev, const char *name, } /* allocate the gate */ - gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - pr_err("%s: could not allocate gated clk\n", __func__); + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_gate_ops; diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c index a71cabedda93..f564e624fb93 100644 --- a/drivers/clk/clk-gpio-gate.c +++ b/drivers/clk/clk-gpio-gate.c @@ -189,7 +189,7 @@ static struct clk *of_clk_gpio_gate_delayed_register_get( /** * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock */ -void __init of_gpio_gate_clk_setup(struct device_node *node) +static void __init of_gpio_gate_clk_setup(struct device_node *node) { struct clk_gpio_gate_delayed_register_data *data; @@ -203,6 +203,5 @@ void __init of_gpio_gate_clk_setup(struct device_node *node) of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data); } -EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup); CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup); #endif diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c index ca80103ac188..d4c61985f448 100644 --- a/drivers/clk/clk-ls1x.c +++ b/drivers/clk/clk-ls1x.c @@ -80,9 +80,9 @@ static struct clk *__init clk_register_pll(struct device *dev, return clk; } -static const char const *cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; -static const char const *ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; -static const char const *dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; +static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; +static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; +static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; void __init ls1x_clk_init(void) { diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c index 6505049d50f1..35af9cb6da4f 100644 --- a/drivers/clk/clk-max-gen.c +++ b/drivers/clk/clk-max-gen.c @@ -31,6 +31,8 @@ #include <linux/of.h> #include <linux/export.h> +#include "clk-max-gen.h" + struct max_gen_clk { struct regmap *regmap; u32 mask; diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index 30a3b6999e10..5181b89c3cb2 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -15,7 +15,7 @@ #include <linux/of_address.h> #include <linux/clkdev.h> -void __init moxart_of_pll_clk_init(struct device_node *node) +static void __init moxart_of_pll_clk_init(struct device_node *node) { static void __iomem *base; struct clk *clk, *ref_clk; @@ -53,7 +53,7 @@ void __init moxart_of_pll_clk_init(struct device_node *node) CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", moxart_of_pll_clk_init); -void __init moxart_of_apb_clk_init(struct device_node *node) +static void __init moxart_of_apb_clk_init(struct device_node *node) { static void __iomem *base; struct clk *clk, *pll_clk; diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 69a094c3783d..6066a01b20ea 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -114,7 +114,8 @@ const struct clk_ops clk_mux_ro_ops = { EXPORT_SYMBOL_GPL(clk_mux_ro_ops); struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { @@ -166,7 +167,8 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, EXPORT_SYMBOL_GPL(clk_register_mux_table); struct clk *clk_register_mux(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) { diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 30335d3b99af..e39e1e680b3c 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -552,7 +552,8 @@ static const struct clk_ops si5351_pll_ops = { * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c * MSx_P3[19:0] = c * - * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0 + * MS[6,7] are integer (P1) divide only, P1 = divide value, + * P2 and P3 are not applicable * * for 150MHz < fOUT <= 160MHz: * @@ -606,9 +607,6 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, if (!hwdata->params.valid) si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); - if (hwdata->params.p3 == 0) - return parent_rate; - /* * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3) * multisync6-7: fOUT = fIN / P1 @@ -616,6 +614,8 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, rate = parent_rate; if (hwdata->num > 5) { m = hwdata->params.p1; + } else if (hwdata->params.p3 == 0) { + return parent_rate; } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { m = 4; @@ -679,6 +679,16 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, c = 1; *parent_rate = a * rate; + } else if (hwdata->num >= 6) { + /* determine the closest integer divider */ + a = DIV_ROUND_CLOSEST(*parent_rate, rate); + if (a < SI5351_MULTISYNTH_A_MIN) + a = SI5351_MULTISYNTH_A_MIN; + if (a > SI5351_MULTISYNTH67_A_MAX) + a = SI5351_MULTISYNTH67_A_MAX; + + b = 0; + c = 1; } else { unsigned long rfrac, denom; @@ -692,9 +702,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, a = *parent_rate / rate; if (a < SI5351_MULTISYNTH_A_MIN) a = SI5351_MULTISYNTH_A_MIN; - if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX) - a = SI5351_MULTISYNTH67_A_MAX; - else if (a > SI5351_MULTISYNTH_A_MAX) + if (a > SI5351_MULTISYNTH_A_MAX) a = SI5351_MULTISYNTH_A_MAX; /* find best approximation for b/c = fVCO mod fOUT */ @@ -723,6 +731,10 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, hwdata->params.p3 = 1; hwdata->params.p2 = 0; hwdata->params.p1 = 0; + } else if (hwdata->num >= 6) { + hwdata->params.p3 = 0; + hwdata->params.p2 = 0; + hwdata->params.p1 = a; } else { hwdata->params.p3 = c; hwdata->params.p2 = (128 * b) % c; diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c new file mode 100644 index 000000000000..b9b12a742970 --- /dev/null +++ b/drivers/clk/clk-stm32f4.c @@ -0,0 +1,380 @@ +/* + * Author: Daniel Thompson <daniel.thompson@linaro.org> + * + * Inspired by clk-asm9260.c . + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define STM32F4_RCC_PLLCFGR 0x04 +#define STM32F4_RCC_CFGR 0x08 +#define STM32F4_RCC_AHB1ENR 0x30 +#define STM32F4_RCC_AHB2ENR 0x34 +#define STM32F4_RCC_AHB3ENR 0x38 +#define STM32F4_RCC_APB1ENR 0x40 +#define STM32F4_RCC_APB2ENR 0x44 + +struct stm32f4_gate_data { + u8 offset; + u8 bit_idx; + const char *name; + const char *parent_name; + unsigned long flags; +}; + +static const struct stm32f4_gate_data stm32f4_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, +}; + +/* + * MAX_CLKS is the maximum value in the enumeration below plus the combined + * hweight of stm32f42xx_gate_map (plus one). + */ +#define MAX_CLKS 74 + +enum { SYSTICK, FCLK }; + +/* + * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx + * have gate bits associated with them. Its combined hweight is 71. + */ +static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull, + 0x0000000000000001ull, + 0x04777f33f6fec9ffull }; + +static struct clk *clks[MAX_CLKS]; +static DEFINE_SPINLOCK(stm32f4_clk_lock); +static void __iomem *base; + +/* + * "Multiplier" device for APBx clocks. + * + * The APBx dividers are power-of-two dividers and, if *not* running in 1:1 + * mode, they also tap out the one of the low order state bits to run the + * timers. ST datasheets represent this feature as a (conditional) clock + * multiplier. + */ +struct clk_apb_mul { + struct clk_hw hw; + u8 bit_idx; +}; + +#define to_clk_apb_mul(_hw) container_of(_hw, struct clk_apb_mul, hw) + +static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + return parent_rate * 2; + + return parent_rate; +} + +static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + unsigned long mult = 1; + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + mult = 2; + + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { + unsigned long best_parent = rate / mult; + + *prate = + __clk_round_rate(__clk_get_parent(hw->clk), best_parent); + } + + return *prate * mult; +} + +static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + /* + * We must report success but we can do so unconditionally because + * clk_apb_mul_round_rate returns values that ensure this call is a + * nop. + */ + + return 0; +} + +static const struct clk_ops clk_apb_mul_factor_ops = { + .round_rate = clk_apb_mul_round_rate, + .set_rate = clk_apb_mul_set_rate, + .recalc_rate = clk_apb_mul_recalc_rate, +}; + +static struct clk *clk_register_apb_mul(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, u8 bit_idx) +{ + struct clk_apb_mul *am; + struct clk_init_data init; + struct clk *clk; + + am = kzalloc(sizeof(*am), GFP_KERNEL); + if (!am) + return ERR_PTR(-ENOMEM); + + am->bit_idx = bit_idx; + am->hw.init = &init; + + init.name = name; + init.ops = &clk_apb_mul_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &am->hw); + + if (IS_ERR(clk)) + kfree(am); + + return clk; +} + +/* + * Decode current PLL state and (statically) model the state we inherit from + * the bootloader. + */ +static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk) +{ + unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); + + unsigned long pllm = pllcfgr & 0x3f; + unsigned long plln = (pllcfgr >> 6) & 0x1ff; + unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1); + const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk; + unsigned long pllq = (pllcfgr >> 24) & 0xf; + + clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm); + clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp); + clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq); +} + +/* + * Converts the primary and secondary indices (as they appear in DT) to an + * offset into our struct clock array. + */ +static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) +{ + u64 table[ARRAY_SIZE(stm32f42xx_gate_map)]; + + if (primary == 1) { + if (WARN_ON(secondary > FCLK)) + return -EINVAL; + return secondary; + } + + memcpy(table, stm32f42xx_gate_map, sizeof(table)); + + /* only bits set in table can be used as indices */ + if (WARN_ON(secondary > 8 * sizeof(table) || + 0 == (table[BIT_ULL_WORD(secondary)] & + BIT_ULL_MASK(secondary)))) + return -EINVAL; + + /* mask out bits above our current index */ + table[BIT_ULL_WORD(secondary)] &= + GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); + + return FCLK + hweight64(table[0]) + + (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + + (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); +} + +static struct clk * +stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data) +{ + int i = stm32f4_rcc_lookup_clk_idx(clkspec->args[0], clkspec->args[1]); + + if (i < 0) + return ERR_PTR(-EINVAL); + + return clks[i]; +} + +static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" }; + +static const struct clk_div_table ahb_div_table[] = { + { 0x0, 1 }, { 0x1, 1 }, { 0x2, 1 }, { 0x3, 1 }, + { 0x4, 1 }, { 0x5, 1 }, { 0x6, 1 }, { 0x7, 1 }, + { 0x8, 2 }, { 0x9, 4 }, { 0xa, 8 }, { 0xb, 16 }, + { 0xc, 64 }, { 0xd, 128 }, { 0xe, 256 }, { 0xf, 512 }, + { 0 }, +}; + +static const struct clk_div_table apb_div_table[] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static void __init stm32f4_rcc_init(struct device_node *np) +{ + const char *hse_clk; + int n; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: unable to map resource", np->name); + return; + } + + hse_clk = of_clk_get_parent_name(np, 0); + + clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0, + 16000000, 160000); + stm32f4_rcc_register_pll(hse_clk, "hsi"); + + sys_parents[1] = hse_clk; + clk_register_mux_table( + NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0, + base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "ahb_div", "sys", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 4, 4, 0, ahb_div_table, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "apb1_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 10, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb1_mul", "apb1_div", + CLK_SET_RATE_PARENT, 12); + + clk_register_divider_table(NULL, "apb2_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 13, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb2_mul", "apb2_div", + CLK_SET_RATE_PARENT, 15); + + clks[SYSTICK] = clk_register_fixed_factor(NULL, "systick", "ahb_div", + 0, 1, 8); + clks[FCLK] = clk_register_fixed_factor(NULL, "fclk", "ahb_div", + 0, 1, 1); + + for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) { + const struct stm32f4_gate_data *gd = &stm32f4_gates[n]; + unsigned int secondary = + 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx; + int idx = stm32f4_rcc_lookup_clk_idx(0, secondary); + + if (idx < 0) + goto fail; + + clks[idx] = clk_register_gate( + NULL, gd->name, gd->parent_name, gd->flags, + base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[n])) { + pr_err("%s: Unable to register leaf clock %s\n", + np->full_name, gd->name); + goto fail; + } + } + + of_clk_add_provider(np, stm32f4_rcc_lookup_clk, NULL); + return; +fail: + iounmap(base); +} +CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c index 406bfc1375b2..18bf5e576b93 100644 --- a/drivers/clk/clk-u300.c +++ b/drivers/clk/clk-u300.c @@ -12,6 +12,7 @@ #include <linux/clk-provider.h> #include <linux/spinlock.h> #include <linux/of.h> +#include <linux/platform_data/clk-u300.h> /* APP side SYSCON registers */ /* CLK Control Register 16bit (R/W) */ diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index dd8a62d8f11f..f26b3ac36b27 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -42,12 +42,12 @@ static DEFINE_SPINLOCK(clk_lock); -static inline u32 xgene_clk_read(void *csr) +static inline u32 xgene_clk_read(void __iomem *csr) { return readl_relaxed(csr); } -static inline void xgene_clk_write(u32 data, void *csr) +static inline void xgene_clk_write(u32 data, void __iomem *csr) { return writel_relaxed(data, csr); } @@ -119,7 +119,7 @@ static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw, return fvco / nout; } -const struct clk_ops xgene_clk_pll_ops = { +static const struct clk_ops xgene_clk_pll_ops = { .is_enabled = xgene_clk_pll_is_enabled, .recalc_rate = xgene_clk_pll_recalc_rate, }; @@ -167,7 +167,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty { const char *clk_name = np->full_name; struct clk *clk; - void *reg; + void __iomem *reg; reg = of_iomap(np, 0); if (reg == NULL) { @@ -222,20 +222,22 @@ static int xgene_clk_enable(struct clk_hw *hw) struct xgene_clk *pclk = to_xgene_clk(hw); unsigned long flags = 0; u32 data; + phys_addr_t reg; if (pclk->lock) spin_lock_irqsave(pclk->lock, flags); if (pclk->param.csr_reg != NULL) { pr_debug("%s clock enabled\n", pclk->name); + reg = __pa(pclk->param.csr_reg); /* First enable the clock */ data = xgene_clk_read(pclk->param.csr_reg + pclk->param.reg_clk_offset); data |= pclk->param.reg_clk_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_clk_offset); - pr_debug("%s clock PADDR base 0x%016LX clk offset 0x%08X mask 0x%08X value 0x%08X\n", - pclk->name, __pa(pclk->param.csr_reg), + pr_debug("%s clock PADDR base %pa clk offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, ®, pclk->param.reg_clk_offset, pclk->param.reg_clk_mask, data); @@ -245,8 +247,8 @@ static int xgene_clk_enable(struct clk_hw *hw) data &= ~pclk->param.reg_csr_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_csr_offset); - pr_debug("%s CSR RESET PADDR base 0x%016LX csr offset 0x%08X mask 0x%08X value 0x%08X\n", - pclk->name, __pa(pclk->param.csr_reg), + pr_debug("%s CSR RESET PADDR base %pa csr offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, ®, pclk->param.reg_csr_offset, pclk->param.reg_csr_mask, data); } @@ -386,7 +388,7 @@ static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate, return parent_rate / divider; } -const struct clk_ops xgene_clk_ops = { +static const struct clk_ops xgene_clk_ops = { .enable = xgene_clk_enable, .disable = xgene_clk_disable, .is_enabled = xgene_clk_is_enabled, @@ -456,7 +458,7 @@ static void __init xgene_devclk_init(struct device_node *np) parameters.csr_reg = NULL; parameters.divider_reg = NULL; for (i = 0; i < 2; i++) { - void *map_res; + void __iomem *map_res; rc = of_address_to_resource(np, i, &res); if (rc != 0) { if (i == 0) { diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5b0f41868b42..ddb4b541016f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/sched.h> +#include <linux/clkdev.h> #include "clk.h" @@ -37,13 +38,6 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); -static long clk_core_get_accuracy(struct clk_core *clk); -static unsigned long clk_core_get_rate(struct clk_core *clk); -static int clk_core_get_phase(struct clk_core *clk); -static bool clk_core_is_prepared(struct clk_core *clk); -static bool clk_core_is_enabled(struct clk_core *clk); -static struct clk_core *clk_core_lookup(const char *name); - /*** private data structures ***/ struct clk_core { @@ -68,11 +62,11 @@ struct clk_core { int phase; struct hlist_head children; struct hlist_node child_node; - struct hlist_node debug_node; struct hlist_head clks; unsigned int notifier_count; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; + struct hlist_node debug_node; #endif struct kref ref; }; @@ -145,382 +139,71 @@ static void clk_enable_unlock(unsigned long flags) spin_unlock_irqrestore(&enable_lock, flags); } -/*** debugfs support ***/ - -#ifdef CONFIG_DEBUG_FS -#include <linux/debugfs.h> - -static struct dentry *rootdir; -static int inited = 0; -static DEFINE_MUTEX(clk_debug_lock); -static HLIST_HEAD(clk_debug_list); - -static struct hlist_head *all_lists[] = { - &clk_root_list, - &clk_orphan_list, - NULL, -}; - -static struct hlist_head *orphan_list[] = { - &clk_orphan_list, - NULL, -}; - -static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, - int level) -{ - if (!c) - return; - - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", - level * 3 + 1, "", - 30 - level * 3, c->name, - c->enable_count, c->prepare_count, clk_core_get_rate(c), - clk_core_get_accuracy(c), clk_core_get_phase(c)); -} - -static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, - int level) -{ - struct clk_core *child; - - if (!c) - return; - - clk_summary_show_one(s, c, level); - - hlist_for_each_entry(child, &c->children, child_node) - clk_summary_show_subtree(s, child, level + 1); -} - -static int clk_summary_show(struct seq_file *s, void *data) -{ - struct clk_core *c; - struct hlist_head **lists = (struct hlist_head **)s->private; - - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); - seq_puts(s, "----------------------------------------------------------------------------------------\n"); - - clk_prepare_lock(); - - for (; *lists; lists++) - hlist_for_each_entry(c, *lists, child_node) - clk_summary_show_subtree(s, c, 0); - - clk_prepare_unlock(); - - return 0; -} - - -static int clk_summary_open(struct inode *inode, struct file *file) -{ - return single_open(file, clk_summary_show, inode->i_private); -} - -static const struct file_operations clk_summary_fops = { - .open = clk_summary_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) -{ - if (!c) - return; - - seq_printf(s, "\"%s\": { ", c->name); - seq_printf(s, "\"enable_count\": %d,", c->enable_count); - seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", clk_core_get_rate(c)); - seq_printf(s, "\"accuracy\": %lu", clk_core_get_accuracy(c)); - seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); -} - -static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) -{ - struct clk_core *child; - - if (!c) - return; - - clk_dump_one(s, c, level); - - hlist_for_each_entry(child, &c->children, child_node) { - seq_printf(s, ","); - clk_dump_subtree(s, child, level + 1); - } - - seq_printf(s, "}"); -} - -static int clk_dump(struct seq_file *s, void *data) -{ - struct clk_core *c; - bool first_node = true; - struct hlist_head **lists = (struct hlist_head **)s->private; - - seq_printf(s, "{"); - - clk_prepare_lock(); - - for (; *lists; lists++) { - hlist_for_each_entry(c, *lists, child_node) { - if (!first_node) - seq_puts(s, ","); - first_node = false; - clk_dump_subtree(s, c, 0); - } - } - - clk_prepare_unlock(); - - seq_printf(s, "}"); - return 0; -} - - -static int clk_dump_open(struct inode *inode, struct file *file) -{ - return single_open(file, clk_dump, inode->i_private); -} - -static const struct file_operations clk_dump_fops = { - .open = clk_dump_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int clk_debug_create_one(struct clk_core *clk, struct dentry *pdentry) -{ - struct dentry *d; - int ret = -ENOMEM; - - if (!clk || !pdentry) { - ret = -EINVAL; - goto out; - } - - d = debugfs_create_dir(clk->name, pdentry); - if (!d) - goto out; - - clk->dentry = d; - - d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry, - (u32 *)&clk->rate); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_accuracy", S_IRUGO, clk->dentry, - (u32 *)&clk->accuracy); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry, - (u32 *)&clk->phase); - if (!d) - goto err_out; - - d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, - (u32 *)&clk->flags); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry, - (u32 *)&clk->prepare_count); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry, - (u32 *)&clk->enable_count); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry, - (u32 *)&clk->notifier_count); - if (!d) - goto err_out; - - if (clk->ops->debug_init) { - ret = clk->ops->debug_init(clk->hw, clk->dentry); - if (ret) - goto err_out; - } - - ret = 0; - goto out; - -err_out: - debugfs_remove_recursive(clk->dentry); - clk->dentry = NULL; -out: - return ret; -} - -/** - * clk_debug_register - add a clk node to the debugfs clk tree - * @clk: the clk being added to the debugfs clk tree - * - * Dynamically adds a clk to the debugfs clk tree if debugfs has been - * initialized. Otherwise it bails out early since the debugfs clk tree - * will be created lazily by clk_debug_init as part of a late_initcall. - */ -static int clk_debug_register(struct clk_core *clk) -{ - int ret = 0; - - mutex_lock(&clk_debug_lock); - hlist_add_head(&clk->debug_node, &clk_debug_list); - - if (!inited) - goto unlock; - - ret = clk_debug_create_one(clk, rootdir); -unlock: - mutex_unlock(&clk_debug_lock); - - return ret; -} - - /** - * clk_debug_unregister - remove a clk node from the debugfs clk tree - * @clk: the clk being removed from the debugfs clk tree - * - * Dynamically removes a clk and all it's children clk nodes from the - * debugfs clk tree if clk->dentry points to debugfs created by - * clk_debug_register in __clk_init. - */ -static void clk_debug_unregister(struct clk_core *clk) +static bool clk_core_is_prepared(struct clk_core *core) { - mutex_lock(&clk_debug_lock); - hlist_del_init(&clk->debug_node); - debugfs_remove_recursive(clk->dentry); - clk->dentry = NULL; - mutex_unlock(&clk_debug_lock); -} - -struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, - void *data, const struct file_operations *fops) -{ - struct dentry *d = NULL; - - if (hw->core->dentry) - d = debugfs_create_file(name, mode, hw->core->dentry, data, - fops); + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!core->ops->is_prepared) + return core->prepare_count; - return d; + return core->ops->is_prepared(core->hw); } -EXPORT_SYMBOL_GPL(clk_debugfs_add_file); -/** - * clk_debug_init - lazily create the debugfs clk tree visualization - * - * clks are often initialized very early during boot before memory can - * be dynamically allocated and well before debugfs is setup. - * clk_debug_init walks the clk tree hierarchy while holding - * prepare_lock and creates the topology as part of a late_initcall, - * thus insuring that clks initialized very early will still be - * represented in the debugfs clk tree. This function should only be - * called once at boot-time, and all other clks added dynamically will - * be done so with clk_debug_register. - */ -static int __init clk_debug_init(void) +static bool clk_core_is_enabled(struct clk_core *core) { - struct clk_core *clk; - struct dentry *d; - - rootdir = debugfs_create_dir("clk", NULL); - - if (!rootdir) - return -ENOMEM; - - d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, - &clk_summary_fops); - if (!d) - return -ENOMEM; - - d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, - &clk_dump_fops); - if (!d) - return -ENOMEM; - - d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, - &orphan_list, &clk_summary_fops); - if (!d) - return -ENOMEM; - - d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, - &orphan_list, &clk_dump_fops); - if (!d) - return -ENOMEM; - - mutex_lock(&clk_debug_lock); - hlist_for_each_entry(clk, &clk_debug_list, debug_node) - clk_debug_create_one(clk, rootdir); - - inited = 1; - mutex_unlock(&clk_debug_lock); + /* + * .is_enabled is only mandatory for clocks that gate + * fall back to software usage counter if .is_enabled is missing + */ + if (!core->ops->is_enabled) + return core->enable_count; - return 0; -} -late_initcall(clk_debug_init); -#else -static inline int clk_debug_register(struct clk_core *clk) { return 0; } -static inline void clk_debug_reparent(struct clk_core *clk, - struct clk_core *new_parent) -{ + return core->ops->is_enabled(core->hw); } -static inline void clk_debug_unregister(struct clk_core *clk) -{ -} -#endif -/* caller must hold prepare_lock */ -static void clk_unprepare_unused_subtree(struct clk_core *clk) +static void clk_unprepare_unused_subtree(struct clk_core *core) { struct clk_core *child; lockdep_assert_held(&prepare_lock); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) clk_unprepare_unused_subtree(child); - if (clk->prepare_count) + if (core->prepare_count) return; - if (clk->flags & CLK_IGNORE_UNUSED) + if (core->flags & CLK_IGNORE_UNUSED) return; - if (clk_core_is_prepared(clk)) { - trace_clk_unprepare(clk); - if (clk->ops->unprepare_unused) - clk->ops->unprepare_unused(clk->hw); - else if (clk->ops->unprepare) - clk->ops->unprepare(clk->hw); - trace_clk_unprepare_complete(clk); + if (clk_core_is_prepared(core)) { + trace_clk_unprepare(core); + if (core->ops->unprepare_unused) + core->ops->unprepare_unused(core->hw); + else if (core->ops->unprepare) + core->ops->unprepare(core->hw); + trace_clk_unprepare_complete(core); } } -/* caller must hold prepare_lock */ -static void clk_disable_unused_subtree(struct clk_core *clk) +static void clk_disable_unused_subtree(struct clk_core *core) { struct clk_core *child; unsigned long flags; lockdep_assert_held(&prepare_lock); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) clk_disable_unused_subtree(child); flags = clk_enable_lock(); - if (clk->enable_count) + if (core->enable_count) goto unlock_out; - if (clk->flags & CLK_IGNORE_UNUSED) + if (core->flags & CLK_IGNORE_UNUSED) goto unlock_out; /* @@ -528,13 +211,13 @@ static void clk_disable_unused_subtree(struct clk_core *clk) * sequence. call .disable_unused if available, otherwise fall * back to .disable */ - if (clk_core_is_enabled(clk)) { - trace_clk_disable(clk); - if (clk->ops->disable_unused) - clk->ops->disable_unused(clk->hw); - else if (clk->ops->disable) - clk->ops->disable(clk->hw); - trace_clk_disable_complete(clk); + if (clk_core_is_enabled(core)) { + trace_clk_disable(core); + if (core->ops->disable_unused) + core->ops->disable_unused(core->hw); + else if (core->ops->disable) + core->ops->disable(core->hw); + trace_clk_disable_complete(core); } unlock_out: @@ -551,7 +234,7 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup); static int clk_disable_unused(void) { - struct clk_core *clk; + struct clk_core *core; if (clk_ignore_unused) { pr_warn("clk: Not disabling unused clocks\n"); @@ -560,17 +243,17 @@ static int clk_disable_unused(void) clk_prepare_lock(); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_disable_unused_subtree(core); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_disable_unused_subtree(core); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_unprepare_unused_subtree(core); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(core); clk_prepare_unlock(); @@ -608,18 +291,61 @@ struct clk *__clk_get_parent(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_parent); -static struct clk_core *clk_core_get_parent_by_index(struct clk_core *clk, +static struct clk_core *__clk_lookup_subtree(const char *name, + struct clk_core *core) +{ + struct clk_core *child; + struct clk_core *ret; + + if (!strcmp(core->name, name)) + return core; + + hlist_for_each_entry(child, &core->children, child_node) { + ret = __clk_lookup_subtree(name, child); + if (ret) + return ret; + } + + return NULL; +} + +static struct clk_core *clk_core_lookup(const char *name) +{ + struct clk_core *root_clk; + struct clk_core *ret; + + if (!name) + return NULL; + + /* search the 'proper' clk tree first */ + hlist_for_each_entry(root_clk, &clk_root_list, child_node) { + ret = __clk_lookup_subtree(name, root_clk); + if (ret) + return ret; + } + + /* if not found, then search the orphan tree */ + hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) { + ret = __clk_lookup_subtree(name, root_clk); + if (ret) + return ret; + } + + return NULL; +} + +static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core, u8 index) { - if (!clk || index >= clk->num_parents) + if (!core || index >= core->num_parents) return NULL; - else if (!clk->parents) - return clk_core_lookup(clk->parent_names[index]); - else if (!clk->parents[index]) - return clk->parents[index] = - clk_core_lookup(clk->parent_names[index]); + else if (!core->parents) + return clk_core_lookup(core->parent_names[index]); + else if (!core->parents[index]) + return core->parents[index] = + clk_core_lookup(core->parent_names[index]); else - return clk->parents[index]; + return core->parents[index]; } struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) @@ -640,21 +366,21 @@ unsigned int __clk_get_enable_count(struct clk *clk) return !clk ? 0 : clk->core->enable_count; } -static unsigned long clk_core_get_rate_nolock(struct clk_core *clk) +static unsigned long clk_core_get_rate_nolock(struct clk_core *core) { unsigned long ret; - if (!clk) { + if (!core) { ret = 0; goto out; } - ret = clk->rate; + ret = core->rate; - if (clk->flags & CLK_IS_ROOT) + if (core->flags & CLK_IS_ROOT) goto out; - if (!clk->parent) + if (!core->parent) ret = 0; out: @@ -670,12 +396,12 @@ unsigned long __clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_rate); -static unsigned long __clk_get_accuracy(struct clk_core *clk) +static unsigned long __clk_get_accuracy(struct clk_core *core) { - if (!clk) + if (!core) return 0; - return clk->accuracy; + return core->accuracy; } unsigned long __clk_get_flags(struct clk *clk) @@ -684,27 +410,6 @@ unsigned long __clk_get_flags(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_flags); -static bool clk_core_is_prepared(struct clk_core *clk) -{ - int ret; - - if (!clk) - return false; - - /* - * .is_prepared is optional for clocks that can prepare - * fall back to software usage counter if it is missing - */ - if (!clk->ops->is_prepared) { - ret = clk->prepare_count ? 1 : 0; - goto out; - } - - ret = clk->ops->is_prepared(clk->hw); -out: - return !!ret; -} - bool __clk_is_prepared(struct clk *clk) { if (!clk) @@ -713,27 +418,6 @@ bool __clk_is_prepared(struct clk *clk) return clk_core_is_prepared(clk->core); } -static bool clk_core_is_enabled(struct clk_core *clk) -{ - int ret; - - if (!clk) - return false; - - /* - * .is_enabled is only mandatory for clocks that gate - * fall back to software usage counter if .is_enabled is missing - */ - if (!clk->ops->is_enabled) { - ret = clk->enable_count ? 1 : 0; - goto out; - } - - ret = clk->ops->is_enabled(clk->hw); -out: - return !!ret; -} - bool __clk_is_enabled(struct clk *clk) { if (!clk) @@ -743,49 +427,6 @@ bool __clk_is_enabled(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_is_enabled); -static struct clk_core *__clk_lookup_subtree(const char *name, - struct clk_core *clk) -{ - struct clk_core *child; - struct clk_core *ret; - - if (!strcmp(clk->name, name)) - return clk; - - hlist_for_each_entry(child, &clk->children, child_node) { - ret = __clk_lookup_subtree(name, child); - if (ret) - return ret; - } - - return NULL; -} - -static struct clk_core *clk_core_lookup(const char *name) -{ - struct clk_core *root_clk; - struct clk_core *ret; - - if (!name) - return NULL; - - /* search the 'proper' clk tree first */ - hlist_for_each_entry(root_clk, &clk_root_list, child_node) { - ret = __clk_lookup_subtree(name, root_clk); - if (ret) - return ret; - } - - /* if not found, then search the orphan tree */ - hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) { - ret = __clk_lookup_subtree(name, root_clk); - if (ret) - return ret; - } - - return NULL; -} - static bool mux_is_better_rate(unsigned long rate, unsigned long now, unsigned long best, unsigned long flags) { @@ -853,7 +494,7 @@ struct clk *__clk_lookup(const char *name) return !core ? NULL : core->hw->clk; } -static void clk_core_get_boundaries(struct clk_core *clk, +static void clk_core_get_boundaries(struct clk_core *core, unsigned long *min_rate, unsigned long *max_rate) { @@ -862,10 +503,10 @@ static void clk_core_get_boundaries(struct clk_core *clk, *min_rate = 0; *max_rate = ULONG_MAX; - hlist_for_each_entry(clk_user, &clk->clks, clks_node) + hlist_for_each_entry(clk_user, &core->clks, clks_node) *min_rate = max(*min_rate, clk_user->min_rate); - hlist_for_each_entry(clk_user, &clk->clks, clks_node) + hlist_for_each_entry(clk_user, &core->clks, clks_node) *max_rate = min(*max_rate, clk_user->max_rate); } @@ -901,26 +542,28 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); /*** clk api ***/ -static void clk_core_unprepare(struct clk_core *clk) +static void clk_core_unprepare(struct clk_core *core) { - if (!clk) + lockdep_assert_held(&prepare_lock); + + if (!core) return; - if (WARN_ON(clk->prepare_count == 0)) + if (WARN_ON(core->prepare_count == 0)) return; - if (--clk->prepare_count > 0) + if (--core->prepare_count > 0) return; - WARN_ON(clk->enable_count > 0); + WARN_ON(core->enable_count > 0); - trace_clk_unprepare(clk); + trace_clk_unprepare(core); - if (clk->ops->unprepare) - clk->ops->unprepare(clk->hw); + if (core->ops->unprepare) + core->ops->unprepare(core->hw); - trace_clk_unprepare_complete(clk); - clk_core_unprepare(clk->parent); + trace_clk_unprepare_complete(core); + clk_core_unprepare(core->parent); } /** @@ -945,32 +588,34 @@ void clk_unprepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_unprepare); -static int clk_core_prepare(struct clk_core *clk) +static int clk_core_prepare(struct clk_core *core) { int ret = 0; - if (!clk) + lockdep_assert_held(&prepare_lock); + + if (!core) return 0; - if (clk->prepare_count == 0) { - ret = clk_core_prepare(clk->parent); + if (core->prepare_count == 0) { + ret = clk_core_prepare(core->parent); if (ret) return ret; - trace_clk_prepare(clk); + trace_clk_prepare(core); - if (clk->ops->prepare) - ret = clk->ops->prepare(clk->hw); + if (core->ops->prepare) + ret = core->ops->prepare(core->hw); - trace_clk_prepare_complete(clk); + trace_clk_prepare_complete(core); if (ret) { - clk_core_unprepare(clk->parent); + clk_core_unprepare(core->parent); return ret; } } - clk->prepare_count++; + core->prepare_count++; return 0; } @@ -1002,33 +647,27 @@ int clk_prepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_prepare); -static void clk_core_disable(struct clk_core *clk) +static void clk_core_disable(struct clk_core *core) { - if (!clk) + lockdep_assert_held(&enable_lock); + + if (!core) return; - if (WARN_ON(clk->enable_count == 0)) + if (WARN_ON(core->enable_count == 0)) return; - if (--clk->enable_count > 0) + if (--core->enable_count > 0) return; - trace_clk_disable(clk); + trace_clk_disable(core); - if (clk->ops->disable) - clk->ops->disable(clk->hw); + if (core->ops->disable) + core->ops->disable(core->hw); - trace_clk_disable_complete(clk); + trace_clk_disable_complete(core); - clk_core_disable(clk->parent); -} - -static void __clk_disable(struct clk *clk) -{ - if (!clk) - return; - - clk_core_disable(clk->core); + clk_core_disable(core->parent); } /** @@ -1051,52 +690,46 @@ void clk_disable(struct clk *clk) return; flags = clk_enable_lock(); - __clk_disable(clk); + clk_core_disable(clk->core); clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); -static int clk_core_enable(struct clk_core *clk) +static int clk_core_enable(struct clk_core *core) { int ret = 0; - if (!clk) + lockdep_assert_held(&enable_lock); + + if (!core) return 0; - if (WARN_ON(clk->prepare_count == 0)) + if (WARN_ON(core->prepare_count == 0)) return -ESHUTDOWN; - if (clk->enable_count == 0) { - ret = clk_core_enable(clk->parent); + if (core->enable_count == 0) { + ret = clk_core_enable(core->parent); if (ret) return ret; - trace_clk_enable(clk); + trace_clk_enable(core); - if (clk->ops->enable) - ret = clk->ops->enable(clk->hw); + if (core->ops->enable) + ret = core->ops->enable(core->hw); - trace_clk_enable_complete(clk); + trace_clk_enable_complete(core); if (ret) { - clk_core_disable(clk->parent); + clk_core_disable(core->parent); return ret; } } - clk->enable_count++; + core->enable_count++; return 0; } -static int __clk_enable(struct clk *clk) -{ - if (!clk) - return 0; - - return clk_core_enable(clk->core); -} - /** * clk_enable - ungate a clock * @clk: the clk being ungated @@ -1115,15 +748,18 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; + if (!clk) + return 0; + flags = clk_enable_lock(); - ret = __clk_enable(clk); + ret = clk_core_enable(clk->core); clk_enable_unlock(flags); return ret; } EXPORT_SYMBOL_GPL(clk_enable); -static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, +static unsigned long clk_core_round_rate_nolock(struct clk_core *core, unsigned long rate, unsigned long min_rate, unsigned long max_rate) @@ -1134,25 +770,25 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, lockdep_assert_held(&prepare_lock); - if (!clk) + if (!core) return 0; - parent = clk->parent; + parent = core->parent; if (parent) parent_rate = parent->rate; - if (clk->ops->determine_rate) { + if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - return clk->ops->determine_rate(clk->hw, rate, + return core->ops->determine_rate(core->hw, rate, min_rate, max_rate, &parent_rate, &parent_hw); - } else if (clk->ops->round_rate) - return clk->ops->round_rate(clk->hw, rate, &parent_rate); - else if (clk->flags & CLK_SET_RATE_PARENT) - return clk_core_round_rate_nolock(clk->parent, rate, min_rate, + } else if (core->ops->round_rate) + return core->ops->round_rate(core->hw, rate, &parent_rate); + else if (core->flags & CLK_SET_RATE_PARENT) + return clk_core_round_rate_nolock(core->parent, rate, min_rate, max_rate); else - return clk->rate; + return core->rate; } /** @@ -1162,8 +798,7 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, * @min_rate: returned rate must be greater than this rate * @max_rate: returned rate must be less than this rate * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate and - * .determine_rate. + * Useful for clk_ops such as .set_rate and .determine_rate. */ unsigned long __clk_determine_rate(struct clk_hw *hw, unsigned long rate, @@ -1182,7 +817,7 @@ EXPORT_SYMBOL_GPL(__clk_determine_rate); * @clk: round the rate of this clock * @rate: the rate which is to be rounded * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate + * Useful for clk_ops such as .set_rate */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { @@ -1224,7 +859,7 @@ EXPORT_SYMBOL_GPL(clk_round_rate); /** * __clk_notify - call clk notifier chain - * @clk: struct clk * that is changing rate + * @core: clk that is changing rate * @msg: clk notifier type (see include/linux/clk.h) * @old_rate: old clk rate * @new_rate: new clk rate @@ -1236,7 +871,7 @@ EXPORT_SYMBOL_GPL(clk_round_rate); * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if * a driver returns that. */ -static int __clk_notify(struct clk_core *clk, unsigned long msg, +static int __clk_notify(struct clk_core *core, unsigned long msg, unsigned long old_rate, unsigned long new_rate) { struct clk_notifier *cn; @@ -1247,7 +882,7 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, cnd.new_rate = new_rate; list_for_each_entry(cn, &clk_notifier_list, node) { - if (cn->clk->core == clk) { + if (cn->clk->core == core) { cnd.clk = cn->clk; ret = srcu_notifier_call_chain(&cn->notifier_head, msg, &cnd); @@ -1259,44 +894,42 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, /** * __clk_recalc_accuracies - * @clk: first clk in the subtree + * @core: first clk in the subtree * * Walks the subtree of clks starting with clk and recalculates accuracies as * it goes. Note that if a clk does not implement the .recalc_accuracy - * callback then it is assumed that the clock will take on the accuracy of it's + * callback then it is assumed that the clock will take on the accuracy of its * parent. - * - * Caller must hold prepare_lock. */ -static void __clk_recalc_accuracies(struct clk_core *clk) +static void __clk_recalc_accuracies(struct clk_core *core) { unsigned long parent_accuracy = 0; struct clk_core *child; lockdep_assert_held(&prepare_lock); - if (clk->parent) - parent_accuracy = clk->parent->accuracy; + if (core->parent) + parent_accuracy = core->parent->accuracy; - if (clk->ops->recalc_accuracy) - clk->accuracy = clk->ops->recalc_accuracy(clk->hw, + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, parent_accuracy); else - clk->accuracy = parent_accuracy; + core->accuracy = parent_accuracy; - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) __clk_recalc_accuracies(child); } -static long clk_core_get_accuracy(struct clk_core *clk) +static long clk_core_get_accuracy(struct clk_core *core) { unsigned long accuracy; clk_prepare_lock(); - if (clk && (clk->flags & CLK_GET_ACCURACY_NOCACHE)) - __clk_recalc_accuracies(clk); + if (core && (core->flags & CLK_GET_ACCURACY_NOCACHE)) + __clk_recalc_accuracies(core); - accuracy = __clk_get_accuracy(clk); + accuracy = __clk_get_accuracy(core); clk_prepare_unlock(); return accuracy; @@ -1320,17 +953,17 @@ long clk_get_accuracy(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_accuracy); -static unsigned long clk_recalc(struct clk_core *clk, +static unsigned long clk_recalc(struct clk_core *core, unsigned long parent_rate) { - if (clk->ops->recalc_rate) - return clk->ops->recalc_rate(clk->hw, parent_rate); + if (core->ops->recalc_rate) + return core->ops->recalc_rate(core->hw, parent_rate); return parent_rate; } /** * __clk_recalc_rates - * @clk: first clk in the subtree + * @core: first clk in the subtree * @msg: notification type (see include/linux/clk.h) * * Walks the subtree of clks starting with clk and recalculates rates as it @@ -1339,10 +972,8 @@ static unsigned long clk_recalc(struct clk_core *clk, * * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, * if necessary. - * - * Caller must hold prepare_lock. */ -static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg) +static void __clk_recalc_rates(struct clk_core *core, unsigned long msg) { unsigned long old_rate; unsigned long parent_rate = 0; @@ -1350,34 +981,34 @@ static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg) lockdep_assert_held(&prepare_lock); - old_rate = clk->rate; + old_rate = core->rate; - if (clk->parent) - parent_rate = clk->parent->rate; + if (core->parent) + parent_rate = core->parent->rate; - clk->rate = clk_recalc(clk, parent_rate); + core->rate = clk_recalc(core, parent_rate); /* * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE * & ABORT_RATE_CHANGE notifiers */ - if (clk->notifier_count && msg) - __clk_notify(clk, msg, old_rate, clk->rate); + if (core->notifier_count && msg) + __clk_notify(core, msg, old_rate, core->rate); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) __clk_recalc_rates(child, msg); } -static unsigned long clk_core_get_rate(struct clk_core *clk) +static unsigned long clk_core_get_rate(struct clk_core *core) { unsigned long rate; clk_prepare_lock(); - if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) - __clk_recalc_rates(clk, 0); + if (core && (core->flags & CLK_GET_RATE_NOCACHE)) + __clk_recalc_rates(core, 0); - rate = clk_core_get_rate_nolock(clk); + rate = clk_core_get_rate_nolock(core); clk_prepare_unlock(); return rate; @@ -1400,15 +1031,15 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); -static int clk_fetch_parent_index(struct clk_core *clk, +static int clk_fetch_parent_index(struct clk_core *core, struct clk_core *parent) { int i; - if (!clk->parents) { - clk->parents = kcalloc(clk->num_parents, + if (!core->parents) { + core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); - if (!clk->parents) + if (!core->parents) return -ENOMEM; } @@ -1417,15 +1048,15 @@ static int clk_fetch_parent_index(struct clk_core *clk, * or if not yet cached, use string name comparison and cache * them now to avoid future calls to clk_core_lookup. */ - for (i = 0; i < clk->num_parents; i++) { - if (clk->parents[i] == parent) + for (i = 0; i < core->num_parents; i++) { + if (core->parents[i] == parent) return i; - if (clk->parents[i]) + if (core->parents[i]) continue; - if (!strcmp(clk->parent_names[i], parent->name)) { - clk->parents[i] = clk_core_lookup(parent->name); + if (!strcmp(core->parent_names[i], parent->name)) { + core->parents[i] = clk_core_lookup(parent->name); return i; } } @@ -1433,28 +1064,28 @@ static int clk_fetch_parent_index(struct clk_core *clk, return -EINVAL; } -static void clk_reparent(struct clk_core *clk, struct clk_core *new_parent) +static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) { - hlist_del(&clk->child_node); + hlist_del(&core->child_node); if (new_parent) { /* avoid duplicate POST_RATE_CHANGE notifications */ - if (new_parent->new_child == clk) + if (new_parent->new_child == core) new_parent->new_child = NULL; - hlist_add_head(&clk->child_node, &new_parent->children); + hlist_add_head(&core->child_node, &new_parent->children); } else { - hlist_add_head(&clk->child_node, &clk_orphan_list); + hlist_add_head(&core->child_node, &clk_orphan_list); } - clk->parent = new_parent; + core->parent = new_parent; } -static struct clk_core *__clk_set_parent_before(struct clk_core *clk, +static struct clk_core *__clk_set_parent_before(struct clk_core *core, struct clk_core *parent) { unsigned long flags; - struct clk_core *old_parent = clk->parent; + struct clk_core *old_parent = core->parent; /* * Migrate prepare state between parents and prevent race with @@ -1473,17 +1104,17 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *clk, * * See also: Comment for clk_set_parent() below. */ - if (clk->prepare_count) { + if (core->prepare_count) { clk_core_prepare(parent); flags = clk_enable_lock(); clk_core_enable(parent); - clk_core_enable(clk); + clk_core_enable(core); clk_enable_unlock(flags); } /* update the clk tree topology */ flags = clk_enable_lock(); - clk_reparent(clk, parent); + clk_reparent(core, parent); clk_enable_unlock(flags); return old_parent; @@ -1508,31 +1139,31 @@ static void __clk_set_parent_after(struct clk_core *core, } } -static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, +static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, u8 p_index) { unsigned long flags; int ret = 0; struct clk_core *old_parent; - old_parent = __clk_set_parent_before(clk, parent); + old_parent = __clk_set_parent_before(core, parent); - trace_clk_set_parent(clk, parent); + trace_clk_set_parent(core, parent); /* change clock input source */ - if (parent && clk->ops->set_parent) - ret = clk->ops->set_parent(clk->hw, p_index); + if (parent && core->ops->set_parent) + ret = core->ops->set_parent(core->hw, p_index); - trace_clk_set_parent_complete(clk, parent); + trace_clk_set_parent_complete(core, parent); if (ret) { flags = clk_enable_lock(); - clk_reparent(clk, old_parent); + clk_reparent(core, old_parent); clk_enable_unlock(flags); - if (clk->prepare_count) { + if (core->prepare_count) { flags = clk_enable_lock(); - clk_core_disable(clk); + clk_core_disable(core); clk_core_disable(parent); clk_enable_unlock(flags); clk_core_unprepare(parent); @@ -1540,14 +1171,14 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, return ret; } - __clk_set_parent_after(clk, parent, old_parent); + __clk_set_parent_after(core, parent, old_parent); return 0; } /** * __clk_speculate_rates - * @clk: first clk in the subtree + * @core: first clk in the subtree * @parent_rate: the "future" rate of clk's parent * * Walks the subtree of clks starting with clk, speculating rates as it @@ -1558,10 +1189,8 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, * subtree have subscribed to the notifications. Note that if a clk does not * implement the .recalc_rate callback then it is assumed that the clock will * take on the rate of its parent. - * - * Caller must hold prepare_lock. */ -static int __clk_speculate_rates(struct clk_core *clk, +static int __clk_speculate_rates(struct clk_core *core, unsigned long parent_rate) { struct clk_core *child; @@ -1570,19 +1199,19 @@ static int __clk_speculate_rates(struct clk_core *clk, lockdep_assert_held(&prepare_lock); - new_rate = clk_recalc(clk, parent_rate); + new_rate = clk_recalc(core, parent_rate); /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ - if (clk->notifier_count) - ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); + if (core->notifier_count) + ret = __clk_notify(core, PRE_RATE_CHANGE, core->rate, new_rate); if (ret & NOTIFY_STOP_MASK) { pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", - __func__, clk->name, ret); + __func__, core->name, ret); goto out; } - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { ret = __clk_speculate_rates(child, new_rate); if (ret & NOTIFY_STOP_MASK) break; @@ -1592,20 +1221,20 @@ out: return ret; } -static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, +static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, struct clk_core *new_parent, u8 p_index) { struct clk_core *child; - clk->new_rate = new_rate; - clk->new_parent = new_parent; - clk->new_parent_index = p_index; + core->new_rate = new_rate; + core->new_parent = new_parent; + core->new_parent_index = p_index; /* include clk in new parent's PRE_RATE_CHANGE notifications */ - clk->new_child = NULL; - if (new_parent && new_parent != clk->parent) - new_parent->new_child = clk; + core->new_child = NULL; + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { child->new_rate = clk_recalc(child, new_rate); clk_calc_subtree(child, child->new_rate, NULL, 0); } @@ -1615,10 +1244,10 @@ static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, * calculate the new rates returning the topmost clock that has to be * changed. */ -static struct clk_core *clk_calc_new_rates(struct clk_core *clk, +static struct clk_core *clk_calc_new_rates(struct clk_core *core, unsigned long rate) { - struct clk_core *top = clk; + struct clk_core *top = core; struct clk_core *old_parent, *parent; struct clk_hw *parent_hw; unsigned long best_parent_rate = 0; @@ -1629,20 +1258,20 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, long ret; /* sanity */ - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR_OR_NULL(core)) return NULL; /* save parent rate, if it exists */ - parent = old_parent = clk->parent; + parent = old_parent = core->parent; if (parent) best_parent_rate = parent->rate; - clk_core_get_boundaries(clk, &min_rate, &max_rate); + clk_core_get_boundaries(core, &min_rate, &max_rate); /* find the closest rate and parent clk/rate */ - if (clk->ops->determine_rate) { + if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - ret = clk->ops->determine_rate(clk->hw, rate, + ret = core->ops->determine_rate(core->hw, rate, min_rate, max_rate, &best_parent_rate, @@ -1652,8 +1281,8 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, new_rate = ret; parent = parent_hw ? parent_hw->core : NULL; - } else if (clk->ops->round_rate) { - ret = clk->ops->round_rate(clk->hw, rate, + } else if (core->ops->round_rate) { + ret = core->ops->round_rate(core->hw, rate, &best_parent_rate); if (ret < 0) return NULL; @@ -1661,9 +1290,9 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, new_rate = ret; if (new_rate < min_rate || new_rate > max_rate) return NULL; - } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { + } else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) { /* pass-through clock without adjustable parent */ - clk->new_rate = clk->rate; + core->new_rate = core->rate; return NULL; } else { /* pass-through clock with adjustable parent */ @@ -1674,28 +1303,28 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, /* some clocks must be gated to change parent */ if (parent != old_parent && - (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + (core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { pr_debug("%s: %s not gated but wants to reparent\n", - __func__, clk->name); + __func__, core->name); return NULL; } /* try finding the new parent index */ - if (parent && clk->num_parents > 1) { - p_index = clk_fetch_parent_index(clk, parent); + if (parent && core->num_parents > 1) { + p_index = clk_fetch_parent_index(core, parent); if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, clk->name); + __func__, parent->name, core->name); return NULL; } } - if ((clk->flags & CLK_SET_RATE_PARENT) && parent && + if ((core->flags & CLK_SET_RATE_PARENT) && parent && best_parent_rate != parent->rate) top = clk_calc_new_rates(parent, best_parent_rate); out: - clk_calc_subtree(clk, new_rate, parent, p_index); + clk_calc_subtree(core, new_rate, parent, p_index); return top; } @@ -1705,33 +1334,33 @@ out: * so that in case of an error we can walk down the whole tree again and * abort the change. */ -static struct clk_core *clk_propagate_rate_change(struct clk_core *clk, +static struct clk_core *clk_propagate_rate_change(struct clk_core *core, unsigned long event) { struct clk_core *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; - if (clk->rate == clk->new_rate) + if (core->rate == core->new_rate) return NULL; - if (clk->notifier_count) { - ret = __clk_notify(clk, event, clk->rate, clk->new_rate); + if (core->notifier_count) { + ret = __clk_notify(core, event, core->rate, core->new_rate); if (ret & NOTIFY_STOP_MASK) - fail_clk = clk; + fail_clk = core; } - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) + if (child->new_parent && child->new_parent != core) continue; tmp_clk = clk_propagate_rate_change(child, event); if (tmp_clk) fail_clk = tmp_clk; } - /* handle the new child who might not be in clk->children yet */ - if (clk->new_child) { - tmp_clk = clk_propagate_rate_change(clk->new_child, event); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) { + tmp_clk = clk_propagate_rate_change(core->new_child, event); if (tmp_clk) fail_clk = tmp_clk; } @@ -1743,7 +1372,7 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *clk, * walk down a subtree and set the new rates notifying the rate * change on the way */ -static void clk_change_rate(struct clk_core *clk) +static void clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; @@ -1752,77 +1381,80 @@ static void clk_change_rate(struct clk_core *clk) bool skip_set_rate = false; struct clk_core *old_parent; - old_rate = clk->rate; + old_rate = core->rate; - if (clk->new_parent) - best_parent_rate = clk->new_parent->rate; - else if (clk->parent) - best_parent_rate = clk->parent->rate; + if (core->new_parent) + best_parent_rate = core->new_parent->rate; + else if (core->parent) + best_parent_rate = core->parent->rate; - if (clk->new_parent && clk->new_parent != clk->parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); - trace_clk_set_parent(clk, clk->new_parent); + if (core->new_parent && core->new_parent != core->parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); - if (clk->ops->set_rate_and_parent) { + if (core->ops->set_rate_and_parent) { skip_set_rate = true; - clk->ops->set_rate_and_parent(clk->hw, clk->new_rate, + core->ops->set_rate_and_parent(core->hw, core->new_rate, best_parent_rate, - clk->new_parent_index); - } else if (clk->ops->set_parent) { - clk->ops->set_parent(clk->hw, clk->new_parent_index); + core->new_parent_index); + } else if (core->ops->set_parent) { + core->ops->set_parent(core->hw, core->new_parent_index); } - trace_clk_set_parent_complete(clk, clk->new_parent); - __clk_set_parent_after(clk, clk->new_parent, old_parent); + trace_clk_set_parent_complete(core, core->new_parent); + __clk_set_parent_after(core, core->new_parent, old_parent); } - trace_clk_set_rate(clk, clk->new_rate); + trace_clk_set_rate(core, core->new_rate); - if (!skip_set_rate && clk->ops->set_rate) - clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); + if (!skip_set_rate && core->ops->set_rate) + core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); - trace_clk_set_rate_complete(clk, clk->new_rate); + trace_clk_set_rate_complete(core, core->new_rate); - clk->rate = clk_recalc(clk, best_parent_rate); + core->rate = clk_recalc(core, best_parent_rate); - if (clk->notifier_count && old_rate != clk->rate) - __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); + if (core->notifier_count && old_rate != core->rate) + __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); + + if (core->flags & CLK_RECALC_NEW_RATES) + (void)clk_calc_new_rates(core, core->new_rate); /* * Use safe iteration, as change_rate can actually swap parents * for certain clock types. */ - hlist_for_each_entry_safe(child, tmp, &clk->children, child_node) { + hlist_for_each_entry_safe(child, tmp, &core->children, child_node) { /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) + if (child->new_parent && child->new_parent != core) continue; clk_change_rate(child); } - /* handle the new child who might not be in clk->children yet */ - if (clk->new_child) - clk_change_rate(clk->new_child); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) + clk_change_rate(core->new_child); } -static int clk_core_set_rate_nolock(struct clk_core *clk, +static int clk_core_set_rate_nolock(struct clk_core *core, unsigned long req_rate) { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; int ret = 0; - if (!clk) + if (!core) return 0; /* bail early if nothing to do */ - if (rate == clk_core_get_rate_nolock(clk)) + if (rate == clk_core_get_rate_nolock(core)) return 0; - if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) + if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) return -EBUSY; /* calculate new rates and get the topmost changed clock */ - top = clk_calc_new_rates(clk, rate); + top = clk_calc_new_rates(core, rate); if (!top) return -EINVAL; @@ -1838,7 +1470,7 @@ static int clk_core_set_rate_nolock(struct clk_core *clk, /* change the rates */ clk_change_rate(top); - clk->req_rate = req_rate; + core->req_rate = req_rate; return ret; } @@ -1977,55 +1609,63 @@ EXPORT_SYMBOL_GPL(clk_get_parent); * .parents array exists, and if so use it to avoid an expensive tree * traversal. If .parents does not exist then walk the tree. */ -static struct clk_core *__clk_init_parent(struct clk_core *clk) +static struct clk_core *__clk_init_parent(struct clk_core *core) { struct clk_core *ret = NULL; u8 index; /* handle the trivial cases */ - if (!clk->num_parents) + if (!core->num_parents) goto out; - if (clk->num_parents == 1) { - if (IS_ERR_OR_NULL(clk->parent)) - clk->parent = clk_core_lookup(clk->parent_names[0]); - ret = clk->parent; + if (core->num_parents == 1) { + if (IS_ERR_OR_NULL(core->parent)) + core->parent = clk_core_lookup(core->parent_names[0]); + ret = core->parent; goto out; } - if (!clk->ops->get_parent) { - WARN(!clk->ops->get_parent, + if (!core->ops->get_parent) { + WARN(!core->ops->get_parent, "%s: multi-parent clocks must implement .get_parent\n", __func__); goto out; }; /* - * Do our best to cache parent clocks in clk->parents. This prevents - * unnecessary and expensive lookups. We don't set clk->parent here; + * Do our best to cache parent clocks in core->parents. This prevents + * unnecessary and expensive lookups. We don't set core->parent here; * that is done by the calling function. */ - index = clk->ops->get_parent(clk->hw); + index = core->ops->get_parent(core->hw); - if (!clk->parents) - clk->parents = - kcalloc(clk->num_parents, sizeof(struct clk *), + if (!core->parents) + core->parents = + kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); - ret = clk_core_get_parent_by_index(clk, index); + ret = clk_core_get_parent_by_index(core, index); out: return ret; } -static void clk_core_reparent(struct clk_core *clk, +static void clk_core_reparent(struct clk_core *core, struct clk_core *new_parent) { - clk_reparent(clk, new_parent); - __clk_recalc_accuracies(clk); - __clk_recalc_rates(clk, POST_RATE_CHANGE); + clk_reparent(core, new_parent); + __clk_recalc_accuracies(core); + __clk_recalc_rates(core, POST_RATE_CHANGE); +} + +void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent) +{ + if (!hw) + return; + + clk_core_reparent(hw->core, !new_parent ? NULL : new_parent->core); } /** @@ -2062,61 +1702,61 @@ bool clk_has_parent(struct clk *clk, struct clk *parent) } EXPORT_SYMBOL_GPL(clk_has_parent); -static int clk_core_set_parent(struct clk_core *clk, struct clk_core *parent) +static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) { int ret = 0; int p_index = 0; unsigned long p_rate = 0; - if (!clk) + if (!core) return 0; /* prevent racing with updates to the clock topology */ clk_prepare_lock(); - if (clk->parent == parent) + if (core->parent == parent) goto out; /* verify ops for for multi-parent clks */ - if ((clk->num_parents > 1) && (!clk->ops->set_parent)) { + if ((core->num_parents > 1) && (!core->ops->set_parent)) { ret = -ENOSYS; goto out; } /* check that we are allowed to re-parent if the clock is in use */ - if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { ret = -EBUSY; goto out; } /* try finding the new parent index */ if (parent) { - p_index = clk_fetch_parent_index(clk, parent); + p_index = clk_fetch_parent_index(core, parent); p_rate = parent->rate; if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, clk->name); + __func__, parent->name, core->name); ret = p_index; goto out; } } /* propagate PRE_RATE_CHANGE notifications */ - ret = __clk_speculate_rates(clk, p_rate); + ret = __clk_speculate_rates(core, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) goto out; /* do the re-parent */ - ret = __clk_set_parent(clk, parent, p_index); + ret = __clk_set_parent(core, parent, p_index); /* propagate rate an accuracy recalculation accordingly */ if (ret) { - __clk_recalc_rates(clk, ABORT_RATE_CHANGE); + __clk_recalc_rates(core, ABORT_RATE_CHANGE); } else { - __clk_recalc_rates(clk, POST_RATE_CHANGE); - __clk_recalc_accuracies(clk); + __clk_recalc_rates(core, POST_RATE_CHANGE); + __clk_recalc_accuracies(core); } out: @@ -2201,21 +1841,16 @@ int clk_set_phase(struct clk *clk, int degrees) } EXPORT_SYMBOL_GPL(clk_set_phase); -static int clk_core_get_phase(struct clk_core *clk) +static int clk_core_get_phase(struct clk_core *core) { - int ret = 0; - - if (!clk) - goto out; + int ret; clk_prepare_lock(); - ret = clk->phase; + ret = core->phase; clk_prepare_unlock(); -out: return ret; } -EXPORT_SYMBOL_GPL(clk_get_phase); /** * clk_get_phase - return the phase shift of a clock signal @@ -2231,6 +1866,7 @@ int clk_get_phase(struct clk *clk) return clk_core_get_phase(clk->core); } +EXPORT_SYMBOL_GPL(clk_get_phase); /** * clk_is_match - check if two clk's point to the same hardware clock @@ -2258,6 +1894,337 @@ bool clk_is_match(const struct clk *p, const struct clk *q) } EXPORT_SYMBOL_GPL(clk_is_match); +/*** debugfs support ***/ + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> + +static struct dentry *rootdir; +static int inited = 0; +static DEFINE_MUTEX(clk_debug_lock); +static HLIST_HEAD(clk_debug_list); + +static struct hlist_head *all_lists[] = { + &clk_root_list, + &clk_orphan_list, + NULL, +}; + +static struct hlist_head *orphan_list[] = { + &clk_orphan_list, + NULL, +}; + +static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, + int level) +{ + if (!c) + return; + + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", + level * 3 + 1, "", + 30 - level * 3, c->name, + c->enable_count, c->prepare_count, clk_core_get_rate(c), + clk_core_get_accuracy(c), clk_core_get_phase(c)); +} + +static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, + int level) +{ + struct clk_core *child; + + if (!c) + return; + + clk_summary_show_one(s, c, level); + + hlist_for_each_entry(child, &c->children, child_node) + clk_summary_show_subtree(s, child, level + 1); +} + +static int clk_summary_show(struct seq_file *s, void *data) +{ + struct clk_core *c; + struct hlist_head **lists = (struct hlist_head **)s->private; + + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); + + clk_prepare_lock(); + + for (; *lists; lists++) + hlist_for_each_entry(c, *lists, child_node) + clk_summary_show_subtree(s, c, 0); + + clk_prepare_unlock(); + + return 0; +} + + +static int clk_summary_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_summary_show, inode->i_private); +} + +static const struct file_operations clk_summary_fops = { + .open = clk_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) +{ + if (!c) + return; + + /* This should be JSON format, i.e. elements separated with a comma */ + seq_printf(s, "\"%s\": { ", c->name); + seq_printf(s, "\"enable_count\": %d,", c->enable_count); + seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); + seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); + seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); +} + +static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) +{ + struct clk_core *child; + + if (!c) + return; + + clk_dump_one(s, c, level); + + hlist_for_each_entry(child, &c->children, child_node) { + seq_printf(s, ","); + clk_dump_subtree(s, child, level + 1); + } + + seq_printf(s, "}"); +} + +static int clk_dump(struct seq_file *s, void *data) +{ + struct clk_core *c; + bool first_node = true; + struct hlist_head **lists = (struct hlist_head **)s->private; + + seq_printf(s, "{"); + + clk_prepare_lock(); + + for (; *lists; lists++) { + hlist_for_each_entry(c, *lists, child_node) { + if (!first_node) + seq_puts(s, ","); + first_node = false; + clk_dump_subtree(s, c, 0); + } + } + + clk_prepare_unlock(); + + seq_puts(s, "}\n"); + return 0; +} + + +static int clk_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, clk_dump, inode->i_private); +} + +static const struct file_operations clk_dump_fops = { + .open = clk_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) +{ + struct dentry *d; + int ret = -ENOMEM; + + if (!core || !pdentry) { + ret = -EINVAL; + goto out; + } + + d = debugfs_create_dir(core->name, pdentry); + if (!d) + goto out; + + core->dentry = d; + + d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, + (u32 *)&core->rate); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, + (u32 *)&core->accuracy); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, + (u32 *)&core->phase); + if (!d) + goto err_out; + + d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, + (u32 *)&core->flags); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, + (u32 *)&core->prepare_count); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, + (u32 *)&core->enable_count); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, + (u32 *)&core->notifier_count); + if (!d) + goto err_out; + + if (core->ops->debug_init) { + ret = core->ops->debug_init(core->hw, core->dentry); + if (ret) + goto err_out; + } + + ret = 0; + goto out; + +err_out: + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; +out: + return ret; +} + +/** + * clk_debug_register - add a clk node to the debugfs clk directory + * @core: the clk being added to the debugfs clk directory + * + * Dynamically adds a clk to the debugfs clk directory if debugfs has been + * initialized. Otherwise it bails out early since the debugfs clk directory + * will be created lazily by clk_debug_init as part of a late_initcall. + */ +static int clk_debug_register(struct clk_core *core) +{ + int ret = 0; + + mutex_lock(&clk_debug_lock); + hlist_add_head(&core->debug_node, &clk_debug_list); + + if (!inited) + goto unlock; + + ret = clk_debug_create_one(core, rootdir); +unlock: + mutex_unlock(&clk_debug_lock); + + return ret; +} + + /** + * clk_debug_unregister - remove a clk node from the debugfs clk directory + * @core: the clk being removed from the debugfs clk directory + * + * Dynamically removes a clk and all its child nodes from the + * debugfs clk directory if clk->dentry points to debugfs created by + * clk_debug_register in __clk_init. + */ +static void clk_debug_unregister(struct clk_core *core) +{ + mutex_lock(&clk_debug_lock); + hlist_del_init(&core->debug_node); + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; + mutex_unlock(&clk_debug_lock); +} + +struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, + void *data, const struct file_operations *fops) +{ + struct dentry *d = NULL; + + if (hw->core->dentry) + d = debugfs_create_file(name, mode, hw->core->dentry, data, + fops); + + return d; +} +EXPORT_SYMBOL_GPL(clk_debugfs_add_file); + +/** + * clk_debug_init - lazily populate the debugfs clk directory + * + * clks are often initialized very early during boot before memory can be + * dynamically allocated and well before debugfs is setup. This function + * populates the debugfs clk directory once at boot-time when we know that + * debugfs is setup. It should only be called once at boot-time, all other clks + * added dynamically will be done so with clk_debug_register. + */ +static int __init clk_debug_init(void) +{ + struct clk_core *core; + struct dentry *d; + + rootdir = debugfs_create_dir("clk", NULL); + + if (!rootdir) + return -ENOMEM; + + d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, + &clk_summary_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, + &clk_dump_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, + &orphan_list, &clk_summary_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, + &orphan_list, &clk_dump_fops); + if (!d) + return -ENOMEM; + + mutex_lock(&clk_debug_lock); + hlist_for_each_entry(core, &clk_debug_list, debug_node) + clk_debug_create_one(core, rootdir); + + inited = 1; + mutex_unlock(&clk_debug_lock); + + return 0; +} +late_initcall(clk_debug_init); +#else +static inline int clk_debug_register(struct clk_core *core) { return 0; } +static inline void clk_debug_reparent(struct clk_core *core, + struct clk_core *new_parent) +{ +} +static inline void clk_debug_unregister(struct clk_core *core) +{ +} +#endif + /** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now @@ -2271,67 +2238,67 @@ static int __clk_init(struct device *dev, struct clk *clk_user) int i, ret = 0; struct clk_core *orphan; struct hlist_node *tmp2; - struct clk_core *clk; + struct clk_core *core; unsigned long rate; if (!clk_user) return -EINVAL; - clk = clk_user->core; + core = clk_user->core; clk_prepare_lock(); /* check to see if a clock with this name is already registered */ - if (clk_core_lookup(clk->name)) { + if (clk_core_lookup(core->name)) { pr_debug("%s: clk %s already initialized\n", - __func__, clk->name); + __func__, core->name); ret = -EEXIST; goto out; } /* check that clk_ops are sane. See Documentation/clk.txt */ - if (clk->ops->set_rate && - !((clk->ops->round_rate || clk->ops->determine_rate) && - clk->ops->recalc_rate)) { + if (core->ops->set_rate && + !((core->ops->round_rate || core->ops->determine_rate) && + core->ops->recalc_rate)) { pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } - if (clk->ops->set_parent && !clk->ops->get_parent) { + if (core->ops->set_parent && !core->ops->get_parent) { pr_warning("%s: %s must implement .get_parent & .set_parent\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } - if (clk->ops->set_rate_and_parent && - !(clk->ops->set_parent && clk->ops->set_rate)) { + if (core->ops->set_rate_and_parent && + !(core->ops->set_parent && core->ops->set_rate)) { pr_warn("%s: %s must implement .set_parent & .set_rate\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } /* throw a WARN if any entries in parent_names are NULL */ - for (i = 0; i < clk->num_parents; i++) - WARN(!clk->parent_names[i], + for (i = 0; i < core->num_parents; i++) + WARN(!core->parent_names[i], "%s: invalid NULL in %s's .parent_names\n", - __func__, clk->name); + __func__, core->name); /* * Allocate an array of struct clk *'s to avoid unnecessary string * look-ups of clk's possible parents. This can fail for clocks passed - * in to clk_init during early boot; thus any access to clk->parents[] + * in to clk_init during early boot; thus any access to core->parents[] * must always check for a NULL pointer and try to populate it if * necessary. * - * If clk->parents is not NULL we skip this entire block. This allows - * for clock drivers to statically initialize clk->parents. + * If core->parents is not NULL we skip this entire block. This allows + * for clock drivers to statically initialize core->parents. */ - if (clk->num_parents > 1 && !clk->parents) { - clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *), + if (core->num_parents > 1 && !core->parents) { + core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); /* * clk_core_lookup returns NULL for parents that have not been @@ -2339,16 +2306,16 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * for a NULL pointer. We can always perform lazy lookups for * missing parents later on. */ - if (clk->parents) - for (i = 0; i < clk->num_parents; i++) - clk->parents[i] = - clk_core_lookup(clk->parent_names[i]); + if (core->parents) + for (i = 0; i < core->num_parents; i++) + core->parents[i] = + clk_core_lookup(core->parent_names[i]); } - clk->parent = __clk_init_parent(clk); + core->parent = __clk_init_parent(core); /* - * Populate clk->parent if parent has already been __clk_init'd. If + * Populate core->parent if parent has already been __clk_init'd. If * parent has not yet been __clk_init'd then place clk in the orphan * list. If clk has set the CLK_IS_ROOT flag then place it in the root * clk list. @@ -2357,13 +2324,13 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * clocks and re-parent any that are children of the clock currently * being clk_init'd. */ - if (clk->parent) - hlist_add_head(&clk->child_node, - &clk->parent->children); - else if (clk->flags & CLK_IS_ROOT) - hlist_add_head(&clk->child_node, &clk_root_list); + if (core->parent) + hlist_add_head(&core->child_node, + &core->parent->children); + else if (core->flags & CLK_IS_ROOT) + hlist_add_head(&core->child_node, &clk_root_list); else - hlist_add_head(&clk->child_node, &clk_orphan_list); + hlist_add_head(&core->child_node, &clk_orphan_list); /* * Set clk's accuracy. The preferred method is to use @@ -2372,23 +2339,23 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * parent (or is orphaned) then accuracy is set to zero (perfect * clock). */ - if (clk->ops->recalc_accuracy) - clk->accuracy = clk->ops->recalc_accuracy(clk->hw, - __clk_get_accuracy(clk->parent)); - else if (clk->parent) - clk->accuracy = clk->parent->accuracy; + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, + __clk_get_accuracy(core->parent)); + else if (core->parent) + core->accuracy = core->parent->accuracy; else - clk->accuracy = 0; + core->accuracy = 0; /* * Set clk's phase. * Since a phase is by definition relative to its parent, just * query the current clock phase, or just assume it's in phase. */ - if (clk->ops->get_phase) - clk->phase = clk->ops->get_phase(clk->hw); + if (core->ops->get_phase) + core->phase = core->ops->get_phase(core->hw); else - clk->phase = 0; + core->phase = 0; /* * Set clk's rate. The preferred method is to use .recalc_rate. For @@ -2396,14 +2363,14 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * parent's rate. If a clock doesn't have a parent (or is orphaned) * then rate is set to zero. */ - if (clk->ops->recalc_rate) - rate = clk->ops->recalc_rate(clk->hw, - clk_core_get_rate_nolock(clk->parent)); - else if (clk->parent) - rate = clk->parent->rate; + if (core->ops->recalc_rate) + rate = core->ops->recalc_rate(core->hw, + clk_core_get_rate_nolock(core->parent)); + else if (core->parent) + rate = core->parent->rate; else rate = 0; - clk->rate = clk->req_rate = rate; + core->rate = core->req_rate = rate; /* * walk the list of orphan clocks and reparent any that are children of @@ -2412,14 +2379,14 @@ 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(clk->name, orphan->parent_names[i])) - clk_core_reparent(orphan, clk); + if (!strcmp(core->name, orphan->parent_names[i])) + clk_core_reparent(orphan, core); continue; } for (i = 0; i < orphan->num_parents; i++) - if (!strcmp(clk->name, orphan->parent_names[i])) { - clk_core_reparent(orphan, clk); + if (!strcmp(core->name, orphan->parent_names[i])) { + clk_core_reparent(orphan, core); break; } } @@ -2432,15 +2399,15 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * Please consider other ways of solving initialization problems before * using this callback, as its use is discouraged. */ - if (clk->ops->init) - clk->ops->init(clk->hw); + if (core->ops->init) + core->ops->init(core->hw); - kref_init(&clk->ref); + kref_init(&core->ref); out: clk_prepare_unlock(); if (!ret) - clk_debug_register(clk); + clk_debug_register(core); return ret; } @@ -2486,63 +2453,58 @@ void __clk_free_clk(struct clk *clk) * * clk_register is the primary interface for populating the clock tree with new * clock nodes. It returns a pointer to the newly allocated struct clk which - * cannot be dereferenced by driver code but may be used in conjuction with the + * cannot be dereferenced by driver code but may be used in conjunction with the * rest of the clock API. In the event of an error clk_register will return an * error code; drivers must test for an error code after calling clk_register. */ struct clk *clk_register(struct device *dev, struct clk_hw *hw) { int i, ret; - struct clk_core *clk; + struct clk_core *core; - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) { - pr_err("%s: could not allocate clk\n", __func__); + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) { ret = -ENOMEM; goto fail_out; } - clk->name = kstrdup_const(hw->init->name, GFP_KERNEL); - if (!clk->name) { - pr_err("%s: could not allocate clk->name\n", __func__); + core->name = kstrdup_const(hw->init->name, GFP_KERNEL); + if (!core->name) { ret = -ENOMEM; goto fail_name; } - clk->ops = hw->init->ops; + core->ops = hw->init->ops; if (dev && dev->driver) - clk->owner = dev->driver->owner; - clk->hw = hw; - clk->flags = hw->init->flags; - clk->num_parents = hw->init->num_parents; - hw->core = clk; + core->owner = dev->driver->owner; + core->hw = hw; + core->flags = hw->init->flags; + core->num_parents = hw->init->num_parents; + hw->core = core; /* allocate local copy in case parent_names is __initdata */ - clk->parent_names = kcalloc(clk->num_parents, sizeof(char *), + core->parent_names = kcalloc(core->num_parents, sizeof(char *), GFP_KERNEL); - if (!clk->parent_names) { - pr_err("%s: could not allocate clk->parent_names\n", __func__); + if (!core->parent_names) { ret = -ENOMEM; goto fail_parent_names; } /* copy each string name in case parent_names is __initdata */ - for (i = 0; i < clk->num_parents; i++) { - clk->parent_names[i] = kstrdup_const(hw->init->parent_names[i], + for (i = 0; i < core->num_parents; i++) { + core->parent_names[i] = kstrdup_const(hw->init->parent_names[i], GFP_KERNEL); - if (!clk->parent_names[i]) { - pr_err("%s: could not copy parent_names\n", __func__); + if (!core->parent_names[i]) { ret = -ENOMEM; goto fail_parent_names_copy; } } - INIT_HLIST_HEAD(&clk->clks); + INIT_HLIST_HEAD(&core->clks); hw->clk = __clk_create_clk(hw, NULL, NULL); if (IS_ERR(hw->clk)) { - pr_err("%s: could not allocate per-user clk\n", __func__); ret = PTR_ERR(hw->clk); goto fail_parent_names_copy; } @@ -2556,35 +2518,32 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) fail_parent_names_copy: while (--i >= 0) - kfree_const(clk->parent_names[i]); - kfree(clk->parent_names); + kfree_const(core->parent_names[i]); + kfree(core->parent_names); fail_parent_names: - kfree_const(clk->name); + kfree_const(core->name); fail_name: - kfree(clk); + kfree(core); fail_out: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(clk_register); -/* - * Free memory allocated for a clock. - * Caller must hold prepare_lock. - */ +/* Free memory allocated for a clock. */ static void __clk_release(struct kref *ref) { - struct clk_core *clk = container_of(ref, struct clk_core, ref); - int i = clk->num_parents; + struct clk_core *core = container_of(ref, struct clk_core, ref); + int i = core->num_parents; lockdep_assert_held(&prepare_lock); - kfree(clk->parents); + kfree(core->parents); while (--i >= 0) - kfree_const(clk->parent_names[i]); + kfree_const(core->parent_names[i]); - kfree(clk->parent_names); - kfree_const(clk->name); - kfree(clk); + kfree(core->parent_names); + kfree_const(core->name); + kfree(core); } /* @@ -3068,6 +3027,27 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) } EXPORT_SYMBOL_GPL(of_clk_get_parent_name); +/** + * of_clk_parent_fill() - Fill @parents with names of @np's parents and return + * number of parents + * @np: Device node pointer associated with clock provider + * @parents: pointer to char array that hold the parents' names + * @size: size of the @parents array + * + * Return: number of parents for the clock node. + */ +int of_clk_parent_fill(struct device_node *np, const char **parents, + unsigned int size) +{ + unsigned int i = 0; + + while (i < size && (parents[i] = of_clk_get_parent_name(np, i)) != NULL) + i++; + + return i; +} +EXPORT_SYMBOL_GPL(of_clk_parent_fill); + struct clock_provider { of_clk_init_cb_t clk_init_cb; struct device_node *np; diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 000000000000..b4165ba75d9f --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,6 @@ +config COMMON_CLK_HI6220 + bool "Hi6220 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the Hisilicon Hi6220 clock driver based on the common clock framework. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 038c02f4d0e7..48f0116a032a 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -2,8 +2,9 @@ # Hisilicon Clock specific Makefile # -obj-y += clk.o clkgate-separated.o +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o +obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index 472dd2cb10b3..715d34a5ef9b 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -38,44 +38,44 @@ #include "clk.h" /* clock parent list */ -static const char *timer0_mux_p[] __initdata = { "osc32k", "timerclk01", }; -static const char *timer1_mux_p[] __initdata = { "osc32k", "timerclk01", }; -static const char *timer2_mux_p[] __initdata = { "osc32k", "timerclk23", }; -static const char *timer3_mux_p[] __initdata = { "osc32k", "timerclk23", }; -static const char *timer4_mux_p[] __initdata = { "osc32k", "timerclk45", }; -static const char *timer5_mux_p[] __initdata = { "osc32k", "timerclk45", }; -static const char *timer6_mux_p[] __initdata = { "osc32k", "timerclk67", }; -static const char *timer7_mux_p[] __initdata = { "osc32k", "timerclk67", }; -static const char *timer8_mux_p[] __initdata = { "osc32k", "timerclk89", }; -static const char *timer9_mux_p[] __initdata = { "osc32k", "timerclk89", }; -static const char *uart0_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart1_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart2_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart3_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart4_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *spi0_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; -static const char *spi1_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; -static const char *spi2_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; +static const char *const timer0_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer1_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer2_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer3_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer4_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer5_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer6_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer7_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer8_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const timer9_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const uart0_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart1_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart2_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart3_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart4_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const spi0_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi1_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi2_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; /* share axi parent */ -static const char *saxi_mux_p[] __initdata = { "armpll3", "armpll2", }; -static const char *pwm0_mux_p[] __initdata = { "osc32k", "osc26m", }; -static const char *pwm1_mux_p[] __initdata = { "osc32k", "osc26m", }; -static const char *sd_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc1_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc1_mux2_p[] __initdata = { "osc26m", "mmc1_div", }; -static const char *g2d_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *venc_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *vdec_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *vpp_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *edc0_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *ldi0_mux_p[] __initdata = { "armpll2", "armpll4", +static const char *const saxi_mux_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const pwm0_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const pwm1_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const sd_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux2_p[] __initconst = { "osc26m", "mmc1_div", }; +static const char *const g2d_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const venc_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vdec_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vpp_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const edc0_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi0_mux_p[] __initconst = { "armpll2", "armpll4", "armpll3", "armpll5", }; -static const char *edc1_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *ldi1_mux_p[] __initdata = { "armpll2", "armpll4", +static const char *const edc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi1_mux_p[] __initconst = { "armpll2", "armpll4", "armpll3", "armpll5", }; -static const char *rclk_hsic_p[] __initdata = { "armpll3", "armpll2", }; -static const char *mmc2_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc3_mux_p[] __initdata = { "armpll2", "armpll3", }; +static const char *const rclk_hsic_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const mmc2_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc3_mux_p[] __initconst = { "armpll2", "armpll3", }; /* fixed rate clocks */ diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c new file mode 100644 index 000000000000..4563343b6420 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -0,0 +1,284 @@ +/* + * Hisilicon Hi6220 clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang <bintian.wang@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/hi6220-clock.h> + +#include "clk.h" + + +/* clocks in AO (always on) controller */ +static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { + { HI6220_REF32K, "ref32k", NULL, CLK_IS_ROOT, 32764, }, + { HI6220_CLK_TCXO, "clk_tcxo", NULL, CLK_IS_ROOT, 19200000, }, + { HI6220_MMC1_PAD, "mmc1_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC2_PAD, "mmc2_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC0_PAD, "mmc0_pad", NULL, CLK_IS_ROOT, 200000000, }, + { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, + { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, + { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, + { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, +}; + +static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = { + { HI6220_300M, "clk_300m", "syspll", 1, 4, 0, }, + { HI6220_150M, "clk_150m", "clk_300m", 1, 2, 0, }, + { HI6220_PICOPHY_SRC, "picophy_src", "clk_150m", 1, 4, 0, }, + { HI6220_MMC0_SRC_SEL, "mmc0srcsel", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SRC_SEL, "mmc1srcsel", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SRC_SEL, "mmc2srcsel", "mmc2_sel", 1, 8, 0, }, + { HI6220_VPU_CODEC, "vpucodec", "codec_jpeg_aclk", 1, 2, 0, }, + { HI6220_MMC0_SMP, "mmc0_sample", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SMP, "mmc1_sample", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SMP, "mmc2_sample", "mmc2_sel", 1, 8, 0, }, +}; + +static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { + { HI6220_WDT0_PCLK, "wdt0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, + { HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, }, + { HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, }, + { HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, }, + { HI6220_TIMER3_PCLK, "timer3_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 18, 0, }, + { HI6220_TIMER4_PCLK, "timer4_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 19, 0, }, + { HI6220_TIMER5_PCLK, "timer5_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 20, 0, }, + { HI6220_TIMER6_PCLK, "timer6_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 21, 0, }, + { HI6220_TIMER7_PCLK, "timer7_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 22, 0, }, + { HI6220_TIMER8_PCLK, "timer8_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 23, 0, }, + { HI6220_UART0_PCLK, "uart0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 24, 0, }, +}; + +static void __init hi6220_clk_ao_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data_ao; + + clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); + if (!clk_data_ao) + return; + + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, + ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); + + hisi_clk_register_fixed_factor(hi6220_fixed_factor_clks, + ARRAY_SIZE(hi6220_fixed_factor_clks), clk_data_ao); + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao, + ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao); +} +CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); + + +/* clocks in sysctrl */ +static const char *mmc0_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc0_mux1_p[] __initdata = { "mmc0_mux0", "pll_media_gate", }; +static const char *mmc0_src_p[] __initdata = { "mmc0srcsel", "mmc0_div", }; +static const char *mmc1_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc1_mux1_p[] __initdata = { "mmc1_mux0", "pll_media_gate", }; +static const char *mmc1_src_p[] __initdata = { "mmc1srcsel", "mmc1_div", }; +static const char *mmc2_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc2_mux1_p[] __initdata = { "mmc2_mux0", "pll_media_gate", }; +static const char *mmc2_src_p[] __initdata = { "mmc2srcsel", "mmc2_div", }; +static const char *mmc0_sample_in[] __initdata = { "mmc0_sample", "mmc0_pad", }; +static const char *mmc1_sample_in[] __initdata = { "mmc1_sample", "mmc1_pad", }; +static const char *mmc2_sample_in[] __initdata = { "mmc2_sample", "mmc2_pad", }; +static const char *uart1_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart2_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart3_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart4_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *hifi_src[] __initdata = { "syspll", "pll_media_gate", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = { + { HI6220_MMC0_CLK, "mmc0_clk", "mmc0_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC0_CIUCLK, "mmc0_ciuclk", "mmc0_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC1_CLK, "mmc1_clk", "mmc1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC1_CIUCLK, "mmc1_ciuclk", "mmc1_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC2_CLK, "mmc2_clk", "mmc2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_MMC2_CIUCLK, "mmc2_ciuclk", "mmc2_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_USBOTG_HCLK, "usbotg_hclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 4, 0, }, + { HI6220_CLK_PICOPHY, "clk_picophy", "cs_dapb", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 5, 0, }, + { HI6220_HIFI, "hifi_clk", "hifi_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 0, 0, }, + { HI6220_DACODEC_PCLK, "dacodec_pclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 5, 0, }, + { HI6220_EDMAC_ACLK, "edmac_aclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x220, 2, 0, }, + { HI6220_CS_ATB, "cs_atb", "cs_atb_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 0, 0, }, + { HI6220_I2C0_CLK, "i2c0_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 1, 0, }, + { HI6220_I2C1_CLK, "i2c1_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 2, 0, }, + { HI6220_I2C2_CLK, "i2c2_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 3, 0, }, + { HI6220_I2C3_CLK, "i2c3_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 4, 0, }, + { HI6220_UART1_PCLK, "uart1_pclk", "uart1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 5, 0, }, + { HI6220_UART2_PCLK, "uart2_pclk", "uart2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 6, 0, }, + { HI6220_UART3_PCLK, "uart3_pclk", "uart3_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 7, 0, }, + { HI6220_UART4_PCLK, "uart4_pclk", "uart4_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 8, 0, }, + { HI6220_SPI_CLK, "spi_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 9, 0, }, + { HI6220_TSENSOR_CLK, "tsensor_clk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 12, 0, }, + { HI6220_MMU_CLK, "mmu_clk", "ddrc_axi1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x240, 11, 0, }, + { HI6220_HIFI_SEL, "hifi_sel", "hifi_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 0, 0, }, + { HI6220_MMC0_SYSPLL, "mmc0_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 1, 0, }, + { HI6220_MMC1_SYSPLL, "mmc1_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 2, 0, }, + { HI6220_MMC2_SYSPLL, "mmc2_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 3, 0, }, + { HI6220_MMC0_SEL, "mmc0_sel", "mmc0_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 6, 0, }, + { HI6220_MMC1_SEL, "mmc1_sel", "mmc1_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 7, 0, }, + { HI6220_BBPPLL_SEL, "bbppll_sel", "pll0_bbp_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 9, 0, }, + { HI6220_MEDIA_PLL_SRC, "media_pll_src", "pll_media_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 10, 0, }, + { HI6220_MMC2_SEL, "mmc2_sel", "mmc2_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 11, 0, }, + { HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 12, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_sys[] __initdata = { + { HI6220_MMC0_SRC, "mmc0_src", mmc0_src_p, ARRAY_SIZE(mmc0_src_p), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC0_SMP_IN, "mmc0_smp_in", mmc0_sample_in, ARRAY_SIZE(mmc0_sample_in), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC1_SRC, "mmc1_src", mmc1_src_p, ARRAY_SIZE(mmc1_src_p), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC1_SMP_IN, "mmc1_smp_in", mmc1_sample_in, ARRAY_SIZE(mmc1_sample_in), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC2_SRC, "mmc2_src", mmc2_src_p, ARRAY_SIZE(mmc2_src_p), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_MMC2_SMP_IN, "mmc2_smp_in", mmc2_sample_in, ARRAY_SIZE(mmc2_sample_in), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_HIFI_SRC, "hifi_src", hifi_src, ARRAY_SIZE(hifi_src), CLK_SET_RATE_PARENT, 0x400, 0, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART1_SRC, "uart1_src", uart1_src, ARRAY_SIZE(uart1_src), CLK_SET_RATE_PARENT, 0x400, 1, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART2_SRC, "uart2_src", uart2_src, ARRAY_SIZE(uart2_src), CLK_SET_RATE_PARENT, 0x400, 2, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART3_SRC, "uart3_src", uart3_src, ARRAY_SIZE(uart3_src), CLK_SET_RATE_PARENT, 0x400, 3, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART4_SRC, "uart4_src", uart4_src, ARRAY_SIZE(uart4_src), CLK_SET_RATE_PARENT, 0x400, 4, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX0, "mmc0_mux0", mmc0_mux0_p, ARRAY_SIZE(mmc0_mux0_p), CLK_SET_RATE_PARENT, 0x400, 5, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX0, "mmc1_mux0", mmc1_mux0_p, ARRAY_SIZE(mmc1_mux0_p), CLK_SET_RATE_PARENT, 0x400, 11, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX0, "mmc2_mux0", mmc2_mux0_p, ARRAY_SIZE(mmc2_mux0_p), CLK_SET_RATE_PARENT, 0x400, 12, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX1, "mmc0_mux1", mmc0_mux1_p, ARRAY_SIZE(mmc0_mux1_p), CLK_SET_RATE_PARENT, 0x400, 13, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX1, "mmc1_mux1", mmc1_mux1_p, ARRAY_SIZE(mmc1_mux1_p), CLK_SET_RATE_PARENT, 0x400, 14, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX1, "mmc2_mux1", mmc2_mux1_p, ARRAY_SIZE(mmc2_mux1_p), CLK_SET_RATE_PARENT, 0x400, 15, 1, CLK_MUX_HIWORD_MASK,}, +}; + +static struct hi6220_divider_clock hi6220_div_clks_sys[] __initdata = { + { HI6220_CLK_BUS, "clk_bus", "clk_300m", CLK_SET_RATE_PARENT, 0x490, 0, 4, 7, }, + { HI6220_MMC0_DIV, "mmc0_div", "mmc0_syspll", CLK_SET_RATE_PARENT, 0x494, 0, 6, 7, }, + { HI6220_MMC1_DIV, "mmc1_div", "mmc1_syspll", CLK_SET_RATE_PARENT, 0x498, 0, 6, 7, }, + { HI6220_MMC2_DIV, "mmc2_div", "mmc2_syspll", CLK_SET_RATE_PARENT, 0x49c, 0, 6, 7, }, + { HI6220_HIFI_DIV, "hifi_div", "hifi_sel", CLK_SET_RATE_PARENT, 0x4a0, 0, 4, 7, }, + { HI6220_BBPPLL0_DIV, "bbppll0_div", "bbppll_sel", CLK_SET_RATE_PARENT, 0x4a0, 8, 6, 15,}, + { HI6220_CS_DAPB, "cs_dapb", "picophy_src", CLK_SET_RATE_PARENT, 0x4a0, 24, 2, 31,}, + { HI6220_CS_ATB_DIV, "cs_atb_div", "cs_atb_syspll", CLK_SET_RATE_PARENT, 0x4a4, 0, 4, 7, }, +}; + +static void __init hi6220_clk_sys_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_SYS_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_sys, + ARRAY_SIZE(hi6220_separated_gate_clks_sys), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_sys, + ARRAY_SIZE(hi6220_mux_clks_sys), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_sys, + ARRAY_SIZE(hi6220_div_clks_sys), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init); + + +/* clocks in media controller */ +static const char *clk_1000_1200_src[] __initdata = { "pll_gpu_gate", "media_syspll_src", }; +static const char *clk_1440_1200_src[] __initdata = { "media_syspll_src", "media_pll_src", }; +static const char *clk_1000_1440_src[] __initdata = { "pll_gpu_gate", "media_pll_src", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_media[] __initdata = { + { HI6220_DSI_PCLK, "dsi_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 0, 0, }, + { HI6220_G3D_PCLK, "g3d_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 1, 0, }, + { HI6220_ACLK_CODEC_VPU, "aclk_codec_vpu", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 3, 0, }, + { HI6220_ISP_SCLK, "isp_sclk", "isp_sclk_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 5, 0, }, + { HI6220_ADE_CORE, "ade_core", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 6, 0, }, + { HI6220_MED_MMU, "media_mmu", "mmu_clk", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 8, 0, }, + { HI6220_CFG_CSI4PHY, "cfg_csi4phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 9, 0, }, + { HI6220_CFG_CSI2PHY, "cfg_csi2phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 10, 0, }, + { HI6220_ISP_SCLK_GATE, "isp_sclk_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 11, 0, }, + { HI6220_ISP_SCLK_GATE1, "isp_sclk_gate1", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 12, 0, }, + { HI6220_ADE_CORE_GATE, "ade_core_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 14, 0, }, + { HI6220_CODEC_VPU_GATE, "codec_vpu_gate", "clk_1000_1440", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 15, 0, }, + { HI6220_MED_SYSPLL, "media_syspll_src", "media_syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 17, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_media[] __initdata = { + { HI6220_1440_1200, "clk_1440_1200", clk_1440_1200_src, ARRAY_SIZE(clk_1440_1200_src), CLK_SET_RATE_PARENT, 0x51c, 0, 1, 0, }, + { HI6220_1000_1200, "clk_1000_1200", clk_1000_1200_src, ARRAY_SIZE(clk_1000_1200_src), CLK_SET_RATE_PARENT, 0x51c, 1, 1, 0, }, + { HI6220_1000_1440, "clk_1000_1440", clk_1000_1440_src, ARRAY_SIZE(clk_1000_1440_src), CLK_SET_RATE_PARENT, 0x51c, 6, 1, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_media[] __initdata = { + { HI6220_CODEC_JPEG, "codec_jpeg_aclk", "media_pll_src", CLK_SET_RATE_PARENT, 0xcbc, 0, 4, 23, }, + { HI6220_ISP_SCLK_SRC, "isp_sclk_src", "isp_sclk_gate", CLK_SET_RATE_PARENT, 0xcbc, 8, 4, 15, }, + { HI6220_ISP_SCLK1, "isp_sclk1", "isp_sclk_gate1", CLK_SET_RATE_PARENT, 0xcbc, 24, 4, 31, }, + { HI6220_ADE_CORE_SRC, "ade_core_src", "ade_core_gate", CLK_SET_RATE_PARENT, 0xcc0, 16, 3, 23, }, + { HI6220_ADE_PIX_SRC, "ade_pix_src", "clk_1440_1200", CLK_SET_RATE_PARENT, 0xcc0, 24, 6, 31, }, + { HI6220_G3D_CLK, "g3d_clk", "clk_1000_1200", CLK_SET_RATE_PARENT, 0xcc4, 8, 4, 15, }, + { HI6220_CODEC_VPU_SRC, "codec_vpu_src", "codec_vpu_gate", CLK_SET_RATE_PARENT, 0xcc4, 24, 6, 31, }, +}; + +static void __init hi6220_clk_media_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_MEDIA_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_media, + ARRAY_SIZE(hi6220_separated_gate_clks_media), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_media, + ARRAY_SIZE(hi6220_mux_clks_media), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_media, + ARRAY_SIZE(hi6220_div_clks_media), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init); + + +/* clocks in pmctrl */ +static struct hisi_gate_clock hi6220_gate_clks_power[] __initdata = { + { HI6220_PLL_GPU_GATE, "pll_gpu_gate", "gpupll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x8, 0, 0, }, + { HI6220_PLL1_DDR_GATE, "pll1_ddr_gate", "ddrpll1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x10, 0, 0, }, + { HI6220_PLL_DDR_GATE, "pll_ddr_gate", "ddrpll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x18, 0, 0, }, + { HI6220_PLL_MEDIA_GATE, "pll_media_gate", "media_pll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x38, 0, 0, }, + { HI6220_PLL0_BBP_GATE, "pll0_bbp_gate", "bbppll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x48, 0, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_power[] __initdata = { + { HI6220_DDRC_SRC, "ddrc_src", "ddr_sel_src", CLK_SET_RATE_PARENT, 0x5a8, 0, 4, 0, }, + { HI6220_DDRC_AXI1, "ddrc_axi1", "ddrc_src", CLK_SET_RATE_PARENT, 0x5a8, 8, 2, 0, }, +}; + +static void __init hi6220_clk_power_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_POWER_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate(hi6220_gate_clks_power, + ARRAY_SIZE(hi6220_gate_clks_power), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_power, + ARRAY_SIZE(hi6220_div_clks_power), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_power, "hisilicon,hi6220-pmctrl", hi6220_clk_power_init); diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index f1d239435826..0aaf29da8491 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -46,15 +46,15 @@ static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { { HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, }, }; -static const char *sfc_mux_p[] __initdata = { +static const char *const sfc_mux_p[] __initconst = { "24m", "150m", "200m", "100m", "75m", }; static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; -static const char *sdio_mux_p[] __initdata = { +static const char *const sdio_mux_p[] __initconst = { "75m", "100m", "50m", "15m", }; static u32 sdio_mux_table[] = {0, 1, 2, 3}; -static const char *fephy_mux_p[] __initdata = { "25m", "125m"}; +static const char *const fephy_mux_p[] __initconst = { "25m", "125m"}; static u32 fephy_mux_table[] = {0, 1}; @@ -252,8 +252,9 @@ static struct clk_ops clk_complex_ops = { .disable = clk_complex_disable, }; -void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, - int nums, struct hisi_clock_data *data) +static void __init +hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, int nums, + struct hisi_clock_data *data) { void __iomem *base = data->base; int i; diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index a078e84f7b05..c90a89739b03 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -232,3 +232,32 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } + +void __init hi6220_clk_register_divider(struct hi6220_divider_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = hi6220_register_clkdiv(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, + clks[i].width, + clks[i].mask_bit, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } +} diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 31083ffc0650..b56fbc1c5f27 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -55,7 +55,7 @@ struct hisi_fixed_factor_clock { struct hisi_mux_clock { unsigned int id; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; unsigned long offset; @@ -79,6 +79,18 @@ struct hisi_divider_clock { const char *alias; }; +struct hi6220_divider_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u32 mask_bit; + const char *alias; +}; + struct hisi_gate_clock { unsigned int id; const char *name; @@ -94,18 +106,23 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *, const char *, unsigned long, void __iomem *, u8, u8, spinlock_t *); +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock); -struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int); -void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_mux(struct hisi_mux_clock *, int, +struct hisi_clock_data *hisi_clk_init(struct device_node *, int); +void hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_mux(struct hisi_mux_clock *, int, struct hisi_clock_data *); -void __init hisi_clk_register_divider(struct hisi_divider_clock *, +void hisi_clk_register_divider(struct hisi_divider_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate(struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate_sep(struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hi6220_clk_register_divider(struct hi6220_divider_clock *, int, struct hisi_clock_data *); -void __init hisi_clk_register_gate(struct hisi_gate_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, - int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c new file mode 100644 index 000000000000..113eee8ed23a --- /dev/null +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -0,0 +1,156 @@ +/* + * Hisilicon hi6220 SoC divider clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang <bintian.wang@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/spinlock.h> + +#define div_mask(width) ((1 << (width)) - 1) + +/** + * struct hi6220_clk_divider - divider clock for hi6220 + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @mask: mask for setting divider rate + * @table: the div table that the divider supports + * @lock: register lock + */ +struct hi6220_clk_divider { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u32 mask; + const struct clk_div_table *table; + spinlock_t *lock; +}; + +#define to_hi6220_clk_divider(_hw) \ + container_of(_hw, struct hi6220_clk_divider, hw) + +static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int val; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= div_mask(dclk->width); + + return divider_recalc_rate(hw, parent_rate, val, dclk->table, + CLK_DIVIDER_ROUND_CLOSEST); +} + +static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + return divider_round_rate(hw, rate, prate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int value; + unsigned long flags = 0; + u32 data; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + value = divider_get_val(rate, parent_rate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(div_mask(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mask; + + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static const struct clk_ops hi6220_clkdiv_ops = { + .recalc_rate = hi6220_clkdiv_recalc_rate, + .round_rate = hi6220_clkdiv_round_rate, + .set_rate = hi6220_clkdiv_set_rate, +}; + +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock) +{ + struct hi6220_clk_divider *div; + struct clk *clk; + struct clk_init_data init; + struct clk_div_table *table; + u32 max_div, min_div; + int i; + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + /* Init the divider table */ + max_div = div_mask(width) + 1; + min_div = 1; + + table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < max_div; i++) { + table[i].div = min_div + i; + table[i].val = table[i].div - 1; + } + + init.name = name; + init.ops = &hi6220_clkdiv_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + /* struct hi6220_clk_divider assignments */ + div->reg = reg; + div->shift = shift; + div->width = width; + div->mask = mask_bit ? BIT(mask_bit) : 0; + div->lock = lock; + div->hw.init = &init; + div->table = table; + + /* register the clock */ + clk = clk_register(dev, &div->hw); + if (IS_ERR(clk)) { + kfree(table); + kfree(div); + } + + return clk; +} diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c index 0dd8a4b12747..4a375ead70e9 100644 --- a/drivers/clk/keystone/pll.c +++ b/drivers/clk/keystone/pll.c @@ -37,7 +37,8 @@ * Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL * or PA PLL available on keystone2. These PLLs are controlled by * this register. Main PLL is controlled by a PLL controller. - * @pllm: PLL register map address + * @pllm: PLL register map address for multiplier bits + * @pllod: PLL register map address for post divider bits * @pll_ctl0: PLL controller map address * @pllm_lower_mask: multiplier lower mask * @pllm_upper_mask: multiplier upper mask @@ -53,6 +54,7 @@ struct clk_pll_data { u32 phy_pllm; u32 phy_pll_ctl0; void __iomem *pllm; + void __iomem *pllod; void __iomem *pll_ctl0; u32 pllm_lower_mask; u32 pllm_upper_mask; @@ -102,7 +104,11 @@ static unsigned long clk_pllclk_recalc(struct clk_hw *hw, /* read post divider from od bits*/ postdiv = ((val & pll_data->clkod_mask) >> pll_data->clkod_shift) + 1; - else + else if (pll_data->pllod) { + postdiv = readl(pll_data->pllod); + postdiv = ((postdiv & pll_data->clkod_mask) >> + pll_data->clkod_shift) + 1; + } else postdiv = pll_data->postdiv; rate /= (prediv + 1); @@ -172,12 +178,21 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) /* assume the PLL has output divider register bits */ pll_data->clkod_mask = CLKOD_MASK; pll_data->clkod_shift = CLKOD_SHIFT; + + /* + * Check if there is an post-divider register. If not + * assume od bits are part of control register. + */ + i = of_property_match_string(node, "reg-names", + "post-divider"); + pll_data->pllod = of_iomap(node, i); } i = of_property_match_string(node, "reg-names", "control"); pll_data->pll_ctl0 = of_iomap(node, i); if (!pll_data->pll_ctl0) { pr_err("%s: ioremap failed\n", __func__); + iounmap(pll_data->pllod); goto out; } @@ -193,6 +208,7 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) pll_data->pllm = of_iomap(node, i); if (!pll_data->pllm) { iounmap(pll_data->pll_ctl0); + iounmap(pll_data->pllod); goto out; } } diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 000000000000..8e4b2a4635b9 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1,4 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o +obj-$(CONFIG_RESET_CONTROLLER) += reset.o +obj-y += clk-mt8135.o +obj-y += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 000000000000..57020368a693 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 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/of.h> +#include <linux/of_address.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init = {}; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 000000000000..6b6780b1e9c5 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c new file mode 100644 index 000000000000..08b4b849b491 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2014 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/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/mfd/syscon.h> +#include <dt-bindings/clock/mt8135-clk.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static DEFINE_SPINLOCK(mt8135_clk_lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(CLK_TOP_DSI0_LNTC_DSICLK, "dsi0_lntc_dsiclk", "clk_null", 1, 1), + FACTOR(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_clkdig_cts", "clk_null", 1, 1), + FACTOR(CLK_TOP_CLKPH_MCK, "clkph_mck", "clk_null", 1, 1), + FACTOR(CLK_TOP_CPUM_TCK_IN, "cpum_tck_in", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CLK_TOP_MAINPLL_806M, "mainpll_806m", "mainpll", 1, 2), + FACTOR(CLK_TOP_MAINPLL_537P3M, "mainpll_537p3m", "mainpll", 1, 3), + FACTOR(CLK_TOP_MAINPLL_322P4M, "mainpll_322p4m", "mainpll", 1, 5), + FACTOR(CLK_TOP_MAINPLL_230P3M, "mainpll_230p3m", "mainpll", 1, 7), + + FACTOR(CLK_TOP_UNIVPLL_624M, "univpll_624m", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_416M, "univpll_416m", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIVPLL_249P6M, "univpll_249p6m", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIVPLL_178P3M, "univpll_178p3m", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIVPLL_48M, "univpll_48m", "univpll", 1, 26), + + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + FACTOR(CLK_TOP_MMPLL_D3, "mmpll_d3", "mmpll", 1, 3), + FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5), + FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7), + FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll_d2", 1, 2), + FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll_d3", 1, 2), + + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll_806m", 1, 1), + FACTOR(CLK_TOP_SYSPLL_D4, "syspll_d4", "mainpll_806m", 1, 2), + FACTOR(CLK_TOP_SYSPLL_D6, "syspll_d6", "mainpll_806m", 1, 3), + FACTOR(CLK_TOP_SYSPLL_D8, "syspll_d8", "mainpll_806m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D10, "syspll_d10", "mainpll_806m", 1, 5), + FACTOR(CLK_TOP_SYSPLL_D12, "syspll_d12", "mainpll_806m", 1, 6), + FACTOR(CLK_TOP_SYSPLL_D16, "syspll_d16", "mainpll_806m", 1, 8), + FACTOR(CLK_TOP_SYSPLL_D24, "syspll_d24", "mainpll_806m", 1, 12), + + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll_537p3m", 1, 1), + + FACTOR(CLK_TOP_SYSPLL_D2P5, "syspll_d2p5", "mainpll_322p4m", 2, 1), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll_322p4m", 1, 1), + + FACTOR(CLK_TOP_SYSPLL_D3P5, "syspll_d3p5", "mainpll_230p3m", 2, 1), + + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_624m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_624m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D6, "univpll1_d6", "univpll_624m", 1, 6), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_624m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL1_D10, "univpll1_d10", "univpll_624m", 1, 10), + + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_416m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_416m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D6, "univpll2_d6", "univpll_416m", 1, 6), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_416m", 1, 8), + + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_416m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_249p6m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_178p3m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D10, "univpll_d10", "univpll_249p6m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_48m", 1, 1), + + FACTOR(CLK_TOP_APLL, "apll_ck", "audpll", 1, 1), + FACTOR(CLK_TOP_APLL_D4, "apll_d4", "audpll", 1, 4), + FACTOR(CLK_TOP_APLL_D8, "apll_d8", "audpll", 1, 8), + FACTOR(CLK_TOP_APLL_D16, "apll_d16", "audpll", 1, 16), + FACTOR(CLK_TOP_APLL_D24, "apll_d24", "audpll", 1, 24), + + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(CLK_TOP_LVDSTX_CLKDIG_CT, "lvdstx_clkdig_cts", "lvdspll", 1, 1), + FACTOR(CLK_TOP_VPLL_DPIX, "vpll_dpix_ck", "lvdspll", 1, 1), + + FACTOR(CLK_TOP_TVHDMI_H, "tvhdmi_h_ck", "tvdpll", 1, 1), + + FACTOR(CLK_TOP_HDMITX_CLKDIG_D2, "hdmitx_clkdig_d2", "hdmitx_clkdig_cts", 1, 2), + FACTOR(CLK_TOP_HDMITX_CLKDIG_D3, "hdmitx_clkdig_d3", "hdmitx_clkdig_cts", 1, 3), + + FACTOR(CLK_TOP_TVHDMI_D2, "tvhdmi_d2", "tvhdmi_h_ck", 1, 2), + FACTOR(CLK_TOP_TVHDMI_D4, "tvhdmi_d4", "tvhdmi_h_ck", 1, 4), + + FACTOR(CLK_TOP_MEMPLL_MCK_D4, "mempll_mck_d4", "clkph_mck", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d4", + "syspll_d6", + "univpll_d5", + "univpll2_d2", + "syspll_d3p5" +}; + +static const char * const smi_parents[] __initconst = { + "clk26m", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d2", + "univpll1_d6", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7", + "vdecpll", + "lvdspll" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "univpll1_d4", + "syspll_d2", + "syspll_d2p5", + "syspll_d3", + "univpll_d5", + "univpll1_d2", + "mmpll_d2", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll1_d6" +}; + +static const char * const cam_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "univpll_d5", + "univpll2_d2", + "univpll_d7", + "univpll1_d4" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll_d6", + "univpll_d10" +}; + +static const char * const jpg_parents[] __initconst = { + "clk26m", + "syspll_d5", + "syspll_d4", + "syspll_d3", + "univpll_d7", + "univpll2_d2", + "univpll_d5" +}; + +static const char * const disp_parents[] __initconst = { + "clk26m", + "syspll_d3p5", + "syspll_d3", + "univpll2_d2", + "univpll_d5", + "univpll1_d2", + "lvdspll", + "vdecpll" +}; + +static const char * const msdc30_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d5", + "univpll1_d4", + "univpll2_d4", + "msdcpll" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "univpll1_d10" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d6", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "clkph_mck" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll1_d6", + "syspll_d16", + "syspll_d8" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll_d24" +}; + +static const char * const fix_parents[] __initconst = { + "rtc32k", + "clk26m", + "univpll_d5", + "univpll_d7", + "univpll1_d2", + "univpll1_d4", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vdecpll", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "syspll_d5", + "syspll_d6", + "syspll_d8", + "univpll1_d2", + "univpll2_d2", + "univpll_d7", + "univpll_d10", + "univpll2_d4", + "lvdspll" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "axi_sel", + "syspll_d12" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d10", + "mempll_mck_d4", + "univpll_d26", + "syspll_d24" +}; + +static const char * const smi_mfg_as_parents[] __initconst = { + "clk26m", + "smi_sel", + "mfg_sel", + "mem_sel" +}; + +static const char * const gcpu_parents[] __initconst = { + "clk26m", + "syspll_d4", + "univpll_d7", + "syspll_d5", + "syspll_d6" +}; + +static const char * const dpi1_parents[] __initconst = { + "clk26m", + "tvhdmi_h_ck", + "tvhdmi_d2", + "tvhdmi_d4" +}; + +static const char * const cci_parents[] __initconst = { + "clk26m", + "mainpll_537p3m", + "univpll_d3", + "syspll_d2p5", + "syspll_d3", + "syspll_d5" +}; + +static const char * const apll_parents[] __initconst = { + "clk26m", + "apll_ck", + "apll_d4", + "apll_d8", + "apll_d16", + "apll_d24" +}; + +static const char * const hdmipll_parents[] __initconst = { + "clk26m", + "hdmitx_clkdig_cts", + "hdmitx_clkdig_d2", + "hdmitx_clkdig_d3" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, + 0x0140, 0, 3, INVALID_MUX_GATE_BIT), + MUX_GATE(CLK_TOP_SMI_SEL, "smi_sel", smi_parents, 0x0140, 8, 4, 15), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0140, 16, 4, 23), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x0140, 24, 2, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_CAM_SEL, "cam_sel", cam_parents, 0x0144, 0, 3, 7), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, + 0x0144, 8, 2, 15), + MUX_GATE(CLK_TOP_JPG_SEL, "jpg_sel", jpg_parents, 0x0144, 16, 3, 23), + MUX_GATE(CLK_TOP_DISP_SEL, "disp_sel", disp_parents, 0x0144, 24, 3, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, 0x0148, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, 0x0148, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, 0x0148, 16, 3, 23), + MUX_GATE(CLK_TOP_MSDC30_4_SEL, "msdc30_4_sel", msdc30_parents, 0x0148, 24, 3, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x014c, 0, 2, 7), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0150, 8, 3, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0150, 16, 3, 23), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0150, 24, 2, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0158, 0, 2, 7), + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0158, 8, 3, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0158, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_FIX_SEL, "fix_sel", fix_parents, 0x015c, 0, 3, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x015c, 8, 4, 15), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, + 0x015c, 16, 2, 23), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x015c, 24, 3, 31), + /* CLK_CFG_8 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0164, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, 0x0164, 8, 3, 15), + MUX_GATE(CLK_TOP_SMI_MFG_AS_SEL, "smi_mfg_as_sel", smi_mfg_as_parents, + 0x0164, 16, 2, 23), + MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0164, 24, 3, 31), + /* CLK_CFG_9 */ + MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0168, 0, 2, 7), + MUX_GATE(CLK_TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15), + MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x0168, 16, 3, 23), + MUX_GATE(CLK_TOP_HDMIPLL_SEL, "hdmipll_sel", hdmipll_parents, 0x0168, 24, 2, 31), +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23), + GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22), + GATE_ICG(CLK_INFRA_CCIF1_AP_CTRL, "ccif1_ap_ctrl", "axi_sel", 21), + GATE_ICG(CLK_INFRA_CCIF0_AP_CTRL, "ccif0_ap_ctrl", "axi_sel", 20), + GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16), + GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15), + GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8), + GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7), + GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), + GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5), + GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2), + GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1), + GATE_ICG(CLK_INFRA_DBGCLK, "dbgclk_ck", "axi_sel", 0), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_gates[] __initconst = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_I2C5, "i2c5_ck", "axi_sel", 31), + GATE_PERI0(CLK_PERI_I2C4, "i2c4_ck", "axi_sel", 30), + GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "axi_sel", 29), + GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 28), + GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 27), + GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 26), + GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 25), + GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 24), + GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 23), + GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 22), + GATE_PERI0(CLK_PERI_IRDA, "irda_ck", "irda_sel", 21), + GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 20), + GATE_PERI0(CLK_PERI_MD_HIF, "md_hif_ck", "axi_sel", 19), + GATE_PERI0(CLK_PERI_AP_HIF, "ap_hif_ck", "axi_sel", 18), + GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_4_sel", 17), + GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC20_2, "msdc20_2_ck", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC20_1, "msdc20_1_ck", "msdc30_0_sel", 13), + GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12), + GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9), + GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2), + GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1), + GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "axi_sel", 0), + /* PERI1 */ + GATE_PERI1(CLK_PERI_USBSLV, "usbslv_ck", "axi_sel", 8), + GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 7), + GATE_PERI1(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 6), + GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "gcpu_sel", 5), + GATE_PERI1(CLK_PERI_FHCTL, "fhctl_ck", "clk26m", 4), + GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi_sel", 3), + GATE_PERI1(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 2), + GATE_PERI1(CLK_PERI_PERI_PWRAP, "peri_pwrap_ck", "axi_sel", 1), + GATE_PERI1(CLK_PERI_I2C6, "i2c6_ck", "axi_sel", 0), +}; + +static const char * const uart_ck_sel_parents[] __initconst = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_clks[] __initconst = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + 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_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, + &mt8135_clk_lock, clk_data); + + clk_prepare_enable(clk_data->clks[CLK_TOP_CCI_SEL]); + + 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_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + clk_prepare_enable(clk_data->clks[CLK_INFRA_M4U]); + + 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_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates), + clk_data); + mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base, + &mt8135_clk_lock, 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); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init); + +#define MT8135_PLL_FMAX (2000 * MHZ) +#define CON0_MT8135_RST_BAR BIT(27) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8135_RST_BAR, \ + .fmax = MT8135_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000001, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000001, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000001, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000001, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000001, 0, 21, 0x278, 6, 0x0, 0x27c, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000001, 0, 31, 0x294, 6, 0x0, 0x298, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0), + PLL(CLK_APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000001, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0), + PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x308, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", + mtk_apmixedsys_init); diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c new file mode 100644 index 000000000000..4b9e04cdf7e8 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -0,0 +1,830 @@ +/* + * Copyright (c) 2014 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/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> + +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_factor top_divs[] __initconst = { + FACTOR(CLK_TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2), + FACTOR(CLK_TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3), + + FACTOR(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2), + FACTOR(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3), + FACTOR(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5), + FACTOR(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7), + + FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4), + FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3), + + FACTOR(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26), + + FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1), + FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), + FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1), + + FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), + FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), + + FACTOR(CLK_TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1), + FACTOR(CLK_TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1), + + FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1), + FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1), + + FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1), + FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2), + FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4), + FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8), + FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16), + + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1), + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + + FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1), + FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2), + FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4), + FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1), + FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2), + FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4), + + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1), + FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2), + FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4), + FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8), + FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16), + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1), + FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2), + FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1), + FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2), + FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1), + FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2), + FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4), + + FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1), + FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2), + FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4), + FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8), + FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16), + + FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2), + + FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3), + FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4), + + FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1), + FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2), + FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "dmpll_ck" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "syspll1_d8" +}; + +static const char * const mm_parents[] __initconst = { + "clk26m", + "vencpll_d2", + "main_h364m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "mmpll_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "syspll_d3", + "syspll1_d2", + "syspll_d5", + "univpll_d3", + "univpll1_d2", + "univpll_d5", + "univpll2_d2" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll2_d2", + "syspll3_d2", + "syspll3_d4", + "univpll1_d4" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d4", + "univpll1_d8" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll1_d8", + "univpll3_d4" +}; + +static const char * const usb30_parents[] __initconst = { + "clk26m", + "univpll3_d2", + "usb_syspll_125m", + "univpll2_d4" +}; + +static const char * const msdc50_0_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const msdc50_0_parents[] __initconst = { + "clk26m", + "msdcpll_ck", + "msdcpll_d2", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "msdcpll_d4", + "vencpll_d4", + "tvdpll_ck", + "univpll_d2", + "univpll1_d2", + "mmpll_ck", + "msdcpll2_ck", + "msdcpll2_d2", + "msdcpll2_d4" +}; + +static const char * const msdc30_1_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4" +}; + +static const char * const msdc30_2_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d2" +}; + +static const char * const msdc30_3_parents[] __initconst = { + "clk26m", + "msdcpll2_ck", + "msdcpll2_d2", + "univpll2_d2", + "msdcpll2_d4", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4", + "msdcpll_ck", + "msdcpll_d2", + "msdcpll_d4" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll3_d4", + "syspll4_d4", + "syspll1_d16" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d8", + "dmpll_d4", + "dmpll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "syspll1_d8", + "syspll3_d4", + "syspll1_d16", + "univpll3_d4", + "univpll_d26", + "dmpll_d8", + "dmpll_d16" +}; + +static const char * const scp_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "syspll_d5", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const atb_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "dmpll_d2" +}; + +static const char * const venc_lt_parents[] __initconst = { + "clk26m", + "univpll_d3", + "vcodecpll_ck", + "tvdpll_445p5m", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "syspll1_d2", + "univpll_d5", + "vcodecpll_370p5", + "dmpll_ck" +}; + +static const char * const dpi0_parents[] __initconst = { + "clk26m", + "tvdpll_d2", + "tvdpll_d4", + "clk26m", + "clk26m", + "tvdpll_d8", + "tvdpll_d16" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "syspll2_d4" +}; + +static const char * const cci400_parents[] __initconst = { + "clk26m", + "vencpll_ck", + "armca7pll_754m", + "armca7pll_502m", + "univpll_d2", + "syspll_d2", + "msdcpll_ck", + "dmpll_ck" +}; + +static const char * const aud_1_parents[] __initconst = { + "clk26m", + "apll1_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const aud_2_parents[] __initconst = { + "clk26m", + "apll2_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const mem_mfg_in_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m" +}; + +static const char * const axi_mfg_in_parents[] __initconst = { + "clk26m", + "axi_sel", + "dmpll_d2" +}; + +static const char * const scam_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "univpll2_d4", + "dmpll_d4" +}; + +static const char * const spinfi_ifr_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll3_d4", + "syspll4_d2", + "univpll2_d4", + "univpll3_d2", + "syspll1_d4", + "univpll1_d4" +}; + +static const char * const hdmi_parents[] __initconst = { + "clk26m", + "hdmitx_dig_cts", + "hdmitxpll_d2", + "hdmitxpll_d3" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8", + "fpc_ck" +}; + +static const char * const msdc50_2_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const hdcp_parents[] __initconst = { + "clk26m", + "syspll4_d2", + "syspll3_d4", + "univpll2_d4" +}; + +static const char * const hdcp_24m_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll_d52", + "univpll2_d8" +}; + +static const char * const rtc_parents[] __initconst = { + "clkrtc_int", + "clkrtc_ext", + "clk26m", + "univpll3_d8" +}; + +static const char * const i2s0_m_ck_parents[] __initconst = { + "apll1_div1", + "apll2_div1" +}; + +static const char * const i2s1_m_ck_parents[] __initconst = { + "apll1_div2", + "apll2_div2" +}; + +static const char * const i2s2_m_ck_parents[] __initconst = { + "apll1_div3", + "apll2_div3" +}; + +static const char * const i2s3_m_ck_parents[] __initconst = { + "apll1_div4", + "apll2_div4" +}; + +static const char * const i2s3_b_ck_parents[] __initconst = { + "apll1_div5", + "apll2_div5" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3), + MUX(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23), + MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15), + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23), + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7), + MUX_GATE(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents, 0x0070, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x0070, 16, 4, 23), + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x0070, 24, 3, 31), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x0080, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x0080, 8, 4, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0080, 16, 2, 23), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0080, 24, 3, 31), + /* CLK_CFG_5 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0090, 0, 3, 7 /* 7:5 */), + MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15), + MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23), + MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15), + MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23), + MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7), + MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents, 0x00b0, 8, 2, 15), + MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents, 0x00b0, 16, 2, 23), + MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31), + /* CLK_CFG_12 */ + MUX_GATE(CLK_TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents, 0x00c0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x00c0, 24, 3, 31), + /* CLK_CFG_13 */ + MUX_GATE(CLK_TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents, 0x00d0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15), + MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents, 0x00d0, 16, 2, 23), + MUX(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2), + + DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24), + DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0), + DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8), + DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16), + DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24), + DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0), + + DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28), + DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0), + DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8), + DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16), + DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24), + DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4), + + MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1), + MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1), + MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1), + MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1), + 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 = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0), + GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1), + GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5), + 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_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 = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_gates[] __initconst = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0), + GATE_PERI0(CLK_PERI_THERM, "peri_therm", "axi_sel", 1), + GATE_PERI0(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2), + GATE_PERI0(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9), + GATE_PERI0(CLK_PERI_USB0, "peri_usb0", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12), + GATE_PERI0(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13), + GATE_PERI0(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17), + GATE_PERI0(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18), + GATE_PERI0(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19), + GATE_PERI0(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20), + GATE_PERI0(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21), + GATE_PERI0(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22), + GATE_PERI0(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23), + GATE_PERI0(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24), + GATE_PERI0(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25), + GATE_PERI0(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26), + GATE_PERI0(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27), + GATE_PERI0(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28), + GATE_PERI0(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29), + GATE_PERI0(CLK_PERI_I2C5, "peri_i2c5", "axi_sel", 30), + GATE_PERI0(CLK_PERI_NFIECC, "peri_nfiecc", "axi_sel", 31), + /* PERI1 */ + GATE_PERI1(CLK_PERI_SPI, "peri_spi", "spi_sel", 0), + GATE_PERI1(CLK_PERI_IRRX, "peri_irrx", "spi_sel", 1), + GATE_PERI1(CLK_PERI_I2C6, "peri_i2c6", "axi_sel", 2), +}; + +static const char * const uart_ck_sel_parents[] __initconst = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_clks[] __initconst = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + 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_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); + + clk_prepare_enable(clk_data->clks[CLK_TOP_CCI400_SEL]); + + 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_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_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); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates), + clk_data); + mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base, + &mt8173_clk_lock, 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); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init); + +#define MT8173_PLL_FMAX (3000UL * MHZ) + +#define CON0_MT8173_RST_BAR BIT(24) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, \ + _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8173_RST_BAR, \ + .fmax = MT8173_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0x00000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0x00000001, 0, 21, 0x214, 24, 0x0, 0x214, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR, 21, 0x220, 4, 0x0, 0x224, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000001, HAVE_RST_BAR, 7, 0x230, 4, 0x0, 0x234, 14), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0x00000001, 0, 21, 0x244, 24, 0x0, 0x244, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0x00000001, 0, 21, 0x250, 4, 0x0, 0x254, 0), + PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0x00000001, 0, 21, 0x260, 4, 0x0, 0x264, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0x00000001, 0, 21, 0x270, 4, 0x0, 0x274, 0), + PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0x00000001, 0, 21, 0x280, 4, 0x0, 0x284, 0), + PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0x00000001, 0, 21, 0x290, 4, 0x0, 0x294, 0), + PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0x00000001, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0), + PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0x00000001, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0x00000001, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0), + PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0x00000001, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); + + clk_prepare_enable(clk_data->clks[CLK_APMIXED_ARMCA15PLL]); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys", + mtk_apmixedsys_init); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 000000000000..18444aea63c9 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 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/of.h> +#include <linux/of_address.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; i++) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data) +{ + struct clk *clk; + int i; + + for (i = 0; i < num; i++) { + const struct mtk_composite *mc = &mcs[i]; + + clk = mtk_clk_register_composite(mc, base, lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mc->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mc->id] = clk; + } +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 000000000000..9dda9d8ad10b --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const *parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +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); + +#ifdef CONFIG_RESET_CONTROLLER +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs); +#else +static inline void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ +} +#endif + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 000000000000..44409e98c52f --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014 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/of.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> +#include <linux/delay.h> + +#include "clk-mtk.h" + +#define REG_CON0 0 +#define REG_CON1 4 + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +#define POSTDIV_MASK 0x7 +#define INTEGER_BITS 7 + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + vco++; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw, + int postdiv) +{ + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(POSTDIV_MASK << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + REG_CON1); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + REG_CON1); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + udelay(20); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(pll, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->en_mask; + writel(r, pll->base_addr + REG_CON0); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + udelay(20); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r &= ~pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr + REG_CON0); + r &= ~CON0_BASE_EN; + writel(r, pll->base_addr + REG_CON0); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init = {}; + struct clk *clk; + const char *parent_name = "clk26m"; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +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; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + 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/mediatek/reset.c b/drivers/clk/mediatek/reset.c new file mode 100644 index 000000000000..9e9fe4b19ac4 --- /dev/null +++ b/drivers/clk/mediatek/reset.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * + * 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/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> + +#include "clk-mtk.h" + +struct mtk_reset { + struct regmap *regmap; + int regofs; + struct reset_controller_dev rcdev; +}; + +static int mtk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), ~0); +} + +static int mtk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), 0); +} + +static int mtk_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = mtk_reset_assert(rcdev, id); + if (ret) + return ret; + + return mtk_reset_deassert(rcdev, id); +} + +static struct reset_control_ops mtk_reset_ops = { + .assert = mtk_reset_assert, + .deassert = mtk_reset_deassert, + .reset = mtk_reset, +}; + +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ + struct mtk_reset *data; + int ret; + struct regmap *regmap; + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", np->full_name, + PTR_ERR(regmap)); + return; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + data->regmap = regmap; + data->regofs = regofs; + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = num_regs * 32; + data->rcdev.ops = &mtk_reset_ops; + data->rcdev.of_node = np; + + ret = reset_controller_register(&data->rcdev); + if (ret) { + pr_err("could not register reset controller: %d\n", ret); + kfree(data); + return; + } +} diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile new file mode 100644 index 000000000000..6d45531df9ab --- /dev/null +++ b/drivers/clk/meson/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Meson specific clk +# + +obj-y += clkc.o clk-pll.o clk-cpu.o +obj-y += meson8b-clkc.o diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c new file mode 100644 index 000000000000..71ad493b94df --- /dev/null +++ b/drivers/clk/meson/clk-cpu.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * CPU clock path: + * + * +-[/N]-----|3| + * MUX2 +--[/3]-+----------|2| MUX1 + * [sys_pll]---|1| |--[/2]------------|1|-|1| + * | |---+------------------|0| | |----- [a5_clk] + * +--|0| | | + * [xtal]---+-------------------------------|0| + * + * + * + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/clk-provider.h> + +#define MESON_CPU_CLK_CNTL1 0x00 +#define MESON_CPU_CLK_CNTL 0x40 + +#define MESON_CPU_CLK_MUX1 BIT(7) +#define MESON_CPU_CLK_MUX2 BIT(0) + +#define MESON_N_WIDTH 9 +#define MESON_N_SHIFT 20 +#define MESON_SEL_WIDTH 2 +#define MESON_SEL_SHIFT 2 + +#include "clkc.h" + +struct meson_clk_cpu { + struct notifier_block clk_nb; + const struct clk_div_table *div_table; + struct clk_hw hw; + void __iomem *base; + u16 reg_off; +}; +#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw) +#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb) + +static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + + return divider_round_rate(hw, rate, prate, clk_cpu->div_table, + MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + unsigned int div, sel, N = 0; + u32 reg; + + div = DIV_ROUND_UP(parent_rate, rate); + + if (div <= 3) { + sel = div - 1; + } else { + sel = 3; + N = div / 2; + } + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N); + writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel); + writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + + return 0; +} + +static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + unsigned int N, sel; + unsigned int div = 1; + u32 reg; + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg); + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg); + + if (sel < 3) + div = sel + 1; + else + div = 2 * N; + + return parent_rate / div; +} + +static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu, + struct clk_notifier_data *ndata) +{ + u32 cpu_clk_cntl; + + /* switch MUX1 to xtal */ + cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + udelay(100); + + /* switch MUX2 to sys-pll */ + cpu_clk_cntl |= MESON_CPU_CLK_MUX2; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + + return 0; +} + +static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu, + struct clk_notifier_data *ndata) +{ + u32 cpu_clk_cntl; + + /* switch MUX1 to divisors' output */ + cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + cpu_clk_cntl |= MESON_CPU_CLK_MUX1; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + udelay(100); + + return 0; +} + +/* + * This clock notifier is called when the frequency of the of the parent + * PLL clock is to be changed. We use the xtal input as temporary parent + * while the PLL frequency is stabilized. + */ +static int meson_clk_cpu_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb); + int ret = 0; + + if (event == PRE_RATE_CHANGE) + ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata); + else if (event == POST_RATE_CHANGE) + ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata); + + return notifier_from_errno(ret); +} + +static const struct clk_ops meson_clk_cpu_ops = { + .recalc_rate = meson_clk_cpu_recalc_rate, + .round_rate = meson_clk_cpu_round_rate, + .set_rate = meson_clk_cpu_set_rate, +}; + +struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, + void __iomem *reg_base, + spinlock_t *lock) +{ + struct clk *clk; + struct clk *pclk; + struct meson_clk_cpu *clk_cpu; + struct clk_init_data init; + int ret; + + clk_cpu = kzalloc(sizeof(*clk_cpu), GFP_KERNEL); + if (!clk_cpu) + return ERR_PTR(-ENOMEM); + + clk_cpu->base = reg_base; + clk_cpu->reg_off = clk_conf->reg_off; + clk_cpu->div_table = clk_conf->conf.div_table; + clk_cpu->clk_nb.notifier_call = meson_clk_cpu_notifier_cb; + + init.name = clk_conf->clk_name; + init.ops = &meson_clk_cpu_ops; + init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE; + init.flags |= CLK_SET_RATE_PARENT; + init.parent_names = clk_conf->clks_parent; + init.num_parents = 1; + + clk_cpu->hw.init = &init; + + pclk = __clk_lookup(clk_conf->clks_parent[0]); + if (!pclk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, clk_conf->clks_parent[0]); + ret = -EINVAL; + goto free_clk; + } + + ret = clk_notifier_register(pclk, &clk_cpu->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, clk_conf->clk_name); + goto free_clk; + } + + clk = clk_register(NULL, &clk_cpu->hw); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto unregister_clk_nb; + } + + return clk; + +unregister_clk_nb: + clk_notifier_unregister(pclk, &clk_cpu->clk_nb); +free_clk: + kfree(clk_cpu); + + return ERR_PTR(ret); +} + diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c new file mode 100644 index 000000000000..664edf0708ea --- /dev/null +++ b/drivers/clk/meson/clk-pll.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * In the most basic form, a Meson PLL is composed as follows: + * + * PLL + * +------------------------------+ + * | | + * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out + * | ^ ^ | + * +------------------------------+ + * | | + * FREF VCO + * + * out = (in * M / N) >> OD + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include "clkc.h" + +#define MESON_PLL_RESET BIT(29) +#define MESON_PLL_LOCK BIT(31) + +struct meson_clk_pll { + struct clk_hw hw; + void __iomem *base; + struct pll_conf *conf; + unsigned int rate_count; + spinlock_t *lock; +}; +#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) + +static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + struct parm *p; + unsigned long parent_rate_mhz = parent_rate / 1000000; + unsigned long rate_mhz; + u16 n, m, od; + u32 reg; + + p = &pll->conf->n; + reg = readl(pll->base + p->reg_off); + n = PARM_GET(p->width, p->shift, reg); + + p = &pll->conf->m; + reg = readl(pll->base + p->reg_off); + m = PARM_GET(p->width, p->shift, reg); + + p = &pll->conf->od; + reg = readl(pll->base + p->reg_off); + od = PARM_GET(p->width, p->shift, reg); + + rate_mhz = (parent_rate_mhz * m / n) >> od; + + return rate_mhz * 1000000; +} + +static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + const struct pll_rate_table *rate_table = pll->conf->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate <= rate_table[i].rate) + return rate_table[i].rate; + } + + /* else return the smallest value */ + return rate_table[0].rate; +} + +static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, + unsigned long rate) +{ + const struct pll_rate_table *rate_table = pll->conf->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate == rate_table[i].rate) + return &rate_table[i]; + } + return NULL; +} + +static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, + struct parm *p_n) +{ + int delay = 24000000; + u32 reg; + + while (delay > 0) { + reg = readl(pll->base + p_n->reg_off); + + if (reg & MESON_PLL_LOCK) + return 0; + delay--; + } + return -ETIMEDOUT; +} + +static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + struct parm *p; + const struct pll_rate_table *rate_set; + unsigned long old_rate; + int ret = 0; + u32 reg; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + old_rate = rate; + + rate_set = meson_clk_get_pll_settings(pll, rate); + if (!rate_set) + return -EINVAL; + + /* PLL reset */ + p = &pll->conf->n; + reg = readl(pll->base + p->reg_off); + writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); + + reg = PARM_SET(p->width, p->shift, reg, rate_set->n); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->m; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->m); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->od; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->od); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->n; + ret = meson_clk_pll_wait_lock(pll, p); + if (ret) { + pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", + __func__, old_rate); + meson_clk_pll_set_rate(hw, old_rate, parent_rate); + } + + return ret; +} + +static const struct clk_ops meson_clk_pll_ops = { + .recalc_rate = meson_clk_pll_recalc_rate, + .round_rate = meson_clk_pll_round_rate, + .set_rate = meson_clk_pll_set_rate, +}; + +static const struct clk_ops meson_clk_pll_ro_ops = { + .recalc_rate = meson_clk_pll_recalc_rate, +}; + +struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf, + void __iomem *reg_base, + spinlock_t *lock) +{ + struct clk *clk; + struct meson_clk_pll *clk_pll; + struct clk_init_data init; + + clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL); + if (!clk_pll) + return ERR_PTR(-ENOMEM); + + clk_pll->base = reg_base + clk_conf->reg_off; + clk_pll->lock = lock; + clk_pll->conf = clk_conf->conf.pll; + + init.name = clk_conf->clk_name; + init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE; + + init.parent_names = &clk_conf->clks_parent[0]; + init.num_parents = 1; + init.ops = &meson_clk_pll_ro_ops; + + /* If no rate_table is specified we assume the PLL is read-only */ + if (clk_pll->conf->rate_table) { + int len; + + for (len = 0; clk_pll->conf->rate_table[len].rate != 0; ) + len++; + + clk_pll->rate_count = len; + init.ops = &meson_clk_pll_ops; + } + + clk_pll->hw.init = &init; + + clk = clk_register(NULL, &clk_pll->hw); + if (IS_ERR(clk)) + kfree(clk_pll); + + return clk; +} diff --git a/drivers/clk/meson/clkc.c b/drivers/clk/meson/clkc.c new file mode 100644 index 000000000000..b8c511c5e7a7 --- /dev/null +++ b/drivers/clk/meson/clkc.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/mfd/syscon.h> +#include <linux/slab.h> + +#include "clkc.h" + +static DEFINE_SPINLOCK(clk_lock); + +static struct clk **clks; +static struct clk_onecell_data clk_data; + +struct clk ** __init meson_clk_init(struct device_node *np, + unsigned long nr_clks) +{ + clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL); + if (!clks) + return ERR_PTR(-ENOMEM); + + clk_data.clks = clks; + clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + return clks; +} + +static void meson_clk_add_lookup(struct clk *clk, unsigned int id) +{ + if (clks && id) + clks[id] = clk; +} + +static struct clk * __init +meson_clk_register_composite(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + const struct clk_ops *mux_ops = NULL; + const struct composite_conf *composite_conf; + + composite_conf = clk_conf->conf.composite; + + if (clk_conf->num_parents > 1) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = clk_base + clk_conf->reg_off + + composite_conf->mux_parm.reg_off; + mux->shift = composite_conf->mux_parm.shift; + mux->mask = BIT(composite_conf->mux_parm.width) - 1; + mux->flags = composite_conf->mux_flags; + mux->lock = &clk_lock; + mux->table = composite_conf->mux_table; + mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ? + &clk_mux_ro_ops : &clk_mux_ops; + } + + if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + clk = ERR_PTR(-ENOMEM); + goto error; + } + + div->reg = clk_base + clk_conf->reg_off + + composite_conf->div_parm.reg_off; + div->shift = composite_conf->div_parm.shift; + div->width = composite_conf->div_parm.width; + div->lock = &clk_lock; + div->flags = composite_conf->div_flags; + div->table = composite_conf->div_table; + } + + if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + clk = ERR_PTR(-ENOMEM); + goto error; + } + + gate->reg = clk_base + clk_conf->reg_off + + composite_conf->div_parm.reg_off; + gate->bit_idx = composite_conf->gate_parm.shift; + gate->flags = composite_conf->gate_flags; + gate->lock = &clk_lock; + } + + clk = clk_register_composite(NULL, clk_conf->clk_name, + clk_conf->clks_parent, + clk_conf->num_parents, + mux ? &mux->hw : NULL, mux_ops, + div ? &div->hw : NULL, &clk_divider_ops, + gate ? &gate->hw : NULL, &clk_gate_ops, + clk_conf->flags); + if (IS_ERR(clk)) + goto error; + + return clk; + +error: + kfree(gate); + kfree(div); + kfree(mux); + + return clk; +} + +static struct clk * __init +meson_clk_register_fixed_factor(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + const struct fixed_fact_conf *fixed_fact_conf; + const struct parm *p; + unsigned int mult, div; + u32 reg; + + fixed_fact_conf = &clk_conf->conf.fixed_fact; + + mult = clk_conf->conf.fixed_fact.mult; + div = clk_conf->conf.fixed_fact.div; + + if (!mult) { + mult = 1; + p = &fixed_fact_conf->mult_parm; + if (MESON_PARM_APPLICABLE(p)) { + reg = readl(clk_base + clk_conf->reg_off + p->reg_off); + mult = PARM_GET(p->width, p->shift, reg); + } + } + + if (!div) { + div = 1; + p = &fixed_fact_conf->div_parm; + if (MESON_PARM_APPLICABLE(p)) { + reg = readl(clk_base + clk_conf->reg_off + p->reg_off); + mult = PARM_GET(p->width, p->shift, reg); + } + } + + clk = clk_register_fixed_factor(NULL, + clk_conf->clk_name, + clk_conf->clks_parent[0], + clk_conf->flags, + mult, div); + + return clk; +} + +static struct clk * __init +meson_clk_register_fixed_rate(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + const struct fixed_rate_conf *fixed_rate_conf; + const struct parm *r; + unsigned long rate; + u32 reg; + + fixed_rate_conf = &clk_conf->conf.fixed_rate; + rate = fixed_rate_conf->rate; + + if (!rate) { + r = &fixed_rate_conf->rate_parm; + reg = readl(clk_base + clk_conf->reg_off + r->reg_off); + rate = PARM_GET(r->width, r->shift, reg); + } + + rate *= 1000000; + + clk = clk_register_fixed_rate(NULL, + clk_conf->clk_name, + clk_conf->num_parents + ? clk_conf->clks_parent[0] : NULL, + clk_conf->flags, rate); + + return clk; +} + +void __init meson_clk_register_clks(const struct clk_conf *clk_confs, + size_t nr_confs, + void __iomem *clk_base) +{ + unsigned int i; + struct clk *clk = NULL; + + for (i = 0; i < nr_confs; i++) { + const struct clk_conf *clk_conf = &clk_confs[i]; + + switch (clk_conf->clk_type) { + case CLK_FIXED_RATE: + clk = meson_clk_register_fixed_rate(clk_conf, + clk_base); + break; + case CLK_FIXED_FACTOR: + clk = meson_clk_register_fixed_factor(clk_conf, + clk_base); + break; + case CLK_COMPOSITE: + clk = meson_clk_register_composite(clk_conf, + clk_base); + break; + case CLK_CPU: + clk = meson_clk_register_cpu(clk_conf, clk_base, + &clk_lock); + break; + case CLK_PLL: + clk = meson_clk_register_pll(clk_conf, clk_base, + &clk_lock); + break; + default: + clk = NULL; + } + + if (!clk) { + pr_err("%s: unknown clock type %d\n", __func__, + clk_conf->clk_type); + continue; + } + + if (IS_ERR(clk)) { + pr_warn("%s: Unable to create %s clock\n", __func__, + clk_conf->clk_name); + continue; + } + + meson_clk_add_lookup(clk, clk_conf->clk_id); + } +} diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h new file mode 100644 index 000000000000..609ae92cc13f --- /dev/null +++ b/drivers/clk/meson/clkc.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __CLKC_H +#define __CLKC_H + +#define PMASK(width) GENMASK(width - 1, 0) +#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) +#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) + +#define PARM_GET(width, shift, reg) \ + (((reg) & SETPMASK(width, shift)) >> (shift)) +#define PARM_SET(width, shift, reg, val) \ + (((reg) & CLRPMASK(width, shift)) | (val << (shift))) + +#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) + +struct parm { + u16 reg_off; + u8 shift; + u8 width; +}; +#define PARM(_r, _s, _w) \ + { \ + .reg_off = (_r), \ + .shift = (_s), \ + .width = (_w), \ + } \ + +struct pll_rate_table { + unsigned long rate; + u16 m; + u16 n; + u16 od; +}; +#define PLL_RATE(_r, _m, _n, _od) \ + { \ + .rate = (_r), \ + .m = (_m), \ + .n = (_n), \ + .od = (_od), \ + } \ + +struct pll_conf { + const struct pll_rate_table *rate_table; + struct parm m; + struct parm n; + struct parm od; +}; + +struct fixed_fact_conf { + unsigned int div; + unsigned int mult; + struct parm div_parm; + struct parm mult_parm; +}; + +struct fixed_rate_conf { + unsigned long rate; + struct parm rate_parm; +}; + +struct composite_conf { + struct parm mux_parm; + struct parm div_parm; + struct parm gate_parm; + struct clk_div_table *div_table; + u32 *mux_table; + u8 mux_flags; + u8 div_flags; + u8 gate_flags; +}; + +#define PNAME(x) static const char *x[] + +enum clk_type { + CLK_FIXED_FACTOR, + CLK_FIXED_RATE, + CLK_COMPOSITE, + CLK_CPU, + CLK_PLL, +}; + +struct clk_conf { + u16 reg_off; + enum clk_type clk_type; + unsigned int clk_id; + const char *clk_name; + const char **clks_parent; + int num_parents; + unsigned long flags; + union { + struct fixed_fact_conf fixed_fact; + struct fixed_rate_conf fixed_rate; + const struct composite_conf *composite; + struct pll_conf *pll; + const struct clk_div_table *div_table; + } conf; +}; + +#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_FIXED_RATE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .flags = (_f), \ + .conf.fixed_rate.rate_parm = _c, \ + } \ + +#define FIXED_RATE(_ci, _cn, _f, _r) \ + { \ + .clk_type = CLK_FIXED_RATE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .flags = (_f), \ + .conf.fixed_rate.rate = (_r), \ + } \ + +#define PLL(_ro, _ci, _cn, _cp, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_PLL, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .flags = (_f), \ + .conf.pll = (_c), \ + } \ + +#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d) \ + { \ + .clk_type = CLK_FIXED_FACTOR, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .conf.fixed_fact.div = (_d), \ + } \ + +#define CPU(_ro, _ci, _cn, _cp, _dt) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_CPU, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .conf.div_table = (_dt), \ + } \ + +#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_COMPOSITE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .flags = (_f), \ + .conf.composite = (_c), \ + } \ + +struct clk **meson_clk_init(struct device_node *np, unsigned long nr_clks); +void meson_clk_register_clks(const struct clk_conf *clk_confs, + unsigned int nr_confs, void __iomem *clk_base); +struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, + void __iomem *reg_base, spinlock_t *lock); +struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf, + void __iomem *reg_base, spinlock_t *lock); + +#endif /* __CLKC_H */ diff --git a/drivers/clk/meson/meson8b-clkc.c b/drivers/clk/meson/meson8b-clkc.c new file mode 100644 index 000000000000..61f6d55c4ac7 --- /dev/null +++ b/drivers/clk/meson/meson8b-clkc.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione <carlo@endlessm.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <dt-bindings/clock/meson8b-clkc.h> + +#include "clkc.h" + +#define MESON8B_REG_CTL0_ADDR 0x0000 +#define MESON8B_REG_SYS_CPU_CNTL1 0x015c +#define MESON8B_REG_HHI_MPEG 0x0174 +#define MESON8B_REG_MALI 0x01b0 +#define MESON8B_REG_PLL_FIXED 0x0280 +#define MESON8B_REG_PLL_SYS 0x0300 +#define MESON8B_REG_PLL_VID 0x0320 + +static const struct pll_rate_table sys_pll_rate_table[] = { + PLL_RATE(312000000, 52, 1, 2), + PLL_RATE(336000000, 56, 1, 2), + PLL_RATE(360000000, 60, 1, 2), + PLL_RATE(384000000, 64, 1, 2), + PLL_RATE(408000000, 68, 1, 2), + PLL_RATE(432000000, 72, 1, 2), + PLL_RATE(456000000, 76, 1, 2), + PLL_RATE(480000000, 80, 1, 2), + PLL_RATE(504000000, 84, 1, 2), + PLL_RATE(528000000, 88, 1, 2), + PLL_RATE(552000000, 92, 1, 2), + PLL_RATE(576000000, 96, 1, 2), + PLL_RATE(600000000, 50, 1, 1), + PLL_RATE(624000000, 52, 1, 1), + PLL_RATE(648000000, 54, 1, 1), + PLL_RATE(672000000, 56, 1, 1), + PLL_RATE(696000000, 58, 1, 1), + PLL_RATE(720000000, 60, 1, 1), + PLL_RATE(744000000, 62, 1, 1), + PLL_RATE(768000000, 64, 1, 1), + PLL_RATE(792000000, 66, 1, 1), + PLL_RATE(816000000, 68, 1, 1), + PLL_RATE(840000000, 70, 1, 1), + PLL_RATE(864000000, 72, 1, 1), + PLL_RATE(888000000, 74, 1, 1), + PLL_RATE(912000000, 76, 1, 1), + PLL_RATE(936000000, 78, 1, 1), + PLL_RATE(960000000, 80, 1, 1), + PLL_RATE(984000000, 82, 1, 1), + PLL_RATE(1008000000, 84, 1, 1), + PLL_RATE(1032000000, 86, 1, 1), + PLL_RATE(1056000000, 88, 1, 1), + PLL_RATE(1080000000, 90, 1, 1), + PLL_RATE(1104000000, 92, 1, 1), + PLL_RATE(1128000000, 94, 1, 1), + PLL_RATE(1152000000, 96, 1, 1), + PLL_RATE(1176000000, 98, 1, 1), + PLL_RATE(1200000000, 50, 1, 0), + PLL_RATE(1224000000, 51, 1, 0), + PLL_RATE(1248000000, 52, 1, 0), + PLL_RATE(1272000000, 53, 1, 0), + PLL_RATE(1296000000, 54, 1, 0), + PLL_RATE(1320000000, 55, 1, 0), + PLL_RATE(1344000000, 56, 1, 0), + PLL_RATE(1368000000, 57, 1, 0), + PLL_RATE(1392000000, 58, 1, 0), + PLL_RATE(1416000000, 59, 1, 0), + PLL_RATE(1440000000, 60, 1, 0), + PLL_RATE(1464000000, 61, 1, 0), + PLL_RATE(1488000000, 62, 1, 0), + PLL_RATE(1512000000, 63, 1, 0), + PLL_RATE(1536000000, 64, 1, 0), + { /* sentinel */ }, +}; + +static const struct clk_div_table cpu_div_table[] = { + { .val = 1, .div = 1 }, + { .val = 2, .div = 2 }, + { .val = 3, .div = 3 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { .val = 8, .div = 16 }, + { /* sentinel */ }, +}; + +PNAME(p_xtal) = { "xtal" }; +PNAME(p_fclk_div) = { "fixed_pll" }; +PNAME(p_cpu_clk) = { "sys_pll" }; +PNAME(p_clk81) = { "fclk_div3", "fclk_div4", "fclk_div5" }; +PNAME(p_mali) = { "fclk_div3", "fclk_div4", "fclk_div5", + "fclk_div7", "zero" }; + +static u32 mux_table_clk81[] = { 6, 5, 7 }; +static u32 mux_table_mali[] = { 6, 5, 7, 4, 0 }; + +static struct pll_conf pll_confs = { + .m = PARM(0x00, 0, 9), + .n = PARM(0x00, 9, 5), + .od = PARM(0x00, 16, 2), +}; + +static struct pll_conf sys_pll_conf = { + .m = PARM(0x00, 0, 9), + .n = PARM(0x00, 9, 5), + .od = PARM(0x00, 16, 2), + .rate_table = sys_pll_rate_table, +}; + +static const struct composite_conf clk81_conf __initconst = { + .mux_table = mux_table_clk81, + .mux_flags = CLK_MUX_READ_ONLY, + .mux_parm = PARM(0x00, 12, 3), + .div_parm = PARM(0x00, 0, 7), + .gate_parm = PARM(0x00, 7, 1), +}; + +static const struct composite_conf mali_conf __initconst = { + .mux_table = mux_table_mali, + .mux_parm = PARM(0x00, 9, 3), + .div_parm = PARM(0x00, 0, 7), + .gate_parm = PARM(0x00, 8, 1), +}; + +static const struct clk_conf meson8b_xtal_conf __initconst = + FIXED_RATE_P(MESON8B_REG_CTL0_ADDR, CLKID_XTAL, "xtal", + CLK_IS_ROOT, PARM(0x00, 4, 7)); + +static const struct clk_conf meson8b_clk_confs[] __initconst = { + FIXED_RATE(CLKID_ZERO, "zero", CLK_IS_ROOT, 0), + PLL(MESON8B_REG_PLL_FIXED, CLKID_PLL_FIXED, "fixed_pll", + p_xtal, 0, &pll_confs), + PLL(MESON8B_REG_PLL_VID, CLKID_PLL_VID, "vid_pll", + p_xtal, 0, &pll_confs), + PLL(MESON8B_REG_PLL_SYS, CLKID_PLL_SYS, "sys_pll", + p_xtal, 0, &sys_pll_conf), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV2, "fclk_div2", p_fclk_div, 0, 2), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV3, "fclk_div3", p_fclk_div, 0, 3), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV4, "fclk_div4", p_fclk_div, 0, 4), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV5, "fclk_div5", p_fclk_div, 0, 5), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV7, "fclk_div7", p_fclk_div, 0, 7), + CPU(MESON8B_REG_SYS_CPU_CNTL1, CLKID_CPUCLK, "a5_clk", p_cpu_clk, + cpu_div_table), + COMPOSITE(MESON8B_REG_HHI_MPEG, CLKID_CLK81, "clk81", p_clk81, + CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED, &clk81_conf), + COMPOSITE(MESON8B_REG_MALI, CLKID_MALI, "mali", p_mali, + CLK_IGNORE_UNUSED, &mali_conf), +}; + +static void __init meson8b_clkc_init(struct device_node *np) +{ + void __iomem *clk_base; + + if (!meson_clk_init(np, CLK_NR_CLKS)) + return; + + /* XTAL */ + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("%s: Unable to map xtal base\n", __func__); + return; + } + + meson_clk_register_clks(&meson8b_xtal_conf, 1, clk_base); + iounmap(clk_base); + + /* Generic clocks and PLLs */ + clk_base = of_iomap(np, 1); + if (!clk_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return; + } + + meson_clk_register_clks(meson8b_clk_confs, + ARRAY_SIZE(meson8b_clk_confs), + clk_base); +} +CLK_OF_DECLARE(meson8b_clock, "amlogic,meson8b-clkc", meson8b_clkc_init); diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile index 3caaf7cc169c..9d4bc41e4239 100644 --- a/drivers/clk/mmp/Makefile +++ b/drivers/clk/mmp/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_MACH_MMP2_DT) += clk-of-mmp2.o obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o + +obj-y += clk-of-pxa1928.o diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c index d14120eaa71f..09d41c717c52 100644 --- a/drivers/clk/mmp/clk-apbc.c +++ b/drivers/clk/mmp/clk-apbc.c @@ -115,7 +115,7 @@ static void clk_apbc_unprepare(struct clk_hw *hw) spin_unlock_irqrestore(apbc->lock, flags); } -struct clk_ops clk_apbc_ops = { +static struct clk_ops clk_apbc_ops = { .prepare = clk_apbc_prepare, .unprepare = clk_apbc_unprepare, }; diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c index abe182b2377f..cdcf2d7f321e 100644 --- a/drivers/clk/mmp/clk-apmu.c +++ b/drivers/clk/mmp/clk-apmu.c @@ -61,7 +61,7 @@ static void clk_apmu_disable(struct clk_hw *hw) spin_unlock_irqrestore(apmu->lock, flags); } -struct clk_ops clk_apmu_ops = { +static struct clk_ops clk_apmu_ops = { .enable = clk_apmu_enable, .disable = clk_apmu_disable, }; diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 5c90a4230fa3..09d2832fbd78 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -63,10 +63,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = { }; static struct mmp_clk_factor_tbl uart_factor_tbl[] = { - {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 8125, .den = 1536}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ - {.num = 9679, .den = 5728}, /*58.9824MHZ */ - {.num = 15850, .den = 9451}, /*59.429MHZ */ }; static const char *uart_parent[] = {"uart_pll", "vctcxo"}; diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 2cbc2b43ae52..251533d87c65 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -30,6 +30,7 @@ #define APBC_TWSI4 0x7c #define APBC_TWSI5 0x80 #define APBC_KPC 0x18 +#define APBC_TIMER 0x24 #define APBC_UART0 0x2c #define APBC_UART1 0x30 #define APBC_UART2 0x34 @@ -98,10 +99,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = { }; static struct mmp_clk_factor_tbl uart_factor_tbl[] = { - {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 8125, .den = 1536}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ - {.num = 9679, .den = 5728}, /*58.9824MHZ */ - {.num = 15850, .den = 9451}, /*59.429MHZ */ }; static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit) @@ -134,6 +133,9 @@ static DEFINE_SPINLOCK(ssp2_lock); static DEFINE_SPINLOCK(ssp3_lock); static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"}; +static DEFINE_SPINLOCK(timer_lock); +static const char *timer_parent_names[] = {"clk32", "vctcxo_2", "vctcxo_4", "vctcxo"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -145,6 +147,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, + {0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock}, }; static struct mmp_param_gate_clk apbc_gate_clks[] = { @@ -170,6 +173,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {MMP2_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x7, 0x3, 0x0, 0, &ssp1_lock}, {MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock}, {MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock}, + {MMP2_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x7, 0x3, 0x0, 0, &timer_lock}, }; static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit) diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 5b1810dc4bd2..64eaf4141c69 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -32,6 +32,7 @@ #define APBC_PWM1 0x10 #define APBC_PWM2 0x14 #define APBC_PWM3 0x18 +#define APBC_TIMER 0x34 #define APBC_SSP0 0x81c #define APBC_SSP1 0x820 #define APBC_SSP2 0x84c @@ -58,6 +59,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { {PXA168_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, {PXA168_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, {PXA168_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, + {PXA168_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { @@ -70,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { {PXA168_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, {PXA168_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, {PXA168_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA168_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, {PXA168_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, {PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, {PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, @@ -119,6 +122,9 @@ static DEFINE_SPINLOCK(ssp3_lock); static DEFINE_SPINLOCK(ssp4_lock); static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static DEFINE_SPINLOCK(timer_lock); +static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -130,6 +136,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, {0, "ssp4_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP4, 4, 3, 0, &ssp4_lock}, + {0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock}, }; static struct mmp_param_gate_clk apbc_gate_clks[] = { @@ -151,6 +158,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {PXA168_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x3, 0x3, 0x0, 0, &ssp2_lock}, {PXA168_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x3, 0x3, 0x0, 0, &ssp3_lock}, {PXA168_CLK_SSP4, "ssp4_clk", "ssp4_mux", CLK_SET_RATE_PARENT, APBC_SSP4, 0x3, 0x3, 0x0, 0, &ssp4_lock}, + {PXA168_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x3, 0x3, 0x0, 0, &timer_lock}, }; static void pxa168_apb_periph_clk_init(struct pxa168_clk_unit *pxa_unit) diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c new file mode 100644 index 000000000000..433a5ae1eae0 --- /dev/null +++ b/drivers/clk/mmp/clk-of-pxa1928.c @@ -0,0 +1,265 @@ +/* + * pxa1928 clock framework source file + * + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * Based on drivers/clk/mmp/clk-of-mmp2.c: + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <dt-bindings/clock/marvell,pxa1928.h> + +#include "clk.h" +#include "reset.h" + +#define MPMU_UART_PLL 0x14 + +struct pxa1928_clk_unit { + struct mmp_clk_unit unit; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + void __iomem *apbcp_base; +}; + +static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { + {0, "clk32", NULL, CLK_IS_ROOT, 32768}, + {0, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, + {0, "pll1_624", NULL, CLK_IS_ROOT, 624000000}, + {0, "pll5p", NULL, CLK_IS_ROOT, 832000000}, + {0, "pll5", NULL, CLK_IS_ROOT, 1248000000}, + {0, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, +}; + +static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { + {0, "pll1_d2", "pll1_624", 1, 2, 0}, + {0, "pll1_d9", "pll1_624", 1, 9, 0}, + {0, "pll1_d12", "pll1_624", 1, 12, 0}, + {0, "pll1_d16", "pll1_624", 1, 16, 0}, + {0, "pll1_d20", "pll1_624", 1, 20, 0}, + {0, "pll1_416", "pll1_624", 2, 3, 0}, + {0, "vctcxo_d2", "vctcxo", 1, 2, 0}, + {0, "vctcxo_d4", "vctcxo", 1, 4, 0}, +}; + +static struct mmp_clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { + {.num = 832, .den = 234}, /*58.5MHZ */ + {.num = 1, .den = 1}, /*26MHZ */ +}; + +static void pxa1928_pll_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_fixed_rate_clks(unit, fixed_rate_clks, + ARRAY_SIZE(fixed_rate_clks)); + + mmp_register_fixed_factor_clks(unit, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + clk = mmp_clk_register_factor("uart_pll", "pll1_416", + CLK_SET_RATE_PARENT, + pxa_unit->mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl), NULL); +} + +static DEFINE_SPINLOCK(uart0_lock); +static DEFINE_SPINLOCK(uart1_lock); +static DEFINE_SPINLOCK(uart2_lock); +static DEFINE_SPINLOCK(uart3_lock); +static const char *uart_parent_names[] = {"uart_pll", "vctcxo"}; + +static DEFINE_SPINLOCK(ssp0_lock); +static DEFINE_SPINLOCK(ssp1_lock); +static const char *ssp_parent_names[] = {"vctcxo_d4", "vctcxo_d2", "vctcxo", "pll1_d12"}; + +static DEFINE_SPINLOCK(reset_lock); + +static struct mmp_param_mux_clk apbc_mux_clks[] = { + {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 4, 3, 0, &uart0_lock}, + {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 4, 3, 0, &uart1_lock}, + {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 4, 3, 0, &uart2_lock}, + {0, "uart3_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 4, 3, 0, &uart3_lock}, + {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 4, 3, 0, &ssp0_lock}, + {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 4, 3, 0, &ssp1_lock}, +}; + +static struct mmp_param_gate_clk apbc_gate_clks[] = { + {PXA1928_CLK_TWSI0, "twsi0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI1, "twsi1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI2, "twsi2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI3, "twsi3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI4, "twsi4_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI4 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI5, "twsi5_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI5 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_GPIO * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_KPC * 4, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1928_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_RTC * 4, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1928_CLK_PWM0, "pwm0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM1, "pwm1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM2, "pwm2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM3, "pwm3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {PXA1928_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 0x3, 0x3, 0x0, 0, &uart0_lock}, + {PXA1928_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 0x3, 0x3, 0x0, 0, &uart1_lock}, + {PXA1928_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 0x3, 0x3, 0x0, 0, &uart2_lock}, + {PXA1928_CLK_UART3, "uart3_clk", "uart3_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 0x3, 0x3, 0x0, 0, &uart3_lock}, + {PXA1928_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 0x3, 0x3, 0x0, 0, &ssp0_lock}, + {PXA1928_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 0x3, 0x3, 0x0, 0, &ssp1_lock}, +}; + +static void pxa1928_apb_periph_clk_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_mux_clks)); + + mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_gate_clks)); +} + +static DEFINE_SPINLOCK(sdh0_lock); +static DEFINE_SPINLOCK(sdh1_lock); +static DEFINE_SPINLOCK(sdh2_lock); +static DEFINE_SPINLOCK(sdh3_lock); +static DEFINE_SPINLOCK(sdh4_lock); +static const char *sdh_parent_names[] = {"pll1_624", "pll5p", "pll5", "pll1_416"}; + +static DEFINE_SPINLOCK(usb_lock); + +static struct mmp_param_mux_clk apmu_mux_clks[] = { + {0, "sdh_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 8, 2, 0, &sdh0_lock}, +}; + +static struct mmp_param_div_clk apmu_div_clks[] = { + {0, "sdh_div", "sdh_mux", 0, PXA1928_CLK_SDH0 * 4, 10, 4, CLK_DIVIDER_ONE_BASED, &sdh0_lock}, +}; + +static struct mmp_param_gate_clk apmu_gate_clks[] = { + {PXA1928_CLK_USB, "usb_clk", "usb_pll", 0, PXA1928_CLK_USB * 4, 0x9, 0x9, 0x0, 0, &usb_lock}, + {PXA1928_CLK_HSIC, "hsic_clk", "usb_pll", 0, PXA1928_CLK_HSIC * 4, 0x9, 0x9, 0x0, 0, &usb_lock}, + /* The gate clocks has mux parent. */ + {PXA1928_CLK_SDH0, "sdh0_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 0x1b, 0x1b, 0x0, 0, &sdh0_lock}, + {PXA1928_CLK_SDH1, "sdh1_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH1 * 4, 0x1b, 0x1b, 0x0, 0, &sdh1_lock}, + {PXA1928_CLK_SDH2, "sdh2_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH2 * 4, 0x1b, 0x1b, 0x0, 0, &sdh2_lock}, + {PXA1928_CLK_SDH3, "sdh3_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH3 * 4, 0x1b, 0x1b, 0x0, 0, &sdh3_lock}, + {PXA1928_CLK_SDH4, "sdh4_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH4 * 4, 0x1b, 0x1b, 0x0, 0, &sdh4_lock}, +}; + +static void pxa1928_axi_periph_clk_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_mux_clks)); + + mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_div_clks)); + + mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_gate_clks)); +} + +static void pxa1928_clk_reset_init(struct device_node *np, + struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int i, base, nr_resets; + + nr_resets = ARRAY_SIZE(apbc_gate_clks); + cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); + if (!cells) + return; + + base = 0; + for (i = 0; i < nr_resets; i++) { + cells[base + i].clk_id = apbc_gate_clks[i].id; + cells[base + i].reg = + pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[base + i].flags = 0; + cells[base + i].lock = apbc_gate_clks[i].lock; + cells[base + i].bits = 0x4; + } + + mmp_clk_reset_register(np, cells, nr_resets); +} + +static void __init pxa1928_mpmu_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->mpmu_base = of_iomap(np, 0); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map mpmu registers\n"); + return; + } + + pxa1928_pll_init(pxa_unit); +} +CLK_OF_DECLARE(pxa1928_mpmu_clk, "marvell,pxa1928-mpmu", pxa1928_mpmu_clk_init); + +static void __init pxa1928_apmu_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->apmu_base = of_iomap(np, 0); + if (!pxa_unit->apmu_base) { + pr_err("failed to map apmu registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA1928_APMU_NR_CLKS); + + pxa1928_axi_periph_clk_init(pxa_unit); +} +CLK_OF_DECLARE(pxa1928_apmu_clk, "marvell,pxa1928-apmu", pxa1928_apmu_clk_init); + +static void __init pxa1928_apbc_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->apbc_base = of_iomap(np, 0); + if (!pxa_unit->apbc_base) { + pr_err("failed to map apbc registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA1928_APBC_NR_CLKS); + + pxa1928_apb_periph_clk_init(pxa_unit); + pxa1928_clk_reset_init(np, pxa_unit); +} +CLK_OF_DECLARE(pxa1928_apbc_clk, "marvell,pxa1928-apbc", pxa1928_apbc_clk_init); diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 5e3c80dad336..13d6173326a4 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -35,6 +35,8 @@ #define APBC_SSP0 0x1c #define APBC_SSP1 0x20 #define APBC_SSP2 0x4c +#define APBC_TIMER0 0x30 +#define APBC_TIMER1 0x44 #define APBCP_TWSI1 0x28 #define APBCP_UART2 0x1c #define APMU_SDH0 0x54 @@ -57,6 +59,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { {PXA910_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, {PXA910_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, {PXA910_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, + {PXA910_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { @@ -69,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { {PXA910_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, {PXA910_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, {PXA910_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA910_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, {PXA910_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, {PXA910_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, {PXA910_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, @@ -115,6 +119,10 @@ static DEFINE_SPINLOCK(ssp0_lock); static DEFINE_SPINLOCK(ssp1_lock); static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static DEFINE_SPINLOCK(timer0_lock); +static DEFINE_SPINLOCK(timer1_lock); +static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -122,6 +130,8 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, + {0, "timer0_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER0, 4, 3, 0, &timer0_lock}, + {0, "timer1_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER1, 4, 3, 0, &timer1_lock}, }; static struct mmp_param_mux_clk apbcp_mux_clks[] = { @@ -142,6 +152,8 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {PXA910_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock}, {PXA910_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x3, 0x3, 0x0, 0, &ssp0_lock}, {PXA910_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x3, 0x3, 0x0, 0, &ssp1_lock}, + {PXA910_CLK_TIMER0, "timer0_clk", "timer0_mux", CLK_SET_RATE_PARENT, APBC_TIMER0, 0x3, 0x3, 0x0, 0, &timer0_lock}, + {PXA910_CLK_TIMER1, "timer1_clk", "timer1_mux", CLK_SET_RATE_PARENT, APBC_TIMER1, 0x3, 0x3, 0x0, 0, &timer1_lock}, }; static struct mmp_param_gate_clk apbcp_gate_clks[] = { diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 756f0f39d6a3..2c7c1085f883 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -163,6 +163,7 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { { "pex1", "pex1_en", 9, 0 }, { "sata0", NULL, 15, 0 }, { "sdio", NULL, 17, 0 }, + { "crypto", NULL, 23, CLK_IGNORE_UNUSED }, { "tdm", NULL, 25, 0 }, { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, { "sata1", NULL, 30, 0 }, diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 22d136aa699f..32216f9b7f03 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -77,12 +77,12 @@ static void __init clk_misc_init(void) writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET); } -static const char *sel_pll[] __initdata = { "pll", "ref_xtal", }; -static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", }; -static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", }; -static const char *sel_io[] __initdata = { "ref_io", "ref_xtal", }; -static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", }; -static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", }; +static const char *const sel_pll[] __initconst = { "pll", "ref_xtal", }; +static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *const sel_io[] __initconst = { "ref_io", "ref_xtal", }; +static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; enum imx23_clk { ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel, diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index b1be3746ce95..a68670868baa 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -125,15 +125,15 @@ static void __init clk_misc_init(void) writel_relaxed(val, FRAC0); } -static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", }; -static const char *sel_io0[] __initdata = { "ref_io0", "ref_xtal", }; -static const char *sel_io1[] __initdata = { "ref_io1", "ref_xtal", }; -static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", }; -static const char *sel_gpmi[] __initdata = { "ref_gpmi", "ref_xtal", }; -static const char *sel_pll0[] __initdata = { "pll0", "ref_xtal", }; -static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", }; -static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", }; -static const char *ptp_sels[] __initdata = { "ref_xtal", "pll0", }; +static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *const sel_io0[] __initconst = { "ref_io0", "ref_xtal", }; +static const char *const sel_io1[] __initconst = { "ref_io1", "ref_xtal", }; +static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *const sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", }; +static const char *const sel_pll0[] __initconst = { "pll0", "ref_xtal", }; +static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; +static const char *const ptp_sels[] __initconst = { "ref_xtal", "pll0", }; enum imx28_clk { ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1, diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index ef10ad9b5daa..f07d821dd75d 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -49,7 +49,7 @@ static inline struct clk *mxs_clk_gate(const char *name, } static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parent_names, int num_parents) + u8 shift, u8 width, const char *const *parent_names, int num_parents) { return clk_register_mux(NULL, name, parent_names, num_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, diff --git a/drivers/clk/nxp/Makefile b/drivers/clk/nxp/Makefile new file mode 100644 index 000000000000..7f608b0ad7b4 --- /dev/null +++ b/drivers/clk/nxp/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-ccu.o diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c new file mode 100644 index 000000000000..eeaee97da110 --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -0,0 +1,293 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Control Unit (CCU) + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <dt-bindings/clock/lpc18xx-ccu.h> + +/* Bit defines for CCU branch configuration register */ +#define LPC18XX_CCU_RUN BIT(0) +#define LPC18XX_CCU_AUTO BIT(1) +#define LPC18XX_CCU_DIV BIT(5) +#define LPC18XX_CCU_DIVSTAT BIT(27) + +/* CCU branch feature bits */ +#define CCU_BRANCH_IS_BUS BIT(0) +#define CCU_BRANCH_HAVE_DIV2 BIT(1) + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +struct lpc18xx_branch_clk_data { + const char **name; + int num; +}; + +struct lpc18xx_clk_branch { + const char *base_name; + const char *name; + u16 offset; + u16 flags; + struct clk *clk; + struct clk_gate gate; +}; + +static struct lpc18xx_clk_branch clk_branches[] = { + {"base_apb3_clk", "apb3_bus", CLK_APB3_BUS, CCU_BRANCH_IS_BUS}, + {"base_apb3_clk", "apb3_i2c1", CLK_APB3_I2C1, 0}, + {"base_apb3_clk", "apb3_dac", CLK_APB3_DAC, 0}, + {"base_apb3_clk", "apb3_adc0", CLK_APB3_ADC0, 0}, + {"base_apb3_clk", "apb3_adc1", CLK_APB3_ADC1, 0}, + {"base_apb3_clk", "apb3_can0", CLK_APB3_CAN0, 0}, + + {"base_apb1_clk", "apb1_bus", CLK_APB1_BUS, CCU_BRANCH_IS_BUS}, + {"base_apb1_clk", "apb1_mc_pwm", CLK_APB1_MOTOCON_PWM, 0}, + {"base_apb1_clk", "apb1_i2c0", CLK_APB1_I2C0, 0}, + {"base_apb1_clk", "apb1_i2s", CLK_APB1_I2S, 0}, + {"base_apb1_clk", "apb1_can1", CLK_APB1_CAN1, 0}, + + {"base_spifi_clk", "spifi", CLK_SPIFI, 0}, + + {"base_cpu_clk", "cpu_bus", CLK_CPU_BUS, CCU_BRANCH_IS_BUS}, + {"base_cpu_clk", "cpu_spifi", CLK_CPU_SPIFI, 0}, + {"base_cpu_clk", "cpu_gpio", CLK_CPU_GPIO, 0}, + {"base_cpu_clk", "cpu_lcd", CLK_CPU_LCD, 0}, + {"base_cpu_clk", "cpu_ethernet", CLK_CPU_ETHERNET, 0}, + {"base_cpu_clk", "cpu_usb0", CLK_CPU_USB0, 0}, + {"base_cpu_clk", "cpu_emc", CLK_CPU_EMC, 0}, + {"base_cpu_clk", "cpu_sdio", CLK_CPU_SDIO, 0}, + {"base_cpu_clk", "cpu_dma", CLK_CPU_DMA, 0}, + {"base_cpu_clk", "cpu_core", CLK_CPU_CORE, 0}, + {"base_cpu_clk", "cpu_sct", CLK_CPU_SCT, 0}, + {"base_cpu_clk", "cpu_usb1", CLK_CPU_USB1, 0}, + {"base_cpu_clk", "cpu_emcdiv", CLK_CPU_EMCDIV, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_flasha", CLK_CPU_FLASHA, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_flashb", CLK_CPU_FLASHB, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_m0app", CLK_CPU_M0APP, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_adchs", CLK_CPU_ADCHS, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_eeprom", CLK_CPU_EEPROM, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_wwdt", CLK_CPU_WWDT, 0}, + {"base_cpu_clk", "cpu_uart0", CLK_CPU_UART0, 0}, + {"base_cpu_clk", "cpu_uart1", CLK_CPU_UART1, 0}, + {"base_cpu_clk", "cpu_ssp0", CLK_CPU_SSP0, 0}, + {"base_cpu_clk", "cpu_timer0", CLK_CPU_TIMER0, 0}, + {"base_cpu_clk", "cpu_timer1", CLK_CPU_TIMER1, 0}, + {"base_cpu_clk", "cpu_scu", CLK_CPU_SCU, 0}, + {"base_cpu_clk", "cpu_creg", CLK_CPU_CREG, 0}, + {"base_cpu_clk", "cpu_ritimer", CLK_CPU_RITIMER, 0}, + {"base_cpu_clk", "cpu_uart2", CLK_CPU_UART2, 0}, + {"base_cpu_clk", "cpu_uart3", CLK_CPU_UART3, 0}, + {"base_cpu_clk", "cpu_timer2", CLK_CPU_TIMER2, 0}, + {"base_cpu_clk", "cpu_timer3", CLK_CPU_TIMER3, 0}, + {"base_cpu_clk", "cpu_ssp1", CLK_CPU_SSP1, 0}, + {"base_cpu_clk", "cpu_qei", CLK_CPU_QEI, 0}, + + {"base_periph_clk", "periph_bus", CLK_PERIPH_BUS, CCU_BRANCH_IS_BUS}, + {"base_periph_clk", "periph_core", CLK_PERIPH_CORE, 0}, + {"base_periph_clk", "periph_sgpio", CLK_PERIPH_SGPIO, 0}, + + {"base_usb0_clk", "usb0", CLK_USB0, 0}, + {"base_usb1_clk", "usb1", CLK_USB1, 0}, + {"base_spi_clk", "spi", CLK_SPI, 0}, + {"base_adchs_clk", "adchs", CLK_ADCHS, 0}, + + {"base_audio_clk", "audio", CLK_AUDIO, 0}, + {"base_uart3_clk", "apb2_uart3", CLK_APB2_UART3, 0}, + {"base_uart2_clk", "apb2_uart2", CLK_APB2_UART2, 0}, + {"base_uart1_clk", "apb0_uart1", CLK_APB0_UART1, 0}, + {"base_uart0_clk", "apb0_uart0", CLK_APB0_UART0, 0}, + {"base_ssp1_clk", "apb2_ssp1", CLK_APB2_SSP1, 0}, + {"base_ssp0_clk", "apb0_ssp0", CLK_APB0_SSP0, 0}, + {"base_sdio_clk", "sdio", CLK_SDIO, 0}, +}; + +static struct clk *lpc18xx_ccu_branch_clk_get(struct of_phandle_args *clkspec, + void *data) +{ + struct lpc18xx_branch_clk_data *clk_data = data; + unsigned int offset = clkspec->args[0]; + int i, j; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (clk_branches[i].offset != offset) + continue; + + for (j = 0; j < clk_data->num; j++) { + if (!strcmp(clk_branches[i].base_name, clk_data->name[j])) + return clk_branches[i].clk; + } + } + + pr_err("%s: invalid clock offset %d\n", __func__, offset); + + return ERR_PTR(-EINVAL); +} + +static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + u32 val; + + /* + * Divider field is write only, so divider stat field must + * be read so divider field can be set accordingly. + */ + val = clk_readl(gate->reg); + if (val & LPC18XX_CCU_DIVSTAT) + val |= LPC18XX_CCU_DIV; + + if (enable) { + val |= LPC18XX_CCU_RUN; + } else { + /* + * To safely disable a branch clock a squence of two separate + * writes must be used. First write should set the AUTO bit + * and the next write should clear the RUN bit. + */ + val |= LPC18XX_CCU_AUTO; + clk_writel(val, gate->reg); + + val &= ~LPC18XX_CCU_RUN; + } + + clk_writel(val, gate->reg); + + return 0; +} + +static int lpc18xx_ccu_gate_enable(struct clk_hw *hw) +{ + return lpc18xx_ccu_gate_endisable(hw, true); +} + +static void lpc18xx_ccu_gate_disable(struct clk_hw *hw) +{ + lpc18xx_ccu_gate_endisable(hw, false); +} + +static int lpc18xx_ccu_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + + return clk_readl(gate->reg) & LPC18XX_CCU_RUN; +} + +static const struct clk_ops lpc18xx_ccu_gate_ops = { + .enable = lpc18xx_ccu_gate_enable, + .disable = lpc18xx_ccu_gate_disable, + .is_enabled = lpc18xx_ccu_gate_is_enabled, +}; + +static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *branch, + void __iomem *reg_base, + const char *parent) +{ + const struct clk_ops *div_ops = NULL; + struct clk_divider *div = NULL; + struct clk_hw *div_hw = NULL; + + if (branch->flags & CCU_BRANCH_HAVE_DIV2) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return; + + div->reg = branch->offset + reg_base; + div->flags = CLK_DIVIDER_READ_ONLY; + div->shift = 27; + div->width = 1; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + branch->gate.reg = branch->offset + reg_base; + branch->gate.bit_idx = 0; + + branch->clk = clk_register_composite(NULL, branch->name, &parent, 1, + NULL, NULL, + div_hw, div_ops, + &branch->gate.hw, &lpc18xx_ccu_gate_ops, 0); + if (IS_ERR(branch->clk)) { + kfree(div); + pr_warn("%s: failed to register %s\n", __func__, branch->name); + return; + } + + /* Grab essential branch clocks for CPU and SDRAM */ + switch (branch->offset) { + case CLK_CPU_EMC: + case CLK_CPU_CORE: + case CLK_CPU_CREG: + case CLK_CPU_EMCDIV: + clk_prepare_enable(branch->clk); + } +} + +static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base, + const char *base_name) +{ + const char *parent = base_name; + int i; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (strcmp(clk_branches[i].base_name, base_name)) + continue; + + lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base, + parent); + + if (clk_branches[i].flags & CCU_BRANCH_IS_BUS) + parent = clk_branches[i].name; + } +} + +static void __init lpc18xx_ccu_init(struct device_node *np) +{ + struct lpc18xx_branch_clk_data *clk_data; + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = of_property_count_strings(np, "clock-names"); + clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL); + if (!clk_data->name) { + kfree(clk_data); + return; + } + + for (i = 0; i < clk_data->num; i++) { + ret = of_property_read_string_index(np, "clock-names", i, + &clk_data->name[i]); + if (ret) { + pr_warn("%s: failed to get clock name at idx %d\n", + __func__, i); + continue; + } + + lpc18xx_ccu_register_branch_clks(reg_base, clk_data->name[i]); + } + + of_clk_add_provider(np, lpc18xx_ccu_branch_clk_get, clk_data); +} +CLK_OF_DECLARE(lpc18xx_ccu, "nxp,lpc1850-ccu", lpc18xx_ccu_init); diff --git a/drivers/clk/nxp/clk-lpc18xx-cgu.c b/drivers/clk/nxp/clk-lpc18xx-cgu.c new file mode 100644 index 000000000000..81e9e1c788f4 --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-cgu.c @@ -0,0 +1,635 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Generation Unit (CGU) + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <dt-bindings/clock/lpc18xx-cgu.h> + +/* Clock Generation Unit (CGU) registers */ +#define LPC18XX_CGU_XTAL_OSC_CTRL 0x018 +#define LPC18XX_CGU_PLL0USB_STAT 0x01c +#define LPC18XX_CGU_PLL0USB_CTRL 0x020 +#define LPC18XX_CGU_PLL0USB_MDIV 0x024 +#define LPC18XX_CGU_PLL0USB_NP_DIV 0x028 +#define LPC18XX_CGU_PLL0AUDIO_STAT 0x02c +#define LPC18XX_CGU_PLL0AUDIO_CTRL 0x030 +#define LPC18XX_CGU_PLL0AUDIO_MDIV 0x034 +#define LPC18XX_CGU_PLL0AUDIO_NP_DIV 0x038 +#define LPC18XX_CGU_PLL0AUDIO_FRAC 0x03c +#define LPC18XX_CGU_PLL1_STAT 0x040 +#define LPC18XX_CGU_PLL1_CTRL 0x044 +#define LPC18XX_PLL1_CTRL_FBSEL BIT(6) +#define LPC18XX_PLL1_CTRL_DIRECT BIT(7) +#define LPC18XX_CGU_IDIV_CTRL(n) (0x048 + (n) * sizeof(u32)) +#define LPC18XX_CGU_BASE_CLK(id) (0x05c + (id) * sizeof(u32)) +#define LPC18XX_CGU_PLL_CTRL_OFFSET 0x4 + +/* PLL0 bits common to both audio and USB PLL */ +#define LPC18XX_PLL0_STAT_LOCK BIT(0) +#define LPC18XX_PLL0_CTRL_PD BIT(0) +#define LPC18XX_PLL0_CTRL_BYPASS BIT(1) +#define LPC18XX_PLL0_CTRL_DIRECTI BIT(2) +#define LPC18XX_PLL0_CTRL_DIRECTO BIT(3) +#define LPC18XX_PLL0_CTRL_CLKEN BIT(4) +#define LPC18XX_PLL0_MDIV_MDEC_MASK 0x1ffff +#define LPC18XX_PLL0_MDIV_SELP_SHIFT 17 +#define LPC18XX_PLL0_MDIV_SELI_SHIFT 22 +#define LPC18XX_PLL0_MSEL_MAX BIT(15) + +/* Register value that gives PLL0 post/pre dividers equal to 1 */ +#define LPC18XX_PLL0_NP_DIVS_1 0x00302062 + +enum { + CLK_SRC_OSC32, + CLK_SRC_IRC, + CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, + CLK_SRC_GP_CLKIN, + CLK_SRC_RESERVED1, + CLK_SRC_OSC, + CLK_SRC_PLL0USB, + CLK_SRC_PLL0AUDIO, + CLK_SRC_PLL1, + CLK_SRC_RESERVED2, + CLK_SRC_RESERVED3, + CLK_SRC_IDIVA, + CLK_SRC_IDIVB, + CLK_SRC_IDIVC, + CLK_SRC_IDIVD, + CLK_SRC_IDIVE, + CLK_SRC_MAX +}; + +static const char *clk_src_names[CLK_SRC_MAX] = { + [CLK_SRC_OSC32] = "osc32", + [CLK_SRC_IRC] = "irc", + [CLK_SRC_ENET_RX_CLK] = "enet_rx_clk", + [CLK_SRC_ENET_TX_CLK] = "enet_tx_clk", + [CLK_SRC_GP_CLKIN] = "gp_clkin", + [CLK_SRC_OSC] = "osc", + [CLK_SRC_PLL0USB] = "pll0usb", + [CLK_SRC_PLL0AUDIO] = "pll0audio", + [CLK_SRC_PLL1] = "pll1", + [CLK_SRC_IDIVA] = "idiva", + [CLK_SRC_IDIVB] = "idivb", + [CLK_SRC_IDIVC] = "idivc", + [CLK_SRC_IDIVD] = "idivd", + [CLK_SRC_IDIVE] = "idive", +}; + +static const char *clk_base_names[BASE_CLK_MAX] = { + [BASE_SAFE_CLK] = "base_safe_clk", + [BASE_USB0_CLK] = "base_usb0_clk", + [BASE_PERIPH_CLK] = "base_periph_clk", + [BASE_USB1_CLK] = "base_usb1_clk", + [BASE_CPU_CLK] = "base_cpu_clk", + [BASE_SPIFI_CLK] = "base_spifi_clk", + [BASE_SPI_CLK] = "base_spi_clk", + [BASE_PHY_RX_CLK] = "base_phy_rx_clk", + [BASE_PHY_TX_CLK] = "base_phy_tx_clk", + [BASE_APB1_CLK] = "base_apb1_clk", + [BASE_APB3_CLK] = "base_apb3_clk", + [BASE_LCD_CLK] = "base_lcd_clk", + [BASE_ADCHS_CLK] = "base_adchs_clk", + [BASE_SDIO_CLK] = "base_sdio_clk", + [BASE_SSP0_CLK] = "base_ssp0_clk", + [BASE_SSP1_CLK] = "base_ssp1_clk", + [BASE_UART0_CLK] = "base_uart0_clk", + [BASE_UART1_CLK] = "base_uart1_clk", + [BASE_UART2_CLK] = "base_uart2_clk", + [BASE_UART3_CLK] = "base_uart3_clk", + [BASE_OUT_CLK] = "base_out_clk", + [BASE_AUDIO_CLK] = "base_audio_clk", + [BASE_CGU_OUT0_CLK] = "base_cgu_out0_clk", + [BASE_CGU_OUT1_CLK] = "base_cgu_out1_clk", +}; + +static u32 lpc18xx_cgu_pll0_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL1, CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC, + CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_pll1_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_IDIVA, + CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_idiva_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1 +}; + +static u32 lpc18xx_cgu_idivbcde_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA, +}; + +static u32 lpc18xx_cgu_base_irc_src_ids[] = {CLK_SRC_IRC}; + +static u32 lpc18xx_cgu_base_usb0_src_ids[] = {CLK_SRC_PLL0USB}; + +static u32 lpc18xx_cgu_base_common_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA, + CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_base_all_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, + CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC, + CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +struct lpc18xx_cgu_src_clk_div { + u8 clk_id; + u8 n_parents; + struct clk_divider div; + struct clk_mux mux; + struct clk_gate gate; +}; + +#define LPC1XX_CGU_SRC_CLK_DIV(_id, _width, _table) \ +{ \ + .clk_id = CLK_SRC_ ##_id, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .div = { \ + .shift = 2, \ + .width = _width, \ + }, \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ +} + +static struct lpc18xx_cgu_src_clk_div lpc18xx_cgu_src_clk_divs[] = { + LPC1XX_CGU_SRC_CLK_DIV(IDIVA, 2, idiva_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVB, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVC, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVD, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVE, 8, idivbcde_src_ids), +}; + +struct lpc18xx_cgu_base_clk { + u8 clk_id; + u8 n_parents; + struct clk_mux mux; + struct clk_gate gate; +}; + +#define LPC1XX_CGU_BASE_CLK(_id, _table, _flags) \ +{ \ + .clk_id = BASE_ ##_id ##_CLK, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + .flags = _flags, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ +} + +static struct lpc18xx_cgu_base_clk lpc18xx_cgu_base_clks[] = { + LPC1XX_CGU_BASE_CLK(SAFE, base_irc_src_ids, CLK_MUX_READ_ONLY), + LPC1XX_CGU_BASE_CLK(USB0, base_usb0_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PERIPH, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(USB1, base_all_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CPU, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SPIFI, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SPI, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PHY_RX, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PHY_TX, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(APB1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(APB3, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(LCD, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(ADCHS, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SDIO, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SSP0, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SSP1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART0, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART2, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART3, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(OUT, base_all_src_ids, 0), + { /* 21 reserved */ }, + { /* 22 reserved */ }, + { /* 23 reserved */ }, + { /* 24 reserved */ }, + LPC1XX_CGU_BASE_CLK(AUDIO, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CGU_OUT0, base_all_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CGU_OUT1, base_all_src_ids, 0), +}; + +struct lpc18xx_pll { + struct clk_hw hw; + void __iomem *reg; + spinlock_t *lock; + u8 flags; +}; + +#define to_lpc_pll(hw) container_of(hw, struct lpc18xx_pll, hw) + +struct lpc18xx_cgu_pll_clk { + u8 clk_id; + u8 n_parents; + u8 reg_offset; + struct clk_mux mux; + struct clk_gate gate; + struct lpc18xx_pll pll; + const struct clk_ops *pll_ops; +}; + +#define LPC1XX_CGU_CLK_PLL(_id, _table, _pll_ops) \ +{ \ + .clk_id = CLK_SRC_ ##_id, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .reg_offset = LPC18XX_CGU_ ##_id ##_STAT, \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ + .pll_ops = &lpc18xx_ ##_pll_ops, \ +} + +/* + * PLL0 uses a special register value encoding. The compute functions below + * are taken or derived from the LPC1850 user manual (section 12.6.3.3). + */ + +/* Compute PLL0 multiplier from decoded version */ +static u32 lpc18xx_pll0_mdec2msel(u32 x) +{ + int i; + + switch (x) { + case 0x18003: return 1; + case 0x10003: return 2; + default: + for (i = LPC18XX_PLL0_MSEL_MAX + 1; x != 0x4000 && i > 0; i--) + x = ((x ^ x >> 14) & 1) | (x << 1 & 0x7fff); + return i; + } +} +/* Compute PLL0 decoded multiplier from binary version */ +static u32 lpc18xx_pll0_msel2mdec(u32 msel) +{ + u32 i, x = 0x4000; + + switch (msel) { + case 0: return 0; + case 1: return 0x18003; + case 2: return 0x10003; + default: + for (i = msel; i <= LPC18XX_PLL0_MSEL_MAX; i++) + x = ((x ^ x >> 1) & 1) << 14 | (x >> 1 & 0xffff); + return x; + } +} + +/* Compute PLL0 bandwidth SELI reg from multiplier */ +static u32 lpc18xx_pll0_msel2seli(u32 msel) +{ + u32 tmp; + + if (msel > 16384) return 1; + if (msel > 8192) return 2; + if (msel > 2048) return 4; + if (msel >= 501) return 8; + if (msel >= 60) { + tmp = 1024 / (msel + 9); + return ((1024 == (tmp * (msel + 9))) == 0) ? tmp * 4 : (tmp + 1) * 4; + } + + return (msel & 0x3c) + 4; +} + +/* Compute PLL0 bandwidth SELP reg from multiplier */ +static u32 lpc18xx_pll0_msel2selp(u32 msel) +{ + if (msel < 60) + return (msel >> 1) + 1; + + return 31; +} + +static unsigned long lpc18xx_pll0_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u32 ctrl, mdiv, msel, npdiv; + + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + mdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_MDIV); + npdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV); + + if (ctrl & LPC18XX_PLL0_CTRL_BYPASS) + return parent_rate; + + if (npdiv != LPC18XX_PLL0_NP_DIVS_1) { + pr_warn("%s: pre/post dividers not supported\n", __func__); + return 0; + } + + msel = lpc18xx_pll0_mdec2msel(mdiv & LPC18XX_PLL0_MDIV_MDEC_MASK); + if (msel) + return 2 * msel * parent_rate; + + pr_warn("%s: unable to calculate rate\n", __func__); + + return 0; +} + +static long lpc18xx_pll0_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long m; + + if (*prate < rate) { + pr_warn("%s: pll dividers not supported\n", __func__); + return -EINVAL; + } + + m = DIV_ROUND_UP_ULL(*prate, rate * 2); + if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) { + pr_warn("%s: unable to support rate %lu\n", __func__, rate); + return -EINVAL; + } + + return 2 * *prate * m; +} + +static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u32 ctrl, stat, m; + int retry = 3; + + if (parent_rate < rate) { + pr_warn("%s: pll dividers not supported\n", __func__); + return -EINVAL; + } + + m = DIV_ROUND_UP_ULL(parent_rate, rate * 2); + if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) { + pr_warn("%s: unable to support rate %lu\n", __func__, rate); + return -EINVAL; + } + + m = lpc18xx_pll0_msel2mdec(m); + m |= lpc18xx_pll0_msel2selp(m) << LPC18XX_PLL0_MDIV_SELP_SHIFT; + m |= lpc18xx_pll0_msel2seli(m) << LPC18XX_PLL0_MDIV_SELI_SHIFT; + + /* Power down PLL, disable clk output and dividers */ + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + ctrl |= LPC18XX_PLL0_CTRL_PD; + ctrl &= ~(LPC18XX_PLL0_CTRL_BYPASS | LPC18XX_PLL0_CTRL_DIRECTI | + LPC18XX_PLL0_CTRL_DIRECTO | LPC18XX_PLL0_CTRL_CLKEN); + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + + /* Configure new PLL settings */ + clk_writel(m, pll->reg + LPC18XX_CGU_PLL0USB_MDIV); + clk_writel(LPC18XX_PLL0_NP_DIVS_1, pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV); + + /* Power up PLL and wait for lock */ + ctrl &= ~LPC18XX_PLL0_CTRL_PD; + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + do { + udelay(10); + stat = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_STAT); + if (stat & LPC18XX_PLL0_STAT_LOCK) { + ctrl |= LPC18XX_PLL0_CTRL_CLKEN; + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + + return 0; + } + } while (retry--); + + pr_warn("%s: unable to lock pll\n", __func__); + + return -EINVAL; +} + +static const struct clk_ops lpc18xx_pll0_ops = { + .recalc_rate = lpc18xx_pll0_recalc_rate, + .round_rate = lpc18xx_pll0_round_rate, + .set_rate = lpc18xx_pll0_set_rate, +}; + +static unsigned long lpc18xx_pll1_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u16 msel, nsel, psel; + bool direct, fbsel; + u32 stat, ctrl; + + stat = clk_readl(pll->reg + LPC18XX_CGU_PLL1_STAT); + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL1_CTRL); + + direct = (ctrl & LPC18XX_PLL1_CTRL_DIRECT) ? true : false; + fbsel = (ctrl & LPC18XX_PLL1_CTRL_FBSEL) ? true : false; + + msel = ((ctrl >> 16) & 0xff) + 1; + nsel = ((ctrl >> 12) & 0x3) + 1; + + if (direct || fbsel) + return msel * (parent_rate / nsel); + + psel = (ctrl >> 8) & 0x3; + psel = 1 << psel; + + return (msel / (2 * psel)) * (parent_rate / nsel); +} + +static const struct clk_ops lpc18xx_pll1_ops = { + .recalc_rate = lpc18xx_pll1_recalc_rate, +}; + +static struct lpc18xx_cgu_pll_clk lpc18xx_cgu_src_clk_plls[] = { + LPC1XX_CGU_CLK_PLL(PLL0USB, pll0_src_ids, pll0_ops), + LPC1XX_CGU_CLK_PLL(PLL0AUDIO, pll0_src_ids, pll0_ops), + LPC1XX_CGU_CLK_PLL(PLL1, pll1_src_ids, pll1_ops), +}; + +static void lpc18xx_fill_parent_names(const char **parent, u32 *id, int size) +{ + int i; + + for (i = 0; i < size; i++) + parent[i] = clk_src_names[id[i]]; +} + +static struct clk *lpc18xx_cgu_register_div(struct lpc18xx_cgu_src_clk_div *clk, + void __iomem *base, int n) +{ + void __iomem *reg = base + LPC18XX_CGU_IDIV_CTRL(n); + const char *name = clk_src_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + clk->div.reg = reg; + clk->mux.reg = reg; + clk->gate.reg = reg; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + &clk->div.hw, &clk_divider_ops, + &clk->gate.hw, &clk_gate_ops, 0); +} + + +static struct clk *lpc18xx_register_base_clk(struct lpc18xx_cgu_base_clk *clk, + void __iomem *reg_base, int n) +{ + void __iomem *reg = reg_base + LPC18XX_CGU_BASE_CLK(n); + const char *name = clk_base_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + if (clk->n_parents == 0) + return ERR_PTR(-ENOENT); + + clk->mux.reg = reg; + clk->gate.reg = reg; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + /* SAFE_CLK can not be turned off */ + if (n == BASE_SAFE_CLK) + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + NULL, NULL, NULL, NULL, 0); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + NULL, NULL, + &clk->gate.hw, &clk_gate_ops, 0); +} + + +static struct clk *lpc18xx_cgu_register_pll(struct lpc18xx_cgu_pll_clk *clk, + void __iomem *base) +{ + const char *name = clk_src_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + clk->pll.reg = base; + clk->mux.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET; + clk->gate.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + &clk->pll.hw, clk->pll_ops, + &clk->gate.hw, &clk_gate_ops, 0); +} + +static void __init lpc18xx_cgu_register_source_clks(struct device_node *np, + void __iomem *base) +{ + const char *parents[CLK_SRC_MAX]; + struct clk *clk; + int i; + + /* Register the internal 12 MHz RC oscillator (IRC) */ + clk = clk_register_fixed_rate(NULL, clk_src_names[CLK_SRC_IRC], + NULL, CLK_IS_ROOT, 12000000); + if (IS_ERR(clk)) + pr_warn("%s: failed to register irc clk\n", __func__); + + /* Register crystal oscillator controlller */ + parents[0] = of_clk_get_parent_name(np, 0); + clk = clk_register_gate(NULL, clk_src_names[CLK_SRC_OSC], parents[0], + 0, base + LPC18XX_CGU_XTAL_OSC_CTRL, + 0, CLK_GATE_SET_TO_DISABLE, NULL); + if (IS_ERR(clk)) + pr_warn("%s: failed to register osc clk\n", __func__); + + /* Register all PLLs */ + for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_plls); i++) { + clk = lpc18xx_cgu_register_pll(&lpc18xx_cgu_src_clk_plls[i], + base); + if (IS_ERR(clk)) + pr_warn("%s: failed to register pll (%d)\n", __func__, i); + } + + /* Register all clock dividers A-E */ + for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_divs); i++) { + clk = lpc18xx_cgu_register_div(&lpc18xx_cgu_src_clk_divs[i], + base, i); + if (IS_ERR(clk)) + pr_warn("%s: failed to register div %d\n", __func__, i); + } +} + +static struct clk *clk_base[BASE_CLK_MAX]; +static struct clk_onecell_data clk_base_data = { + .clks = clk_base, + .clk_num = BASE_CLK_MAX, +}; + +static void __init lpc18xx_cgu_register_base_clks(void __iomem *reg_base) +{ + int i; + + for (i = BASE_SAFE_CLK; i < BASE_CLK_MAX; i++) { + clk_base[i] = lpc18xx_register_base_clk(&lpc18xx_cgu_base_clks[i], + reg_base, i); + if (IS_ERR(clk_base[i]) && PTR_ERR(clk_base[i]) != -ENOENT) + pr_warn("%s: register base clk %d failed\n", __func__, i); + } +} + +static void __init lpc18xx_cgu_init(struct device_node *np) +{ + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + lpc18xx_cgu_register_source_clks(np, reg_base); + lpc18xx_cgu_register_base_clks(reg_base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_base_data); +} +CLK_OF_DECLARE(lpc18xx_cgu, "nxp,lpc1850-cgu", lpc18xx_cgu_init); diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index de537560bf70..e17dada0dd21 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -6,9 +6,12 @@ * version 2, as published by the Free Software Foundation. */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/printk.h> #include <linux/slab.h> #include "clk.h" @@ -50,6 +53,18 @@ #define PLL_CTRL4 0x10 #define PLL_FRAC_CTRL4_BYPASS BIT(28) +#define MIN_PFD 9600000UL +#define MIN_VCO_LA 400000000UL +#define MAX_VCO_LA 1600000000UL +#define MIN_VCO_FRAC_INT 600000000UL +#define MAX_VCO_FRAC_INT 1600000000UL +#define MIN_VCO_FRAC_FRAC 600000000UL +#define MAX_VCO_FRAC_FRAC 2400000000UL +#define MIN_OUTPUT_LA 8000000UL +#define MAX_OUTPUT_LA 1600000000UL +#define MIN_OUTPUT_FRAC 12000000UL +#define MAX_OUTPUT_FRAC 1600000000UL + struct pistachio_clk_pll { struct clk_hw hw; void __iomem *base; @@ -67,6 +82,12 @@ static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg) writel(val, pll->base + reg); } +static inline void pll_lock(struct pistachio_clk_pll *pll) +{ + while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) + cpu_relax(); +} + static inline u32 do_div_round_closest(u64 dividend, u32 divisor) { dividend += divisor / 2; @@ -124,6 +145,8 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw) val &= ~PLL_FRAC_CTRL4_BYPASS; pll_writel(pll, val, PLL_CTRL4); + pll_lock(pll); + return 0; } @@ -149,16 +172,29 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; - bool was_enabled; - u32 val; + int enabled = pll_gf40lp_frac_is_enabled(hw); + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) + return -EINVAL; params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (!params || !params->refdiv) return -EINVAL; - was_enabled = pll_gf40lp_frac_is_enabled(hw); - if (!was_enabled) - pll_gf40lp_frac_enable(hw); + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); val = pll_readl(pll, PLL_CTRL1); val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | @@ -168,6 +204,19 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, pll_writel(pll, val, PLL_CTRL1); val = pll_readl(pll, PLL_CTRL2); + + old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | (PLL_FRAC_CTRL2_POSTDIV1_MASK << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | @@ -178,11 +227,8 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL2); - while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) - cpu_relax(); - - if (!was_enabled) - pll_gf40lp_frac_disable(hw); + if (enabled) + pll_lock(pll); return 0; } @@ -241,6 +287,8 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw) val &= ~PLL_INT_CTRL2_BYPASS; pll_writel(pll, val, PLL_CTRL2); + pll_lock(pll); + return 0; } @@ -266,18 +314,44 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; - bool was_enabled; - u32 val; + int enabled = pll_gf40lp_laint_is_enabled(hw); + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) + return -EINVAL; params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (!params || !params->refdiv) return -EINVAL; - was_enabled = pll_gf40lp_laint_is_enabled(hw); - if (!was_enabled) - pll_gf40lp_laint_enable(hw); + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_LA, MAX_VCO_LA); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); val = pll_readl(pll, PLL_CTRL1); + + old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & + PLL_INT_CTRL1_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & + PLL_INT_CTRL1_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | @@ -288,11 +362,8 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL1); - while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) - cpu_relax(); - - if (!was_enabled) - pll_gf40lp_laint_disable(hw); + if (enabled) + pll_lock(pll); return 0; } diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h index b04c5b9c0ea8..d1de805df867 100644 --- a/drivers/clk/pxa/clk-pxa.h +++ b/drivers/clk/pxa/clk-pxa.h @@ -14,7 +14,7 @@ #define _CLK_PXA_ #define PARENTS(name) \ - static const char *name ## _parents[] __initdata + static const char *const name ## _parents[] __initconst #define MUX_RO_RATE_RO_OPS(name, clk_name) \ static struct clk_hw name ## _mux_hw; \ static struct clk_hw name ## _rate_hw; \ @@ -72,7 +72,7 @@ struct desc_clk_cken { const char *name; const char *dev_id; const char *con_id; - const char **parent_names; + const char * const *parent_names; struct clk_fixed_factor lp; struct clk_fixed_factor hp; struct clk_gate gate; diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 8539c4fd34cc..fb7721bd37e6 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -231,7 +231,7 @@ static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb, } struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates, void __iomem *reg_base, spinlock_t *lock) diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index c842e3b60f21..e9f8df324e7c 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -120,7 +120,7 @@ static const struct clk_ops rockchip_mmc_clk_ops = { }; struct clk *rockchip_clk_register_mmc(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift) { struct clk_init_data init; diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index f8d3baf275b2..76027261f7ed 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -329,10 +329,10 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { */ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int mode_offset, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int mode_offset, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock) { const char *pll_parents[3]; diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 556ce041d371..e4f9d472f1ff 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -26,7 +26,7 @@ enum rk3188_plls { apll, cpll, dpll, gpll, }; -struct rockchip_pll_rate_table rk3188_pll_rates[] = { +static struct rockchip_pll_rate_table rk3188_pll_rates[] = { RK3066_PLL_RATE(2208000000, 1, 92, 1), RK3066_PLL_RATE(2184000000, 1, 91, 1), RK3066_PLL_RATE(2160000000, 1, 90, 1), diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index d17eb4528a28..4f817ed9e6ee 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -27,7 +27,7 @@ enum rk3288_plls { apll, dpll, cpll, gpll, npll, }; -struct rockchip_pll_rate_table rk3288_pll_rates[] = { +static struct rockchip_pll_rate_table rk3288_pll_rates[] = { RK3066_PLL_RATE(2208000000, 1, 92, 1), RK3066_PLL_RATE(2184000000, 1, 91, 1), RK3066_PLL_RATE(2160000000, 1, 90, 1), diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index edb5d489ae61..052b94db0ff9 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -39,7 +39,7 @@ * sometimes without one of those components. */ static struct clk *rockchip_clk_register_branch(const char *name, - const char **parent_names, u8 num_parents, void __iomem *base, + const char *const *parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, u8 div_shift, u8 div_width, u8 div_flags, struct clk_div_table *div_table, int gate_offset, @@ -103,8 +103,8 @@ static struct clk *rockchip_clk_register_branch(const char *name, } static struct clk *rockchip_clk_register_frac_branch(const char *name, - const char **parent_names, u8 num_parents, void __iomem *base, - int muxdiv_offset, u8 div_flags, + const char *const *parent_names, u8 num_parents, + void __iomem *base, int muxdiv_offset, u8 div_flags, int gate_offset, u8 gate_shift, u8 gate_flags, unsigned long flags, spinlock_t *lock) { @@ -297,7 +297,7 @@ void __init rockchip_clk_register_branches( } void __init rockchip_clk_register_armclk(unsigned int lookup_id, - const char *name, const char **parent_names, + const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index e63cafe893e1..6b092673048a 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -108,7 +108,7 @@ struct rockchip_pll_rate_table { struct rockchip_pll_clock { unsigned int id; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; int con_offset; @@ -140,10 +140,10 @@ struct rockchip_pll_clock { } struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int reg_mode, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int reg_mode, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock); struct rockchip_cpuclk_clksel { @@ -173,16 +173,16 @@ struct rockchip_cpuclk_reg_data { }; struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates, void __iomem *reg_base, spinlock_t *lock); struct clk *rockchip_clk_register_mmc(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift); -#define PNAME(x) static const char *x[] __initdata +#define PNAME(x) static const char *const x[] __initconst enum rockchip_clk_branch_type { branch_composite, @@ -197,7 +197,7 @@ struct rockchip_clk_branch { unsigned int id; enum rockchip_clk_branch_type branch_type; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; int muxdiv_offset; @@ -403,7 +403,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates); diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index a17683b2cf27..5f6833ea355d 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -2,7 +2,7 @@ # Samsung Clock specific Makefile # -obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o +obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c new file mode 100644 index 000000000000..3a1fe07cfe9e --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham <thomas.ab@samsung.com> + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.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 file contains the utility function to register CPU clock for Samsung + * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a + * group of CPUs. The CPU clock is typically derived from a hierarchy of clock + * blocks which includes mux and divider blocks. There are a number of other + * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI + * clock for CPU domain. The rates of these auxiliary clocks are related to the + * CPU clock rate and this relation is usually specified in the hardware manual + * of the SoC or supplied after the SoC characterization. + * + * The below implementation of the CPU clock allows the rate changes of the CPU + * clock and the corresponding rate changes of the auxillary clocks of the CPU + * domain. The platform clock driver provides a clock register configuration + * for each configurable rate which is then used to program the clock hardware + * registers to acheive a fast co-oridinated rate change for all the CPU domain + * clocks. + * + * On a rate change request for the CPU clock, the rate change is propagated + * upto the PLL supplying the clock to the CPU domain clock blocks. While the + * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an + * alternate clock source. If required, the alternate clock source is divided + * down in order to keep the output clock rate within the previous OPP limits. +*/ + +#include <linux/errno.h> +#include "clk-cpu.h" + +#define E4210_SRC_CPU 0x0 +#define E4210_STAT_CPU 0x200 +#define E4210_DIV_CPU0 0x300 +#define E4210_DIV_CPU1 0x304 +#define E4210_DIV_STAT_CPU0 0x400 +#define E4210_DIV_STAT_CPU1 0x404 + +#define E4210_DIV0_RATIO0_MASK 0x7 +#define E4210_DIV1_HPM_MASK (0x7 << 4) +#define E4210_DIV1_COPY_MASK (0x7 << 0) +#define E4210_MUX_HPM_MASK (1 << 20) +#define E4210_DIV0_ATB_SHIFT 16 +#define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT) + +#define MAX_DIV 8 +#define DIV_MASK 7 +#define DIV_MASK_ALL 0xffffffff +#define MUX_MASK 7 + +/* + * Helper function to wait until divider(s) have stabilized after the divider + * value has changed. + */ +static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + do { + if (!(readl(div_reg) & mask)) + return; + } while (time_before(jiffies, timeout)); + + if (!(readl(div_reg) & mask)) + return; + + pr_err("%s: timeout in divider stablization\n", __func__); +} + +/* + * Helper function to wait until mux has stabilized after the mux selection + * value was changed. + */ +static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos, + unsigned long mux_value) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + do { + if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) + return; + } while (time_before(jiffies, timeout)); + + if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) + return; + + pr_err("%s: re-parenting mux timed-out\n", __func__); +} + +/* common round rate callback useable for all types of CPU clocks */ +static long exynos_cpuclk_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + struct clk *parent = __clk_get_parent(hw->clk); + *prate = __clk_round_rate(parent, drate); + return *prate; +} + +/* common recalc rate callback useable for all types of CPU clocks */ +static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* + * The CPU clock output (armclk) rate is the same as its parent + * rate. Although there exist certain dividers inside the CPU + * clock block that could be used to divide the parent clock, + * the driver does not make use of them currently, except during + * frequency transitions. + */ + return parent_rate; +} + +static const struct clk_ops exynos_cpuclk_clk_ops = { + .recalc_rate = exynos_cpuclk_recalc_rate, + .round_rate = exynos_cpuclk_round_rate, +}; + +/* + * Helper function to set the 'safe' dividers for the CPU clock. The parameters + * div and mask contain the divider value and the register bit mask of the + * dividers to be programmed. + */ +static void exynos_set_safe_div(void __iomem *base, unsigned long div, + unsigned long mask) +{ + unsigned long div0; + + div0 = readl(base + E4210_DIV_CPU0); + div0 = (div0 & ~mask) | (div & mask); + writel(div0, base + E4210_DIV_CPU0); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask); +} + +/* handler for pre-rate change notification from parent clock */ +static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, + struct exynos_cpuclk *cpuclk, void __iomem *base) +{ + const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; + unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent); + unsigned long alt_div = 0, alt_div_mask = DIV_MASK; + unsigned long div0, div1 = 0, mux_reg; + + /* find out the divider values to use for clock data */ + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; + cfg_data++; + } + + spin_lock(cpuclk->lock); + + /* + * For the selected PLL clock frequency, get the pre-defined divider + * values. If the clock for sclk_hpm is not sourced from apll, then + * 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)) { + div1 = cfg_data->div1; + if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK) + div1 = readl(base + E4210_DIV_CPU1) & + (E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK); + } + + /* + * If the old parent clock speed is less than the clock speed of + * the alternate parent, then it should be ensured that at no point + * the armclk speed is more than the old_prate until the dividers are + * set. Also workaround the issue of the dividers being set to lower + * values before the parent clock speed is set to new lower speed + * (this can result in too high speed of armclk output clocks). + */ + if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { + unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); + + 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)) { + /* + * In Exynos4210, ATB clock parent is also mout_core. So + * ATB clock also needs to be mantained at safe speed. + */ + alt_div |= E4210_DIV0_ATB_MASK; + alt_div_mask |= E4210_DIV0_ATB_MASK; + } + exynos_set_safe_div(base, alt_div, alt_div_mask); + div0 |= alt_div; + } + + /* select sclk_mpll as the alternate parent */ + mux_reg = readl(base + E4210_SRC_CPU); + writel(mux_reg | (1 << 16), base + E4210_SRC_CPU); + wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2); + + /* alternate parent is active now. set the dividers */ + 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)) { + writel(div1, base + E4210_DIV_CPU1); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU1, + DIV_MASK_ALL); + } + + spin_unlock(cpuclk->lock); + return 0; +} + +/* handler for post-rate change notification from parent clock */ +static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, + struct exynos_cpuclk *cpuclk, void __iomem *base) +{ + const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; + unsigned long div = 0, div_mask = DIV_MASK; + 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)) { + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; + cfg_data++; + } + } + + spin_lock(cpuclk->lock); + + /* select mout_apll as the alternate parent */ + mux_reg = readl(base + E4210_SRC_CPU); + 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)) { + div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); + div_mask |= E4210_DIV0_ATB_MASK; + } + + exynos_set_safe_div(base, div, div_mask); + spin_unlock(cpuclk->lock); + return 0; +} + +/* + * This notifier function is called for the pre-rate and post-rate change + * notifications of the parent clock of cpuclk. + */ +static int exynos_cpuclk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct exynos_cpuclk *cpuclk; + void __iomem *base; + int err = 0; + + cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb); + base = cpuclk->ctrl_base; + + if (event == PRE_RATE_CHANGE) + err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base); + else if (event == POST_RATE_CHANGE) + err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base); + + return notifier_from_errno(err); +} + +/* helper function to register a CPU clock */ +int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *parent, + const char *alt_parent, unsigned long offset, + const struct exynos_cpuclk_cfg_data *cfg, + unsigned long num_cfgs, unsigned long flags) +{ + struct exynos_cpuclk *cpuclk; + struct clk_init_data init; + struct clk *clk; + int ret = 0; + + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); + if (!cpuclk) + return -ENOMEM; + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent; + init.num_parents = 1; + init.ops = &exynos_cpuclk_clk_ops; + + cpuclk->hw.init = &init; + cpuclk->ctrl_base = ctx->reg_base + offset; + cpuclk->lock = &ctx->lock; + cpuclk->flags = flags; + cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb; + + cpuclk->alt_parent = __clk_lookup(alt_parent); + if (!cpuclk->alt_parent) { + pr_err("%s: could not lookup alternate parent %s\n", + __func__, alt_parent); + ret = -EINVAL; + goto free_cpuclk; + } + + clk = __clk_lookup(parent); + if (!clk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, parent); + ret = -EINVAL; + goto free_cpuclk; + } + + ret = clk_notifier_register(clk, &cpuclk->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, name); + goto free_cpuclk; + } + + cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL); + if (!cpuclk->cfg) { + pr_err("%s: could not allocate memory for cpuclk data\n", + __func__); + ret = -ENOMEM; + goto unregister_clk_nb; + } + + clk = clk_register(NULL, &cpuclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register cpuclk %s\n", __func__, name); + ret = PTR_ERR(clk); + goto free_cpuclk_data; + } + + samsung_clk_add_lookup(ctx, clk, lookup_id); + return 0; + +free_cpuclk_data: + kfree(cpuclk->cfg); +unregister_clk_nb: + clk_notifier_unregister(__clk_lookup(parent), &cpuclk->clk_nb); +free_cpuclk: + kfree(cpuclk); + return ret; +} diff --git a/drivers/clk/samsung/clk-cpu.h b/drivers/clk/samsung/clk-cpu.h new file mode 100644 index 000000000000..37874d3c3165 --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for all PLL's in Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_CPU_H +#define __SAMSUNG_CLK_CPU_H + +#include "clk.h" + +/** + * struct exynos_cpuclk_data: config data to setup cpu clocks. + * @prate: frequency of the primary parent clock (in KHz). + * @div0: value to be programmed in the div_cpu0 register. + * @div1: value to be programmed in the div_cpu1 register. + * + * This structure holds the divider configuration data for dividers in the CPU + * clock domain. The parent frequency at which these divider values are valid is + * specified in @prate. The @prate is the frequency of the primary parent clock. + * For CPU clock domains that do not have a DIV1 register, the @div1 member + * value is not used. + */ +struct exynos_cpuclk_cfg_data { + unsigned long prate; + unsigned long div0; + unsigned long div1; +}; + +/** + * struct exynos_cpuclk: information about clock supplied to a CPU core. + * @hw: handle between CCF and CPU clock. + * @alt_parent: alternate parent clock to use when switching the speed + * of the primary parent clock. + * @ctrl_base: base address of the clock controller. + * @lock: cpu clock domain register access lock. + * @cfg: cpu clock rate configuration data. + * @num_cfgs: number of array elements in @cfg array. + * @clk_nb: clock notifier registered for changes in clock speed of the + * primary parent clock. + * @flags: configuration flags for the CPU clock. + * + * This structure holds information required for programming the CPU clock for + * various clock speeds. + */ +struct exynos_cpuclk { + struct clk_hw hw; + struct clk *alt_parent; + void __iomem *ctrl_base; + spinlock_t *lock; + const struct exynos_cpuclk_cfg_data *cfg; + const unsigned long num_cfgs; + struct notifier_block clk_nb; + unsigned long flags; + +/* The CPU clock registers has DIV1 configuration register */ +#define CLK_CPU_HAS_DIV1 (1 << 0) +/* When ALT parent is active, debug clocks need safe divider values */ +#define CLK_CPU_NEEDS_DEBUG_ALT_DIV (1 << 1) +}; + +extern int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, + unsigned int lookup_id, const char *name, + const char *parent, const char *alt_parent, + unsigned long offset, + const struct exynos_cpuclk_cfg_data *cfg, + unsigned long num_cfgs, unsigned long flags); + +#endif /* __SAMSUNG_CLK_CPU_H */ diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 714d6ba782c8..cae2c048488d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -19,6 +19,7 @@ #include <linux/syscore_ops.h> #include "clk.h" +#include "clk-cpu.h" /* Exynos4 clock controller register offsets */ #define SRC_LEFTBUS 0x4200 @@ -534,7 +535,8 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda /* list of mux clocks supported in all exynos4 soc's */ static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, - CLK_SET_RATE_PARENT, 0, "mout_apll"), + CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0, + "mout_apll"), MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), @@ -1378,6 +1380,22 @@ static void __init exynos4x12_core_down_clock(void) __raw_writel(0x0, reg_base + E4X12_PWR_CTRL2); } +#define E4210_CPU_DIV0(apll, pclk_dbg, atb, periph, corem1, corem0) \ + (((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \ + ((periph) << 12) | ((corem1) << 8) | ((corem0) << 4)) +#define E4210_CPU_DIV1(hpm, copy) \ + (((hpm) << 4) | ((copy) << 0)) + +static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = { + { 1200000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 5), }, + { 1000000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 4), }, + { 800000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 500000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 400000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 200000, E4210_CPU_DIV0(0, 1, 1, 1, 3, 1), E4210_CPU_DIV1(0, 3), }, + { 0 }, +}; + /* register exynos4 clocks */ static void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc soc) @@ -1455,6 +1473,10 @@ static void __init exynos4_clk_init(struct device_node *np, samsung_clk_register_fixed_factor(ctx, exynos4210_fixed_factor_clks, ARRAY_SIZE(exynos4210_fixed_factor_clks)); + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", + mout_core_p4210[0], mout_core_p4210[1], 0x14200, + e4210_armclk_d, ARRAY_SIZE(e4210_armclk_d), + CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); } else { samsung_clk_register_mux(ctx, exynos4x12_mux_clks, ARRAY_SIZE(exynos4x12_mux_clks)); diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index e2e5193d1049..06f96eb7cf93 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -94,7 +94,7 @@ PNAME(mout_aud_pll_user_p) = {"fin_pll", "fout_aud_pll"}; PNAME(mout_sclk_aud_i2s_p) = {"mout_aud_pll_user", "ioclk_i2s_cdclk"}; PNAME(mout_sclk_aud_pcm_p) = {"mout_aud_pll_user", "ioclk_pcm_extclk"}; -struct samsung_mux_clock aud_mux_clks[] __initdata = { +static struct samsung_mux_clock aud_mux_clks[] __initdata = { MUX(AUD_MOUT_AUD_PLL_USER, "mout_aud_pll_user", mout_aud_pll_user_p, MUX_SEL_AUD, 0, 1), MUX(AUD_MOUT_SCLK_AUD_I2S, "mout_sclk_aud_i2s", mout_sclk_aud_i2s_p, @@ -103,7 +103,7 @@ struct samsung_mux_clock aud_mux_clks[] __initdata = { MUX_SEL_AUD, 8, 1), }; -struct samsung_div_clock aud_div_clks[] __initdata = { +static struct samsung_div_clock aud_div_clks[] __initdata = { DIV(AUD_DOUT_ACLK_AUD_131, "dout_aclk_aud_131", "mout_aud_pll_user", DIV_AUD0, 0, 4), @@ -115,7 +115,7 @@ struct samsung_div_clock aud_div_clks[] __initdata = { DIV_AUD1, 12, 4), }; -struct samsung_gate_clock aud_gate_clks[] __initdata = { +static struct samsung_gate_clock aud_gate_clks[] __initdata = { GATE(AUD_SCLK_I2S, "sclk_aud_i2s", "dout_sclk_aud_i2s", EN_SCLK_AUD, 0, CLK_SET_RATE_PARENT, 0), GATE(AUD_SCLK_PCM, "sclk_aud_pcm", "dout_sclk_aud_pcm", @@ -135,7 +135,7 @@ struct samsung_gate_clock aud_gate_clks[] __initdata = { static void __init exynos5260_clk_aud_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = aud_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(aud_mux_clks); @@ -203,7 +203,7 @@ PNAME(mout_phyclk_mipi_dphy_4lmrxclk_esc0_user_p) = {"fin_pll", PNAME(mout_sclk_hdmi_spdif_p) = {"fin_pll", "ioclk_spdif_extclk", "dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"}; -struct samsung_mux_clock disp_mux_clks[] __initdata = { +static struct samsung_mux_clock disp_mux_clks[] __initdata = { MUX(DISP_MOUT_ACLK_DISP_333_USER, "mout_aclk_disp_333_user", mout_aclk_disp_333_user_p, MUX_SEL_DISP0, 0, 1), @@ -272,7 +272,7 @@ struct samsung_mux_clock disp_mux_clks[] __initdata = { MUX_SEL_DISP4, 4, 2), }; -struct samsung_div_clock disp_div_clks[] __initdata = { +static struct samsung_div_clock disp_div_clks[] __initdata = { DIV(DISP_DOUT_PCLK_DISP_111, "dout_pclk_disp_111", "mout_aclk_disp_222_user", DIV_DISP, 8, 4), @@ -285,7 +285,7 @@ struct samsung_div_clock disp_div_clks[] __initdata = { DIV_DISP, 16, 4), }; -struct samsung_gate_clock disp_gate_clks[] __initdata = { +static struct samsung_gate_clock disp_gate_clks[] __initdata = { GATE(DISP_MOUT_HDMI_PHY_PIXEL_USER, "sclk_hdmi_link_i_pixel", "mout_phyclk_hdmi_phy_pixel_clko_user", EN_SCLK_DISP0, 26, CLK_SET_RATE_PARENT, 0), @@ -325,7 +325,7 @@ struct samsung_gate_clock disp_gate_clks[] __initdata = { static void __init exynos5260_clk_disp_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = disp_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(disp_mux_clks); @@ -363,13 +363,13 @@ static unsigned long egl_clk_regs[] __initdata = { PNAME(mout_egl_b_p) = {"mout_egl_pll", "dout_bus_pll"}; PNAME(mout_egl_pll_p) = {"fin_pll", "fout_egl_pll"}; -struct samsung_mux_clock egl_mux_clks[] __initdata = { +static struct samsung_mux_clock egl_mux_clks[] __initdata = { MUX(EGL_MOUT_EGL_PLL, "mout_egl_pll", mout_egl_pll_p, MUX_SEL_EGL, 4, 1), MUX(EGL_MOUT_EGL_B, "mout_egl_b", mout_egl_b_p, MUX_SEL_EGL, 16, 1), }; -struct samsung_div_clock egl_div_clks[] __initdata = { +static struct samsung_div_clock egl_div_clks[] __initdata = { DIV(EGL_DOUT_EGL1, "dout_egl1", "mout_egl_b", DIV_EGL, 0, 3), DIV(EGL_DOUT_EGL2, "dout_egl2", "dout_egl1", DIV_EGL, 4, 3), DIV(EGL_DOUT_ACLK_EGL, "dout_aclk_egl", "dout_egl2", DIV_EGL, 8, 3), @@ -389,7 +389,7 @@ static struct samsung_pll_clock egl_pll_clks[] __initdata = { static void __init exynos5260_clk_egl_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = egl_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(egl_pll_clks); @@ -433,7 +433,7 @@ PNAME(mout_phyclk_usbdrd30_pipe_pclk_user_p) = {"fin_pll", PNAME(mout_phyclk_usbdrd30_phyclock_user_p) = {"fin_pll", "phyclk_usbdrd30_udrd30_phyclock"}; -struct samsung_mux_clock fsys_mux_clks[] __initdata = { +static struct samsung_mux_clock fsys_mux_clks[] __initdata = { MUX(FSYS_MOUT_PHYCLK_USBDRD30_PHYCLOCK_USER, "mout_phyclk_usbdrd30_phyclock_user", mout_phyclk_usbdrd30_phyclock_user_p, @@ -456,7 +456,7 @@ struct samsung_mux_clock fsys_mux_clks[] __initdata = { MUX_SEL_FSYS1, 16, 1), }; -struct samsung_gate_clock fsys_gate_clks[] __initdata = { +static struct samsung_gate_clock fsys_gate_clks[] __initdata = { GATE(FSYS_PHYCLK_USBHOST20, "phyclk_usbhost20_phyclock", "mout_phyclk_usbdrd30_phyclock_user", EN_SCLK_FSYS, 1, 0, 0), @@ -491,7 +491,7 @@ struct samsung_gate_clock fsys_gate_clks[] __initdata = { static void __init exynos5260_clk_fsys_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = fsys_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks); @@ -537,18 +537,18 @@ static unsigned long g2d_clk_regs[] __initdata = { PNAME(mout_aclk_g2d_333_user_p) = {"fin_pll", "dout_aclk_g2d_333"}; -struct samsung_mux_clock g2d_mux_clks[] __initdata = { +static struct samsung_mux_clock g2d_mux_clks[] __initdata = { MUX(G2D_MOUT_ACLK_G2D_333_USER, "mout_aclk_g2d_333_user", mout_aclk_g2d_333_user_p, MUX_SEL_G2D, 0, 1), }; -struct samsung_div_clock g2d_div_clks[] __initdata = { +static struct samsung_div_clock g2d_div_clks[] __initdata = { DIV(G2D_DOUT_PCLK_G2D_83, "dout_pclk_g2d_83", "mout_aclk_g2d_333_user", DIV_G2D, 0, 3), }; -struct samsung_gate_clock g2d_gate_clks[] __initdata = { +static struct samsung_gate_clock g2d_gate_clks[] __initdata = { GATE(G2D_CLK_G2D, "clk_g2d", "mout_aclk_g2d_333_user", EN_IP_G2D, 4, 0, 0), GATE(G2D_CLK_JPEG, "clk_jpeg", "mout_aclk_g2d_333_user", @@ -580,7 +580,7 @@ struct samsung_gate_clock g2d_gate_clks[] __initdata = { static void __init exynos5260_clk_g2d_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = g2d_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(g2d_mux_clks); @@ -617,17 +617,17 @@ static unsigned long g3d_clk_regs[] __initdata = { PNAME(mout_g3d_pll_p) = {"fin_pll", "fout_g3d_pll"}; -struct samsung_mux_clock g3d_mux_clks[] __initdata = { +static struct samsung_mux_clock g3d_mux_clks[] __initdata = { MUX(G3D_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, MUX_SEL_G3D, 0, 1), }; -struct samsung_div_clock g3d_div_clks[] __initdata = { +static struct samsung_div_clock g3d_div_clks[] __initdata = { DIV(G3D_DOUT_PCLK_G3D, "dout_pclk_g3d", "dout_aclk_g3d", DIV_G3D, 0, 3), DIV(G3D_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_g3d_pll", DIV_G3D, 4, 3), }; -struct samsung_gate_clock g3d_gate_clks[] __initdata = { +static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(G3D_CLK_G3D, "clk_g3d", "dout_aclk_g3d", EN_IP_G3D, 2, 0, 0), GATE(G3D_CLK_G3D_HPM, "clk_g3d_hpm", "dout_aclk_g3d", EN_IP_G3D, 3, 0, 0), @@ -641,7 +641,7 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = { static void __init exynos5260_clk_g3d_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = g3d_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(g3d_pll_clks); @@ -694,7 +694,7 @@ PNAME(mout_aclk_m2m_400_user_p) = {"fin_pll", "dout_aclk_gscl_400"}; PNAME(mout_aclk_gscl_fimc_user_p) = {"fin_pll", "dout_aclk_gscl_400"}; PNAME(mout_aclk_csis_p) = {"dout_aclk_csis_200", "mout_aclk_gscl_fimc_user"}; -struct samsung_mux_clock gscl_mux_clks[] __initdata = { +static struct samsung_mux_clock gscl_mux_clks[] __initdata = { MUX(GSCL_MOUT_ACLK_GSCL_333_USER, "mout_aclk_gscl_333_user", mout_aclk_gscl_333_user_p, MUX_SEL_GSCL, 0, 1), @@ -708,7 +708,7 @@ struct samsung_mux_clock gscl_mux_clks[] __initdata = { MUX_SEL_GSCL, 24, 1), }; -struct samsung_div_clock gscl_div_clks[] __initdata = { +static struct samsung_div_clock gscl_div_clks[] __initdata = { DIV(GSCL_DOUT_PCLK_M2M_100, "dout_pclk_m2m_100", "mout_aclk_m2m_400_user", DIV_GSCL, 0, 3), @@ -717,7 +717,7 @@ struct samsung_div_clock gscl_div_clks[] __initdata = { DIV_GSCL, 4, 3), }; -struct samsung_gate_clock gscl_gate_clks[] __initdata = { +static struct samsung_gate_clock gscl_gate_clks[] __initdata = { GATE(GSCL_SCLK_CSIS0_WRAP, "sclk_csis0_wrap", "dout_aclk_csis_200", EN_SCLK_GSCL_FIMC, 0, CLK_SET_RATE_PARENT, 0), GATE(GSCL_SCLK_CSIS1_WRAP, "sclk_csis1_wrap", "dout_aclk_csis_200", @@ -776,7 +776,7 @@ struct samsung_gate_clock gscl_gate_clks[] __initdata = { static void __init exynos5260_clk_gscl_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = gscl_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(gscl_mux_clks); @@ -813,14 +813,14 @@ static unsigned long isp_clk_regs[] __initdata = { PNAME(mout_isp_400_user_p) = {"fin_pll", "dout_aclk_isp1_400"}; PNAME(mout_isp_266_user_p) = {"fin_pll", "dout_aclk_isp1_266"}; -struct samsung_mux_clock isp_mux_clks[] __initdata = { +static struct samsung_mux_clock isp_mux_clks[] __initdata = { MUX(ISP_MOUT_ISP_266_USER, "mout_isp_266_user", mout_isp_266_user_p, MUX_SEL_ISP0, 0, 1), MUX(ISP_MOUT_ISP_400_USER, "mout_isp_400_user", mout_isp_400_user_p, MUX_SEL_ISP0, 4, 1), }; -struct samsung_div_clock isp_div_clks[] __initdata = { +static struct samsung_div_clock isp_div_clks[] __initdata = { DIV(ISP_DOUT_PCLK_ISP_66, "dout_pclk_isp_66", "mout_kfc", DIV_ISP, 0, 3), DIV(ISP_DOUT_PCLK_ISP_133, "dout_pclk_isp_133", "mout_kfc", @@ -832,7 +832,7 @@ struct samsung_div_clock isp_div_clks[] __initdata = { DIV(ISP_DOUT_SCLK_MPWM, "dout_sclk_mpwm", "mout_kfc", DIV_ISP, 20, 2), }; -struct samsung_gate_clock isp_gate_clks[] __initdata = { +static struct samsung_gate_clock isp_gate_clks[] __initdata = { GATE(ISP_CLK_GIC, "clk_isp_gic", "mout_aclk_isp1_266", EN_IP_ISP0, 15, 0, 0), @@ -895,7 +895,7 @@ struct samsung_gate_clock isp_gate_clks[] __initdata = { static void __init exynos5260_clk_isp_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = isp_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(isp_mux_clks); @@ -934,13 +934,13 @@ static unsigned long kfc_clk_regs[] __initdata = { PNAME(mout_kfc_pll_p) = {"fin_pll", "fout_kfc_pll"}; PNAME(mout_kfc_p) = {"mout_kfc_pll", "dout_media_pll"}; -struct samsung_mux_clock kfc_mux_clks[] __initdata = { +static struct samsung_mux_clock kfc_mux_clks[] __initdata = { MUX(KFC_MOUT_KFC_PLL, "mout_kfc_pll", mout_kfc_pll_p, MUX_SEL_KFC0, 0, 1), MUX(KFC_MOUT_KFC, "mout_kfc", mout_kfc_p, MUX_SEL_KFC2, 0, 1), }; -struct samsung_div_clock kfc_div_clks[] __initdata = { +static struct samsung_div_clock kfc_div_clks[] __initdata = { DIV(KFC_DOUT_KFC1, "dout_kfc1", "mout_kfc", DIV_KFC, 0, 3), DIV(KFC_DOUT_KFC2, "dout_kfc2", "dout_kfc1", DIV_KFC, 4, 3), DIV(KFC_DOUT_KFC_ATCLK, "dout_kfc_atclk", "dout_kfc2", DIV_KFC, 8, 3), @@ -959,7 +959,7 @@ static struct samsung_pll_clock kfc_pll_clks[] __initdata = { static void __init exynos5260_clk_kfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = kfc_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(kfc_pll_clks); @@ -993,18 +993,18 @@ static unsigned long mfc_clk_regs[] __initdata = { PNAME(mout_aclk_mfc_333_user_p) = {"fin_pll", "dout_aclk_mfc_333"}; -struct samsung_mux_clock mfc_mux_clks[] __initdata = { +static struct samsung_mux_clock mfc_mux_clks[] __initdata = { MUX(MFC_MOUT_ACLK_MFC_333_USER, "mout_aclk_mfc_333_user", mout_aclk_mfc_333_user_p, MUX_SEL_MFC, 0, 1), }; -struct samsung_div_clock mfc_div_clks[] __initdata = { +static struct samsung_div_clock mfc_div_clks[] __initdata = { DIV(MFC_DOUT_PCLK_MFC_83, "dout_pclk_mfc_83", "mout_aclk_mfc_333_user", DIV_MFC, 0, 3), }; -struct samsung_gate_clock mfc_gate_clks[] __initdata = { +static struct samsung_gate_clock mfc_gate_clks[] __initdata = { GATE(MFC_CLK_MFC, "clk_mfc", "mout_aclk_mfc_333_user", EN_IP_MFC, 1, 0, 0), GATE(MFC_CLK_SMMU2_MFCM0, "clk_smmu2_mfcm0", "mout_aclk_mfc_333_user", @@ -1015,7 +1015,7 @@ struct samsung_gate_clock mfc_gate_clks[] __initdata = { static void __init exynos5260_clk_mfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = mfc_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(mfc_mux_clks); @@ -1078,7 +1078,7 @@ PNAME(mout_mif_drex2x_p) = {"dout_mem_pll", "dout_bus_pll"}; PNAME(mout_clkm_phy_p) = {"mout_mif_drex", "dout_media_pll"}; PNAME(mout_clk2x_phy_p) = {"mout_mif_drex2x", "dout_media_pll"}; -struct samsung_mux_clock mif_mux_clks[] __initdata = { +static struct samsung_mux_clock mif_mux_clks[] __initdata = { MUX(MIF_MOUT_MEM_PLL, "mout_mem_pll", mout_mem_pll_p, MUX_SEL_MIF, 0, 1), MUX(MIF_MOUT_BUS_PLL, "mout_bus_pll", mout_bus_pll_p, @@ -1095,7 +1095,7 @@ struct samsung_mux_clock mif_mux_clks[] __initdata = { MUX_SEL_MIF, 24, 1), }; -struct samsung_div_clock mif_div_clks[] __initdata = { +static struct samsung_div_clock mif_div_clks[] __initdata = { DIV(MIF_DOUT_MEDIA_PLL, "dout_media_pll", "mout_media_pll", DIV_MIF, 0, 3), DIV(MIF_DOUT_MEM_PLL, "dout_mem_pll", "mout_mem_pll", @@ -1114,7 +1114,7 @@ struct samsung_div_clock mif_div_clks[] __initdata = { DIV_MIF, 28, 4), }; -struct samsung_gate_clock mif_gate_clks[] __initdata = { +static struct samsung_gate_clock mif_gate_clks[] __initdata = { GATE(MIF_CLK_LPDDR3PHY_WRAP0, "clk_lpddr3phy_wrap0", "dout_clk2x_phy", EN_IP_MIF, 12, CLK_IGNORE_UNUSED, 0), GATE(MIF_CLK_LPDDR3PHY_WRAP1, "clk_lpddr3phy_wrap1", "dout_clk2x_phy", @@ -1162,7 +1162,7 @@ static struct samsung_pll_clock mif_pll_clks[] __initdata = { static void __init exynos5260_clk_mif_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = mif_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(mif_pll_clks); @@ -1221,7 +1221,7 @@ PNAME(mout_sclk_i2scod_p) = {"ioclk_i2s_cdclk", "fin_pll", "dout_aclk_peri_aud", PNAME(mout_sclk_spdif_p) = {"ioclk_spdif_extclk", "fin_pll", "dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"}; -struct samsung_mux_clock peri_mux_clks[] __initdata = { +static struct samsung_mux_clock peri_mux_clks[] __initdata = { MUX(PERI_MOUT_SCLK_PCM, "mout_sclk_pcm", mout_sclk_pcm_p, MUX_SEL_PERI1, 4, 2), MUX(PERI_MOUT_SCLK_I2SCOD, "mout_sclk_i2scod", mout_sclk_i2scod_p, @@ -1230,12 +1230,12 @@ struct samsung_mux_clock peri_mux_clks[] __initdata = { MUX_SEL_PERI1, 20, 2), }; -struct samsung_div_clock peri_div_clks[] __initdata = { +static struct samsung_div_clock peri_div_clks[] __initdata = { DIV(PERI_DOUT_PCM, "dout_pcm", "mout_sclk_pcm", DIV_PERI, 0, 8), DIV(PERI_DOUT_I2S, "dout_i2s", "mout_sclk_i2scod", DIV_PERI, 8, 6), }; -struct samsung_gate_clock peri_gate_clks[] __initdata = { +static struct samsung_gate_clock peri_gate_clks[] __initdata = { GATE(PERI_SCLK_PCM1, "sclk_pcm1", "dout_pcm", EN_SCLK_PERI, 0, CLK_SET_RATE_PARENT, 0), GATE(PERI_SCLK_I2S, "sclk_i2s", "dout_i2s", EN_SCLK_PERI, 1, @@ -1370,7 +1370,7 @@ struct samsung_gate_clock peri_gate_clks[] __initdata = { static void __init exynos5260_clk_peri_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = peri_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(peri_mux_clks); @@ -1432,7 +1432,7 @@ static unsigned long top_clk_regs[] __initdata = { }; /* fixed rate clocks generated inside the soc */ -struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { +static struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { FRATE(PHYCLK_DPTX_PHY_CH3_TXD_CLK, "phyclk_dptx_phy_ch3_txd_clk", NULL, CLK_IS_ROOT, 270000000), FRATE(PHYCLK_DPTX_PHY_CH2_TXD_CLK, "phyclk_dptx_phy_ch2_txd_clk", NULL, @@ -1519,7 +1519,7 @@ PNAME(mout_sclk_fsys_mmc1_sdclkin_b_p) = {"mout_sclk_fsys_mmc1_sdclkin_a", PNAME(mout_sclk_fsys_mmc2_sdclkin_b_p) = {"mout_sclk_fsys_mmc2_sdclkin_a", "mout_mediatop_pll_user"}; -struct samsung_mux_clock top_mux_clks[] __initdata = { +static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX(TOP_MOUT_MEDIATOP_PLL_USER, "mout_mediatop_pll_user", mout_mediatop_pll_user_p, MUX_SEL_TOP_PLL0, 0, 1), @@ -1679,7 +1679,7 @@ struct samsung_mux_clock top_mux_clks[] __initdata = { MUX_SEL_TOP_GSCL, 20, 1), }; -struct samsung_div_clock top_div_clks[] __initdata = { +static struct samsung_div_clock top_div_clks[] __initdata = { DIV(TOP_DOUT_ACLK_G2D_333, "dout_aclk_g2d_333", "mout_aclk_g2d_333", DIV_TOP_G2D_MFC, 0, 3), DIV(TOP_DOUT_ACLK_MFC_333, "dout_aclk_mfc_333", "mout_aclk_mfc_333", @@ -1800,7 +1800,7 @@ struct samsung_div_clock top_div_clks[] __initdata = { }; -struct samsung_gate_clock top_gate_clks[] __initdata = { +static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(TOP_SCLK_MMC0, "sclk_fsys_mmc0_sdclkin", "dout_sclk_fsys_mmc0_sdclkin_b", EN_SCLK_TOP, 7, CLK_SET_RATE_PARENT, 0), @@ -1826,7 +1826,7 @@ static struct samsung_pll_clock top_pll_clks[] __initdata = { static void __init exynos5260_clk_top_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = top_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(top_pll_clks); diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index bea4a173eef5..a1d731ca8f48 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -504,7 +504,7 @@ static struct samsung_fixed_factor_clock FFACTOR(0, "ff_dout_spll2", "mout_sclk_spll", 1, 2, 0), }; -struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { +static struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { MUX(0, "mout_aclk400_isp", mout_group3_5800_p, SRC_TOP0, 0, 3), MUX(0, "mout_aclk400_mscl", mout_group3_5800_p, SRC_TOP0, 4, 3), MUX(0, "mout_aclk400_wcore", mout_group2_5800_p, SRC_TOP0, 16, 3), @@ -553,7 +553,7 @@ struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { MUX(0, "mout_fimd1", mout_group2_p, SRC_DISP10, 4, 3), }; -struct samsung_div_clock exynos5800_div_clks[] __initdata = { +static struct samsung_div_clock exynos5800_div_clks[] __initdata = { DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore", DIV_TOP0, 16, 3), DIV(0, "dout_aclk550_cam", "mout_aclk550_cam", @@ -569,14 +569,14 @@ struct samsung_div_clock exynos5800_div_clks[] __initdata = { DIV(0, "dout_sclk_sw", "sclk_spll", DIV_TOP9, 24, 6), }; -struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { +static struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam", GATE_BUS_TOP, 24, 0, 0), GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", GATE_BUS_TOP, 27, 0, 0), }; -struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { +static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "sclk_bpll", mout_bpll_p, TOP_SPARE2, 0, 1), MUX(0, "mout_aclk400_wcore_bpll", mout_aclk400_wcore_bpll_p, TOP_SPARE2, 4, 1), @@ -606,7 +606,7 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1), }; -struct samsung_div_clock exynos5420_div_clks[] __initdata = { +static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll", DIV_TOP0, 16, 3), }; diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 9e04ae2bb4d7..39c95649d3d0 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -835,6 +835,7 @@ static unsigned long cpif_clk_regs[] __initdata = { MPHY_PLL_CON1, MPHY_PLL_FREQ_DET, MUX_SEL_CPIF0, + DIV_CPIF, ENABLE_SCLK_CPIF, }; @@ -1389,7 +1390,7 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = { /* ENABLE_ACLK_MIF2 */ GATE(CLK_ACLK_MIFND_266, "aclk_mifnd_266", "div_aclk_mif_266", - ENABLE_ACLK_MIF2, 20, 0, 0), + ENABLE_ACLK_MIF2, 20, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_PPMU_DREX1S3, "aclk_ppmu_drex1s3", "div_aclk_drex1", ENABLE_ACLK_MIF2, 17, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_PPMU_DREX1S1, "aclk_ppmu_drex1s1", "div_aclk_drex1", @@ -1832,39 +1833,39 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { /* ENABLE_PCLK_PERIS_SECURE_TZPC */ GATE(CLK_PCLK_TZPC12, "pclk_tzpc12", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 12, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 12, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC11, "pclk_tzpc11", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 11, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 11, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC10, "pclk_tzpc10", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 10, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 10, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC9, "pclk_tzpc9", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 9, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 9, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC8, "pclk_tzpc8", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 8, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 8, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC7, "pclk_tzpc7", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 7, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 7, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC6, "pclk_tzpc6", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 6, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 6, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC5, "pclk_tzpc5", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 5, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 5, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC4, "pclk_tzpc4", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 4, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 4, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC3, "pclk_tzpc3", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 3, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC2, "pclk_tzpc2", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 2, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 2, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC1, "pclk_tzpc1", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 1, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC0, "pclk_tzpc0", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF */ GATE(CLK_PCLK_SECKEY_APBIF, "pclk_seckey_apbif", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF */ GATE(CLK_PCLK_CHIPID_APBIF, "pclk_chipid_apbif", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_TOPRTC */ GATE(CLK_PCLK_TOPRTC, "pclk_toprtc", "aclk_peris_66", @@ -1895,11 +1896,11 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { /* ENABLE_SCLK_PERIS_SECURE_SECKEY */ GATE(CLK_SCLK_SECKEY, "sclk_seckey", "oscclk_efuse_common", - ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, 0, 0), + ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_PERIS_SECURE_CHIPID */ GATE(CLK_SCLK_CHIPID, "sclk_chipid", "oscclk_efuse_common", - ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, 0, 0), + ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_PERIS_SECURE_TOPRTC */ GATE(CLK_SCLK_TOPRTC, "sclk_toprtc", "oscclk_efuse_common", @@ -3286,10 +3287,10 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = { static struct samsung_mux_clock g3d_mux_clks[] __initdata = { /* MUX_SEL_G3D */ - MUX(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, - MUX_SEL_G3D, 8, 1), - MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, - MUX_SEL_G3D, 0, 1), + MUX_F(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, + MUX_SEL_G3D, 8, 1, CLK_SET_RATE_PARENT, 0), + MUX_F(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, + MUX_SEL_G3D, 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock g3d_div_clks[] __initdata = { @@ -3298,8 +3299,8 @@ static struct samsung_div_clock g3d_div_clks[] __initdata = { 8, 2), DIV(CLK_DIV_PCLK_G3D, "div_pclk_g3d", "div_aclk_g3d", DIV_G3D, 4, 3), - DIV(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, - 0, 3), + DIV_F(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, + 0, 3, CLK_SET_RATE_PARENT, 0), }; static struct samsung_gate_clock g3d_gate_clks[] __initdata = { @@ -3309,9 +3310,9 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(CLK_ACLK_BTS_G3D0, "aclk_bts_g3d0", "div_aclk_g3d", ENABLE_ACLK_G3D, 6, 0, 0), GATE(CLK_ACLK_ASYNCAPBS_G3D, "aclk_asyncapbs_g3d", "div_pclk_g3d", - ENABLE_ACLK_G3D, 5, 0, 0), + ENABLE_ACLK_G3D, 5, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_ASYNCAPBM_G3D, "aclk_asyncapbm_g3d", "div_aclk_g3d", - ENABLE_ACLK_G3D, 4, 0, 0), + ENABLE_ACLK_G3D, 4, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_AHB2APB_G3DP, "aclk_ahb2apb_g3dp", "div_pclk_g3d", ENABLE_ACLK_G3D, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G3DNP_150, "aclk_g3dnp_150", "div_pclk_g3d", @@ -3319,7 +3320,7 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(CLK_ACLK_G3DND_600, "aclk_g3dnd_600", "div_aclk_g3d", ENABLE_ACLK_G3D, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G3D, "aclk_g3d", "div_aclk_g3d", - ENABLE_ACLK_G3D, 0, 0, 0), + ENABLE_ACLK_G3D, 0, CLK_SET_RATE_PARENT, 0), /* ENABLE_PCLK_G3D */ GATE(CLK_PCLK_BTS_G3D1, "pclk_bts_g3d1", "div_pclk_g3d", @@ -3582,7 +3583,7 @@ static struct samsung_pll_clock apollo_pll_clks[] __initdata = { static struct samsung_mux_clock apollo_mux_clks[] __initdata = { /* MUX_SEL_APOLLO0 */ MUX_F(CLK_MOUT_APOLLO_PLL, "mout_apollo_pll", mout_apollo_pll_p, - MUX_SEL_APOLLO0, 0, 1, 0, CLK_MUX_READ_ONLY), + MUX_SEL_APOLLO0, 0, 1, CLK_SET_RATE_PARENT, 0), /* MUX_SEL_APOLLO1 */ MUX(CLK_MOUT_BUS_PLL_APOLLO_USER, "mout_bus_pll_apollo_user", @@ -3590,7 +3591,7 @@ static struct samsung_mux_clock apollo_mux_clks[] __initdata = { /* MUX_SEL_APOLLO2 */ MUX_F(CLK_MOUT_APOLLO, "mout_apollo", mout_apollo_p, MUX_SEL_APOLLO2, - 0, 1, 0, CLK_MUX_READ_ONLY), + 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock apollo_div_clks[] __initdata = { @@ -3611,11 +3612,9 @@ static struct samsung_div_clock apollo_div_clks[] __initdata = { DIV_APOLLO0, 8, 3, CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), DIV_F(CLK_DIV_APOLLO2, "div_apollo2", "div_apollo1", - DIV_APOLLO0, 4, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_APOLLO0, 4, 3, CLK_SET_RATE_PARENT, 0), DIV_F(CLK_DIV_APOLLO1, "div_apollo1", "mout_apollo", - DIV_APOLLO0, 0, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_APOLLO0, 0, 3, CLK_SET_RATE_PARENT, 0), /* DIV_APOLLO1 */ DIV_F(CLK_DIV_SCLK_HPM_APOLLO, "div_sclk_hpm_apollo", "mout_apollo", @@ -3666,7 +3665,8 @@ static struct samsung_gate_clock apollo_gate_clks[] __initdata = { GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo", ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo2", - ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0), + ENABLE_SCLK_APOLLO, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), }; static struct samsung_cmu_info apollo_cmu_info __initdata = { @@ -3775,7 +3775,7 @@ static struct samsung_pll_clock atlas_pll_clks[] __initdata = { static struct samsung_mux_clock atlas_mux_clks[] __initdata = { /* MUX_SEL_ATLAS0 */ MUX_F(CLK_MOUT_ATLAS_PLL, "mout_atlas_pll", mout_atlas_pll_p, - MUX_SEL_ATLAS0, 0, 1, 0, CLK_MUX_READ_ONLY), + MUX_SEL_ATLAS0, 0, 1, CLK_SET_RATE_PARENT, 0), /* MUX_SEL_ATLAS1 */ MUX(CLK_MOUT_BUS_PLL_ATLAS_USER, "mout_bus_pll_atlas_user", @@ -3783,7 +3783,7 @@ static struct samsung_mux_clock atlas_mux_clks[] __initdata = { /* MUX_SEL_ATLAS2 */ MUX_F(CLK_MOUT_ATLAS, "mout_atlas", mout_atlas_p, MUX_SEL_ATLAS2, - 0, 1, 0, CLK_MUX_READ_ONLY), + 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock atlas_div_clks[] __initdata = { @@ -3804,11 +3804,9 @@ static struct samsung_div_clock atlas_div_clks[] __initdata = { DIV_ATLAS0, 8, 3, CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), DIV_F(CLK_DIV_ATLAS2, "div_atlas2", "div_atlas1", - DIV_ATLAS0, 4, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_ATLAS0, 4, 3, CLK_SET_RATE_PARENT, 0), DIV_F(CLK_DIV_ATLAS1, "div_atlas1", "mout_atlas", - DIV_ATLAS0, 0, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_ATLAS0, 0, 3, CLK_SET_RATE_PARENT, 0), /* DIV_ATLAS1 */ DIV_F(CLK_DIV_SCLK_HPM_ATLAS, "div_sclk_hpm_atlas", "mout_atlas", @@ -3885,7 +3883,8 @@ static struct samsung_gate_clock atlas_gate_clks[] __initdata = { GATE(CLK_ATCLK, "atclk", "div_atclk_atlas", ENABLE_SCLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_ATLAS, "sclk_atlas", "div_atlas2", - ENABLE_SCLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0), + ENABLE_SCLK_ATLAS, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), }; static struct samsung_cmu_info atlas_cmu_info __initdata = { diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 9d70e5c03804..bebc61b5fce1 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -1156,7 +1156,7 @@ static const struct clk_ops samsung_pll2650xx_clk_min_ops = { }; static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_clk, + const struct samsung_pll_clock *pll_clk, void __iomem *base) { struct samsung_clk_pll *pll; @@ -1303,7 +1303,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, } void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_list, + const struct samsung_pll_clock *pll_list, unsigned int nr_pll, void __iomem *base) { int cnt; diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index f4f29ed6bd25..e56df5064889 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -81,13 +81,13 @@ static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index) return ret; } -const struct clk_ops s3c24xx_clkout_ops = { +static const struct clk_ops s3c24xx_clkout_ops = { .get_parent = s3c24xx_clkout_get_parent, .set_parent = s3c24xx_clkout_set_parent, .determine_rate = __clk_mux_determine_rate, }; -struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, +static struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, const char **parent_names, u8 num_parents, u8 shift, u32 mask) { @@ -404,7 +404,7 @@ static struct s3c24xx_dclk_drv_data dclk_variants[] = { }, }; -static struct platform_device_id s3c24xx_dclk_driver_ids[] = { +static const struct platform_device_id s3c24xx_dclk_driver_ids[] = { { .name = "s3c2410-dclk", .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410], diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c index e668e479a697..cf7e8fa7b624 100644 --- a/drivers/clk/samsung/clk-s5pv210.c +++ b/drivers/clk/samsung/clk-s5pv210.c @@ -169,44 +169,44 @@ static inline void s5pv210_clk_sleep_init(void) { } #endif /* Mux parent lists. */ -static const char *fin_pll_p[] __initdata = { +static const char *const fin_pll_p[] __initconst = { "xxti", "xusbxti" }; -static const char *mout_apll_p[] __initdata = { +static const char *const mout_apll_p[] __initconst = { "fin_pll", "fout_apll" }; -static const char *mout_mpll_p[] __initdata = { +static const char *const mout_mpll_p[] __initconst = { "fin_pll", "fout_mpll" }; -static const char *mout_epll_p[] __initdata = { +static const char *const mout_epll_p[] __initconst = { "fin_pll", "fout_epll" }; -static const char *mout_vpllsrc_p[] __initdata = { +static const char *const mout_vpllsrc_p[] __initconst = { "fin_pll", "sclk_hdmi27m" }; -static const char *mout_vpll_p[] __initdata = { +static const char *const mout_vpll_p[] __initconst = { "mout_vpllsrc", "fout_vpll" }; -static const char *mout_group1_p[] __initdata = { +static const char *const mout_group1_p[] __initconst = { "dout_a2m", "mout_mpll", "mout_epll", "mout_vpll" }; -static const char *mout_group2_p[] __initdata = { +static const char *const mout_group2_p[] __initconst = { "xxti", "xusbxti", "sclk_hdmi27m", @@ -218,7 +218,7 @@ static const char *mout_group2_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio0_p[] __initdata = { +static const char *const mout_audio0_p[] __initconst = { "xxti", "pcmcdclk0", "sclk_hdmi27m", @@ -230,7 +230,7 @@ static const char *mout_audio0_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio1_p[] __initdata = { +static const char *const mout_audio1_p[] __initconst = { "i2scdclk1", "pcmcdclk1", "sclk_hdmi27m", @@ -242,7 +242,7 @@ static const char *mout_audio1_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio2_p[] __initdata = { +static const char *const mout_audio2_p[] __initconst = { "i2scdclk2", "pcmcdclk2", "sclk_hdmi27m", @@ -254,63 +254,63 @@ static const char *mout_audio2_p[] __initdata = { "mout_vpll", }; -static const char *mout_spdif_p[] __initdata = { +static const char *const mout_spdif_p[] __initconst = { "dout_audio0", "dout_audio1", "dout_audio3", }; -static const char *mout_group3_p[] __initdata = { +static const char *const mout_group3_p[] __initconst = { "mout_apll", "mout_mpll" }; -static const char *mout_group4_p[] __initdata = { +static const char *const mout_group4_p[] __initconst = { "mout_mpll", "dout_a2m" }; -static const char *mout_flash_p[] __initdata = { +static const char *const mout_flash_p[] __initconst = { "dout_hclkd", "dout_hclkp" }; -static const char *mout_dac_p[] __initdata = { +static const char *const mout_dac_p[] __initconst = { "mout_vpll", "sclk_hdmiphy" }; -static const char *mout_hdmi_p[] __initdata = { +static const char *const mout_hdmi_p[] __initconst = { "sclk_hdmiphy", "dout_tblk" }; -static const char *mout_mixer_p[] __initdata = { +static const char *const mout_mixer_p[] __initconst = { "mout_dac", "mout_hdmi" }; -static const char *mout_vpll_6442_p[] __initdata = { +static const char *const mout_vpll_6442_p[] __initconst = { "fin_pll", "fout_vpll" }; -static const char *mout_mixer_6442_p[] __initdata = { +static const char *const mout_mixer_6442_p[] __initconst = { "mout_vpll", "dout_mixer" }; -static const char *mout_d0sync_6442_p[] __initdata = { +static const char *const mout_d0sync_6442_p[] __initconst = { "mout_dsys", "div_apll" }; -static const char *mout_d1sync_6442_p[] __initdata = { +static const char *const mout_d1sync_6442_p[] __initconst = { "mout_psys", "div_apll" }; -static const char *mout_group2_6442_p[] __initdata = { +static const char *const mout_group2_6442_p[] __initconst = { "fin_pll", "none", "none", @@ -322,7 +322,7 @@ static const char *mout_group2_6442_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio0_6442_p[] __initdata = { +static const char *const mout_audio0_6442_p[] __initconst = { "fin_pll", "pcmcdclk0", "none", @@ -334,7 +334,7 @@ static const char *mout_audio0_6442_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio1_6442_p[] __initdata = { +static const char *const mout_audio1_6442_p[] __initconst = { "i2scdclk1", "pcmcdclk1", "none", @@ -347,7 +347,7 @@ static const char *mout_audio1_6442_p[] __initdata = { "fin_pll", }; -static const char *mout_clksel_p[] __initdata = { +static const char *const mout_clksel_p[] __initconst = { "fout_apll_clkout", "fout_mpll_clkout", "fout_epll", @@ -370,7 +370,7 @@ static const char *mout_clksel_p[] __initdata = { "div_dclk" }; -static const char *mout_clksel_6442_p[] __initdata = { +static const char *const mout_clksel_6442_p[] __initconst = { "fout_apll_clkout", "fout_mpll_clkout", "fout_epll", @@ -393,7 +393,7 @@ static const char *mout_clksel_6442_p[] __initdata = { "div_dclk" }; -static const char *mout_clkout_p[] __initdata = { +static const char *const mout_clkout_p[] __initconst = { "dout_clkout", "none", "xxti", @@ -401,20 +401,20 @@ static const char *mout_clkout_p[] __initdata = { }; /* Common fixed factor clocks. */ -static struct samsung_fixed_factor_clock ffactor_clks[] __initdata = { +static const struct samsung_fixed_factor_clock ffactor_clks[] __initconst = { FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0), FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0), FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0), }; /* PLL input mux (fin_pll), which needs to be registered before PLLs. */ -static struct samsung_mux_clock early_mux_clks[] __initdata = { +static const struct samsung_mux_clock early_mux_clks[] __initconst = { MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1, CLK_MUX_READ_ONLY, 0), }; /* Common clock muxes. */ -static struct samsung_mux_clock mux_clks[] __initdata = { +static const struct samsung_mux_clock mux_clks[] __initconst = { MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1), MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1), MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1), @@ -427,7 +427,7 @@ static struct samsung_mux_clock mux_clks[] __initdata = { }; /* S5PV210-specific clock muxes. */ -static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { +static const struct samsung_mux_clock s5pv210_mux_clks[] __initconst = { MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1), MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1), @@ -472,7 +472,7 @@ static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { }; /* S5P6442-specific clock muxes. */ -static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { +static const struct samsung_mux_clock s5p6442_mux_clks[] __initconst = { MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1), MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4), @@ -504,7 +504,7 @@ static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { }; /* S5PV210-specific fixed rate clocks generated inside the SoC. */ -static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { +static const struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initconst = { FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), @@ -512,12 +512,12 @@ static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { }; /* S5P6442-specific fixed rate clocks generated inside the SoC. */ -static struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initdata = { +static const struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initconst = { FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 30000000), }; /* Common clock dividers. */ -static struct samsung_div_clock div_clks[] __initdata = { +static const struct samsung_div_clock div_clks[] __initconst = { DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3), DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3), DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3), @@ -549,7 +549,7 @@ static struct samsung_div_clock div_clks[] __initdata = { }; /* S5PV210-specific clock dividers. */ -static struct samsung_div_clock s5pv210_div_clks[] __initdata = { +static const struct samsung_div_clock s5pv210_div_clks[] __initconst = { DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4), DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4), DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3), @@ -578,7 +578,7 @@ static struct samsung_div_clock s5pv210_div_clks[] __initdata = { }; /* S5P6442-specific clock dividers. */ -static struct samsung_div_clock s5p6442_div_clks[] __initdata = { +static const struct samsung_div_clock s5p6442_div_clks[] __initconst = { DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4), DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4), @@ -586,7 +586,7 @@ static struct samsung_div_clock s5p6442_div_clks[] __initdata = { }; /* Common clock gates. */ -static struct samsung_gate_clock gate_clks[] __initdata = { +static const struct samsung_gate_clock gate_clks[] __initconst = { GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0), GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0), GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0), @@ -666,7 +666,7 @@ static struct samsung_gate_clock gate_clks[] __initdata = { }; /* S5PV210-specific clock gates. */ -static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { +static const struct samsung_gate_clock s5pv210_gate_clks[] __initconst = { GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0), GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0), GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), @@ -728,7 +728,7 @@ static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { }; /* S5P6442-specific clock gates. */ -static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { +static const struct samsung_gate_clock s5p6442_gate_clks[] __initconst = { GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0), GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0), GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), @@ -748,14 +748,14 @@ static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { * Clock aliases for legacy clkdev look-up. * NOTE: Needed only to support legacy board files. */ -static struct samsung_clock_alias s5pv210_aliases[] = { +static const struct samsung_clock_alias s5pv210_aliases[] __initconst = { ALIAS(DOUT_APLL, NULL, "armclk"), ALIAS(DOUT_HCLKM, NULL, "hclk_msys"), ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"), }; /* S5PV210-specific PLLs. */ -static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { +static const struct samsung_pll_clock s5pv210_pll_clks[] __initconst = { [apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", @@ -767,7 +767,7 @@ static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { }; /* S5P6442-specific PLLs. */ -static struct samsung_pll_clock s5p6442_pll_clks[] __initdata = { +static const struct samsung_pll_clock s5p6442_pll_clks[] __initconst = { [apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 9e1f88c04fd4..0117238391d6 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -98,7 +98,7 @@ void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, /* register a list of aliases */ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, - struct samsung_clock_alias *list, + const struct samsung_clock_alias *list, unsigned int nr_clk) { struct clk *clk; @@ -132,7 +132,8 @@ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, /* register a list of fixed clocks */ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, - struct samsung_fixed_rate_clock *list, unsigned int nr_clk) + const struct samsung_fixed_rate_clock *list, + unsigned int nr_clk) { struct clk *clk; unsigned int idx, ret; @@ -161,7 +162,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, /* register a list of fixed factor clocks */ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, - struct samsung_fixed_factor_clock *list, unsigned int nr_clk) + const struct samsung_fixed_factor_clock *list, unsigned int nr_clk) { struct clk *clk; unsigned int idx; @@ -181,7 +182,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, /* register a list of mux clocks */ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, - struct samsung_mux_clock *list, + const struct samsung_mux_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -213,7 +214,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, /* register a list of div clocks */ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, - struct samsung_div_clock *list, + const struct samsung_div_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -252,7 +253,7 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, /* register a list of gate clocks */ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, - struct samsung_gate_clock *list, + const struct samsung_gate_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -389,7 +390,7 @@ struct samsung_clk_provider * __init samsung_cmu_register_one( ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids); if (!ctx) { - panic("%s: unable to alllocate ctx\n", __func__); + panic("%s: unable to allocate ctx\n", __func__); return ctx; } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index e4c75383cea7..b775fc29caa5 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -121,7 +121,7 @@ struct samsung_mux_clock { unsigned int id; const char *dev_name; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; unsigned long offset; @@ -368,28 +368,28 @@ extern void __init samsung_clk_of_register_fixed_ext( extern void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, unsigned int id); -extern void samsung_clk_register_alias(struct samsung_clk_provider *ctx, - struct samsung_clock_alias *list, +extern void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, + const struct samsung_clock_alias *list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_rate( struct samsung_clk_provider *ctx, - struct samsung_fixed_rate_clock *clk_list, + const struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_factor( struct samsung_clk_provider *ctx, - struct samsung_fixed_factor_clock *list, + const struct samsung_fixed_factor_clock *list, unsigned int nr_clk); extern void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, - struct samsung_mux_clock *clk_list, + const struct samsung_mux_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, - struct samsung_div_clock *clk_list, + const struct samsung_div_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, - struct samsung_gate_clock *clk_list, + const struct samsung_gate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_list, + const struct samsung_pll_clock *pll_list, unsigned int nr_clk, void __iomem *base); extern struct samsung_clk_provider __init *samsung_cmu_register_one( diff --git a/drivers/clk/shmobile/clk-emev2.c b/drivers/clk/shmobile/clk-emev2.c index 6c7c929c7765..5b60beb7d0eb 100644 --- a/drivers/clk/shmobile/clk-emev2.c +++ b/drivers/clk/shmobile/clk-emev2.c @@ -34,7 +34,7 @@ static DEFINE_SPINLOCK(lock); /* not pretty, but hey */ -void __iomem *smu_base; +static void __iomem *smu_base; static void __init emev2_smu_write(unsigned long value, int offs) { diff --git a/drivers/clk/sirf/Makefile b/drivers/clk/sirf/Makefile index 36b8e203f6e7..09b4210d9124 100644 --- a/drivers/clk/sirf/Makefile +++ b/drivers/clk/sirf/Makefile @@ -2,4 +2,4 @@ # Makefile for sirf specific clk # -obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o +obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o clk-atlas7.o diff --git a/drivers/clk/sirf/clk-atlas7.c b/drivers/clk/sirf/clk-atlas7.c new file mode 100644 index 000000000000..db8ab691dbf6 --- /dev/null +++ b/drivers/clk/sirf/clk-atlas7.c @@ -0,0 +1,1632 @@ +/* + * Clock tree for CSR SiRFAtlas7 + * + * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/of_address.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> + +#define SIRFSOC_CLKC_MEMPLL_AB_FREQ 0x0000 +#define SIRFSOC_CLKC_MEMPLL_AB_SSC 0x0004 +#define SIRFSOC_CLKC_MEMPLL_AB_CTRL0 0x0008 +#define SIRFSOC_CLKC_MEMPLL_AB_CTRL1 0x000c +#define SIRFSOC_CLKC_MEMPLL_AB_STATUS 0x0010 +#define SIRFSOC_CLKC_MEMPLL_AB_SSRAM_ADDR 0x0014 +#define SIRFSOC_CLKC_MEMPLL_AB_SSRAM_DATA 0x0018 + +#define SIRFSOC_CLKC_CPUPLL_AB_FREQ 0x001c +#define SIRFSOC_CLKC_CPUPLL_AB_SSC 0x0020 +#define SIRFSOC_CLKC_CPUPLL_AB_CTRL0 0x0024 +#define SIRFSOC_CLKC_CPUPLL_AB_CTRL1 0x0028 +#define SIRFSOC_CLKC_CPUPLL_AB_STATUS 0x002c + +#define SIRFSOC_CLKC_SYS0PLL_AB_FREQ 0x0030 +#define SIRFSOC_CLKC_SYS0PLL_AB_SSC 0x0034 +#define SIRFSOC_CLKC_SYS0PLL_AB_CTRL0 0x0038 +#define SIRFSOC_CLKC_SYS0PLL_AB_CTRL1 0x003c +#define SIRFSOC_CLKC_SYS0PLL_AB_STATUS 0x0040 + +#define SIRFSOC_CLKC_SYS1PLL_AB_FREQ 0x0044 +#define SIRFSOC_CLKC_SYS1PLL_AB_SSC 0x0048 +#define SIRFSOC_CLKC_SYS1PLL_AB_CTRL0 0x004c +#define SIRFSOC_CLKC_SYS1PLL_AB_CTRL1 0x0050 +#define SIRFSOC_CLKC_SYS1PLL_AB_STATUS 0x0054 + +#define SIRFSOC_CLKC_SYS2PLL_AB_FREQ 0x0058 +#define SIRFSOC_CLKC_SYS2PLL_AB_SSC 0x005c +#define SIRFSOC_CLKC_SYS2PLL_AB_CTRL0 0x0060 +#define SIRFSOC_CLKC_SYS2PLL_AB_CTRL1 0x0064 +#define SIRFSOC_CLKC_SYS2PLL_AB_STATUS 0x0068 + +#define SIRFSOC_CLKC_SYS3PLL_AB_FREQ 0x006c +#define SIRFSOC_CLKC_SYS3PLL_AB_SSC 0x0070 +#define SIRFSOC_CLKC_SYS3PLL_AB_CTRL0 0x0074 +#define SIRFSOC_CLKC_SYS3PLL_AB_CTRL1 0x0078 +#define SIRFSOC_CLKC_SYS3PLL_AB_STATUS 0x007c + +#define SIRFSOC_ABPLL_CTRL0_SSEN 0x00001000 +#define SIRFSOC_ABPLL_CTRL0_BYPASS 0x00000010 +#define SIRFSOC_ABPLL_CTRL0_RESET 0x00000001 + +#define SIRFSOC_CLKC_AUDIO_DTO_INC 0x0088 +#define SIRFSOC_CLKC_DISP0_DTO_INC 0x008c +#define SIRFSOC_CLKC_DISP1_DTO_INC 0x0090 + +#define SIRFSOC_CLKC_AUDIO_DTO_SRC 0x0094 +#define SIRFSOC_CLKC_AUDIO_DTO_ENA 0x0098 +#define SIRFSOC_CLKC_AUDIO_DTO_DROFF 0x009c + +#define SIRFSOC_CLKC_DISP0_DTO_SRC 0x00a0 +#define SIRFSOC_CLKC_DISP0_DTO_ENA 0x00a4 +#define SIRFSOC_CLKC_DISP0_DTO_DROFF 0x00a8 + +#define SIRFSOC_CLKC_DISP1_DTO_SRC 0x00ac +#define SIRFSOC_CLKC_DISP1_DTO_ENA 0x00b0 +#define SIRFSOC_CLKC_DISP1_DTO_DROFF 0x00b4 + +#define SIRFSOC_CLKC_I2S_CLK_SEL 0x00b8 +#define SIRFSOC_CLKC_I2S_SEL_STAT 0x00bc + +#define SIRFSOC_CLKC_USBPHY_CLKDIV_CFG 0x00c0 +#define SIRFSOC_CLKC_USBPHY_CLKDIV_ENA 0x00c4 +#define SIRFSOC_CLKC_USBPHY_CLK_SEL 0x00c8 +#define SIRFSOC_CLKC_USBPHY_CLK_SEL_STAT 0x00cc + +#define SIRFSOC_CLKC_BTSS_CLKDIV_CFG 0x00d0 +#define SIRFSOC_CLKC_BTSS_CLKDIV_ENA 0x00d4 +#define SIRFSOC_CLKC_BTSS_CLK_SEL 0x00d8 +#define SIRFSOC_CLKC_BTSS_CLK_SEL_STAT 0x00dc + +#define SIRFSOC_CLKC_RGMII_CLKDIV_CFG 0x00e0 +#define SIRFSOC_CLKC_RGMII_CLKDIV_ENA 0x00e4 +#define SIRFSOC_CLKC_RGMII_CLK_SEL 0x00e8 +#define SIRFSOC_CLKC_RGMII_CLK_SEL_STAT 0x00ec + +#define SIRFSOC_CLKC_CPU_CLKDIV_CFG 0x00f0 +#define SIRFSOC_CLKC_CPU_CLKDIV_ENA 0x00f4 +#define SIRFSOC_CLKC_CPU_CLK_SEL 0x00f8 +#define SIRFSOC_CLKC_CPU_CLK_SEL_STAT 0x00fc + +#define SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG 0x0100 +#define SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA 0x0104 +#define SIRFSOC_CLKC_SDPHY01_CLK_SEL 0x0108 +#define SIRFSOC_CLKC_SDPHY01_CLK_SEL_STAT 0x010c + +#define SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG 0x0110 +#define SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA 0x0114 +#define SIRFSOC_CLKC_SDPHY23_CLK_SEL 0x0118 +#define SIRFSOC_CLKC_SDPHY23_CLK_SEL_STAT 0x011c + +#define SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG 0x0120 +#define SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA 0x0124 +#define SIRFSOC_CLKC_SDPHY45_CLK_SEL 0x0128 +#define SIRFSOC_CLKC_SDPHY45_CLK_SEL_STAT 0x012c + +#define SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG 0x0130 +#define SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA 0x0134 +#define SIRFSOC_CLKC_SDPHY67_CLK_SEL 0x0138 +#define SIRFSOC_CLKC_SDPHY67_CLK_SEL_STAT 0x013c + +#define SIRFSOC_CLKC_CAN_CLKDIV_CFG 0x0140 +#define SIRFSOC_CLKC_CAN_CLKDIV_ENA 0x0144 +#define SIRFSOC_CLKC_CAN_CLK_SEL 0x0148 +#define SIRFSOC_CLKC_CAN_CLK_SEL_STAT 0x014c + +#define SIRFSOC_CLKC_DEINT_CLKDIV_CFG 0x0150 +#define SIRFSOC_CLKC_DEINT_CLKDIV_ENA 0x0154 +#define SIRFSOC_CLKC_DEINT_CLK_SEL 0x0158 +#define SIRFSOC_CLKC_DEINT_CLK_SEL_STAT 0x015c + +#define SIRFSOC_CLKC_NAND_CLKDIV_CFG 0x0160 +#define SIRFSOC_CLKC_NAND_CLKDIV_ENA 0x0164 +#define SIRFSOC_CLKC_NAND_CLK_SEL 0x0168 +#define SIRFSOC_CLKC_NAND_CLK_SEL_STAT 0x016c + +#define SIRFSOC_CLKC_DISP0_CLKDIV_CFG 0x0170 +#define SIRFSOC_CLKC_DISP0_CLKDIV_ENA 0x0174 +#define SIRFSOC_CLKC_DISP0_CLK_SEL 0x0178 +#define SIRFSOC_CLKC_DISP0_CLK_SEL_STAT 0x017c + +#define SIRFSOC_CLKC_DISP1_CLKDIV_CFG 0x0180 +#define SIRFSOC_CLKC_DISP1_CLKDIV_ENA 0x0184 +#define SIRFSOC_CLKC_DISP1_CLK_SEL 0x0188 +#define SIRFSOC_CLKC_DISP1_CLK_SEL_STAT 0x018c + +#define SIRFSOC_CLKC_GPU_CLKDIV_CFG 0x0190 +#define SIRFSOC_CLKC_GPU_CLKDIV_ENA 0x0194 +#define SIRFSOC_CLKC_GPU_CLK_SEL 0x0198 +#define SIRFSOC_CLKC_GPU_CLK_SEL_STAT 0x019c + +#define SIRFSOC_CLKC_GNSS_CLKDIV_CFG 0x01a0 +#define SIRFSOC_CLKC_GNSS_CLKDIV_ENA 0x01a4 +#define SIRFSOC_CLKC_GNSS_CLK_SEL 0x01a8 +#define SIRFSOC_CLKC_GNSS_CLK_SEL_STAT 0x01ac + +#define SIRFSOC_CLKC_SHARED_DIVIDER_CFG0 0x01b0 +#define SIRFSOC_CLKC_SHARED_DIVIDER_CFG1 0x01b4 +#define SIRFSOC_CLKC_SHARED_DIVIDER_ENA 0x01b8 + +#define SIRFSOC_CLKC_SYS_CLK_SEL 0x01bc +#define SIRFSOC_CLKC_SYS_CLK_SEL_STAT 0x01c0 +#define SIRFSOC_CLKC_IO_CLK_SEL 0x01c4 +#define SIRFSOC_CLKC_IO_CLK_SEL_STAT 0x01c8 +#define SIRFSOC_CLKC_G2D_CLK_SEL 0x01cc +#define SIRFSOC_CLKC_G2D_CLK_SEL_STAT 0x01d0 +#define SIRFSOC_CLKC_JPENC_CLK_SEL 0x01d4 +#define SIRFSOC_CLKC_JPENC_CLK_SEL_STAT 0x01d8 +#define SIRFSOC_CLKC_VDEC_CLK_SEL 0x01dc +#define SIRFSOC_CLKC_VDEC_CLK_SEL_STAT 0x01e0 +#define SIRFSOC_CLKC_GMAC_CLK_SEL 0x01e4 +#define SIRFSOC_CLKC_GMAC_CLK_SEL_STAT 0x01e8 +#define SIRFSOC_CLKC_USB_CLK_SEL 0x01ec +#define SIRFSOC_CLKC_USB_CLK_SEL_STAT 0x01f0 +#define SIRFSOC_CLKC_KAS_CLK_SEL 0x01f4 +#define SIRFSOC_CLKC_KAS_CLK_SEL_STAT 0x01f8 +#define SIRFSOC_CLKC_SEC_CLK_SEL 0x01fc +#define SIRFSOC_CLKC_SEC_CLK_SEL_STAT 0x0200 +#define SIRFSOC_CLKC_SDR_CLK_SEL 0x0204 +#define SIRFSOC_CLKC_SDR_CLK_SEL_STAT 0x0208 +#define SIRFSOC_CLKC_VIP_CLK_SEL 0x020c +#define SIRFSOC_CLKC_VIP_CLK_SEL_STAT 0x0210 +#define SIRFSOC_CLKC_NOCD_CLK_SEL 0x0214 +#define SIRFSOC_CLKC_NOCD_CLK_SEL_STAT 0x0218 +#define SIRFSOC_CLKC_NOCR_CLK_SEL 0x021c +#define SIRFSOC_CLKC_NOCR_CLK_SEL_STAT 0x0220 +#define SIRFSOC_CLKC_TPIU_CLK_SEL 0x0224 +#define SIRFSOC_CLKC_TPIU_CLK_SEL_STAT 0x0228 + +#define SIRFSOC_CLKC_ROOT_CLK_EN0_SET 0x022c +#define SIRFSOC_CLKC_ROOT_CLK_EN0_CLR 0x0230 +#define SIRFSOC_CLKC_ROOT_CLK_EN0_STAT 0x0234 +#define SIRFSOC_CLKC_ROOT_CLK_EN1_SET 0x0238 +#define SIRFSOC_CLKC_ROOT_CLK_EN1_CLR 0x023c +#define SIRFSOC_CLKC_ROOT_CLK_EN1_STAT 0x0240 + +#define SIRFSOC_CLKC_LEAF_CLK_EN0_SET 0x0244 +#define SIRFSOC_CLKC_LEAF_CLK_EN0_CLR 0x0248 +#define SIRFSOC_CLKC_LEAF_CLK_EN0_STAT 0x024c + +#define SIRFSOC_CLKC_RSTC_A7_SW_RST 0x0308 + +#define SIRFSOC_CLKC_LEAF_CLK_EN1_SET 0x04a0 +#define SIRFSOC_CLKC_LEAF_CLK_EN2_SET 0x04b8 +#define SIRFSOC_CLKC_LEAF_CLK_EN3_SET 0x04d0 +#define SIRFSOC_CLKC_LEAF_CLK_EN4_SET 0x04e8 +#define SIRFSOC_CLKC_LEAF_CLK_EN5_SET 0x0500 +#define SIRFSOC_CLKC_LEAF_CLK_EN6_SET 0x0518 +#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 }, +}; + +struct clk_pll { + struct clk_hw hw; + u16 regofs; /* register offset */ +}; +#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw) + +struct clk_dto { + struct clk_hw hw; + u16 inc_offset; /* dto increment offset */ + u16 src_offset; /* dto src offset */ +}; +#define to_dtoclk(_hw) container_of(_hw, struct clk_dto, hw) + +struct clk_unit { + struct clk_hw hw; + u16 regofs; + u16 bit; + spinlock_t *lock; +}; +#define to_unitclk(_hw) container_of(_hw, struct clk_unit, hw) + +struct atlas7_div_init_data { + const char *div_name; + const char *parent_name; + const char *gate_name; + unsigned long flags; + u8 divider_flags; + u8 gate_flags; + u32 div_offset; + u8 shift; + u8 width; + u32 gate_offset; + u8 gate_bit; + spinlock_t *lock; +}; + +struct atlas7_mux_init_data { + const char *mux_name; + const char * const *parent_names; + u8 parent_num; + unsigned long flags; + u8 mux_flags; + u32 mux_offset; + u8 shift; + u8 width; +}; + +struct atlas7_unit_init_data { + u32 index; + const char *unit_name; + const char *parent_name; + unsigned long flags; + u32 regofs; + u8 bit; + spinlock_t *lock; +}; + +struct atlas7_reset_desc { + const char *name; + u32 clk_ofs; + u8 clk_bit; + u32 rst_ofs; + u8 rst_bit; + spinlock_t *lock; +}; + +static DEFINE_SPINLOCK(cpupll_ctrl1_lock); +static DEFINE_SPINLOCK(mempll_ctrl1_lock); +static DEFINE_SPINLOCK(sys0pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys1pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys2pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys3pll_ctrl1_lock); +static DEFINE_SPINLOCK(usbphy_div_lock); +static DEFINE_SPINLOCK(btss_div_lock); +static DEFINE_SPINLOCK(rgmii_div_lock); +static DEFINE_SPINLOCK(cpu_div_lock); +static DEFINE_SPINLOCK(sdphy01_div_lock); +static DEFINE_SPINLOCK(sdphy23_div_lock); +static DEFINE_SPINLOCK(sdphy45_div_lock); +static DEFINE_SPINLOCK(sdphy67_div_lock); +static DEFINE_SPINLOCK(can_div_lock); +static DEFINE_SPINLOCK(deint_div_lock); +static DEFINE_SPINLOCK(nand_div_lock); +static DEFINE_SPINLOCK(disp0_div_lock); +static DEFINE_SPINLOCK(disp1_div_lock); +static DEFINE_SPINLOCK(gpu_div_lock); +static DEFINE_SPINLOCK(gnss_div_lock); +/* gate register shared */ +static DEFINE_SPINLOCK(share_div_lock); +static DEFINE_SPINLOCK(root0_gate_lock); +static DEFINE_SPINLOCK(root1_gate_lock); +static DEFINE_SPINLOCK(leaf0_gate_lock); +static DEFINE_SPINLOCK(leaf1_gate_lock); +static DEFINE_SPINLOCK(leaf2_gate_lock); +static DEFINE_SPINLOCK(leaf3_gate_lock); +static DEFINE_SPINLOCK(leaf4_gate_lock); +static DEFINE_SPINLOCK(leaf5_gate_lock); +static DEFINE_SPINLOCK(leaf6_gate_lock); +static DEFINE_SPINLOCK(leaf7_gate_lock); +static DEFINE_SPINLOCK(leaf8_gate_lock); + +static inline unsigned long clkc_readl(unsigned reg) +{ + return readl(sirfsoc_clk_vbase + reg); +} + +static inline void clkc_writel(u32 val, unsigned reg) +{ + writel(val, sirfsoc_clk_vbase + reg); +} + +/* +* ABPLL +* integer mode: Fvco = Fin * 2 * NF / NR +* Spread Spectrum mode: Fvco = Fin * SSN / NR +* SSN = 2^24 / (256 * ((ssdiv >> ssdepth) << ssdepth) + (ssmod << ssdepth)) +*/ +static unsigned long pll_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long fin = parent_rate; + struct clk_pll *clk = to_pllclk(hw); + u64 rate; + u32 regctrl0 = clkc_readl(clk->regofs + SIRFSOC_CLKC_MEMPLL_AB_CTRL0 - + SIRFSOC_CLKC_MEMPLL_AB_FREQ); + u32 regfreq = clkc_readl(clk->regofs); + u32 regssc = clkc_readl(clk->regofs + SIRFSOC_CLKC_MEMPLL_AB_SSC - + SIRFSOC_CLKC_MEMPLL_AB_FREQ); + u32 nr = (regfreq >> 16 & (BIT(3) - 1)) + 1; + u32 nf = (regfreq & (BIT(9) - 1)) + 1; + u32 ssdiv = regssc >> 8 & (BIT(12) - 1); + u32 ssdepth = regssc >> 20 & (BIT(2) - 1); + u32 ssmod = regssc & (BIT(8) - 1); + + if (regctrl0 & SIRFSOC_ABPLL_CTRL0_BYPASS) + return fin; + + if (regctrl0 & SIRFSOC_ABPLL_CTRL0_SSEN) { + rate = fin; + rate *= 1 << 24; + do_div(rate, (256 * ((ssdiv >> ssdepth) << ssdepth) + + (ssmod << ssdepth))); + } else { + rate = 2 * fin; + rate *= nf; + do_div(rate, nr); + } + return rate; +} + +static const struct clk_ops ab_pll_ops = { + .recalc_rate = pll_clk_recalc_rate, +}; + +static const char * const pll_clk_parents[] = { + "xin", +}; + +static struct clk_init_data clk_cpupll_init = { + .name = "cpupll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_cpupll = { + .regofs = SIRFSOC_CLKC_CPUPLL_AB_FREQ, + .hw = { + .init = &clk_cpupll_init, + }, +}; + +static struct clk_init_data clk_mempll_init = { + .name = "mempll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_mempll = { + .regofs = SIRFSOC_CLKC_MEMPLL_AB_FREQ, + .hw = { + .init = &clk_mempll_init, + }, +}; + +static struct clk_init_data clk_sys0pll_init = { + .name = "sys0pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys0pll = { + .regofs = SIRFSOC_CLKC_SYS0PLL_AB_FREQ, + .hw = { + .init = &clk_sys0pll_init, + }, +}; + +static struct clk_init_data clk_sys1pll_init = { + .name = "sys1pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys1pll = { + .regofs = SIRFSOC_CLKC_SYS1PLL_AB_FREQ, + .hw = { + .init = &clk_sys1pll_init, + }, +}; + +static struct clk_init_data clk_sys2pll_init = { + .name = "sys2pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys2pll = { + .regofs = SIRFSOC_CLKC_SYS2PLL_AB_FREQ, + .hw = { + .init = &clk_sys2pll_init, + }, +}; + +static struct clk_init_data clk_sys3pll_init = { + .name = "sys3pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys3pll = { + .regofs = SIRFSOC_CLKC_SYS3PLL_AB_FREQ, + .hw = { + .init = &clk_sys3pll_init, + }, +}; + +/* + * DTO in clkc, default enable double resolution mode + * double resolution mode:fout = fin * finc / 2^29 + * normal mode:fout = fin * finc / 2^28 + */ +static int dto_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_dto *clk = to_dtoclk(hw); + int reg; + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + return !!(clkc_readl(reg) & BIT(0)); +} + +static int dto_clk_enable(struct clk_hw *hw) +{ + u32 val, reg; + struct clk_dto *clk = to_dtoclk(hw); + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + val = clkc_readl(reg) | BIT(0); + clkc_writel(val, reg); + return 0; +} + +static void dto_clk_disable(struct clk_hw *hw) +{ + u32 val, reg; + struct clk_dto *clk = to_dtoclk(hw); + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + val = clkc_readl(reg) & ~BIT(0); + clkc_writel(val, reg); +} + +static unsigned long dto_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u64 rate = parent_rate; + struct clk_dto *clk = to_dtoclk(hw); + u32 finc = clkc_readl(clk->inc_offset); + u32 droff = clkc_readl(clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_DROFF - SIRFSOC_CLKC_AUDIO_DTO_SRC); + + rate *= finc; + if (droff & BIT(0)) + /* Double resolution off */ + do_div(rate, 1 << 28); + else + do_div(rate, 1 << 29); + + return rate; +} + +static long dto_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u64 dividend = rate * (1 << 29); + + do_div(dividend, *parent_rate); + dividend *= *parent_rate; + do_div(dividend, 1 << 29); + + return dividend; +} + +static int dto_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u64 dividend = rate * (1 << 29); + struct clk_dto *clk = to_dtoclk(hw); + + do_div(dividend, parent_rate); + clkc_writel(0, clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_DROFF - SIRFSOC_CLKC_AUDIO_DTO_SRC); + clkc_writel(dividend, clk->inc_offset); + + return 0; +} + +static u8 dto_clk_get_parent(struct clk_hw *hw) +{ + struct clk_dto *clk = to_dtoclk(hw); + + return clkc_readl(clk->src_offset); +} + +/* + * dto need CLK_SET_PARENT_GATE + */ +static int dto_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_dto *clk = to_dtoclk(hw); + + clkc_writel(index, clk->src_offset); + return 0; +} + +static const struct clk_ops dto_ops = { + .is_enabled = dto_clk_is_enabled, + .enable = dto_clk_enable, + .disable = dto_clk_disable, + .recalc_rate = dto_clk_recalc_rate, + .round_rate = dto_clk_round_rate, + .set_rate = dto_clk_set_rate, + .get_parent = dto_clk_get_parent, + .set_parent = dto_clk_set_parent, +}; + +/* dto parent clock as syspllvco/clk1 */ +static const char * const audiodto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_audiodto_init = { + .name = "audio_dto", + .ops = &dto_ops, + .parent_names = audiodto_clk_parents, + .num_parents = ARRAY_SIZE(audiodto_clk_parents), +}; + +static struct clk_dto clk_audio_dto = { + .inc_offset = SIRFSOC_CLKC_AUDIO_DTO_INC, + .src_offset = SIRFSOC_CLKC_AUDIO_DTO_SRC, + .hw = { + .init = &clk_audiodto_init, + }, +}; + +static const char * const disp0dto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_disp0dto_init = { + .name = "disp0_dto", + .ops = &dto_ops, + .parent_names = disp0dto_clk_parents, + .num_parents = ARRAY_SIZE(disp0dto_clk_parents), +}; + +static struct clk_dto clk_disp0_dto = { + .inc_offset = SIRFSOC_CLKC_DISP0_DTO_INC, + .src_offset = SIRFSOC_CLKC_DISP0_DTO_SRC, + .hw = { + .init = &clk_disp0dto_init, + }, +}; + +static const char * const disp1dto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_disp1dto_init = { + .name = "disp1_dto", + .ops = &dto_ops, + .parent_names = disp1dto_clk_parents, + .num_parents = ARRAY_SIZE(disp1dto_clk_parents), +}; + +static struct clk_dto clk_disp1_dto = { + .inc_offset = SIRFSOC_CLKC_DISP1_DTO_INC, + .src_offset = SIRFSOC_CLKC_DISP1_DTO_SRC, + .hw = { + .init = &clk_disp1dto_init, + }, +}; + +static struct atlas7_div_init_data divider_list[] __initdata = { + /* div_name, parent_name, gate_name, clk_flag, divider_flag, gate_flag, div_offset, shift, wdith, gate_offset, bit_enable, lock */ + { "sys0pll_qa1", "sys0pll_fixdiv", "sys0pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 0, &usbphy_div_lock }, + { "sys1pll_qa1", "sys1pll_fixdiv", "sys1pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 4, &usbphy_div_lock }, + { "sys2pll_qa1", "sys2pll_fixdiv", "sys2pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 8, &usbphy_div_lock }, + { "sys3pll_qa1", "sys3pll_fixdiv", "sys3pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 12, &usbphy_div_lock }, + { "sys0pll_qa2", "sys0pll_fixdiv", "sys0pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 0, &btss_div_lock }, + { "sys1pll_qa2", "sys1pll_fixdiv", "sys1pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 4, &btss_div_lock }, + { "sys2pll_qa2", "sys2pll_fixdiv", "sys2pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 8, &btss_div_lock }, + { "sys3pll_qa2", "sys3pll_fixdiv", "sys3pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 12, &btss_div_lock }, + { "sys0pll_qa3", "sys0pll_fixdiv", "sys0pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 0, &rgmii_div_lock }, + { "sys1pll_qa3", "sys1pll_fixdiv", "sys1pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 4, &rgmii_div_lock }, + { "sys2pll_qa3", "sys2pll_fixdiv", "sys2pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 8, &rgmii_div_lock }, + { "sys3pll_qa3", "sys3pll_fixdiv", "sys3pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 12, &rgmii_div_lock }, + { "sys0pll_qa4", "sys0pll_fixdiv", "sys0pll_a4", 0, 0, 0, SIRFSOC_CLKC_CPU_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_CPU_CLKDIV_ENA, 0, &cpu_div_lock }, + { "sys1pll_qa4", "sys1pll_fixdiv", "sys1pll_a4", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_CPU_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_CPU_CLKDIV_ENA, 4, &cpu_div_lock }, + { "sys0pll_qa5", "sys0pll_fixdiv", "sys0pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 0, &sdphy01_div_lock }, + { "sys1pll_qa5", "sys1pll_fixdiv", "sys1pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 4, &sdphy01_div_lock }, + { "sys2pll_qa5", "sys2pll_fixdiv", "sys2pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 8, &sdphy01_div_lock }, + { "sys3pll_qa5", "sys3pll_fixdiv", "sys3pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 12, &sdphy01_div_lock }, + { "sys0pll_qa6", "sys0pll_fixdiv", "sys0pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 0, &sdphy23_div_lock }, + { "sys1pll_qa6", "sys1pll_fixdiv", "sys1pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 4, &sdphy23_div_lock }, + { "sys2pll_qa6", "sys2pll_fixdiv", "sys2pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 8, &sdphy23_div_lock }, + { "sys3pll_qa6", "sys3pll_fixdiv", "sys3pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 12, &sdphy23_div_lock }, + { "sys0pll_qa7", "sys0pll_fixdiv", "sys0pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 0, &sdphy45_div_lock }, + { "sys1pll_qa7", "sys1pll_fixdiv", "sys1pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 4, &sdphy45_div_lock }, + { "sys2pll_qa7", "sys2pll_fixdiv", "sys2pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 8, &sdphy45_div_lock }, + { "sys3pll_qa7", "sys3pll_fixdiv", "sys3pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 12, &sdphy45_div_lock }, + { "sys0pll_qa8", "sys0pll_fixdiv", "sys0pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 0, &sdphy67_div_lock }, + { "sys1pll_qa8", "sys1pll_fixdiv", "sys1pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 4, &sdphy67_div_lock }, + { "sys2pll_qa8", "sys2pll_fixdiv", "sys2pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 8, &sdphy67_div_lock }, + { "sys3pll_qa8", "sys3pll_fixdiv", "sys3pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 12, &sdphy67_div_lock }, + { "sys0pll_qa9", "sys0pll_fixdiv", "sys0pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 0, &can_div_lock }, + { "sys1pll_qa9", "sys1pll_fixdiv", "sys1pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 4, &can_div_lock }, + { "sys2pll_qa9", "sys2pll_fixdiv", "sys2pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 8, &can_div_lock }, + { "sys3pll_qa9", "sys3pll_fixdiv", "sys3pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 12, &can_div_lock }, + { "sys0pll_qa10", "sys0pll_fixdiv", "sys0pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 0, &deint_div_lock }, + { "sys1pll_qa10", "sys1pll_fixdiv", "sys1pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 4, &deint_div_lock }, + { "sys2pll_qa10", "sys2pll_fixdiv", "sys2pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 8, &deint_div_lock }, + { "sys3pll_qa10", "sys3pll_fixdiv", "sys3pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 12, &deint_div_lock }, + { "sys0pll_qa11", "sys0pll_fixdiv", "sys0pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 0, &nand_div_lock }, + { "sys1pll_qa11", "sys1pll_fixdiv", "sys1pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 4, &nand_div_lock }, + { "sys2pll_qa11", "sys2pll_fixdiv", "sys2pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 8, &nand_div_lock }, + { "sys3pll_qa11", "sys3pll_fixdiv", "sys3pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 12, &nand_div_lock }, + { "sys0pll_qa12", "sys0pll_fixdiv", "sys0pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 0, &disp0_div_lock }, + { "sys1pll_qa12", "sys1pll_fixdiv", "sys1pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 4, &disp0_div_lock }, + { "sys2pll_qa12", "sys2pll_fixdiv", "sys2pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 8, &disp0_div_lock }, + { "sys3pll_qa12", "sys3pll_fixdiv", "sys3pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 12, &disp0_div_lock }, + { "sys0pll_qa13", "sys0pll_fixdiv", "sys0pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 0, &disp1_div_lock }, + { "sys1pll_qa13", "sys1pll_fixdiv", "sys1pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 4, &disp1_div_lock }, + { "sys2pll_qa13", "sys2pll_fixdiv", "sys2pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 8, &disp1_div_lock }, + { "sys3pll_qa13", "sys3pll_fixdiv", "sys3pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 12, &disp1_div_lock }, + { "sys0pll_qa14", "sys0pll_fixdiv", "sys0pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 0, &gpu_div_lock }, + { "sys1pll_qa14", "sys1pll_fixdiv", "sys1pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 4, &gpu_div_lock }, + { "sys2pll_qa14", "sys2pll_fixdiv", "sys2pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 8, &gpu_div_lock }, + { "sys3pll_qa14", "sys3pll_fixdiv", "sys3pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 12, &gpu_div_lock }, + { "sys0pll_qa15", "sys0pll_fixdiv", "sys0pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 0, &gnss_div_lock }, + { "sys1pll_qa15", "sys1pll_fixdiv", "sys1pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 4, &gnss_div_lock }, + { "sys2pll_qa15", "sys2pll_fixdiv", "sys2pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 8, &gnss_div_lock }, + { "sys3pll_qa15", "sys3pll_fixdiv", "sys3pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 12, &gnss_div_lock }, + { "sys1pll_qa18", "sys1pll_fixdiv", "sys1pll_a18", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 24, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 12, &share_div_lock }, + { "sys1pll_qa19", "sys1pll_fixdiv", "sys1pll_a19", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 16, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 8, &share_div_lock }, + { "sys1pll_qa20", "sys1pll_fixdiv", "sys1pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 8, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 4, &share_div_lock }, + { "sys2pll_qa20", "sys2pll_fixdiv", "sys2pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 0, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 0, &share_div_lock }, + { "sys1pll_qa17", "sys1pll_fixdiv", "sys1pll_a17", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_SHARED_DIVIDER_CFG1, 8, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 20, &share_div_lock }, + { "sys0pll_qa20", "sys0pll_fixdiv", "sys0pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG1, 0, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 16, &share_div_lock }, +}; + +static const char * const i2s_clk_parents[] = { + "xin", + "xinw", + "audio_dto", + /* "pwm_i2s01" */ +}; + +static const char * const usbphy_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a1", + "sys1pll_a1", + "sys2pll_a1", + "sys3pll_a1", +}; + +static const char * const btss_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a2", + "sys1pll_a2", + "sys2pll_a2", + "sys3pll_a2", +}; + +static const char * const rgmii_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a3", + "sys1pll_a3", + "sys2pll_a3", + "sys3pll_a3", +}; + +static const char * const cpu_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a4", + "sys1pll_a4", + "cpupll_clk1", +}; + +static const char * const sdphy01_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a5", + "sys1pll_a5", + "sys2pll_a5", + "sys3pll_a5", +}; + +static const char * const sdphy23_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a6", + "sys1pll_a6", + "sys2pll_a6", + "sys3pll_a6", +}; + +static const char * const sdphy45_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a7", + "sys1pll_a7", + "sys2pll_a7", + "sys3pll_a7", +}; + +static const char * const sdphy67_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a8", + "sys1pll_a8", + "sys2pll_a8", + "sys3pll_a8", +}; + +static const char * const can_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a9", + "sys1pll_a9", + "sys2pll_a9", + "sys3pll_a9", +}; + +static const char * const deint_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a10", + "sys1pll_a10", + "sys2pll_a10", + "sys3pll_a10", +}; + +static const char * const nand_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a11", + "sys1pll_a11", + "sys2pll_a11", + "sys3pll_a11", +}; + +static const char * const disp0_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a12", + "sys1pll_a12", + "sys2pll_a12", + "sys3pll_a12", + "disp0_dto", +}; + +static const char * const disp1_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a13", + "sys1pll_a13", + "sys2pll_a13", + "sys3pll_a13", + "disp1_dto", +}; + +static const char * const gpu_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a14", + "sys1pll_a14", + "sys2pll_a14", + "sys3pll_a14", +}; + +static const char * const gnss_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a15", + "sys1pll_a15", + "sys2pll_a15", + "sys3pll_a15", +}; + +static const char * const sys_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const io_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const g2d_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const jpenc_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const vdec_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const gmac_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const usb_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const kas_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const sec_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const sdr_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const vip_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const nocd_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const nocr_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const tpiu_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static struct atlas7_mux_init_data mux_list[] __initdata = { + /* mux_name, parent_names, parent_num, flags, mux_flags, mux_offset, shift, width */ + { "i2s_mux", i2s_clk_parents, ARRAY_SIZE(i2s_clk_parents), 0, 0, SIRFSOC_CLKC_I2S_CLK_SEL, 0, 2 }, + { "usbphy_mux", usbphy_clk_parents, ARRAY_SIZE(usbphy_clk_parents), 0, 0, SIRFSOC_CLKC_I2S_CLK_SEL, 0, 3 }, + { "btss_mux", btss_clk_parents, ARRAY_SIZE(btss_clk_parents), 0, 0, SIRFSOC_CLKC_BTSS_CLK_SEL, 0, 3 }, + { "rgmii_mux", rgmii_clk_parents, ARRAY_SIZE(rgmii_clk_parents), 0, 0, SIRFSOC_CLKC_RGMII_CLK_SEL, 0, 3 }, + { "cpu_mux", cpu_clk_parents, ARRAY_SIZE(cpu_clk_parents), 0, 0, SIRFSOC_CLKC_CPU_CLK_SEL, 0, 3 }, + { "sdphy01_mux", sdphy01_clk_parents, ARRAY_SIZE(sdphy01_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY01_CLK_SEL, 0, 3 }, + { "sdphy23_mux", sdphy23_clk_parents, ARRAY_SIZE(sdphy23_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY23_CLK_SEL, 0, 3 }, + { "sdphy45_mux", sdphy45_clk_parents, ARRAY_SIZE(sdphy45_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY45_CLK_SEL, 0, 3 }, + { "sdphy67_mux", sdphy67_clk_parents, ARRAY_SIZE(sdphy67_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY67_CLK_SEL, 0, 3 }, + { "can_mux", can_clk_parents, ARRAY_SIZE(can_clk_parents), 0, 0, SIRFSOC_CLKC_CAN_CLK_SEL, 0, 3 }, + { "deint_mux", deint_clk_parents, ARRAY_SIZE(deint_clk_parents), 0, 0, SIRFSOC_CLKC_DEINT_CLK_SEL, 0, 3 }, + { "nand_mux", nand_clk_parents, ARRAY_SIZE(nand_clk_parents), 0, 0, SIRFSOC_CLKC_NAND_CLK_SEL, 0, 3 }, + { "disp0_mux", disp0_clk_parents, ARRAY_SIZE(disp0_clk_parents), 0, 0, SIRFSOC_CLKC_DISP0_CLK_SEL, 0, 3 }, + { "disp1_mux", disp1_clk_parents, ARRAY_SIZE(disp1_clk_parents), 0, 0, SIRFSOC_CLKC_DISP1_CLK_SEL, 0, 3 }, + { "gpu_mux", gpu_clk_parents, ARRAY_SIZE(gpu_clk_parents), 0, 0, SIRFSOC_CLKC_GPU_CLK_SEL, 0, 3 }, + { "gnss_mux", gnss_clk_parents, ARRAY_SIZE(gnss_clk_parents), 0, 0, SIRFSOC_CLKC_GNSS_CLK_SEL, 0, 3 }, + { "sys_mux", sys_clk_parents, ARRAY_SIZE(sys_clk_parents), 0, 0, SIRFSOC_CLKC_SYS_CLK_SEL, 0, 3 }, + { "io_mux", io_clk_parents, ARRAY_SIZE(io_clk_parents), 0, 0, SIRFSOC_CLKC_IO_CLK_SEL, 0, 3 }, + { "g2d_mux", g2d_clk_parents, ARRAY_SIZE(g2d_clk_parents), 0, 0, SIRFSOC_CLKC_G2D_CLK_SEL, 0, 3 }, + { "jpenc_mux", jpenc_clk_parents, ARRAY_SIZE(jpenc_clk_parents), 0, 0, SIRFSOC_CLKC_JPENC_CLK_SEL, 0, 3 }, + { "vdec_mux", vdec_clk_parents, ARRAY_SIZE(vdec_clk_parents), 0, 0, SIRFSOC_CLKC_VDEC_CLK_SEL, 0, 3 }, + { "gmac_mux", gmac_clk_parents, ARRAY_SIZE(gmac_clk_parents), 0, 0, SIRFSOC_CLKC_GMAC_CLK_SEL, 0, 3 }, + { "usb_mux", usb_clk_parents, ARRAY_SIZE(usb_clk_parents), 0, 0, SIRFSOC_CLKC_USB_CLK_SEL, 0, 3 }, + { "kas_mux", kas_clk_parents, ARRAY_SIZE(kas_clk_parents), 0, 0, SIRFSOC_CLKC_KAS_CLK_SEL, 0, 3 }, + { "sec_mux", sec_clk_parents, ARRAY_SIZE(sec_clk_parents), 0, 0, SIRFSOC_CLKC_SEC_CLK_SEL, 0, 3 }, + { "sdr_mux", sdr_clk_parents, ARRAY_SIZE(sdr_clk_parents), 0, 0, SIRFSOC_CLKC_SDR_CLK_SEL, 0, 3 }, + { "vip_mux", vip_clk_parents, ARRAY_SIZE(vip_clk_parents), 0, 0, SIRFSOC_CLKC_VIP_CLK_SEL, 0, 3 }, + { "nocd_mux", nocd_clk_parents, ARRAY_SIZE(nocd_clk_parents), 0, 0, SIRFSOC_CLKC_NOCD_CLK_SEL, 0, 3 }, + { "nocr_mux", nocr_clk_parents, ARRAY_SIZE(nocr_clk_parents), 0, 0, SIRFSOC_CLKC_NOCR_CLK_SEL, 0, 3 }, + { "tpiu_mux", tpiu_clk_parents, ARRAY_SIZE(tpiu_clk_parents), 0, 0, SIRFSOC_CLKC_TPIU_CLK_SEL, 0, 3 }, +}; + + /* 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, "thcpum_cpudiv4", "cpum_cpu", 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 }, +}; + +static struct clk *atlas7_clks[ARRAY_SIZE(unit_list)]; + +static int unit_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_unit *clk = to_unitclk(hw); + u32 reg; + + reg = clk->regofs + SIRFSOC_CLKC_ROOT_CLK_EN0_STAT - SIRFSOC_CLKC_ROOT_CLK_EN0_SET; + + return !!(clkc_readl(reg) & BIT(clk->bit)); +} + +static int unit_clk_enable(struct clk_hw *hw) +{ + u32 reg; + struct clk_unit *clk = to_unitclk(hw); + unsigned long flags; + + reg = clk->regofs; + + spin_lock_irqsave(clk->lock, flags); + clkc_writel(BIT(clk->bit), reg); + spin_unlock_irqrestore(clk->lock, flags); + return 0; +} + +static void unit_clk_disable(struct clk_hw *hw) +{ + u32 reg; + 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); + clkc_writel(BIT(clk->bit), reg); + spin_unlock_irqrestore(clk->lock, flags); +} + +static const struct clk_ops unit_clk_ops = { + .is_enabled = unit_clk_is_enabled, + .enable = unit_clk_enable, + .disable = unit_clk_disable, +}; + +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) +{ + struct clk *clk; + struct clk_unit *unit; + struct clk_init_data init; + + unit = kzalloc(sizeof(*unit), GFP_KERNEL); + if (!unit) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &unit_clk_ops; + init.flags = flags; + + unit->hw.init = &init; + unit->regofs = regofs; + unit->bit = bit; + unit->lock = lock; + + clk = clk_register(dev, &unit->hw); + if (IS_ERR(clk)) + kfree(unit); + + return clk; +} + +static struct atlas7_reset_desc atlas7_reset_unit[] = { + { "PWM", 0x0244, 0, 0x0320, 0, &leaf0_gate_lock }, /* 0-5 */ + { "THCGUM", 0x0244, 3, 0x0320, 1, &leaf0_gate_lock }, + { "CVD", 0x04A0, 0, 0x032C, 0, &leaf1_gate_lock }, + { "TIMER", 0x04A0, 1, 0x032C, 1, &leaf1_gate_lock }, + { "PULSEC", 0x04A0, 2, 0x032C, 2, &leaf1_gate_lock }, + { "TSC", 0x04A0, 3, 0x032C, 3, &leaf1_gate_lock }, + { "IOCTOP", 0x04A0, 4, 0x032C, 4, &leaf1_gate_lock }, /* 6-10 */ + { "RSC", 0x04A0, 5, 0x032C, 5, &leaf1_gate_lock }, + { "DVM", 0x04A0, 6, 0x032C, 6, &leaf1_gate_lock }, + { "LVDS", 0x04A0, 7, 0x032C, 7, &leaf1_gate_lock }, + { "KAS", 0x04A0, 8, 0x032C, 8, &leaf1_gate_lock }, + { "AC97", 0x04A0, 9, 0x032C, 9, &leaf1_gate_lock }, /* 11-15 */ + { "USP0", 0x04A0, 10, 0x032C, 10, &leaf1_gate_lock }, + { "USP1", 0x04A0, 11, 0x032C, 11, &leaf1_gate_lock }, + { "USP2", 0x04A0, 12, 0x032C, 12, &leaf1_gate_lock }, + { "DMAC2", 0x04A0, 13, 0x032C, 13, &leaf1_gate_lock }, + { "DMAC3", 0x04A0, 14, 0x032C, 14, &leaf1_gate_lock }, /* 16-20 */ + { "AUDIO", 0x04A0, 15, 0x032C, 15, &leaf1_gate_lock }, + { "I2S1", 0x04A0, 17, 0x032C, 16, &leaf1_gate_lock }, + { "PMU_AUDIO", 0x04A0, 22, 0x032C, 17, &leaf1_gate_lock }, + { "THAUDMSCM", 0x04A0, 23, 0x032C, 18, &leaf1_gate_lock }, + { "SYS2PCI", 0x04B8, 0, 0x0338, 0, &leaf2_gate_lock }, /* 21-25 */ + { "PCIARB", 0x04B8, 1, 0x0338, 1, &leaf2_gate_lock }, + { "PCICOPY", 0x04B8, 2, 0x0338, 2, &leaf2_gate_lock }, + { "ROM", 0x04B8, 3, 0x0338, 3, &leaf2_gate_lock }, + { "SDIO23", 0x04B8, 4, 0x0338, 4, &leaf2_gate_lock }, + { "SDIO45", 0x04B8, 5, 0x0338, 5, &leaf2_gate_lock }, /* 26-30 */ + { "SDIO67", 0x04B8, 6, 0x0338, 6, &leaf2_gate_lock }, + { "VIP1", 0x04B8, 7, 0x0338, 7, &leaf2_gate_lock }, + { "VPP0", 0x04B8, 11, 0x0338, 8, &leaf2_gate_lock }, + { "LCD0", 0x04B8, 12, 0x0338, 9, &leaf2_gate_lock }, + { "VPP1", 0x04B8, 13, 0x0338, 10, &leaf2_gate_lock }, /* 31-35 */ + { "LCD1", 0x04B8, 14, 0x0338, 11, &leaf2_gate_lock }, + { "DCU", 0x04B8, 15, 0x0338, 12, &leaf2_gate_lock }, + { "GPIO", 0x04B8, 18, 0x0338, 13, &leaf2_gate_lock }, + { "DAPA_VDIFM", 0x04B8, 17, 0x0338, 15, &leaf2_gate_lock }, + { "THVDIFM", 0x04B8, 19, 0x0338, 16, &leaf2_gate_lock }, /* 36-40 */ + { "RGMII", 0x04D0, 0, 0x0344, 0, &leaf3_gate_lock }, + { "GMAC", 0x04D0, 1, 0x0344, 1, &leaf3_gate_lock }, + { "UART1", 0x04D0, 2, 0x0344, 2, &leaf3_gate_lock }, + { "DMAC0", 0x04D0, 3, 0x0344, 3, &leaf3_gate_lock }, + { "UART0", 0x04D0, 4, 0x0344, 4, &leaf3_gate_lock }, /* 41-45 */ + { "UART2", 0x04D0, 5, 0x0344, 5, &leaf3_gate_lock }, + { "UART3", 0x04D0, 6, 0x0344, 6, &leaf3_gate_lock }, + { "UART4", 0x04D0, 7, 0x0344, 7, &leaf3_gate_lock }, + { "UART5", 0x04D0, 8, 0x0344, 8, &leaf3_gate_lock }, + { "SPI1", 0x04D0, 9, 0x0344, 9, &leaf3_gate_lock }, /* 46-50 */ + { "GNSS_SYS_M0", 0x04D0, 10, 0x0344, 10, &leaf3_gate_lock }, + { "CANBUS1", 0x04D0, 12, 0x0344, 11, &leaf3_gate_lock }, + { "CCSEC", 0x04D0, 15, 0x0344, 12, &leaf3_gate_lock }, + { "CCPUB", 0x04D0, 16, 0x0344, 13, &leaf3_gate_lock }, + { "DAPA_GNSSM", 0x04D0, 13, 0x0344, 14, &leaf3_gate_lock }, /* 51-55 */ + { "THGNSSM", 0x04D0, 14, 0x0344, 15, &leaf3_gate_lock }, + { "VDEC", 0x04E8, 0, 0x0350, 0, &leaf4_gate_lock }, + { "JPENC", 0x04E8, 1, 0x0350, 1, &leaf4_gate_lock }, + { "G2D", 0x04E8, 2, 0x0350, 2, &leaf4_gate_lock }, + { "I2C0", 0x04E8, 3, 0x0350, 3, &leaf4_gate_lock }, /* 56-60 */ + { "I2C1", 0x04E8, 4, 0x0350, 4, &leaf4_gate_lock }, + { "GPIO0", 0x04E8, 5, 0x0350, 5, &leaf4_gate_lock }, + { "NAND", 0x04E8, 6, 0x0350, 6, &leaf4_gate_lock }, + { "SDIO01", 0x04E8, 7, 0x0350, 7, &leaf4_gate_lock }, + { "SYS2PCI2", 0x04E8, 8, 0x0350, 8, &leaf4_gate_lock }, /* 61-65 */ + { "USB0", 0x04E8, 11, 0x0350, 9, &leaf4_gate_lock }, + { "USB1", 0x04E8, 12, 0x0350, 10, &leaf4_gate_lock }, + { "THMEDIAM", 0x04E8, 15, 0x0350, 11, &leaf4_gate_lock }, + { "MEMC_DDRPHY", 0x0500, 0, 0x035C, 0, &leaf5_gate_lock }, + { "MEMC_UPCTL", 0x0500, 0, 0x035C, 1, &leaf5_gate_lock }, /* 66-70 */ + { "DAPA_MEM", 0x0500, 1, 0x035C, 2, &leaf5_gate_lock }, + { "MEMC_MEMDIV", 0x0500, 0, 0x035C, 3, &leaf5_gate_lock }, + { "THDDRM", 0x0500, 3, 0x035C, 4, &leaf5_gate_lock }, + { "CORESIGHT", 0x0518, 3, 0x0368, 13, &leaf6_gate_lock }, + { "THCPUM", 0x0518, 4, 0x0368, 17, &leaf6_gate_lock }, /* 71-75 */ + { "GRAPHIC", 0x0530, 0, 0x0374, 0, &leaf7_gate_lock }, + { "VSS_SDR", 0x0530, 1, 0x0374, 1, &leaf7_gate_lock }, + { "THGPUM", 0x0530, 2, 0x0374, 2, &leaf7_gate_lock }, + { "DMAC4", 0x0548, 2, 0x0380, 1, &leaf8_gate_lock }, + { "UART6", 0x0548, 3, 0x0380, 2, &leaf8_gate_lock }, /* 76- */ + { "USP3", 0x0548, 4, 0x0380, 3, &leaf8_gate_lock }, + { "THBTM", 0x0548, 5, 0x0380, 5, &leaf8_gate_lock }, + { "A7CA", 0x0548, 1, 0x0380, 0, &leaf8_gate_lock }, + { "A7CA_APB", 0x0548, 5, 0x0380, 4, &leaf8_gate_lock }, +}; + +static int atlas7_reset_module(struct reset_controller_dev *rcdev, + unsigned long reset_idx) +{ + struct atlas7_reset_desc *reset = &atlas7_reset_unit[reset_idx]; + unsigned long flags; + + /* + * HW suggest unit reset sequence: + * assert sw reset (0) + * setting sw clk_en to if the clock was disabled before reset + * delay 16 clocks + * disable clock (sw clk_en = 0) + * de-assert reset (1) + * after this sequence, restore clock or not is decided by SW + */ + + spin_lock_irqsave(reset->lock, flags); + /* clock enable or not */ + if (clkc_readl(reset->clk_ofs + 8) & (1 << reset->clk_bit)) { + clkc_writel(1 << reset->rst_bit, reset->rst_ofs + 4); + udelay(2); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs + 4); + clkc_writel(1 << reset->rst_bit, reset->rst_ofs); + /* restore clock enable */ + clkc_writel(1 << reset->clk_bit, reset->clk_ofs); + } else { + clkc_writel(1 << reset->rst_bit, reset->rst_ofs + 4); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs); + udelay(2); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs + 4); + clkc_writel(1 << reset->rst_bit, reset->rst_ofs); + } + spin_unlock_irqrestore(reset->lock, flags); + + return 0; +} + +static struct reset_control_ops atlas7_rst_ops = { + .reset = atlas7_reset_module, +}; + +static struct reset_controller_dev atlas7_rst_ctlr = { + .ops = &atlas7_rst_ops, + .owner = THIS_MODULE, + .of_reset_n_cells = 1, +}; + +static void __init atlas7_clk_init(struct device_node *np) +{ + struct clk *clk; + struct atlas7_div_init_data *div; + struct atlas7_mux_init_data *mux; + struct atlas7_unit_init_data *unit; + int i; + int ret; + + sirfsoc_clk_vbase = of_iomap(np, 0); + if (!sirfsoc_clk_vbase) + panic("unable to map clkc registers\n"); + + of_node_put(np); + + clk = clk_register(NULL, &clk_cpupll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_mempll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys0pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys1pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys2pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys3pll.hw); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "cpupll_div1", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "cpupll_div2", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "cpupll_div3", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "mempll_div1", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "mempll_div2", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "mempll_div3", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "sys0pll_div1", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys0pll_div2", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys0pll_div3", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys0pll_fixdiv", "sys0pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys1pll_div1", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys1pll_div2", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys1pll_div3", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys1pll_fixdiv", "sys1pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys2pll_div1", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys2pll_div2", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys2pll_div3", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys2pll_fixdiv", "sys2pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys3pll_div1", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys3pll_div2", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys3pll_div3", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys3pll_fixdiv", "sys3pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "xinw_fixdiv_btslow", "xinw", + CLK_SET_RATE_PARENT, 1, 4); + + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk1", "cpupll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 12, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk2", "cpupll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 13, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk3", "cpupll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 14, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "mempll_clk1", "mempll_div1", + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 12, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "mempll_clk2", "mempll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 13, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "mempll_clk3", "mempll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 14, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys0pll_clk1", "sys0pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 12, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys0pll_clk2", "sys0pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 13, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys0pll_clk3", "sys0pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 14, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys1pll_clk1", "sys1pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 12, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys1pll_clk2", "sys1pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 13, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys1pll_clk3", "sys1pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 14, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys2pll_clk1", "sys2pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 12, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys2pll_clk2", "sys2pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 13, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys2pll_clk3", "sys2pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 14, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys3pll_clk1", "sys3pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 12, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys3pll_clk2", "sys3pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 13, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys3pll_clk3", "sys3pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 14, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_audio_dto.hw); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_disp0_dto.hw); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_disp1_dto.hw); + BUG_ON(!clk); + + for (i = 0; i < ARRAY_SIZE(divider_list); i++) { + div = ÷r_list[i]; + clk = clk_register_divider(NULL, div->div_name, + div->parent_name, div->divider_flags, sirfsoc_clk_vbase + div->div_offset, + div->shift, div->width, 0, div->lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, div->gate_name, div->div_name, + div->gate_flags, sirfsoc_clk_vbase + div->gate_offset, + div->gate_bit, 0, div->lock); + BUG_ON(!clk); + } + /* ignore selector status register check */ + for (i = 0; i < ARRAY_SIZE(mux_list); i++) { + mux = &mux_list[i]; + clk = clk_register_mux(NULL, mux->mux_name, mux->parent_names, + mux->parent_num, mux->flags, + sirfsoc_clk_vbase + mux->mux_offset, + mux->shift, mux->width, + mux->mux_flags, NULL); + BUG_ON(!clk); + } + + 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); + BUG_ON(!atlas7_clks[i]); + } + + clk_data.clks = atlas7_clks; + clk_data.clk_num = ARRAY_SIZE(unit_list); + + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + BUG_ON(ret); + + atlas7_rst_ctlr.of_node = np; + atlas7_rst_ctlr.nr_resets = ARRAY_SIZE(atlas7_reset_unit); + reset_controller_register(&atlas7_rst_ctlr); +} +CLK_OF_DECLARE(atlas7_clk, "sirf,atlas7-car", atlas7_clk_init); diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c index 37af51c5f213..9fc285d784d3 100644 --- a/drivers/clk/sirf/clk-common.c +++ b/drivers/clk/sirf/clk-common.c @@ -10,8 +10,8 @@ #define KHZ 1000 #define MHZ (KHZ * KHZ) -static void *sirfsoc_clk_vbase; -static void *sirfsoc_rsc_vbase; +static void __iomem *sirfsoc_clk_vbase; +static void __iomem *sirfsoc_rsc_vbase; static struct clk_onecell_data clk_data; /* @@ -188,7 +188,7 @@ static struct clk_ops std_pll_ops = { .set_rate = pll_clk_set_rate, }; -static const char *pll_clk_parents[] = { +static const char * const pll_clk_parents[] = { "osc", }; @@ -284,7 +284,7 @@ static struct clk_hw usb_pll_clk_hw = { * clock domains - cpu, mem, sys/io, dsp, gfx */ -static const char *dmn_clk_parents[] = { +static const char * const dmn_clk_parents[] = { "rtc", "osc", "pll1", @@ -673,7 +673,7 @@ static void std_clk_disable(struct clk_hw *hw) clkc_writel(val, reg); } -static const char *std_clk_io_parents[] = { +static const char * const std_clk_io_parents[] = { "io", }; @@ -949,7 +949,7 @@ static struct clk_std clk_pulse = { }, }; -static const char *std_clk_dsp_parents[] = { +static const char * const std_clk_dsp_parents[] = { "dsp", }; @@ -981,7 +981,7 @@ static struct clk_std clk_mf = { }, }; -static const char *std_clk_sys_parents[] = { +static const char * const std_clk_sys_parents[] = { "sys", }; @@ -999,7 +999,7 @@ static struct clk_std clk_security = { }, }; -static const char *std_clk_usb_parents[] = { +static const char * const std_clk_usb_parents[] = { "usb_pll", }; diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index 7e2d15a0c7b8..d8bb239753a4 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile @@ -2,3 +2,4 @@ obj-y += clk.o obj-y += clk-gate.o obj-y += clk-pll.o obj-y += clk-periph.o +obj-y += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c new file mode 100644 index 000000000000..83c6780ff4b2 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate-a10.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include "clk.h" + +#define streq(a, b) (strcmp((a), (b)) == 0) + +#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) + +/* SDMMC Group for System Manager defines */ +#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x28 + +static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + u32 div = 1, val; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + div = (1 << val); + } + + return parent_rate / div; +} + +static int socfpga_clk_prepare(struct clk_hw *hwclk) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + int i; + u32 hs_timing; + u32 clk_phase[2]; + + if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { + for (i = 0; i < ARRAY_SIZE(clk_phase); i++) { + switch (socfpgaclk->clk_phase[i]) { + case 0: + clk_phase[i] = 0; + break; + case 45: + clk_phase[i] = 1; + break; + case 90: + clk_phase[i] = 2; + break; + case 135: + clk_phase[i] = 3; + break; + case 180: + clk_phase[i] = 4; + break; + case 225: + clk_phase[i] = 5; + break; + case 270: + clk_phase[i] = 6; + break; + case 315: + clk_phase[i] = 7; + break; + default: + clk_phase[i] = 0; + break; + } + } + + hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); + if (!IS_ERR(socfpgaclk->sys_mgr_base_addr)) + regmap_write(socfpgaclk->sys_mgr_base_addr, + SYSMGR_SDMMCGRP_CTRL_OFFSET, hs_timing); + else + pr_err("%s: cannot set clk_phase because sys_mgr_base_addr is not available!\n", + __func__); + } + return 0; +} + +static struct clk_ops gateclk_ops = { + .prepare = socfpga_clk_prepare, + .recalc_rate = socfpga_gate_clk_recalc_rate, +}; + +static void __init __socfpga_gate_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 clk_gate[2]; + u32 div_reg[3]; + u32 clk_phase[2]; + u32 fixed_div; + struct clk *clk; + struct socfpga_gate_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return; + + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); + if (rc) + clk_gate[0] = 0; + + if (clk_gate[0]) { + socfpga_clk->hw.reg = clk_mgr_a10_base_addr + clk_gate[0]; + socfpga_clk->hw.bit_idx = clk_gate[1]; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + socfpga_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; + socfpga_clk->shift = div_reg[1]; + socfpga_clk->width = div_reg[2]; + } else { + socfpga_clk->div_reg = NULL; + } + + rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); + if (!rc) { + socfpga_clk->clk_phase[0] = clk_phase[0]; + socfpga_clk->clk_phase[1] = clk_phase[1]; + + socfpga_clk->sys_mgr_base_addr = + syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(socfpga_clk->sys_mgr_base_addr)) { + pr_err("%s: failed to find altr,sys-mgr regmap!\n", + __func__); + return; + } + } + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; + socfpga_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(rc)) + return; +} + +void __init socfpga_a10_gate_init(struct device_node *node) +{ + __socfpga_gate_init(node, &gateclk_ops); +} diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index dd3a78c64795..82449cd76fd7 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -32,14 +32,10 @@ #define SOCFPGA_MMC_CLK "sdmmc_clk" #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 -#define streq(a, b) (strcmp((a), (b)) == 0) - #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) /* SDMMC Group for System Manager defines */ #define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 -#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ - ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) { @@ -194,7 +190,6 @@ static void __init __socfpga_gate_init(struct device_node *node, const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; int rc; - int i = 0; socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); if (WARN_ON(!socfpga_clk)) @@ -224,7 +219,7 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->shift = div_reg[1]; socfpga_clk->width = div_reg[2]; } else { - socfpga_clk->div_reg = 0; + socfpga_clk->div_reg = NULL; } rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); @@ -238,12 +233,9 @@ static void __init __socfpga_gate_init(struct device_node *node, init.name = clk_name; init.ops = ops; init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; + init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); init.parent_names = parent_name; - init.num_parents = i; socfpga_clk->hw.hw.init = &init; clk = clk_register(NULL, &socfpga_clk->hw.hw); diff --git a/drivers/clk/socfpga/clk-periph-a10.c b/drivers/clk/socfpga/clk-periph-a10.c new file mode 100644 index 000000000000..9d0181b5a6a4 --- /dev/null +++ b/drivers/clk/socfpga/clk-periph-a10.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "clk.h" + +#define CLK_MGR_FREE_SHIFT 16 +#define CLK_MGR_FREE_MASK 0x7 + +#define SOCFPGA_MPU_FREE_CLK "mpu_free_clk" +#define SOCFPGA_NOC_FREE_CLK "noc_free_clk" +#define SOCFPGA_SDMMC_FREE_CLK "sdmmc_free_clk" +#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 div; + + if (socfpgaclk->fixed_div) { + div = socfpgaclk->fixed_div; + } else if (socfpgaclk->div_reg) { + div = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + div &= div_mask(socfpgaclk->width); + div += 1; + } else { + div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); + } + + return parent_rate / div; +} + +static u8 clk_periclk_get_parent(struct clk_hw *hwclk) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 clk_src; + + clk_src = readl(socfpgaclk->hw.reg); + if (streq(hwclk->init->name, SOCFPGA_MPU_FREE_CLK) || + streq(hwclk->init->name, SOCFPGA_NOC_FREE_CLK) || + streq(hwclk->init->name, SOCFPGA_SDMMC_FREE_CLK)) + return (clk_src >> CLK_MGR_FREE_SHIFT) & + CLK_MGR_FREE_MASK; + else + return 0; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, + .get_parent = clk_periclk_get_parent, +}; + +static __init void __socfpga_periph_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_periph_clk *periph_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + u32 fixed_div; + u32 div_reg[3]; + + of_property_read_u32(node, "reg", ®); + + periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); + if (WARN_ON(!periph_clk)) + return; + + periph_clk->hw.reg = clk_mgr_a10_base_addr + reg; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + periph_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; + periph_clk->shift = div_reg[1]; + periph_clk->width = div_reg[2]; + } else { + periph_clk->div_reg = NULL; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + periph_clk->fixed_div = 0; + else + periph_clk->fixed_div = fixed_div; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + parent_name = of_clk_get_parent_name(node, 0); + init.num_parents = 1; + init.parent_names = &parent_name; + + periph_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &periph_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(periph_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (rc < 0) { + pr_err("Could not register clock provider for node:%s\n", + clk_name); + goto err_clk; + } + + return; + +err_clk: + clk_unregister(clk); +} + +void __init socfpga_a10_periph_init(struct device_node *node) +{ + __socfpga_periph_init(node, &periclk_ops); +} diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c index 46531c34ec9b..83aeaa219d14 100644 --- a/drivers/clk/socfpga/clk-periph.c +++ b/drivers/clk/socfpga/clk-periph.c @@ -76,7 +76,7 @@ static __init void __socfpga_periph_init(struct device_node *node, periph_clk->shift = div_reg[1]; periph_clk->width = div_reg[2]; } else { - periph_clk->div_reg = 0; + periph_clk->div_reg = NULL; } rc = of_property_read_u32(node, "fixed-divider", &fixed_div); diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c new file mode 100644 index 000000000000..1178b11babca --- /dev/null +++ b/drivers/clk/socfpga/clk-pll-a10.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk.h" + +/* Clock Manager offsets */ +#define CLK_MGR_PLL_CLK_SRC_SHIFT 8 +#define CLK_MGR_PLL_CLK_SRC_MASK 0x3 + +/* Clock bypass bits */ +#define SOCFPGA_PLL_BG_PWRDWN 0 +#define SOCFPGA_PLL_PWR_DOWN 1 +#define SOCFPGA_PLL_EXT_ENA 2 +#define SOCFPGA_PLL_DIVF_MASK 0x00001FFF +#define SOCFPGA_PLL_DIVF_SHIFT 0 +#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 +#define SOCFPGA_PLL_DIVQ_SHIFT 16 +#define SOCFGPA_MAX_PARENTS 5 + +#define SOCFPGA_MAIN_PLL_CLK "main_pll" +#define SOCFPGA_PERIP_PLL_CLK "periph_pll" + +#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) + +void __iomem *clk_mgr_a10_base_addr; + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + unsigned long divf, divq, reg; + unsigned long long vco_freq; + + /* read VCO1 reg for numerator and denominator */ + reg = readl(socfpgaclk->hw.reg + 0x4); + divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; + divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; + vco_freq = (unsigned long long)parent_rate * (divf + 1); + do_div(vco_freq, (1 + divq)); + return (unsigned long)vco_freq; +} + +static u8 clk_pll_get_parent(struct clk_hw *hwclk) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + u32 pll_src; + + pll_src = readl(socfpgaclk->hw.reg); + + return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & + CLK_MGR_PLL_CLK_SRC_MASK; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .get_parent = clk_pll_get_parent, +}; + +static struct __init clk * __socfpga_pll_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_pll *pll_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFGPA_MAX_PARENTS]; + struct clk_init_data init; + struct device_node *clkmgr_np; + int rc; + int i = 0; + + of_property_read_u32(node, "reg", ®); + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (WARN_ON(!pll_clk)) + return NULL; + + clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); + clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); + BUG_ON(!clk_mgr_a10_base_addr); + pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + init.num_parents = i; + init.parent_names = parent_name; + pll_clk->hw.hw.init = &init; + + pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; + clk_pll_ops.enable = clk_gate_ops.enable; + clk_pll_ops.disable = clk_gate_ops.disable; + + clk = clk_register(NULL, &pll_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(pll_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} + +void __init socfpga_a10_pll_init(struct device_node *node) +{ + __socfpga_pll_init(node, &clk_pll_ops); +} diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index de6da957a09d..8f26b5234947 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -92,7 +92,6 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, struct clk_init_data init; struct device_node *clkmgr_np; int rc; - int i = 0; of_property_read_u32(node, "reg", ®); @@ -111,11 +110,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, init.ops = ops; init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; - - init.num_parents = i; + init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); init.parent_names = parent_name; pll_clk->hw.hw.init = &init; diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 43db947e5f0e..7564d2e35f32 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -24,4 +24,9 @@ CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init); CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init); CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init); - +CLK_OF_DECLARE(socfpga_a10_pll_clk, "altr,socfpga-a10-pll-clock", + socfpga_a10_pll_init); +CLK_OF_DECLARE(socfpga_a10_perip_clk, "altr,socfpga-a10-perip-clk", + socfpga_a10_periph_init); +CLK_OF_DECLARE(socfpga_a10_gate_clk, "altr,socfpga-a10-gate-clk", + socfpga_a10_gate_init); diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index d291f60c46e1..603973ab7e29 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -26,14 +26,22 @@ #define CLKMGR_L4SRC 0x70 #define CLKMGR_PERPLL_SRC 0xAC -#define SOCFPGA_MAX_PARENTS 3 +#define SOCFPGA_MAX_PARENTS 5 #define div_mask(width) ((1 << (width)) - 1) +#define streq(a, b) (strcmp((a), (b)) == 0) +#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ + ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) + extern void __iomem *clk_mgr_base_addr; +extern void __iomem *clk_mgr_a10_base_addr; void __init socfpga_pll_init(struct device_node *node); void __init socfpga_periph_init(struct device_node *node); void __init socfpga_gate_init(struct device_node *node); +void socfpga_a10_pll_init(struct device_node *node); +void socfpga_a10_periph_init(struct device_node *node); +void socfpga_a10_gate_init(struct device_node *node); struct socfpga_pll { struct clk_gate hw; @@ -44,6 +52,7 @@ struct socfpga_gate_clk { char *parent_name; u32 fixed_div; void __iomem *div_reg; + struct regmap *sys_mgr_base_addr; u32 width; /* only valid if div_reg != 0 */ u32 shift; /* only valid if div_reg != 0 */ u32 clk_phase[2]; diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index bf12a25eb3a2..657ca14ba709 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -116,7 +116,7 @@ static long flexgen_round_rate(struct clk_hw *hw, unsigned long rate, return *prate / div; } -unsigned long flexgen_recalc_rate(struct clk_hw *hw, +static unsigned long flexgen_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct flexgen *flexgen = to_flexgen(hw); @@ -174,7 +174,7 @@ static const struct clk_ops flexgen_ops = { .set_rate = flexgen_set_rate, }; -struct clk *clk_register_flexgen(const char *name, +static struct clk *clk_register_flexgen(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, spinlock_t *lock, u32 idx, unsigned long flexgen_flags) { @@ -245,7 +245,7 @@ static const char ** __init flexgen_get_parents(struct device_node *np, const char **parents; int nparents, i; - nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + nparents = of_clk_get_parent_count(np); if (WARN_ON(nparents <= 0)) return NULL; @@ -260,7 +260,7 @@ static const char ** __init flexgen_get_parents(struct device_node *np, return parents; } -void __init st_of_flexgen_setup(struct device_node *np) +static void __init st_of_flexgen_setup(struct device_node *np) { struct device_node *pnode; void __iomem *reg; diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index a917c4c7eaa9..e94197f04b0b 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -492,7 +492,7 @@ static int quadfs_pll_is_enabled(struct clk_hw *hw) return !!npda; } -int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, +static int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, unsigned long *rate) { unsigned long nd = fs->ndiv + 16; /* ndiv value */ @@ -519,7 +519,7 @@ static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw, return rate; } -int clk_fs660c32_vco_get_params(unsigned long input, +static int clk_fs660c32_vco_get_params(unsigned long input, unsigned long output, struct stm_fs *fs) { /* Formula diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index fdcff10f6d30..4fbe6e099587 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -26,7 +26,7 @@ static const char ** __init clkgen_mux_get_parents(struct device_node *np, const char **parents; int nparents, i; - nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + nparents = of_clk_get_parent_count(np); if (WARN_ON(nparents <= 0)) return ERR_PTR(-EINVAL); @@ -131,7 +131,7 @@ static int clkgena_divmux_is_enabled(struct clk_hw *hw) return (s8)clk_mux_ops.get_parent(mux_hw) > 0; } -u8 clkgena_divmux_get_parent(struct clk_hw *hw) +static u8 clkgena_divmux_get_parent(struct clk_hw *hw) { struct clkgena_divmux *genamux = to_clkgena_divmux(hw); struct clk_hw *mux_hw = &genamux->mux.hw; @@ -168,7 +168,7 @@ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) return 0; } -unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, +static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clkgena_divmux *genamux = to_clkgena_divmux(hw); @@ -215,7 +215,7 @@ static const struct clk_ops clkgena_divmux_ops = { /** * clk_register_genamux - register a genamux clock with the clock framework */ -struct clk *clk_register_genamux(const char *name, +static struct clk *clk_register_genamux(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, const struct clkgena_divmux_data *muxdata, @@ -385,7 +385,7 @@ static void __iomem * __init clkgen_get_register_base( return reg; } -void __init st_of_clkgena_divmux_setup(struct device_node *np) +static void __init st_of_clkgena_divmux_setup(struct device_node *np) { const struct of_device_id *match; const struct clkgena_divmux_data *data; @@ -485,7 +485,7 @@ static const struct of_device_id clkgena_prediv_of_match[] = { {} }; -void __init st_of_clkgena_prediv_setup(struct device_node *np) +static void __init st_of_clkgena_prediv_setup(struct device_node *np) { const struct of_device_id *match; void __iomem *reg; @@ -622,7 +622,7 @@ static const struct of_device_id mux_of_match[] = { {} }; -void __init st_of_clkgen_mux_setup(struct device_node *np) +static void __init st_of_clkgen_mux_setup(struct device_node *np) { const struct of_device_id *match; struct clk *clk; @@ -699,7 +699,7 @@ static const struct of_device_id vcc_of_match[] = { {} }; -void __init st_of_clkgen_vcc_setup(struct device_node *np) +static void __init st_of_clkgen_vcc_setup(struct device_node *np) { const struct of_device_id *match; void __iomem *reg; diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index d204ba85db3a..106532207213 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -270,7 +270,7 @@ static int clkgen_pll_is_enabled(struct clk_hw *hw) return !poweroff; } -unsigned long recalc_stm_pll800c65(struct clk_hw *hw, +static unsigned long recalc_stm_pll800c65(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -297,7 +297,7 @@ unsigned long recalc_stm_pll800c65(struct clk_hw *hw, } -unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, +static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -321,7 +321,7 @@ unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, return rate; } -unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, +static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -343,7 +343,7 @@ unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, return rate; } -unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, +static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -544,7 +544,7 @@ CLK_OF_DECLARE(clkgena_c65_plls, "st,clkgena-plls-c65", clkgena_c65_pll_setup); static struct clk * __init clkgen_odf_register(const char *parent_name, - void * __iomem reg, + void __iomem *reg, struct clkgen_pll_data *pll_data, int odf, spinlock_t *odf_lock, diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c index d8da77d72861..887f4ea161bb 100644 --- a/drivers/clk/sunxi/clk-sun9i-core.c +++ b/drivers/clk/sunxi/clk-sun9i-core.c @@ -93,7 +93,7 @@ static void __init sun9i_a80_pll4_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-pll4-clk: %s\n", node->name); return; @@ -154,7 +154,7 @@ static void __init sun9i_a80_gt_setup(struct device_node *node) struct clk *gt; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-gt-clk: %s\n", node->name); return; @@ -218,7 +218,7 @@ static void __init sun9i_a80_ahb_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-ahb-clk: %s\n", node->name); return; @@ -244,7 +244,7 @@ static void __init sun9i_a80_apb0_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-apb0-clk: %s\n", node->name); return; @@ -310,7 +310,7 @@ static void __init sun9i_a80_apb1_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-apb1-clk: %s\n", node->name); return; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 7e1e2bd189b6..9a82f17d2d73 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -198,6 +198,8 @@ static void __init sun6i_ahb1_clk_setup(struct device_node *node) int i = 0; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; /* we have a mux, we will have >1 parents */ while (i < SUN6I_AHB1_MAX_PARENTS && diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index a86ed2f8d7af..3a25f9588e67 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c @@ -204,6 +204,17 @@ static void __init sun6i_a31_usb_setup(struct device_node *node) } CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); +static const struct usb_clk_data sun8i_a23_usb_clk_data __initconst = { + .clk_mask = BIT(16) | BIT(11) | BIT(10) | BIT(9) | BIT(8), + .reset_mask = BIT(2) | BIT(1) | BIT(0), +}; + +static void __init sun8i_a23_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun8i_a23_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk", sun8i_a23_usb_setup); + static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), .reset_mask = BIT(19) | BIT(18) | BIT(17), diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig new file mode 100644 index 000000000000..1ba30d1e14f2 --- /dev/null +++ b/drivers/clk/tegra/Kconfig @@ -0,0 +1,3 @@ +config TEGRA_CLK_EMC + def_bool y + depends on TEGRA124_EMC diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index edb8358fa6ce..aec862ba7a17 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -11,6 +11,7 @@ obj-y += clk-tegra-periph.o obj-y += clk-tegra-pmc.o obj-y += clk-tegra-fixed.o obj-y += clk-tegra-super-gen4.o +obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c new file mode 100644 index 000000000000..7649685c86bc --- /dev/null +++ b/drivers/clk/tegra/clk-emc.c @@ -0,0 +1,538 @@ +/* + * drivers/clk/tegra/clk-emc.c + * + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Author: + * Mikko Perttunen <mperttunen@nvidia.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/sort.h> +#include <linux/string.h> + +#include <soc/tegra/fuse.h> +#include <soc/tegra/emc.h> + +#include "clk.h" + +#define CLK_SOURCE_EMC 0x19c + +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT 0 +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK 0xff +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK) << \ + CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT) + +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT 29 +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK 0x7 +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK) << \ + CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT) + +static const char * const emc_parent_clk_names[] = { + "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", + "pll_c2", "pll_c3", "pll_c_ud" +}; + +/* + * List of clock sources for various parents the EMC clock can have. + * When we change the timing to a timing with a parent that has the same + * clock source as the current parent, we must first change to a backup + * timing that has a different clock source. + */ + +#define EMC_SRC_PLL_M 0 +#define EMC_SRC_PLL_C 1 +#define EMC_SRC_PLL_P 2 +#define EMC_SRC_CLK_M 3 +#define EMC_SRC_PLL_C2 4 +#define EMC_SRC_PLL_C3 5 + +static const char emc_parent_clk_sources[] = { + EMC_SRC_PLL_M, EMC_SRC_PLL_C, EMC_SRC_PLL_P, EMC_SRC_CLK_M, + EMC_SRC_PLL_M, EMC_SRC_PLL_C2, EMC_SRC_PLL_C3, EMC_SRC_PLL_C +}; + +struct emc_timing { + unsigned long rate, parent_rate; + u8 parent_index; + struct clk *parent; + u32 ram_code; +}; + +struct tegra_clk_emc { + struct clk_hw hw; + void __iomem *clk_regs; + struct clk *prev_parent; + bool changing_timing; + + struct device_node *emc_node; + struct tegra_emc *emc; + + int num_timings; + struct emc_timing *timings; + spinlock_t *lock; +}; + +/* Common clock framework callback implementations */ + +static unsigned long emc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_emc *tegra; + u32 val, div; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + /* + * CCF wrongly assumes that the parent won't change during set_rate, + * so get the parent rate explicitly. + */ + parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); + + val = readl(tegra->clk_regs + CLK_SOURCE_EMC); + div = val & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK; + + return parent_rate / (div + 2) * 2; +} + +/* + * Rounds up unless no higher rate exists, in which case down. This way is + * safer since things have EMC rate floors. Also don't touch parent_rate + * since we don't want the CCF to play with our parent clocks. + */ +static long emc_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_hw) +{ + struct tegra_clk_emc *tegra; + u8 ram_code = tegra_read_ram_code(); + struct emc_timing *timing = NULL; + int i; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].ram_code != ram_code) + continue; + + timing = tegra->timings + i; + + if (timing->rate > max_rate) { + i = min(i, 1); + return tegra->timings[i - 1].rate; + } + + if (timing->rate < min_rate) + continue; + + if (timing->rate >= rate) + return timing->rate; + } + + if (timing) + return timing->rate; + + return __clk_get_rate(hw->clk); +} + +static u8 emc_get_parent(struct clk_hw *hw) +{ + struct tegra_clk_emc *tegra; + u32 val; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + val = readl(tegra->clk_regs + CLK_SOURCE_EMC); + + return (val >> CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT) + & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK; +} + +static struct tegra_emc *emc_ensure_emc_driver(struct tegra_clk_emc *tegra) +{ + struct platform_device *pdev; + + if (tegra->emc) + return tegra->emc; + + if (!tegra->emc_node) + return NULL; + + pdev = of_find_device_by_node(tegra->emc_node); + if (!pdev) { + pr_err("%s: could not get external memory controller\n", + __func__); + return NULL; + } + + of_node_put(tegra->emc_node); + tegra->emc_node = NULL; + + tegra->emc = platform_get_drvdata(pdev); + if (!tegra->emc) { + pr_err("%s: cannot find EMC driver\n", __func__); + return NULL; + } + + return tegra->emc; +} + +static int emc_set_timing(struct tegra_clk_emc *tegra, + struct emc_timing *timing) +{ + int err; + u8 div; + u32 car_value; + unsigned long flags = 0; + struct tegra_emc *emc = emc_ensure_emc_driver(tegra); + + if (!emc) + return -ENOENT; + + pr_debug("going to rate %ld prate %ld p %s\n", timing->rate, + timing->parent_rate, __clk_get_name(timing->parent)); + + if (emc_get_parent(&tegra->hw) == timing->parent_index && + clk_get_rate(timing->parent) != timing->parent_rate) { + BUG(); + return -EINVAL; + } + + tegra->changing_timing = true; + + err = clk_set_rate(timing->parent, timing->parent_rate); + if (err) { + pr_err("cannot change parent %s rate to %ld: %d\n", + __clk_get_name(timing->parent), timing->parent_rate, + err); + + return err; + } + + err = clk_prepare_enable(timing->parent); + if (err) { + pr_err("cannot enable parent clock: %d\n", err); + return err; + } + + div = timing->parent_rate / (timing->rate / 2) - 2; + + err = tegra_emc_prepare_timing_change(emc, timing->rate); + if (err) + return err; + + spin_lock_irqsave(tegra->lock, flags); + + car_value = readl(tegra->clk_regs + CLK_SOURCE_EMC); + + car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_SRC(~0); + car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_SRC(timing->parent_index); + + car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(~0); + car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(div); + + writel(car_value, tegra->clk_regs + CLK_SOURCE_EMC); + + spin_unlock_irqrestore(tegra->lock, flags); + + tegra_emc_complete_timing_change(emc, timing->rate); + + clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent)); + clk_disable_unprepare(tegra->prev_parent); + + tegra->prev_parent = timing->parent; + tegra->changing_timing = false; + + return 0; +} + +/* + * Get backup timing to use as an intermediate step when a change between + * two timings with the same clock source has been requested. First try to + * find a timing with a higher clock rate to avoid a rate below any set rate + * floors. If that is not possible, find a lower rate. + */ +static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, + int timing_index) +{ + int i; + u32 ram_code = tegra_read_ram_code(); + struct emc_timing *timing; + + for (i = timing_index+1; i < tegra->num_timings; i++) { + timing = tegra->timings + i; + if (timing->ram_code != ram_code) + continue; + + if (emc_parent_clk_sources[timing->parent_index] != + emc_parent_clk_sources[ + tegra->timings[timing_index].parent_index]) + return timing; + } + + for (i = timing_index-1; i >= 0; --i) { + timing = tegra->timings + i; + if (timing->ram_code != ram_code) + continue; + + if (emc_parent_clk_sources[timing->parent_index] != + emc_parent_clk_sources[ + tegra->timings[timing_index].parent_index]) + return timing; + } + + return NULL; +} + +static int emc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_emc *tegra; + struct emc_timing *timing = NULL; + int i, err; + u32 ram_code = tegra_read_ram_code(); + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + if (__clk_get_rate(hw->clk) == rate) + return 0; + + /* + * When emc_set_timing changes the parent rate, CCF will propagate + * that downward to us, so ignore any set_rate calls while a rate + * change is already going on. + */ + if (tegra->changing_timing) + return 0; + + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].rate == rate && + tegra->timings[i].ram_code == ram_code) { + timing = tegra->timings + i; + break; + } + } + + if (!timing) { + pr_err("cannot switch to rate %ld without emc table\n", rate); + return -EINVAL; + } + + if (emc_parent_clk_sources[emc_get_parent(hw)] == + emc_parent_clk_sources[timing->parent_index] && + clk_get_rate(timing->parent) != timing->parent_rate) { + /* + * Parent clock source not changed but parent rate has changed, + * need to temporarily switch to another parent + */ + + struct emc_timing *backup_timing; + + backup_timing = get_backup_timing(tegra, i); + if (!backup_timing) { + pr_err("cannot find backup timing\n"); + return -EINVAL; + } + + pr_debug("using %ld as backup rate when going to %ld\n", + backup_timing->rate, rate); + + err = emc_set_timing(tegra, backup_timing); + if (err) { + pr_err("cannot set backup timing: %d\n", err); + return err; + } + } + + return emc_set_timing(tegra, timing); +} + +/* Initialization and deinitialization */ + +static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, + struct emc_timing *timing, + struct device_node *node) +{ + int err, i; + u32 tmp; + + err = of_property_read_u32(node, "clock-frequency", &tmp); + if (err) { + pr_err("timing %s: failed to read rate\n", node->full_name); + return err; + } + + timing->rate = tmp; + + err = of_property_read_u32(node, "nvidia,parent-clock-frequency", &tmp); + if (err) { + pr_err("timing %s: failed to read parent rate\n", + node->full_name); + return err; + } + + timing->parent_rate = tmp; + + timing->parent = of_clk_get_by_name(node, "emc-parent"); + if (IS_ERR(timing->parent)) { + pr_err("timing %s: failed to get parent clock\n", + node->full_name); + return PTR_ERR(timing->parent); + } + + timing->parent_index = 0xff; + for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) { + if (!strcmp(emc_parent_clk_names[i], + __clk_get_name(timing->parent))) { + timing->parent_index = i; + break; + } + } + if (timing->parent_index == 0xff) { + pr_err("timing %s: %s is not a valid parent\n", + node->full_name, __clk_get_name(timing->parent)); + clk_put(timing->parent); + return -EINVAL; + } + + return 0; +} + +static int cmp_timings(const void *_a, const void *_b) +{ + const struct emc_timing *a = _a; + const struct emc_timing *b = _b; + + if (a->rate < b->rate) + return -1; + else if (a->rate == b->rate) + return 0; + else + return 1; +} + +static int load_timings_from_dt(struct tegra_clk_emc *tegra, + struct device_node *node, + u32 ram_code) +{ + struct device_node *child; + int child_count = of_get_child_count(node); + int i = 0, err; + + tegra->timings = kcalloc(child_count, sizeof(struct emc_timing), + GFP_KERNEL); + if (!tegra->timings) + return -ENOMEM; + + tegra->num_timings = child_count; + + for_each_child_of_node(node, child) { + struct emc_timing *timing = tegra->timings + (i++); + + err = load_one_timing_from_dt(tegra, timing, child); + if (err) + return err; + + timing->ram_code = ram_code; + } + + sort(tegra->timings, tegra->num_timings, sizeof(struct emc_timing), + cmp_timings, NULL); + + return 0; +} + +static const struct clk_ops tegra_clk_emc_ops = { + .recalc_rate = emc_recalc_rate, + .determine_rate = emc_determine_rate, + .set_rate = emc_set_rate, + .get_parent = emc_get_parent, +}; + +struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, + spinlock_t *lock) +{ + struct tegra_clk_emc *tegra; + struct clk_init_data init; + struct device_node *node; + u32 node_ram_code; + struct clk *clk; + int err; + + tegra = kcalloc(1, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return ERR_PTR(-ENOMEM); + + tegra->clk_regs = base; + tegra->lock = lock; + + tegra->num_timings = 0; + + 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); + continue; + } + + /* + * Store timings for all ram codes as we cannot read the + * fuses until the apbmisc driver is loaded. + */ + err = load_timings_from_dt(tegra, node, node_ram_code); + if (err) + return ERR_PTR(err); + of_node_put(node); + break; + } + + if (tegra->num_timings == 0) + pr_warn("%s: no memory timings registered\n", __func__); + + tegra->emc_node = of_parse_phandle(np, + "nvidia,external-memory-controller", 0); + if (!tegra->emc_node) + pr_warn("%s: couldn't find node for EMC driver\n", __func__); + + init.name = "emc"; + init.ops = &tegra_clk_emc_ops; + init.flags = 0; + init.parent_names = emc_parent_clk_names; + init.num_parents = ARRAY_SIZE(emc_parent_clk_names); + + tegra->hw.init = &init; + + clk = clk_register(NULL, &tegra->hw); + if (IS_ERR(clk)) + return clk; + + tegra->prev_parent = clk_get_parent_by_index( + tegra->hw.clk, emc_get_parent(&tegra->hw)); + tegra->changing_timing = false; + + /* Allow debugging tools to see the EMC clock */ + clk_register_clkdev(clk, "emc", "tegra-clk-debug"); + + clk_prepare_enable(clk); + + return clk; +}; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 11f857cd5f6a..e8cca3eac007 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -152,11 +152,6 @@ static unsigned long tegra124_input_freq[] = { [12] = 260000000, }; -static const char *mux_pllmcp_clkm[] = { - "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_c2", "pll_c3", -}; -#define mux_pllmcp_clkm_idx NULL - static struct div_nmp pllxc_nmp = { .divm_shift = 0, .divm_width = 8, @@ -791,7 +786,6 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_i2c2] = { .dt_id = TEGRA124_CLK_I2C2, .present = true }, [tegra_clk_uartc] = { .dt_id = TEGRA124_CLK_UARTC, .present = true }, [tegra_clk_mipi_cal] = { .dt_id = TEGRA124_CLK_MIPI_CAL, .present = true }, - [tegra_clk_emc] = { .dt_id = TEGRA124_CLK_EMC, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA124_CLK_USB2, .present = true }, [tegra_clk_usb3] = { .dt_id = TEGRA124_CLK_USB3, .present = true }, [tegra_clk_vde_8] = { .dt_id = TEGRA124_CLK_VDE, .present = true }, @@ -1127,13 +1121,7 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, periph_clk_enb_refcnt); clks[TEGRA124_CLK_DSIB] = clk; - /* emc mux */ - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, - clk_base + CLK_SOURCE_EMC, - 29, 3, 0, &emc_lock); - - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, &emc_lock); clks[TEGRA124_CLK_MC] = clk; @@ -1389,7 +1377,6 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { {TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0}, {TEGRA124_CLK_SATA, TEGRA124_CLK_PLL_P, 104000000, 0}, {TEGRA124_CLK_SATA_OOB, TEGRA124_CLK_PLL_P, 204000000, 0}, - {TEGRA124_CLK_EMC, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_MSELECT, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_CSITE, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_TSENSOR, TEGRA124_CLK_CLK_M, 400000, 0}, @@ -1513,6 +1500,10 @@ static void __init tegra124_132_clock_init_post(struct device_node *np) tegra_super_clk_gen4_init(clk_base, pmc_base, tegra124_clks, &pll_x_params); tegra_add_of_provider(np); + + clks[TEGRA124_CLK_EMC] = tegra_clk_register_emc(clk_base, np, + &emc_lock); + tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_cpu_car_ops = &tegra124_cpu_car_ops; diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 4b26509fc218..0af3e834dd24 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -679,7 +679,7 @@ static struct tegra_devclk devclks[] __initdata = { { .dev_id = "tegra30-dam.1", .dt_id = TEGRA30_CLK_DAM1 }, { .dev_id = "tegra30-dam.2", .dt_id = TEGRA30_CLK_DAM2 }, { .con_id = "hda", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA }, - { .con_id = "hda2codec", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X }, + { .con_id = "hda2codec_2x", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X }, { .dev_id = "spi_tegra.0", .dt_id = TEGRA30_CLK_SBC1 }, { .dev_id = "spi_tegra.1", .dt_id = TEGRA30_CLK_SBC2 }, { .dev_id = "spi_tegra.2", .dt_id = TEGRA30_CLK_SBC3 }, diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index d6ac00647faf..75ddc8ff8bd4 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -623,6 +623,18 @@ void tegra_super_clk_gen4_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, struct tegra_clk_pll_params *pll_params); +#ifdef CONFIG_TEGRA_CLK_EMC +struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, + spinlock_t *lock); +#else +static inline struct clk *tegra_clk_register_emc(void __iomem *base, + struct device_node *np, + spinlock_t *lock) +{ + return NULL; +} +#endif + void tegra114_clock_tune_cpu_trimmers_high(void); void tegra114_clock_tune_cpu_trimmers_low(void); void tegra114_clock_tune_cpu_trimmers_init(void); diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index d86bc46b93bd..19e543a32e2b 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -155,7 +155,7 @@ static int atl_clk_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops atl_clk_ops = { +static const struct clk_ops atl_clk_ops = { .enable = atl_clk_enable, .disable = atl_clk_disable, .is_enabled = atl_clk_is_enabled, @@ -167,7 +167,7 @@ const struct clk_ops atl_clk_ops = { static void __init of_dra7_atl_clock_setup(struct device_node *node) { struct dra7_atl_desc *clk_hw = NULL; - struct clk_init_data init = { 0 }; + struct clk_init_data init = { NULL }; const char **parent_names = NULL; struct clk *clk; @@ -252,6 +252,11 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) } clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + pr_err("%s: failed to get atl clock %d from provider\n", + __func__, i); + return PTR_ERR(clk); + } cdesc = to_atl_desc(__clk_get_hw(clk)); cdesc->cinfo = cinfo; diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 0ebe5c51062b..64bb5e8a3b8c 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -122,14 +122,14 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) if (i == CLK_MAX_MEMMAPS) { pr_err("clk-provider not found for %s!\n", node->name); - return ERR_PTR(-ENOENT); + return IOMEM_ERR_PTR(-ENOENT); } reg->index = i; if (of_property_read_u32_index(node, "reg", index, &val)) { pr_err("%s must have reg[%d]!\n", node->name, index); - return ERR_PTR(-EINVAL); + return IOMEM_ERR_PTR(-EINVAL); } reg->offset = val; diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index 35fe1085480c..b82ef07f3403 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -32,7 +32,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) int i; int num_clks; - num_clks = of_count_phandle_with_args(node, "clocks", "#clock-cells"); + num_clks = of_clk_get_parent_count(node); for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 11478a501c30..2aacf7a3bcae 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -177,7 +177,7 @@ cleanup: } #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_ATAGS) -void __iomem *_get_reg(u8 module, u16 offset) +static void __iomem *_get_reg(u8 module, u16 offset) { u32 reg; struct clk_omap_reg *reg_setup; diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index ffcd8e09e85b..730aa62454a2 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -621,13 +621,13 @@ static void __init ti_fapll_setup(struct device_node *node) /* Check for hardwired audio_pll_clk1 */ if (is_audio_pll_clk1(freq)) { - freq = 0; - div = 0; + freq = NULL; + div = NULL; } else { /* Does the synthesizer have a FREQ register? */ v = readl_relaxed(freq); if (!v) - freq = 0; + freq = NULL; } synth_clk = ti_fapll_synth_setup(fd, freq, div, output_instance, output_name, node->name, diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c index 80069c370a47..4626b97b7d83 100644 --- a/drivers/clk/ux500/u8500_clk.c +++ b/drivers/clk/ux500/u8500_clk.c @@ -116,11 +116,12 @@ void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk_register_clkdev(clk, NULL, "hdmi"); clk_register_clkdev(clk, "hdmi", "mcde"); - clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apeatclk", NULL, PRCMU_APEATCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "apeat"); - clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, - CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apetraceclk", NULL, PRCMU_APETRACECLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "apetrace"); clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 7b55ef89baa5..e319ef912dc6 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -166,8 +166,8 @@ void u8500_of_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); prcmu_clk[PRCMU_APEATCLK] = clk; - clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, - CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apetraceclk", NULL, PRCMU_APETRACECLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); prcmu_clk[PRCMU_APETRACECLK] = clk; clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c index c6e86a9a2aa3..a96dd8e53fdb 100644 --- a/drivers/clk/versatile/clk-sp810.c +++ b/drivers/clk/versatile/clk-sp810.c @@ -135,7 +135,7 @@ static struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec, return sp810->timerclken[clkspec->args[0]].clk; } -void __init clk_sp810_of_setup(struct device_node *node) +static void __init clk_sp810_of_setup(struct device_node *node) { struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL); const char *parent_names[2]; @@ -156,7 +156,7 @@ void __init clk_sp810_of_setup(struct device_node *node) "timclk"); parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index); - if (parent_names[0] <= 0 || parent_names[1] <= 0) { + if (!parent_names[0] || !parent_names[1]) { pr_warn("Failed to obtain parent clocks for SP810!\n"); return; } diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 40cb113be6af..de614384bb44 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -85,22 +85,29 @@ static DEFINE_SPINLOCK(canmioclk_lock); static DEFINE_SPINLOCK(dbgclk_lock); static DEFINE_SPINLOCK(aperclk_lock); -static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"}; -static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"}; -static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"}; +static const char *const armpll_parents[] __initconst = {"armpll_int", + "ps_clk"}; +static const char *const ddrpll_parents[] __initconst = {"ddrpll_int", + "ps_clk"}; +static const char *const iopll_parents[] __initconst = {"iopll_int", + "ps_clk"}; static const char *gem0_mux_parents[] __initdata = {"gem0_div1", "dummy_name"}; static const char *gem1_mux_parents[] __initdata = {"gem1_div1", "dummy_name"}; -static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", +static const char *const can0_mio_mux2_parents[] __initconst = {"can0_gate", "can0_mio_mux"}; -static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", +static const char *const can1_mio_mux2_parents[] __initconst = {"can1_gate", "can1_mio_mux"}; static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div", "dummy_name"}; -static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"}; -static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"}; -static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"}; -static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"}; +static const char *const dbgtrc_emio_input_names[] __initconst = { + "trace_emio_clk"}; +static const char *const gem0_emio_input_names[] __initconst = { + "gem0_emio_clk"}; +static const char *const gem1_emio_input_names[] __initconst = { + "gem1_emio_clk"}; +static const char *const swdt_ext_clk_input_names[] __initconst = { + "swdt_ext_clk"}; static void __init zynq_clk_register_fclk(enum zynq_clk fclk, const char *clk_name, void __iomem *fclk_ctrl_reg, |