[v2,4/4] clk: Add Ingenic JZ4755 CGU driver
Commit Message
Add support for the clocks provided by the CGU in the Ingenic JZ4755
SoC.
Signed-off-by: Siarhei Volkau <lis8215@gmail.com>
---
drivers/clk/ingenic/Kconfig | 10 +
drivers/clk/ingenic/Makefile | 1 +
drivers/clk/ingenic/jz4755-cgu.c | 350 +++++++++++++++++++++++++++++++
3 files changed, 361 insertions(+)
create mode 100644 drivers/clk/ingenic/jz4755-cgu.c
Comments
Hi Siarhei,
Le dim., oct. 16 2022 at 18:01:09 +0300, Siarhei Volkau
<lis8215@gmail.com> a écrit :
> Add support for the clocks provided by the CGU in the Ingenic JZ4755
> SoC.
>
> Signed-off-by: Siarhei Volkau <lis8215@gmail.com>
> ---
> drivers/clk/ingenic/Kconfig | 10 +
> drivers/clk/ingenic/Makefile | 1 +
> drivers/clk/ingenic/jz4755-cgu.c | 350
> +++++++++++++++++++++++++++++++
> 3 files changed, 361 insertions(+)
> create mode 100644 drivers/clk/ingenic/jz4755-cgu.c
>
> diff --git a/drivers/clk/ingenic/Kconfig b/drivers/clk/ingenic/Kconfig
> index 898f1bc47..f80ac4f29 100644
> --- a/drivers/clk/ingenic/Kconfig
> +++ b/drivers/clk/ingenic/Kconfig
> @@ -15,6 +15,16 @@ config INGENIC_CGU_JZ4740
>
> If building for a JZ4740 SoC, you want to say Y here.
>
> +config INGENIC_CGU_JZ4755
> + bool "Ingenic JZ4755 CGU driver"
> + default MACH_JZ4755
> + select INGENIC_CGU_COMMON
> + help
> + Support the clocks provided by the CGU hardware on Ingenic JZ4755
> + and compatible SoCs.
> +
> + If building for a JZ4755 SoC, you want to say Y here.
> +
> config INGENIC_CGU_JZ4725B
> bool "Ingenic JZ4725B CGU driver"
> default MACH_JZ4725B
> diff --git a/drivers/clk/ingenic/Makefile
> b/drivers/clk/ingenic/Makefile
> index 9edfaf461..81d8e23c2 100644
> --- a/drivers/clk/ingenic/Makefile
> +++ b/drivers/clk/ingenic/Makefile
> @@ -1,6 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0-only
> obj-$(CONFIG_INGENIC_CGU_COMMON) += cgu.o pm.o
> obj-$(CONFIG_INGENIC_CGU_JZ4740) += jz4740-cgu.o
> +obj-$(CONFIG_INGENIC_CGU_JZ4755) += jz4755-cgu.o
> obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o
> obj-$(CONFIG_INGENIC_CGU_JZ4760) += jz4760-cgu.o
> obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o
> diff --git a/drivers/clk/ingenic/jz4755-cgu.c
> b/drivers/clk/ingenic/jz4755-cgu.c
> new file mode 100644
> index 000000000..16728546a
> --- /dev/null
> +++ b/drivers/clk/ingenic/jz4755-cgu.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Ingenic JZ4755 SoC CGU driver
> + * Heavily based on JZ4725b CGU driver
> + *
> + * Copyright (C) 2022 Siarhei Volkau
> + * Author: Siarhei Volkau <lis8215@gmail.com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/of.h>
> +
> +#include <dt-bindings/clock/ingenic,jz4755-cgu.h>
> +
> +#include "cgu.h"
> +#include "pm.h"
> +
> +/* CGU register offsets */
> +#define CGU_REG_CPCCR 0x00
> +#define CGU_REG_LCR 0x04
> +#define CGU_REG_CPPCR 0x10
> +#define CGU_REG_CLKGR 0x20
> +#define CGU_REG_OPCR 0x24
> +#define CGU_REG_I2SCDR 0x60
> +#define CGU_REG_LPCDR 0x64
> +#define CGU_REG_MSCCDR 0x68
> +#define CGU_REG_SSICDR 0x74
> +#define CGU_REG_CIMCDR 0x7C
> +
> +/* bits within the LCR register */
> +#define LCR_SLEEP BIT(0)
> +
> +static struct ingenic_cgu *cgu;
> +
> +static const s8 pll_od_encoding[4] = {
> + 0x0, 0x1, -1, 0x3,
> +};
> +
> +static const u8 jz4755_cgu_cpccr_div_table[] = {
> + 1, 2, 3, 4, 6, 8,
> +};
> +
> +static const u8 jz4755_cgu_pll_half_div_table[] = {
> + 2, 1,
> +};
> +
> +static const struct ingenic_cgu_clk_info jz4755_cgu_clocks[] = {
> +
> + /* External clocks */
> +
> + [JZ4755_CLK_EXT] = { "ext", CGU_CLK_EXT },
> + [JZ4755_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
> +
> + [JZ4755_CLK_PLL] = {
> + "pll", CGU_CLK_PLL,
> + .parents = { JZ4755_CLK_EXT, -1, -1, -1 },
> + .pll = {
> + .reg = CGU_REG_CPPCR,
> + .rate_multiplier = 1,
> + .m_shift = 23,
> + .m_bits = 9,
> + .m_offset = 2,
> + .n_shift = 18,
> + .n_bits = 5,
> + .n_offset = 2,
> + .od_shift = 16,
> + .od_bits = 2,
> + .od_max = 4,
> + .od_encoding = pll_od_encoding,
> + .stable_bit = 10,
> + .bypass_reg = CGU_REG_CPPCR,
> + .bypass_bit = 9,
> + .enable_bit = 8,
> + },
> + },
> +
> + /* Muxes & dividers */
> +
> + [JZ4755_CLK_PLL_HALF] = {
> + "pll half", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,
> + jz4755_cgu_pll_half_div_table,
> + },
> + },
> +
> + [JZ4755_CLK_EXT_HALF] = {
> + "ext half", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_EXT, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 30, 1, 1, -1, -1, -1, 0,
> + NULL,
> + },
> + },
> +
> + [JZ4755_CLK_CCLK] = {
> + "cclk", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
> + jz4755_cgu_cpccr_div_table,
> + },
> + },
> +
> + [JZ4755_CLK_H0CLK] = {
> + "hclk", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
> + jz4755_cgu_cpccr_div_table,
> + },
> + },
> +
> + [JZ4755_CLK_PCLK] = {
> + "pclk", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
> + jz4755_cgu_cpccr_div_table,
> + },
> + },
> +
> + [JZ4755_CLK_MCLK] = {
> + "mclk", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
> + jz4755_cgu_cpccr_div_table,
> + },
> + },
> +
> + [JZ4755_CLK_H1CLK] = {
> + "h1clk", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
> + .div = {
> + CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
> + jz4755_cgu_cpccr_div_table,
> + },
> + },
> +
> + [JZ4755_CLK_UDC] = {
> + "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, -1, -1 },
> + .mux = { CGU_REG_CPCCR, 29, 1 },
> + .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 10 },
> + },
> +
> + [JZ4755_CLK_LCD] = {
> + "lcd", CGU_CLK_DIV | CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
> + .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 9 },
> + },
> +
> + [JZ4755_CLK_MMC] = {
> + "mmc", CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
> + .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
> + },
> +
> + [JZ4755_CLK_I2S] = {
> + "i2s", CGU_CLK_MUX | CGU_CLK_DIV,
> + .parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, -1, -1 },
> + .mux = { CGU_REG_CPCCR, 31, 1 },
> + .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
> + },
> +
> + [JZ4755_CLK_SPI] = {
> + "spi", CGU_CLK_DIV | CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
> + .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 4 },
> + },
> +
> + [JZ4755_CLK_TVE] = {
> + "tve", CGU_CLK_MUX | CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_LCD, JZ4755_CLK_EXT, -1, -1 },
> + .mux = { CGU_REG_LPCDR, 31, 1 },
> + .gate = { CGU_REG_CLKGR, 18 },
> + },
> +
> + [JZ4755_CLK_RTC] = {
> + "rtc", CGU_CLK_MUX | CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT512, JZ4755_CLK_OSC32K, -1, -1 },
> + .mux = { CGU_REG_OPCR, 2, 1},
> + .gate = { CGU_REG_CLKGR, 2 },
> + },
> +
> + [JZ4755_CLK_CIM] = {
> + "cim", CGU_CLK_DIV | CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
> + .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 8 },
> + },
> +
> + /* Gate-only clocks */
> +
> + [JZ4755_CLK_UART0] = {
> + "uart0", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 0 },
> + },
> +
> + [JZ4755_CLK_UART1] = {
> + "uart1", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 14 },
> + },
> +
> + [JZ4755_CLK_UART2] = {
> + "uart2", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 15 },
> + },
> +
> + [JZ4755_CLK_ADC] = {
> + "adc", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 7 },
> + },
> +
> + [JZ4755_CLK_AIC] = {
> + "aic", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_I2S, -1, -1, -1 },
Wrong parent here, should be JZ4755_CLK_EXT_HALF.
> + .gate = { CGU_REG_CLKGR, 5 },
> + },
> +
> + [JZ4755_CLK_I2C] = {
> + "i2c", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 3 },
> + },
> +
> + [JZ4755_CLK_BCH] = {
> + "bch", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_MCLK/* not sure */, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 11 },
> + },
> +
> + [JZ4755_CLK_TCU] = {
> + "tcu", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 1 },
> + },
> +
> + [JZ4755_CLK_DMA] = {
> + "dma", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_PCLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 12 },
> + },
> +
> + [JZ4755_CLK_MMC0] = {
> + "mmc0", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_MMC, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 6 },
> + },
> +
> + [JZ4755_CLK_MMC1] = {
> + "mmc1", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_MMC, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 16 },
> + },
> +
> + [JZ4755_CLK_AUX_CPU] = {
> + "aux_cpu", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 24 },
> + },
> +
> + [JZ4755_CLK_AHB1] = {
> + "ahb1", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 23 },
> + },
> +
> + [JZ4755_CLK_IDCT] = {
> + "idct", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 22 },
> + },
> +
> + [JZ4755_CLK_DB] = {
> + "db", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 21 },
> + },
> +
> + [JZ4755_CLK_ME] = {
> + "me", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 20 },
> + },
> +
> + [JZ4755_CLK_MC] = {
> + "mc", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 19 },
> + },
> +
> + [JZ4755_CLK_TSSI] = {
> + "tssi", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF/* not sure */, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 17 },
> + },
> +
> + [JZ4755_CLK_IPU] = {
> + "ipu", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_PLL_HALF/* not sure */, -1, -1, -1 },
> + .gate = { CGU_REG_CLKGR, 13 },
> + },
> +
> + [JZ4755_CLK_EXT512] = {
> + "ext/512", CGU_CLK_FIXDIV,
> + .parents = { JZ4755_CLK_EXT },
> +
> + /* JZ4725b doc calls it EXT512, but it seems to be /256...
> + * Not sure if it applied to JZ4755 too, and which actual
> + * source is used EXT or EXT_HALF
> + */
Well it would be good to know. It's easy to verify though. Parent the
watchdog clock to EXT/512, configure it for e.g. 5 seconds, if it shuts
down in 10 seconds, it's a /256 divider.
Cheers,
-Paul
> + .fixdiv = { 256 },
> + },
> +
> + [JZ4755_CLK_UDC_PHY] = {
> + "udc_phy", CGU_CLK_GATE,
> + .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
> + .gate = { CGU_REG_OPCR, 6, true },
> + },
> +};
> +
> +static void __init jz4755_cgu_init(struct device_node *np)
> +{
> + int retval;
> +
> + cgu = ingenic_cgu_new(jz4755_cgu_clocks,
> + ARRAY_SIZE(jz4755_cgu_clocks), np);
> + if (!cgu) {
> + pr_err("%s: failed to initialise CGU\n", __func__);
> + return;
> + }
> +
> + retval = ingenic_cgu_register_clocks(cgu);
> + if (retval)
> + pr_err("%s: failed to register CGU Clocks\n", __func__);
> +
> + ingenic_cgu_register_syscore_ops(cgu);
> +}
> +CLK_OF_DECLARE_DRIVER(jz4755_cgu, "ingenic,jz4755-cgu",
> jz4755_cgu_init);
> --
> 2.36.1
>
пн, 17 окт. 2022 г. в 12:24, Paul Cercueil <paul@crapouillou.net>:
> > + [JZ4755_CLK_AIC] = {
> > + "aic", CGU_CLK_GATE,
> > + .parents = { JZ4755_CLK_I2S, -1, -1, -1 },
>
> Wrong parent here, should be JZ4755_CLK_EXT_HALF.
I don't agree, see Figure 20-13 in the JZ4755 PM.
> Well it would be good to know...
Indeed, I will try to figure it out.
Le lun., oct. 17 2022 at 20:10:56 +0300, Siarhei Volkau
<lis8215@gmail.com> a écrit :
> пн, 17 окт. 2022 г. в 12:24, Paul Cercueil
> <paul@crapouillou.net>:
>
>> > + [JZ4755_CLK_AIC] = {
>> > + "aic", CGU_CLK_GATE,
>> > + .parents = { JZ4755_CLK_I2S, -1, -1, -1 },
>>
>> Wrong parent here, should be JZ4755_CLK_EXT_HALF.
>
> I don't agree, see Figure 20-13 in the JZ4755 PM.
20-13 describes the I2S clock, no?
AIC clock's parent is EXT/2 according to the diagram in 8.2.2.
>> Well it would be good to know...
>
> Indeed, I will try to figure it out.
Cheers,
-Paul
пн, 17 окт. 2022 г. в 20:24, Paul Cercueil <paul@crapouillou.net>:
>
>
>
> Le lun., oct. 17 2022 at 20:10:56 +0300, Siarhei Volkau
> <lis8215@gmail.com> a écrit :
> > пн, 17 окт. 2022 г. в 12:24, Paul Cercueil
> > <paul@crapouillou.net>:
> >
> >> > + [JZ4755_CLK_AIC] = {
> >> > + "aic", CGU_CLK_GATE,
> >> > + .parents = { JZ4755_CLK_I2S, -1, -1, -1 },
> >>
> >> Wrong parent here, should be JZ4755_CLK_EXT_HALF.
> >
> > I don't agree, see Figure 20-13 in the JZ4755 PM.
>
> 20-13 describes the I2S clock, no?
See 20.4.9 Serial Audio Clocks and Sampling Frequencies.
It stated that: "For internal CODEC ... CODEC needs a 12MHz
clock from CPM called SYS_CLK ...", but SYS_CLK is described
only in the I2S Controller section. I assume it is the same clock.
>
> AIC clock's parent is EXT/2 according to the diagram in 8.2.2.
>
It's a bit cryptic manual, who knows how it's done in the HW.
I observed that codec runs on a desired sample rate only when PLL
equals 432 or 216 MHz, but SYS_CLK is definitely configured to be
12MHz - from EXTCLK. On other PLL frequencies it is lower by
2-4% than expected. That isn't observed on JZ4725B.
> >> Well it would be good to know...
> >
> > Indeed, I will try to figure it out.
>
> Cheers,
> -Paul
>
>
Hi Siarhei,
Le lun., oct. 17 2022 at 21:07:47 +0300, Siarhei Volkau
<lis8215@gmail.com> a écrit :
> пн, 17 окт. 2022 г. в 20:24, Paul Cercueil
> <paul@crapouillou.net>:
>>
>>
>>
>> Le lun., oct. 17 2022 at 20:10:56 +0300, Siarhei Volkau
>> <lis8215@gmail.com> a écrit :
>> > пн, 17 окт. 2022 г. в 12:24, Paul Cercueil
>> > <paul@crapouillou.net>:
>> >
>> >> > + [JZ4755_CLK_AIC] = {
>> >> > + "aic", CGU_CLK_GATE,
>> >> > + .parents = { JZ4755_CLK_I2S, -1, -1, -1 },
>> >>
>> >> Wrong parent here, should be JZ4755_CLK_EXT_HALF.
>> >
>> > I don't agree, see Figure 20-13 in the JZ4755 PM.
>>
>> 20-13 describes the I2S clock, no?
>
> See 20.4.9 Serial Audio Clocks and Sampling Frequencies.
> It stated that: "For internal CODEC ... CODEC needs a 12MHz
> clock from CPM called SYS_CLK ...", but SYS_CLK is described
> only in the I2S Controller section. I assume it is the same clock.
Yes, and your SYS_CLK is the I2S clock, not the AIC clock.
>>
>> AIC clock's parent is EXT/2 according to the diagram in 8.2.2.
>>
>
> It's a bit cryptic manual, who knows how it's done in the HW.
> I observed that codec runs on a desired sample rate only when PLL
> equals 432 or 216 MHz, but SYS_CLK is definitely configured to be
> 12MHz - from EXTCLK. On other PLL frequencies it is lower by
> 2-4% than expected. That isn't observed on JZ4725B.
The audio codec supplies SYS_CLK to the controller, not the other way
around. Parent the I2S clock to EXT/2 (which is 12 MHz) and it should
work fine, independently of the PLL.
The AIC clock does not drive anything, it only "powers" the AIC module.
It should be parented only to EXT/2, similar to what's done in every
other CGU driver.
Cheers,
-Paul
@@ -15,6 +15,16 @@ config INGENIC_CGU_JZ4740
If building for a JZ4740 SoC, you want to say Y here.
+config INGENIC_CGU_JZ4755
+ bool "Ingenic JZ4755 CGU driver"
+ default MACH_JZ4755
+ select INGENIC_CGU_COMMON
+ help
+ Support the clocks provided by the CGU hardware on Ingenic JZ4755
+ and compatible SoCs.
+
+ If building for a JZ4755 SoC, you want to say Y here.
+
config INGENIC_CGU_JZ4725B
bool "Ingenic JZ4725B CGU driver"
default MACH_JZ4725B
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_INGENIC_CGU_COMMON) += cgu.o pm.o
obj-$(CONFIG_INGENIC_CGU_JZ4740) += jz4740-cgu.o
+obj-$(CONFIG_INGENIC_CGU_JZ4755) += jz4755-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4760) += jz4760-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o
new file mode 100644
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ingenic JZ4755 SoC CGU driver
+ * Heavily based on JZ4725b CGU driver
+ *
+ * Copyright (C) 2022 Siarhei Volkau
+ * Author: Siarhei Volkau <lis8215@gmail.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/ingenic,jz4755-cgu.h>
+
+#include "cgu.h"
+#include "pm.h"
+
+/* CGU register offsets */
+#define CGU_REG_CPCCR 0x00
+#define CGU_REG_LCR 0x04
+#define CGU_REG_CPPCR 0x10
+#define CGU_REG_CLKGR 0x20
+#define CGU_REG_OPCR 0x24
+#define CGU_REG_I2SCDR 0x60
+#define CGU_REG_LPCDR 0x64
+#define CGU_REG_MSCCDR 0x68
+#define CGU_REG_SSICDR 0x74
+#define CGU_REG_CIMCDR 0x7C
+
+/* bits within the LCR register */
+#define LCR_SLEEP BIT(0)
+
+static struct ingenic_cgu *cgu;
+
+static const s8 pll_od_encoding[4] = {
+ 0x0, 0x1, -1, 0x3,
+};
+
+static const u8 jz4755_cgu_cpccr_div_table[] = {
+ 1, 2, 3, 4, 6, 8,
+};
+
+static const u8 jz4755_cgu_pll_half_div_table[] = {
+ 2, 1,
+};
+
+static const struct ingenic_cgu_clk_info jz4755_cgu_clocks[] = {
+
+ /* External clocks */
+
+ [JZ4755_CLK_EXT] = { "ext", CGU_CLK_EXT },
+ [JZ4755_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
+
+ [JZ4755_CLK_PLL] = {
+ "pll", CGU_CLK_PLL,
+ .parents = { JZ4755_CLK_EXT, -1, -1, -1 },
+ .pll = {
+ .reg = CGU_REG_CPPCR,
+ .rate_multiplier = 1,
+ .m_shift = 23,
+ .m_bits = 9,
+ .m_offset = 2,
+ .n_shift = 18,
+ .n_bits = 5,
+ .n_offset = 2,
+ .od_shift = 16,
+ .od_bits = 2,
+ .od_max = 4,
+ .od_encoding = pll_od_encoding,
+ .stable_bit = 10,
+ .bypass_reg = CGU_REG_CPPCR,
+ .bypass_bit = 9,
+ .enable_bit = 8,
+ },
+ },
+
+ /* Muxes & dividers */
+
+ [JZ4755_CLK_PLL_HALF] = {
+ "pll half", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,
+ jz4755_cgu_pll_half_div_table,
+ },
+ },
+
+ [JZ4755_CLK_EXT_HALF] = {
+ "ext half", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_EXT, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 30, 1, 1, -1, -1, -1, 0,
+ NULL,
+ },
+ },
+
+ [JZ4755_CLK_CCLK] = {
+ "cclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_H0CLK] = {
+ "hclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_PCLK] = {
+ "pclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_MCLK] = {
+ "mclk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_H1CLK] = {
+ "h1clk", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL, -1, -1, -1 },
+ .div = {
+ CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
+ jz4755_cgu_cpccr_div_table,
+ },
+ },
+
+ [JZ4755_CLK_UDC] = {
+ "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, -1, -1 },
+ .mux = { CGU_REG_CPCCR, 29, 1 },
+ .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 10 },
+ },
+
+ [JZ4755_CLK_LCD] = {
+ "lcd", CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
+ .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 9 },
+ },
+
+ [JZ4755_CLK_MMC] = {
+ "mmc", CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
+ .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
+ },
+
+ [JZ4755_CLK_I2S] = {
+ "i2s", CGU_CLK_MUX | CGU_CLK_DIV,
+ .parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, -1, -1 },
+ .mux = { CGU_REG_CPCCR, 31, 1 },
+ .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
+ },
+
+ [JZ4755_CLK_SPI] = {
+ "spi", CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
+ .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 4 },
+ },
+
+ [JZ4755_CLK_TVE] = {
+ "tve", CGU_CLK_MUX | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_LCD, JZ4755_CLK_EXT, -1, -1 },
+ .mux = { CGU_REG_LPCDR, 31, 1 },
+ .gate = { CGU_REG_CLKGR, 18 },
+ },
+
+ [JZ4755_CLK_RTC] = {
+ "rtc", CGU_CLK_MUX | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT512, JZ4755_CLK_OSC32K, -1, -1 },
+ .mux = { CGU_REG_OPCR, 2, 1},
+ .gate = { CGU_REG_CLKGR, 2 },
+ },
+
+ [JZ4755_CLK_CIM] = {
+ "cim", CGU_CLK_DIV | CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF, -1, -1, -1 },
+ .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 8 },
+ },
+
+ /* Gate-only clocks */
+
+ [JZ4755_CLK_UART0] = {
+ "uart0", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 0 },
+ },
+
+ [JZ4755_CLK_UART1] = {
+ "uart1", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 14 },
+ },
+
+ [JZ4755_CLK_UART2] = {
+ "uart2", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 15 },
+ },
+
+ [JZ4755_CLK_ADC] = {
+ "adc", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 7 },
+ },
+
+ [JZ4755_CLK_AIC] = {
+ "aic", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_I2S, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 5 },
+ },
+
+ [JZ4755_CLK_I2C] = {
+ "i2c", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 3 },
+ },
+
+ [JZ4755_CLK_BCH] = {
+ "bch", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_MCLK/* not sure */, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 11 },
+ },
+
+ [JZ4755_CLK_TCU] = {
+ "tcu", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 1 },
+ },
+
+ [JZ4755_CLK_DMA] = {
+ "dma", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PCLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 12 },
+ },
+
+ [JZ4755_CLK_MMC0] = {
+ "mmc0", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_MMC, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 6 },
+ },
+
+ [JZ4755_CLK_MMC1] = {
+ "mmc1", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_MMC, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 16 },
+ },
+
+ [JZ4755_CLK_AUX_CPU] = {
+ "aux_cpu", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 24 },
+ },
+
+ [JZ4755_CLK_AHB1] = {
+ "ahb1", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 23 },
+ },
+
+ [JZ4755_CLK_IDCT] = {
+ "idct", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 22 },
+ },
+
+ [JZ4755_CLK_DB] = {
+ "db", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 21 },
+ },
+
+ [JZ4755_CLK_ME] = {
+ "me", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 20 },
+ },
+
+ [JZ4755_CLK_MC] = {
+ "mc", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_H1CLK, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 19 },
+ },
+
+ [JZ4755_CLK_TSSI] = {
+ "tssi", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF/* not sure */, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 17 },
+ },
+
+ [JZ4755_CLK_IPU] = {
+ "ipu", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_PLL_HALF/* not sure */, -1, -1, -1 },
+ .gate = { CGU_REG_CLKGR, 13 },
+ },
+
+ [JZ4755_CLK_EXT512] = {
+ "ext/512", CGU_CLK_FIXDIV,
+ .parents = { JZ4755_CLK_EXT },
+
+ /* JZ4725b doc calls it EXT512, but it seems to be /256...
+ * Not sure if it applied to JZ4755 too, and which actual
+ * source is used EXT or EXT_HALF
+ */
+ .fixdiv = { 256 },
+ },
+
+ [JZ4755_CLK_UDC_PHY] = {
+ "udc_phy", CGU_CLK_GATE,
+ .parents = { JZ4755_CLK_EXT_HALF, -1, -1, -1 },
+ .gate = { CGU_REG_OPCR, 6, true },
+ },
+};
+
+static void __init jz4755_cgu_init(struct device_node *np)
+{
+ int retval;
+
+ cgu = ingenic_cgu_new(jz4755_cgu_clocks,
+ ARRAY_SIZE(jz4755_cgu_clocks), np);
+ if (!cgu) {
+ pr_err("%s: failed to initialise CGU\n", __func__);
+ return;
+ }
+
+ retval = ingenic_cgu_register_clocks(cgu);
+ if (retval)
+ pr_err("%s: failed to register CGU Clocks\n", __func__);
+
+ ingenic_cgu_register_syscore_ops(cgu);
+}
+CLK_OF_DECLARE_DRIVER(jz4755_cgu, "ingenic,jz4755-cgu", jz4755_cgu_init);