diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-16 06:41:21 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-16 06:41:21 +0200 |
commit | db06f826ec12bf0701ea7fc0a3c0aa00b84417c8 (patch) | |
tree | 0f5cd2bb7af57574ae8a20bfc0e916512c5f2255 /drivers/clk | |
parent | Merge tag 'gpio-v4.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/lin... (diff) | |
parent | Merge branches 'clk-actions-s700', 'clk-exynos-unused', 'clk-qcom-dispcc-845'... (diff) | |
download | linux-db06f826ec12bf0701ea7fc0a3c0aa00b84417c8.tar.xz linux-db06f826ec12bf0701ea7fc0a3c0aa00b84417c8.zip |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd:
"The new and exciting feature this time around is in the clk core.
We've added duty cycle support to the clk API so that clk signal duty
cycle ratios can be adjusted while taking into account things like clk
dividers and clk tree hierarchy. So far only one SoC has implemented
support for this, but I expect there will be more to come in the
future.
Outside of the core, we have the usual pile of clk driver updates and
additions. The Amlogic meson driver got the most lines in the diffstat
this time around because it added support for a whole bunch of
hardware and duty cycle configuration. After that the Rockchip PX30,
Qualcomm SDM845, and Renesas SoC drivers fill in a majority of the
diff. We're left with the collection of non-critical fixes after that.
Overall it looks pretty quiet this time.
Core:
- Clk duty cycle support
- Proper CLK_SET_RATE_GATE support throughout the tree
New Drivers:
- Actions Semi Owl series S700 SoC clk driver
- Qualcomm SDM845 display clock controller
- i.MX6SX ocram_s clk support
- Uniphier NAND, USB3 PHY, and SPI clk support
- Qualcomm RPMh clk driver
- i.MX7D mailbox clk support
- Maxim 9485 Programmable Clock Generator
- expose 32 kHz PLL on PXA SoCs
- imx6sll GPIO clk gate support
- Atmel at91 I2S audio clk support
- SI544/SI514 clk on/off support
- i.MX6UL GPIO clock gates in CCM CCGR
- Renesas Crypto Engine clocks on R-Car H3
- Renesas clk support for the new RZ/N1D SoC
- Allwinner A64 display engine clock support
- support for Rockchip's PX30 SoC
- Amlogic Meson axg PCIe and audio clocks
- Amlogic Meson GEN CLK on gxbb, gxl and axg
Updates:
- remove an unused variable from Exynos4412 ISP driver
- fix a thinko bug in SCMI clk division logic
- add missing of_node_put()s in some i.MX clk drivers
- Tegra SDMMC clk jitter improvements with high speed signaling modes
- SPDX tagging for qcom and cs2000-cp drivers
- stop leaking con ids in __clk_put()
- fix a corner case in fixed factor clk probing where node is in DT
but parent clk is registered much later
- Marvell Armada 3700 clk_pm_cpu_get_parent() had an invalid return
value
- i.MX clk init arrays removed in place of CLK_IS_CRITICAL
- convert to CLK_IS_CRITICAL for i.MX51/53 driver
- fix Tegra BPMP driver oops when xlating a NULL clk
- proper default configuration for vic03 and vde clks on Tegra124
- mark Tegra memory controller clks as critical
- fix array bounds clamp in Tegra's emc determine_rate() op
- Ingenic i2s bit update and allow UDC clk to gate
- fix name of aspeed SDC clk define to have only one 'CLK'
- fix i.MX6QDL video clk parent
- critical clk markings for qcom SDM845
- fix Stratix10 mpu_free_clk and sdmmc_free_clk parents
- mark Rockchip's pclk_rkpwm_pmu as critical clock, due to it
supplying the pwm used to drive the logic supply of the rk3399
core"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (85 commits)
clk: rockchip: Add pclk_rkpwm_pmu to PMU critical clocks in rk3399
clk: cs2000-cp: convert to SPDX identifiers
clk: scmi: Fix the rounding of clock rate
clk: qcom: Add display clock controller driver for SDM845
clk: mvebu: armada-37xx-periph: Remove unused var num_parents
clk: samsung: Remove unused mout_user_aclk400_mcuisp_p4x12 variable
clk: actions: Add S700 SoC clock support
dt-bindings: clock: Add S700 support for Actions Semi Soc's
clk: actions: Add missing REGMAP_MMIO dependency
clk: uniphier: add clock frequency support for SPI
clk: uniphier: add more USB3 PHY clocks
clk: uniphier: add NAND 200MHz clock
clk: tegra: make sdmmc2 and sdmmc4 as sdmmc clocks
clk: tegra: Add sdmmc mux divider clock
clk: tegra: Refactor fractional divider calculation
clk: tegra: Fix includes required by fence_udelay()
clk: imx6sll: fix missing of_node_put()
clk: imx6ul: fix missing of_node_put()
clk: imx: add ocram_s clock for i.mx6sx
clk: mvebu: armada-37xx-periph: Fix wrong return value in get_parent
...
Diffstat (limited to 'drivers/clk')
99 files changed, 6845 insertions, 568 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 721572a8c429..292056bbb30e 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -45,6 +45,12 @@ config COMMON_CLK_MAX77686 This driver supports Maxim 77620/77686/77802 crystal oscillator clock. +config COMMON_CLK_MAX9485 + tristate "Maxim 9485 Programmable Clock Generator" + depends on I2C + help + This driver supports Maxim 9485 Programmable Audio Clock Generator + config COMMON_CLK_RK808 tristate "Clock driver for RK805/RK808/RK818" depends on MFD_RK808 diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 0bb25dd009d1..a84c5573cabe 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o diff --git a/drivers/clk/actions/Kconfig b/drivers/clk/actions/Kconfig index 8854adb37847..dc38c85a4833 100644 --- a/drivers/clk/actions/Kconfig +++ b/drivers/clk/actions/Kconfig @@ -1,14 +1,21 @@ config CLK_ACTIONS bool "Clock driver for Actions Semi SoCs" depends on ARCH_ACTIONS || COMPILE_TEST + select REGMAP_MMIO default ARCH_ACTIONS if CLK_ACTIONS # SoC Drivers +config CLK_OWL_S700 + bool "Support for the Actions Semi OWL S700 clocks" + depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST + default ARM64 && ARCH_ACTIONS + config CLK_OWL_S900 bool "Support for the Actions Semi OWL S900 clocks" depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST default ARM64 && ARCH_ACTIONS + endif diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 76e431434d10..78c17d56f991 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -9,4 +9,5 @@ clk-owl-y += owl-composite.o clk-owl-y += owl-pll.o # SoC support +obj-$(CONFIG_CLK_OWL_S700) += owl-s700.o obj-$(CONFIG_CLK_OWL_S900) += owl-s900.o diff --git a/drivers/clk/actions/owl-s700.c b/drivers/clk/actions/owl-s700.c new file mode 100644 index 000000000000..5e9531392ee5 --- /dev/null +++ b/drivers/clk/actions/owl-s700.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Actions Semi S700 clock driver + * + * Copyright (c) 2014 Actions Semi Inc. + * Author: David Liu <liuwei@actions-semi.com> + * + * Author: Pathiban Nallathambi <pn@denx.de> + * Author: Saravanan Sekar <sravanhome@gmail.com> + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "owl-common.h" +#include "owl-composite.h" +#include "owl-divider.h" +#include "owl-factor.h" +#include "owl-fixed-factor.h" +#include "owl-gate.h" +#include "owl-mux.h" +#include "owl-pll.h" + +#include <dt-bindings/clock/actions,s700-cmu.h> + +#define CMU_COREPLL (0x0000) +#define CMU_DEVPLL (0x0004) +#define CMU_DDRPLL (0x0008) +#define CMU_NANDPLL (0x000C) +#define CMU_DISPLAYPLL (0x0010) +#define CMU_AUDIOPLL (0x0014) +#define CMU_TVOUTPLL (0x0018) +#define CMU_BUSCLK (0x001C) +#define CMU_SENSORCLK (0x0020) +#define CMU_LCDCLK (0x0024) +#define CMU_DSIPLLCLK (0x0028) +#define CMU_CSICLK (0x002C) +#define CMU_DECLK (0x0030) +#define CMU_SICLK (0x0034) +#define CMU_BUSCLK1 (0x0038) +#define CMU_HDECLK (0x003C) +#define CMU_VDECLK (0x0040) +#define CMU_VCECLK (0x0044) +#define CMU_NANDCCLK (0x004C) +#define CMU_SD0CLK (0x0050) +#define CMU_SD1CLK (0x0054) +#define CMU_SD2CLK (0x0058) +#define CMU_UART0CLK (0x005C) +#define CMU_UART1CLK (0x0060) +#define CMU_UART2CLK (0x0064) +#define CMU_UART3CLK (0x0068) +#define CMU_UART4CLK (0x006C) +#define CMU_UART5CLK (0x0070) +#define CMU_UART6CLK (0x0074) +#define CMU_PWM0CLK (0x0078) +#define CMU_PWM1CLK (0x007C) +#define CMU_PWM2CLK (0x0080) +#define CMU_PWM3CLK (0x0084) +#define CMU_PWM4CLK (0x0088) +#define CMU_PWM5CLK (0x008C) +#define CMU_GPU3DCLK (0x0090) +#define CMU_CORECTL (0x009C) +#define CMU_DEVCLKEN0 (0x00A0) +#define CMU_DEVCLKEN1 (0x00A4) +#define CMU_DEVRST0 (0x00A8) +#define CMU_DEVRST1 (0x00AC) +#define CMU_USBPLL (0x00B0) +#define CMU_ETHERNETPLL (0x00B4) +#define CMU_CVBSPLL (0x00B8) +#define CMU_SSTSCLK (0x00C0) + +static struct clk_pll_table clk_audio_pll_table[] = { + {0, 45158400}, {1, 49152000}, + {0, 0}, +}; + +static struct clk_pll_table clk_cvbs_pll_table[] = { + {27, 29 * 12000000}, {28, 30 * 12000000}, {29, 31 * 12000000}, + {30, 32 * 12000000}, {31, 33 * 12000000}, {32, 34 * 12000000}, + {33, 35 * 12000000}, {34, 36 * 12000000}, {35, 37 * 12000000}, + {36, 38 * 12000000}, {37, 39 * 12000000}, {38, 40 * 12000000}, + {39, 41 * 12000000}, {40, 42 * 12000000}, {41, 43 * 12000000}, + {42, 44 * 12000000}, {43, 45 * 12000000}, {0, 0}, +}; + +/* pll clocks */ +static OWL_PLL_NO_PARENT(clk_core_pll, "core_pll", CMU_COREPLL, 12000000, 9, 0, 8, 4, 174, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_dev_pll, "dev_pll", CMU_DEVPLL, 6000000, 8, 0, 8, 8, 126, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_ddr_pll, "ddr_pll", CMU_DDRPLL, 6000000, 8, 0, 8, 2, 180, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_nand_pll, "nand_pll", CMU_NANDPLL, 6000000, 8, 0, 8, 2, 86, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_display_pll, "display_pll", CMU_DISPLAYPLL, 6000000, 8, 0, 8, 2, 140, NULL, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_cvbs_pll, "cvbs_pll", CMU_CVBSPLL, 0, 8, 0, 8, 27, 43, clk_cvbs_pll_table, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_audio_pll, "audio_pll", CMU_AUDIOPLL, 0, 4, 0, 1, 0, 0, clk_audio_pll_table, CLK_IGNORE_UNUSED); +static OWL_PLL_NO_PARENT(clk_ethernet_pll, "ethernet_pll", CMU_ETHERNETPLL, 500000000, 0, 0, 0, 0, 0, NULL, CLK_IGNORE_UNUSED); + +static const char *cpu_clk_mux_p[] = {"losc", "hosc", "core_pll", "noc1_clk_div"}; +static const char *dev_clk_p[] = { "hosc", "dev_pll"}; +static const char *noc_clk_mux_p[] = { "dev_clk", "display_pll", "nand_pll", "ddr_pll", "cvbs_pll"}; + +static const char *csi_clk_mux_p[] = { "display_pll", "dev_clk"}; +static const char *de_clk_mux_p[] = { "display_pll", "dev_clk"}; +static const char *hde_clk_mux_p[] = { "dev_clk", "display_pll", "nand_pll", "ddr_pll"}; +static const char *nand_clk_mux_p[] = { "nand_pll", "display_pll", "dev_clk", "ddr_pll"}; +static const char *sd_clk_mux_p[] = { "dev_clk", "nand_pll", }; +static const char *uart_clk_mux_p[] = { "hosc", "dev_pll"}; +static const char *pwm_clk_mux_p[] = { "losc", "hosc"}; +static const char *gpu_clk_mux_p[] = { "dev_clk", "display_pll", "nand_pll", "ddr_clk", "cvbs_pll"}; +static const char *lcd_clk_mux_p[] = { "display_pll", "dev_clk" }; +static const char *i2s_clk_mux_p[] = { "audio_pll" }; +static const char *sensor_clk_mux_p[] = { "hosc", "si"}; + +/* mux clocks */ +static OWL_MUX(clk_cpu, "cpu_clk", cpu_clk_mux_p, CMU_BUSCLK, 0, 2, CLK_SET_RATE_PARENT); +static OWL_MUX(clk_dev, "dev_clk", dev_clk_p, CMU_DEVPLL, 12, 1, CLK_SET_RATE_PARENT); +static OWL_MUX(clk_noc0_clk_mux, "noc0_clk_mux", noc_clk_mux_p, CMU_BUSCLK, 4, 3, CLK_SET_RATE_PARENT); +static OWL_MUX(clk_noc1_clk_mux, "noc1_clk_mux", noc_clk_mux_p, CMU_BUSCLK1, 4, 3, CLK_SET_RATE_PARENT); +static OWL_MUX(clk_hp_clk_mux, "hp_clk_mux", noc_clk_mux_p, CMU_BUSCLK1, 8, 3, CLK_SET_RATE_PARENT); + +static struct clk_factor_table sd_factor_table[] = { + /* bit0 ~ 4 */ + {0, 1, 1}, {1, 1, 2}, {2, 1, 3}, {3, 1, 4}, + {4, 1, 5}, {5, 1, 6}, {6, 1, 7}, {7, 1, 8}, + {8, 1, 9}, {9, 1, 10}, {10, 1, 11}, {11, 1, 12}, + {12, 1, 13}, {13, 1, 14}, {14, 1, 15}, {15, 1, 16}, + {16, 1, 17}, {17, 1, 18}, {18, 1, 19}, {19, 1, 20}, + {20, 1, 21}, {21, 1, 22}, {22, 1, 23}, {23, 1, 24}, + {24, 1, 25}, {25, 1, 26}, + + /* bit8: /128 */ + {256, 1, 1 * 128}, {257, 1, 2 * 128}, {258, 1, 3 * 128}, {259, 1, 4 * 128}, + {260, 1, 5 * 128}, {261, 1, 6 * 128}, {262, 1, 7 * 128}, {263, 1, 8 * 128}, + {264, 1, 9 * 128}, {265, 1, 10 * 128}, {266, 1, 11 * 128}, {267, 1, 12 * 128}, + {268, 1, 13 * 128}, {269, 1, 14 * 128}, {270, 1, 15 * 128}, {271, 1, 16 * 128}, + {272, 1, 17 * 128}, {273, 1, 18 * 128}, {274, 1, 19 * 128}, {275, 1, 20 * 128}, + {276, 1, 21 * 128}, {277, 1, 22 * 128}, {278, 1, 23 * 128}, {279, 1, 24 * 128}, + {280, 1, 25 * 128}, {281, 1, 26 * 128}, + + {0, 0}, +}; + +static struct clk_factor_table lcd_factor_table[] = { + /* bit0 ~ 3 */ + {0, 1, 1}, {1, 1, 2}, {2, 1, 3}, {3, 1, 4}, + {4, 1, 5}, {5, 1, 6}, {6, 1, 7}, {7, 1, 8}, + {8, 1, 9}, {9, 1, 10}, {10, 1, 11}, {11, 1, 12}, + + /* bit8: /7 */ + {256, 1, 1 * 7}, {257, 1, 2 * 7}, {258, 1, 3 * 7}, {259, 1, 4 * 7}, + {260, 1, 5 * 7}, {261, 1, 6 * 7}, {262, 1, 7 * 7}, {263, 1, 8 * 7}, + {264, 1, 9 * 7}, {265, 1, 10 * 7}, {266, 1, 11 * 7}, {267, 1, 12 * 7}, + {0, 0}, +}; + +static struct clk_div_table hdmia_div_table[] = { + {0, 1}, {1, 2}, {2, 3}, {3, 4}, + {4, 6}, {5, 8}, {6, 12}, {7, 16}, + {8, 24}, + {0, 0}, +}; + +static struct clk_div_table rmii_div_table[] = { + {0, 4}, {1, 10}, +}; + +/* divider clocks */ +static OWL_DIVIDER(clk_noc0, "noc0_clk", "noc0_clk_mux", CMU_BUSCLK, 16, 2, NULL, 0, 0); +static OWL_DIVIDER(clk_noc1, "noc1_clk", "noc1_clk_mux", CMU_BUSCLK1, 16, 2, NULL, 0, 0); +static OWL_DIVIDER(clk_noc1_clk_div, "noc1_clk_div", "noc1_clk", CMU_BUSCLK1, 20, 1, NULL, 0, 0); +static OWL_DIVIDER(clk_hp_clk_div, "hp_clk_div", "hp_clk_mux", CMU_BUSCLK1, 12, 2, NULL, 0, 0); +static OWL_DIVIDER(clk_ahb, "ahb_clk", "hp_clk_div", CMU_BUSCLK1, 2, 2, NULL, 0, 0); +static OWL_DIVIDER(clk_apb, "apb_clk", "ahb_clk", CMU_BUSCLK1, 14, 2, NULL, 0, 0); +static OWL_DIVIDER(clk_sensor0, "sensor0", "sensor_src", CMU_SENSORCLK, 0, 4, NULL, 0, 0); +static OWL_DIVIDER(clk_sensor1, "sensor1", "sensor_src", CMU_SENSORCLK, 8, 4, NULL, 0, 0); +static OWL_DIVIDER(clk_rmii_ref, "rmii_ref", "ethernet_pll", CMU_ETHERNETPLL, 2, 1, rmii_div_table, 0, 0); + +static struct clk_factor_table de_factor_table[] = { + {0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 2, 5}, + {4, 1, 3}, {5, 1, 4}, {6, 1, 6}, {7, 1, 8}, + {8, 1, 12}, {0, 0, 0}, +}; + +static struct clk_factor_table hde_factor_table[] = { + {0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 2, 5}, + {4, 1, 3}, {5, 1, 4}, {6, 1, 6}, {7, 1, 8}, + {0, 0, 0}, +}; + +/* gate clocks */ +static OWL_GATE(clk_gpio, "gpio", "apb_clk", CMU_DEVCLKEN1, 25, 0, 0); +static OWL_GATE(clk_dmac, "dmac", "hp_clk_div", CMU_DEVCLKEN0, 17, 0, 0); +static OWL_GATE(clk_timer, "timer", "hosc", CMU_DEVCLKEN1, 22, 0, 0); +static OWL_GATE_NO_PARENT(clk_dsi, "dsi_clk", CMU_DEVCLKEN0, 2, 0, 0); +static OWL_GATE_NO_PARENT(clk_tvout, "tvout_clk", CMU_DEVCLKEN0, 3, 0, 0); +static OWL_GATE_NO_PARENT(clk_hdmi_dev, "hdmi_dev", CMU_DEVCLKEN0, 5, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb3_480mpll0, "usb3_480mpll0", CMU_USBPLL, 3, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb3_480mphy0, "usb3_480mphy0", CMU_USBPLL, 2, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb3_5gphy, "usb3_5gphy", CMU_USBPLL, 1, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb3_cce, "usb3_cce", CMU_DEVCLKEN0, 25, 0, 0); +static OWL_GATE(clk_i2c0, "i2c0", "hosc", CMU_DEVCLKEN1, 0, 0, 0); +static OWL_GATE(clk_i2c1, "i2c1", "hosc", CMU_DEVCLKEN1, 1, 0, 0); +static OWL_GATE(clk_i2c2, "i2c2", "hosc", CMU_DEVCLKEN1, 2, 0, 0); +static OWL_GATE(clk_i2c3, "i2c3", "hosc", CMU_DEVCLKEN1, 3, 0, 0); +static OWL_GATE(clk_spi0, "spi0", "ahb_clk", CMU_DEVCLKEN1, 4, 0, 0); +static OWL_GATE(clk_spi1, "spi1", "ahb_clk", CMU_DEVCLKEN1, 5, 0, 0); +static OWL_GATE(clk_spi2, "spi2", "ahb_clk", CMU_DEVCLKEN1, 6, 0, 0); +static OWL_GATE(clk_spi3, "spi3", "ahb_clk", CMU_DEVCLKEN1, 7, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb2h0_pllen, "usbh0_pllen", CMU_USBPLL, 12, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb2h0_phy, "usbh0_phy", CMU_USBPLL, 10, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb2h0_cce, "usbh0_cce", CMU_DEVCLKEN0, 26, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb2h1_pllen, "usbh1_pllen", CMU_USBPLL, 13, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb2h1_phy, "usbh1_phy", CMU_USBPLL, 11, 0, 0); +static OWL_GATE_NO_PARENT(clk_usb2h1_cce, "usbh1_cce", CMU_DEVCLKEN0, 27, 0, 0); +static OWL_GATE_NO_PARENT(clk_irc_switch, "irc_switch", CMU_DEVCLKEN1, 15, 0, 0); + +/* composite clocks */ + +static OWL_COMP_DIV(clk_csi, "csi", csi_clk_mux_p, + OWL_MUX_HW(CMU_CSICLK, 4, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 13, 0), + OWL_DIVIDER_HW(CMU_CSICLK, 0, 4, 0, NULL), + 0); + +static OWL_COMP_DIV(clk_si, "si", csi_clk_mux_p, + OWL_MUX_HW(CMU_SICLK, 4, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 14, 0), + OWL_DIVIDER_HW(CMU_SICLK, 0, 4, 0, NULL), + 0); + +static OWL_COMP_FACTOR(clk_de, "de", de_clk_mux_p, + OWL_MUX_HW(CMU_DECLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 0, 0), + OWL_FACTOR_HW(CMU_DECLK, 0, 3, 0, de_factor_table), + 0); + +static OWL_COMP_FACTOR(clk_hde, "hde", hde_clk_mux_p, + OWL_MUX_HW(CMU_HDECLK, 4, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 9, 0), + OWL_FACTOR_HW(CMU_HDECLK, 0, 3, 0, hde_factor_table), + 0); + +static OWL_COMP_FACTOR(clk_vde, "vde", hde_clk_mux_p, + OWL_MUX_HW(CMU_VDECLK, 4, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 10, 0), + OWL_FACTOR_HW(CMU_VDECLK, 0, 3, 0, hde_factor_table), + 0); + +static OWL_COMP_FACTOR(clk_vce, "vce", hde_clk_mux_p, + OWL_MUX_HW(CMU_VCECLK, 4, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 11, 0), + OWL_FACTOR_HW(CMU_VCECLK, 0, 3, 0, hde_factor_table), + 0); + +static OWL_COMP_DIV(clk_nand, "nand", nand_clk_mux_p, + OWL_MUX_HW(CMU_NANDCCLK, 8, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 21, 0), + OWL_DIVIDER_HW(CMU_NANDCCLK, 0, 3, 0, NULL), + CLK_SET_RATE_PARENT); + +static OWL_COMP_FACTOR(clk_sd0, "sd0", sd_clk_mux_p, + OWL_MUX_HW(CMU_SD0CLK, 9, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 22, 0), + OWL_FACTOR_HW(CMU_SD0CLK, 0, 9, 0, sd_factor_table), + 0); + +static OWL_COMP_FACTOR(clk_sd1, "sd1", sd_clk_mux_p, + OWL_MUX_HW(CMU_SD1CLK, 9, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 23, 0), + OWL_FACTOR_HW(CMU_SD1CLK, 0, 9, 0, sd_factor_table), + 0); + +static OWL_COMP_FACTOR(clk_sd2, "sd2", sd_clk_mux_p, + OWL_MUX_HW(CMU_SD2CLK, 9, 1), + OWL_GATE_HW(CMU_DEVCLKEN0, 24, 0), + OWL_FACTOR_HW(CMU_SD2CLK, 0, 9, 0, sd_factor_table), + 0); + +static OWL_COMP_DIV(clk_uart0, "uart0", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART0CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 8, 0), + OWL_DIVIDER_HW(CMU_UART0CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_uart1, "uart1", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART1CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 9, 0), + OWL_DIVIDER_HW(CMU_UART1CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_uart2, "uart2", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART2CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 10, 0), + OWL_DIVIDER_HW(CMU_UART2CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_uart3, "uart3", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART3CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 11, 0), + OWL_DIVIDER_HW(CMU_UART3CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_uart4, "uart4", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART4CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 12, 0), + OWL_DIVIDER_HW(CMU_UART4CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_uart5, "uart5", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART5CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 13, 0), + OWL_DIVIDER_HW(CMU_UART5CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_uart6, "uart6", uart_clk_mux_p, + OWL_MUX_HW(CMU_UART6CLK, 16, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 14, 0), + OWL_DIVIDER_HW(CMU_UART6CLK, 0, 9, CLK_DIVIDER_ROUND_CLOSEST, NULL), + 0); + +static OWL_COMP_DIV(clk_pwm0, "pwm0", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM0CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 16, 0), + OWL_DIVIDER_HW(CMU_PWM0CLK, 0, 10, 0, NULL), + CLK_IGNORE_UNUSED); + +static OWL_COMP_DIV(clk_pwm1, "pwm1", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM1CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 17, 0), + OWL_DIVIDER_HW(CMU_PWM1CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(clk_pwm2, "pwm2", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM2CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 18, 0), + OWL_DIVIDER_HW(CMU_PWM2CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(clk_pwm3, "pwm3", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM3CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 19, 0), + OWL_DIVIDER_HW(CMU_PWM3CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(clk_pwm4, "pwm4", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM4CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 20, 0), + OWL_DIVIDER_HW(CMU_PWM4CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_DIV(clk_pwm5, "pwm5", pwm_clk_mux_p, + OWL_MUX_HW(CMU_PWM5CLK, 12, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 21, 0), + OWL_DIVIDER_HW(CMU_PWM5CLK, 0, 10, 0, NULL), + 0); + +static OWL_COMP_FACTOR(clk_gpu3d, "gpu3d", gpu_clk_mux_p, + OWL_MUX_HW(CMU_GPU3DCLK, 4, 3), + OWL_GATE_HW(CMU_DEVCLKEN0, 8, 0), + OWL_FACTOR_HW(CMU_GPU3DCLK, 0, 3, 0, hde_factor_table), + 0); + +static OWL_COMP_FACTOR(clk_lcd, "lcd", lcd_clk_mux_p, + OWL_MUX_HW(CMU_LCDCLK, 12, 2), + OWL_GATE_HW(CMU_DEVCLKEN0, 1, 0), + OWL_FACTOR_HW(CMU_LCDCLK, 0, 9, 0, lcd_factor_table), + 0); + +static OWL_COMP_DIV(clk_hdmi_audio, "hdmia", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), /*CMU_AUDIOPLL 24,1 unused*/ + OWL_GATE_HW(CMU_DEVCLKEN1, 28, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 24, 4, 0, hdmia_div_table), + 0); + +static OWL_COMP_DIV(clk_i2srx, "i2srx", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 27, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 20, 4, 0, hdmia_div_table), + 0); + +static OWL_COMP_DIV(clk_i2stx, "i2stx", i2s_clk_mux_p, + OWL_MUX_HW(CMU_AUDIOPLL, 24, 1), + OWL_GATE_HW(CMU_DEVCLKEN1, 26, 0), + OWL_DIVIDER_HW(CMU_AUDIOPLL, 16, 4, 0, hdmia_div_table), + 0); + +/* for bluetooth pcm communication */ +static OWL_COMP_FIXED_FACTOR(clk_pcm1, "pcm1", "audio_pll", + OWL_GATE_HW(CMU_DEVCLKEN1, 31, 0), + 1, 2, 0); + +static OWL_COMP_DIV(clk_sensor_src, "sensor_src", sensor_clk_mux_p, + OWL_MUX_HW(CMU_SENSORCLK, 4, 1), + {0}, + OWL_DIVIDER_HW(CMU_SENSORCLK, 5, 2, 0, NULL), + 0); + +static OWL_COMP_FIXED_FACTOR(clk_ethernet, "ethernet", "ethernet_pll", + OWL_GATE_HW(CMU_DEVCLKEN1, 23, 0), + 1, 20, 0); + +static OWL_COMP_DIV_FIXED(clk_thermal_sensor, "thermal_sensor", "hosc", + OWL_GATE_HW(CMU_DEVCLKEN0, 31, 0), + OWL_DIVIDER_HW(CMU_SSTSCLK, 20, 10, 0, NULL), + 0); + +static struct owl_clk_common *s700_clks[] = { + &clk_core_pll.common, + &clk_dev_pll.common, + &clk_ddr_pll.common, + &clk_nand_pll.common, + &clk_display_pll.common, + &clk_cvbs_pll .common, + &clk_audio_pll.common, + &clk_ethernet_pll.common, + &clk_cpu.common, + &clk_dev.common, + &clk_ahb.common, + &clk_apb.common, + &clk_dmac.common, + &clk_noc0_clk_mux.common, + &clk_noc1_clk_mux.common, + &clk_hp_clk_mux.common, + &clk_hp_clk_div.common, + &clk_noc1_clk_div.common, + &clk_noc0.common, + &clk_noc1.common, + &clk_sensor_src.common, + &clk_gpio.common, + &clk_timer.common, + &clk_dsi.common, + &clk_csi.common, + &clk_si.common, + &clk_de.common, + &clk_hde.common, + &clk_vde.common, + &clk_vce.common, + &clk_nand.common, + &clk_sd0.common, + &clk_sd1.common, + &clk_sd2.common, + &clk_uart0.common, + &clk_uart1.common, + &clk_uart2.common, + &clk_uart3.common, + &clk_uart4.common, + &clk_uart5.common, + &clk_uart6.common, + &clk_pwm0.common, + &clk_pwm1.common, + &clk_pwm2.common, + &clk_pwm3.common, + &clk_pwm4.common, + &clk_pwm5.common, + &clk_gpu3d.common, + &clk_i2c0.common, + &clk_i2c1.common, + &clk_i2c2.common, + &clk_i2c3.common, + &clk_spi0.common, + &clk_spi1.common, + &clk_spi2.common, + &clk_spi3.common, + &clk_usb3_480mpll0.common, + &clk_usb3_480mphy0.common, + &clk_usb3_5gphy.common, + &clk_usb3_cce.common, + &clk_lcd.common, + &clk_hdmi_audio.common, + &clk_i2srx.common, + &clk_i2stx.common, + &clk_sensor0.common, + &clk_sensor1.common, + &clk_hdmi_dev.common, + &clk_ethernet.common, + &clk_rmii_ref.common, + &clk_usb2h0_pllen.common, + &clk_usb2h0_phy.common, + &clk_usb2h0_cce.common, + &clk_usb2h1_pllen.common, + &clk_usb2h1_phy.common, + &clk_usb2h1_cce.common, + &clk_tvout.common, + &clk_thermal_sensor.common, + &clk_irc_switch.common, + &clk_pcm1.common, +}; + +static struct clk_hw_onecell_data s700_hw_clks = { + .hws = { + [CLK_CORE_PLL] = &clk_core_pll.common.hw, + [CLK_DEV_PLL] = &clk_dev_pll.common.hw, + [CLK_DDR_PLL] = &clk_ddr_pll.common.hw, + [CLK_NAND_PLL] = &clk_nand_pll.common.hw, + [CLK_DISPLAY_PLL] = &clk_display_pll.common.hw, + [CLK_CVBS_PLL] = &clk_cvbs_pll .common.hw, + [CLK_AUDIO_PLL] = &clk_audio_pll.common.hw, + [CLK_ETHERNET_PLL] = &clk_ethernet_pll.common.hw, + [CLK_CPU] = &clk_cpu.common.hw, + [CLK_DEV] = &clk_dev.common.hw, + [CLK_AHB] = &clk_ahb.common.hw, + [CLK_APB] = &clk_apb.common.hw, + [CLK_DMAC] = &clk_dmac.common.hw, + [CLK_NOC0_CLK_MUX] = &clk_noc0_clk_mux.common.hw, + [CLK_NOC1_CLK_MUX] = &clk_noc1_clk_mux.common.hw, + [CLK_HP_CLK_MUX] = &clk_hp_clk_mux.common.hw, + [CLK_HP_CLK_DIV] = &clk_hp_clk_div.common.hw, + [CLK_NOC1_CLK_DIV] = &clk_noc1_clk_div.common.hw, + [CLK_NOC0] = &clk_noc0.common.hw, + [CLK_NOC1] = &clk_noc1.common.hw, + [CLK_SENOR_SRC] = &clk_sensor_src.common.hw, + [CLK_GPIO] = &clk_gpio.common.hw, + [CLK_TIMER] = &clk_timer.common.hw, + [CLK_DSI] = &clk_dsi.common.hw, + [CLK_CSI] = &clk_csi.common.hw, + [CLK_SI] = &clk_si.common.hw, + [CLK_DE] = &clk_de.common.hw, + [CLK_HDE] = &clk_hde.common.hw, + [CLK_VDE] = &clk_vde.common.hw, + [CLK_VCE] = &clk_vce.common.hw, + [CLK_NAND] = &clk_nand.common.hw, + [CLK_SD0] = &clk_sd0.common.hw, + [CLK_SD1] = &clk_sd1.common.hw, + [CLK_SD2] = &clk_sd2.common.hw, + [CLK_UART0] = &clk_uart0.common.hw, + [CLK_UART1] = &clk_uart1.common.hw, + [CLK_UART2] = &clk_uart2.common.hw, + [CLK_UART3] = &clk_uart3.common.hw, + [CLK_UART4] = &clk_uart4.common.hw, + [CLK_UART5] = &clk_uart5.common.hw, + [CLK_UART6] = &clk_uart6.common.hw, + [CLK_PWM0] = &clk_pwm0.common.hw, + [CLK_PWM1] = &clk_pwm1.common.hw, + [CLK_PWM2] = &clk_pwm2.common.hw, + [CLK_PWM3] = &clk_pwm3.common.hw, + [CLK_PWM4] = &clk_pwm4.common.hw, + [CLK_PWM5] = &clk_pwm5.common.hw, + [CLK_GPU3D] = &clk_gpu3d.common.hw, + [CLK_I2C0] = &clk_i2c0.common.hw, + [CLK_I2C1] = &clk_i2c1.common.hw, + [CLK_I2C2] = &clk_i2c2.common.hw, + [CLK_I2C3] = &clk_i2c3.common.hw, + [CLK_SPI0] = &clk_spi0.common.hw, + [CLK_SPI1] = &clk_spi1.common.hw, + [CLK_SPI2] = &clk_spi2.common.hw, + [CLK_SPI3] = &clk_spi3.common.hw, + [CLK_USB3_480MPLL0] = &clk_usb3_480mpll0.common.hw, + [CLK_USB3_480MPHY0] = &clk_usb3_480mphy0.common.hw, + [CLK_USB3_5GPHY] = &clk_usb3_5gphy.common.hw, + [CLK_USB3_CCE] = &clk_usb3_cce.common.hw, + [CLK_LCD] = &clk_lcd.common.hw, + [CLK_HDMI_AUDIO] = &clk_hdmi_audio.common.hw, + [CLK_I2SRX] = &clk_i2srx.common.hw, + [CLK_I2STX] = &clk_i2stx.common.hw, + [CLK_SENSOR0] = &clk_sensor0.common.hw, + [CLK_SENSOR1] = &clk_sensor1.common.hw, + [CLK_HDMI_DEV] = &clk_hdmi_dev.common.hw, + [CLK_ETHERNET] = &clk_ethernet.common.hw, + [CLK_RMII_REF] = &clk_rmii_ref.common.hw, + [CLK_USB2H0_PLLEN] = &clk_usb2h0_pllen.common.hw, + [CLK_USB2H0_PHY] = &clk_usb2h0_phy.common.hw, + [CLK_USB2H0_CCE] = &clk_usb2h0_cce.common.hw, + [CLK_USB2H1_PLLEN] = &clk_usb2h1_pllen.common.hw, + [CLK_USB2H1_PHY] = &clk_usb2h1_phy.common.hw, + [CLK_USB2H1_CCE] = &clk_usb2h1_cce.common.hw, + [CLK_TVOUT] = &clk_tvout.common.hw, + [CLK_THERMAL_SENSOR] = &clk_thermal_sensor.common.hw, + [CLK_IRC_SWITCH] = &clk_irc_switch.common.hw, + [CLK_PCM1] = &clk_pcm1.common.hw, + }, + .num = CLK_NR_CLKS, +}; + +static const struct owl_clk_desc s700_clk_desc = { + .clks = s700_clks, + .num_clks = ARRAY_SIZE(s700_clks), + + .hw_clks = &s700_hw_clks, +}; + +static int s700_clk_probe(struct platform_device *pdev) +{ + const struct owl_clk_desc *desc; + + desc = &s700_clk_desc; + owl_clk_regmap_init(pdev, desc); + + return owl_clk_probe(&pdev->dev, desc->hw_clks); +} + +static const struct of_device_id s700_clk_of_match[] = { + { .compatible = "actions,s700-cmu", }, + { /* sentinel */ } +}; + +static struct platform_driver s700_clk_driver = { + .probe = s700_clk_probe, + .driver = { + .name = "s700-cmu", + .of_match_table = s700_clk_of_match + }, +}; + +static int __init s700_clk_init(void) +{ + return platform_driver_register(&s700_clk_driver); +} +core_initcall(s700_clk_init); diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 082596f37c1d..facc169ebb68 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o +obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c new file mode 100644 index 000000000000..f0c3c3079f04 --- /dev/null +++ b/drivers/clk/at91/clk-i2s-mux.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Microchip Technology Inc, + * Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + * + * + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <soc/at91/atmel-sfr.h> + +#define I2S_BUS_NR 2 + +struct clk_i2s_mux { + struct clk_hw hw; + struct regmap *regmap; + u8 bus_id; +}; + +#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw) + +static u8 clk_i2s_mux_get_parent(struct clk_hw *hw) +{ + struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); + u32 val; + + regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val); + + return (val & BIT(mux->bus_id)) >> mux->bus_id; +} + +static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); + + return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL, + BIT(mux->bus_id), index << mux->bus_id); +} + +static const struct clk_ops clk_i2s_mux_ops = { + .get_parent = clk_i2s_mux_get_parent, + .set_parent = clk_i2s_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +static struct clk_hw * __init +at91_clk_i2s_mux_register(struct regmap *regmap, const char *name, + const char * const *parent_names, + unsigned int num_parents, u8 bus_id) +{ + struct clk_init_data init = {}; + struct clk_i2s_mux *i2s_ck; + int ret; + + i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL); + if (!i2s_ck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_i2s_mux_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + + i2s_ck->hw.init = &init; + i2s_ck->bus_id = bus_id; + i2s_ck->regmap = regmap; + + ret = clk_hw_register(NULL, &i2s_ck->hw); + if (ret) { + kfree(i2s_ck); + return ERR_PTR(ret); + } + + return &i2s_ck->hw; +} + +static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) +{ + struct regmap *regmap_sfr; + u8 bus_id; + const char *parent_names[2]; + struct device_node *i2s_mux_np; + struct clk_hw *hw; + int ret; + + regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); + if (IS_ERR(regmap_sfr)) + return; + + for_each_child_of_node(np, i2s_mux_np) { + if (of_property_read_u8(i2s_mux_np, "reg", &bus_id)) + continue; + + if (bus_id > I2S_BUS_NR) + continue; + + ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2); + if (ret != 2) + continue; + + hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name, + parent_names, 2, bus_id); + if (IS_ERR(hw)) + continue; + + of_clk_add_hw_provider(i2s_mux_np, of_clk_hw_simple_get, hw); + } +} + +CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux", + of_sama5d2_clk_i2s_mux_setup); diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c index 7b70a074095d..596136793fc4 100644 --- a/drivers/clk/clk-aspeed.c +++ b/drivers/clk/clk-aspeed.c @@ -109,7 +109,7 @@ static const struct aspeed_gate_data aspeed_gates[] = { [ASPEED_CLK_GATE_RSACLK] = { 24, -1, "rsaclk-gate", NULL, 0 }, /* RSA */ [ASPEED_CLK_GATE_UART3CLK] = { 25, -1, "uart3clk-gate", "uart", 0 }, /* UART3 */ [ASPEED_CLK_GATE_UART4CLK] = { 26, -1, "uart4clk-gate", "uart", 0 }, /* UART4 */ - [ASPEED_CLK_GATE_SDCLKCLK] = { 27, 16, "sdclk-gate", NULL, 0 }, /* SDIO/SD */ + [ASPEED_CLK_GATE_SDCLK] = { 27, 16, "sdclk-gate", NULL, 0 }, /* SDIO/SD */ [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ }; diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index a2f8c42e527a..92bc4aca0f95 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * CS2000 -- CIRRUS LOGIC Fractional-N Clock Synthesizer & Clock Multiplier * * Copyright (C) 2015 Renesas Electronics Corporation * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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/clk-provider.h> #include <linux/delay.h> diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index a5d402de5584..20724abd38bd 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -177,8 +177,15 @@ static struct clk *_of_fixed_factor_clk_setup(struct device_node *node) clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags, mult, div); - if (IS_ERR(clk)) + if (IS_ERR(clk)) { + /* + * If parent clock is not registered, registration would fail. + * Clear OF_POPULATED flag so that clock registration can be + * attempted again from probe function. + */ + of_node_clear_flag(node, OF_POPULATED); return clk; + } ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); if (ret) { diff --git a/drivers/clk/clk-max9485.c b/drivers/clk/clk-max9485.c new file mode 100644 index 000000000000..5e80f3d090f3 --- /dev/null +++ b/drivers/clk/clk-max9485.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> + +#include <dt-bindings/clock/maxim,max9485.h> + +#define MAX9485_NUM_CLKS 4 + +/* This chip has only one register of 8 bit width. */ + +#define MAX9485_FS_12KHZ (0 << 0) +#define MAX9485_FS_32KHZ (1 << 0) +#define MAX9485_FS_44_1KHZ (2 << 0) +#define MAX9485_FS_48KHZ (3 << 0) + +#define MAX9485_SCALE_256 (0 << 2) +#define MAX9485_SCALE_384 (1 << 2) +#define MAX9485_SCALE_768 (2 << 2) + +#define MAX9485_DOUBLE BIT(4) +#define MAX9485_CLKOUT1_ENABLE BIT(5) +#define MAX9485_CLKOUT2_ENABLE BIT(6) +#define MAX9485_MCLK_ENABLE BIT(7) +#define MAX9485_FREQ_MASK 0x1f + +struct max9485_rate { + unsigned long out; + u8 reg_value; +}; + +/* + * Ordered by frequency. For frequency the hardware can generate with + * multiple settings, the one with lowest jitter is listed first. + */ +static const struct max9485_rate max9485_rates[] = { + { 3072000, MAX9485_FS_12KHZ | MAX9485_SCALE_256 }, + { 4608000, MAX9485_FS_12KHZ | MAX9485_SCALE_384 }, + { 8192000, MAX9485_FS_32KHZ | MAX9485_SCALE_256 }, + { 9126000, MAX9485_FS_12KHZ | MAX9485_SCALE_768 }, + { 11289600, MAX9485_FS_44_1KHZ | MAX9485_SCALE_256 }, + { 12288000, MAX9485_FS_48KHZ | MAX9485_SCALE_256 }, + { 12288000, MAX9485_FS_32KHZ | MAX9485_SCALE_384 }, + { 16384000, MAX9485_FS_32KHZ | MAX9485_SCALE_256 | MAX9485_DOUBLE }, + { 16934400, MAX9485_FS_44_1KHZ | MAX9485_SCALE_384 }, + { 18384000, MAX9485_FS_48KHZ | MAX9485_SCALE_384 }, + { 22579200, MAX9485_FS_44_1KHZ | MAX9485_SCALE_256 | MAX9485_DOUBLE }, + { 24576000, MAX9485_FS_48KHZ | MAX9485_SCALE_256 | MAX9485_DOUBLE }, + { 24576000, MAX9485_FS_32KHZ | MAX9485_SCALE_384 | MAX9485_DOUBLE }, + { 24576000, MAX9485_FS_32KHZ | MAX9485_SCALE_768 }, + { 33868800, MAX9485_FS_44_1KHZ | MAX9485_SCALE_384 | MAX9485_DOUBLE }, + { 33868800, MAX9485_FS_44_1KHZ | MAX9485_SCALE_768 }, + { 36864000, MAX9485_FS_48KHZ | MAX9485_SCALE_384 | MAX9485_DOUBLE }, + { 36864000, MAX9485_FS_48KHZ | MAX9485_SCALE_768 }, + { 49152000, MAX9485_FS_32KHZ | MAX9485_SCALE_768 | MAX9485_DOUBLE }, + { 67737600, MAX9485_FS_44_1KHZ | MAX9485_SCALE_768 | MAX9485_DOUBLE }, + { 73728000, MAX9485_FS_48KHZ | MAX9485_SCALE_768 | MAX9485_DOUBLE }, + { } /* sentinel */ +}; + +struct max9485_driver_data; + +struct max9485_clk_hw { + struct clk_hw hw; + struct clk_init_data init; + u8 enable_bit; + struct max9485_driver_data *drvdata; +}; + +struct max9485_driver_data { + struct clk *xclk; + struct i2c_client *client; + u8 reg_value; + struct regulator *supply; + struct gpio_desc *reset_gpio; + struct max9485_clk_hw hw[MAX9485_NUM_CLKS]; +}; + +static inline struct max9485_clk_hw *to_max9485_clk(struct clk_hw *hw) +{ + return container_of(hw, struct max9485_clk_hw, hw); +} + +static int max9485_update_bits(struct max9485_driver_data *drvdata, + u8 mask, u8 value) +{ + int ret; + + drvdata->reg_value &= ~mask; + drvdata->reg_value |= value; + + dev_dbg(&drvdata->client->dev, + "updating mask 0x%02x value 0x%02x -> 0x%02x\n", + mask, value, drvdata->reg_value); + + ret = i2c_master_send(drvdata->client, + &drvdata->reg_value, + sizeof(drvdata->reg_value)); + + return ret < 0 ? ret : 0; +} + +static int max9485_clk_prepare(struct clk_hw *hw) +{ + struct max9485_clk_hw *clk_hw = to_max9485_clk(hw); + + return max9485_update_bits(clk_hw->drvdata, + clk_hw->enable_bit, + clk_hw->enable_bit); +} + +static void max9485_clk_unprepare(struct clk_hw *hw) +{ + struct max9485_clk_hw *clk_hw = to_max9485_clk(hw); + + max9485_update_bits(clk_hw->drvdata, clk_hw->enable_bit, 0); +} + +/* + * CLKOUT - configurable clock output + */ +static int max9485_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct max9485_clk_hw *clk_hw = to_max9485_clk(hw); + const struct max9485_rate *entry; + + for (entry = max9485_rates; entry->out != 0; entry++) + if (entry->out == rate) + break; + + if (entry->out == 0) + return -EINVAL; + + return max9485_update_bits(clk_hw->drvdata, + MAX9485_FREQ_MASK, + entry->reg_value); +} + +static unsigned long max9485_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct max9485_clk_hw *clk_hw = to_max9485_clk(hw); + struct max9485_driver_data *drvdata = clk_hw->drvdata; + u8 val = drvdata->reg_value & MAX9485_FREQ_MASK; + const struct max9485_rate *entry; + + for (entry = max9485_rates; entry->out != 0; entry++) + if (val == entry->reg_value) + return entry->out; + + return 0; +} + +static long max9485_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + const struct max9485_rate *curr, *prev = NULL; + + for (curr = max9485_rates; curr->out != 0; curr++) { + /* Exact matches */ + if (curr->out == rate) + return rate; + + /* + * Find the first entry that has a frequency higher than the + * requested one. + */ + if (curr->out > rate) { + unsigned int mid; + + /* + * If this is the first entry, clamp the value to the + * lowest possible frequency. + */ + if (!prev) + return curr->out; + + /* + * Otherwise, determine whether the previous entry or + * current one is closer. + */ + mid = prev->out + ((curr->out - prev->out) / 2); + + return (mid > rate) ? prev->out : curr->out; + } + + prev = curr; + } + + /* If the last entry was still too high, clamp the value */ + return prev->out; +} + +struct max9485_clk { + const char *name; + int parent_index; + const struct clk_ops ops; + u8 enable_bit; +}; + +static const struct max9485_clk max9485_clks[MAX9485_NUM_CLKS] = { + [MAX9485_MCLKOUT] = { + .name = "mclkout", + .parent_index = -1, + .enable_bit = MAX9485_MCLK_ENABLE, + .ops = { + .prepare = max9485_clk_prepare, + .unprepare = max9485_clk_unprepare, + }, + }, + [MAX9485_CLKOUT] = { + .name = "clkout", + .parent_index = -1, + .ops = { + .set_rate = max9485_clkout_set_rate, + .round_rate = max9485_clkout_round_rate, + .recalc_rate = max9485_clkout_recalc_rate, + }, + }, + [MAX9485_CLKOUT1] = { + .name = "clkout1", + .parent_index = MAX9485_CLKOUT, + .enable_bit = MAX9485_CLKOUT1_ENABLE, + .ops = { + .prepare = max9485_clk_prepare, + .unprepare = max9485_clk_unprepare, + }, + }, + [MAX9485_CLKOUT2] = { + .name = "clkout2", + .parent_index = MAX9485_CLKOUT, + .enable_bit = MAX9485_CLKOUT2_ENABLE, + .ops = { + .prepare = max9485_clk_prepare, + .unprepare = max9485_clk_unprepare, + }, + }, +}; + +static struct clk_hw * +max9485_of_clk_get(struct of_phandle_args *clkspec, void *data) +{ + struct max9485_driver_data *drvdata = data; + unsigned int idx = clkspec->args[0]; + + return &drvdata->hw[idx].hw; +} + +static int max9485_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max9485_driver_data *drvdata; + struct device *dev = &client->dev; + const char *xclk_name; + int i, ret; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(drvdata->xclk)) + return PTR_ERR(drvdata->xclk); + + xclk_name = __clk_get_name(drvdata->xclk); + + drvdata->supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(drvdata->supply)) + return PTR_ERR(drvdata->supply); + + ret = regulator_enable(drvdata->supply); + if (ret < 0) + return ret; + + drvdata->reset_gpio = + devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(drvdata->reset_gpio)) + return PTR_ERR(drvdata->reset_gpio); + + i2c_set_clientdata(client, drvdata); + drvdata->client = client; + + ret = i2c_master_recv(drvdata->client, &drvdata->reg_value, + sizeof(drvdata->reg_value)); + if (ret < 0) { + dev_warn(dev, "Unable to read device register: %d\n", ret); + return ret; + } + + for (i = 0; i < MAX9485_NUM_CLKS; i++) { + int parent_index = max9485_clks[i].parent_index; + const char *name; + + if (of_property_read_string_index(dev->of_node, + "clock-output-names", + i, &name) == 0) { + drvdata->hw[i].init.name = name; + } else { + drvdata->hw[i].init.name = max9485_clks[i].name; + } + + drvdata->hw[i].init.ops = &max9485_clks[i].ops; + drvdata->hw[i].init.num_parents = 1; + drvdata->hw[i].init.flags = 0; + + if (parent_index > 0) { + drvdata->hw[i].init.parent_names = + &drvdata->hw[parent_index].init.name; + drvdata->hw[i].init.flags |= CLK_SET_RATE_PARENT; + } else { + drvdata->hw[i].init.parent_names = &xclk_name; + } + + drvdata->hw[i].enable_bit = max9485_clks[i].enable_bit; + drvdata->hw[i].hw.init = &drvdata->hw[i].init; + drvdata->hw[i].drvdata = drvdata; + + ret = devm_clk_hw_register(dev, &drvdata->hw[i].hw); + if (ret < 0) + return ret; + } + + return devm_of_clk_add_hw_provider(dev, max9485_of_clk_get, drvdata); +} + +static int __maybe_unused max9485_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max9485_driver_data *drvdata = i2c_get_clientdata(client); + + gpiod_set_value_cansleep(drvdata->reset_gpio, 0); + + return 0; +} + +static int __maybe_unused max9485_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max9485_driver_data *drvdata = i2c_get_clientdata(client); + int ret; + + gpiod_set_value_cansleep(drvdata->reset_gpio, 1); + + ret = i2c_master_send(client, &drvdata->reg_value, + sizeof(drvdata->reg_value)); + + return ret < 0 ? ret : 0; +} + +static const struct dev_pm_ops max9485_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(max9485_suspend, max9485_resume) +}; + +static const struct of_device_id max9485_dt_ids[] = { + { .compatible = "maxim,max9485", }, + { } +}; +MODULE_DEVICE_TABLE(of, max9485_dt_ids); + +static const struct i2c_device_id max9485_i2c_ids[] = { + { .name = "max9485", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9485_i2c_ids); + +static struct i2c_driver max9485_driver = { + .driver = { + .name = "max9485", + .pm = &max9485_pm_ops, + .of_match_table = max9485_dt_ids, + }, + .probe = max9485_i2c_probe, + .id_table = max9485_i2c_ids, +}; +module_i2c_driver(max9485_driver); + +MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>"); +MODULE_DESCRIPTION("MAX9485 Programmable Audio Clock Generator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index bb2a6f2f5516..a985bf5e1ac6 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -38,7 +38,6 @@ static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw, static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - int step; u64 fmin, fmax, ftmp; struct scmi_clk *clk = to_scmi_clk(hw); @@ -60,9 +59,9 @@ static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate, ftmp = rate - fmin; ftmp += clk->info->range.step_size - 1; /* to round up */ - step = do_div(ftmp, clk->info->range.step_size); + do_div(ftmp, clk->info->range.step_size); - return step * clk->info->range.step_size + fmin; + return ftmp * clk->info->range.step_size + fmin; } static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c index 09b6718956bd..153b3a2b5857 100644 --- a/drivers/clk/clk-si514.c +++ b/drivers/clk/clk-si514.c @@ -74,6 +74,33 @@ static int si514_enable_output(struct clk_si514 *data, bool enable) SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0); } +static int si514_prepare(struct clk_hw *hw) +{ + struct clk_si514 *data = to_clk_si514(hw); + + return si514_enable_output(data, true); +} + +static void si514_unprepare(struct clk_hw *hw) +{ + struct clk_si514 *data = to_clk_si514(hw); + + si514_enable_output(data, false); +} + +static int si514_is_prepared(struct clk_hw *hw) +{ + struct clk_si514 *data = to_clk_si514(hw); + unsigned int val; + int err; + + err = regmap_read(data->regmap, SI514_REG_CONTROL, &val); + if (err < 0) + return err; + + return !!(val & SI514_CONTROL_OE); +} + /* Retrieve clock multiplier and dividers from hardware */ static int si514_get_muldiv(struct clk_si514 *data, struct clk_si514_muldiv *settings) @@ -235,12 +262,17 @@ static int si514_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_si514 *data = to_clk_si514(hw); struct clk_si514_muldiv settings; + unsigned int old_oe_state; int err; err = si514_calc_muldiv(&settings, rate); if (err) return err; + err = regmap_read(data->regmap, SI514_REG_CONTROL, &old_oe_state); + if (err) + return err; + si514_enable_output(data, false); err = si514_set_muldiv(data, &settings); @@ -255,12 +287,16 @@ static int si514_set_rate(struct clk_hw *hw, unsigned long rate, /* Applying a new frequency can take up to 10ms */ usleep_range(10000, 12000); - si514_enable_output(data, true); + if (old_oe_state & SI514_CONTROL_OE) + si514_enable_output(data, true); return err; } static const struct clk_ops si514_clk_ops = { + .prepare = si514_prepare, + .unprepare = si514_unprepare, + .is_prepared = si514_is_prepared, .recalc_rate = si514_recalc_rate, .round_rate = si514_round_rate, .set_rate = si514_set_rate, diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c index 1e2a3b8f9454..64e607f3232a 100644 --- a/drivers/clk/clk-si544.c +++ b/drivers/clk/clk-si544.c @@ -86,6 +86,33 @@ static int si544_enable_output(struct clk_si544 *data, bool enable) SI544_OE_STATE_ODC_OE, enable ? SI544_OE_STATE_ODC_OE : 0); } +static int si544_prepare(struct clk_hw *hw) +{ + struct clk_si544 *data = to_clk_si544(hw); + + return si544_enable_output(data, true); +} + +static void si544_unprepare(struct clk_hw *hw) +{ + struct clk_si544 *data = to_clk_si544(hw); + + si544_enable_output(data, false); +} + +static int si544_is_prepared(struct clk_hw *hw) +{ + struct clk_si544 *data = to_clk_si544(hw); + unsigned int val; + int err; + + err = regmap_read(data->regmap, SI544_REG_OE_STATE, &val); + if (err < 0) + return err; + + return !!(val & SI544_OE_STATE_ODC_OE); +} + /* Retrieve clock multiplier and dividers from hardware */ static int si544_get_muldiv(struct clk_si544 *data, struct clk_si544_muldiv *settings) @@ -273,6 +300,7 @@ static int si544_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_si544 *data = to_clk_si544(hw); struct clk_si544_muldiv settings; + unsigned int old_oe_state; int err; if (!is_valid_frequency(data, rate)) @@ -282,6 +310,10 @@ static int si544_set_rate(struct clk_hw *hw, unsigned long rate, if (err) return err; + err = regmap_read(data->regmap, SI544_REG_OE_STATE, &old_oe_state); + if (err) + return err; + si544_enable_output(data, false); /* Allow FCAL for this frequency update */ @@ -303,12 +335,16 @@ static int si544_set_rate(struct clk_hw *hw, unsigned long rate, /* Applying a new frequency can take up to 10ms */ usleep_range(10000, 12000); - si544_enable_output(data, true); + if (old_oe_state & SI544_OE_STATE_ODC_OE) + si544_enable_output(data, true); return err; } static const struct clk_ops si544_clk_ops = { + .prepare = si544_prepare, + .unprepare = si544_unprepare, + .is_prepared = si544_is_prepared, .recalc_rate = si544_recalc_rate, .round_rate = si544_round_rate, .set_rate = si544_set_rate, diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 976f59e11f9a..d31055ae6ec6 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -691,6 +691,9 @@ static void clk_core_unprepare(struct clk_core *core) "Unpreparing critical %s\n", core->name)) return; + if (core->flags & CLK_SET_RATE_GATE) + clk_core_rate_unprotect(core); + if (--core->prepare_count > 0) return; @@ -765,6 +768,16 @@ static int clk_core_prepare(struct clk_core *core) core->prepare_count++; + /* + * CLK_SET_RATE_GATE is a special case of clock protection + * Instead of a consumer claiming exclusive rate control, it is + * actually the provider which prevents any consumer from making any + * operation which could result in a rate change or rate glitch while + * the clock is prepared. + */ + if (core->flags & CLK_SET_RATE_GATE) + clk_core_rate_protect(core); + return 0; unprepare: clk_core_unprepare(core->parent); @@ -1888,9 +1901,6 @@ static int clk_core_set_rate_nolock(struct clk_core *core, if (clk_core_rate_is_protected(core)) return -EBUSY; - 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(core, req_rate); if (!top) @@ -3122,6 +3132,7 @@ struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id, return clk; } +/* keep in sync with __clk_put */ void __clk_free_clk(struct clk *clk) { clk_prepare_lock(); @@ -3501,6 +3512,7 @@ int __clk_get(struct clk *clk) return 1; } +/* keep in sync with __clk_free_clk */ void __clk_put(struct clk *clk) { struct module *owner; @@ -3534,6 +3546,7 @@ void __clk_put(struct clk *clk) module_put(owner); + kfree_const(clk->con_id); kfree(clk); } diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c index caa8bd40692c..fc8e782d817b 100644 --- a/drivers/clk/imx/clk-imx51-imx53.c +++ b/drivers/clk/imx/clk-imx51-imx53.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/sizes.h> #include <soc/imx/revision.h> #include <dt-bindings/clock/imx5-clock.h> @@ -175,13 +176,13 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base) clk[IMX5_CLK_PER_ROOT] = imx_clk_mux("per_root", MXC_CCM_CBCMR, 0, 1, per_root_sel, ARRAY_SIZE(per_root_sel)); clk[IMX5_CLK_AHB] = imx_clk_divider("ahb", "main_bus", MXC_CCM_CBCDR, 10, 3); - clk[IMX5_CLK_AHB_MAX] = imx_clk_gate2("ahb_max", "ahb", MXC_CCM_CCGR0, 28); - clk[IMX5_CLK_AIPS_TZ1] = imx_clk_gate2("aips_tz1", "ahb", MXC_CCM_CCGR0, 24); - clk[IMX5_CLK_AIPS_TZ2] = imx_clk_gate2("aips_tz2", "ahb", MXC_CCM_CCGR0, 26); - clk[IMX5_CLK_TMAX1] = imx_clk_gate2("tmax1", "ahb", MXC_CCM_CCGR1, 0); - clk[IMX5_CLK_TMAX2] = imx_clk_gate2("tmax2", "ahb", MXC_CCM_CCGR1, 2); - clk[IMX5_CLK_TMAX3] = imx_clk_gate2("tmax3", "ahb", MXC_CCM_CCGR1, 4); - clk[IMX5_CLK_SPBA] = imx_clk_gate2("spba", "ipg", MXC_CCM_CCGR5, 0); + clk[IMX5_CLK_AHB_MAX] = imx_clk_gate2_flags("ahb_max", "ahb", MXC_CCM_CCGR0, 28, CLK_IS_CRITICAL); + clk[IMX5_CLK_AIPS_TZ1] = imx_clk_gate2_flags("aips_tz1", "ahb", MXC_CCM_CCGR0, 24, CLK_IS_CRITICAL); + clk[IMX5_CLK_AIPS_TZ2] = imx_clk_gate2_flags("aips_tz2", "ahb", MXC_CCM_CCGR0, 26, CLK_IS_CRITICAL); + clk[IMX5_CLK_TMAX1] = imx_clk_gate2_flags("tmax1", "ahb", MXC_CCM_CCGR1, 0, CLK_IS_CRITICAL); + clk[IMX5_CLK_TMAX2] = imx_clk_gate2_flags("tmax2", "ahb", MXC_CCM_CCGR1, 2, CLK_IS_CRITICAL); + clk[IMX5_CLK_TMAX3] = imx_clk_gate2_flags("tmax3", "ahb", MXC_CCM_CCGR1, 4, CLK_IS_CRITICAL); + clk[IMX5_CLK_SPBA] = imx_clk_gate2_flags("spba", "ipg", MXC_CCM_CCGR5, 0, CLK_IS_CRITICAL); clk[IMX5_CLK_IPG] = imx_clk_divider("ipg", "ahb", MXC_CCM_CBCDR, 8, 2); clk[IMX5_CLK_AXI_A] = imx_clk_divider("axi_a", "main_bus", MXC_CCM_CBCDR, 16, 3); clk[IMX5_CLK_AXI_B] = imx_clk_divider("axi_b", "main_bus", MXC_CCM_CBCDR, 19, 3); @@ -252,8 +253,8 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base) clk[IMX5_CLK_ECSPI2_PER_GATE] = imx_clk_gate2("ecspi2_per_gate", "ecspi_podf", MXC_CCM_CCGR4, 24); clk[IMX5_CLK_CSPI_IPG_GATE] = imx_clk_gate2("cspi_ipg_gate", "ipg", MXC_CCM_CCGR4, 26); clk[IMX5_CLK_SDMA_GATE] = imx_clk_gate2("sdma_gate", "ipg", MXC_CCM_CCGR4, 30); - clk[IMX5_CLK_EMI_FAST_GATE] = imx_clk_gate2("emi_fast_gate", "dummy", MXC_CCM_CCGR5, 14); - clk[IMX5_CLK_EMI_SLOW_GATE] = imx_clk_gate2("emi_slow_gate", "emi_slow_podf", MXC_CCM_CCGR5, 16); + clk[IMX5_CLK_EMI_FAST_GATE] = imx_clk_gate2_flags("emi_fast_gate", "dummy", MXC_CCM_CCGR5, 14, CLK_IS_CRITICAL); + clk[IMX5_CLK_EMI_SLOW_GATE] = imx_clk_gate2_flags("emi_slow_gate", "emi_slow_podf", MXC_CCM_CCGR5, 16, CLK_IS_CRITICAL); clk[IMX5_CLK_IPU_SEL] = imx_clk_mux("ipu_sel", MXC_CCM_CBCMR, 6, 2, ipu_sel, ARRAY_SIZE(ipu_sel)); clk[IMX5_CLK_IPU_GATE] = imx_clk_gate2("ipu_gate", "ipu_sel", MXC_CCM_CCGR5, 10); clk[IMX5_CLK_NFC_GATE] = imx_clk_gate2("nfc_gate", "nfc_podf", MXC_CCM_CCGR5, 20); @@ -267,7 +268,7 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base) clk[IMX5_CLK_VPU_SEL] = imx_clk_mux("vpu_sel", MXC_CCM_CBCMR, 14, 2, vpu_sel, ARRAY_SIZE(vpu_sel)); clk[IMX5_CLK_VPU_GATE] = imx_clk_gate2("vpu_gate", "vpu_sel", MXC_CCM_CCGR5, 6); clk[IMX5_CLK_VPU_REFERENCE_GATE] = imx_clk_gate2("vpu_reference_gate", "osc", MXC_CCM_CCGR5, 8); - clk[IMX5_CLK_GPC_DVFS] = imx_clk_gate2("gpc_dvfs", "dummy", MXC_CCM_CCGR5, 24); + clk[IMX5_CLK_GPC_DVFS] = imx_clk_gate2_flags("gpc_dvfs", "dummy", MXC_CCM_CCGR5, 24, CLK_IS_CRITICAL); clk[IMX5_CLK_SSI_APM] = imx_clk_mux("ssi_apm", MXC_CCM_CSCMR1, 8, 2, ssi_apm_sels, ARRAY_SIZE(ssi_apm_sels)); clk[IMX5_CLK_SSI1_ROOT_SEL] = imx_clk_mux("ssi1_root_sel", MXC_CCM_CSCMR1, 14, 2, ssi_clk_sels, ARRAY_SIZE(ssi_clk_sels)); @@ -316,21 +317,6 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base) /* move usb phy clk to 24MHz */ clk_set_parent(clk[IMX5_CLK_USB_PHY_SEL], clk[IMX5_CLK_OSC]); - - clk_prepare_enable(clk[IMX5_CLK_GPC_DVFS]); - clk_prepare_enable(clk[IMX5_CLK_AHB_MAX]); /* esdhc3 */ - clk_prepare_enable(clk[IMX5_CLK_AIPS_TZ1]); - clk_prepare_enable(clk[IMX5_CLK_AIPS_TZ2]); /* fec */ - clk_prepare_enable(clk[IMX5_CLK_SPBA]); - clk_prepare_enable(clk[IMX5_CLK_EMI_FAST_GATE]); /* fec */ - clk_prepare_enable(clk[IMX5_CLK_EMI_SLOW_GATE]); /* eim */ - clk_prepare_enable(clk[IMX5_CLK_MIPI_HSC1_GATE]); - clk_prepare_enable(clk[IMX5_CLK_MIPI_HSC2_GATE]); - clk_prepare_enable(clk[IMX5_CLK_MIPI_ESC_GATE]); - clk_prepare_enable(clk[IMX5_CLK_MIPI_HSP_GATE]); - clk_prepare_enable(clk[IMX5_CLK_TMAX1]); - clk_prepare_enable(clk[IMX5_CLK_TMAX2]); /* esdhc2, fec */ - clk_prepare_enable(clk[IMX5_CLK_TMAX3]); /* esdhc1, esdhc4 */ } static void __init mx50_clocks_init(struct device_node *np) @@ -442,10 +428,10 @@ static void __init mx51_clocks_init(struct device_node *np) clk[IMX5_CLK_ESDHC4_PER_GATE] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14); clk[IMX5_CLK_USB_PHY_GATE] = imx_clk_gate2("usb_phy_gate", "usb_phy_sel", MXC_CCM_CCGR2, 0); clk[IMX5_CLK_HSI2C_GATE] = imx_clk_gate2("hsi2c_gate", "ipg", MXC_CCM_CCGR1, 22); - clk[IMX5_CLK_MIPI_HSC1_GATE] = imx_clk_gate2("mipi_hsc1_gate", "ipg", MXC_CCM_CCGR4, 6); - clk[IMX5_CLK_MIPI_HSC2_GATE] = imx_clk_gate2("mipi_hsc2_gate", "ipg", MXC_CCM_CCGR4, 8); - clk[IMX5_CLK_MIPI_ESC_GATE] = imx_clk_gate2("mipi_esc_gate", "ipg", MXC_CCM_CCGR4, 10); - clk[IMX5_CLK_MIPI_HSP_GATE] = imx_clk_gate2("mipi_hsp_gate", "ipg", MXC_CCM_CCGR4, 12); + clk[IMX5_CLK_MIPI_HSC1_GATE] = imx_clk_gate2_flags("mipi_hsc1_gate", "ipg", MXC_CCM_CCGR4, 6, CLK_IS_CRITICAL); + clk[IMX5_CLK_MIPI_HSC2_GATE] = imx_clk_gate2_flags("mipi_hsc2_gate", "ipg", MXC_CCM_CCGR4, 8, CLK_IS_CRITICAL); + clk[IMX5_CLK_MIPI_ESC_GATE] = imx_clk_gate2_flags("mipi_esc_gate", "ipg", MXC_CCM_CCGR4, 10, CLK_IS_CRITICAL); + clk[IMX5_CLK_MIPI_HSP_GATE] = imx_clk_gate2_flags("mipi_hsp_gate", "ipg", MXC_CCM_CCGR4, 12, CLK_IS_CRITICAL); clk[IMX5_CLK_SPDIF_XTAL_SEL] = imx_clk_mux("spdif_xtal_sel", MXC_CCM_CSCMR1, 2, 2, mx51_spdif_xtal_sel, ARRAY_SIZE(mx51_spdif_xtal_sel)); clk[IMX5_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", MXC_CCM_CSCMR2, 2, 2, diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index b9ea7037e193..8c7c2fcb8d94 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -65,7 +65,7 @@ static const char *ipg_per_sels[] = { "ipg", "osc", }; static const char *ecspi_sels[] = { "pll3_60m", "osc", }; static const char *can_sels[] = { "pll3_60m", "osc", "pll3_80m", }; static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div", - "dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0", + "video_27m", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0", "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio_div", }; static const char *cko2_sels[] = { "mmdc_ch0_axi", "mmdc_ch1_axi", "usdhc4", "usdhc1", @@ -96,12 +96,6 @@ static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; static struct clk *clk[IMX6QDL_CLK_END]; static struct clk_onecell_data clk_data; -static unsigned int const clks_init_on[] __initconst = { - IMX6QDL_CLK_MMDC_CH0_AXI, - IMX6QDL_CLK_ROM, - IMX6QDL_CLK_ARM, -}; - static struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, @@ -417,7 +411,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) { struct device_node *np; void __iomem *anatop_base, *base; - int i; int ret; clk[IMX6QDL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); @@ -794,7 +787,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "mlb_podf", base + 0x74, 18); else clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); - clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); + clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2_flags("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20, CLK_IS_CRITICAL); clk[IMX6QDL_CLK_MMDC_CH1_AXI] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); clk[IMX6QDL_CLK_OCRAM] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); clk[IMX6QDL_CLK_OPENVG_AXI] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30); @@ -808,7 +801,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_GPMI_BCH] = imx_clk_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); clk[IMX6QDL_CLK_GPMI_IO] = imx_clk_gate2("gpmi_io", "enfc", base + 0x78, 28); clk[IMX6QDL_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); - clk[IMX6QDL_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); + clk[IMX6QDL_CLK_ROM] = imx_clk_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); clk[IMX6QDL_CLK_SATA] = imx_clk_gate2("sata", "ahb", base + 0x7c, 4); clk[IMX6QDL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); clk[IMX6QDL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); @@ -878,9 +871,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) */ clk_set_parent(clk[IMX6QDL_CLK_ENFC_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]); - for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) - clk_prepare_enable(clk[clks_init_on[i]]); - if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { clk_prepare_enable(clk[IMX6QDL_CLK_USBPHY1_GATE]); clk_prepare_enable(clk[IMX6QDL_CLK_USBPHY2_GATE]); diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c index 66b1dd1cfad0..eb6bcbf345a3 100644 --- a/drivers/clk/imx/clk-imx6sl.c +++ b/drivers/clk/imx/clk-imx6sl.c @@ -104,10 +104,6 @@ static struct clk_onecell_data clk_data; static void __iomem *ccm_base; static void __iomem *anatop_base; -static const u32 clks_init_on[] __initconst = { - IMX6SL_CLK_IPG, IMX6SL_CLK_ARM, IMX6SL_CLK_MMDC_ROOT, -}; - /* * ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken * during WAIT mode entry process could cause cache memory @@ -195,7 +191,6 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) { struct device_node *np; void __iomem *base; - int i; int ret; clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); @@ -426,13 +421,6 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) pr_warn("%s: failed to set AHB clock rate %d!\n", __func__, ret); - /* - * Make sure those always on clocks are enabled to maintain the correct - * usecount and enabling/disabling of parent PLLs. - */ - for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) - clk_prepare_enable(clks[clks_init_on[i]]); - if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]); clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]); diff --git a/drivers/clk/imx/clk-imx6sll.c b/drivers/clk/imx/clk-imx6sll.c index 3651c77fbabe..52379ee49aec 100644 --- a/drivers/clk/imx/clk-imx6sll.c +++ b/drivers/clk/imx/clk-imx6sll.c @@ -92,6 +92,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6sll-anatop"); base = of_iomap(np, 0); + of_node_put(np); WARN_ON(!base); /* Do not bypass PLLs initially */ @@ -253,6 +254,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node) clks[IMX6SLL_CLK_DCP] = imx_clk_gate2("dcp", "ahb", base + 0x68, 10); clks[IMX6SLL_CLK_UART2_IPG] = imx_clk_gate2("uart2_ipg", "ipg", base + 0x68, 28); clks[IMX6SLL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28); + clks[IMX6SLL_CLK_GPIO2] = imx_clk_gate2("gpio2", "ipg", base + 0x68, 30); /* CCGR1 */ clks[IMX6SLL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); @@ -267,13 +269,17 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node) clks[IMX6SLL_CLK_GPT_SERIAL] = imx_clk_gate2("gpt1_serial", "perclk", base + 0x6c, 22); clks[IMX6SLL_CLK_UART4_IPG] = imx_clk_gate2("uart4_ipg", "ipg", base + 0x6c, 24); clks[IMX6SLL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24); + clks[IMX6SLL_CLK_GPIO1] = imx_clk_gate2("gpio1", "ipg", base + 0x6c, 26); + clks[IMX6SLL_CLK_GPIO5] = imx_clk_gate2("gpio5", "ipg", base + 0x6c, 30); /* CCGR2 */ + clks[IMX6SLL_CLK_GPIO6] = imx_clk_gate2("gpio6", "ipg", base + 0x70, 0); clks[IMX6SLL_CLK_CSI] = imx_clk_gate2("csi", "axi", base + 0x70, 2); clks[IMX6SLL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6); clks[IMX6SLL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8); clks[IMX6SLL_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10); clks[IMX6SLL_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12); + clks[IMX6SLL_CLK_GPIO3] = imx_clk_gate2("gpio3", "ipg", base + 0x70, 26); clks[IMX6SLL_CLK_LCDIF_APB] = imx_clk_gate2("lcdif_apb", "axi", base + 0x70, 28); clks[IMX6SLL_CLK_PXP] = imx_clk_gate2("pxp", "axi", base + 0x70, 30); @@ -283,6 +289,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node) clks[IMX6SLL_CLK_EPDC_AXI] = imx_clk_gate2("epdc_aclk", "axi", base + 0x74, 4); clks[IMX6SLL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_podf", base + 0x74, 4); clks[IMX6SLL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10); + clks[IMX6SLL_CLK_GPIO4] = imx_clk_gate2("gpio4", "ipg", base + 0x74, 12); clks[IMX6SLL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16); clks[IMX6SLL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); clks[IMX6SLL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index 10c771b91ef6..d9f2890ffe62 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -92,14 +92,6 @@ static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; static struct clk *clks[IMX6SX_CLK_CLK_END]; static struct clk_onecell_data clk_data; -static int const clks_init_on[] __initconst = { - IMX6SX_CLK_AIPS_TZ1, IMX6SX_CLK_AIPS_TZ2, IMX6SX_CLK_AIPS_TZ3, - IMX6SX_CLK_IPMUX1, IMX6SX_CLK_IPMUX2, IMX6SX_CLK_IPMUX3, - IMX6SX_CLK_WAKEUP, IMX6SX_CLK_MMDC_P0_FAST, IMX6SX_CLK_MMDC_P0_IPG, - IMX6SX_CLK_ROM, IMX6SX_CLK_ARM, IMX6SX_CLK_IPG, IMX6SX_CLK_OCRAM, - IMX6SX_CLK_PER2_MAIN, IMX6SX_CLK_PERCLK, IMX6SX_CLK_TZASC1, -}; - static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, @@ -142,7 +134,6 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) { struct device_node *np; void __iomem *base; - int i; clks[IMX6SX_CLK_DUMMY] = imx_clk_fixed("dummy", 0); @@ -332,7 +323,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_QSPI1_PODF] = imx_clk_divider("qspi1_podf", "qspi1_sel", base + 0x1c, 26, 3); clks[IMX6SX_CLK_EIM_SLOW_PODF] = imx_clk_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3); clks[IMX6SX_CLK_LCDIF2_PODF] = imx_clk_divider("lcdif2_podf", "lcdif2_pred", base + 0x1c, 20, 3); - clks[IMX6SX_CLK_PERCLK] = imx_clk_divider("perclk", "perclk_sel", base + 0x1c, 0, 6); + clks[IMX6SX_CLK_PERCLK] = imx_clk_divider_flags("perclk", "perclk_sel", base + 0x1c, 0, 6, CLK_IS_CRITICAL); clks[IMX6SX_CLK_VID_PODF] = imx_clk_divider("vid_podf", "vid_sel", base + 0x20, 24, 2); clks[IMX6SX_CLK_CAN_PODF] = imx_clk_divider("can_podf", "can_sel", base + 0x20, 2, 6); clks[IMX6SX_CLK_USDHC4_PODF] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); @@ -380,8 +371,8 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) /* name parent_name reg shift */ /* CCGR0 */ - clks[IMX6SX_CLK_AIPS_TZ1] = imx_clk_gate2("aips_tz1", "ahb", base + 0x68, 0); - clks[IMX6SX_CLK_AIPS_TZ2] = imx_clk_gate2("aips_tz2", "ahb", base + 0x68, 2); + clks[IMX6SX_CLK_AIPS_TZ1] = imx_clk_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_AIPS_TZ2] = imx_clk_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL); clks[IMX6SX_CLK_APBH_DMA] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); clks[IMX6SX_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); clks[IMX6SX_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); @@ -394,7 +385,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_CAN2_SERIAL] = imx_clk_gate2("can2_serial", "can_podf", base + 0x68, 20); clks[IMX6SX_CLK_DCIC1] = imx_clk_gate2("dcic1", "display_podf", base + 0x68, 24); clks[IMX6SX_CLK_DCIC2] = imx_clk_gate2("dcic2", "display_podf", base + 0x68, 26); - clks[IMX6SX_CLK_AIPS_TZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x68, 30); + clks[IMX6SX_CLK_AIPS_TZ3] = imx_clk_gate2_flags("aips_tz3", "ahb", base + 0x68, 30, CLK_IS_CRITICAL); /* CCGR1 */ clks[IMX6SX_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); @@ -407,10 +398,11 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x6c, 16, &share_count_esai); clks[IMX6SX_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x6c, 16, &share_count_esai); clks[IMX6SX_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai); - clks[IMX6SX_CLK_WAKEUP] = imx_clk_gate2("wakeup", "ipg", base + 0x6c, 18); + clks[IMX6SX_CLK_WAKEUP] = imx_clk_gate2_flags("wakeup", "ipg", base + 0x6c, 18, CLK_IS_CRITICAL); clks[IMX6SX_CLK_GPT_BUS] = imx_clk_gate2("gpt_bus", "perclk", base + 0x6c, 20); clks[IMX6SX_CLK_GPT_SERIAL] = imx_clk_gate2("gpt_serial", "perclk", base + 0x6c, 22); clks[IMX6SX_CLK_GPU] = imx_clk_gate2("gpu", "gpu_core_podf", base + 0x6c, 26); + clks[IMX6SX_CLK_OCRAM_S] = imx_clk_gate2("ocram_s", "ahb", base + 0x6c, 28); clks[IMX6SX_CLK_CANFD] = imx_clk_gate2("canfd", "can_podf", base + 0x6c, 30); /* CCGR2 */ @@ -420,10 +412,10 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10); clks[IMX6SX_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12); clks[IMX6SX_CLK_IOMUXC] = imx_clk_gate2("iomuxc", "lcdif1_podf", base + 0x70, 14); - clks[IMX6SX_CLK_IPMUX1] = imx_clk_gate2("ipmux1", "ahb", base + 0x70, 16); - clks[IMX6SX_CLK_IPMUX2] = imx_clk_gate2("ipmux2", "ahb", base + 0x70, 18); - clks[IMX6SX_CLK_IPMUX3] = imx_clk_gate2("ipmux3", "ahb", base + 0x70, 20); - clks[IMX6SX_CLK_TZASC1] = imx_clk_gate2("tzasc1", "mmdc_podf", base + 0x70, 22); + clks[IMX6SX_CLK_IPMUX1] = imx_clk_gate2_flags("ipmux1", "ahb", base + 0x70, 16, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_IPMUX2] = imx_clk_gate2_flags("ipmux2", "ahb", base + 0x70, 18, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_IPMUX3] = imx_clk_gate2_flags("ipmux3", "ahb", base + 0x70, 20, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_TZASC1] = imx_clk_gate2_flags("tzasc1", "mmdc_podf", base + 0x70, 22, CLK_IS_CRITICAL); clks[IMX6SX_CLK_LCDIF_APB] = imx_clk_gate2("lcdif_apb", "display_podf", base + 0x70, 28); clks[IMX6SX_CLK_PXP_AXI] = imx_clk_gate2("pxp_axi", "display_podf", base + 0x70, 30); @@ -437,15 +429,15 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_LDB_DI0] = imx_clk_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12); clks[IMX6SX_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_podf", base + 0x74, 14); clks[IMX6SX_CLK_MLB] = imx_clk_gate2("mlb", "ahb", base + 0x74, 18); - clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20); - clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2("mmdc_p0_ipg", "ipg", base + 0x74, 24); - clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28); + clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2_flags("ocram", "ocram_podf", base + 0x74, 28, CLK_IS_CRITICAL); /* CCGR4 */ clks[IMX6SX_CLK_PCIE_AXI] = imx_clk_gate2("pcie_axi", "display_podf", base + 0x78, 0); clks[IMX6SX_CLK_QSPI2] = imx_clk_gate2("qspi2", "qspi2_podf", base + 0x78, 10); clks[IMX6SX_CLK_PER1_BCH] = imx_clk_gate2("per1_bch", "usdhc3", base + 0x78, 12); - clks[IMX6SX_CLK_PER2_MAIN] = imx_clk_gate2("per2_main", "ahb", base + 0x78, 14); + clks[IMX6SX_CLK_PER2_MAIN] = imx_clk_gate2_flags("per2_main", "ahb", base + 0x78, 14, CLK_IS_CRITICAL); clks[IMX6SX_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16); clks[IMX6SX_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18); clks[IMX6SX_CLK_PWM3] = imx_clk_gate2("pwm3", "perclk", base + 0x78, 20); @@ -456,7 +448,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); /* CCGR5 */ - clks[IMX6SX_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); + clks[IMX6SX_CLK_ROM] = imx_clk_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); clks[IMX6SX_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); clks[IMX6SX_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); clks[IMX6SX_CLK_AUDIO] = imx_clk_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio); @@ -502,9 +494,6 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clk_data.clk_num = ARRAY_SIZE(clks); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) - clk_prepare_enable(clks[clks_init_on[i]]); - if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { clk_prepare_enable(clks[IMX6SX_CLK_USBPHY1_GATE]); clk_prepare_enable(clks[IMX6SX_CLK_USBPHY2_GATE]); diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index ba563ba50b40..361b43f9742e 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -79,12 +79,6 @@ static const char *cko_sels[] = { "cko1", "cko2", }; static struct clk *clks[IMX6UL_CLK_END]; static struct clk_onecell_data clk_data; -static int const clks_init_on[] __initconst = { - IMX6UL_CLK_AIPSTZ1, IMX6UL_CLK_AIPSTZ2, - IMX6UL_CLK_AXI, IMX6UL_CLK_ARM, IMX6UL_CLK_ROM, - IMX6UL_CLK_MMDC_P0_FAST, IMX6UL_CLK_MMDC_P0_IPG, -}; - static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, @@ -129,7 +123,6 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) { struct device_node *np; void __iomem *base; - int i; clks[IMX6UL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); @@ -142,6 +135,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-anatop"); base = of_iomap(np, 0); + of_node_put(np); WARN_ON(!base); clks[IMX6UL_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); @@ -336,8 +330,8 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_AHB] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); /* CCGR0 */ - clks[IMX6UL_CLK_AIPSTZ1] = imx_clk_gate2("aips_tz1", "ahb", base + 0x68, 0); - clks[IMX6UL_CLK_AIPSTZ2] = imx_clk_gate2("aips_tz2", "ahb", base + 0x68, 2); + clks[IMX6UL_CLK_AIPSTZ1] = imx_clk_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL); + clks[IMX6UL_CLK_AIPSTZ2] = imx_clk_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL); clks[IMX6UL_CLK_APBHDMA] = imx_clk_gate2("apbh_dma", "bch_podf", base + 0x68, 4); clks[IMX6UL_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); clks[IMX6UL_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); @@ -360,6 +354,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_UART2_SERIAL] = imx_clk_gate2("uart2_serial", "uart_podf", base + 0x68, 28); if (clk_on_imx6ull()) clks[IMX6UL_CLK_AIPSTZ3] = imx_clk_gate2("aips_tz3", "ahb", base + 0x80, 18); + clks[IMX6UL_CLK_GPIO2] = imx_clk_gate2("gpio2", "ipg", base + 0x68, 30); /* CCGR1 */ clks[IMX6UL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); @@ -376,6 +371,8 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_GPT1_SERIAL] = imx_clk_gate2("gpt1_serial", "perclk", base + 0x6c, 22); clks[IMX6UL_CLK_UART4_IPG] = imx_clk_gate2("uart4_ipg", "ipg", base + 0x6c, 24); clks[IMX6UL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serial", "uart_podf", base + 0x6c, 24); + clks[IMX6UL_CLK_GPIO1] = imx_clk_gate2("gpio1", "ipg", base + 0x6c, 26); + clks[IMX6UL_CLK_GPIO5] = imx_clk_gate2("gpio5", "ipg", base + 0x6c, 30); /* CCGR2 */ if (clk_on_imx6ull()) { @@ -389,6 +386,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10); clks[IMX6UL_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12); clks[IMX6UL_CLK_IOMUXC] = imx_clk_gate2("iomuxc", "lcdif_podf", base + 0x70, 14); + clks[IMX6UL_CLK_GPIO3] = imx_clk_gate2("gpio3", "ipg", base + 0x70, 26); clks[IMX6UL_CLK_LCDIF_APB] = imx_clk_gate2("lcdif_apb", "axi", base + 0x70, 28); clks[IMX6UL_CLK_PXP] = imx_clk_gate2("pxp", "axi", base + 0x70, 30); @@ -405,11 +403,12 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_UART6_IPG] = imx_clk_gate2("uart6_ipg", "ipg", base + 0x74, 6); clks[IMX6UL_CLK_UART6_SERIAL] = imx_clk_gate2("uart6_serial", "uart_podf", base + 0x74, 6); clks[IMX6UL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10); + clks[IMX6UL_CLK_GPIO4] = imx_clk_gate2("gpio4", "ipg", base + 0x74, 12); clks[IMX6UL_CLK_QSPI] = imx_clk_gate2("qspi1", "qspi1_podf", base + 0x74, 14); clks[IMX6UL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16); - clks[IMX6UL_CLK_MMDC_P0_FAST] = imx_clk_gate("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20); - clks[IMX6UL_CLK_MMDC_P0_IPG] = imx_clk_gate2("mmdc_p0_ipg", "ipg", base + 0x74, 24); - clks[IMX6UL_CLK_AXI] = imx_clk_gate("axi", "axi_podf", base + 0x74, 28); + clks[IMX6UL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); + clks[IMX6UL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + clks[IMX6UL_CLK_AXI] = imx_clk_gate_flags("axi", "axi_podf", base + 0x74, 28, CLK_IS_CRITICAL); /* CCGR4 */ clks[IMX6UL_CLK_PER_BCH] = imx_clk_gate2("per_bch", "bch_podf", base + 0x78, 12); @@ -423,7 +422,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "bch_podf", base + 0x78, 30); /* CCGR5 */ - clks[IMX6UL_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); + clks[IMX6UL_CLK_ROM] = imx_clk_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); clks[IMX6UL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); clks[IMX6UL_CLK_KPP] = imx_clk_gate2("kpp", "ipg", base + 0x7c, 8); clks[IMX6UL_CLK_WDOG2] = imx_clk_gate2("wdog2", "ipg", base + 0x7c, 10); @@ -497,10 +496,6 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clk_set_rate(clks[IMX6UL_CLK_ENET2_REF], 50000000); clk_set_rate(clks[IMX6UL_CLK_CSI], 24000000); - /* keep all the clks on just for bringup */ - for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) - clk_prepare_enable(clks[clks_init_on[i]]); - if (clk_on_imx6ull()) clk_prepare_enable(clks[IMX6UL_CLK_AIPSTZ3]); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 27217a7ea17e..881b772c4ac9 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -797,6 +797,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0); clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0); clks[IMX7D_SNVS_CLK] = imx_clk_gate4("snvs_clk", "ipg_root_clk", base + 0x4250, 0); + clks[IMX7D_MU_ROOT_CLK] = imx_clk_gate4("mu_root_clk", "ipg_root_clk", base + 0x4270, 0); clks[IMX7D_CAAM_CLK] = imx_clk_gate4("caam_clk", "ipg_root_clk", base + 0x4240, 0); clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4690, 0); clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0); diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c index 32fcc75f6f77..4479c102e899 100644 --- a/drivers/clk/ingenic/jz4740-cgu.c +++ b/drivers/clk/ingenic/jz4740-cgu.c @@ -134,7 +134,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 31, 1 }, - .div = { CGU_REG_I2SCDR, 0, 1, 8, -1, -1, -1 }, + .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 }, .gate = { CGU_REG_CLKGR, 6 }, }, @@ -161,7 +161,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { }, [JZ4740_CLK_UDC] = { - "udc", CGU_CLK_MUX | CGU_CLK_DIV, + "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, .mux = { CGU_REG_CPCCR, 29, 1 }, .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 815659eebea3..efaa70f682b4 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -1,13 +1,19 @@ config COMMON_CLK_AMLOGIC bool - depends on OF depends on ARCH_MESON || COMPILE_TEST + select COMMON_CLK_REGMAP_MESON + +config COMMON_CLK_AMLOGIC_AUDIO + bool + depends on ARCH_MESON || COMPILE_TEST + select COMMON_CLK_AMLOGIC config COMMON_CLK_MESON_AO bool depends on OF depends on ARCH_MESON || COMPILE_TEST select COMMON_CLK_REGMAP_MESON + select RESET_CONTROLLER config COMMON_CLK_REGMAP_MESON bool @@ -15,9 +21,8 @@ config COMMON_CLK_REGMAP_MESON config COMMON_CLK_MESON8B bool - depends on COMMON_CLK_AMLOGIC + select COMMON_CLK_AMLOGIC select RESET_CONTROLLER - select COMMON_CLK_REGMAP_MESON help Support for the clock controller on AmLogic S802 (Meson8), S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you @@ -25,10 +30,8 @@ config COMMON_CLK_MESON8B config COMMON_CLK_GXBB bool - depends on COMMON_CLK_AMLOGIC - select RESET_CONTROLLER + select COMMON_CLK_AMLOGIC select COMMON_CLK_MESON_AO - select COMMON_CLK_REGMAP_MESON select MFD_SYSCON help Support for the clock controller on AmLogic S905 devices, aka gxbb. @@ -36,11 +39,18 @@ config COMMON_CLK_GXBB config COMMON_CLK_AXG bool - depends on COMMON_CLK_AMLOGIC - select RESET_CONTROLLER + select COMMON_CLK_AMLOGIC select COMMON_CLK_MESON_AO - select COMMON_CLK_REGMAP_MESON select MFD_SYSCON help Support for the clock controller on AmLogic A113D devices, aka axg. Say Y if you want peripherals and CPU frequency scaling to work. + +config COMMON_CLK_AXG_AUDIO + tristate "Meson AXG Audio Clock Controller Driver" + depends on COMMON_CLK_AXG + select COMMON_CLK_AMLOGIC_AUDIO + select MFD_SYSCON + help + Support for the audio clock controller on AmLogic A113D devices, + aka axg, Say Y if you want audio subsystem to work. diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index d0d13aeb369a..72ec8c40d848 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -2,9 +2,11 @@ # Makefile for Meson specific clk # -obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o +obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-phase.o +obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o obj-$(CONFIG_COMMON_CLK_MESON_AO) += meson-aoclk.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o +obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c new file mode 100644 index 000000000000..a0ed41e73bde --- /dev/null +++ b/drivers/clk/meson/axg-audio.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/init.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include "clkc-audio.h" +#include "axg-audio.h" + +#define AXG_MST_IN_COUNT 8 +#define AXG_SLV_SCLK_COUNT 10 +#define AXG_SLV_LRCLK_COUNT 10 + +#define AXG_AUD_GATE(_name, _reg, _bit, _pname, _iflags) \ +struct clk_regmap axg_##_name = { \ + .data = &(struct clk_regmap_gate_data){ \ + .offset = (_reg), \ + .bit_idx = (_bit), \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "axg_"#_name, \ + .ops = &clk_regmap_gate_ops, \ + .parent_names = (const char *[]){ _pname }, \ + .num_parents = 1, \ + .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \ + }, \ +} + +#define AXG_AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pnames, _iflags) \ +struct clk_regmap axg_##_name = { \ + .data = &(struct clk_regmap_mux_data){ \ + .offset = (_reg), \ + .mask = (_mask), \ + .shift = (_shift), \ + .flags = (_dflags), \ + }, \ + .hw.init = &(struct clk_init_data){ \ + .name = "axg_"#_name, \ + .ops = &clk_regmap_mux_ops, \ + .parent_names = (_pnames), \ + .num_parents = ARRAY_SIZE(_pnames), \ + .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \ + }, \ +} + +#define AXG_AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) \ +struct clk_regmap axg_##_name = { \ + .data = &(struct clk_regmap_div_data){ \ + .offset = (_reg), \ + .shift = (_shift), \ + .width = (_width), \ + .flags = (_dflags), \ + }, \ + .hw.init = &(struct clk_init_data){ \ + .name = "axg_"#_name, \ + .ops = &clk_regmap_divider_ops, \ + .parent_names = (const char *[]) { _pname }, \ + .num_parents = 1, \ + .flags = (_iflags), \ + }, \ +} + +#define AXG_PCLK_GATE(_name, _bit) \ + AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0) + +/* Audio peripheral clocks */ +static AXG_PCLK_GATE(ddr_arb, 0); +static AXG_PCLK_GATE(pdm, 1); +static AXG_PCLK_GATE(tdmin_a, 2); +static AXG_PCLK_GATE(tdmin_b, 3); +static AXG_PCLK_GATE(tdmin_c, 4); +static AXG_PCLK_GATE(tdmin_lb, 5); +static AXG_PCLK_GATE(tdmout_a, 6); +static AXG_PCLK_GATE(tdmout_b, 7); +static AXG_PCLK_GATE(tdmout_c, 8); +static AXG_PCLK_GATE(frddr_a, 9); +static AXG_PCLK_GATE(frddr_b, 10); +static AXG_PCLK_GATE(frddr_c, 11); +static AXG_PCLK_GATE(toddr_a, 12); +static AXG_PCLK_GATE(toddr_b, 13); +static AXG_PCLK_GATE(toddr_c, 14); +static AXG_PCLK_GATE(loopback, 15); +static AXG_PCLK_GATE(spdifin, 16); +static AXG_PCLK_GATE(spdifout, 17); +static AXG_PCLK_GATE(resample, 18); +static AXG_PCLK_GATE(power_detect, 19); + +/* Audio Master Clocks */ +static const char * const mst_mux_parent_names[] = { + "axg_mst_in0", "axg_mst_in1", "axg_mst_in2", "axg_mst_in3", + "axg_mst_in4", "axg_mst_in5", "axg_mst_in6", "axg_mst_in7", +}; + +#define AXG_MST_MCLK_MUX(_name, _reg) \ + AXG_AUD_MUX(_name##_sel, _reg, 0x7, 24, CLK_MUX_ROUND_CLOSEST, \ + mst_mux_parent_names, CLK_SET_RATE_PARENT) + +static AXG_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL); +static AXG_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL); +static AXG_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL); +static AXG_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL); +static AXG_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL); +static AXG_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL); +static AXG_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); +static AXG_MST_MCLK_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); +static AXG_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); +static AXG_MST_MCLK_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); + +#define AXG_MST_MCLK_DIV(_name, _reg) \ + AXG_AUD_DIV(_name##_div, _reg, 0, 16, CLK_DIVIDER_ROUND_CLOSEST, \ + "axg_"#_name"_sel", CLK_SET_RATE_PARENT) \ + +static AXG_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL); +static AXG_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL); +static AXG_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL); +static AXG_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL); +static AXG_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL); +static AXG_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL); +static AXG_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); +static AXG_MST_MCLK_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); +static AXG_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); +static AXG_MST_MCLK_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); + +#define AXG_MST_MCLK_GATE(_name, _reg) \ + AXG_AUD_GATE(_name, _reg, 31, "axg_"#_name"_div", \ + CLK_SET_RATE_PARENT) + +static AXG_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL); +static AXG_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL); +static AXG_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL); +static AXG_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL); +static AXG_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL); +static AXG_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL); +static AXG_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); +static AXG_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); +static AXG_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); +static AXG_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); + +/* Sample Clocks */ +#define AXG_MST_SCLK_PRE_EN(_name, _reg) \ + AXG_AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \ + "axg_mst_"#_name"_mclk", 0) + +static AXG_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0); +static AXG_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0); +static AXG_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0); +static AXG_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0); +static AXG_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0); +static AXG_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0); + +#define AXG_AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width, \ + _hi_shift, _hi_width, _pname, _iflags) \ +struct clk_regmap axg_##_name = { \ + .data = &(struct meson_sclk_div_data) { \ + .div = { \ + .reg_off = (_reg), \ + .shift = (_div_shift), \ + .width = (_div_width), \ + }, \ + .hi = { \ + .reg_off = (_reg), \ + .shift = (_hi_shift), \ + .width = (_hi_width), \ + }, \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "axg_"#_name, \ + .ops = &meson_sclk_div_ops, \ + .parent_names = (const char *[]) { _pname }, \ + .num_parents = 1, \ + .flags = (_iflags), \ + }, \ +} + +#define AXG_MST_SCLK_DIV(_name, _reg) \ + AXG_AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \ + "axg_mst_"#_name"_sclk_pre_en", \ + CLK_SET_RATE_PARENT) + +static AXG_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0); +static AXG_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0); +static AXG_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0); +static AXG_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0); +static AXG_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0); +static AXG_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0); + +#define AXG_MST_SCLK_POST_EN(_name, _reg) \ + AXG_AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \ + "axg_mst_"#_name"_sclk_div", CLK_SET_RATE_PARENT) + +static AXG_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0); +static AXG_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0); +static AXG_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0); +static AXG_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0); +static AXG_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0); +static AXG_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0); + +#define AXG_AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \ + _pname, _iflags) \ +struct clk_regmap axg_##_name = { \ + .data = &(struct meson_clk_triphase_data) { \ + .ph0 = { \ + .reg_off = (_reg), \ + .shift = (_shift0), \ + .width = (_width), \ + }, \ + .ph1 = { \ + .reg_off = (_reg), \ + .shift = (_shift1), \ + .width = (_width), \ + }, \ + .ph2 = { \ + .reg_off = (_reg), \ + .shift = (_shift2), \ + .width = (_width), \ + }, \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "axg_"#_name, \ + .ops = &meson_clk_triphase_ops, \ + .parent_names = (const char *[]) { _pname }, \ + .num_parents = 1, \ + .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \ + }, \ +} + +#define AXG_MST_SCLK(_name, _reg) \ + AXG_AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4, \ + "axg_mst_"#_name"_sclk_post_en", CLK_SET_RATE_PARENT) + +static AXG_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1); +static AXG_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1); +static AXG_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1); +static AXG_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1); +static AXG_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1); +static AXG_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1); + +#define AXG_MST_LRCLK_DIV(_name, _reg) \ + AXG_AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10, \ + "axg_mst_"#_name"_sclk_post_en", 0) \ + +static AXG_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0); +static AXG_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0); +static AXG_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0); +static AXG_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0); +static AXG_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0); +static AXG_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0); + +#define AXG_MST_LRCLK(_name, _reg) \ + AXG_AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5, \ + "axg_mst_"#_name"_lrclk_div", CLK_SET_RATE_PARENT) + +static AXG_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1); +static AXG_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1); +static AXG_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1); +static AXG_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1); +static AXG_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1); +static AXG_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1); + +static const char * const tdm_sclk_parent_names[] = { + "axg_mst_a_sclk", "axg_mst_b_sclk", "axg_mst_c_sclk", + "axg_mst_d_sclk", "axg_mst_e_sclk", "axg_mst_f_sclk", + "axg_slv_sclk0", "axg_slv_sclk1", "axg_slv_sclk2", + "axg_slv_sclk3", "axg_slv_sclk4", "axg_slv_sclk5", + "axg_slv_sclk6", "axg_slv_sclk7", "axg_slv_sclk8", + "axg_slv_sclk9" +}; + +#define AXG_TDM_SCLK_MUX(_name, _reg) \ + AXG_AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \ + CLK_MUX_ROUND_CLOSEST, \ + tdm_sclk_parent_names, 0) + +static AXG_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static AXG_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static AXG_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static AXG_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static AXG_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static AXG_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static AXG_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +#define AXG_TDM_SCLK_PRE_EN(_name, _reg) \ + AXG_AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \ + "axg_tdm"#_name"_sclk_sel", CLK_SET_RATE_PARENT) + +static AXG_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static AXG_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static AXG_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static AXG_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static AXG_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static AXG_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static AXG_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +#define AXG_TDM_SCLK_POST_EN(_name, _reg) \ + AXG_AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \ + "axg_tdm"#_name"_sclk_pre_en", CLK_SET_RATE_PARENT) + +static AXG_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static AXG_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static AXG_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static AXG_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static AXG_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static AXG_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static AXG_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +#define AXG_TDM_SCLK(_name, _reg) \ + struct clk_regmap axg_tdm##_name##_sclk = { \ + .data = &(struct meson_clk_phase_data) { \ + .ph = { \ + .reg_off = (_reg), \ + .shift = 29, \ + .width = 1, \ + }, \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "axg_tdm"#_name"_sclk", \ + .ops = &meson_clk_phase_ops, \ + .parent_names = (const char *[]) \ + { "axg_tdm"#_name"_sclk_post_en" }, \ + .num_parents = 1, \ + .flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT, \ + }, \ +} + +static AXG_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static AXG_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static AXG_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static AXG_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static AXG_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static AXG_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static AXG_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +static const char * const tdm_lrclk_parent_names[] = { + "axg_mst_a_lrclk", "axg_mst_b_lrclk", "axg_mst_c_lrclk", + "axg_mst_d_lrclk", "axg_mst_e_lrclk", "axg_mst_f_lrclk", + "axg_slv_lrclk0", "axg_slv_lrclk1", "axg_slv_lrclk2", + "axg_slv_lrclk3", "axg_slv_lrclk4", "axg_slv_lrclk5", + "axg_slv_lrclk6", "axg_slv_lrclk7", "axg_slv_lrclk8", + "axg_slv_lrclk9" +}; + +#define AXG_TDM_LRLCK(_name, _reg) \ + AXG_AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \ + CLK_MUX_ROUND_CLOSEST, \ + tdm_lrclk_parent_names, 0) + +static AXG_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static AXG_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static AXG_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static AXG_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static AXG_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static AXG_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static AXG_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +/* + * Array of all clocks provided by this provider + * The input clocks of the controller will be populated at runtime + */ +static struct clk_hw_onecell_data axg_audio_hw_onecell_data = { + .hws = { + [AUD_CLKID_DDR_ARB] = &axg_ddr_arb.hw, + [AUD_CLKID_PDM] = &axg_pdm.hw, + [AUD_CLKID_TDMIN_A] = &axg_tdmin_a.hw, + [AUD_CLKID_TDMIN_B] = &axg_tdmin_b.hw, + [AUD_CLKID_TDMIN_C] = &axg_tdmin_c.hw, + [AUD_CLKID_TDMIN_LB] = &axg_tdmin_lb.hw, + [AUD_CLKID_TDMOUT_A] = &axg_tdmout_a.hw, + [AUD_CLKID_TDMOUT_B] = &axg_tdmout_b.hw, + [AUD_CLKID_TDMOUT_C] = &axg_tdmout_c.hw, + [AUD_CLKID_FRDDR_A] = &axg_frddr_a.hw, + [AUD_CLKID_FRDDR_B] = &axg_frddr_b.hw, + [AUD_CLKID_FRDDR_C] = &axg_frddr_c.hw, + [AUD_CLKID_TODDR_A] = &axg_toddr_a.hw, + [AUD_CLKID_TODDR_B] = &axg_toddr_b.hw, + [AUD_CLKID_TODDR_C] = &axg_toddr_c.hw, + [AUD_CLKID_LOOPBACK] = &axg_loopback.hw, + [AUD_CLKID_SPDIFIN] = &axg_spdifin.hw, + [AUD_CLKID_SPDIFOUT] = &axg_spdifout.hw, + [AUD_CLKID_RESAMPLE] = &axg_resample.hw, + [AUD_CLKID_POWER_DETECT] = &axg_power_detect.hw, + [AUD_CLKID_MST_A_MCLK_SEL] = &axg_mst_a_mclk_sel.hw, + [AUD_CLKID_MST_B_MCLK_SEL] = &axg_mst_b_mclk_sel.hw, + [AUD_CLKID_MST_C_MCLK_SEL] = &axg_mst_c_mclk_sel.hw, + [AUD_CLKID_MST_D_MCLK_SEL] = &axg_mst_d_mclk_sel.hw, + [AUD_CLKID_MST_E_MCLK_SEL] = &axg_mst_e_mclk_sel.hw, + [AUD_CLKID_MST_F_MCLK_SEL] = &axg_mst_f_mclk_sel.hw, + [AUD_CLKID_MST_A_MCLK_DIV] = &axg_mst_a_mclk_div.hw, + [AUD_CLKID_MST_B_MCLK_DIV] = &axg_mst_b_mclk_div.hw, + [AUD_CLKID_MST_C_MCLK_DIV] = &axg_mst_c_mclk_div.hw, + [AUD_CLKID_MST_D_MCLK_DIV] = &axg_mst_d_mclk_div.hw, + [AUD_CLKID_MST_E_MCLK_DIV] = &axg_mst_e_mclk_div.hw, + [AUD_CLKID_MST_F_MCLK_DIV] = &axg_mst_f_mclk_div.hw, + [AUD_CLKID_MST_A_MCLK] = &axg_mst_a_mclk.hw, + [AUD_CLKID_MST_B_MCLK] = &axg_mst_b_mclk.hw, + [AUD_CLKID_MST_C_MCLK] = &axg_mst_c_mclk.hw, + [AUD_CLKID_MST_D_MCLK] = &axg_mst_d_mclk.hw, + [AUD_CLKID_MST_E_MCLK] = &axg_mst_e_mclk.hw, + [AUD_CLKID_MST_F_MCLK] = &axg_mst_f_mclk.hw, + [AUD_CLKID_SPDIFOUT_CLK_SEL] = &axg_spdifout_clk_sel.hw, + [AUD_CLKID_SPDIFOUT_CLK_DIV] = &axg_spdifout_clk_div.hw, + [AUD_CLKID_SPDIFOUT_CLK] = &axg_spdifout_clk.hw, + [AUD_CLKID_SPDIFIN_CLK_SEL] = &axg_spdifin_clk_sel.hw, + [AUD_CLKID_SPDIFIN_CLK_DIV] = &axg_spdifin_clk_div.hw, + [AUD_CLKID_SPDIFIN_CLK] = &axg_spdifin_clk.hw, + [AUD_CLKID_PDM_DCLK_SEL] = &axg_pdm_dclk_sel.hw, + [AUD_CLKID_PDM_DCLK_DIV] = &axg_pdm_dclk_div.hw, + [AUD_CLKID_PDM_DCLK] = &axg_pdm_dclk.hw, + [AUD_CLKID_PDM_SYSCLK_SEL] = &axg_pdm_sysclk_sel.hw, + [AUD_CLKID_PDM_SYSCLK_DIV] = &axg_pdm_sysclk_div.hw, + [AUD_CLKID_PDM_SYSCLK] = &axg_pdm_sysclk.hw, + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &axg_mst_a_sclk_pre_en.hw, + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &axg_mst_b_sclk_pre_en.hw, + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &axg_mst_c_sclk_pre_en.hw, + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &axg_mst_d_sclk_pre_en.hw, + [AUD_CLKID_MST_E_SCLK_PRE_EN] = &axg_mst_e_sclk_pre_en.hw, + [AUD_CLKID_MST_F_SCLK_PRE_EN] = &axg_mst_f_sclk_pre_en.hw, + [AUD_CLKID_MST_A_SCLK_DIV] = &axg_mst_a_sclk_div.hw, + [AUD_CLKID_MST_B_SCLK_DIV] = &axg_mst_b_sclk_div.hw, + [AUD_CLKID_MST_C_SCLK_DIV] = &axg_mst_c_sclk_div.hw, + [AUD_CLKID_MST_D_SCLK_DIV] = &axg_mst_d_sclk_div.hw, + [AUD_CLKID_MST_E_SCLK_DIV] = &axg_mst_e_sclk_div.hw, + [AUD_CLKID_MST_F_SCLK_DIV] = &axg_mst_f_sclk_div.hw, + [AUD_CLKID_MST_A_SCLK_POST_EN] = &axg_mst_a_sclk_post_en.hw, + [AUD_CLKID_MST_B_SCLK_POST_EN] = &axg_mst_b_sclk_post_en.hw, + [AUD_CLKID_MST_C_SCLK_POST_EN] = &axg_mst_c_sclk_post_en.hw, + [AUD_CLKID_MST_D_SCLK_POST_EN] = &axg_mst_d_sclk_post_en.hw, + [AUD_CLKID_MST_E_SCLK_POST_EN] = &axg_mst_e_sclk_post_en.hw, + [AUD_CLKID_MST_F_SCLK_POST_EN] = &axg_mst_f_sclk_post_en.hw, + [AUD_CLKID_MST_A_SCLK] = &axg_mst_a_sclk.hw, + [AUD_CLKID_MST_B_SCLK] = &axg_mst_b_sclk.hw, + [AUD_CLKID_MST_C_SCLK] = &axg_mst_c_sclk.hw, + [AUD_CLKID_MST_D_SCLK] = &axg_mst_d_sclk.hw, + [AUD_CLKID_MST_E_SCLK] = &axg_mst_e_sclk.hw, + [AUD_CLKID_MST_F_SCLK] = &axg_mst_f_sclk.hw, + [AUD_CLKID_MST_A_LRCLK_DIV] = &axg_mst_a_lrclk_div.hw, + [AUD_CLKID_MST_B_LRCLK_DIV] = &axg_mst_b_lrclk_div.hw, + [AUD_CLKID_MST_C_LRCLK_DIV] = &axg_mst_c_lrclk_div.hw, + [AUD_CLKID_MST_D_LRCLK_DIV] = &axg_mst_d_lrclk_div.hw, + [AUD_CLKID_MST_E_LRCLK_DIV] = &axg_mst_e_lrclk_div.hw, + [AUD_CLKID_MST_F_LRCLK_DIV] = &axg_mst_f_lrclk_div.hw, + [AUD_CLKID_MST_A_LRCLK] = &axg_mst_a_lrclk.hw, + [AUD_CLKID_MST_B_LRCLK] = &axg_mst_b_lrclk.hw, + [AUD_CLKID_MST_C_LRCLK] = &axg_mst_c_lrclk.hw, + [AUD_CLKID_MST_D_LRCLK] = &axg_mst_d_lrclk.hw, + [AUD_CLKID_MST_E_LRCLK] = &axg_mst_e_lrclk.hw, + [AUD_CLKID_MST_F_LRCLK] = &axg_mst_f_lrclk.hw, + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &axg_tdmin_a_sclk_sel.hw, + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &axg_tdmin_b_sclk_sel.hw, + [AUD_CLKID_TDMIN_C_SCLK_SEL] = &axg_tdmin_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &axg_tdmin_lb_sclk_sel.hw, + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &axg_tdmout_a_sclk_sel.hw, + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &axg_tdmout_b_sclk_sel.hw, + [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &axg_tdmout_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &axg_tdmin_a_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &axg_tdmin_b_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &axg_tdmin_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &axg_tdmin_lb_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &axg_tdmout_a_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &axg_tdmout_b_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &axg_tdmout_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &axg_tdmin_a_sclk_post_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &axg_tdmin_b_sclk_post_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &axg_tdmin_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &axg_tdmin_lb_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &axg_tdmout_a_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &axg_tdmout_b_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &axg_tdmout_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_A_SCLK] = &axg_tdmin_a_sclk.hw, + [AUD_CLKID_TDMIN_B_SCLK] = &axg_tdmin_b_sclk.hw, + [AUD_CLKID_TDMIN_C_SCLK] = &axg_tdmin_c_sclk.hw, + [AUD_CLKID_TDMIN_LB_SCLK] = &axg_tdmin_lb_sclk.hw, + [AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw, + [AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw, + [AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw, + [AUD_CLKID_TDMIN_A_LRCLK] = &axg_tdmin_a_lrclk.hw, + [AUD_CLKID_TDMIN_B_LRCLK] = &axg_tdmin_b_lrclk.hw, + [AUD_CLKID_TDMIN_C_LRCLK] = &axg_tdmin_c_lrclk.hw, + [AUD_CLKID_TDMIN_LB_LRCLK] = &axg_tdmin_lb_lrclk.hw, + [AUD_CLKID_TDMOUT_A_LRCLK] = &axg_tdmout_a_lrclk.hw, + [AUD_CLKID_TDMOUT_B_LRCLK] = &axg_tdmout_b_lrclk.hw, + [AUD_CLKID_TDMOUT_C_LRCLK] = &axg_tdmout_c_lrclk.hw, + [NR_CLKS] = NULL, + }, + .num = NR_CLKS, +}; + +/* Convenience table to populate regmap in .probe() */ +static struct clk_regmap *const axg_audio_clk_regmaps[] = { + &axg_ddr_arb, + &axg_pdm, + &axg_tdmin_a, + &axg_tdmin_b, + &axg_tdmin_c, + &axg_tdmin_lb, + &axg_tdmout_a, + &axg_tdmout_b, + &axg_tdmout_c, + &axg_frddr_a, + &axg_frddr_b, + &axg_frddr_c, + &axg_toddr_a, + &axg_toddr_b, + &axg_toddr_c, + &axg_loopback, + &axg_spdifin, + &axg_spdifout, + &axg_resample, + &axg_power_detect, + &axg_mst_a_mclk_sel, + &axg_mst_b_mclk_sel, + &axg_mst_c_mclk_sel, + &axg_mst_d_mclk_sel, + &axg_mst_e_mclk_sel, + &axg_mst_f_mclk_sel, + &axg_mst_a_mclk_div, + &axg_mst_b_mclk_div, + &axg_mst_c_mclk_div, + &axg_mst_d_mclk_div, + &axg_mst_e_mclk_div, + &axg_mst_f_mclk_div, + &axg_mst_a_mclk, + &axg_mst_b_mclk, + &axg_mst_c_mclk, + &axg_mst_d_mclk, + &axg_mst_e_mclk, + &axg_mst_f_mclk, + &axg_spdifout_clk_sel, + &axg_spdifout_clk_div, + &axg_spdifout_clk, + &axg_spdifin_clk_sel, + &axg_spdifin_clk_div, + &axg_spdifin_clk, + &axg_pdm_dclk_sel, + &axg_pdm_dclk_div, + &axg_pdm_dclk, + &axg_pdm_sysclk_sel, + &axg_pdm_sysclk_div, + &axg_pdm_sysclk, + &axg_mst_a_sclk_pre_en, + &axg_mst_b_sclk_pre_en, + &axg_mst_c_sclk_pre_en, + &axg_mst_d_sclk_pre_en, + &axg_mst_e_sclk_pre_en, + &axg_mst_f_sclk_pre_en, + &axg_mst_a_sclk_div, + &axg_mst_b_sclk_div, + &axg_mst_c_sclk_div, + &axg_mst_d_sclk_div, + &axg_mst_e_sclk_div, + &axg_mst_f_sclk_div, + &axg_mst_a_sclk_post_en, + &axg_mst_b_sclk_post_en, + &axg_mst_c_sclk_post_en, + &axg_mst_d_sclk_post_en, + &axg_mst_e_sclk_post_en, + &axg_mst_f_sclk_post_en, + &axg_mst_a_sclk, + &axg_mst_b_sclk, + &axg_mst_c_sclk, + &axg_mst_d_sclk, + &axg_mst_e_sclk, + &axg_mst_f_sclk, + &axg_mst_a_lrclk_div, + &axg_mst_b_lrclk_div, + &axg_mst_c_lrclk_div, + &axg_mst_d_lrclk_div, + &axg_mst_e_lrclk_div, + &axg_mst_f_lrclk_div, + &axg_mst_a_lrclk, + &axg_mst_b_lrclk, + &axg_mst_c_lrclk, + &axg_mst_d_lrclk, + &axg_mst_e_lrclk, + &axg_mst_f_lrclk, + &axg_tdmin_a_sclk_sel, + &axg_tdmin_b_sclk_sel, + &axg_tdmin_c_sclk_sel, + &axg_tdmin_lb_sclk_sel, + &axg_tdmout_a_sclk_sel, + &axg_tdmout_b_sclk_sel, + &axg_tdmout_c_sclk_sel, + &axg_tdmin_a_sclk_pre_en, + &axg_tdmin_b_sclk_pre_en, + &axg_tdmin_c_sclk_pre_en, + &axg_tdmin_lb_sclk_pre_en, + &axg_tdmout_a_sclk_pre_en, + &axg_tdmout_b_sclk_pre_en, + &axg_tdmout_c_sclk_pre_en, + &axg_tdmin_a_sclk_post_en, + &axg_tdmin_b_sclk_post_en, + &axg_tdmin_c_sclk_post_en, + &axg_tdmin_lb_sclk_post_en, + &axg_tdmout_a_sclk_post_en, + &axg_tdmout_b_sclk_post_en, + &axg_tdmout_c_sclk_post_en, + &axg_tdmin_a_sclk, + &axg_tdmin_b_sclk, + &axg_tdmin_c_sclk, + &axg_tdmin_lb_sclk, + &axg_tdmout_a_sclk, + &axg_tdmout_b_sclk, + &axg_tdmout_c_sclk, + &axg_tdmin_a_lrclk, + &axg_tdmin_b_lrclk, + &axg_tdmin_c_lrclk, + &axg_tdmin_lb_lrclk, + &axg_tdmout_a_lrclk, + &axg_tdmout_b_lrclk, + &axg_tdmout_c_lrclk, +}; + +static struct clk *devm_clk_get_enable(struct device *dev, char *id) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, id); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(dev, "failed to get %s", id); + return clk; + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "failed to enable %s", id); + return ERR_PTR(ret); + } + + ret = devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + clk); + if (ret) { + dev_err(dev, "failed to add reset action on %s", id); + return ERR_PTR(ret); + } + + return clk; +} + +static const struct clk_ops axg_clk_no_ops = {}; + +static struct clk_hw *axg_clk_hw_register_bypass(struct device *dev, + const char *name, + const char *parent_name) +{ + struct clk_hw *hw; + struct clk_init_data init; + char *clk_name; + int ret; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return ERR_PTR(-ENOMEM); + + clk_name = kasprintf(GFP_KERNEL, "axg_%s", name); + if (!clk_name) + return ERR_PTR(-ENOMEM); + + init.name = clk_name; + init.ops = &axg_clk_no_ops; + init.flags = 0; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + hw->init = &init; + + ret = devm_clk_hw_register(dev, hw); + kfree(clk_name); + + return ret ? ERR_PTR(ret) : hw; +} + +static int axg_register_clk_hw_input(struct device *dev, + const char *name, + unsigned int clkid) +{ + struct clk *parent_clk = devm_clk_get(dev, name); + struct clk_hw *hw = NULL; + + if (IS_ERR(parent_clk)) { + int err = PTR_ERR(parent_clk); + + /* It is ok if an input clock is missing */ + if (err == -ENOENT) { + dev_dbg(dev, "%s not provided", name); + } else { + if (err != -EPROBE_DEFER) + dev_err(dev, "failed to get %s clock", name); + return err; + } + } else { + hw = axg_clk_hw_register_bypass(dev, name, + __clk_get_name(parent_clk)); + } + + if (IS_ERR(hw)) { + dev_err(dev, "failed to register %s clock", name); + return PTR_ERR(hw); + } + + axg_audio_hw_onecell_data.hws[clkid] = hw; + return 0; +} + +static int axg_register_clk_hw_inputs(struct device *dev, + const char *basename, + unsigned int count, + unsigned int clkid) +{ + char *name; + int i, ret; + + for (i = 0; i < count; i++) { + name = kasprintf(GFP_KERNEL, "%s%d", basename, i); + if (!name) + return -ENOMEM; + + ret = axg_register_clk_hw_input(dev, name, clkid + i); + kfree(name); + if (ret) + return ret; + } + + return 0; +} + +static const struct regmap_config axg_audio_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = AUDIO_CLK_PDMIN_CTRL1, +}; + +static int axg_audio_clkc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *map; + struct resource *res; + void __iomem *regs; + struct clk *clk; + struct clk_hw *hw; + int ret, i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg); + if (IS_ERR(map)) { + dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map)); + return PTR_ERR(map); + } + + /* Get the mandatory peripheral clock */ + clk = devm_clk_get_enable(dev, "pclk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = device_reset(dev); + if (ret) { + dev_err(dev, "failed to reset device\n"); + return ret; + } + + /* Register the peripheral input clock */ + hw = axg_clk_hw_register_bypass(dev, "audio_pclk", + __clk_get_name(clk)); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + axg_audio_hw_onecell_data.hws[AUD_CLKID_PCLK] = hw; + + /* Register optional input master clocks */ + ret = axg_register_clk_hw_inputs(dev, "mst_in", + AXG_MST_IN_COUNT, + AUD_CLKID_MST0); + if (ret) + return ret; + + /* Register optional input slave sclks */ + ret = axg_register_clk_hw_inputs(dev, "slv_sclk", + AXG_SLV_SCLK_COUNT, + AUD_CLKID_SLV_SCLK0); + if (ret) + return ret; + + /* Register optional input slave lrclks */ + ret = axg_register_clk_hw_inputs(dev, "slv_lrclk", + AXG_SLV_LRCLK_COUNT, + AUD_CLKID_SLV_LRCLK0); + if (ret) + return ret; + + /* Populate regmap for the regmap backed clocks */ + for (i = 0; i < ARRAY_SIZE(axg_audio_clk_regmaps); i++) + axg_audio_clk_regmaps[i]->map = map; + + /* Take care to skip the registered input clocks */ + for (i = AUD_CLKID_DDR_ARB; i < axg_audio_hw_onecell_data.num; i++) { + hw = axg_audio_hw_onecell_data.hws[i]; + /* array might be sparse */ + if (!hw) + continue; + + ret = devm_clk_hw_register(dev, hw); + if (ret) { + dev_err(dev, "failed to register clock %s\n", + hw->init->name); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &axg_audio_hw_onecell_data); +} + +static const struct of_device_id clkc_match_table[] = { + { .compatible = "amlogic,axg-audio-clkc" }, + {} +}; +MODULE_DEVICE_TABLE(of, clkc_match_table); + +static struct platform_driver axg_audio_driver = { + .probe = axg_audio_clkc_probe, + .driver = { + .name = "axg-audio-clkc", + .of_match_table = clkc_match_table, + }, +}; +module_platform_driver(axg_audio_driver); + +MODULE_DESCRIPTION("Amlogic A113x Audio Clock driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h new file mode 100644 index 000000000000..7191b39c9d65 --- /dev/null +++ b/drivers/clk/meson/axg-audio.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __AXG_AUDIO_CLKC_H +#define __AXG_AUDIO_CLKC_H + +/* + * Audio Clock register offsets + * + * Register offsets from the datasheet must be multiplied by 4 before + * to get the right offset + */ +#define AUDIO_CLK_GATE_EN 0x000 +#define AUDIO_MCLK_A_CTRL 0x004 +#define AUDIO_MCLK_B_CTRL 0x008 +#define AUDIO_MCLK_C_CTRL 0x00C +#define AUDIO_MCLK_D_CTRL 0x010 +#define AUDIO_MCLK_E_CTRL 0x014 +#define AUDIO_MCLK_F_CTRL 0x018 +#define AUDIO_MST_A_SCLK_CTRL0 0x040 +#define AUDIO_MST_A_SCLK_CTRL1 0x044 +#define AUDIO_MST_B_SCLK_CTRL0 0x048 +#define AUDIO_MST_B_SCLK_CTRL1 0x04C +#define AUDIO_MST_C_SCLK_CTRL0 0x050 +#define AUDIO_MST_C_SCLK_CTRL1 0x054 +#define AUDIO_MST_D_SCLK_CTRL0 0x058 +#define AUDIO_MST_D_SCLK_CTRL1 0x05C +#define AUDIO_MST_E_SCLK_CTRL0 0x060 +#define AUDIO_MST_E_SCLK_CTRL1 0x064 +#define AUDIO_MST_F_SCLK_CTRL0 0x068 +#define AUDIO_MST_F_SCLK_CTRL1 0x06C +#define AUDIO_CLK_TDMIN_A_CTRL 0x080 +#define AUDIO_CLK_TDMIN_B_CTRL 0x084 +#define AUDIO_CLK_TDMIN_C_CTRL 0x088 +#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C +#define AUDIO_CLK_TDMOUT_A_CTRL 0x090 +#define AUDIO_CLK_TDMOUT_B_CTRL 0x094 +#define AUDIO_CLK_TDMOUT_C_CTRL 0x098 +#define AUDIO_CLK_SPDIFIN_CTRL 0x09C +#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0 +#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4 +#define AUDIO_CLK_LOCKER_CTRL 0x0A8 +#define AUDIO_CLK_PDMIN_CTRL0 0x0AC +#define AUDIO_CLK_PDMIN_CTRL1 0x0B0 + +/* + * CLKID index values + * These indices are entirely contrived and do not map onto the hardware. + */ + +#define AUD_CLKID_PCLK 0 +#define AUD_CLKID_MST0 1 +#define AUD_CLKID_MST1 2 +#define AUD_CLKID_MST2 3 +#define AUD_CLKID_MST3 4 +#define AUD_CLKID_MST4 5 +#define AUD_CLKID_MST5 6 +#define AUD_CLKID_MST6 7 +#define AUD_CLKID_MST7 8 +#define AUD_CLKID_MST_A_MCLK_SEL 59 +#define AUD_CLKID_MST_B_MCLK_SEL 60 +#define AUD_CLKID_MST_C_MCLK_SEL 61 +#define AUD_CLKID_MST_D_MCLK_SEL 62 +#define AUD_CLKID_MST_E_MCLK_SEL 63 +#define AUD_CLKID_MST_F_MCLK_SEL 64 +#define AUD_CLKID_MST_A_MCLK_DIV 65 +#define AUD_CLKID_MST_B_MCLK_DIV 66 +#define AUD_CLKID_MST_C_MCLK_DIV 67 +#define AUD_CLKID_MST_D_MCLK_DIV 68 +#define AUD_CLKID_MST_E_MCLK_DIV 69 +#define AUD_CLKID_MST_F_MCLK_DIV 70 +#define AUD_CLKID_SPDIFOUT_CLK_SEL 71 +#define AUD_CLKID_SPDIFOUT_CLK_DIV 72 +#define AUD_CLKID_SPDIFIN_CLK_SEL 73 +#define AUD_CLKID_SPDIFIN_CLK_DIV 74 +#define AUD_CLKID_PDM_DCLK_SEL 75 +#define AUD_CLKID_PDM_DCLK_DIV 76 +#define AUD_CLKID_PDM_SYSCLK_SEL 77 +#define AUD_CLKID_PDM_SYSCLK_DIV 78 +#define AUD_CLKID_MST_A_SCLK_PRE_EN 92 +#define AUD_CLKID_MST_B_SCLK_PRE_EN 93 +#define AUD_CLKID_MST_C_SCLK_PRE_EN 94 +#define AUD_CLKID_MST_D_SCLK_PRE_EN 95 +#define AUD_CLKID_MST_E_SCLK_PRE_EN 96 +#define AUD_CLKID_MST_F_SCLK_PRE_EN 97 +#define AUD_CLKID_MST_A_SCLK_DIV 98 +#define AUD_CLKID_MST_B_SCLK_DIV 99 +#define AUD_CLKID_MST_C_SCLK_DIV 100 +#define AUD_CLKID_MST_D_SCLK_DIV 101 +#define AUD_CLKID_MST_E_SCLK_DIV 102 +#define AUD_CLKID_MST_F_SCLK_DIV 103 +#define AUD_CLKID_MST_A_SCLK_POST_EN 104 +#define AUD_CLKID_MST_B_SCLK_POST_EN 105 +#define AUD_CLKID_MST_C_SCLK_POST_EN 106 +#define AUD_CLKID_MST_D_SCLK_POST_EN 107 +#define AUD_CLKID_MST_E_SCLK_POST_EN 108 +#define AUD_CLKID_MST_F_SCLK_POST_EN 109 +#define AUD_CLKID_MST_A_LRCLK_DIV 110 +#define AUD_CLKID_MST_B_LRCLK_DIV 111 +#define AUD_CLKID_MST_C_LRCLK_DIV 112 +#define AUD_CLKID_MST_D_LRCLK_DIV 113 +#define AUD_CLKID_MST_E_LRCLK_DIV 114 +#define AUD_CLKID_MST_F_LRCLK_DIV 115 +#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 137 +#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 138 +#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN 139 +#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 140 +#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 141 +#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 142 +#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN 143 +#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 144 +#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 145 +#define AUD_CLKID_TDMIN_C_SCLK_POST_EN 146 +#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 147 +#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 148 +#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 149 +#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150 + +/* include the CLKIDs which are part of the DT bindings */ +#include <dt-bindings/clock/axg-audio-clkc.h> + +#define NR_CLKS 151 + +#endif /*__AXG_AUDIO_CLKC_H */ diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c index bd4dbc696b88..00ce62ad6416 100644 --- a/drivers/clk/meson/axg.c +++ b/drivers/clk/meson/axg.c @@ -12,7 +12,6 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/init.h> -#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/mfd/syscon.h> #include <linux/platform_device.h> @@ -626,6 +625,137 @@ static struct clk_regmap axg_mpll3 = { }, }; +static const struct pll_rate_table axg_pcie_pll_rate_table[] = { + { + .rate = 100000000, + .m = 200, + .n = 3, + .od = 1, + .od2 = 3, + }, + { /* sentinel */ }, +}; + +static const struct reg_sequence axg_pcie_init_regs[] = { + { .reg = HHI_PCIE_PLL_CNTL, .def = 0x400106c8 }, + { .reg = HHI_PCIE_PLL_CNTL1, .def = 0x0084a2aa }, + { .reg = HHI_PCIE_PLL_CNTL2, .def = 0xb75020be }, + { .reg = HHI_PCIE_PLL_CNTL3, .def = 0x0a47488e }, + { .reg = HHI_PCIE_PLL_CNTL4, .def = 0xc000004d }, + { .reg = HHI_PCIE_PLL_CNTL5, .def = 0x00078000 }, + { .reg = HHI_PCIE_PLL_CNTL6, .def = 0x002323c6 }, +}; + +static struct clk_regmap axg_pcie_pll = { + .data = &(struct meson_clk_pll_data){ + .m = { + .reg_off = HHI_PCIE_PLL_CNTL, + .shift = 0, + .width = 9, + }, + .n = { + .reg_off = HHI_PCIE_PLL_CNTL, + .shift = 9, + .width = 5, + }, + .od = { + .reg_off = HHI_PCIE_PLL_CNTL, + .shift = 16, + .width = 2, + }, + .od2 = { + .reg_off = HHI_PCIE_PLL_CNTL6, + .shift = 6, + .width = 2, + }, + .frac = { + .reg_off = HHI_PCIE_PLL_CNTL1, + .shift = 0, + .width = 12, + }, + .l = { + .reg_off = HHI_PCIE_PLL_CNTL, + .shift = 31, + .width = 1, + }, + .rst = { + .reg_off = HHI_PCIE_PLL_CNTL, + .shift = 29, + .width = 1, + }, + .table = axg_pcie_pll_rate_table, + .init_regs = axg_pcie_init_regs, + .init_count = ARRAY_SIZE(axg_pcie_init_regs), + }, + .hw.init = &(struct clk_init_data){ + .name = "pcie_pll", + .ops = &meson_clk_pll_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + }, +}; + +static struct clk_regmap axg_pcie_mux = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_PCIE_PLL_CNTL6, + .mask = 0x1, + .shift = 2, + }, + .hw.init = &(struct clk_init_data){ + .name = "pcie_mux", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "mpll3", "pcie_pll" }, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_pcie_ref = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_PCIE_PLL_CNTL6, + .mask = 0x1, + .shift = 1, + /* skip the parent 0, reserved for debug */ + .table = (u32[]){ 1 }, + }, + .hw.init = &(struct clk_init_data){ + .name = "pcie_ref", + .ops = &clk_regmap_mux_ops, + .parent_names = (const char *[]){ "pcie_mux" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_pcie_cml_en0 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_PCIE_PLL_CNTL6, + .bit_idx = 4, + }, + .hw.init = &(struct clk_init_data) { + .name = "pcie_cml_en0", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "pcie_ref" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + + }, +}; + +static struct clk_regmap axg_pcie_cml_en1 = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_PCIE_PLL_CNTL6, + .bit_idx = 3, + }, + .hw.init = &(struct clk_init_data) { + .name = "pcie_cml_en1", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "pcie_ref" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + static u32 mux_table_clk81[] = { 0, 2, 3, 4, 5, 6, 7 }; static const char * const clk81_parent_names[] = { "xtal", "fclk_div7", "mpll1", "mpll2", "fclk_div4", @@ -779,6 +909,63 @@ static struct clk_regmap axg_sd_emmc_c_clk0 = { }, }; +static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8, + 9, 10, 11, 13, 14, }; +static const char * const gen_clk_parent_names[] = { + "xtal", "hifi_pll", "mpll0", "mpll1", "mpll2", "mpll3", + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll", +}; + +static struct clk_regmap axg_gen_clk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_GEN_CLK_CNTL, + .mask = 0xf, + .shift = 12, + .table = mux_table_gen_clk, + }, + .hw.init = &(struct clk_init_data){ + .name = "gen_clk_sel", + .ops = &clk_regmap_mux_ops, + /* + * bits 15:12 selects from 14 possible parents: + * xtal, [rtc_oscin_i], [sys_cpu_div16], [ddr_dpll_pt], + * hifi_pll, mpll0, mpll1, mpll2, mpll3, fdiv4, + * fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll + */ + .parent_names = gen_clk_parent_names, + .num_parents = ARRAY_SIZE(gen_clk_parent_names), + }, +}; + +static struct clk_regmap axg_gen_clk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_GEN_CLK_CNTL, + .shift = 0, + .width = 11, + }, + .hw.init = &(struct clk_init_data){ + .name = "gen_clk_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "gen_clk_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap axg_gen_clk = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_GEN_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "gen_clk", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "gen_clk_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(axg_ddr, HHI_GCLK_MPEG0, 0); static MESON_GATE(axg_audio_locker, HHI_GCLK_MPEG0, 2); @@ -821,6 +1008,7 @@ static MESON_GATE(axg_mmc_pclk, HHI_GCLK_MPEG2, 11); static MESON_GATE(axg_vpu_intr, HHI_GCLK_MPEG2, 25); static MESON_GATE(axg_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26); static MESON_GATE(axg_gic, HHI_GCLK_MPEG2, 30); +static MESON_GATE(axg_mipi_enable, HHI_MIPI_CNTL0, 29); /* Always On (AO) domain gates */ @@ -910,6 +1098,15 @@ static struct clk_hw_onecell_data axg_hw_onecell_data = { [CLKID_FCLK_DIV4_DIV] = &axg_fclk_div4_div.hw, [CLKID_FCLK_DIV5_DIV] = &axg_fclk_div5_div.hw, [CLKID_FCLK_DIV7_DIV] = &axg_fclk_div7_div.hw, + [CLKID_PCIE_PLL] = &axg_pcie_pll.hw, + [CLKID_PCIE_MUX] = &axg_pcie_mux.hw, + [CLKID_PCIE_REF] = &axg_pcie_ref.hw, + [CLKID_PCIE_CML_EN0] = &axg_pcie_cml_en0.hw, + [CLKID_PCIE_CML_EN1] = &axg_pcie_cml_en1.hw, + [CLKID_MIPI_ENABLE] = &axg_mipi_enable.hw, + [CLKID_GEN_CLK_SEL] = &axg_gen_clk_sel.hw, + [CLKID_GEN_CLK_DIV] = &axg_gen_clk_div.hw, + [CLKID_GEN_CLK] = &axg_gen_clk.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -988,6 +1185,15 @@ static struct clk_regmap *const axg_clk_regmaps[] = { &axg_fclk_div4, &axg_fclk_div5, &axg_fclk_div7, + &axg_pcie_pll, + &axg_pcie_mux, + &axg_pcie_ref, + &axg_pcie_cml_en0, + &axg_pcie_cml_en1, + &axg_mipi_enable, + &axg_gen_clk_sel, + &axg_gen_clk_div, + &axg_gen_clk, }; static const struct of_device_id clkc_match_table[] = { @@ -995,49 +1201,17 @@ static const struct of_device_id clkc_match_table[] = { {} }; -static const struct regmap_config clkc_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - static int axg_clkc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; - void __iomem *clk_base = NULL; struct regmap *map; int ret, i; /* Get the hhi system controller node if available */ map = syscon_node_to_regmap(of_get_parent(dev->of_node)); if (IS_ERR(map)) { - dev_err(dev, - "failed to get HHI regmap - Trying obsolete regs\n"); - - /* - * FIXME: HHI registers should be accessed through - * the appropriate system controller. This is required because - * there is more than just clocks in this register space - * - * This fallback method is only provided temporarily until - * all the platform DTs are properly using the syscon node - */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - - clk_base = devm_ioremap(dev, res->start, resource_size(res)); - if (!clk_base) { - dev_err(dev, "Unable to map clk base\n"); - return -ENXIO; - } - - map = devm_regmap_init_mmio(dev, clk_base, - &clkc_regmap_config); - if (IS_ERR(map)) - return PTR_ERR(map); + dev_err(dev, "failed to get HHI regmap\n"); + return PTR_ERR(map); } /* Populate regmap for the regmap backed clocks */ diff --git a/drivers/clk/meson/axg.h b/drivers/clk/meson/axg.h index b421df6a7ea0..1d04144a1b2c 100644 --- a/drivers/clk/meson/axg.h +++ b/drivers/clk/meson/axg.h @@ -16,6 +16,7 @@ * Register offsets from the data sheet must be multiplied by 4 before * adding them to the base address to get the right value. */ +#define HHI_MIPI_CNTL0 0x00 #define HHI_GP0_PLL_CNTL 0x40 #define HHI_GP0_PLL_CNTL2 0x44 #define HHI_GP0_PLL_CNTL3 0x48 @@ -127,8 +128,13 @@ #define CLKID_FCLK_DIV4_DIV 73 #define CLKID_FCLK_DIV5_DIV 74 #define CLKID_FCLK_DIV7_DIV 75 +#define CLKID_PCIE_PLL 76 +#define CLKID_PCIE_MUX 77 +#define CLKID_PCIE_REF 78 +#define CLKID_GEN_CLK_SEL 82 +#define CLKID_GEN_CLK_DIV 83 -#define NR_CLKS 76 +#define NR_CLKS 85 /* include the CLKIDs that have been made part of the DT binding */ #include <dt-bindings/clock/axg-clkc.h> diff --git a/drivers/clk/meson/clk-audio-divider.c b/drivers/clk/meson/clk-audio-divider.c deleted file mode 100644 index e4cf96ba704e..000000000000 --- a/drivers/clk/meson/clk-audio-divider.c +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2017 AmLogic, Inc. - * Author: Jerome Brunet <jbrunet@baylibre.com> - */ - -/* - * i2s master clock divider: The algorithm of the generic clk-divider used with - * a very precise clock parent such as the mpll tends to select a low divider - * factor. This gives poor results with this particular divider, especially with - * high frequencies (> 100 MHz) - * - * This driver try to select the maximum possible divider with the rate the - * upstream clock can provide. - */ - -#include <linux/clk-provider.h> -#include "clkc.h" - -static inline struct meson_clk_audio_div_data * -meson_clk_audio_div_data(struct clk_regmap *clk) -{ - return (struct meson_clk_audio_div_data *)clk->data; -} - -static int _div_round(unsigned long parent_rate, unsigned long rate, - unsigned long flags) -{ - if (flags & CLK_DIVIDER_ROUND_CLOSEST) - return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, rate); - - return DIV_ROUND_UP_ULL((u64)parent_rate, rate); -} - -static int _get_val(unsigned long parent_rate, unsigned long rate) -{ - return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1; -} - -static int _valid_divider(unsigned int width, int divider) -{ - int max_divider = 1 << width; - - return clamp(divider, 1, max_divider); -} - -static unsigned long audio_divider_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk); - unsigned long divider; - - divider = meson_parm_read(clk->map, &adiv->div) + 1; - - return DIV_ROUND_UP_ULL((u64)parent_rate, divider); -} - -static long audio_divider_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *parent_rate) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk); - unsigned long max_prate; - int divider; - - if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { - divider = _div_round(*parent_rate, rate, adiv->flags); - divider = _valid_divider(adiv->div.width, divider); - return DIV_ROUND_UP_ULL((u64)*parent_rate, divider); - } - - /* Get the maximum parent rate */ - max_prate = clk_hw_round_rate(clk_hw_get_parent(hw), ULONG_MAX); - - /* Get the corresponding rounded down divider */ - divider = max_prate / rate; - divider = _valid_divider(adiv->div.width, divider); - - /* Get actual rate of the parent */ - *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), - divider * rate); - - return DIV_ROUND_UP_ULL((u64)*parent_rate, divider); -} - -static int audio_divider_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk); - int val = _get_val(parent_rate, rate); - - meson_parm_write(clk->map, &adiv->div, val); - - return 0; -} - -const struct clk_ops meson_clk_audio_divider_ro_ops = { - .recalc_rate = audio_divider_recalc_rate, - .round_rate = audio_divider_round_rate, -}; - -const struct clk_ops meson_clk_audio_divider_ops = { - .recalc_rate = audio_divider_recalc_rate, - .round_rate = audio_divider_round_rate, - .set_rate = audio_divider_set_rate, -}; diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c new file mode 100644 index 000000000000..cba43748ce3d --- /dev/null +++ b/drivers/clk/meson/clk-phase.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#include <linux/clk-provider.h> +#include "clkc.h" + +#define phase_step(_width) (360 / (1 << (_width))) + +static inline struct meson_clk_phase_data * +meson_clk_phase_data(struct clk_regmap *clk) +{ + return (struct meson_clk_phase_data *)clk->data; +} + +int meson_clk_degrees_from_val(unsigned int val, unsigned int width) +{ + return phase_step(width) * val; +} +EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val); + +unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width) +{ + unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width)); + + /* + * This last calculation is here for cases when degrees is rounded + * to 360, in which case val == (1 << width). + */ + return val % (1 << width); +} +EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val); + +static int meson_clk_phase_get_phase(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_phase_data *phase = meson_clk_phase_data(clk); + unsigned int val; + + val = meson_parm_read(clk->map, &phase->ph); + + return meson_clk_degrees_from_val(val, phase->ph.width); +} + +static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_phase_data *phase = meson_clk_phase_data(clk); + unsigned int val; + + val = meson_clk_degrees_to_val(degrees, phase->ph.width); + meson_parm_write(clk->map, &phase->ph, val); + + return 0; +} + +const struct clk_ops meson_clk_phase_ops = { + .get_phase = meson_clk_phase_get_phase, + .set_phase = meson_clk_phase_set_phase, +}; +EXPORT_SYMBOL_GPL(meson_clk_phase_ops); diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c new file mode 100644 index 000000000000..4a59936251e5 --- /dev/null +++ b/drivers/clk/meson/clk-triphase.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#include <linux/clk-provider.h> +#include "clkc-audio.h" + +/* + * This is a special clock for the audio controller. + * The phase of mst_sclk clock output can be controlled independently + * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2). + * Controlling these 3 phases as just one makes things simpler and + * give the same clock view to all the element on the i2s bus. + * If necessary, we can still control the phase in the tdm block + * which makes these independent control redundant. + */ +static inline struct meson_clk_triphase_data * +meson_clk_triphase_data(struct clk_regmap *clk) +{ + return (struct meson_clk_triphase_data *)clk->data; +} + +static void meson_clk_triphase_sync(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + /* Get phase 0 and sync it to phase 1 and 2 */ + val = meson_parm_read(clk->map, &tph->ph0); + meson_parm_write(clk->map, &tph->ph1, val); + meson_parm_write(clk->map, &tph->ph2, val); +} + +static int meson_clk_triphase_get_phase(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + /* Phase are in sync, reading phase 0 is enough */ + val = meson_parm_read(clk->map, &tph->ph0); + + return meson_clk_degrees_from_val(val, tph->ph0.width); +} + +static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk); + unsigned int val; + + val = meson_clk_degrees_to_val(degrees, tph->ph0.width); + meson_parm_write(clk->map, &tph->ph0, val); + meson_parm_write(clk->map, &tph->ph1, val); + meson_parm_write(clk->map, &tph->ph2, val); + + return 0; +} + +const struct clk_ops meson_clk_triphase_ops = { + .init = meson_clk_triphase_sync, + .get_phase = meson_clk_triphase_get_phase, + .set_phase = meson_clk_triphase_set_phase, +}; +EXPORT_SYMBOL_GPL(meson_clk_triphase_ops); diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h new file mode 100644 index 000000000000..0a7c157ebf81 --- /dev/null +++ b/drivers/clk/meson/clkc-audio.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __MESON_CLKC_AUDIO_H +#define __MESON_CLKC_AUDIO_H + +#include "clkc.h" + +struct meson_clk_triphase_data { + struct parm ph0; + struct parm ph1; + struct parm ph2; +}; + +struct meson_sclk_div_data { + struct parm div; + struct parm hi; + unsigned int cached_div; + struct clk_duty cached_duty; +}; + +extern const struct clk_ops meson_clk_triphase_ops; +extern const struct clk_ops meson_sclk_div_ops; + +#endif /* __MESON_CLKC_AUDIO_H */ diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index 2fb084330ee9..24cec16b6038 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -91,11 +91,13 @@ struct meson_clk_mpll_data { #define CLK_MESON_MPLL_ROUND_CLOSEST BIT(0) -struct meson_clk_audio_div_data { - struct parm div; - u8 flags; +struct meson_clk_phase_data { + struct parm ph; }; +int meson_clk_degrees_from_val(unsigned int val, unsigned int width); +unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width); + #define MESON_GATE(_name, _reg, _bit) \ struct clk_regmap _name = { \ .data = &(struct clk_regmap_gate_data){ \ @@ -117,7 +119,6 @@ extern const struct clk_ops meson_clk_pll_ops; extern const struct clk_ops meson_clk_cpu_ops; extern const struct clk_ops meson_clk_mpll_ro_ops; extern const struct clk_ops meson_clk_mpll_ops; -extern const struct clk_ops meson_clk_audio_divider_ro_ops; -extern const struct clk_ops meson_clk_audio_divider_ops; +extern const struct clk_ops meson_clk_phase_ops; #endif /* __CLKC_H */ diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 177fffb9ebef..86d3ae58e84c 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -7,7 +7,6 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/init.h> -#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/mfd/syscon.h> #include <linux/platform_device.h> @@ -971,28 +970,26 @@ static struct clk_regmap gxbb_cts_amclk_sel = { .mask = 0x3, .shift = 9, .table = (u32[]){ 1, 2, 3 }, + .flags = CLK_MUX_ROUND_CLOSEST, }, .hw.init = &(struct clk_init_data){ .name = "cts_amclk_sel", .ops = &clk_regmap_mux_ops, .parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" }, .num_parents = 3, - .flags = CLK_SET_RATE_PARENT, }, }; static struct clk_regmap gxbb_cts_amclk_div = { - .data = &(struct meson_clk_audio_div_data){ - .div = { - .reg_off = HHI_AUD_CLK_CNTL, - .shift = 0, - .width = 8, - }, + .data = &(struct clk_regmap_div_data) { + .offset = HHI_AUD_CLK_CNTL, + .shift = 0, + .width = 8, .flags = CLK_DIVIDER_ROUND_CLOSEST, }, .hw.init = &(struct clk_init_data){ .name = "cts_amclk_div", - .ops = &meson_clk_audio_divider_ops, + .ops = &clk_regmap_divider_ops, .parent_names = (const char *[]){ "cts_amclk_sel" }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1019,13 +1016,13 @@ static struct clk_regmap gxbb_cts_mclk_i958_sel = { .mask = 0x3, .shift = 25, .table = (u32[]){ 1, 2, 3 }, + .flags = CLK_MUX_ROUND_CLOSEST, }, .hw.init = &(struct clk_init_data) { .name = "cts_mclk_i958_sel", .ops = &clk_regmap_mux_ops, .parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" }, .num_parents = 3, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -1627,6 +1624,63 @@ static struct clk_regmap gxbb_vdec_hevc = { }, }; +static u32 mux_table_gen_clk[] = { 0, 4, 5, 6, 7, 8, + 9, 10, 11, 13, 14, }; +static const char * const gen_clk_parent_names[] = { + "xtal", "vdec_1", "vdec_hevc", "mpll0", "mpll1", "mpll2", + "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "gp0_pll", +}; + +static struct clk_regmap gxbb_gen_clk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_GEN_CLK_CNTL, + .mask = 0xf, + .shift = 12, + .table = mux_table_gen_clk, + }, + .hw.init = &(struct clk_init_data){ + .name = "gen_clk_sel", + .ops = &clk_regmap_mux_ops, + /* + * bits 15:12 selects from 14 possible parents: + * xtal, [rtc_oscin_i], [sys_cpu_div16], [ddr_dpll_pt], + * vid_pll, vid2_pll (hevc), mpll0, mpll1, mpll2, fdiv4, + * fdiv3, fdiv5, [cts_msr_clk], fdiv7, gp0_pll + */ + .parent_names = gen_clk_parent_names, + .num_parents = ARRAY_SIZE(gen_clk_parent_names), + }, +}; + +static struct clk_regmap gxbb_gen_clk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_GEN_CLK_CNTL, + .shift = 0, + .width = 11, + }, + .hw.init = &(struct clk_init_data){ + .name = "gen_clk_div", + .ops = &clk_regmap_divider_ops, + .parent_names = (const char *[]){ "gen_clk_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap gxbb_gen_clk = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_GEN_CLK_CNTL, + .bit_idx = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "gen_clk", + .ops = &clk_regmap_gate_ops, + .parent_names = (const char *[]){ "gen_clk_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0); static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1); @@ -1876,6 +1930,9 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw, [CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw, [CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw, + [CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw, + [CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw, + [CLKID_GEN_CLK] = &gxbb_gen_clk.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -2038,6 +2095,9 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = { [CLKID_VDEC_HEVC_SEL] = &gxbb_vdec_hevc_sel.hw, [CLKID_VDEC_HEVC_DIV] = &gxbb_vdec_hevc_div.hw, [CLKID_VDEC_HEVC] = &gxbb_vdec_hevc.hw, + [CLKID_GEN_CLK_SEL] = &gxbb_gen_clk_sel.hw, + [CLKID_GEN_CLK_DIV] = &gxbb_gen_clk_div.hw, + [CLKID_GEN_CLK] = &gxbb_gen_clk.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -2202,6 +2262,9 @@ static struct clk_regmap *const gx_clk_regmaps[] = { &gxbb_vdec_hevc_sel, &gxbb_vdec_hevc_div, &gxbb_vdec_hevc, + &gxbb_gen_clk_sel, + &gxbb_gen_clk_div, + &gxbb_gen_clk, }; struct clkc_data { @@ -2228,17 +2291,9 @@ static const struct of_device_id clkc_match_table[] = { {}, }; -static const struct regmap_config clkc_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - static int gxbb_clkc_probe(struct platform_device *pdev) { const struct clkc_data *clkc_data; - struct resource *res; - void __iomem *clk_base; struct regmap *map; int ret, i; struct device *dev = &pdev->dev; @@ -2250,31 +2305,8 @@ static int gxbb_clkc_probe(struct platform_device *pdev) /* Get the hhi system controller node if available */ map = syscon_node_to_regmap(of_get_parent(dev->of_node)); if (IS_ERR(map)) { - dev_err(dev, - "failed to get HHI regmap - Trying obsolete regs\n"); - - /* - * FIXME: HHI registers should be accessed through - * the appropriate system controller. This is required because - * there is more than just clocks in this register space - * - * This fallback method is only provided temporarily until - * all the platform DTs are properly using the syscon node - */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - clk_base = devm_ioremap(dev, res->start, resource_size(res)); - if (!clk_base) { - dev_err(dev, "Unable to map clk base\n"); - return -ENXIO; - } - - map = devm_regmap_init_mmio(dev, clk_base, - &clkc_regmap_config); - if (IS_ERR(map)) - return PTR_ERR(map); + dev_err(dev, "failed to get HHI regmap\n"); + return PTR_ERR(map); } /* Populate regmap for the common regmap backed clocks */ diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index ec1a812bf1fd..20dfb1daf5b8 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -66,7 +66,6 @@ #define HHI_USB_CLK_CNTL 0x220 /* 0x88 offset in data sheet */ #define HHI_32K_CLK_CNTL 0x224 /* 0x89 offset in data sheet */ #define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */ -#define HHI_GEN_CLK_CNTL 0x228 /* 0x8a offset in data sheet */ #define HHI_PCM_CLK_CNTL 0x258 /* 0x96 offset in data sheet */ #define HHI_NAND_CLK_CNTL 0x25C /* 0x97 offset in data sheet */ @@ -158,8 +157,10 @@ #define CLKID_VDEC_1_DIV 152 #define CLKID_VDEC_HEVC_SEL 154 #define CLKID_VDEC_HEVC_DIV 155 +#define CLKID_GEN_CLK_SEL 157 +#define CLKID_GEN_CLK_DIV 158 -#define NR_CLKS 157 +#define NR_CLKS 160 /* include the CLKIDs that have been made part of the DT binding */ #include <dt-bindings/clock/gxbb-clkc.h> diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c new file mode 100644 index 000000000000..bc64019b8eeb --- /dev/null +++ b/drivers/clk/meson/sclk-div.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + * + * Sample clock generator divider: + * This HW divider gates with value 0 but is otherwise a zero based divider: + * + * val >= 1 + * divider = val + 1 + * + * The duty cycle may also be set for the LR clock variant. The duty cycle + * ratio is: + * + * hi = [0 - val] + * duty_cycle = (1 + hi) / (1 + val) + */ + +#include "clkc-audio.h" + +static inline struct meson_sclk_div_data * +meson_sclk_div_data(struct clk_regmap *clk) +{ + return (struct meson_sclk_div_data *)clk->data; +} + +static int sclk_div_maxval(struct meson_sclk_div_data *sclk) +{ + return (1 << sclk->div.width) - 1; +} + +static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk) +{ + return sclk_div_maxval(sclk) + 1; +} + +static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate, + unsigned long prate, int maxdiv) +{ + int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate); + + return clamp(div, 2, maxdiv); +} + +static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate, + unsigned long *prate, + struct meson_sclk_div_data *sclk) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + int bestdiv = 0, i; + unsigned long maxdiv, now, parent_now; + unsigned long best = 0, best_parent = 0; + + if (!rate) + rate = 1; + + maxdiv = sclk_div_maxdiv(sclk); + + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) + return sclk_div_getdiv(hw, rate, *prate, maxdiv); + + /* + * The maximum divider we can use without overflowing + * unsigned long in rate * i below + */ + maxdiv = min(ULONG_MAX / rate, maxdiv); + + for (i = 2; i <= maxdiv; i++) { + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without needing to change + * parent rate, so return the divider immediately. + */ + if (rate * i == *prate) + return i; + + parent_now = clk_hw_round_rate(parent, rate * i); + now = DIV_ROUND_UP_ULL((u64)parent_now, i); + + if (abs(rate - now) < abs(rate - best)) { + bestdiv = i; + best = now; + best_parent = parent_now; + } + } + + if (!bestdiv) + bestdiv = sclk_div_maxdiv(sclk); + else + *prate = best_parent; + + return bestdiv; +} + +static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + int div; + + div = sclk_div_bestdiv(hw, rate, prate, sclk); + + return DIV_ROUND_UP_ULL((u64)*prate, div); +} + +static void sclk_apply_ratio(struct clk_regmap *clk, + struct meson_sclk_div_data *sclk) +{ + unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div * + sclk->cached_duty.num, + sclk->cached_duty.den); + + if (hi) + hi -= 1; + + meson_parm_write(clk->map, &sclk->hi, hi); +} + +static int sclk_div_set_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + + if (MESON_PARM_APPLICABLE(&sclk->hi)) { + memcpy(&sclk->cached_duty, duty, sizeof(*duty)); + sclk_apply_ratio(clk, sclk); + } + + return 0; +} + +static int sclk_div_get_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + int hi; + + if (!MESON_PARM_APPLICABLE(&sclk->hi)) { + duty->num = 1; + duty->den = 2; + return 0; + } + + hi = meson_parm_read(clk->map, &sclk->hi); + duty->num = hi + 1; + duty->den = sclk->cached_div; + return 0; +} + +static void sclk_apply_divider(struct clk_regmap *clk, + struct meson_sclk_div_data *sclk) +{ + if (MESON_PARM_APPLICABLE(&sclk->hi)) + sclk_apply_ratio(clk, sclk); + + meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1); +} + +static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + unsigned long maxdiv = sclk_div_maxdiv(sclk); + + sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv); + + if (clk_hw_is_enabled(hw)) + sclk_apply_divider(clk, sclk); + + return 0; +} + +static unsigned long sclk_div_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + + return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div); +} + +static int sclk_div_enable(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + + sclk_apply_divider(clk, sclk); + + return 0; +} + +static void sclk_div_disable(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + + meson_parm_write(clk->map, &sclk->div, 0); +} + +static int sclk_div_is_enabled(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + + if (meson_parm_read(clk->map, &sclk->div)) + return 1; + + return 0; +} + +static void sclk_div_init(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk); + unsigned int val; + + val = meson_parm_read(clk->map, &sclk->div); + + /* if the divider is initially disabled, assume max */ + if (!val) + sclk->cached_div = sclk_div_maxdiv(sclk); + else + sclk->cached_div = val + 1; + + sclk_div_get_duty_cycle(hw, &sclk->cached_duty); +} + +const struct clk_ops meson_sclk_div_ops = { + .recalc_rate = sclk_div_recalc_rate, + .round_rate = sclk_div_round_rate, + .set_rate = sclk_div_set_rate, + .enable = sclk_div_enable, + .disable = sclk_div_disable, + .is_enabled = sclk_div_is_enabled, + .get_duty_cycle = sclk_div_get_duty_cycle, + .set_duty_cycle = sclk_div_set_duty_cycle, + .init = sclk_div_init, +}; +EXPORT_SYMBOL_GPL(meson_sclk_div_ops); diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index 44e4e27eddad..499f5962c8b0 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Marvell Armada 37xx SoC Peripheral clocks * @@ -5,10 +6,6 @@ * * Gregory CLEMENT <gregory.clement@free-electrons.com> * - * This file is licensed under the terms of the GNU General Public - * License version 2 or later. This program is licensed "as is" - * without any warranty of any kind, whether express or implied. - * * Most of the peripheral clocks can be modelled like this: * _____ _______ _______ * TBG-A-P --| | | | | | ______ @@ -419,7 +416,6 @@ static unsigned int armada_3700_pm_dvfs_get_cpu_parent(struct regmap *base) static u8 clk_pm_cpu_get_parent(struct clk_hw *hw) { struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw); - int num_parents = clk_hw_get_num_parents(hw); u32 val; if (armada_3700_pm_dvfs_is_enabled(pm_cpu->nb_pm_base)) { @@ -429,9 +425,6 @@ static u8 clk_pm_cpu_get_parent(struct clk_hw *hw) val &= pm_cpu->mask_mux; } - if (val >= num_parents) - return -EINVAL; - return val; } diff --git a/drivers/clk/pxa/clk-pxa25x.c b/drivers/clk/pxa/clk-pxa25x.c index 6416c1f8e632..e88f8e01fe3a 100644 --- a/drivers/clk/pxa/clk-pxa25x.c +++ b/drivers/clk/pxa/clk-pxa25x.c @@ -292,8 +292,10 @@ static void __init pxa25x_register_plls(void) { clk_register_fixed_rate(NULL, "osc_3_6864mhz", NULL, CLK_GET_RATE_NOCACHE, 3686400); - clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, - CLK_GET_RATE_NOCACHE, 32768); + clkdev_pxa_register(CLK_OSC32k768, "osc_32_768khz", NULL, + clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, + CLK_GET_RATE_NOCACHE, + 32768)); clk_register_fixed_rate(NULL, "clk_dummy", NULL, 0, 0); clk_register_fixed_factor(NULL, "ppll_95_85mhz", "osc_3_6864mhz", 0, 26, 1); diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c index 25a30194d27a..d40b63e7bbce 100644 --- a/drivers/clk/pxa/clk-pxa27x.c +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -314,9 +314,10 @@ static void __init pxa27x_register_plls(void) clk_register_fixed_rate(NULL, "osc_13mhz", NULL, CLK_GET_RATE_NOCACHE, 13 * MHz); - clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, - CLK_GET_RATE_NOCACHE, - 32768 * KHz); + clkdev_pxa_register(CLK_OSC32k768, "osc_32_768khz", NULL, + clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, + CLK_GET_RATE_NOCACHE, + 32768 * KHz)); clk_register_fixed_rate(NULL, "clk_dummy", NULL, 0, 0); clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1); } diff --git a/drivers/clk/pxa/clk-pxa3xx.c b/drivers/clk/pxa/clk-pxa3xx.c index 2d126df2bccd..7aa120c3bd08 100644 --- a/drivers/clk/pxa/clk-pxa3xx.c +++ b/drivers/clk/pxa/clk-pxa3xx.c @@ -286,9 +286,10 @@ static void __init pxa3xx_register_plls(void) clk_register_fixed_rate(NULL, "osc_13mhz", NULL, CLK_GET_RATE_NOCACHE, 13 * MHz); - clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, - CLK_GET_RATE_NOCACHE, - 32768); + clkdev_pxa_register(CLK_OSC32k768, "osc_32_768khz", NULL, + clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, + CLK_GET_RATE_NOCACHE, + 32768)); clk_register_fixed_rate(NULL, "ring_osc_120mhz", NULL, CLK_GET_RATE_NOCACHE, 120 * MHz); diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 9c3480dcc38a..064768699fe7 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -59,6 +59,15 @@ config QCOM_CLK_SMD_RPM Say Y if you want to support the clocks exposed by the RPM on platforms such as apq8016, apq8084, msm8974 etc. +config QCOM_CLK_RPMH + tristate "RPMh Clock Driver" + depends on COMMON_CLK_QCOM && QCOM_RPMH + help + RPMh manages shared resources on some Qualcomm Technologies, Inc. + SoCs. It accepts requests from other hardware subsystems via RSC. + Say Y if you want to support the clocks exposed by RPMh on + platforms such as SDM845. + config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" select QCOM_GDSC @@ -245,6 +254,16 @@ config SDM_VIDEOCC_845 Say Y if you want to support video devices and functionality such as video encode and decode. +config SDM_DISPCC_845 + tristate "SDM845 Display Clock Controller" + select SDM_GCC_845 + depends on COMMON_CLK_QCOM + help + Support for the display clock controller on Qualcomm Technologies, Inc + SDM845 devices. + Say Y if you want to support display devices and functionality such as + splash screen. + config SPMI_PMIC_CLKDIV tristate "SPMI PMIC clkdiv Support" depends on (COMMON_CLK_QCOM && SPMI) || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 762c01137c2f..21a45035930d 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -37,7 +37,9 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o +obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o +obj-$(CONFIG_SDM_DISPCC_845) += dispcc-sdm845.o obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 3c49a60072f1..a91d97cecbad 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/kernel.h> diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index f981b486c468..66755f0f84fc 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -1,15 +1,5 @@ -/* - * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_ALPHA_PLL_H__ #define __QCOM_CLK_ALPHA_PLL_H__ diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index c58c5538b1b6..bc2205c450b6 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/kernel.h> diff --git a/drivers/clk/qcom/clk-branch.h b/drivers/clk/qcom/clk-branch.h index 1702efb1c511..b3561e0a3984 100644 --- a/drivers/clk/qcom/clk-branch.h +++ b/drivers/clk/qcom/clk-branch.h @@ -1,15 +1,5 @@ -/* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_BRANCH_H__ #define __QCOM_CLK_BRANCH_H__ diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index b209a2fe86b9..dbd5a9e83554 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -7,6 +7,8 @@ #include <linux/clk-provider.h> #include "clk-regmap.h" +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + struct freq_tbl { unsigned long freq; u8 src; diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c index 1c856d330733..ce80db27ccf2 100644 --- a/drivers/clk/qcom/clk-regmap.c +++ b/drivers/clk/qcom/clk-regmap.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * 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/device.h> diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h index 90d95cd11ec6..6cfc1bccb255 100644 --- a/drivers/clk/qcom/clk-regmap.h +++ b/drivers/clk/qcom/clk-regmap.h @@ -1,15 +1,5 @@ -/* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_REGMAP_H__ #define __QCOM_CLK_REGMAP_H__ diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c new file mode 100644 index 000000000000..9f4fc7773fb2 --- /dev/null +++ b/drivers/clk/qcom/clk-rpmh.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <soc/qcom/cmd-db.h> +#include <soc/qcom/rpmh.h> + +#include <dt-bindings/clock/qcom,rpmh.h> + +#define CLK_RPMH_ARC_EN_OFFSET 0 +#define CLK_RPMH_VRM_EN_OFFSET 4 + +/** + * struct clk_rpmh - individual rpmh clock data structure + * @hw: handle between common and hardware-specific interfaces + * @res_name: resource name for the rpmh clock + * @div: clock divider to compute the clock rate + * @res_addr: base address of the rpmh resource within the RPMh + * @res_on_val: rpmh clock enable value + * @state: rpmh clock requested state + * @aggr_state: rpmh clock aggregated state + * @last_sent_aggr_state: rpmh clock last aggr state sent to RPMh + * @valid_state_mask: mask to determine the state of the rpmh clock + * @dev: device to which it is attached + * @peer: pointer to the clock rpmh sibling + */ +struct clk_rpmh { + struct clk_hw hw; + const char *res_name; + u8 div; + u32 res_addr; + u32 res_on_val; + u32 state; + u32 aggr_state; + u32 last_sent_aggr_state; + u32 valid_state_mask; + struct device *dev; + struct clk_rpmh *peer; +}; + +struct clk_rpmh_desc { + struct clk_hw **clks; + size_t num_clks; +}; + +static DEFINE_MUTEX(rpmh_clk_lock); + +#define __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \ + _res_en_offset, _res_on, _div) \ + static struct clk_rpmh _platform##_##_name_active; \ + static struct clk_rpmh _platform##_##_name = { \ + .res_name = _res_name, \ + .res_addr = _res_en_offset, \ + .res_on_val = _res_on, \ + .div = _div, \ + .peer = &_platform##_##_name_active, \ + .valid_state_mask = (BIT(RPMH_WAKE_ONLY_STATE) | \ + BIT(RPMH_ACTIVE_ONLY_STATE) | \ + BIT(RPMH_SLEEP_STATE)), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpmh_ops, \ + .name = #_name, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + }; \ + static struct clk_rpmh _platform##_##_name_active = { \ + .res_name = _res_name, \ + .res_addr = _res_en_offset, \ + .res_on_val = _res_on, \ + .div = _div, \ + .peer = &_platform##_##_name, \ + .valid_state_mask = (BIT(RPMH_WAKE_ONLY_STATE) | \ + BIT(RPMH_ACTIVE_ONLY_STATE)), \ + .hw.init = &(struct clk_init_data){ \ + .ops = &clk_rpmh_ops, \ + .name = #_name_active, \ + .parent_names = (const char *[]){ "xo_board" }, \ + .num_parents = 1, \ + }, \ + } + +#define DEFINE_CLK_RPMH_ARC(_platform, _name, _name_active, _res_name, \ + _res_on, _div) \ + __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \ + CLK_RPMH_ARC_EN_OFFSET, _res_on, _div) + +#define DEFINE_CLK_RPMH_VRM(_platform, _name, _name_active, _res_name, \ + _div) \ + __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name, \ + CLK_RPMH_VRM_EN_OFFSET, 1, _div) + +static inline struct clk_rpmh *to_clk_rpmh(struct clk_hw *_hw) +{ + return container_of(_hw, struct clk_rpmh, hw); +} + +static inline bool has_state_changed(struct clk_rpmh *c, u32 state) +{ + return (c->last_sent_aggr_state & BIT(state)) + != (c->aggr_state & BIT(state)); +} + +static int clk_rpmh_send_aggregate_command(struct clk_rpmh *c) +{ + struct tcs_cmd cmd = { 0 }; + u32 cmd_state, on_val; + enum rpmh_state state = RPMH_SLEEP_STATE; + int ret; + + cmd.addr = c->res_addr; + cmd_state = c->aggr_state; + on_val = c->res_on_val; + + for (; state <= RPMH_ACTIVE_ONLY_STATE; state++) { + if (has_state_changed(c, state)) { + if (cmd_state & BIT(state)) + cmd.data = on_val; + + ret = rpmh_write_async(c->dev, state, &cmd, 1); + if (ret) { + dev_err(c->dev, "set %s state of %s failed: (%d)\n", + !state ? "sleep" : + state == RPMH_WAKE_ONLY_STATE ? + "wake" : "active", c->res_name, ret); + return ret; + } + } + } + + c->last_sent_aggr_state = c->aggr_state; + c->peer->last_sent_aggr_state = c->last_sent_aggr_state; + + return 0; +} + +/* + * Update state and aggregate state values based on enable value. + */ +static int clk_rpmh_aggregate_state_send_command(struct clk_rpmh *c, + bool enable) +{ + int ret; + + /* Nothing required to be done if already off or on */ + if (enable == c->state) + return 0; + + c->state = enable ? c->valid_state_mask : 0; + c->aggr_state = c->state | c->peer->state; + c->peer->aggr_state = c->aggr_state; + + ret = clk_rpmh_send_aggregate_command(c); + if (!ret) + return 0; + + if (ret && enable) + c->state = 0; + else if (ret) + c->state = c->valid_state_mask; + + WARN(1, "clk: %s failed to %s\n", c->res_name, + enable ? "enable" : "disable"); + return ret; +} + +static int clk_rpmh_prepare(struct clk_hw *hw) +{ + struct clk_rpmh *c = to_clk_rpmh(hw); + int ret = 0; + + mutex_lock(&rpmh_clk_lock); + ret = clk_rpmh_aggregate_state_send_command(c, true); + mutex_unlock(&rpmh_clk_lock); + + return ret; +}; + +static void clk_rpmh_unprepare(struct clk_hw *hw) +{ + struct clk_rpmh *c = to_clk_rpmh(hw); + + mutex_lock(&rpmh_clk_lock); + clk_rpmh_aggregate_state_send_command(c, false); + mutex_unlock(&rpmh_clk_lock); +}; + +static unsigned long clk_rpmh_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct clk_rpmh *r = to_clk_rpmh(hw); + + /* + * RPMh clocks have a fixed rate. Return static rate. + */ + return prate / r->div; +} + +static const struct clk_ops clk_rpmh_ops = { + .prepare = clk_rpmh_prepare, + .unprepare = clk_rpmh_unprepare, + .recalc_rate = clk_rpmh_recalc_rate, +}; + +/* Resource name must match resource id present in cmd-db. */ +DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2); +DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2); +DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2); +DEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", 1); +DEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", 1); +DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", 1); + +static struct clk_hw *sdm845_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw, + [RPMH_CXO_CLK_A] = &sdm845_bi_tcxo_ao.hw, + [RPMH_LN_BB_CLK2] = &sdm845_ln_bb_clk2.hw, + [RPMH_LN_BB_CLK2_A] = &sdm845_ln_bb_clk2_ao.hw, + [RPMH_LN_BB_CLK3] = &sdm845_ln_bb_clk3.hw, + [RPMH_LN_BB_CLK3_A] = &sdm845_ln_bb_clk3_ao.hw, + [RPMH_RF_CLK1] = &sdm845_rf_clk1.hw, + [RPMH_RF_CLK1_A] = &sdm845_rf_clk1_ao.hw, + [RPMH_RF_CLK2] = &sdm845_rf_clk2.hw, + [RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw, + [RPMH_RF_CLK3] = &sdm845_rf_clk3.hw, + [RPMH_RF_CLK3_A] = &sdm845_rf_clk3_ao.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_sdm845 = { + .clks = sdm845_rpmh_clocks, + .num_clks = ARRAY_SIZE(sdm845_rpmh_clocks), +}; + +static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_rpmh_desc *rpmh = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= rpmh->num_clks) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return rpmh->clks[idx]; +} + +static int clk_rpmh_probe(struct platform_device *pdev) +{ + struct clk_hw **hw_clks; + struct clk_rpmh *rpmh_clk; + const struct clk_rpmh_desc *desc; + int ret, i; + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -ENODEV; + + hw_clks = desc->clks; + + for (i = 0; i < desc->num_clks; i++) { + u32 res_addr; + + rpmh_clk = to_clk_rpmh(hw_clks[i]); + res_addr = cmd_db_read_addr(rpmh_clk->res_name); + if (!res_addr) { + dev_err(&pdev->dev, "missing RPMh resource address for %s\n", + rpmh_clk->res_name); + return -ENODEV; + } + rpmh_clk->res_addr += res_addr; + rpmh_clk->dev = &pdev->dev; + + ret = devm_clk_hw_register(&pdev->dev, hw_clks[i]); + if (ret) { + dev_err(&pdev->dev, "failed to register %s\n", + hw_clks[i]->init->name); + return ret; + } + } + + /* typecast to silence compiler warning */ + ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_rpmh_hw_get, + (void *)desc); + if (ret) { + dev_err(&pdev->dev, "Failed to add clock provider\n"); + return ret; + } + + dev_dbg(&pdev->dev, "Registered RPMh clocks\n"); + + return 0; +} + +static const struct of_device_id clk_rpmh_match_table[] = { + { .compatible = "qcom,sdm845-rpmh-clk", .data = &clk_rpmh_sdm845}, + { } +}; +MODULE_DEVICE_TABLE(of, clk_rpmh_match_table); + +static struct platform_driver clk_rpmh_driver = { + .probe = clk_rpmh_probe, + .driver = { + .name = "clk-rpmh", + .of_match_table = clk_rpmh_match_table, + }, +}; + +static int __init clk_rpmh_init(void) +{ + return platform_driver_register(&clk_rpmh_driver); +} +subsys_initcall(clk_rpmh_init); + +static void __exit clk_rpmh_exit(void) +{ + platform_driver_unregister(&clk_rpmh_driver); +} +module_exit(clk_rpmh_exit); + +MODULE_DESCRIPTION("QCOM RPMh Clock Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 39ce64c2783b..db9b2471ac40 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * 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/export.h> diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 00196ee15e73..4aa33ee70bae 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -1,15 +1,6 @@ -/* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * 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. - */ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. */ + #ifndef __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__ diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c new file mode 100644 index 000000000000..0cc4909b5dbe --- /dev/null +++ b/drivers/clk/qcom/dispcc-sdm845.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/clock/qcom,dispcc-sdm845.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap-divider.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_DISP_CC_PLL0_OUT_MAIN, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_DSI1_PHY_PLL_OUT_BYTECLK, + P_DSI1_PHY_PLL_OUT_DSICLK, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, +}; + +static const struct parent_map disp_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 }, + { P_DSI1_PHY_PLL_OUT_BYTECLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_0[] = { + "bi_tcxo", + "dsi0_phy_pll_out_byteclk", + "dsi1_phy_pll_out_byteclk", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_2[] = { + "bi_tcxo", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, + { P_GPLL0_OUT_MAIN, 4 }, + { P_GPLL0_OUT_MAIN_DIV, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_3[] = { + "bi_tcxo", + "disp_cc_pll0", + "gcc_disp_gpll0_clk_src", + "gcc_disp_gpll0_div_clk_src", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_DSI1_PHY_PLL_OUT_DSICLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_4[] = { + "bi_tcxo", + "dsi0_phy_pll_out_dsiclk", + "dsi1_phy_pll_out_dsiclk", + "core_bi_pll_test_se", +}; + +static struct clk_alpha_pll disp_cc_pll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_pll0", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { + .cmd_rcgr = 0x20d0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { + .cmd_rcgr = 0x20ec, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_byte2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_esc0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { + .cmd_rcgr = 0x2108, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = { + .cmd_rcgr = 0x2120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc1_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(85714286, P_GPLL0_OUT_MAIN, 7, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(171428571, P_GPLL0_OUT_MAIN, 3.5, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(344000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(430000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { + .cmd_rcgr = 0x2088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_clk_src", + .parent_names = disp_cc_parent_names_3, + .num_parents = 5, + .ops = &clk_rcg2_shared_ops, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { + .cmd_rcgr = 0x2058, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk0_clk_src", + .parent_names = disp_cc_parent_names_4, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_pixel_ops, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { + .cmd_rcgr = 0x2070, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk1_clk_src", + .parent_names = disp_cc_parent_names_4, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_pixel_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_rot_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(171428571, P_GPLL0_OUT_MAIN, 3.5, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + F(344000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + F(430000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_rot_clk_src = { + .cmd_rcgr = 0x20a0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_rot_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rot_clk_src", + .parent_names = disp_cc_parent_names_3, + .num_parents = 5, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { + .cmd_rcgr = 0x20b8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk_src", + .parent_names = disp_cc_parent_names_2, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch disp_cc_mdss_ahb_clk = { + .halt_reg = 0x4004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_axi_clk = { + .halt_reg = 0x4008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_branch disp_cc_mdss_byte0_clk = { + .halt_reg = 0x2028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { + .reg = 0x20e8, + .shift = 0, + .width = 2, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_div_clk_src", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte0_clk_src", + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_branch disp_cc_mdss_byte0_intf_clk = { + .halt_reg = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte0_div_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_branch disp_cc_mdss_byte1_clk = { + .halt_reg = 0x2030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { + .reg = 0x2104, + .shift = 0, + .width = 2, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_div_clk_src", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte1_clk_src", + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_branch disp_cc_mdss_byte1_intf_clk = { + .halt_reg = 0x2034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte1_div_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc0_clk = { + .halt_reg = 0x2038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_esc0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc1_clk = { + .halt_reg = 0x203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x203c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_esc1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_clk = { + .halt_reg = 0x200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x200c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_mdp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut_clk = { + .halt_reg = 0x201c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x201c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_lut_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_mdp_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_branch disp_cc_mdss_pclk0_clk = { + .halt_reg = 0x2004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk0_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_pclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* Return the HW recalc rate for idle use case */ +static struct clk_branch disp_cc_mdss_pclk1_clk = { + .halt_reg = 0x2008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_pclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rot_clk = { + .halt_reg = 0x2014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rot_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_rot_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_ahb_clk = { + .halt_reg = 0x5004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rscc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_vsync_clk = { + .halt_reg = 0x5008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x5008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rscc_vsync_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_vsync_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_vsync_clk = { + .halt_reg = 0x2024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_vsync_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x3000, + .pd = { + .name = "mdss_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *disp_cc_sdm845_clocks[] = { + [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr, + [DISP_CC_MDSS_AXI_CLK] = &disp_cc_mdss_axi_clk.clkr, + [DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr, + [DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr, + [DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = + &disp_cc_mdss_byte0_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_CLK] = &disp_cc_mdss_byte1_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK_SRC] = &disp_cc_mdss_byte1_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_INTF_CLK] = &disp_cc_mdss_byte1_intf_clk.clkr, + [DISP_CC_MDSS_BYTE1_DIV_CLK_SRC] = + &disp_cc_mdss_byte1_div_clk_src.clkr, + [DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr, + [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr, + [DISP_CC_MDSS_ESC1_CLK] = &disp_cc_mdss_esc1_clk.clkr, + [DISP_CC_MDSS_ESC1_CLK_SRC] = &disp_cc_mdss_esc1_clk_src.clkr, + [DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr, + [DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr, + [DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr, + [DISP_CC_MDSS_PCLK1_CLK] = &disp_cc_mdss_pclk1_clk.clkr, + [DISP_CC_MDSS_PCLK1_CLK_SRC] = &disp_cc_mdss_pclk1_clk_src.clkr, + [DISP_CC_MDSS_ROT_CLK] = &disp_cc_mdss_rot_clk.clkr, + [DISP_CC_MDSS_ROT_CLK_SRC] = &disp_cc_mdss_rot_clk_src.clkr, + [DISP_CC_MDSS_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr, + [DISP_CC_MDSS_RSCC_VSYNC_CLK] = &disp_cc_mdss_rscc_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr, + [DISP_CC_PLL0] = &disp_cc_pll0.clkr, +}; + +static const struct qcom_reset_map disp_cc_sdm845_resets[] = { + [DISP_CC_MDSS_RSCC_BCR] = { 0x5000 }, +}; + +static struct gdsc *disp_cc_sdm845_gdscs[] = { + [MDSS_GDSC] = &mdss_gdsc, +}; + +static const struct regmap_config disp_cc_sdm845_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static const struct qcom_cc_desc disp_cc_sdm845_desc = { + .config = &disp_cc_sdm845_regmap_config, + .clks = disp_cc_sdm845_clocks, + .num_clks = ARRAY_SIZE(disp_cc_sdm845_clocks), + .resets = disp_cc_sdm845_resets, + .num_resets = ARRAY_SIZE(disp_cc_sdm845_resets), + .gdscs = disp_cc_sdm845_gdscs, + .num_gdscs = ARRAY_SIZE(disp_cc_sdm845_gdscs), +}; + +static const struct of_device_id disp_cc_sdm845_match_table[] = { + { .compatible = "qcom,sdm845-dispcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, disp_cc_sdm845_match_table); + +static int disp_cc_sdm845_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct alpha_pll_config disp_cc_pll0_config = {}; + + regmap = qcom_cc_map(pdev, &disp_cc_sdm845_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + disp_cc_pll0_config.l = 0x2c; + disp_cc_pll0_config.alpha = 0xcaaa; + + clk_fabia_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config); + + /* Enable hardware clock gating for DSI and MDP clocks */ + regmap_update_bits(regmap, 0x8000, 0x7f0, 0x7f0); + + return qcom_cc_really_probe(pdev, &disp_cc_sdm845_desc, regmap); +} + +static struct platform_driver disp_cc_sdm845_driver = { + .probe = disp_cc_sdm845_probe, + .driver = { + .name = "disp_cc-sdm845", + .of_match_table = disp_cc_sdm845_match_table, + }, +}; + +static int __init disp_cc_sdm845_init(void) +{ + return platform_driver_register(&disp_cc_sdm845_driver); +} +subsys_initcall(disp_cc_sdm845_init); + +static void __exit disp_cc_sdm845_exit(void) +{ + platform_driver_unregister(&disp_cc_sdm845_driver); +} +module_exit(disp_cc_sdm845_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("QTI DISPCC SDM845 Driver"); diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c index 486d9610355c..9c99a71ea71e 100644 --- a/drivers/clk/qcom/gcc-apq8084.c +++ b/drivers/clk/qcom/gcc-apq8084.c @@ -106,8 +106,6 @@ static const char * const gcc_xo_pcie_sleep[] = { "sleep_clk_src", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static struct clk_pll gpll0 = { .l_reg = 0x0004, .m_reg = 0x0008, diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c index 46cb256b4aa2..8902ad42ba87 100644 --- a/drivers/clk/qcom/gcc-ipq4019.c +++ b/drivers/clk/qcom/gcc-ipq4019.c @@ -179,8 +179,6 @@ static const char * const gcc_xo_ddr_500_200[] = { "ddrpllapss", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static const struct freq_tbl ftbl_gcc_audio_pwm_clk[] = { F(48000000, P_XO, 1, 0, 0), F(200000000, P_FEPLL200, 1, 0, 0), diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index 28eb200d0f1e..5f61225657ab 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -1220,7 +1220,6 @@ static struct clk_rcg sdc1_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1269,7 +1268,6 @@ static struct clk_rcg sdc3_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1353,7 +1351,6 @@ static struct clk_rcg tsif_ref_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index 0462f4a8c932..505c6263141d 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -32,8 +32,6 @@ #include "clk-regmap-mux.h" #include "reset.h" -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - enum { P_XO, P_GPLL0, diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c index b99dd406e907..849046fbed6d 100644 --- a/drivers/clk/qcom/gcc-mdm9615.c +++ b/drivers/clk/qcom/gcc-mdm9615.c @@ -947,7 +947,6 @@ static struct clk_rcg sdc1_src = { .parent_names = gcc_cxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -996,7 +995,6 @@ static struct clk_rcg sdc2_src = { .parent_names = gcc_cxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c index c347a0d44bc8..7e930e25c79f 100644 --- a/drivers/clk/qcom/gcc-msm8660.c +++ b/drivers/clk/qcom/gcc-msm8660.c @@ -1558,7 +1558,6 @@ static struct clk_rcg sdc1_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1607,7 +1606,6 @@ static struct clk_rcg sdc2_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1656,7 +1654,6 @@ static struct clk_rcg sdc3_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1705,7 +1702,6 @@ static struct clk_rcg sdc4_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1754,7 +1750,6 @@ static struct clk_rcg sdc5_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index d6c7f50ba86a..ac2b0aa1e8b5 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -264,8 +264,6 @@ static const char * const gcc_xo_gpll1_emclk_sleep[] = { "sleep_clk", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static struct clk_pll gpll0 = { .l_reg = 0x21004, .m_reg = 0x21008, diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index eb551c75fba6..fd495e0471bb 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -1628,7 +1628,6 @@ static struct clk_rcg sdc1_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1677,7 +1676,6 @@ static struct clk_rcg sdc2_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1726,7 +1724,6 @@ static struct clk_rcg sdc3_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1775,7 +1772,6 @@ static struct clk_rcg sdc4_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; @@ -1824,7 +1820,6 @@ static struct clk_rcg sdc5_src = { .parent_names = gcc_pxo_pll8, .num_parents = 2, .ops = &clk_rcg_ops, - .flags = CLK_SET_RATE_GATE, }, } }; diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c index 348e30da4f18..08e2900d172c 100644 --- a/drivers/clk/qcom/gcc-msm8974.c +++ b/drivers/clk/qcom/gcc-msm8974.c @@ -62,8 +62,6 @@ static const char * const gcc_xo_gpll0_gpll4[] = { "gpll4_vote", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static struct clk_pll gpll0 = { .l_reg = 0x0004, .m_reg = 0x0008, diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c index 1e38efc37180..53f0f369a33e 100644 --- a/drivers/clk/qcom/gcc-msm8994.c +++ b/drivers/clk/qcom/gcc-msm8994.c @@ -57,8 +57,6 @@ static const char * const gcc_xo_gpll0_gpll4[] = { "gpll4", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static struct clk_fixed_factor xo = { .mult = 1, .div = 1, diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index ff8d66fd94e6..9a3290fdd01b 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -32,8 +32,6 @@ #include "reset.h" #include "gdsc.h" -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - enum { P_XO, P_GPLL0, diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 78d87f5c7098..9f0ae403d5f5 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -25,8 +25,6 @@ #include "reset.h" #include "gdsc.h" -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - enum { P_AUD_REF_CLK, P_CORE_BI_PLL_TEST_SE, diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index e78e6f5b99fc..fa1a196350f1 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -25,8 +25,6 @@ #include "gdsc.h" #include "reset.h" -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - enum { P_BI_TCXO, P_AUD_REF_CLK, @@ -1103,6 +1101,7 @@ static struct clk_branch gcc_camera_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_camera_ahb_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -1129,6 +1128,7 @@ static struct clk_branch gcc_camera_xo_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_camera_xo_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -1270,6 +1270,7 @@ static struct clk_branch gcc_disp_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_disp_ahb_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -1328,6 +1329,7 @@ static struct clk_branch gcc_disp_xo_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_disp_xo_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -1397,6 +1399,7 @@ static struct clk_branch gcc_gpu_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_gpu_cfg_ahb_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -2985,6 +2988,7 @@ static struct clk_branch gcc_video_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_video_ahb_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -3011,6 +3015,7 @@ static struct clk_branch gcc_video_xo_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_video_xo_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -3049,6 +3054,36 @@ static struct clk_branch gcc_vs_ctrl_clk = { }, }; +static struct clk_branch gcc_cpuss_dvm_bus_clk = { + .halt_reg = 0x48190, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x48190, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_dvm_bus_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cpuss_gnoc_clk = { + .halt_reg = 0x48004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x48004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52004, + .enable_mask = BIT(22), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_gnoc_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct gdsc pcie_0_gdsc = { .gdscr = 0x6b004, .pd = { @@ -3344,6 +3379,8 @@ static struct clk_regmap *gcc_sdm845_clocks[] = { [GPLL0] = &gpll0.clkr, [GPLL0_OUT_EVEN] = &gpll0_out_even.clkr, [GPLL4] = &gpll4.clkr, + [GCC_CPUSS_DVM_BUS_CLK] = &gcc_cpuss_dvm_bus_clk.clkr, + [GCC_CPUSS_GNOC_CLK] = &gcc_cpuss_gnoc_clk.clkr, }; static const struct qcom_reset_map gcc_sdm845_resets[] = { @@ -3433,10 +3470,6 @@ static int gcc_sdm845_probe(struct platform_device *pdev) regmap_update_bits(regmap, 0x09ffc, 0x3, 0x3); regmap_update_bits(regmap, 0x71028, 0x3, 0x3); - /* Enable CPUSS clocks */ - regmap_update_bits(regmap, 0x48190, BIT(0), 0x1); - regmap_update_bits(regmap, 0x52004, BIT(22), 0x1); - return qcom_cc_really_probe(pdev, &gcc_sdm845_desc, regmap); } diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index 30777f9f1a43..4ce1d7c88377 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -219,8 +219,6 @@ static const char * const mmcc_xo_mmpll0_1_4_gpll1_0_sleep[] = { "sleep_clk_src", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static struct clk_pll mmpll0 = { .l_reg = 0x0004, .m_reg = 0x0008, diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index 715e7cd94125..91818516c3e0 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -184,8 +184,6 @@ static const char * const mmcc_xo_dsibyte_hdmi_edp_gpll0[] = { "dsi1pllbyte", }; -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - static struct clk_pll mmpll0 = { .l_reg = 0x0004, .m_reg = 0x0008, diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index 4b20d1b67a1b..7d4ee109435c 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -34,8 +34,6 @@ #include "reset.h" #include "gdsc.h" -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - enum { P_XO, P_MMPLL0, diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c index 9073b7a710ac..5d6a7724a194 100644 --- a/drivers/clk/qcom/videocc-sdm845.c +++ b/drivers/clk/qcom/videocc-sdm845.c @@ -18,8 +18,6 @@ #include "clk-pll.h" #include "gdsc.h" -#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } - enum { P_BI_TCXO, P_CORE_BI_PLL_TEST_SE, diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index f9ba71311727..9022bbe1297e 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -21,6 +21,7 @@ config CLK_RENESAS select CLK_R8A77980 if ARCH_R8A77980 select CLK_R8A77990 if ARCH_R8A77990 select CLK_R8A77995 if ARCH_R8A77995 + select CLK_R9A06G032 if ARCH_R9A06G032 select CLK_SH73A0 if ARCH_SH73A0 if CLK_RENESAS @@ -125,6 +126,11 @@ config CLK_R8A77995 bool "R-Car D3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG +config CLK_R9A06G032 + bool "Renesas R9A06G032 clock driver" + help + This is a driver for R9A06G032 clocks + config CLK_SH73A0 bool "SH-Mobile AG5 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index fe5bac9215e5..e4aa3d6143d2 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_CLK_R8A77970) += r8a77970-cpg-mssr.o obj-$(CONFIG_CLK_R8A77980) += r8a77980-cpg-mssr.o obj-$(CONFIG_CLK_R8A77990) += r8a77990-cpg-mssr.o obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o +obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o # Family diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index 775b0ceaa337..a85dd50e8911 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -103,6 +103,7 @@ static struct cpg_core_clk r8a7795_core_clks[] __initdata = { DEF_GEN3_SD("sd3", R8A7795_CLK_SD3, CLK_SDSRC, 0x26c), DEF_FIXED("cl", R8A7795_CLK_CL, CLK_PLL1_DIV2, 48, 1), + DEF_FIXED("cr", R8A7795_CLK_CR, CLK_PLL1_DIV4, 2, 1), DEF_FIXED("cp", R8A7795_CLK_CP, CLK_EXTAL, 2, 1), DEF_DIV6P1("canfd", R8A7795_CLK_CANFD, CLK_PLL1_DIV4, 0x244), @@ -132,6 +133,7 @@ static struct mssr_mod_clk r8a7795_mod_clks[] __initdata = { DEF_MOD("sys-dmac2", 217, R8A7795_CLK_S0D3), DEF_MOD("sys-dmac1", 218, R8A7795_CLK_S0D3), DEF_MOD("sys-dmac0", 219, R8A7795_CLK_S0D3), + DEF_MOD("sceg-pub", 229, R8A7795_CLK_CR), DEF_MOD("cmt3", 300, R8A7795_CLK_R), DEF_MOD("cmt2", 301, R8A7795_CLK_R), DEF_MOD("cmt1", 302, R8A7795_CLK_R), diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c new file mode 100644 index 000000000000..a0b6ecdc63dd --- /dev/null +++ b/drivers/clk/renesas/r9a06g032-clocks.c @@ -0,0 +1,893 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R9A09G032 clock driver + * + * Copyright (C) 2018 Renesas Electronics Europe Limited + * + * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com> + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <dt-bindings/clock/r9a06g032-sysctrl.h> + +struct r9a06g032_gate { + u16 gate, reset, ready, midle, + scon, mirack, mistat; +}; + +/* This is used to describe a clock for instantiation */ +struct r9a06g032_clkdesc { + const char *name; + uint32_t type: 3; + uint32_t index: 8; + uint32_t source : 8; /* source index + 1 (0 == none) */ + /* these are used to populate the bitsel struct */ + union { + struct r9a06g032_gate gate; + /* for dividers */ + struct { + unsigned int div_min : 10, div_max : 10, reg: 10; + u16 div_table[4]; + }; + /* For fixed-factor ones */ + struct { + u16 div, mul; + }; + unsigned int factor; + unsigned int frequency; + /* for dual gate */ + struct { + uint16_t group : 1, index: 3; + u16 sel, g1, r1, g2, r2; + } dual; + }; +} __packed; + +#define I_GATE(_clk, _rst, _rdy, _midle, _scon, _mirack, _mistat) \ + { .gate = _clk, .reset = _rst, \ + .ready = _rdy, .midle = _midle, \ + .scon = _scon, .mirack = _mirack, .mistat = _mistat } +#define D_GATE(_idx, _n, _src, ...) \ + { .type = K_GATE, .index = R9A06G032_##_idx, \ + .source = 1 + R9A06G032_##_src, .name = _n, \ + .gate = I_GATE(__VA_ARGS__), } +#define D_ROOT(_idx, _n, _mul, _div) \ + { .type = K_FFC, .index = R9A06G032_##_idx, .name = _n, \ + .div = _div, .mul = _mul } +#define D_FFC(_idx, _n, _src, _div) \ + { .type = K_FFC, .index = R9A06G032_##_idx, \ + .source = 1 + R9A06G032_##_src, .name = _n, \ + .div = _div, .mul = 1} +#define D_DIV(_idx, _n, _src, _reg, _min, _max, ...) \ + { .type = K_DIV, .index = R9A06G032_##_idx, \ + .source = 1 + R9A06G032_##_src, .name = _n, \ + .reg = _reg, .div_min = _min, .div_max = _max, \ + .div_table = { __VA_ARGS__ } } +#define D_UGATE(_idx, _n, _src, _g, _gi, _g1, _r1, _g2, _r2) \ + { .type = K_DUALGATE, .index = R9A06G032_##_idx, \ + .source = 1 + R9A06G032_##_src, .name = _n, \ + .dual = { .group = _g, .index = _gi, \ + .g1 = _g1, .r1 = _r1, .g2 = _g2, .r2 = _r2 }, } + +enum { K_GATE = 0, K_FFC, K_DIV, K_BITSEL, K_DUALGATE }; + +/* Internal clock IDs */ +#define R9A06G032_CLKOUT 0 +#define R9A06G032_CLKOUT_D10 2 +#define R9A06G032_CLKOUT_D16 3 +#define R9A06G032_CLKOUT_D160 4 +#define R9A06G032_CLKOUT_D1OR2 5 +#define R9A06G032_CLKOUT_D20 6 +#define R9A06G032_CLKOUT_D40 7 +#define R9A06G032_CLKOUT_D5 8 +#define R9A06G032_CLKOUT_D8 9 +#define R9A06G032_DIV_ADC 10 +#define R9A06G032_DIV_I2C 11 +#define R9A06G032_DIV_NAND 12 +#define R9A06G032_DIV_P1_PG 13 +#define R9A06G032_DIV_P2_PG 14 +#define R9A06G032_DIV_P3_PG 15 +#define R9A06G032_DIV_P4_PG 16 +#define R9A06G032_DIV_P5_PG 17 +#define R9A06G032_DIV_P6_PG 18 +#define R9A06G032_DIV_QSPI0 19 +#define R9A06G032_DIV_QSPI1 20 +#define R9A06G032_DIV_REF_SYNC 21 +#define R9A06G032_DIV_SDIO0 22 +#define R9A06G032_DIV_SDIO1 23 +#define R9A06G032_DIV_SWITCH 24 +#define R9A06G032_DIV_UART 25 +#define R9A06G032_DIV_MOTOR 64 +#define R9A06G032_CLK_DDRPHY_PLLCLK_D4 78 +#define R9A06G032_CLK_ECAT100_D4 79 +#define R9A06G032_CLK_HSR100_D2 80 +#define R9A06G032_CLK_REF_SYNC_D4 81 +#define R9A06G032_CLK_REF_SYNC_D8 82 +#define R9A06G032_CLK_SERCOS100_D2 83 +#define R9A06G032_DIV_CA7 84 + +#define R9A06G032_UART_GROUP_012 154 +#define R9A06G032_UART_GROUP_34567 155 + +#define R9A06G032_CLOCK_COUNT (R9A06G032_UART_GROUP_34567 + 1) + +static const struct r9a06g032_clkdesc r9a06g032_clocks[] __initconst = { + D_ROOT(CLKOUT, "clkout", 25, 1), + D_ROOT(CLK_PLL_USB, "clk_pll_usb", 12, 10), + D_FFC(CLKOUT_D10, "clkout_d10", CLKOUT, 10), + D_FFC(CLKOUT_D16, "clkout_d16", CLKOUT, 16), + D_FFC(CLKOUT_D160, "clkout_d160", CLKOUT, 160), + D_DIV(CLKOUT_D1OR2, "clkout_d1or2", CLKOUT, 0, 1, 2), + D_FFC(CLKOUT_D20, "clkout_d20", CLKOUT, 20), + D_FFC(CLKOUT_D40, "clkout_d40", CLKOUT, 40), + D_FFC(CLKOUT_D5, "clkout_d5", CLKOUT, 5), + D_FFC(CLKOUT_D8, "clkout_d8", CLKOUT, 8), + D_DIV(DIV_ADC, "div_adc", CLKOUT, 77, 50, 250), + D_DIV(DIV_I2C, "div_i2c", CLKOUT, 78, 12, 16), + D_DIV(DIV_NAND, "div_nand", CLKOUT, 82, 12, 32), + D_DIV(DIV_P1_PG, "div_p1_pg", CLKOUT, 68, 12, 200), + D_DIV(DIV_P2_PG, "div_p2_pg", CLKOUT, 62, 12, 128), + D_DIV(DIV_P3_PG, "div_p3_pg", CLKOUT, 64, 8, 128), + D_DIV(DIV_P4_PG, "div_p4_pg", CLKOUT, 66, 8, 128), + D_DIV(DIV_P5_PG, "div_p5_pg", CLKOUT, 71, 10, 40), + D_DIV(DIV_P6_PG, "div_p6_pg", CLKOUT, 18, 12, 64), + D_DIV(DIV_QSPI0, "div_qspi0", CLKOUT, 73, 3, 7), + D_DIV(DIV_QSPI1, "div_qspi1", CLKOUT, 25, 3, 7), + D_DIV(DIV_REF_SYNC, "div_ref_sync", CLKOUT, 56, 2, 16, 2, 4, 8, 16), + D_DIV(DIV_SDIO0, "div_sdio0", CLKOUT, 74, 20, 128), + D_DIV(DIV_SDIO1, "div_sdio1", CLKOUT, 75, 20, 128), + D_DIV(DIV_SWITCH, "div_switch", CLKOUT, 37, 5, 40), + D_DIV(DIV_UART, "div_uart", CLKOUT, 79, 12, 128), + D_GATE(CLK_25_PG4, "clk_25_pg4", CLKOUT_D40, 0x749, 0x74a, 0x74b, 0, 0xae3, 0, 0), + D_GATE(CLK_25_PG5, "clk_25_pg5", CLKOUT_D40, 0x74c, 0x74d, 0x74e, 0, 0xae4, 0, 0), + D_GATE(CLK_25_PG6, "clk_25_pg6", CLKOUT_D40, 0x74f, 0x750, 0x751, 0, 0xae5, 0, 0), + D_GATE(CLK_25_PG7, "clk_25_pg7", CLKOUT_D40, 0x752, 0x753, 0x754, 0, 0xae6, 0, 0), + D_GATE(CLK_25_PG8, "clk_25_pg8", CLKOUT_D40, 0x755, 0x756, 0x757, 0, 0xae7, 0, 0), + D_GATE(CLK_ADC, "clk_adc", DIV_ADC, 0x1ea, 0x1eb, 0, 0, 0, 0, 0), + D_GATE(CLK_ECAT100, "clk_ecat100", CLKOUT_D10, 0x405, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_HSR100, "clk_hsr100", CLKOUT_D10, 0x483, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_I2C0, "clk_i2c0", DIV_I2C, 0x1e6, 0x1e7, 0, 0, 0, 0, 0), + D_GATE(CLK_I2C1, "clk_i2c1", DIV_I2C, 0x1e8, 0x1e9, 0, 0, 0, 0, 0), + D_GATE(CLK_MII_REF, "clk_mii_ref", CLKOUT_D40, 0x342, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_NAND, "clk_nand", DIV_NAND, 0x284, 0x285, 0, 0, 0, 0, 0), + D_GATE(CLK_NOUSBP2_PG6, "clk_nousbp2_pg6", DIV_P2_PG, 0x774, 0x775, 0, 0, 0, 0, 0), + D_GATE(CLK_P1_PG2, "clk_p1_pg2", DIV_P1_PG, 0x862, 0x863, 0, 0, 0, 0, 0), + D_GATE(CLK_P1_PG3, "clk_p1_pg3", DIV_P1_PG, 0x864, 0x865, 0, 0, 0, 0, 0), + D_GATE(CLK_P1_PG4, "clk_p1_pg4", DIV_P1_PG, 0x866, 0x867, 0, 0, 0, 0, 0), + D_GATE(CLK_P4_PG3, "clk_p4_pg3", DIV_P4_PG, 0x824, 0x825, 0, 0, 0, 0, 0), + D_GATE(CLK_P4_PG4, "clk_p4_pg4", DIV_P4_PG, 0x826, 0x827, 0, 0, 0, 0, 0), + D_GATE(CLK_P6_PG1, "clk_p6_pg1", DIV_P6_PG, 0x8a0, 0x8a1, 0x8a2, 0, 0xb60, 0, 0), + D_GATE(CLK_P6_PG2, "clk_p6_pg2", DIV_P6_PG, 0x8a3, 0x8a4, 0x8a5, 0, 0xb61, 0, 0), + D_GATE(CLK_P6_PG3, "clk_p6_pg3", DIV_P6_PG, 0x8a6, 0x8a7, 0x8a8, 0, 0xb62, 0, 0), + D_GATE(CLK_P6_PG4, "clk_p6_pg4", DIV_P6_PG, 0x8a9, 0x8aa, 0x8ab, 0, 0xb63, 0, 0), + D_GATE(CLK_QSPI0, "clk_qspi0", DIV_QSPI0, 0x2a4, 0x2a5, 0, 0, 0, 0, 0), + D_GATE(CLK_QSPI1, "clk_qspi1", DIV_QSPI1, 0x484, 0x485, 0, 0, 0, 0, 0), + D_GATE(CLK_RGMII_REF, "clk_rgmii_ref", CLKOUT_D8, 0x340, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_RMII_REF, "clk_rmii_ref", CLKOUT_D20, 0x341, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_SDIO0, "clk_sdio0", DIV_SDIO0, 0x64, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_SDIO1, "clk_sdio1", DIV_SDIO1, 0x644, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_SERCOS100, "clk_sercos100", CLKOUT_D10, 0x425, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_SLCD, "clk_slcd", DIV_P1_PG, 0x860, 0x861, 0, 0, 0, 0, 0), + D_GATE(CLK_SPI0, "clk_spi0", DIV_P3_PG, 0x7e0, 0x7e1, 0, 0, 0, 0, 0), + D_GATE(CLK_SPI1, "clk_spi1", DIV_P3_PG, 0x7e2, 0x7e3, 0, 0, 0, 0, 0), + D_GATE(CLK_SPI2, "clk_spi2", DIV_P3_PG, 0x7e4, 0x7e5, 0, 0, 0, 0, 0), + D_GATE(CLK_SPI3, "clk_spi3", DIV_P3_PG, 0x7e6, 0x7e7, 0, 0, 0, 0, 0), + D_GATE(CLK_SPI4, "clk_spi4", DIV_P4_PG, 0x820, 0x821, 0, 0, 0, 0, 0), + D_GATE(CLK_SPI5, "clk_spi5", DIV_P4_PG, 0x822, 0x823, 0, 0, 0, 0, 0), + D_GATE(CLK_SWITCH, "clk_switch", DIV_SWITCH, 0x982, 0x983, 0, 0, 0, 0, 0), + D_DIV(DIV_MOTOR, "div_motor", CLKOUT_D5, 84, 2, 8), + D_GATE(HCLK_ECAT125, "hclk_ecat125", CLKOUT_D8, 0x400, 0x401, 0, 0x402, 0, 0x440, 0x441), + D_GATE(HCLK_PINCONFIG, "hclk_pinconfig", CLKOUT_D40, 0x740, 0x741, 0x742, 0, 0xae0, 0, 0), + D_GATE(HCLK_SERCOS, "hclk_sercos", CLKOUT_D10, 0x420, 0x422, 0, 0x421, 0, 0x460, 0x461), + D_GATE(HCLK_SGPIO2, "hclk_sgpio2", DIV_P5_PG, 0x8c3, 0x8c4, 0x8c5, 0, 0xb41, 0, 0), + D_GATE(HCLK_SGPIO3, "hclk_sgpio3", DIV_P5_PG, 0x8c6, 0x8c7, 0x8c8, 0, 0xb42, 0, 0), + D_GATE(HCLK_SGPIO4, "hclk_sgpio4", DIV_P5_PG, 0x8c9, 0x8ca, 0x8cb, 0, 0xb43, 0, 0), + D_GATE(HCLK_TIMER0, "hclk_timer0", CLKOUT_D40, 0x743, 0x744, 0x745, 0, 0xae1, 0, 0), + D_GATE(HCLK_TIMER1, "hclk_timer1", CLKOUT_D40, 0x746, 0x747, 0x748, 0, 0xae2, 0, 0), + D_GATE(HCLK_USBF, "hclk_usbf", CLKOUT_D8, 0xe3, 0, 0, 0xe4, 0, 0x102, 0x103), + D_GATE(HCLK_USBH, "hclk_usbh", CLKOUT_D8, 0xe0, 0xe1, 0, 0xe2, 0, 0x100, 0x101), + D_GATE(HCLK_USBPM, "hclk_usbpm", CLKOUT_D8, 0xe5, 0, 0, 0, 0, 0, 0), + D_GATE(CLK_48_PG_F, "clk_48_pg_f", CLK_48, 0x78c, 0x78d, 0, 0x78e, 0, 0xb04, 0xb05), + D_GATE(CLK_48_PG4, "clk_48_pg4", CLK_48, 0x789, 0x78a, 0x78b, 0, 0xb03, 0, 0), + D_FFC(CLK_DDRPHY_PLLCLK_D4, "clk_ddrphy_pllclk_d4", CLK_DDRPHY_PLLCLK, 4), + D_FFC(CLK_ECAT100_D4, "clk_ecat100_d4", CLK_ECAT100, 4), + D_FFC(CLK_HSR100_D2, "clk_hsr100_d2", CLK_HSR100, 2), + D_FFC(CLK_REF_SYNC_D4, "clk_ref_sync_d4", CLK_REF_SYNC, 4), + D_FFC(CLK_REF_SYNC_D8, "clk_ref_sync_d8", CLK_REF_SYNC, 8), + D_FFC(CLK_SERCOS100_D2, "clk_sercos100_d2", CLK_SERCOS100, 2), + D_DIV(DIV_CA7, "div_ca7", CLK_REF_SYNC, 57, 1, 4, 1, 2, 4), + D_GATE(HCLK_CAN0, "hclk_can0", CLK_48, 0x783, 0x784, 0x785, 0, 0xb01, 0, 0), + D_GATE(HCLK_CAN1, "hclk_can1", CLK_48, 0x786, 0x787, 0x788, 0, 0xb02, 0, 0), + D_GATE(HCLK_DELTASIGMA, "hclk_deltasigma", DIV_MOTOR, 0x1ef, 0x1f0, 0x1f1, 0, 0, 0, 0), + D_GATE(HCLK_PWMPTO, "hclk_pwmpto", DIV_MOTOR, 0x1ec, 0x1ed, 0x1ee, 0, 0, 0, 0), + D_GATE(HCLK_RSV, "hclk_rsv", CLK_48, 0x780, 0x781, 0x782, 0, 0xb00, 0, 0), + D_GATE(HCLK_SGPIO0, "hclk_sgpio0", DIV_MOTOR, 0x1e0, 0x1e1, 0x1e2, 0, 0, 0, 0), + D_GATE(HCLK_SGPIO1, "hclk_sgpio1", DIV_MOTOR, 0x1e3, 0x1e4, 0x1e5, 0, 0, 0, 0), + D_DIV(RTOS_MDC, "rtos_mdc", CLK_REF_SYNC, 100, 80, 640, 80, 160, 320, 640), + D_GATE(CLK_CM3, "clk_cm3", CLK_REF_SYNC_D4, 0xba0, 0xba1, 0, 0xba2, 0, 0xbc0, 0xbc1), + D_GATE(CLK_DDRC, "clk_ddrc", CLK_DDRPHY_PLLCLK_D4, 0x323, 0x324, 0, 0, 0, 0, 0), + D_GATE(CLK_ECAT25, "clk_ecat25", CLK_ECAT100_D4, 0x403, 0x404, 0, 0, 0, 0, 0), + D_GATE(CLK_HSR50, "clk_hsr50", CLK_HSR100_D2, 0x484, 0x485, 0, 0, 0, 0, 0), + D_GATE(CLK_HW_RTOS, "clk_hw_rtos", CLK_REF_SYNC_D4, 0xc60, 0xc61, 0, 0, 0, 0, 0), + D_GATE(CLK_SERCOS50, "clk_sercos50", CLK_SERCOS100_D2, 0x424, 0x423, 0, 0, 0, 0, 0), + D_GATE(HCLK_ADC, "hclk_adc", CLK_REF_SYNC_D8, 0x1af, 0x1b0, 0x1b1, 0, 0, 0, 0), + D_GATE(HCLK_CM3, "hclk_cm3", CLK_REF_SYNC_D4, 0xc20, 0xc21, 0xc22, 0, 0, 0, 0), + D_GATE(HCLK_CRYPTO_EIP150, "hclk_crypto_eip150", CLK_REF_SYNC_D4, 0x123, 0x124, 0x125, 0, 0x142, 0, 0), + D_GATE(HCLK_CRYPTO_EIP93, "hclk_crypto_eip93", CLK_REF_SYNC_D4, 0x120, 0x121, 0, 0x122, 0, 0x140, 0x141), + D_GATE(HCLK_DDRC, "hclk_ddrc", CLK_REF_SYNC_D4, 0x320, 0x322, 0, 0x321, 0, 0x3a0, 0x3a1), + D_GATE(HCLK_DMA0, "hclk_dma0", CLK_REF_SYNC_D4, 0x260, 0x261, 0x262, 0x263, 0x2c0, 0x2c1, 0x2c2), + D_GATE(HCLK_DMA1, "hclk_dma1", CLK_REF_SYNC_D4, 0x264, 0x265, 0x266, 0x267, 0x2c3, 0x2c4, 0x2c5), + D_GATE(HCLK_GMAC0, "hclk_gmac0", CLK_REF_SYNC_D4, 0x360, 0x361, 0x362, 0x363, 0x3c0, 0x3c1, 0x3c2), + D_GATE(HCLK_GMAC1, "hclk_gmac1", CLK_REF_SYNC_D4, 0x380, 0x381, 0x382, 0x383, 0x3e0, 0x3e1, 0x3e2), + D_GATE(HCLK_GPIO0, "hclk_gpio0", CLK_REF_SYNC_D4, 0x212, 0x213, 0x214, 0, 0, 0, 0), + D_GATE(HCLK_GPIO1, "hclk_gpio1", CLK_REF_SYNC_D4, 0x215, 0x216, 0x217, 0, 0, 0, 0), + D_GATE(HCLK_GPIO2, "hclk_gpio2", CLK_REF_SYNC_D4, 0x229, 0x22a, 0x22b, 0, 0, 0, 0), + D_GATE(HCLK_HSR, "hclk_hsr", CLK_HSR100_D2, 0x480, 0x482, 0, 0x481, 0, 0x4c0, 0x4c1), + D_GATE(HCLK_I2C0, "hclk_i2c0", CLK_REF_SYNC_D8, 0x1a9, 0x1aa, 0x1ab, 0, 0, 0, 0), + D_GATE(HCLK_I2C1, "hclk_i2c1", CLK_REF_SYNC_D8, 0x1ac, 0x1ad, 0x1ae, 0, 0, 0, 0), + D_GATE(HCLK_LCD, "hclk_lcd", CLK_REF_SYNC_D4, 0x7a0, 0x7a1, 0x7a2, 0, 0xb20, 0, 0), + D_GATE(HCLK_MSEBI_M, "hclk_msebi_m", CLK_REF_SYNC_D4, 0x164, 0x165, 0x166, 0, 0x183, 0, 0), + D_GATE(HCLK_MSEBI_S, "hclk_msebi_s", CLK_REF_SYNC_D4, 0x160, 0x161, 0x162, 0x163, 0x180, 0x181, 0x182), + D_GATE(HCLK_NAND, "hclk_nand", CLK_REF_SYNC_D4, 0x280, 0x281, 0x282, 0x283, 0x2e0, 0x2e1, 0x2e2), + D_GATE(HCLK_PG_I, "hclk_pg_i", CLK_REF_SYNC_D4, 0x7ac, 0x7ad, 0, 0x7ae, 0, 0xb24, 0xb25), + D_GATE(HCLK_PG19, "hclk_pg19", CLK_REF_SYNC_D4, 0x22c, 0x22d, 0x22e, 0, 0, 0, 0), + D_GATE(HCLK_PG20, "hclk_pg20", CLK_REF_SYNC_D4, 0x22f, 0x230, 0x231, 0, 0, 0, 0), + D_GATE(HCLK_PG3, "hclk_pg3", CLK_REF_SYNC_D4, 0x7a6, 0x7a7, 0x7a8, 0, 0xb22, 0, 0), + D_GATE(HCLK_PG4, "hclk_pg4", CLK_REF_SYNC_D4, 0x7a9, 0x7aa, 0x7ab, 0, 0xb23, 0, 0), + D_GATE(HCLK_QSPI0, "hclk_qspi0", CLK_REF_SYNC_D4, 0x2a0, 0x2a1, 0x2a2, 0x2a3, 0x300, 0x301, 0x302), + D_GATE(HCLK_QSPI1, "hclk_qspi1", CLK_REF_SYNC_D4, 0x480, 0x481, 0x482, 0x483, 0x4c0, 0x4c1, 0x4c2), + D_GATE(HCLK_ROM, "hclk_rom", CLK_REF_SYNC_D4, 0xaa0, 0xaa1, 0xaa2, 0, 0xb80, 0, 0), + D_GATE(HCLK_RTC, "hclk_rtc", CLK_REF_SYNC_D8, 0xa00, 0, 0, 0, 0, 0, 0), + D_GATE(HCLK_SDIO0, "hclk_sdio0", CLK_REF_SYNC_D4, 0x60, 0x61, 0x62, 0x63, 0x80, 0x81, 0x82), + D_GATE(HCLK_SDIO1, "hclk_sdio1", CLK_REF_SYNC_D4, 0x640, 0x641, 0x642, 0x643, 0x660, 0x661, 0x662), + D_GATE(HCLK_SEMAP, "hclk_semap", CLK_REF_SYNC_D4, 0x7a3, 0x7a4, 0x7a5, 0, 0xb21, 0, 0), + D_GATE(HCLK_SPI0, "hclk_spi0", CLK_REF_SYNC_D4, 0x200, 0x201, 0x202, 0, 0, 0, 0), + D_GATE(HCLK_SPI1, "hclk_spi1", CLK_REF_SYNC_D4, 0x203, 0x204, 0x205, 0, 0, 0, 0), + D_GATE(HCLK_SPI2, "hclk_spi2", CLK_REF_SYNC_D4, 0x206, 0x207, 0x208, 0, 0, 0, 0), + D_GATE(HCLK_SPI3, "hclk_spi3", CLK_REF_SYNC_D4, 0x209, 0x20a, 0x20b, 0, 0, 0, 0), + D_GATE(HCLK_SPI4, "hclk_spi4", CLK_REF_SYNC_D4, 0x20c, 0x20d, 0x20e, 0, 0, 0, 0), + D_GATE(HCLK_SPI5, "hclk_spi5", CLK_REF_SYNC_D4, 0x20f, 0x210, 0x211, 0, 0, 0, 0), + D_GATE(HCLK_SWITCH, "hclk_switch", CLK_REF_SYNC_D4, 0x980, 0, 0x981, 0, 0, 0, 0), + D_GATE(HCLK_SWITCH_RG, "hclk_switch_rg", CLK_REF_SYNC_D4, 0xc40, 0xc41, 0xc42, 0, 0, 0, 0), + D_GATE(HCLK_UART0, "hclk_uart0", CLK_REF_SYNC_D8, 0x1a0, 0x1a1, 0x1a2, 0, 0, 0, 0), + D_GATE(HCLK_UART1, "hclk_uart1", CLK_REF_SYNC_D8, 0x1a3, 0x1a4, 0x1a5, 0, 0, 0, 0), + D_GATE(HCLK_UART2, "hclk_uart2", CLK_REF_SYNC_D8, 0x1a6, 0x1a7, 0x1a8, 0, 0, 0, 0), + D_GATE(HCLK_UART3, "hclk_uart3", CLK_REF_SYNC_D4, 0x218, 0x219, 0x21a, 0, 0, 0, 0), + D_GATE(HCLK_UART4, "hclk_uart4", CLK_REF_SYNC_D4, 0x21b, 0x21c, 0x21d, 0, 0, 0, 0), + D_GATE(HCLK_UART5, "hclk_uart5", CLK_REF_SYNC_D4, 0x220, 0x221, 0x222, 0, 0, 0, 0), + D_GATE(HCLK_UART6, "hclk_uart6", CLK_REF_SYNC_D4, 0x223, 0x224, 0x225, 0, 0, 0, 0), + D_GATE(HCLK_UART7, "hclk_uart7", CLK_REF_SYNC_D4, 0x226, 0x227, 0x228, 0, 0, 0, 0), + /* + * These are not hardware clocks, but are needed to handle the special + * case where we have a 'selector bit' that doesn't just change the + * parent for a clock, but also the gate it's suposed to use. + */ + { + .index = R9A06G032_UART_GROUP_012, + .name = "uart_group_012", + .type = K_BITSEL, + .source = 1 + R9A06G032_DIV_UART, + /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */ + .dual.sel = ((0xec / 4) << 5) | 24, + .dual.group = 0, + }, + { + .index = R9A06G032_UART_GROUP_34567, + .name = "uart_group_34567", + .type = K_BITSEL, + .source = 1 + R9A06G032_DIV_P2_PG, + /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */ + .dual.sel = ((0x34 / 4) << 5) | 30, + .dual.group = 1, + }, + D_UGATE(CLK_UART0, "clk_uart0", UART_GROUP_012, 0, 0, 0x1b2, 0x1b3, 0x1b4, 0x1b5), + D_UGATE(CLK_UART1, "clk_uart1", UART_GROUP_012, 0, 1, 0x1b6, 0x1b7, 0x1b8, 0x1b9), + D_UGATE(CLK_UART2, "clk_uart2", UART_GROUP_012, 0, 2, 0x1ba, 0x1bb, 0x1bc, 0x1bd), + D_UGATE(CLK_UART3, "clk_uart3", UART_GROUP_34567, 1, 0, 0x760, 0x761, 0x762, 0x763), + D_UGATE(CLK_UART4, "clk_uart4", UART_GROUP_34567, 1, 1, 0x764, 0x765, 0x766, 0x767), + D_UGATE(CLK_UART5, "clk_uart5", UART_GROUP_34567, 1, 2, 0x768, 0x769, 0x76a, 0x76b), + D_UGATE(CLK_UART6, "clk_uart6", UART_GROUP_34567, 1, 3, 0x76c, 0x76d, 0x76e, 0x76f), + D_UGATE(CLK_UART7, "clk_uart7", UART_GROUP_34567, 1, 4, 0x770, 0x771, 0x772, 0x773), +}; + +struct r9a06g032_priv { + struct clk_onecell_data data; + spinlock_t lock; /* protects concurent access to gates */ + void __iomem *reg; +}; + +/* register/bit pairs are encoded as an uint16_t */ +static void +clk_rdesc_set(struct r9a06g032_priv *clocks, + u16 one, unsigned int on) +{ + u32 __iomem *reg = clocks->reg + (4 * (one >> 5)); + u32 val = readl(reg); + + val = (val & ~(1U << (one & 0x1f))) | ((!!on) << (one & 0x1f)); + writel(val, reg); +} + +static int +clk_rdesc_get(struct r9a06g032_priv *clocks, + uint16_t one) +{ + u32 __iomem *reg = clocks->reg + (4 * (one >> 5)); + u32 val = readl(reg); + + return !!(val & (1U << (one & 0x1f))); +} + +/* + * This implements the R9A09G032 clock gate 'driver'. We cannot use the system's + * clock gate framework as the gates on the R9A09G032 have a special enabling + * sequence, therefore we use this little proxy. + */ +struct r9a06g032_clk_gate { + struct clk_hw hw; + struct r9a06g032_priv *clocks; + u16 index; + + struct r9a06g032_gate gate; +}; + +#define to_r9a06g032_gate(_hw) container_of(_hw, struct r9a06g032_clk_gate, hw) + +static void +r9a06g032_clk_gate_set(struct r9a06g032_priv *clocks, + struct r9a06g032_gate *g, int on) +{ + unsigned long flags; + + WARN_ON(!g->gate); + + spin_lock_irqsave(&clocks->lock, flags); + clk_rdesc_set(clocks, g->gate, on); + /* De-assert reset */ + if (g->reset) + clk_rdesc_set(clocks, g->reset, 1); + spin_unlock_irqrestore(&clocks->lock, flags); + + /* Hardware manual recommends 5us delay after enabling clock & reset */ + udelay(5); + + /* If the peripheral is memory mapped (i.e. an AXI slave), there is an + * associated SLVRDY bit in the System Controller that needs to be set + * so that the FlexWAY bus fabric passes on the read/write requests. + */ + if (g->ready || g->midle) { + spin_lock_irqsave(&clocks->lock, flags); + if (g->ready) + clk_rdesc_set(clocks, g->ready, on); + /* Clear 'Master Idle Request' bit */ + if (g->midle) + clk_rdesc_set(clocks, g->midle, !on); + spin_unlock_irqrestore(&clocks->lock, flags); + } + /* Note: We don't wait for FlexWAY Socket Connection signal */ +} + +static int r9a06g032_clk_gate_enable(struct clk_hw *hw) +{ + struct r9a06g032_clk_gate *g = to_r9a06g032_gate(hw); + + r9a06g032_clk_gate_set(g->clocks, &g->gate, 1); + return 0; +} + +static void r9a06g032_clk_gate_disable(struct clk_hw *hw) +{ + struct r9a06g032_clk_gate *g = to_r9a06g032_gate(hw); + + r9a06g032_clk_gate_set(g->clocks, &g->gate, 0); +} + +static int r9a06g032_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct r9a06g032_clk_gate *g = to_r9a06g032_gate(hw); + + /* if clock is in reset, the gate might be on, and still not 'be' on */ + if (g->gate.reset && !clk_rdesc_get(g->clocks, g->gate.reset)) + return 0; + + return clk_rdesc_get(g->clocks, g->gate.gate); +} + +static const struct clk_ops r9a06g032_clk_gate_ops = { + .enable = r9a06g032_clk_gate_enable, + .disable = r9a06g032_clk_gate_disable, + .is_enabled = r9a06g032_clk_gate_is_enabled, +}; + +static struct clk * +r9a06g032_register_gate(struct r9a06g032_priv *clocks, + const char *parent_name, + const struct r9a06g032_clkdesc *desc) +{ + struct clk *clk; + struct r9a06g032_clk_gate *g; + struct clk_init_data init; + + g = kzalloc(sizeof(*g), GFP_KERNEL); + if (!g) + return NULL; + + init.name = desc->name; + init.ops = &r9a06g032_clk_gate_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + g->clocks = clocks; + g->index = desc->index; + g->gate = desc->gate; + g->hw.init = &init; + + /* + * important here, some clocks are already in use by the CM3, we + * have to assume they are not Linux's to play with and try to disable + * at the end of the boot! + */ + if (r9a06g032_clk_gate_is_enabled(&g->hw)) { + init.flags |= CLK_IS_CRITICAL; + pr_debug("%s was enabled, making read-only\n", desc->name); + } + + clk = clk_register(NULL, &g->hw); + if (IS_ERR(clk)) { + kfree(g); + return NULL; + } + return clk; +} + +struct r9a06g032_clk_div { + struct clk_hw hw; + struct r9a06g032_priv *clocks; + u16 index; + u16 reg; + u16 min, max; + u8 table_size; + u16 table[8]; /* we know there are no more than 8 */ +}; + +#define to_r9a06g032_div(_hw) \ + container_of(_hw, struct r9a06g032_clk_div, hw) + +static unsigned long +r9a06g032_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct r9a06g032_clk_div *clk = to_r9a06g032_div(hw); + u32 __iomem *reg = clk->clocks->reg + (4 * clk->reg); + u32 div = readl(reg); + + if (div < clk->min) + div = clk->min; + else if (div > clk->max) + div = clk->max; + return DIV_ROUND_UP(parent_rate, div); +} + +/* + * Attempts to find a value that is in range of min,max, + * and if a table of set dividers was specified for this + * register, try to find the fixed divider that is the closest + * to the target frequency + */ +static long +r9a06g032_div_clamp_div(struct r9a06g032_clk_div *clk, + unsigned long rate, unsigned long prate) +{ + /* + 1 to cope with rates that have the remainder dropped */ + u32 div = DIV_ROUND_UP(prate, rate + 1); + int i; + + if (div <= clk->min) + return clk->min; + if (div >= clk->max) + return clk->max; + + for (i = 0; clk->table_size && i < clk->table_size - 1; i++) { + if (div >= clk->table[i] && div <= clk->table[i + 1]) { + unsigned long m = rate - + DIV_ROUND_UP(prate, clk->table[i]); + unsigned long p = + DIV_ROUND_UP(prate, clk->table[i + 1]) - + rate; + /* + * select the divider that generates + * the value closest to the ideal frequency + */ + div = p >= m ? clk->table[i] : clk->table[i + 1]; + return div; + } + } + return div; +} + +static long +r9a06g032_div_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *prate) +{ + struct r9a06g032_clk_div *clk = to_r9a06g032_div(hw); + u32 div = DIV_ROUND_UP(*prate, rate); + + pr_devel("%s %pC %ld (prate %ld) (wanted div %u)\n", __func__, + hw->clk, rate, *prate, div); + pr_devel(" min %d (%ld) max %d (%ld)\n", + clk->min, DIV_ROUND_UP(*prate, clk->min), + clk->max, DIV_ROUND_UP(*prate, clk->max)); + + div = r9a06g032_div_clamp_div(clk, rate, *prate); + /* + * this is a hack. Currently the serial driver asks for a clock rate + * that is 16 times the baud rate -- and that is wildly outside the + * range of the UART divider, somehow there is no provision for that + * case of 'let the divider as is if outside range'. + * The serial driver *shouldn't* play with these clocks anyway, there's + * several uarts attached to this divider, and changing this impacts + * everyone. + */ + if (clk->index == R9A06G032_DIV_UART) { + pr_devel("%s div uart hack!\n", __func__); + return clk_get_rate(hw->clk); + } + pr_devel("%s %pC %ld / %u = %ld\n", __func__, hw->clk, + *prate, div, DIV_ROUND_UP(*prate, div)); + return DIV_ROUND_UP(*prate, div); +} + +static int +r9a06g032_div_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct r9a06g032_clk_div *clk = to_r9a06g032_div(hw); + /* + 1 to cope with rates that have the remainder dropped */ + u32 div = DIV_ROUND_UP(parent_rate, rate + 1); + u32 __iomem *reg = clk->clocks->reg + (4 * clk->reg); + + pr_devel("%s %pC rate %ld parent %ld div %d\n", __func__, hw->clk, + rate, parent_rate, div); + + /* + * Need to write the bit 31 with the divider value to + * latch it. Technically we should wait until it has been + * cleared too. + * TODO: Find whether this callback is sleepable, in case + * the hardware /does/ require some sort of spinloop here. + */ + writel(div | BIT(31), reg); + + return 0; +} + +static const struct clk_ops r9a06g032_clk_div_ops = { + .recalc_rate = r9a06g032_div_recalc_rate, + .round_rate = r9a06g032_div_round_rate, + .set_rate = r9a06g032_div_set_rate, +}; + +static struct clk * +r9a06g032_register_div(struct r9a06g032_priv *clocks, + const char *parent_name, + const struct r9a06g032_clkdesc *desc) +{ + struct r9a06g032_clk_div *div; + struct clk *clk; + struct clk_init_data init; + unsigned int i; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return NULL; + + init.name = desc->name; + init.ops = &r9a06g032_clk_div_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + div->clocks = clocks; + div->index = desc->index; + div->reg = desc->reg; + div->hw.init = &init; + div->min = desc->div_min; + div->max = desc->div_max; + /* populate (optional) divider table fixed values */ + for (i = 0; i < ARRAY_SIZE(div->table) && + i < ARRAY_SIZE(desc->div_table) && desc->div_table[i]; i++) { + div->table[div->table_size++] = desc->div_table[i]; + } + + clk = clk_register(NULL, &div->hw); + if (IS_ERR(clk)) { + kfree(div); + return NULL; + } + return clk; +} + +/* + * This clock provider handles the case of the R9A06G032 where you have + * peripherals that have two potential clock source and two gates, one for + * each of the clock source - the used clock source (for all sub clocks) + * is selected by a single bit. + * That single bit affects all sub-clocks, and therefore needs to change the + * active gate (and turn the others off) and force a recalculation of the rates. + * + * This implements two clock providers, one 'bitselect' that + * handles the switch between both parents, and another 'dualgate' + * that knows which gate to poke at, depending on the parent's bit position. + */ +struct r9a06g032_clk_bitsel { + struct clk_hw hw; + struct r9a06g032_priv *clocks; + u16 index; + u16 selector; /* selector register + bit */ +}; + +#define to_clk_bitselect(_hw) \ + container_of(_hw, struct r9a06g032_clk_bitsel, hw) + +static u8 r9a06g032_clk_mux_get_parent(struct clk_hw *hw) +{ + struct r9a06g032_clk_bitsel *set = to_clk_bitselect(hw); + + return clk_rdesc_get(set->clocks, set->selector); +} + +static int r9a06g032_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct r9a06g032_clk_bitsel *set = to_clk_bitselect(hw); + + /* a single bit in the register selects one of two parent clocks */ + clk_rdesc_set(set->clocks, set->selector, !!index); + + return 0; +} + +static const struct clk_ops clk_bitselect_ops = { + .get_parent = r9a06g032_clk_mux_get_parent, + .set_parent = r9a06g032_clk_mux_set_parent, +}; + +static struct clk * +r9a06g032_register_bitsel(struct r9a06g032_priv *clocks, + const char *parent_name, + const struct r9a06g032_clkdesc *desc) +{ + struct clk *clk; + struct r9a06g032_clk_bitsel *g; + struct clk_init_data init; + const char *names[2]; + + /* allocate the gate */ + g = kzalloc(sizeof(*g), GFP_KERNEL); + if (!g) + return NULL; + + names[0] = parent_name; + names[1] = "clk_pll_usb"; + + init.name = desc->name; + init.ops = &clk_bitselect_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.parent_names = names; + init.num_parents = 2; + + g->clocks = clocks; + g->index = desc->index; + g->selector = desc->dual.sel; + g->hw.init = &init; + + clk = clk_register(NULL, &g->hw); + if (IS_ERR(clk)) { + kfree(g); + return NULL; + } + return clk; +} + +struct r9a06g032_clk_dualgate { + struct clk_hw hw; + struct r9a06g032_priv *clocks; + u16 index; + u16 selector; /* selector register + bit */ + struct r9a06g032_gate gate[2]; +}; + +#define to_clk_dualgate(_hw) \ + container_of(_hw, struct r9a06g032_clk_dualgate, hw) + +static int +r9a06g032_clk_dualgate_setenable(struct r9a06g032_clk_dualgate *g, int enable) +{ + u8 sel_bit = clk_rdesc_get(g->clocks, g->selector); + + /* we always turn off the 'other' gate, regardless */ + r9a06g032_clk_gate_set(g->clocks, &g->gate[!sel_bit], 0); + r9a06g032_clk_gate_set(g->clocks, &g->gate[sel_bit], enable); + + return 0; +} + +static int r9a06g032_clk_dualgate_enable(struct clk_hw *hw) +{ + struct r9a06g032_clk_dualgate *gate = to_clk_dualgate(hw); + + r9a06g032_clk_dualgate_setenable(gate, 1); + + return 0; +} + +static void r9a06g032_clk_dualgate_disable(struct clk_hw *hw) +{ + struct r9a06g032_clk_dualgate *gate = to_clk_dualgate(hw); + + r9a06g032_clk_dualgate_setenable(gate, 0); +} + +static int r9a06g032_clk_dualgate_is_enabled(struct clk_hw *hw) +{ + struct r9a06g032_clk_dualgate *g = to_clk_dualgate(hw); + u8 sel_bit = clk_rdesc_get(g->clocks, g->selector); + + return clk_rdesc_get(g->clocks, g->gate[sel_bit].gate); +} + +static const struct clk_ops r9a06g032_clk_dualgate_ops = { + .enable = r9a06g032_clk_dualgate_enable, + .disable = r9a06g032_clk_dualgate_disable, + .is_enabled = r9a06g032_clk_dualgate_is_enabled, +}; + +static struct clk * +r9a06g032_register_dualgate(struct r9a06g032_priv *clocks, + const char *parent_name, + const struct r9a06g032_clkdesc *desc, + uint16_t sel) +{ + struct r9a06g032_clk_dualgate *g; + struct clk *clk; + struct clk_init_data init; + + /* allocate the gate */ + g = kzalloc(sizeof(*g), GFP_KERNEL); + if (!g) + return NULL; + g->clocks = clocks; + g->index = desc->index; + g->selector = sel; + g->gate[0].gate = desc->dual.g1; + g->gate[0].reset = desc->dual.r1; + g->gate[1].gate = desc->dual.g2; + g->gate[1].reset = desc->dual.r2; + + init.name = desc->name; + init.ops = &r9a06g032_clk_dualgate_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + g->hw.init = &init; + /* + * important here, some clocks are already in use by the CM3, we + * have to assume they are not Linux's to play with and try to disable + * at the end of the boot! + */ + if (r9a06g032_clk_dualgate_is_enabled(&g->hw)) { + init.flags |= CLK_IS_CRITICAL; + pr_debug("%s was enabled, making read-only\n", desc->name); + } + + clk = clk_register(NULL, &g->hw); + if (IS_ERR(clk)) { + kfree(g); + return NULL; + } + return clk; +} + +static void r9a06g032_clocks_del_clk_provider(void *data) +{ + of_clk_del_provider(data); +} + +static int __init r9a06g032_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct r9a06g032_priv *clocks; + struct clk **clks; + struct clk *mclk; + unsigned int i; + u16 uart_group_sel[2]; + int error; + + clocks = devm_kzalloc(dev, sizeof(*clocks), GFP_KERNEL); + clks = devm_kcalloc(dev, R9A06G032_CLOCK_COUNT, sizeof(struct clk *), + GFP_KERNEL); + if (!clocks || !clks) + return -ENOMEM; + + spin_lock_init(&clocks->lock); + + clocks->data.clks = clks; + clocks->data.clk_num = R9A06G032_CLOCK_COUNT; + + mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(mclk)) + return PTR_ERR(mclk); + + clocks->reg = of_iomap(np, 0); + if (WARN_ON(!clocks->reg)) + return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(r9a06g032_clocks); ++i) { + const struct r9a06g032_clkdesc *d = &r9a06g032_clocks[i]; + const char *parent_name = d->source ? + __clk_get_name(clocks->data.clks[d->source - 1]) : + __clk_get_name(mclk); + struct clk *clk = NULL; + + switch (d->type) { + case K_FFC: + clk = clk_register_fixed_factor(NULL, d->name, + parent_name, 0, + d->mul, d->div); + break; + case K_GATE: + clk = r9a06g032_register_gate(clocks, parent_name, d); + break; + case K_DIV: + clk = r9a06g032_register_div(clocks, parent_name, d); + break; + case K_BITSEL: + /* keep that selector register around */ + uart_group_sel[d->dual.group] = d->dual.sel; + clk = r9a06g032_register_bitsel(clocks, parent_name, d); + break; + case K_DUALGATE: + clk = r9a06g032_register_dualgate(clocks, parent_name, + d, + uart_group_sel[d->dual.group]); + break; + } + clocks->data.clks[d->index] = clk; + } + error = of_clk_add_provider(np, of_clk_src_onecell_get, &clocks->data); + if (error) + return error; + + return devm_add_action_or_reset(dev, + r9a06g032_clocks_del_clk_provider, np); +} + +static const struct of_device_id r9a06g032_match[] = { + { .compatible = "renesas,r9a06g032-sysctrl" }, + { } +}; + +static struct platform_driver r9a06g032_clock_driver = { + .driver = { + .name = "renesas,r9a06g032-sysctrl", + .of_match_table = r9a06g032_match, + }, +}; + +static int __init r9a06g032_clocks_init(void) +{ + return platform_driver_probe(&r9a06g032_clock_driver, + r9a06g032_clocks_probe); +} + +subsys_initcall(r9a06g032_clocks_init); diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 98e7b9429b83..ff35ab463a6f 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -6,12 +6,14 @@ obj-y += clk.o obj-y += clk-pll.o obj-y += clk-cpu.o +obj-y += clk-half-divider.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o obj-y += clk-muxgrf.o obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o +obj-y += clk-px30.o obj-y += clk-rv1108.o obj-y += clk-rk3036.o obj-y += clk-rk3128.o diff --git a/drivers/clk/rockchip/clk-half-divider.c b/drivers/clk/rockchip/clk-half-divider.c new file mode 100644 index 000000000000..b8da6e799423 --- /dev/null +++ b/drivers/clk/rockchip/clk-half-divider.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd + */ + +#include <linux/slab.h> +#include <linux/clk-provider.h> +#include "clk.h" + +#define div_mask(width) ((1 << (width)) - 1) + +static bool _is_best_half_div(unsigned long rate, unsigned long now, + unsigned long best, unsigned long flags) +{ + if (flags & CLK_DIVIDER_ROUND_CLOSEST) + return abs(rate - now) < abs(rate - best); + + return now <= rate && now > best; +} + +static unsigned long clk_half_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + unsigned int val; + + val = clk_readl(divider->reg) >> divider->shift; + val &= div_mask(divider->width); + val = val * 2 + 3; + + return DIV_ROUND_UP_ULL(((u64)parent_rate * 2), val); +} + +static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, u8 width, + unsigned long flags) +{ + unsigned int i, bestdiv = 0; + unsigned long parent_rate, best = 0, now, maxdiv; + unsigned long parent_rate_saved = *best_parent_rate; + + if (!rate) + rate = 1; + + maxdiv = div_mask(width); + + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; + bestdiv = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate); + if (bestdiv < 3) + bestdiv = 0; + else + bestdiv = (bestdiv - 3) / 2; + bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; + return bestdiv; + } + + /* + * The maximum divider we can use without overflowing + * unsigned long in rate * i below + */ + maxdiv = min(ULONG_MAX / rate, maxdiv); + + for (i = 0; i <= maxdiv; i++) { + if (((u64)rate * (i * 2 + 3)) == ((u64)parent_rate_saved * 2)) { + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without needing to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return i; + } + parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), + ((u64)rate * (i * 2 + 3)) / 2); + now = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), + (i * 2 + 3)); + + if (_is_best_half_div(rate, now, best, flags)) { + bestdiv = i; + best = now; + *best_parent_rate = parent_rate; + } + } + + if (!bestdiv) { + bestdiv = div_mask(width); + *best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1); + } + + return bestdiv; +} + +static long clk_half_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_divider *divider = to_clk_divider(hw); + int div; + + div = clk_half_divider_bestdiv(hw, rate, prate, + divider->width, + divider->flags); + + return DIV_ROUND_UP_ULL(((u64)*prate * 2), div * 2 + 3); +} + +static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + unsigned int value; + unsigned long flags = 0; + u32 val; + + value = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate); + value = (value - 3) / 2; + value = min_t(unsigned int, value, div_mask(divider->width)); + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + else + __acquire(divider->lock); + + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = div_mask(divider->width) << (divider->shift + 16); + } else { + val = clk_readl(divider->reg); + val &= ~(div_mask(divider->width) << divider->shift); + } + val |= value << divider->shift; + clk_writel(val, divider->reg); + + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + else + __release(divider->lock); + + return 0; +} + +const struct clk_ops clk_half_divider_ops = { + .recalc_rate = clk_half_divider_recalc_rate, + .round_rate = clk_half_divider_round_rate, + .set_rate = clk_half_divider_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_half_divider_ops); + +/** + * Register a clock branch. + * Most clock branches have a form like + * + * src1 --|--\ + * |M |--[GATE]-[DIV]- + * src2 --|--/ + * + * sometimes without one of those components. + */ +struct clk *rockchip_clk_register_halfdiv(const char *name, + 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, int gate_offset, + u8 gate_shift, u8 gate_flags, + unsigned long flags, + spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + const struct clk_ops *mux_ops = NULL, *div_ops = NULL, + *gate_ops = NULL; + + if (num_parents > 1) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + muxdiv_offset; + mux->shift = mux_shift; + mux->mask = BIT(mux_width) - 1; + mux->flags = mux_flags; + mux->lock = lock; + mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops + : &clk_mux_ops; + } + + if (gate_offset >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto err_gate; + + gate->flags = gate_flags; + gate->reg = base + gate_offset; + gate->bit_idx = gate_shift; + gate->lock = lock; + gate_ops = &clk_gate_ops; + } + + if (div_width > 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto err_div; + + div->flags = div_flags; + div->reg = base + muxdiv_offset; + div->shift = div_shift; + div->width = div_width; + div->lock = lock; + div_ops = &clk_half_divider_ops; + } + + clk = clk_register_composite(NULL, name, parent_names, num_parents, + mux ? &mux->hw : NULL, mux_ops, + div ? &div->hw : NULL, div_ops, + gate ? &gate->hw : NULL, gate_ops, + flags); + + return clk; +err_div: + kfree(gate); +err_gate: + kfree(mux); + return ERR_PTR(-ENOMEM); +} diff --git a/drivers/clk/rockchip/clk-px30.c b/drivers/clk/rockchip/clk-px30.c new file mode 100644 index 000000000000..601a77f1af78 --- /dev/null +++ b/drivers/clk/rockchip/clk-px30.c @@ -0,0 +1,1039 @@ +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * Author: Elaine Zhang<zhangqing@rock-chips.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/syscore_ops.h> +#include <dt-bindings/clock/px30-cru.h> +#include "clk.h" + +#define PX30_GRF_SOC_STATUS0 0x480 + +enum px30_plls { + apll, dpll, cpll, npll, apll_b_h, apll_b_l, +}; + +enum px30_pmu_plls { + gpll, +}; + +static struct rockchip_pll_rate_table px30_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0), + RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0), + RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0), + RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0), + RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0), + RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0), + RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0), + RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0), + RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0), + RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0), + RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0), + RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0), + RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0), + RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0), + RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), + RK3036_PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0), + RK3036_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), + RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + RK3036_PLL_RATE(984000000, 1, 82, 2, 1, 1, 0), + RK3036_PLL_RATE(960000000, 1, 80, 2, 1, 1, 0), + RK3036_PLL_RATE(936000000, 1, 78, 2, 1, 1, 0), + RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0), + RK3036_PLL_RATE(900000000, 4, 300, 2, 1, 1, 0), + RK3036_PLL_RATE(888000000, 1, 74, 2, 1, 1, 0), + RK3036_PLL_RATE(864000000, 1, 72, 2, 1, 1, 0), + RK3036_PLL_RATE(840000000, 1, 70, 2, 1, 1, 0), + RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE(800000000, 6, 400, 2, 1, 1, 0), + RK3036_PLL_RATE(700000000, 6, 350, 2, 1, 1, 0), + RK3036_PLL_RATE(696000000, 1, 58, 2, 1, 1, 0), + RK3036_PLL_RATE(624000000, 1, 52, 2, 1, 1, 0), + RK3036_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0), + RK3036_PLL_RATE(594000000, 2, 99, 2, 1, 1, 0), + RK3036_PLL_RATE(504000000, 1, 63, 3, 1, 1, 0), + RK3036_PLL_RATE(500000000, 6, 250, 2, 1, 1, 0), + RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0), + RK3036_PLL_RATE(312000000, 1, 52, 2, 2, 1, 0), + RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0), + RK3036_PLL_RATE(96000000, 1, 64, 4, 4, 1, 0), + { /* sentinel */ }, +}; + +#define PX30_DIV_ACLKM_MASK 0x7 +#define PX30_DIV_ACLKM_SHIFT 12 +#define PX30_DIV_PCLK_DBG_MASK 0xf +#define PX30_DIV_PCLK_DBG_SHIFT 8 + +#define PX30_CLKSEL0(_aclk_core, _pclk_dbg) \ +{ \ + .reg = PX30_CLKSEL_CON(0), \ + .val = HIWORD_UPDATE(_aclk_core, PX30_DIV_ACLKM_MASK, \ + PX30_DIV_ACLKM_SHIFT) | \ + HIWORD_UPDATE(_pclk_dbg, PX30_DIV_PCLK_DBG_MASK, \ + PX30_DIV_PCLK_DBG_SHIFT), \ +} + +#define PX30_CPUCLK_RATE(_prate, _aclk_core, _pclk_dbg) \ +{ \ + .prate = _prate, \ + .divs = { \ + PX30_CLKSEL0(_aclk_core, _pclk_dbg), \ + }, \ +} + +static struct rockchip_cpuclk_rate_table px30_cpuclk_rates[] __initdata = { + PX30_CPUCLK_RATE(1608000000, 1, 7), + PX30_CPUCLK_RATE(1584000000, 1, 7), + PX30_CPUCLK_RATE(1560000000, 1, 7), + PX30_CPUCLK_RATE(1536000000, 1, 7), + PX30_CPUCLK_RATE(1512000000, 1, 7), + PX30_CPUCLK_RATE(1488000000, 1, 5), + PX30_CPUCLK_RATE(1464000000, 1, 5), + PX30_CPUCLK_RATE(1440000000, 1, 5), + PX30_CPUCLK_RATE(1416000000, 1, 5), + PX30_CPUCLK_RATE(1392000000, 1, 5), + PX30_CPUCLK_RATE(1368000000, 1, 5), + PX30_CPUCLK_RATE(1344000000, 1, 5), + PX30_CPUCLK_RATE(1320000000, 1, 5), + PX30_CPUCLK_RATE(1296000000, 1, 5), + PX30_CPUCLK_RATE(1272000000, 1, 5), + PX30_CPUCLK_RATE(1248000000, 1, 5), + PX30_CPUCLK_RATE(1224000000, 1, 5), + PX30_CPUCLK_RATE(1200000000, 1, 5), + PX30_CPUCLK_RATE(1104000000, 1, 5), + PX30_CPUCLK_RATE(1008000000, 1, 5), + PX30_CPUCLK_RATE(912000000, 1, 5), + PX30_CPUCLK_RATE(816000000, 1, 3), + PX30_CPUCLK_RATE(696000000, 1, 3), + PX30_CPUCLK_RATE(600000000, 1, 3), + PX30_CPUCLK_RATE(408000000, 1, 1), + PX30_CPUCLK_RATE(312000000, 1, 1), + PX30_CPUCLK_RATE(216000000, 1, 1), + PX30_CPUCLK_RATE(96000000, 1, 1), +}; + +static const struct rockchip_cpuclk_reg_data px30_cpuclk_data = { + .core_reg = PX30_CLKSEL_CON(0), + .div_core_shift = 0, + .div_core_mask = 0xf, + .mux_core_alt = 1, + .mux_core_main = 0, + .mux_core_shift = 7, + .mux_core_mask = 0x1, +}; + +PNAME(mux_pll_p) = { "xin24m"}; +PNAME(mux_usb480m_p) = { "xin24m", "usb480m_phy", "clk_rtc32k_pmu" }; +PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; +PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; +PNAME(mux_ddrstdby_p) = { "clk_ddrphy1x", "clk_stdby_2wrap" }; +PNAME(mux_4plls_p) = { "gpll", "dummy_cpll", "usb480m", "npll" }; +PNAME(mux_cpll_npll_p) = { "cpll", "npll" }; +PNAME(mux_npll_cpll_p) = { "npll", "cpll" }; +PNAME(mux_gpll_cpll_p) = { "gpll", "dummy_cpll" }; +PNAME(mux_gpll_npll_p) = { "gpll", "npll" }; +PNAME(mux_gpll_xin24m_p) = { "gpll", "xin24m"}; +PNAME(mux_gpll_cpll_npll_p) = { "gpll", "dummy_cpll", "npll" }; +PNAME(mux_gpll_cpll_npll_xin24m_p) = { "gpll", "dummy_cpll", "npll", "xin24m" }; +PNAME(mux_gpll_xin24m_npll_p) = { "gpll", "xin24m", "npll"}; +PNAME(mux_pdm_p) = { "clk_pdm_src", "clk_pdm_frac" }; +PNAME(mux_i2s0_tx_p) = { "clk_i2s0_tx_src", "clk_i2s0_tx_frac", "mclk_i2s0_tx_in", "xin12m"}; +PNAME(mux_i2s0_rx_p) = { "clk_i2s0_rx_src", "clk_i2s0_rx_frac", "mclk_i2s0_rx_in", "xin12m"}; +PNAME(mux_i2s1_p) = { "clk_i2s1_src", "clk_i2s1_frac", "i2s1_clkin", "xin12m"}; +PNAME(mux_i2s2_p) = { "clk_i2s2_src", "clk_i2s2_frac", "i2s2_clkin", "xin12m"}; +PNAME(mux_i2s0_tx_out_p) = { "clk_i2s0_tx", "xin12m", "clk_i2s0_rx"}; +PNAME(mux_i2s0_rx_out_p) = { "clk_i2s0_rx", "xin12m", "clk_i2s0_tx"}; +PNAME(mux_i2s1_out_p) = { "clk_i2s1", "xin12m"}; +PNAME(mux_i2s2_out_p) = { "clk_i2s2", "xin12m"}; +PNAME(mux_i2s0_tx_rx_p) = { "clk_i2s0_tx_mux", "clk_i2s0_rx_mux"}; +PNAME(mux_i2s0_rx_tx_p) = { "clk_i2s0_rx_mux", "clk_i2s0_tx_mux"}; +PNAME(mux_uart_src_p) = { "gpll", "xin24m", "usb480m", "npll" }; +PNAME(mux_uart1_p) = { "clk_uart1_src", "clk_uart1_np5", "clk_uart1_frac" }; +PNAME(mux_uart2_p) = { "clk_uart2_src", "clk_uart2_np5", "clk_uart2_frac" }; +PNAME(mux_uart3_p) = { "clk_uart3_src", "clk_uart3_np5", "clk_uart3_frac" }; +PNAME(mux_uart4_p) = { "clk_uart4_src", "clk_uart4_np5", "clk_uart4_frac" }; +PNAME(mux_uart5_p) = { "clk_uart5_src", "clk_uart5_np5", "clk_uart5_frac" }; +PNAME(mux_cif_out_p) = { "xin24m", "dummy_cpll", "npll", "usb480m" }; +PNAME(mux_dclk_vopb_p) = { "dclk_vopb_src", "dclk_vopb_frac", "xin24m" }; +PNAME(mux_dclk_vopl_p) = { "dclk_vopl_src", "dclk_vopl_frac", "xin24m" }; +PNAME(mux_gmac_p) = { "clk_gmac_src", "gmac_clkin" }; +PNAME(mux_gmac_rmii_sel_p) = { "clk_gmac_rx_tx_div20", "clk_gmac_rx_tx_div2" }; +PNAME(mux_rtc32k_pmu_p) = { "xin32k", "pmu_pvtm_32k", "clk_rtc32k_frac", }; +PNAME(mux_wifi_pmu_p) = { "xin24m", "clk_wifi_pmu_src" }; +PNAME(mux_uart0_pmu_p) = { "clk_uart0_pmu_src", "clk_uart0_np5", "clk_uart0_frac" }; +PNAME(mux_usbphy_ref_p) = { "xin24m", "clk_ref24m_pmu" }; +PNAME(mux_mipidsiphy_ref_p) = { "xin24m", "clk_ref24m_pmu" }; +PNAME(mux_gpu_p) = { "clk_gpu_div", "clk_gpu_np5" }; + +static struct rockchip_pll_clock px30_pll_clks[] __initdata = { + [apll] = PLL(pll_rk3328, PLL_APLL, "apll", mux_pll_p, + 0, PX30_PLL_CON(0), + PX30_MODE_CON, 0, 0, 0, px30_pll_rates), + [dpll] = PLL(pll_rk3328, PLL_DPLL, "dpll", mux_pll_p, + 0, PX30_PLL_CON(8), + PX30_MODE_CON, 4, 1, 0, NULL), + [cpll] = PLL(pll_rk3328, PLL_CPLL, "cpll", mux_pll_p, + 0, PX30_PLL_CON(16), + PX30_MODE_CON, 2, 2, 0, px30_pll_rates), + [npll] = PLL(pll_rk3328, PLL_NPLL, "npll", mux_pll_p, + 0, PX30_PLL_CON(24), + PX30_MODE_CON, 6, 4, 0, px30_pll_rates), +}; + +static struct rockchip_pll_clock px30_pmu_pll_clks[] __initdata = { + [gpll] = PLL(pll_rk3328, PLL_GPLL, "gpll", mux_pll_p, 0, PX30_PMU_PLL_CON(0), + PX30_PMU_MODE, 0, 3, 0, px30_pll_rates), +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) + +static struct rockchip_clk_branch px30_pdm_fracmux __initdata = + MUX(0, "clk_pdm_mux", mux_pdm_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(26), 15, 1, MFLAGS); + +static struct rockchip_clk_branch px30_i2s0_tx_fracmux __initdata = + MUX(0, "clk_i2s0_tx_mux", mux_i2s0_tx_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(28), 10, 2, MFLAGS); + +static struct rockchip_clk_branch px30_i2s0_rx_fracmux __initdata = + MUX(0, "clk_i2s0_rx_mux", mux_i2s0_rx_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(58), 10, 2, MFLAGS); + +static struct rockchip_clk_branch px30_i2s1_fracmux __initdata = + MUX(0, "clk_i2s1_mux", mux_i2s1_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(30), 10, 2, MFLAGS); + +static struct rockchip_clk_branch px30_i2s2_fracmux __initdata = + MUX(0, "clk_i2s2_mux", mux_i2s2_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(32), 10, 2, MFLAGS); + +static struct rockchip_clk_branch px30_uart1_fracmux __initdata = + MUX(0, "clk_uart1_mux", mux_uart1_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(35), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_uart2_fracmux __initdata = + MUX(0, "clk_uart2_mux", mux_uart2_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(38), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_uart3_fracmux __initdata = + MUX(0, "clk_uart3_mux", mux_uart3_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(41), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_uart4_fracmux __initdata = + MUX(0, "clk_uart4_mux", mux_uart4_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(44), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_uart5_fracmux __initdata = + MUX(0, "clk_uart5_mux", mux_uart5_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(47), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_dclk_vopb_fracmux __initdata = + MUX(0, "dclk_vopb_mux", mux_dclk_vopb_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(5), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_dclk_vopl_fracmux __initdata = + MUX(0, "dclk_vopl_mux", mux_dclk_vopl_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(8), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_rtc32k_pmu_fracmux __initdata = + MUX(SCLK_RTC32K_PMU, "clk_rtc32k_pmu", mux_rtc32k_pmu_p, CLK_SET_RATE_PARENT, + PX30_PMU_CLKSEL_CON(0), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_uart0_pmu_fracmux __initdata = + MUX(0, "clk_uart0_pmu_mux", mux_uart0_pmu_p, CLK_SET_RATE_PARENT, + PX30_PMU_CLKSEL_CON(4), 14, 2, MFLAGS); + +static struct rockchip_clk_branch px30_clk_branches[] __initdata = { + /* + * Clock-Architecture Diagram 1 + */ + + MUX(USB480M, "usb480m", mux_usb480m_p, CLK_SET_RATE_PARENT, + PX30_MODE_CON, 8, 2, MFLAGS), + FACTOR(0, "xin12m", "xin24m", 0, 1, 2), + + /* + * Clock-Architecture Diagram 3 + */ + + /* PD_CORE */ + GATE(0, "apll_core", "apll", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 0, GFLAGS), + GATE(0, "gpll_core", "gpll", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 0, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_dbg", "armclk", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(0), 8, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, + PX30_CLKGATE_CON(0), 2, GFLAGS), + COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(0), 12, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, + PX30_CLKGATE_CON(0), 1, GFLAGS), + GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 4, GFLAGS), + GATE(0, "aclk_core_prf", "aclk_core", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(17), 5, GFLAGS), + GATE(0, "pclk_dbg_niu", "pclk_dbg", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 5, GFLAGS), + GATE(0, "pclk_core_dbg", "pclk_dbg", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 6, GFLAGS), + GATE(0, "pclk_core_grf", "pclk_dbg", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(17), 6, GFLAGS), + + GATE(0, "clk_jtag", "jtag_clkin", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 3, GFLAGS), + GATE(SCLK_PVTM, "clk_pvtm", "xin24m", 0, + PX30_CLKGATE_CON(17), 4, GFLAGS), + + /* PD_GPU */ + COMPOSITE_NODIV(0, "clk_gpu_src", mux_4plls_p, 0, + PX30_CLKSEL_CON(1), 6, 2, MFLAGS, + PX30_CLKGATE_CON(0), 8, GFLAGS), + COMPOSITE_NOMUX(0, "clk_gpu_div", "clk_gpu_src", 0, + PX30_CLKSEL_CON(1), 0, 4, DFLAGS, + PX30_CLKGATE_CON(0), 12, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_gpu_np5", "clk_gpu_src", 0, + PX30_CLKSEL_CON(1), 8, 4, DFLAGS, + PX30_CLKGATE_CON(0), 9, GFLAGS), + COMPOSITE_NODIV(SCLK_GPU, "clk_gpu", mux_gpu_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(1), 15, 1, MFLAGS, + PX30_CLKGATE_CON(0), 10, GFLAGS), + COMPOSITE_NOMUX(0, "aclk_gpu", "clk_gpu", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(1), 13, 2, DFLAGS, + PX30_CLKGATE_CON(17), 10, GFLAGS), + GATE(0, "aclk_gpu_niu", "aclk_gpu", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 11, GFLAGS), + GATE(0, "aclk_gpu_prf", "aclk_gpu", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(17), 8, GFLAGS), + GATE(0, "pclk_gpu_grf", "aclk_gpu", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(17), 9, GFLAGS), + + /* + * Clock-Architecture Diagram 4 + */ + + /* PD_DDR */ + GATE(0, "dpll_ddr", "dpll", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 7, GFLAGS), + GATE(0, "gpll_ddr", "gpll", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 13, GFLAGS), + COMPOSITE_NOGATE(SCLK_DDRCLK, "sclk_ddrc", mux_ddrphy_p, CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(2), 7, 1, MFLAGS, 0, 3, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + COMPOSITE_NOGATE(0, "clk_ddrphy4x", mux_ddrphy_p, CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(2), 7, 1, MFLAGS, 0, 3, DFLAGS), + FACTOR_GATE(0, "clk_ddrphy1x", "clk_ddrphy4x", CLK_IGNORE_UNUSED, 1, 4, + PX30_CLKGATE_CON(0), 14, GFLAGS), + FACTOR_GATE(0, "clk_stdby_2wrap", "clk_ddrphy4x", CLK_IGNORE_UNUSED, 1, 4, + PX30_CLKGATE_CON(1), 0, GFLAGS), + COMPOSITE_NODIV(0, "clk_ddrstdby", mux_ddrstdby_p, CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(2), 4, 1, MFLAGS, + PX30_CLKGATE_CON(1), 13, GFLAGS), + GATE(0, "aclk_split", "clk_ddrphy1x", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 15, GFLAGS), + GATE(0, "clk_msch", "clk_ddrphy1x", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 8, GFLAGS), + GATE(0, "aclk_ddrc", "clk_ddrphy1x", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 5, GFLAGS), + GATE(0, "clk_core_ddrc", "clk_ddrphy1x", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 6, GFLAGS), + GATE(0, "aclk_cmd_buff", "clk_ddrphy1x", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 6, GFLAGS), + GATE(0, "clk_ddrmon", "clk_ddrphy1x", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 11, GFLAGS), + + GATE(0, "clk_ddrmon_timer", "xin24m", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(0), 15, GFLAGS), + + COMPOSITE_NOMUX(PCLK_DDR, "pclk_ddr", "gpll", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(2), 8, 5, DFLAGS, + PX30_CLKGATE_CON(1), 1, GFLAGS), + GATE(0, "pclk_ddrmon", "pclk_ddr", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 10, GFLAGS), + GATE(0, "pclk_ddrc", "pclk_ddr", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 7, GFLAGS), + GATE(0, "pclk_msch", "pclk_ddr", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 9, GFLAGS), + GATE(0, "pclk_stdby", "pclk_ddr", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 12, GFLAGS), + GATE(0, "pclk_ddr_grf", "pclk_ddr", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 14, GFLAGS), + GATE(0, "pclk_cmdbuff", "pclk_ddr", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(1), 3, GFLAGS), + + /* + * Clock-Architecture Diagram 5 + */ + + /* PD_VI */ + COMPOSITE(ACLK_VI_PRE, "aclk_vi_pre", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(11), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(4), 8, GFLAGS), + COMPOSITE_NOMUX(HCLK_VI_PRE, "hclk_vi_pre", "aclk_vi_pre", 0, + PX30_CLKSEL_CON(11), 8, 4, DFLAGS, + PX30_CLKGATE_CON(4), 12, GFLAGS), + COMPOSITE(SCLK_ISP, "clk_isp", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(12), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(4), 9, GFLAGS), + COMPOSITE(SCLK_CIF_OUT, "clk_cif_out", mux_cif_out_p, 0, + PX30_CLKSEL_CON(13), 6, 2, MFLAGS, 0, 6, DFLAGS, + PX30_CLKGATE_CON(4), 11, GFLAGS), + GATE(PCLK_ISP, "pclkin_isp", "ext_pclkin", 0, + PX30_CLKGATE_CON(4), 13, GFLAGS), + GATE(PCLK_CIF, "pclkin_cif", "ext_pclkin", 0, + PX30_CLKGATE_CON(4), 14, GFLAGS), + + /* + * Clock-Architecture Diagram 6 + */ + + /* PD_VO */ + COMPOSITE(ACLK_VO_PRE, "aclk_vo_pre", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(3), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(2), 0, GFLAGS), + COMPOSITE_NOMUX(HCLK_VO_PRE, "hclk_vo_pre", "aclk_vo_pre", 0, + PX30_CLKSEL_CON(3), 8, 4, DFLAGS, + PX30_CLKGATE_CON(2), 12, GFLAGS), + COMPOSITE_NOMUX(PCLK_VO_PRE, "pclk_vo_pre", "aclk_vo_pre", 0, + PX30_CLKSEL_CON(3), 12, 4, DFLAGS, + PX30_CLKGATE_CON(2), 13, GFLAGS), + COMPOSITE(SCLK_RGA_CORE, "clk_rga_core", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(4), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(2), 1, GFLAGS), + + COMPOSITE(SCLK_VOPB_PWM, "clk_vopb_pwm", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(7), 7, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(2), 5, GFLAGS), + COMPOSITE(0, "dclk_vopb_src", mux_cpll_npll_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + PX30_CLKSEL_CON(5), 11, 1, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(2), 2, GFLAGS), + COMPOSITE_FRACMUX(0, "dclk_vopb_frac", "dclk_vopb_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(6), 0, + PX30_CLKGATE_CON(2), 3, GFLAGS, + &px30_dclk_vopb_fracmux), + GATE(DCLK_VOPB, "dclk_vopb", "dclk_vopb_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(2), 4, GFLAGS), + COMPOSITE(0, "dclk_vopl_src", mux_npll_cpll_p, 0, + PX30_CLKSEL_CON(8), 11, 1, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(2), 6, GFLAGS), + COMPOSITE_FRACMUX(0, "dclk_vopl_frac", "dclk_vopl_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(9), 0, + PX30_CLKGATE_CON(2), 7, GFLAGS, + &px30_dclk_vopl_fracmux), + GATE(DCLK_VOPL, "dclk_vopl", "dclk_vopl_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(2), 8, GFLAGS), + + /* PD_VPU */ + COMPOSITE(0, "aclk_vpu_pre", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(10), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(4), 0, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_vpu_pre", "aclk_vpu_pre", 0, + PX30_CLKSEL_CON(10), 8, 4, DFLAGS, + PX30_CLKGATE_CON(4), 2, GFLAGS), + COMPOSITE(SCLK_CORE_VPU, "sclk_core_vpu", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(13), 14, 2, MFLAGS, 8, 5, DFLAGS, + PX30_CLKGATE_CON(4), 1, GFLAGS), + + /* + * Clock-Architecture Diagram 7 + */ + + COMPOSITE_NODIV(ACLK_PERI_SRC, "aclk_peri_src", mux_gpll_cpll_p, 0, + PX30_CLKSEL_CON(14), 15, 1, MFLAGS, + PX30_CLKGATE_CON(5), 7, GFLAGS), + COMPOSITE_NOMUX(ACLK_PERI_PRE, "aclk_peri_pre", "aclk_peri_src", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(14), 0, 5, DFLAGS, + PX30_CLKGATE_CON(5), 8, GFLAGS), + DIV(HCLK_PERI_PRE, "hclk_peri_pre", "aclk_peri_src", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(14), 8, 5, DFLAGS), + + /* PD_MMC_NAND */ + GATE(HCLK_MMC_NAND, "hclk_mmc_nand", "hclk_peri_pre", 0, + PX30_CLKGATE_CON(6), 0, GFLAGS), + COMPOSITE(SCLK_NANDC, "clk_nandc", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(5), 13, GFLAGS), + + COMPOSITE(SCLK_SDIO, "clk_sdio", mux_gpll_cpll_npll_xin24m_p, 0, + PX30_CLKSEL_CON(18), 14, 2, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 3, GFLAGS), + + COMPOSITE(SCLK_EMMC, "clk_emmc", mux_gpll_cpll_npll_xin24m_p, 0, + PX30_CLKSEL_CON(20), 14, 2, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 6, GFLAGS), + + COMPOSITE(SCLK_SFC, "clk_sfc", mux_gpll_cpll_p, 0, + PX30_CLKSEL_CON(22), 7, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(6), 7, GFLAGS), + + MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc", + PX30_SDMMC_CON0, 1), + MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc", + PX30_SDMMC_CON1, 1), + + MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio", + PX30_SDIO_CON0, 1), + MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio", + PX30_SDIO_CON1, 1), + + MMC(SCLK_EMMC_DRV, "emmc_drv", "clk_emmc", + PX30_EMMC_CON0, 1), + MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "clk_emmc", + PX30_EMMC_CON1, 1), + + /* PD_SDCARD */ + GATE(0, "hclk_sdmmc_pre", "hclk_peri_pre", 0, + PX30_CLKGATE_CON(6), 12, GFLAGS), + COMPOSITE(SCLK_SDMMC, "clk_sdmmc", mux_gpll_cpll_npll_xin24m_p, 0, + PX30_CLKSEL_CON(16), 14, 2, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 15, GFLAGS), + + /* PD_USB */ + GATE(HCLK_USB, "hclk_usb", "hclk_peri_pre", 0, + PX30_CLKGATE_CON(7), 2, GFLAGS), + GATE(SCLK_OTG_ADP, "clk_otg_adp", "clk_rtc32k_pmu", 0, + PX30_CLKGATE_CON(7), 3, GFLAGS), + + /* PD_GMAC */ + COMPOSITE(SCLK_GMAC_SRC, "clk_gmac_src", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(22), 14, 2, MFLAGS, 8, 5, DFLAGS, + PX30_CLKGATE_CON(7), 11, GFLAGS), + MUX(SCLK_GMAC, "clk_gmac", mux_gmac_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(23), 6, 1, MFLAGS), + GATE(SCLK_MAC_REF, "clk_mac_ref", "clk_gmac", 0, + PX30_CLKGATE_CON(7), 15, GFLAGS), + GATE(SCLK_GMAC_RX_TX, "clk_gmac_rx_tx", "clk_gmac", 0, + PX30_CLKGATE_CON(7), 13, GFLAGS), + FACTOR(0, "clk_gmac_rx_tx_div2", "clk_gmac_rx_tx", 0, 1, 2), + FACTOR(0, "clk_gmac_rx_tx_div20", "clk_gmac_rx_tx", 0, 1, 20), + MUX(SCLK_GMAC_RMII, "clk_gmac_rmii_sel", mux_gmac_rmii_sel_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(23), 7, 1, MFLAGS), + + GATE(0, "aclk_gmac_pre", "aclk_peri_pre", 0, + PX30_CLKGATE_CON(7), 10, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_gmac_pre", "aclk_gmac_pre", 0, + PX30_CLKSEL_CON(23), 0, 4, DFLAGS, + PX30_CLKGATE_CON(7), 12, GFLAGS), + + COMPOSITE(SCLK_MAC_OUT, "clk_mac_out", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 5, DFLAGS, + PX30_CLKGATE_CON(8), 5, GFLAGS), + + /* + * Clock-Architecture Diagram 8 + */ + + /* PD_BUS */ + COMPOSITE_NODIV(ACLK_BUS_SRC, "aclk_bus_src", mux_gpll_cpll_p, CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(23), 15, 1, MFLAGS, + PX30_CLKGATE_CON(8), 6, GFLAGS), + COMPOSITE_NOMUX(HCLK_BUS_PRE, "hclk_bus_pre", "aclk_bus_src", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(24), 0, 5, DFLAGS, + PX30_CLKGATE_CON(8), 8, GFLAGS), + COMPOSITE_NOMUX(ACLK_BUS_PRE, "aclk_bus_pre", "aclk_bus_src", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(23), 8, 5, DFLAGS, + PX30_CLKGATE_CON(8), 7, GFLAGS), + COMPOSITE_NOMUX(PCLK_BUS_PRE, "pclk_bus_pre", "aclk_bus_pre", CLK_IGNORE_UNUSED, + PX30_CLKSEL_CON(24), 8, 2, DFLAGS, + PX30_CLKGATE_CON(8), 9, GFLAGS), + GATE(0, "pclk_top_pre", "pclk_bus_pre", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(8), 10, GFLAGS), + + COMPOSITE(0, "clk_pdm_src", mux_gpll_xin24m_npll_p, 0, + PX30_CLKSEL_CON(26), 8, 2, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(9), 9, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_pdm_frac", "clk_pdm_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(27), 0, + PX30_CLKGATE_CON(9), 10, GFLAGS, + &px30_pdm_fracmux), + GATE(SCLK_PDM, "clk_pdm", "clk_pdm_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(9), 11, GFLAGS), + + COMPOSITE(0, "clk_i2s0_tx_src", mux_gpll_npll_p, 0, + PX30_CLKSEL_CON(28), 8, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(9), 12, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s0_tx_frac", "clk_i2s0_tx_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(29), 0, + PX30_CLKGATE_CON(9), 13, GFLAGS, + &px30_i2s0_tx_fracmux), + COMPOSITE_NODIV(SCLK_I2S0_TX, "clk_i2s0_tx", mux_i2s0_tx_rx_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(28), 12, 1, MFLAGS, + PX30_CLKGATE_CON(9), 14, GFLAGS), + COMPOSITE_NODIV(0, "clk_i2s0_tx_out_pre", mux_i2s0_tx_out_p, 0, + PX30_CLKSEL_CON(28), 14, 2, MFLAGS, + PX30_CLKGATE_CON(9), 15, GFLAGS), + GATE(SCLK_I2S0_TX_OUT, "clk_i2s0_tx_out", "clk_i2s0_tx_out_pre", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 8, CLK_GATE_HIWORD_MASK), + + COMPOSITE(0, "clk_i2s0_rx_src", mux_gpll_npll_p, 0, + PX30_CLKSEL_CON(58), 8, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(17), 0, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s0_rx_frac", "clk_i2s0_rx_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(59), 0, + PX30_CLKGATE_CON(17), 1, GFLAGS, + &px30_i2s0_rx_fracmux), + COMPOSITE_NODIV(SCLK_I2S0_RX, "clk_i2s0_rx", mux_i2s0_rx_tx_p, CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(58), 12, 1, MFLAGS, + PX30_CLKGATE_CON(17), 2, GFLAGS), + COMPOSITE_NODIV(0, "clk_i2s0_rx_out_pre", mux_i2s0_rx_out_p, 0, + PX30_CLKSEL_CON(58), 14, 2, MFLAGS, + PX30_CLKGATE_CON(17), 3, GFLAGS), + GATE(SCLK_I2S0_RX_OUT, "clk_i2s0_rx_out", "clk_i2s0_rx_out_pre", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 11, CLK_GATE_HIWORD_MASK), + + COMPOSITE(0, "clk_i2s1_src", mux_gpll_npll_p, 0, + PX30_CLKSEL_CON(30), 8, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(10), 0, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(31), 0, + PX30_CLKGATE_CON(10), 1, GFLAGS, + &px30_i2s1_fracmux), + GATE(SCLK_I2S1, "clk_i2s1", "clk_i2s1_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 2, GFLAGS), + COMPOSITE_NODIV(0, "clk_i2s1_out_pre", mux_i2s1_out_p, 0, + PX30_CLKSEL_CON(30), 15, 1, MFLAGS, + PX30_CLKGATE_CON(10), 3, GFLAGS), + GATE(SCLK_I2S1_OUT, "clk_i2s1_out", "clk_i2s1_out_pre", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 9, CLK_GATE_HIWORD_MASK), + + COMPOSITE(0, "clk_i2s2_src", mux_gpll_npll_p, 0, + PX30_CLKSEL_CON(32), 8, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(10), 4, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(33), 0, + PX30_CLKGATE_CON(10), 5, GFLAGS, + &px30_i2s2_fracmux), + GATE(SCLK_I2S2, "clk_i2s2", "clk_i2s2_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 6, GFLAGS), + COMPOSITE_NODIV(0, "clk_i2s2_out_pre", mux_i2s2_out_p, 0, + PX30_CLKSEL_CON(32), 15, 1, MFLAGS, + PX30_CLKGATE_CON(10), 7, GFLAGS), + GATE(SCLK_I2S2_OUT, "clk_i2s2_out", "clk_i2s2_out_pre", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 10, CLK_GATE_HIWORD_MASK), + + COMPOSITE(SCLK_UART1_SRC, "clk_uart1_src", mux_uart_src_p, CLK_SET_RATE_NO_REPARENT, + PX30_CLKSEL_CON(34), 14, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(10), 12, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_uart1_np5", "clk_uart1_src", 0, + PX30_CLKSEL_CON(35), 0, 5, DFLAGS, + PX30_CLKGATE_CON(10), 13, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(36), 0, + PX30_CLKGATE_CON(10), 14, GFLAGS, + &px30_uart1_fracmux), + GATE(SCLK_UART1, "clk_uart1", "clk_uart1_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(10), 15, GFLAGS), + + COMPOSITE(SCLK_UART2_SRC, "clk_uart2_src", mux_uart_src_p, 0, + PX30_CLKSEL_CON(37), 14, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 0, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_uart2_np5", "clk_uart2_src", 0, + PX30_CLKSEL_CON(38), 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 1, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(39), 0, + PX30_CLKGATE_CON(11), 2, GFLAGS, + &px30_uart2_fracmux), + GATE(SCLK_UART2, "clk_uart2", "clk_uart2_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(11), 3, GFLAGS), + + COMPOSITE(0, "clk_uart3_src", mux_uart_src_p, 0, + PX30_CLKSEL_CON(40), 14, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 4, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_uart3_np5", "clk_uart3_src", 0, + PX30_CLKSEL_CON(41), 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 5, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart3_frac", "clk_uart3_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(42), 0, + PX30_CLKGATE_CON(11), 6, GFLAGS, + &px30_uart3_fracmux), + GATE(SCLK_UART3, "clk_uart3", "clk_uart3_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(11), 7, GFLAGS), + + COMPOSITE(0, "clk_uart4_src", mux_uart_src_p, 0, + PX30_CLKSEL_CON(43), 14, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 8, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_uart4_np5", "clk_uart4_src", 0, + PX30_CLKSEL_CON(44), 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 9, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart4_frac", "clk_uart4_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(45), 0, + PX30_CLKGATE_CON(11), 10, GFLAGS, + &px30_uart4_fracmux), + GATE(SCLK_UART4, "clk_uart4", "clk_uart4_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(11), 11, GFLAGS), + + COMPOSITE(0, "clk_uart5_src", mux_uart_src_p, 0, + PX30_CLKSEL_CON(46), 14, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 12, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_uart5_np5", "clk_uart5_src", 0, + PX30_CLKSEL_CON(47), 0, 5, DFLAGS, + PX30_CLKGATE_CON(11), 13, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart5_frac", "clk_uart5_src", CLK_SET_RATE_PARENT, + PX30_CLKSEL_CON(48), 0, + PX30_CLKGATE_CON(11), 14, GFLAGS, + &px30_uart5_fracmux), + GATE(SCLK_UART5, "clk_uart5", "clk_uart5_mux", CLK_SET_RATE_PARENT, + PX30_CLKGATE_CON(11), 15, GFLAGS), + + COMPOSITE(SCLK_I2C0, "clk_i2c0", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(49), 7, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(12), 0, GFLAGS), + COMPOSITE(SCLK_I2C1, "clk_i2c1", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(49), 15, 1, MFLAGS, 8, 7, DFLAGS, + PX30_CLKGATE_CON(12), 1, GFLAGS), + COMPOSITE(SCLK_I2C2, "clk_i2c2", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(50), 7, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(12), 2, GFLAGS), + COMPOSITE(SCLK_I2C3, "clk_i2c3", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(50), 15, 1, MFLAGS, 8, 7, DFLAGS, + PX30_CLKGATE_CON(12), 3, GFLAGS), + COMPOSITE(SCLK_PWM0, "clk_pwm0", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(52), 7, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(12), 5, GFLAGS), + COMPOSITE(SCLK_PWM1, "clk_pwm1", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(52), 15, 1, MFLAGS, 8, 7, DFLAGS, + PX30_CLKGATE_CON(12), 6, GFLAGS), + COMPOSITE(SCLK_SPI0, "clk_spi0", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(53), 7, 1, MFLAGS, 0, 7, DFLAGS, + PX30_CLKGATE_CON(12), 7, GFLAGS), + COMPOSITE(SCLK_SPI1, "clk_spi1", mux_gpll_xin24m_p, 0, + PX30_CLKSEL_CON(53), 15, 1, MFLAGS, 8, 7, DFLAGS, + PX30_CLKGATE_CON(12), 8, GFLAGS), + + GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 0, + PX30_CLKGATE_CON(13), 0, GFLAGS), + GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 0, + PX30_CLKGATE_CON(13), 1, GFLAGS), + GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 0, + PX30_CLKGATE_CON(13), 2, GFLAGS), + GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 0, + PX30_CLKGATE_CON(13), 3, GFLAGS), + GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 0, + PX30_CLKGATE_CON(13), 4, GFLAGS), + GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 0, + PX30_CLKGATE_CON(13), 5, GFLAGS), + + COMPOSITE_NOMUX(SCLK_TSADC, "clk_tsadc", "xin24m", 0, + PX30_CLKSEL_CON(54), 0, 11, DFLAGS, + PX30_CLKGATE_CON(12), 9, GFLAGS), + COMPOSITE_NOMUX(SCLK_SARADC, "clk_saradc", "xin24m", 0, + PX30_CLKSEL_CON(55), 0, 11, DFLAGS, + PX30_CLKGATE_CON(12), 10, GFLAGS), + COMPOSITE_NOMUX(SCLK_OTP, "clk_otp", "xin24m", 0, + PX30_CLKSEL_CON(56), 0, 3, DFLAGS, + PX30_CLKGATE_CON(12), 11, GFLAGS), + COMPOSITE_NOMUX(SCLK_OTP_USR, "clk_otp_usr", "clk_otp", 0, + PX30_CLKSEL_CON(56), 4, 2, DFLAGS, + PX30_CLKGATE_CON(13), 6, GFLAGS), + + GATE(0, "clk_cpu_boost", "xin24m", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(12), 12, GFLAGS), + + /* PD_CRYPTO */ + GATE(0, "aclk_crypto_pre", "aclk_bus_pre", 0, + PX30_CLKGATE_CON(8), 12, GFLAGS), + GATE(0, "hclk_crypto_pre", "hclk_bus_pre", 0, + PX30_CLKGATE_CON(8), 13, GFLAGS), + COMPOSITE(SCLK_CRYPTO, "clk_crypto", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(25), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(8), 14, GFLAGS), + COMPOSITE(SCLK_CRYPTO_APK, "clk_crypto_apk", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(25), 14, 2, MFLAGS, 8, 5, DFLAGS, + PX30_CLKGATE_CON(8), 15, GFLAGS), + + /* + * Clock-Architecture Diagram 9 + */ + + /* PD_BUS_TOP */ + GATE(0, "pclk_top_niu", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 0, GFLAGS), + GATE(0, "pclk_top_cru", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 1, GFLAGS), + GATE(PCLK_OTP_PHY, "pclk_otp_phy", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 2, GFLAGS), + GATE(0, "pclk_ddrphy", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 3, GFLAGS), + GATE(PCLK_MIPIDSIPHY, "pclk_mipidsiphy", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 4, GFLAGS), + GATE(PCLK_MIPICSIPHY, "pclk_mipicsiphy", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 5, GFLAGS), + GATE(PCLK_USB_GRF, "pclk_usb_grf", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 6, GFLAGS), + GATE(0, "pclk_cpu_hoost", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 7, GFLAGS), + + /* PD_VI */ + GATE(0, "aclk_vi_niu", "aclk_vi_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(4), 15, GFLAGS), + GATE(ACLK_CIF, "aclk_cif", "aclk_vi_pre", 0, PX30_CLKGATE_CON(5), 1, GFLAGS), + GATE(ACLK_ISP, "aclk_isp", "aclk_vi_pre", 0, PX30_CLKGATE_CON(5), 3, GFLAGS), + GATE(0, "hclk_vi_niu", "hclk_vi_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(5), 0, GFLAGS), + GATE(HCLK_CIF, "hclk_cif", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 2, GFLAGS), + GATE(HCLK_ISP, "hclk_isp", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 4, GFLAGS), + + /* PD_VO */ + GATE(0, "aclk_vo_niu", "aclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 0, GFLAGS), + GATE(ACLK_VOPB, "aclk_vopb", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 3, GFLAGS), + GATE(ACLK_RGA, "aclk_rga", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 7, GFLAGS), + GATE(ACLK_VOPL, "aclk_vopl", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 5, GFLAGS), + + GATE(0, "hclk_vo_niu", "hclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 1, GFLAGS), + GATE(HCLK_VOPB, "hclk_vopb", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 4, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 8, GFLAGS), + GATE(HCLK_VOPL, "hclk_vopl", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 6, GFLAGS), + + GATE(0, "pclk_vo_niu", "pclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 2, GFLAGS), + GATE(PCLK_MIPI_DSI, "pclk_mipi_dsi", "pclk_vo_pre", 0, PX30_CLKGATE_CON(3), 9, GFLAGS), + + /* PD_BUS */ + GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 8, GFLAGS), + GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 11, GFLAGS), + GATE(ACLK_GIC, "aclk_gic", "aclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 12, GFLAGS), + GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, PX30_CLKGATE_CON(13), 15, GFLAGS), + + GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 9, GFLAGS), + GATE(0, "hclk_rom", "hclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 14, GFLAGS), + GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, PX30_CLKGATE_CON(14), 1, GFLAGS), + GATE(HCLK_I2S0, "hclk_i2s0", "hclk_bus_pre", 0, PX30_CLKGATE_CON(14), 2, GFLAGS), + GATE(HCLK_I2S1, "hclk_i2s1", "hclk_bus_pre", 0, PX30_CLKGATE_CON(14), 3, GFLAGS), + GATE(HCLK_I2S2, "hclk_i2s2", "hclk_bus_pre", 0, PX30_CLKGATE_CON(14), 4, GFLAGS), + + GATE(0, "pclk_bus_niu", "pclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 10, GFLAGS), + GATE(PCLK_DCF, "pclk_dcf", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 0, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 5, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 6, GFLAGS), + GATE(PCLK_UART3, "pclk_uart3", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 7, GFLAGS), + GATE(PCLK_UART4, "pclk_uart4", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 8, GFLAGS), + GATE(PCLK_UART5, "pclk_uart5", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 9, GFLAGS), + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 10, GFLAGS), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 11, GFLAGS), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 12, GFLAGS), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 13, GFLAGS), + GATE(PCLK_I2C4, "pclk_i2c4", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 14, GFLAGS), + GATE(PCLK_PWM0, "pclk_pwm0", "pclk_bus_pre", 0, PX30_CLKGATE_CON(14), 15, GFLAGS), + GATE(PCLK_PWM1, "pclk_pwm1", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 0, GFLAGS), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 1, GFLAGS), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 2, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 3, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 4, GFLAGS), + GATE(PCLK_TIMER, "pclk_timer", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 5, GFLAGS), + GATE(PCLK_OTP_NS, "pclk_otp_ns", "pclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(15), 6, GFLAGS), + GATE(PCLK_WDT_NS, "pclk_wdt_ns", "pclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(15), 7, GFLAGS), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 8, GFLAGS), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 9, GFLAGS), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus_pre", 0, PX30_CLKGATE_CON(15), 10, GFLAGS), + GATE(0, "pclk_grf", "pclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(15), 11, GFLAGS), + GATE(0, "pclk_sgrf", "pclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(15), 12, GFLAGS), + + /* PD_VPU */ + GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(4), 7, GFLAGS), + GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", 0, PX30_CLKGATE_CON(4), 6, GFLAGS), + GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(4), 5, GFLAGS), + GATE(ACLK_VPU, "aclk_vpu", "aclk_vpu_pre", 0, PX30_CLKGATE_CON(4), 4, GFLAGS), + + /* PD_CRYPTO */ + GATE(0, "hclk_crypto_niu", "hclk_crypto_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(9), 3, GFLAGS), + GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_crypto_pre", 0, PX30_CLKGATE_CON(9), 5, GFLAGS), + GATE(0, "aclk_crypto_niu", "aclk_crypto_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(9), 2, GFLAGS), + GATE(ACLK_CRYPTO, "aclk_crypto", "aclk_crypto_pre", 0, PX30_CLKGATE_CON(9), 4, GFLAGS), + + /* PD_SDCARD */ + GATE(0, "hclk_sdmmc_niu", "hclk_sdmmc_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(7), 0, GFLAGS), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_sdmmc_pre", 0, PX30_CLKGATE_CON(7), 1, GFLAGS), + + /* PD_PERI */ + GATE(0, "aclk_peri_niu", "aclk_peri_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(5), 9, GFLAGS), + + /* PD_MMC_NAND */ + GATE(HCLK_NANDC, "hclk_nandc", "hclk_mmc_nand", 0, PX30_CLKGATE_CON(5), 15, GFLAGS), + GATE(0, "hclk_mmc_nand_niu", "hclk_mmc_nand", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(6), 8, GFLAGS), + GATE(HCLK_SDIO, "hclk_sdio", "hclk_mmc_nand", 0, PX30_CLKGATE_CON(6), 9, GFLAGS), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_mmc_nand", 0, PX30_CLKGATE_CON(6), 10, GFLAGS), + GATE(HCLK_SFC, "hclk_sfc", "hclk_mmc_nand", 0, PX30_CLKGATE_CON(6), 11, GFLAGS), + + /* PD_USB */ + GATE(0, "hclk_usb_niu", "hclk_usb", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(7), 4, GFLAGS), + GATE(HCLK_OTG, "hclk_otg", "hclk_usb", 0, PX30_CLKGATE_CON(7), 5, GFLAGS), + GATE(HCLK_HOST, "hclk_host", "hclk_usb", 0, PX30_CLKGATE_CON(7), 6, GFLAGS), + GATE(HCLK_HOST_ARB, "hclk_host_arb", "hclk_usb", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(7), 8, GFLAGS), + + /* PD_GMAC */ + GATE(0, "aclk_gmac_niu", "aclk_gmac_pre", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(8), 0, GFLAGS), + GATE(ACLK_GMAC, "aclk_gmac", "aclk_gmac_pre", 0, + PX30_CLKGATE_CON(8), 2, GFLAGS), + GATE(0, "pclk_gmac_niu", "pclk_gmac_pre", CLK_IGNORE_UNUSED, + PX30_CLKGATE_CON(8), 1, GFLAGS), + GATE(PCLK_GMAC, "pclk_gmac", "pclk_gmac_pre", 0, + PX30_CLKGATE_CON(8), 3, GFLAGS), +}; + +static struct rockchip_clk_branch px30_clk_pmu_branches[] __initdata = { + /* + * Clock-Architecture Diagram 2 + */ + + COMPOSITE_FRACMUX(0, "clk_rtc32k_frac", "xin24m", CLK_IGNORE_UNUSED, + PX30_PMU_CLKSEL_CON(1), 0, + PX30_PMU_CLKGATE_CON(0), 13, GFLAGS, + &px30_rtc32k_pmu_fracmux), + + COMPOSITE_NOMUX(XIN24M_DIV, "xin24m_div", "xin24m", CLK_IGNORE_UNUSED, + PX30_PMU_CLKSEL_CON(0), 8, 5, DFLAGS, + PX30_PMU_CLKGATE_CON(0), 12, GFLAGS), + + COMPOSITE_NOMUX(0, "clk_wifi_pmu_src", "gpll", 0, + PX30_PMU_CLKSEL_CON(2), 8, 6, DFLAGS, + PX30_PMU_CLKGATE_CON(0), 14, GFLAGS), + COMPOSITE_NODIV(SCLK_WIFI_PMU, "clk_wifi_pmu", mux_wifi_pmu_p, CLK_SET_RATE_PARENT, + PX30_PMU_CLKSEL_CON(2), 15, 1, MFLAGS, + PX30_PMU_CLKGATE_CON(0), 15, GFLAGS), + + COMPOSITE(0, "clk_uart0_pmu_src", mux_uart_src_p, 0, + PX30_PMU_CLKSEL_CON(3), 14, 2, MFLAGS, 0, 5, DFLAGS, + PX30_PMU_CLKGATE_CON(1), 0, GFLAGS), + COMPOSITE_NOMUX_HALFDIV(0, "clk_uart0_np5", "clk_uart0_pmu_src", 0, + PX30_PMU_CLKSEL_CON(4), 0, 5, DFLAGS, + PX30_PMU_CLKGATE_CON(1), 1, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_pmu_src", CLK_SET_RATE_PARENT, + PX30_PMU_CLKSEL_CON(5), 0, + PX30_PMU_CLKGATE_CON(1), 2, GFLAGS, + &px30_uart0_pmu_fracmux), + GATE(SCLK_UART0_PMU, "clk_uart0_pmu", "clk_uart0_pmu_mux", CLK_SET_RATE_PARENT, + PX30_PMU_CLKGATE_CON(1), 3, GFLAGS), + + GATE(SCLK_PVTM_PMU, "clk_pvtm_pmu", "xin24m", 0, + PX30_PMU_CLKGATE_CON(1), 4, GFLAGS), + + COMPOSITE_NOMUX(PCLK_PMU_PRE, "pclk_pmu_pre", "gpll", 0, + PX30_PMU_CLKSEL_CON(0), 0, 5, DFLAGS, + PX30_PMU_CLKGATE_CON(0), 0, GFLAGS), + + COMPOSITE_NOMUX(SCLK_REF24M_PMU, "clk_ref24m_pmu", "gpll", 0, + PX30_PMU_CLKSEL_CON(2), 0, 6, DFLAGS, + PX30_PMU_CLKGATE_CON(1), 8, GFLAGS), + COMPOSITE_NODIV(SCLK_USBPHY_REF, "clk_usbphy_ref", mux_usbphy_ref_p, CLK_SET_RATE_PARENT, + PX30_PMU_CLKSEL_CON(2), 6, 1, MFLAGS, + PX30_PMU_CLKGATE_CON(1), 9, GFLAGS), + COMPOSITE_NODIV(SCLK_MIPIDSIPHY_REF, "clk_mipidsiphy_ref", mux_mipidsiphy_ref_p, CLK_SET_RATE_PARENT, + PX30_PMU_CLKSEL_CON(2), 7, 1, MFLAGS, + PX30_PMU_CLKGATE_CON(1), 10, GFLAGS), + + /* + * Clock-Architecture Diagram 9 + */ + + /* PD_PMU */ + GATE(0, "pclk_pmu_niu", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 1, GFLAGS), + GATE(0, "pclk_pmu_sgrf", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 2, GFLAGS), + GATE(0, "pclk_pmu_grf", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 3, GFLAGS), + GATE(0, "pclk_pmu", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 4, GFLAGS), + GATE(0, "pclk_pmu_mem", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 5, GFLAGS), + GATE(PCLK_GPIO0_PMU, "pclk_gpio0_pmu", "pclk_pmu_pre", 0, PX30_PMU_CLKGATE_CON(0), 6, GFLAGS), + GATE(PCLK_UART0_PMU, "pclk_uart0_pmu", "pclk_pmu_pre", 0, PX30_PMU_CLKGATE_CON(0), 7, GFLAGS), + GATE(0, "pclk_cru_pmu", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 8, GFLAGS), +}; + +static const char *const px30_pmucru_critical_clocks[] __initconst = { + "aclk_bus_pre", + "pclk_bus_pre", + "hclk_bus_pre", + "aclk_peri_pre", + "hclk_peri_pre", + "aclk_gpu_niu", + "pclk_top_pre", + "pclk_pmu_pre", + "hclk_usb_niu", + "pll_npll", + "usb480m", + "clk_uart2", + "pclk_uart2", +}; + +static void __init px30_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + void __iomem *reg_base; + struct clk *clk; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } + + /* aclk_dmac is controlled by sgrf_soc_con1[11]. */ + clk = clk_register_fixed_factor(NULL, "aclk_dmac", "aclk_bus_pre", 0, 1, 1); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock aclk_dmac: %ld\n", + __func__, PTR_ERR(clk)); + else + rockchip_clk_add_lookup(ctx, clk, ACLK_DMAC); + + rockchip_clk_register_plls(ctx, px30_pll_clks, + ARRAY_SIZE(px30_pll_clks), + PX30_GRF_SOC_STATUS0); + rockchip_clk_register_branches(ctx, px30_clk_branches, + ARRAY_SIZE(px30_clk_branches)); + + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &px30_cpuclk_data, px30_cpuclk_rates, + ARRAY_SIZE(px30_cpuclk_rates)); + + rockchip_register_softrst(np, 12, reg_base + PX30_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(ctx, PX30_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(px30_cru, "rockchip,px30-cru", px30_clk_init); + +static void __init px30_pmu_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru pmu region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, CLKPMU_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip pmu clk init failed\n", __func__); + return; + } + + rockchip_clk_register_plls(ctx, px30_pmu_pll_clks, + ARRAY_SIZE(px30_pmu_pll_clks), PX30_GRF_SOC_STATUS0); + + rockchip_clk_register_branches(ctx, px30_clk_pmu_branches, + ARRAY_SIZE(px30_clk_pmu_branches)); + + rockchip_clk_protect_critical(px30_pmucru_critical_clocks, + ARRAY_SIZE(px30_pmucru_critical_clocks)); + + rockchip_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(px30_cru_pmu, "rockchip,px30-pmucru", px30_pmu_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index bca10d618f0a..5a628148f3f0 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -631,7 +631,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { MUX(0, "clk_i2sout_src", mux_i2sch_p, CLK_SET_RATE_PARENT, RK3399_CLKSEL_CON(31), 0, 2, MFLAGS), COMPOSITE_NODIV(SCLK_I2S_8CH_OUT, "clk_i2sout", mux_i2sout_p, CLK_SET_RATE_PARENT, - RK3399_CLKSEL_CON(30), 8, 2, MFLAGS, + RK3399_CLKSEL_CON(31), 2, 1, MFLAGS, RK3399_CLKGATE_CON(8), 12, GFLAGS), /* uart */ @@ -1523,6 +1523,7 @@ static const char *const rk3399_pmucru_critical_clocks[] __initconst = { "pclk_pmu_src", "fclk_cm0s_src_pmu", "clk_timer_src_pmu", + "pclk_rkpwm_pmu", }; static void __init rk3399_clk_init(struct device_node *np) diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 326b3fa44f5d..c3ad92965823 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -492,6 +492,16 @@ void __init rockchip_clk_register_branches( list->gate_flags, flags, list->child, &ctx->lock); break; + case branch_half_divider: + clk = rockchip_clk_register_halfdiv(list->name, + list->parent_names, list->num_parents, + ctx->reg_base, list->muxdiv_offset, + list->mux_shift, list->mux_width, + list->mux_flags, list->div_shift, + list->div_width, list->div_flags, + list->gate_offset, list->gate_shift, + list->gate_flags, flags, &ctx->lock); + break; case branch_gate: flags |= CLK_SET_RATE_PARENT; diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index ef601dded32c..6b53fff4cc96 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -34,7 +34,46 @@ struct clk; #define HIWORD_UPDATE(val, mask, shift) \ ((val) << (shift) | (mask) << ((shift) + 16)) -/* register positions shared by RV1108, RK2928, RK3036, RK3066, RK3188 and RK3228 */ +/* register positions shared by PX30, RV1108, RK2928, RK3036, RK3066, RK3188 and RK3228 */ +#define BOOST_PLL_H_CON(x) ((x) * 0x4) +#define BOOST_CLK_CON 0x0008 +#define BOOST_BOOST_CON 0x000c +#define BOOST_SWITCH_CNT 0x0010 +#define BOOST_HIGH_PERF_CNT0 0x0014 +#define BOOST_HIGH_PERF_CNT1 0x0018 +#define BOOST_STATIS_THRESHOLD 0x001c +#define BOOST_SHORT_SWITCH_CNT 0x0020 +#define BOOST_SWITCH_THRESHOLD 0x0024 +#define BOOST_FSM_STATUS 0x0028 +#define BOOST_PLL_L_CON(x) ((x) * 0x4 + 0x2c) +#define BOOST_RECOVERY_MASK 0x1 +#define BOOST_RECOVERY_SHIFT 1 +#define BOOST_SW_CTRL_MASK 0x1 +#define BOOST_SW_CTRL_SHIFT 2 +#define BOOST_LOW_FREQ_EN_MASK 0x1 +#define BOOST_LOW_FREQ_EN_SHIFT 3 +#define BOOST_BUSY_STATE BIT(8) + +#define PX30_PLL_CON(x) ((x) * 0x4) +#define PX30_CLKSEL_CON(x) ((x) * 0x4 + 0x100) +#define PX30_CLKGATE_CON(x) ((x) * 0x4 + 0x200) +#define PX30_GLB_SRST_FST 0xb8 +#define PX30_GLB_SRST_SND 0xbc +#define PX30_SOFTRST_CON(x) ((x) * 0x4 + 0x300) +#define PX30_MODE_CON 0xa0 +#define PX30_MISC_CON 0xa4 +#define PX30_SDMMC_CON0 0x380 +#define PX30_SDMMC_CON1 0x384 +#define PX30_SDIO_CON0 0x388 +#define PX30_SDIO_CON1 0x38c +#define PX30_EMMC_CON0 0x390 +#define PX30_EMMC_CON1 0x394 + +#define PX30_PMU_PLL_CON(x) ((x) * 0x4) +#define PX30_PMU_CLKSEL_CON(x) ((x) * 0x4 + 0x40) +#define PX30_PMU_CLKGATE_CON(x) ((x) * 0x4 + 0x80) +#define PX30_PMU_MODE 0x0020 + #define RV1108_PLL_CON(x) ((x) * 0x4) #define RV1108_CLKSEL_CON(x) ((x) * 0x4 + 0x60) #define RV1108_CLKGATE_CON(x) ((x) * 0x4 + 0x120) @@ -354,6 +393,7 @@ enum rockchip_clk_branch_type { branch_inverter, branch_factor, branch_ddrclk, + branch_half_divider, }; struct rockchip_clk_branch { @@ -684,6 +724,79 @@ struct rockchip_clk_branch { .gate_flags = gf, \ } +#define COMPOSITE_HALFDIV(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\ + df, go, gs, gf) \ + { \ + .id = _id, \ + .branch_type = branch_half_divider, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = f, \ + .muxdiv_offset = mo, \ + .mux_shift = ms, \ + .mux_width = mw, \ + .mux_flags = mf, \ + .div_shift = ds, \ + .div_width = dw, \ + .div_flags = df, \ + .gate_offset = go, \ + .gate_shift = gs, \ + .gate_flags = gf, \ + } + +#define COMPOSITE_NOGATE_HALFDIV(_id, cname, pnames, f, mo, ms, mw, mf, \ + ds, dw, df) \ + { \ + .id = _id, \ + .branch_type = branch_half_divider, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = f, \ + .muxdiv_offset = mo, \ + .mux_shift = ms, \ + .mux_width = mw, \ + .mux_flags = mf, \ + .div_shift = ds, \ + .div_width = dw, \ + .div_flags = df, \ + .gate_offset = -1, \ + } + +#define COMPOSITE_NOMUX_HALFDIV(_id, cname, pname, f, mo, ds, dw, df, \ + go, gs, gf) \ + { \ + .id = _id, \ + .branch_type = branch_half_divider, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .num_parents = 1, \ + .flags = f, \ + .muxdiv_offset = mo, \ + .div_shift = ds, \ + .div_width = dw, \ + .div_flags = df, \ + .gate_offset = go, \ + .gate_shift = gs, \ + .gate_flags = gf, \ + } + +#define DIV_HALF(_id, cname, pname, f, o, s, w, df) \ + { \ + .id = _id, \ + .branch_type = branch_half_divider, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .num_parents = 1, \ + .flags = f, \ + .muxdiv_offset = o, \ + .div_shift = s, \ + .div_width = w, \ + .div_flags = df, \ + .gate_offset = -1, \ + } + struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_clks); void rockchip_clk_of_add_provider(struct device_node *np, @@ -708,6 +821,17 @@ void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx, #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) +struct clk *rockchip_clk_register_halfdiv(const char *name, + 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, int gate_offset, + u8 gate_shift, u8 gate_flags, + unsigned long flags, + spinlock_t *lock); + #ifdef CONFIG_RESET_CONTROLLER void rockchip_register_softrst(struct device_node *np, unsigned int num_regs, diff --git a/drivers/clk/samsung/clk-exynos4412-isp.c b/drivers/clk/samsung/clk-exynos4412-isp.c index d5f1ccb36300..cfaa057035ad 100644 --- a/drivers/clk/samsung/clk-exynos4412-isp.c +++ b/drivers/clk/samsung/clk-exynos4412-isp.c @@ -37,8 +37,6 @@ static const unsigned long exynos4x12_clk_isp_save[] __initconst = { E4X12_GATE_ISP1, }; -PNAME(mout_user_aclk400_mcuisp_p4x12) = { "fin_pll", "div_aclk400_mcuisp", }; - static struct samsung_div_clock exynos4x12_isp_div_clks[] = { DIV(CLK_ISP_DIV_ISP0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), DIV(CLK_ISP_DIV_ISP1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), diff --git a/drivers/clk/socfpga/clk-s10.c b/drivers/clk/socfpga/clk-s10.c index 72714633e39c..5b238fc314ac 100644 --- a/drivers/clk/socfpga/clk-s10.c +++ b/drivers/clk/socfpga/clk-s10.c @@ -28,7 +28,7 @@ static const char * const emaca_free_mux[] = {"peri_emaca_clk", "boot_clk"}; static const char * const emacb_free_mux[] = {"peri_emacb_clk", "boot_clk"}; static const char * const emac_ptp_free_mux[] = {"peri_emac_ptp_clk", "boot_clk"}; static const char * const gpio_db_free_mux[] = {"peri_gpio_db_clk", "boot_clk"}; -static const char * const sdmmc_free_mux[] = {"peri_sdmmc_clk", "boot_clk"}; +static const char * const sdmmc_free_mux[] = {"main_sdmmc_clk", "boot_clk"}; static const char * const s2f_usr1_free_mux[] = {"peri_s2f_usr1_clk", "boot_clk"}; static const char * const psi_ref_free_mux[] = {"peri_psi_ref_clk", "boot_clk"}; static const char * const mpu_mux[] = { "mpu_free_clk", "boot_clk",}; @@ -37,6 +37,11 @@ static const char * const s2f_usr0_mux[] = {"f2s_free_clk", "boot_clk"}; static const char * const emac_mux[] = {"emaca_free_clk", "emacb_free_clk"}; static const char * const noc_mux[] = {"noc_free_clk", "boot_clk"}; +static const char * const mpu_free_mux[] = {"main_mpu_base_clk", + "peri_mpu_base_clk", + "osc1", "cb_intosc_hs_div2_clk", + "f2s_free_clk"}; + /* clocks in AO (always on) controller */ static const struct stratix10_pll_clock s10_pll_clks[] = { { STRATIX10_BOOT_CLK, "boot_clk", boot_mux, ARRAY_SIZE(boot_mux), 0, @@ -57,7 +62,7 @@ static const struct stratix10_perip_c_clock s10_main_perip_c_clks[] = { }; static const struct stratix10_perip_cnt_clock s10_main_perip_cnt_clks[] = { - { STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux), + { STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, mpu_free_mux, ARRAY_SIZE(mpu_free_mux), 0, 0x48, 0, 0, 0}, { STRATIX10_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux), 0, 0x4C, 0, 0, 0}, diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c index 468d1abaf0ee..bae5ee67a797 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c @@ -289,16 +289,13 @@ static const struct of_device_id sunxi_de2_clk_ids[] = { .data = &sun8i_v3s_de2_clk_desc, }, { + .compatible = "allwinner,sun50i-a64-de2-clk", + .data = &sun50i_a64_de2_clk_desc, + }, + { .compatible = "allwinner,sun50i-h5-de2-clk", .data = &sun50i_a64_de2_clk_desc, }, - /* - * The Allwinner A64 SoC needs some bit to be poke in syscon to make - * DE2 really working. - * So there's currently no A64 compatible here. - * H5 shares the same reset line with A64, so here H5 is using the - * clock description of A64. - */ { } }; diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c index 65ba6455feb7..0f388f6944d5 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c @@ -66,17 +66,18 @@ static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", CLK_SET_RATE_UNGATE); /* TODO: The result of N/M is required to be in [8, 25] range. */ -static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0", - "osc24M", 0x0010, - 8, 7, /* N */ - 0, 4, /* M */ - BIT(24), /* frac enable */ - BIT(25), /* frac select */ - 270000000, /* frac rate 0 */ - 297000000, /* frac rate 1 */ - BIT(31), /* gate */ - BIT(28), /* lock */ - CLK_SET_RATE_UNGATE); +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN(pll_video0_clk, "pll-video0", + "osc24M", 0x0010, + 192000000, /* Minimum rate */ + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); /* TODO: The result of N/M is required to be in [8, 25] range. */ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", @@ -152,17 +153,18 @@ static struct ccu_nk pll_periph1_clk = { }; /* TODO: The result of N/M is required to be in [8, 25] range. */ -static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1", - "osc24M", 0x030, - 8, 7, /* N */ - 0, 4, /* M */ - BIT(24), /* frac enable */ - BIT(25), /* frac select */ - 270000000, /* frac rate 0 */ - 297000000, /* frac rate 1 */ - BIT(31), /* gate */ - BIT(28), /* lock */ - CLK_SET_RATE_UNGATE); +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN(pll_video1_clk, "pll-video1", + "osc24M", 0x030, + 192000000, /* Minimum rate */ + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); static struct ccu_nkm pll_sata_clk = { .enable = BIT(31), @@ -654,7 +656,8 @@ static SUNXI_CCU_GATE(dram_deinterlace_clk, "dram-deinterlace", "dram", static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" }; static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, - 0x104, 0, 4, 24, 3, BIT(31), 0); + 0x104, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); static SUNXI_CCU_M_WITH_MUX_GATE(mp_clk, "mp", de_parents, 0x108, 0, 4, 24, 3, BIT(31), 0); @@ -666,9 +669,11 @@ static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_parents, static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd1_clk, "tcon-lcd1", tcon_parents, 0x114, 24, 3, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_M_WITH_MUX_GATE(tcon_tv0_clk, "tcon-tv0", tcon_parents, - 0x118, 0, 4, 24, 3, BIT(31), 0); + 0x118, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); static SUNXI_CCU_M_WITH_MUX_GATE(tcon_tv1_clk, "tcon-tv1", tcon_parents, - 0x11c, 0, 4, 24, 3, BIT(31), 0); + 0x11c, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" }; @@ -698,7 +703,8 @@ static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" }; static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, - 0x150, 0, 4, 24, 2, BIT(31), 0); + 0x150, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", 0x154, BIT(31), 0); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.h b/drivers/clk/sunxi-ng/ccu-sun8i-r40.h index 0db8e1e97af8..db2a1243f9ff 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r40.h +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.h @@ -25,7 +25,9 @@ #define CLK_PLL_AUDIO_2X 4 #define CLK_PLL_AUDIO_4X 5 #define CLK_PLL_AUDIO_8X 6 -#define CLK_PLL_VIDEO0 7 + +/* PLL_VIDEO0 is exported */ + #define CLK_PLL_VIDEO0_2X 8 #define CLK_PLL_VE 9 #define CLK_PLL_DDR0 10 @@ -34,7 +36,9 @@ #define CLK_PLL_PERIPH0_2X 13 #define CLK_PLL_PERIPH1 14 #define CLK_PLL_PERIPH1_2X 15 -#define CLK_PLL_VIDEO1 16 + +/* PLL_VIDEO1 is exported */ + #define CLK_PLL_VIDEO1_2X 17 #define CLK_PLL_SATA 18 #define CLK_PLL_SATA_OUT 19 diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index b71692391bd6..6507acc843c7 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -8,6 +8,7 @@ obj-y += clk-periph-fixed.o obj-y += clk-periph-gate.o obj-y += clk-pll.o obj-y += clk-pll-out.o +obj-y += clk-sdmmc-mux.o obj-y += clk-super.o obj-y += clk-tegra-audio.o obj-y += clk-tegra-periph.o @@ -24,3 +25,4 @@ obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o obj-y += cvb.o obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o +obj-y += clk-utils.o diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index a896692b74ec..01dada561c10 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -586,9 +586,15 @@ static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec, unsigned int id = clkspec->args[0], i; struct tegra_bpmp *bpmp = data; - for (i = 0; i < bpmp->num_clocks; i++) - if (bpmp->clocks[i]->id == id) - return &bpmp->clocks[i]->hw; + for (i = 0; i < bpmp->num_clocks; i++) { + struct tegra_bpmp_clk *clk = bpmp->clocks[i]; + + if (!clk) + continue; + + if (clk->id == id) + return &clk->hw; + } return NULL; } diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 16e0aee14773..205fe8ff63f0 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -32,35 +32,15 @@ static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, unsigned long parent_rate) { - u64 divider_ux1 = parent_rate; - u8 flags = divider->flags; - int mul; - - if (!rate) - return 0; - - mul = get_mul(divider); - - if (!(flags & TEGRA_DIVIDER_INT)) - divider_ux1 *= mul; - - if (flags & TEGRA_DIVIDER_ROUND_UP) - divider_ux1 += rate - 1; - - do_div(divider_ux1, rate); - - if (flags & TEGRA_DIVIDER_INT) - divider_ux1 *= mul; + int div; - divider_ux1 -= mul; + div = div_frac_get(rate, parent_rate, divider->width, + divider->frac_width, divider->flags); - if ((s64)divider_ux1 < 0) + if (div < 0) return 0; - if (divider_ux1 > get_max_div(divider)) - return get_max_div(divider); - - return divider_ux1; + return div; } static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, @@ -194,6 +174,7 @@ static const struct clk_div_table mc_div_table[] = { struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, void __iomem *reg, spinlock_t *lock) { - return clk_register_divider_table(NULL, name, parent_name, 0, reg, - 16, 1, 0, mc_div_table, lock); + return clk_register_divider_table(NULL, name, parent_name, + CLK_IS_CRITICAL, reg, 16, 1, 0, + mc_div_table, lock); } diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 5234acd30e89..0621a3a82ea6 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -132,7 +132,7 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) timing = tegra->timings + i; if (timing->rate > req->max_rate) { - i = min(i, 1); + i = max(i, 1); req->rate = tegra->timings[i - 1].rate; return 0; } diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index b616e33c5255..de466b4446da 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -227,13 +227,11 @@ enum clk_id { tegra_clk_sdmmc1_9, tegra_clk_sdmmc2, tegra_clk_sdmmc2_8, - tegra_clk_sdmmc2_9, tegra_clk_sdmmc3, tegra_clk_sdmmc3_8, tegra_clk_sdmmc3_9, tegra_clk_sdmmc4, tegra_clk_sdmmc4_8, - tegra_clk_sdmmc4_9, tegra_clk_se, tegra_clk_soc_therm, tegra_clk_soc_therm_8, diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c new file mode 100644 index 000000000000..473d418533cb --- /dev/null +++ b/drivers/clk/tegra/clk-sdmmc-mux.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 NVIDIA CORPORATION. All rights reserved. + * + * based on clk-mux.c + * + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/types.h> + +#include "clk.h" + +#define DIV_MASK GENMASK(7, 0) +#define MUX_SHIFT 29 +#define MUX_MASK GENMASK(MUX_SHIFT + 2, MUX_SHIFT) +#define SDMMC_MUL 2 + +#define get_max_div(d) DIV_MASK +#define get_div_field(val) ((val) & DIV_MASK) +#define get_mux_field(val) (((val) & MUX_MASK) >> MUX_SHIFT) + +static const char * const mux_sdmmc_parents[] = { + "pll_p", "pll_c4_out2", "pll_c4_out0", "pll_c4_out1", "clk_m" +}; + +static const u8 mux_lj_idx[] = { + [0] = 0, [1] = 1, [2] = 2, [3] = 5, [4] = 6 +}; + +static const u8 mux_non_lj_idx[] = { + [0] = 0, [1] = 3, [2] = 7, [3] = 4, [4] = 6 +}; + +static u8 clk_sdmmc_mux_get_parent(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + int num_parents, i; + u32 src, val; + const u8 *mux_idx; + + num_parents = clk_hw_get_num_parents(hw); + + val = readl_relaxed(sdmmc_mux->reg); + src = get_mux_field(val); + if (get_div_field(val)) + mux_idx = mux_non_lj_idx; + else + mux_idx = mux_lj_idx; + + for (i = 0; i < num_parents; i++) { + if (mux_idx[i] == src) + return i; + } + + WARN(1, "Unknown parent selector %d\n", src); + + return 0; +} + +static int clk_sdmmc_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + u32 val; + + + val = readl_relaxed(sdmmc_mux->reg); + if (get_div_field(val)) + index = mux_non_lj_idx[index]; + else + index = mux_lj_idx[index]; + + val &= ~MUX_MASK; + val |= index << MUX_SHIFT; + + writel(val, sdmmc_mux->reg); + + return 0; +} + +static unsigned long clk_sdmmc_mux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + u32 val; + int div; + u64 rate = parent_rate; + + val = readl_relaxed(sdmmc_mux->reg); + div = get_div_field(val); + + div += SDMMC_MUL; + + rate *= SDMMC_MUL; + rate += div - 1; + do_div(rate, div); + + return rate; +} + +static int clk_sdmmc_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + int div; + unsigned long output_rate = req->best_parent_rate; + + req->rate = max(req->rate, req->min_rate); + req->rate = min(req->rate, req->max_rate); + + if (!req->rate) + return output_rate; + + div = div_frac_get(req->rate, output_rate, 8, 1, sdmmc_mux->div_flags); + if (div < 0) + div = 0; + + if (sdmmc_mux->div_flags & TEGRA_DIVIDER_ROUND_UP) + req->rate = DIV_ROUND_UP(output_rate * SDMMC_MUL, + div + SDMMC_MUL); + else + req->rate = output_rate * SDMMC_MUL / (div + SDMMC_MUL); + + return 0; +} + +static int clk_sdmmc_mux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + int div; + unsigned long flags = 0; + u32 val; + u8 src; + + div = div_frac_get(rate, parent_rate, 8, 1, sdmmc_mux->div_flags); + if (div < 0) + return div; + + if (sdmmc_mux->lock) + spin_lock_irqsave(sdmmc_mux->lock, flags); + + src = clk_sdmmc_mux_get_parent(hw); + if (div) + src = mux_non_lj_idx[src]; + else + src = mux_lj_idx[src]; + + val = src << MUX_SHIFT; + val |= div; + writel(val, sdmmc_mux->reg); + fence_udelay(2, sdmmc_mux->reg); + + if (sdmmc_mux->lock) + spin_unlock_irqrestore(sdmmc_mux->lock, flags); + + return 0; +} + +static int clk_sdmmc_mux_is_enabled(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + const struct clk_ops *gate_ops = sdmmc_mux->gate_ops; + struct clk_hw *gate_hw = &sdmmc_mux->gate.hw; + + __clk_hw_set_clk(gate_hw, hw); + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_sdmmc_mux_enable(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + const struct clk_ops *gate_ops = sdmmc_mux->gate_ops; + struct clk_hw *gate_hw = &sdmmc_mux->gate.hw; + + __clk_hw_set_clk(gate_hw, hw); + + return gate_ops->enable(gate_hw); +} + +static void clk_sdmmc_mux_disable(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + const struct clk_ops *gate_ops = sdmmc_mux->gate_ops; + struct clk_hw *gate_hw = &sdmmc_mux->gate.hw; + + gate_ops->disable(gate_hw); +} + +static const struct clk_ops tegra_clk_sdmmc_mux_ops = { + .get_parent = clk_sdmmc_mux_get_parent, + .set_parent = clk_sdmmc_mux_set_parent, + .determine_rate = clk_sdmmc_mux_determine_rate, + .recalc_rate = clk_sdmmc_mux_recalc_rate, + .set_rate = clk_sdmmc_mux_set_rate, + .is_enabled = clk_sdmmc_mux_is_enabled, + .enable = clk_sdmmc_mux_enable, + .disable = clk_sdmmc_mux_disable, +}; + +struct clk *tegra_clk_register_sdmmc_mux_div(const char *name, + void __iomem *clk_base, u32 offset, u32 clk_num, u8 div_flags, + unsigned long flags, void *lock) +{ + struct clk *clk; + struct clk_init_data init; + const struct tegra_clk_periph_regs *bank; + struct tegra_sdmmc_mux *sdmmc_mux; + + init.ops = &tegra_clk_sdmmc_mux_ops; + init.name = name; + init.flags = flags; + init.parent_names = mux_sdmmc_parents; + init.num_parents = ARRAY_SIZE(mux_sdmmc_parents); + + bank = get_reg_bank(clk_num); + if (!bank) + return ERR_PTR(-EINVAL); + + sdmmc_mux = kzalloc(sizeof(*sdmmc_mux), GFP_KERNEL); + if (!sdmmc_mux) + return ERR_PTR(-ENOMEM); + + /* Data in .init is copied by clk_register(), so stack variable OK */ + sdmmc_mux->hw.init = &init; + sdmmc_mux->reg = clk_base + offset; + sdmmc_mux->lock = lock; + sdmmc_mux->gate.clk_base = clk_base; + sdmmc_mux->gate.regs = bank; + sdmmc_mux->gate.enable_refcnt = periph_clk_enb_refcnt; + sdmmc_mux->gate.clk_num = clk_num; + sdmmc_mux->gate.flags = TEGRA_PERIPH_ON_APB; + sdmmc_mux->div_flags = div_flags; + sdmmc_mux->gate_ops = &tegra_clk_periph_gate_ops; + + clk = clk_register(NULL, &sdmmc_mux->hw); + if (IS_ERR(clk)) { + kfree(sdmmc_mux); + return clk; + } + + sdmmc_mux->gate.hw.clk = clk; + + return clk; +} diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 2acba2986bc6..38c4eb28c8bf 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -451,15 +451,6 @@ static u32 mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0_idx[] = { [0] = 0, [1] = 3, [2] = 4, [3] = 6, [4] = 7, }; -static const char *mux_pllp_clkm_pllc4_out2_out1_out0_lj[] = { - "pll_p", - "pll_c4_out2", "pll_c4_out0", /* LJ input */ - "pll_c4_out2", "pll_c4_out1", - "pll_c4_out1", /* LJ input */ - "clk_m", "pll_c4_out0" -}; -#define mux_pllp_clkm_pllc4_out2_out1_out0_lj_idx NULL - static const char *mux_pllp_pllc2_c_c3_clkm[] = { "pll_p", "pll_c2", "pll_c", "pll_c3", "clk_m" }; @@ -686,9 +677,7 @@ static struct tegra_periph_init_data periph_clks[] = { MUX("sdmmc3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3), MUX("sdmmc4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc4), MUX8("sdmmc1", mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_SDMMC1, 14, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc1_9), - MUX8("sdmmc2", mux_pllp_clkm_pllc4_out2_out1_out0_lj, CLK_SOURCE_SDMMC2, 9, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc2_9), MUX8("sdmmc3", mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3_9), - MUX8("sdmmc4", mux_pllp_clkm_pllc4_out2_out1_out0_lj, CLK_SOURCE_SDMMC4, 15, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc4_9), MUX("la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, TEGRA_PERIPH_ON_APB, tegra_clk_la), MUX("trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, TEGRA_PERIPH_ON_APB, tegra_clk_trace), MUX("owr", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, TEGRA_PERIPH_ON_APB, tegra_clk_owr), diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 0c69c7970950..b6cf28ca2ed2 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1267,7 +1267,7 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { { TEGRA124_CLK_I2S2, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 }, { TEGRA124_CLK_I2S3, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 }, { TEGRA124_CLK_I2S4, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 }, - { TEGRA124_CLK_VDE, TEGRA124_CLK_CLK_MAX, 600000000, 0 }, + { TEGRA124_CLK_VDE, TEGRA124_CLK_PLL_C3, 600000000, 0 }, { TEGRA124_CLK_HOST1X, TEGRA124_CLK_PLL_P, 136000000, 1 }, { TEGRA124_CLK_DSIALP, TEGRA124_CLK_PLL_P, 68000000, 0 }, { TEGRA124_CLK_DSIBLP, TEGRA124_CLK_PLL_P, 68000000, 0 }, @@ -1290,6 +1290,7 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { { 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 }, + { TEGRA124_CLK_VIC03, TEGRA124_CLK_PLL_C3, 0, 0 }, /* must be the last entry */ { TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 5435d01c636a..9eb1cb14fce1 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -44,6 +44,8 @@ #define CLK_SOURCE_EMC 0x19c #define CLK_SOURCE_SOR1 0x410 #define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC4 0x164 #define PLLC_BASE 0x80 #define PLLC_OUT 0x84 @@ -2286,11 +2288,9 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { [tegra_clk_rtc] = { .dt_id = TEGRA210_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA210_CLK_TIMER, .present = true }, [tegra_clk_uarta_8] = { .dt_id = TEGRA210_CLK_UARTA, .present = true }, - [tegra_clk_sdmmc2_9] = { .dt_id = TEGRA210_CLK_SDMMC2, .present = true }, [tegra_clk_i2s1] = { .dt_id = TEGRA210_CLK_I2S1, .present = true }, [tegra_clk_i2c1] = { .dt_id = TEGRA210_CLK_I2C1, .present = true }, [tegra_clk_sdmmc1_9] = { .dt_id = TEGRA210_CLK_SDMMC1, .present = true }, - [tegra_clk_sdmmc4_9] = { .dt_id = TEGRA210_CLK_SDMMC4, .present = true }, [tegra_clk_pwm] = { .dt_id = TEGRA210_CLK_PWM, .present = true }, [tegra_clk_i2s2] = { .dt_id = TEGRA210_CLK_I2S2, .present = true }, [tegra_clk_usbd] = { .dt_id = TEGRA210_CLK_USBD, .present = true }, @@ -3030,6 +3030,16 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, 0, NULL); clks[TEGRA210_CLK_ACLK] = clk; + clk = tegra_clk_register_sdmmc_mux_div("sdmmc2", clk_base, + CLK_SOURCE_SDMMC2, 9, + TEGRA_DIVIDER_ROUND_UP, 0, NULL); + clks[TEGRA210_CLK_SDMMC2] = clk; + + clk = tegra_clk_register_sdmmc_mux_div("sdmmc4", clk_base, + CLK_SOURCE_SDMMC4, 15, + TEGRA_DIVIDER_ROUND_UP, 0, NULL); + clks[TEGRA210_CLK_SDMMC4] = clk; + for (i = 0; i < ARRAY_SIZE(tegra210_periph); i++) { struct tegra_periph_init_data *init = &tegra210_periph[i]; struct clk **clkp; diff --git a/drivers/clk/tegra/clk-utils.c b/drivers/clk/tegra/clk-utils.c new file mode 100644 index 000000000000..1a5daae4e501 --- /dev/null +++ b/drivers/clk/tegra/clk-utils.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. + */ + +#include <asm/div64.h> + +#include "clk.h" + +#define div_mask(w) ((1 << (w)) - 1) + +int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width, + u8 frac_width, u8 flags) +{ + u64 divider_ux1 = parent_rate; + int mul; + + if (!rate) + return 0; + + mul = 1 << frac_width; + + if (!(flags & TEGRA_DIVIDER_INT)) + divider_ux1 *= mul; + + if (flags & TEGRA_DIVIDER_ROUND_UP) + divider_ux1 += rate - 1; + + do_div(divider_ux1, rate); + + if (flags & TEGRA_DIVIDER_INT) + divider_ux1 *= mul; + + if (divider_ux1 < mul) + return 0; + + divider_ux1 -= mul; + + if (divider_ux1 > div_mask(width)) + return div_mask(width); + + return divider_ux1; +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index e1f88463b600..d2c3a010f8e9 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -19,6 +19,7 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/delay.h> /** * struct tegra_clk_sync_source - external clock source from codec @@ -705,6 +706,32 @@ struct clk *tegra_clk_register_super_clk(const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 clk_super_flags, spinlock_t *lock); + +/** + * struct tegra_sdmmc_mux - switch divider with Low Jitter inputs for SDMMC + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register controlling mux and divider + * @flags: hardware-specific flags + * @lock: optional register lock + * @gate: gate clock + * @gate_ops: gate clock ops + */ +struct tegra_sdmmc_mux { + struct clk_hw hw; + void __iomem *reg; + spinlock_t *lock; + const struct clk_ops *gate_ops; + struct tegra_clk_periph_gate gate; + u8 div_flags; +}; + +#define to_clk_sdmmc_mux(_hw) container_of(_hw, struct tegra_sdmmc_mux, hw) + +struct clk *tegra_clk_register_sdmmc_mux_div(const char *name, + void __iomem *clk_base, u32 offset, u32 clk_num, u8 div_flags, + unsigned long flags, void *lock); + /** * struct clk_init_table - clock initialization table * @clk_id: clock id as mentioned in device tree bindings @@ -811,6 +838,9 @@ extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; int tegra_pll_wait_for_lock(struct tegra_clk_pll *pll); u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate); int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div); +int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width, + u8 frac_width, u8 flags); + /* Combined read fence with delay */ #define fence_udelay(delay, reg) \ diff --git a/drivers/clk/uniphier/clk-uniphier-peri.c b/drivers/clk/uniphier/clk-uniphier-peri.c index 521c80e9a06f..89b3ac378b3f 100644 --- a/drivers/clk/uniphier/clk-uniphier-peri.c +++ b/drivers/clk/uniphier/clk-uniphier-peri.c @@ -27,6 +27,12 @@ #define UNIPHIER_PERI_CLK_FI2C(idx, ch) \ UNIPHIER_CLK_GATE("i2c" #ch, (idx), "i2c", 0x24, 24 + (ch)) +#define UNIPHIER_PERI_CLK_SCSSI(idx) \ + UNIPHIER_CLK_GATE("scssi", (idx), "spi", 0x20, 17) + +#define UNIPHIER_PERI_CLK_MCSSI(idx) \ + UNIPHIER_CLK_GATE("mcssi", (idx), "spi", 0x24, 14) + const struct uniphier_clk_data uniphier_ld4_peri_clk_data[] = { UNIPHIER_PERI_CLK_UART(0, 0), UNIPHIER_PERI_CLK_UART(1, 1), @@ -38,6 +44,7 @@ const struct uniphier_clk_data uniphier_ld4_peri_clk_data[] = { UNIPHIER_PERI_CLK_I2C(6, 2), UNIPHIER_PERI_CLK_I2C(7, 3), UNIPHIER_PERI_CLK_I2C(8, 4), + UNIPHIER_PERI_CLK_SCSSI(11), { /* sentinel */ } }; @@ -53,5 +60,7 @@ const struct uniphier_clk_data uniphier_pro4_peri_clk_data[] = { UNIPHIER_PERI_CLK_FI2C(8, 4), UNIPHIER_PERI_CLK_FI2C(9, 5), UNIPHIER_PERI_CLK_FI2C(10, 6), + UNIPHIER_PERI_CLK_SCSSI(11), + UNIPHIER_PERI_CLK_MCSSI(12), { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index 4f5ff9fa11fd..2bb5a8570adc 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -29,18 +29,20 @@ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 10), \ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15) -/* Denali driver requires clk_x rate (clk: 50MHz, clk_x & ecc_clk: 200MHz) */ #define UNIPHIER_LD4_SYS_CLK_NAND(idx) \ - UNIPHIER_CLK_FACTOR("nand-200m", -1, "spll", 1, 8), \ - UNIPHIER_CLK_GATE("nand", (idx), "nand-200m", 0x2104, 2) + UNIPHIER_CLK_FACTOR("nand-50m", -1, "spll", 1, 32), \ + UNIPHIER_CLK_GATE("nand", (idx), "nand-50m", 0x2104, 2) #define UNIPHIER_PRO5_SYS_CLK_NAND(idx) \ - UNIPHIER_CLK_FACTOR("nand-200m", -1, "spll", 1, 12), \ - UNIPHIER_CLK_GATE("nand", (idx), "nand-200m", 0x2104, 2) + UNIPHIER_CLK_FACTOR("nand-50m", -1, "spll", 1, 48), \ + UNIPHIER_CLK_GATE("nand", (idx), "nand-50m", 0x2104, 2) #define UNIPHIER_LD11_SYS_CLK_NAND(idx) \ - UNIPHIER_CLK_FACTOR("nand-200m", -1, "spll", 1, 10), \ - UNIPHIER_CLK_GATE("nand", (idx), "nand-200m", 0x210c, 0) + UNIPHIER_CLK_FACTOR("nand-50m", -1, "spll", 1, 40), \ + UNIPHIER_CLK_GATE("nand", (idx), "nand-50m", 0x210c, 0) + +#define UNIPHIER_SYS_CLK_NAND_4X(idx) \ + UNIPHIER_CLK_FACTOR("nand-4x", (idx), "nand", 4, 1) #define UNIPHIER_LD11_SYS_CLK_EMMC(idx) \ UNIPHIER_CLK_GATE("emmc", (idx), NULL, 0x210c, 2) @@ -93,7 +95,9 @@ const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 32), UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ @@ -108,7 +112,9 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("gpll", -1, "ref", 10, 1), /* 250 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32), + UNIPHIER_CLK_FACTOR("spi", 1, "spll", 1, 32), UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_PRO4_SYS_CLK_ETHER(6), @@ -118,6 +124,9 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), + UNIPHIER_CLK_FACTOR("usb30-hsphy0", 16, "upll", 1, 12), + UNIPHIER_CLK_FACTOR("usb30-ssphy0", 17, "ref", 1, 1), + UNIPHIER_CLK_FACTOR("usb31-ssphy0", 20, "ref", 1, 1), UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 18), UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x2104, 19), UNIPHIER_PRO4_SYS_CLK_AIO(40), @@ -130,7 +139,9 @@ const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 32), UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ @@ -143,7 +154,9 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("dapll2", -1, "dapll1", 144, 125), /* 2949.12 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "dapll2", 1, 40), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_PRO5_SYS_CLK_SD, UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC */ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */ @@ -158,7 +171,9 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("spll", -1, "ref", 96, 1), /* 2400 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 27), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_PRO5_SYS_CLK_SD, UNIPHIER_PRO4_SYS_CLK_ETHER(6), UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, RLE */ @@ -166,8 +181,11 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), /* The document mentions 0x2104 bit 18, but not functional */ - UNIPHIER_CLK_GATE("usb30-phy", 16, NULL, 0x2104, 19), - UNIPHIER_CLK_GATE("usb31-phy", 20, NULL, 0x2104, 20), + UNIPHIER_CLK_GATE("usb30-hsphy0", 16, NULL, 0x2104, 19), + UNIPHIER_CLK_FACTOR("usb30-ssphy0", 17, "ref", 1, 1), + UNIPHIER_CLK_FACTOR("usb30-ssphy1", 18, "ref", 1, 1), + UNIPHIER_CLK_GATE("usb31-hsphy0", 20, NULL, 0x2104, 20), + UNIPHIER_CLK_FACTOR("usb31-ssphy0", 21, "ref", 1, 1), UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 22), UNIPHIER_PRO5_SYS_CLK_AIO(40), { /* sentinel */ } @@ -180,7 +198,9 @@ const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vspll", -1, "ref", 80, 1), /* 2000 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 40), UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_LD11_SYS_CLK_EMMC(4), /* Index 5 reserved for eMMC PHY */ UNIPHIER_LD11_SYS_CLK_ETHER(6), @@ -213,7 +233,9 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vppll", -1, "ref", 504, 5), /* 2520 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 40), UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_LD11_SYS_CLK_EMMC(4), /* Index 5 reserved for eMMC PHY */ UNIPHIER_LD20_SYS_CLK_SD, @@ -226,8 +248,10 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { * We do not use bit 15 here. */ UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14), - UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12), - UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13), + UNIPHIER_CLK_GATE("usb30-hsphy0", 16, NULL, 0x210c, 12), + UNIPHIER_CLK_GATE("usb30-hsphy1", 17, NULL, 0x210c, 13), + UNIPHIER_CLK_FACTOR("usb30-ssphy0", 18, "ref", 1, 1), + UNIPHIER_CLK_FACTOR("usb30-ssphy1", 19, "ref", 1, 1), UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 4), UNIPHIER_LD11_SYS_CLK_AIO(40), UNIPHIER_LD11_SYS_CLK_EVEA(41), @@ -254,19 +278,21 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("s2pll", -1, "ref", 88, 1), /* IPP: 2400 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_CLK_FACTOR("spi", -1, "spll", 1, 40), UNIPHIER_LD20_SYS_CLK_SD, UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_SYS_CLK_NAND_4X(3), UNIPHIER_LD11_SYS_CLK_EMMC(4), UNIPHIER_CLK_GATE("ether0", 6, NULL, 0x210c, 9), UNIPHIER_CLK_GATE("ether1", 7, NULL, 0x210c, 10), UNIPHIER_CLK_GATE("usb30", 12, NULL, 0x210c, 4), /* =GIO0 */ UNIPHIER_CLK_GATE("usb31-0", 13, NULL, 0x210c, 5), /* =GIO1 */ UNIPHIER_CLK_GATE("usb31-1", 14, NULL, 0x210c, 6), /* =GIO1-1 */ - UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 16), - UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 18), - UNIPHIER_CLK_GATE("usb30-phy2", 18, NULL, 0x210c, 20), - UNIPHIER_CLK_GATE("usb31-phy0", 20, NULL, 0x210c, 17), - UNIPHIER_CLK_GATE("usb31-phy1", 21, NULL, 0x210c, 19), + UNIPHIER_CLK_GATE("usb30-hsphy0", 16, NULL, 0x210c, 16), + UNIPHIER_CLK_GATE("usb30-ssphy0", 17, NULL, 0x210c, 18), + UNIPHIER_CLK_GATE("usb30-ssphy1", 18, NULL, 0x210c, 20), + UNIPHIER_CLK_GATE("usb31-hsphy0", 20, NULL, 0x210c, 17), + UNIPHIER_CLK_GATE("usb31-ssphy0", 21, NULL, 0x210c, 19), UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 3), UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x210c, 7), UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x210c, 8), |