[PATCH 11/17] clk: sunxi-ng: add support for Allwinner R329 CCU

Samuel Holland samuel at sholland.org
Thu Aug 19 19:41:26 PDT 2021


On 8/2/21 1:22 AM, Icenowy Zheng wrote:
> Allwinner R329 has a CCU that is similar to the H616 one, but it's cut
> down and have PLLs moved out.
> 
> Add support for it.
> 
> Signed-off-by: Icenowy Zheng <icenowy at sipeed.com>
> ---
>  drivers/clk/sunxi-ng/Kconfig                |   5 +
>  drivers/clk/sunxi-ng/Makefile               |   1 +
>  drivers/clk/sunxi-ng/ccu-sun50i-r329.c      | 526 ++++++++++++++++++++
>  drivers/clk/sunxi-ng/ccu-sun50i-r329.h      |  32 ++
>  include/dt-bindings/clock/sun50i-r329-ccu.h |  73 +++
>  include/dt-bindings/reset/sun50i-r329-ccu.h |  45 ++
>  6 files changed, 682 insertions(+)
>  create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-r329.c
>  create mode 100644 drivers/clk/sunxi-ng/ccu-sun50i-r329.h
>  create mode 100644 include/dt-bindings/clock/sun50i-r329-ccu.h
>  create mode 100644 include/dt-bindings/reset/sun50i-r329-ccu.h
> 
> diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
> index e49b2c2fa5b7..4b32d5f81ea8 100644
> --- a/drivers/clk/sunxi-ng/Kconfig
> +++ b/drivers/clk/sunxi-ng/Kconfig
> @@ -42,6 +42,11 @@ config SUN50I_H6_R_CCU
>  	default ARM64 && ARCH_SUNXI
>  	depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
>  
> +config SUN50I_R329_CCU
> +	bool "Support for the Allwinner R329 CCU"
> +	default ARM64 && ARCH_SUNXI
> +	depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
> +
>  config SUN50I_R329_R_CCU
>  	bool "Support for the Allwinner R329 PRCM CCU"
>  	default ARM64 && ARCH_SUNXI
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index db338a2188fd..62f3c5bf331c 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_SUN50I_A100_R_CCU)	+= ccu-sun50i-a100-r.o
>  obj-$(CONFIG_SUN50I_H6_CCU)	+= ccu-sun50i-h6.o
>  obj-$(CONFIG_SUN50I_H616_CCU)	+= ccu-sun50i-h616.o
>  obj-$(CONFIG_SUN50I_H6_R_CCU)	+= ccu-sun50i-h6-r.o
> +obj-$(CONFIG_SUN50I_R329_CCU)	+= ccu-sun50i-r329.o
>  obj-$(CONFIG_SUN50I_R329_R_CCU)	+= ccu-sun50i-r329-r.o
>  obj-$(CONFIG_SUN4I_A10_CCU)	+= ccu-sun4i-a10.o
>  obj-$(CONFIG_SUN5I_CCU)		+= ccu-sun5i.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-r329.c b/drivers/clk/sunxi-ng/ccu-sun50i-r329.c
> new file mode 100644
> index 000000000000..a0b4cfd6e1db
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun50i-r329.c
> @@ -0,0 +1,526 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Based on the H616 CCU driver, which is:
> + *   Copyright (c) 2020 Arm Ltd.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +
> +#include "ccu_common.h"
> +#include "ccu_reset.h"
> +
> +#include "ccu_div.h"
> +#include "ccu_gate.h"
> +#include "ccu_mp.h"
> +#include "ccu_mult.h"
> +#include "ccu_nk.h"
> +#include "ccu_nkm.h"
> +#include "ccu_nkmp.h"
> +#include "ccu_nm.h"
> +
> +#include "ccu-sun50i-r329.h"
> +
> +/*
> + * An external divider of PLL-CPUX is controlled here. As it's similar to
> + * the external divider of PLL-CPUX on previous SoCs (only usable under
> + * 288MHz}, ignore it.

Mismatched (braces} here

> + */
> +static const char * const cpux_parents[] = { "osc24M", "osc32k", "iosc",
> +					     "pll-cpux", "pll-periph",
> +					     "pll-periph-2x",
> +					     "pll=periph-800m" };

= should be a -.

Now that these PLLs are in a different device, how is this supposed to affect
the DT binding? Do we put all of them in the clocks property?

If so, we can use .fw_name at some point. If not, why bother with the clocks
property at all? This is another part of the "let's get the clock tree right
from the start" discussion.

> +static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
> +		     0x500, 24, 3, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
> +static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x500, 0, 2, 0);
> +static SUNXI_CCU_M(cpux_apb_clk, "cpux-apb", "cpux", 0x500, 8, 2, 0);
> +
> +static const char * const ahb_parents[] = { "osc24M", "osc32k",
> +					    "iosc", "pll-periph" };
> +static SUNXI_CCU_MP_WITH_MUX(ahb_clk, "ahb",
> +			     ahb_parents, 0x510,
> +			     0, 2,	/* M */
> +			     8, 2,	/* P */
> +			     24, 3,	/* mux */
> +			     0);
> +
> +static const char * const apb_parents[] = { "osc24M", "osc32k",
> +					    "ahb", "pll-periph" };
> +static SUNXI_CCU_MP_WITH_MUX(apb1_clk, "apb1", apb_parents, 0x520,
> +			     0, 2,	/* M */
> +			     8, 2,	/* P */
> +			     24, 3,	/* mux */
> +			     0);
> +
> +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb_parents, 0x524,
> +			     0, 2,	/* M */
> +			     8, 2,	/* P */
> +			     24, 3,	/* mux */
> +			     0);

Hmm, so Allwinner is inconsistent with these. The manual has APB0/APB1, but the
BSP has APB1/APB2.

> +
> +static const char * const ce_parents[] = { "osc24M", "pll-periph-2x" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x680,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 1,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb",
> +		      0x68c, BIT(0), 0);
> +
> +static const char * const aipu_parents[] = { "pll-periph-2x", "pll-periph-800m",
> +					     "pll-audio0-div2", "pll-audio0-div5",
> +					     "pll-cpux" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(aipu_clk, "aipu", aipu_parents, 0x6f0,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 3,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_aipu_clk, "bus-aipu", "ahb",
> +		      0x6fc, BIT(0), 0);

The manual has separate gates for an AIPU AHB master at bit 1, and an AHB slave
at bit 0.

> +
> +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb",
> +		      0x70c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb",
> +		      0x71c, BIT(0), 0);

There are two message boxes (bits 0 and 1).

> +
> +static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb",
> +		      0x72c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb",
> +		      0x73c, BIT(0), 0);
> +
> +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x740, BIT(31), 0);
> +
> +static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb",
> +		      0x78c, BIT(0), 0);

There is a gate for the PSI clock (the parent for the AHB clocks) at 0x79c.

> +
> +static SUNXI_CCU_GATE(bus_pwm_clk, "bus-pwm", "apb1", 0x7ac, BIT(0), 0);
> +
> +static const char * const dram_parents[] = { "pll-periph-2x",
> +					     "pll-periph-800m",
> +					     "pll-audio0-div2",
> +					     "pll-audio0-div5" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(dram_clk, "dram", dram_parents, 0x800,
> +				  0, 2,		/* M */
> +				  8, 2,		/* P */
> +				  24, 2,	/* mux */
> +				  BIT(31),	/* gate */
> +				  CLK_IS_CRITICAL);
> +
> +
> +static SUNXI_CCU_GATE(mbus_dma_clk, "mbus-dma", "dram",
> +		      0x804, BIT(0), 0);
> +static SUNXI_CCU_GATE(mbus_ce_clk, "mbus-ce", "dram",
> +		      0x804, BIT(2), 0);
> +static SUNXI_CCU_GATE(mbus_r_dma_clk, "mbus-r-dma", "dram",
> +		      0x804, BIT(3), 0);
> +static SUNXI_CCU_GATE(mbus_nand_clk, "mbus-nand", "dram",
> +		      0x804, BIT(5), 0);
> +static SUNXI_CCU_GATE(mbus_aipu_clk, "mbus-aipu", "dram",
> +		      0x804, BIT(16), 0);
> +
> +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb",
> +		      0x80c, BIT(0), CLK_IS_CRITICAL);

The DRAM bus clock is not critical. It only needs to be enabled for register
access (PMU, DVFS).

> +
> +static const char * const nand_parents[] = { "osc24M", "pll-periph",
> +					     "pll-audio-div2",
> +					     "pll-periph-2x" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_clk, "nand0", nand_parents, 0x810,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 3,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_clk, "nand1", nand_parents, 0x814,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 3,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb", 0x82c, BIT(0), 0);
> +
> +static const char * const mmc_parents[] = { "osc24M", "pll-periph",
> +					    "pll-periph-2x",
> +					    "pll-audio0-div2" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc0_clk, "mmc0", mmc_parents, 0x830,
> +					  0, 4,		/* M */
> +					  8, 2,		/* P */
> +					  24, 2,	/* mux */
> +					  BIT(31),	/* gate */
> +					  2,		/* post-div */
> +					  0);
> +
> +static SUNXI_CCU_MP_WITH_MUX_GATE_POSTDIV(mmc1_clk, "mmc1", mmc_parents, 0x834,
> +					  0, 4,		/* M */
> +					  8, 2,		/* P */
> +					  24, 2,	/* mux */
> +					  BIT(31),	/* gate */
> +					  2,		/* post-div */

Where does the post-divider come from?

> +					  0);
> +
> +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb", 0x84c, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb", 0x84c, BIT(1), 0);
> +
> +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", 0x90c, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", 0x90c, BIT(1), 0);
> +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", 0x90c, BIT(2), 0);
> +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", 0x90c, BIT(3), 0);
> +
> +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", 0x91c, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", 0x91c, BIT(1), 0);
> +
> +static SUNXI_CCU_GATE(bus_scr_clk, "bus-scr", "apb2", 0x93c, BIT(0), 0);
> +
> +static const char * const spi_parents[] = { "osc24M", "pll-periph",
> +					    "pll-periph-2x",
> +					    "pll-audio0-div2",
> +					    "pll-audio0-div5" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", spi_parents, 0x940,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 3,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", spi_parents, 0x944,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 3,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb", 0x96c, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb", 0x96c, BIT(1), 0);
> +
> +static CLK_FIXED_FACTOR(emac_25m_div_clk, "emac-25m-div", "pll-periph",
> +			2, 1, 0);
> +static SUNXI_CCU_GATE(emac_25m_clk, "emac-25m", "emac-25m-div", 0x970,
> +		      BIT(31) | BIT(30), 0);

The divider should be 24, not 2. And you can use CCU_FEATURE_ALL_PREDIV on the
gate to remove the separate divider clock.

> +
> +static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb", 0x97c, BIT(0), 0);
> +
> +static const char * const ir_parents[] = { "osc32k", "iosc",

Both the manual and the BSP have the second parent as osc24M, not iosc.

> +					   "pll-periph", "pll-audio0-div2" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(ir_rx_clk, "ir-rx", ir_parents, 0x990,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 2,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_ir_rx_clk, "bus-ir-rx", "apb1", 0x99c, BIT(0), 0);
> +
> +static SUNXI_CCU_MP_WITH_MUX_GATE(ir_tx_clk, "ir-tx", ir_parents, 0x9c0,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 2,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_ir_tx_clk, "bus-ir-tx", "apb1", 0x9cc, BIT(0), 0);
> +
> +static const char * const audio_parents[] = { "pll-audio1",
> +					      "pll-audio1-4x",
> +					      "pll-audio0-div2",
> +					      "pll-audio0-div5" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(i2s0_clk, "i2s0", audio_parents, 0xa10,
> +				  0, 4,		/* M */

All of the audio modules have 5-bit M dividers.

> +				  8, 2,		/* P */
> +				  24, 2,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_MP_WITH_MUX_GATE(i2s1_clk, "i2s1", audio_parents, 0xa14,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 2,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", 0xa20, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", 0xa20, BIT(1), 0);
> +
> +static SUNXI_CCU_MP_WITH_MUX_GATE(spdif_clk, "spdif", audio_parents, 0xa20,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 2,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb1", 0xa2c, BIT(0), 0);
> +
> +/*
> + * There are OHCI 12M clock source selection bits for 2 USB 2.0 ports.
> + * We will force them to 0 (12M divided from 48M).
> + */
> +#define SUN50I_R329_USB0_CLK_REG		0xa70
> +#define SUN50I_R329_USB1_CLK_REG		0xa74

This "48M" clock is divided/synchronized from pll-periph, as shown in the
"Module Clock Generation" diagram and USB "PHY Connection Diagram" in the
manual. So I recommend the following structure:

static CLK_FIXED_FACTOR(usb_48M_clk, "usb-48M",
                        "pll-periph", 25, 2, 0);

static CLK_FIXED_FACTOR_HW(usb_12M_clk, "usb-12M",
                           &usb_48M_clk.hw, 4, 1, 0);

static const char *const usb_ohci_parents[] = { "usb-12M", "osc12M",
                                                "osc32k" };
static SUNXI_CCU_MUX_WITH_GATE(usb_ohci0_clk, "usb-ohci0",
                               usb_ohci_parents, 0xa70,
                               24, 2,           /* mux */
                               BIT(31),         /* gate */
                               0);

static SUNXI_CCU_MUX_WITH_GATE(usb_ohci1_clk, "usb-ohci1",
                               usb_ohci_parents, 0xa74,
                               24, 2,           /* mux */
                               BIT(31),         /* gate */
                               0);

For anyone following along without R329 documents, this is all the same as the
D1, though the labels in the diagrams are slightly different. Hooray for finally
documenting this so it is no longer "mysterious"!

Regards,
Samuel

> +
> +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc12M", 0xa70, BIT(31), 0);
> +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", 0xa70, BIT(29), 0);
> +
> +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "osc12M", 0xa74, BIT(31), 0);
> +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", 0xa74, BIT(29), 0);
> +
> +static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb", 0xa8c, BIT(0), 0);
> +static SUNXI_CCU_GATE(bus_ohci1_clk, "bus-ohci1", "ahb", 0xa8c, BIT(1), 0);
> +static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb", 0xa8c, BIT(4), 0);
> +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb", 0xa8c, BIT(8), 0);
> +
> +static const char * const ledc_parents[] = { "osc24M", "pll-periph" };
> +static SUNXI_CCU_MP_WITH_MUX_GATE(ledc_clk, "ledc", ledc_parents, 0xbf0,
> +				  0, 4,		/* M */
> +				  8, 2,		/* P */
> +				  24, 1,	/* mux */
> +				  BIT(31),	/* gate */
> +				  0);
> +
> +static SUNXI_CCU_GATE(bus_ledc_clk, "bus-ledc", "apb1", 0xbfc, BIT(0), 0);
> +
> +/* Fixed factor clocks */
> +static CLK_FIXED_FACTOR_FW_NAME(osc12M_clk, "osc12M", "hosc", 2, 1, 0);
> +
> +static struct ccu_common *sun50i_r329_ccu_clks[] = {
> +	&cpux_clk.common,
> +	&axi_clk.common,
> +	&cpux_apb_clk.common,
> +	&ahb_clk.common,
> +	&apb1_clk.common,
> +	&apb2_clk.common,
> +	&ce_clk.common,
> +	&bus_ce_clk.common,
> +	&aipu_clk.common,
> +	&bus_aipu_clk.common,
> +	&bus_dma_clk.common,
> +	&bus_msgbox_clk.common,
> +	&bus_spinlock_clk.common,
> +	&bus_hstimer_clk.common,
> +	&avs_clk.common,
> +	&bus_dbg_clk.common,
> +	&bus_pwm_clk.common,
> +	&dram_clk.common,
> +	&mbus_dma_clk.common,
> +	&mbus_ce_clk.common,
> +	&mbus_r_dma_clk.common,
> +	&mbus_nand_clk.common,
> +	&mbus_aipu_clk.common,
> +	&bus_dram_clk.common,
> +	&nand0_clk.common,
> +	&nand1_clk.common,
> +	&bus_nand_clk.common,
> +	&mmc0_clk.common,
> +	&mmc1_clk.common,
> +	&bus_mmc0_clk.common,
> +	&bus_mmc1_clk.common,
> +	&bus_uart0_clk.common,
> +	&bus_uart1_clk.common,
> +	&bus_uart2_clk.common,
> +	&bus_uart3_clk.common,
> +	&bus_i2c0_clk.common,
> +	&bus_i2c1_clk.common,
> +	&bus_scr_clk.common,
> +	&spi0_clk.common,
> +	&spi1_clk.common,
> +	&bus_spi0_clk.common,
> +	&bus_spi1_clk.common,
> +	&emac_25m_clk.common,
> +	&bus_emac_clk.common,
> +	&ir_rx_clk.common,
> +	&bus_ir_rx_clk.common,
> +	&ir_tx_clk.common,
> +	&bus_ir_tx_clk.common,
> +	&i2s0_clk.common,
> +	&i2s1_clk.common,
> +	&bus_i2s0_clk.common,
> +	&bus_i2s1_clk.common,
> +	&spdif_clk.common,
> +	&bus_spdif_clk.common,
> +	&usb_ohci0_clk.common,
> +	&usb_phy0_clk.common,
> +	&usb_ohci1_clk.common,
> +	&usb_phy1_clk.common,
> +	&bus_ohci0_clk.common,
> +	&bus_ohci1_clk.common,
> +	&bus_ehci0_clk.common,
> +	&bus_otg_clk.common,
> +	&ledc_clk.common,
> +	&bus_ledc_clk.common,
> +};
> +
> +static struct clk_hw_onecell_data sun50i_r329_hw_clks = {
> +	.hws	= {
> +		[CLK_OSC12M]		= &osc12M_clk.hw,
> +		[CLK_CPUX]		= &cpux_clk.common.hw,
> +		[CLK_AXI]		= &axi_clk.common.hw,
> +		[CLK_CPUX_APB]		= &cpux_apb_clk.common.hw,
> +		[CLK_AHB]		= &ahb_clk.common.hw,
> +		[CLK_APB1]		= &apb1_clk.common.hw,
> +		[CLK_APB2]		= &apb2_clk.common.hw,
> +		[CLK_CE]		= &ce_clk.common.hw,
> +		[CLK_BUS_CE]		= &bus_ce_clk.common.hw,
> +		[CLK_AIPU]		= &aipu_clk.common.hw,
> +		[CLK_BUS_AIPU]		= &bus_aipu_clk.common.hw,
> +		[CLK_BUS_DMA]		= &bus_dma_clk.common.hw,
> +		[CLK_BUS_MSGBOX]	= &bus_msgbox_clk.common.hw,
> +		[CLK_BUS_SPINLOCK]	= &bus_spinlock_clk.common.hw,
> +		[CLK_BUS_HSTIMER]	= &bus_hstimer_clk.common.hw,
> +		[CLK_AVS]		= &avs_clk.common.hw,
> +		[CLK_BUS_DBG]		= &bus_dbg_clk.common.hw,
> +		[CLK_BUS_PWM]		= &bus_pwm_clk.common.hw,
> +		[CLK_DRAM]		= &dram_clk.common.hw,
> +		[CLK_MBUS_DMA]		= &mbus_dma_clk.common.hw,
> +		[CLK_MBUS_CE]		= &mbus_ce_clk.common.hw,
> +		[CLK_MBUS_R_DMA]	= &mbus_r_dma_clk.common.hw,
> +		[CLK_MBUS_NAND]		= &mbus_nand_clk.common.hw,
> +		[CLK_MBUS_AIPU]		= &mbus_aipu_clk.common.hw,
> +		[CLK_BUS_DRAM]		= &bus_dram_clk.common.hw,
> +		[CLK_NAND0]		= &nand0_clk.common.hw,
> +		[CLK_NAND1]		= &nand1_clk.common.hw,
> +		[CLK_BUS_NAND]		= &bus_nand_clk.common.hw,
> +		[CLK_MMC0]		= &mmc0_clk.common.hw,
> +		[CLK_MMC1]		= &mmc1_clk.common.hw,
> +		[CLK_BUS_MMC0]		= &bus_mmc0_clk.common.hw,
> +		[CLK_BUS_MMC1]		= &bus_mmc1_clk.common.hw,
> +		[CLK_BUS_UART0]		= &bus_uart0_clk.common.hw,
> +		[CLK_BUS_UART1]		= &bus_uart1_clk.common.hw,
> +		[CLK_BUS_UART2]		= &bus_uart2_clk.common.hw,
> +		[CLK_BUS_UART3]		= &bus_uart3_clk.common.hw,
> +		[CLK_BUS_I2C0]		= &bus_i2c0_clk.common.hw,
> +		[CLK_BUS_I2C1]		= &bus_i2c1_clk.common.hw,
> +		[CLK_BUS_SCR]		= &bus_scr_clk.common.hw,
> +		[CLK_SPI0]		= &spi0_clk.common.hw,
> +		[CLK_SPI1]		= &spi1_clk.common.hw,
> +		[CLK_BUS_SPI0]		= &bus_spi0_clk.common.hw,
> +		[CLK_BUS_SPI1]		= &bus_spi1_clk.common.hw,
> +		[CLK_EMAC_25M_DIV]	= &emac_25m_div_clk.hw,
> +		[CLK_EMAC_25M]		= &emac_25m_clk.common.hw,
> +		[CLK_BUS_EMAC]		= &bus_emac_clk.common.hw,
> +		[CLK_IR_RX]		= &ir_rx_clk.common.hw,
> +		[CLK_BUS_IR_RX]		= &bus_ir_rx_clk.common.hw,
> +		[CLK_IR_TX]		= &ir_tx_clk.common.hw,
> +		[CLK_BUS_IR_TX]		= &bus_ir_tx_clk.common.hw,
> +		[CLK_I2S0]		= &i2s0_clk.common.hw,
> +		[CLK_I2S1]		= &i2s1_clk.common.hw,
> +		[CLK_BUS_I2S0]		= &bus_i2s0_clk.common.hw,
> +		[CLK_BUS_I2S1]		= &bus_i2s1_clk.common.hw,
> +		[CLK_SPDIF]		= &spdif_clk.common.hw,
> +		[CLK_BUS_SPDIF]		= &bus_spdif_clk.common.hw,
> +		[CLK_USB_OHCI0]		= &usb_ohci0_clk.common.hw,
> +		[CLK_USB_PHY0]		= &usb_phy0_clk.common.hw,
> +		[CLK_USB_OHCI1]		= &usb_ohci1_clk.common.hw,
> +		[CLK_USB_PHY1]		= &usb_phy1_clk.common.hw,
> +		[CLK_BUS_OHCI0]		= &bus_ohci0_clk.common.hw,
> +		[CLK_BUS_OHCI1]		= &bus_ohci1_clk.common.hw,
> +		[CLK_BUS_EHCI0]		= &bus_ehci0_clk.common.hw,
> +		[CLK_BUS_OTG]		= &bus_otg_clk.common.hw,
> +		[CLK_LEDC]		= &ledc_clk.common.hw,
> +		[CLK_BUS_LEDC]		= &bus_ledc_clk.common.hw,
> +	},
> +	.num = CLK_NUMBER,
> +};
> +
> +static struct ccu_reset_map sun50i_r329_ccu_resets[] = {
> +	[RST_MBUS]		= { 0x540, BIT(30) },
> +
> +	[RST_BUS_CE]		= { 0x68c, BIT(16) },
> +	[RST_BUS_AIPU]		= { 0x6fc, BIT(16) },
> +	[RST_BUS_DMA]		= { 0x70c, BIT(16) },
> +	[RST_BUS_MSGBOX]	= { 0x71c, BIT(16) },
> +	[RST_BUS_SPINLOCK]	= { 0x72c, BIT(16) },
> +	[RST_BUS_HSTIMER]	= { 0x73c, BIT(16) },
> +	[RST_BUS_DBG]		= { 0x78c, BIT(16) },
> +	[RST_BUS_PWM]		= { 0x7ac, BIT(16) },
> +	[RST_BUS_DRAM]		= { 0x80c, BIT(16) },
> +	[RST_BUS_NAND]		= { 0x82c, BIT(16) },
> +	[RST_BUS_MMC0]		= { 0x84c, BIT(16) },
> +	[RST_BUS_MMC1]		= { 0x84c, BIT(17) },
> +	[RST_BUS_UART0]		= { 0x90c, BIT(16) },
> +	[RST_BUS_UART1]		= { 0x90c, BIT(17) },
> +	[RST_BUS_UART2]		= { 0x90c, BIT(18) },
> +	[RST_BUS_UART3]		= { 0x90c, BIT(19) },
> +	[RST_BUS_I2C0]		= { 0x91c, BIT(16) },
> +	[RST_BUS_I2C1]		= { 0x91c, BIT(17) },
> +	[RST_BUS_SCR]		= { 0x93c, BIT(16) },
> +	[RST_BUS_SPI0]		= { 0x96c, BIT(16) },
> +	[RST_BUS_SPI1]		= { 0x96c, BIT(17) },
> +	[RST_BUS_EMAC]		= { 0x97c, BIT(16) },
> +	[RST_BUS_IR_RX]		= { 0x99c, BIT(16) },
> +	[RST_BUS_IR_TX]		= { 0x9cc, BIT(16) },
> +	[RST_BUS_I2S0]		= { 0xa1c, BIT(16) },
> +	[RST_BUS_I2S1]		= { 0xa1c, BIT(17) },
> +	[RST_BUS_SPDIF]		= { 0xa2c, BIT(16) },
> +
> +	[RST_USB_PHY0]		= { 0xa70, BIT(30) },
> +	[RST_USB_PHY1]		= { 0xa74, BIT(30) },
> +
> +	[RST_BUS_OHCI0]		= { 0xa8c, BIT(16) },
> +	[RST_BUS_OHCI1]		= { 0xa8c, BIT(17) },
> +	[RST_BUS_EHCI0]		= { 0xa8c, BIT(20) },
> +	[RST_BUS_OTG]		= { 0xa8c, BIT(24) },
> +
> +	[RST_BUS_LEDC]		= { 0xbfc, BIT(16) },
> +};
> +
> +static const struct sunxi_ccu_desc sun50i_r329_ccu_desc = {
> +	.ccu_clks	= sun50i_r329_ccu_clks,
> +	.num_ccu_clks	= ARRAY_SIZE(sun50i_r329_ccu_clks),
> +
> +	.hw_clks	= &sun50i_r329_hw_clks,
> +
> +	.resets		= sun50i_r329_ccu_resets,
> +	.num_resets	= ARRAY_SIZE(sun50i_r329_ccu_resets),
> +};
> +
> +static const u32 sun50i_r329_usb_clk_regs[] = {
> +	SUN50I_R329_USB0_CLK_REG,
> +	SUN50I_R329_USB1_CLK_REG,
> +};
> +
> +static int sun50i_r329_ccu_probe(struct platform_device *pdev)
> +{
> +	void __iomem *reg;
> +	u32 val;
> +	int i;
> +
> +	reg = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(reg))
> +		return PTR_ERR(reg);
> +
> +	/*
> +	 * Force OHCI 12M clock sources to 00 (12MHz divided from 48MHz)
> +	 *
> +	 * This clock mux is still mysterious, and the code just enforces
> +	 * it to have a valid clock parent.
> +	 */
> +	for (i = 0; i < ARRAY_SIZE(sun50i_r329_usb_clk_regs); i++) {
> +		val = readl(reg + sun50i_r329_usb_clk_regs[i]);
> +		val &= ~GENMASK(25, 24);
> +		writel(val, reg + sun50i_r329_usb_clk_regs[i]);
> +	}
> +
> +	return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_r329_ccu_desc);
> +}
> +
> +static const struct of_device_id sun50i_r329_ccu_ids[] = {
> +	{ .compatible = "allwinner,sun50i-r329-ccu" },
> +	{ }
> +};
> +
> +static struct platform_driver sun50i_r329_ccu_driver = {
> +	.probe	= sun50i_r329_ccu_probe,
> +	.driver	= {
> +		.name	= "sun50i-r329-ccu",
> +		.of_match_table	= sun50i_r329_ccu_ids,
> +	},
> +};
> +module_platform_driver(sun50i_r329_ccu_driver);
> diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-r329.h b/drivers/clk/sunxi-ng/ccu-sun50i-r329.h
> new file mode 100644
> index 000000000000..144ac9954ef3
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu-sun50i-r329.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Sipeed
> + */
> +
> +#ifndef _CCU_SUN50I_R329_H_
> +#define _CCU_SUN50I_R329_H_
> +
> +#include <dt-bindings/clock/sun50i-r329-ccu.h>
> +#include <dt-bindings/reset/sun50i-r329-ccu.h>
> +
> +#define CLK_OSC12M		0
> +
> +/* CPUX exported for DVFS */
> +
> +#define CLK_AXI			2
> +#define CLK_CPUX_APB		3
> +#define CLK_AHB			4
> +
> +/* APB1 exported for PIO */
> +
> +#define CLK_APB2		6
> +
> +/* Peripheral module and gate clock exported except for DRAM ones */
> +
> +#define CLK_DRAM		18
> +
> +#define CLK_BUS_DRAM		24
> +
> +#define CLK_NUMBER		(CLK_BUS_LEDC + 1)
> +
> +#endif /* _CCU_SUN50I_R329_H_ */
> diff --git a/include/dt-bindings/clock/sun50i-r329-ccu.h b/include/dt-bindings/clock/sun50i-r329-ccu.h
> new file mode 100644
> index 000000000000..116f8d13a9b3
> --- /dev/null
> +++ b/include/dt-bindings/clock/sun50i-r329-ccu.h
> @@ -0,0 +1,73 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Sipeed
> + */
> +
> +#ifndef _DT_BINDINGS_CLK_SUN50I_R329_CCU_H_
> +#define _DT_BINDINGS_CLK_SUN50I_R329_CCU_H_
> +
> +#define CLK_CPUX		1
> +
> +#define CLK_APB1		5
> +
> +#define CLK_CE			7
> +#define CLK_BUS_CE		8
> +#define CLK_AIPU		9
> +#define CLK_BUS_AIPU		10
> +#define CLK_BUS_DMA		11
> +#define CLK_BUS_MSGBOX		12
> +#define CLK_BUS_SPINLOCK	13
> +#define CLK_BUS_HSTIMER		14
> +#define CLK_AVS			15
> +#define CLK_BUS_DBG		16
> +#define CLK_BUS_PWM		17
> +
> +#define CLK_MBUS_DMA		19
> +#define CLK_MBUS_CE		20
> +#define CLK_MBUS_R_DMA		21
> +#define CLK_MBUS_NAND		22
> +#define CLK_MBUS_AIPU		23
> +
> +#define CLK_NAND0		25
> +#define CLK_NAND1		26
> +#define CLK_BUS_NAND		27
> +#define CLK_MMC0		28
> +#define CLK_MMC1		29
> +#define CLK_BUS_MMC0		30
> +#define CLK_BUS_MMC1		31
> +#define CLK_BUS_UART0		32
> +#define CLK_BUS_UART1		33
> +#define CLK_BUS_UART2		34
> +#define CLK_BUS_UART3		35
> +#define CLK_BUS_I2C0		36
> +#define CLK_BUS_I2C1		37
> +#define CLK_BUS_SCR		38
> +#define CLK_SPI0		39
> +#define CLK_SPI1		40
> +#define CLK_BUS_SPI0		41
> +#define CLK_BUS_SPI1		42
> +#define CLK_EMAC_25M_DIV	43
> +#define CLK_EMAC_25M		44
> +#define CLK_BUS_EMAC		45
> +#define CLK_IR_RX		46
> +#define CLK_BUS_IR_RX		47
> +#define CLK_IR_TX		48
> +#define CLK_BUS_IR_TX		49
> +#define CLK_I2S0		50
> +#define CLK_I2S1		51
> +#define CLK_BUS_I2S0		52
> +#define CLK_BUS_I2S1		53
> +#define CLK_SPDIF		54
> +#define CLK_BUS_SPDIF		55
> +#define CLK_USB_OHCI0		56
> +#define CLK_USB_PHY0		57
> +#define CLK_USB_OHCI1		58
> +#define CLK_USB_PHY1		59
> +#define CLK_BUS_OHCI0		60
> +#define CLK_BUS_OHCI1		61
> +#define CLK_BUS_EHCI0		62
> +#define CLK_BUS_OTG		63
> +#define CLK_LEDC		64
> +#define CLK_BUS_LEDC		65
> +
> +#endif /* _DT_BINDINGS_CLK_SUN50I_R329_CCU_H_ */
> diff --git a/include/dt-bindings/reset/sun50i-r329-ccu.h b/include/dt-bindings/reset/sun50i-r329-ccu.h
> new file mode 100644
> index 000000000000..bb704a82443f
> --- /dev/null
> +++ b/include/dt-bindings/reset/sun50i-r329-ccu.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
> +/*
> + * Copyright (c) 2021 Sipeed
> + */
> +
> +#ifndef _DT_BINDINGS_RST_SUN50I_R329_CCU_H_
> +#define _DT_BINDINGS_RST_SUN50I_R329_CCU_H_
> +
> +#define RST_MBUS		0
> +#define RST_BUS_CE		1
> +#define RST_BUS_AIPU		2
> +#define RST_BUS_DMA		3
> +#define RST_BUS_MSGBOX		4
> +#define RST_BUS_SPINLOCK	5
> +#define RST_BUS_HSTIMER		6
> +#define RST_BUS_DBG		7
> +#define RST_BUS_PWM		8
> +#define RST_BUS_DRAM		9
> +#define RST_BUS_NAND		10
> +#define RST_BUS_MMC0		11
> +#define RST_BUS_MMC1		12
> +#define RST_BUS_UART0		13
> +#define RST_BUS_UART1		14
> +#define RST_BUS_UART2		15
> +#define RST_BUS_UART3		16
> +#define RST_BUS_I2C0		17
> +#define RST_BUS_I2C1		18
> +#define RST_BUS_SCR		19
> +#define RST_BUS_SPI0		20
> +#define RST_BUS_SPI1		21
> +#define RST_BUS_EMAC		22
> +#define RST_BUS_IR_RX		23
> +#define RST_BUS_IR_TX		24
> +#define RST_BUS_I2S0		25
> +#define RST_BUS_I2S1		26
> +#define RST_BUS_SPDIF		27
> +#define RST_USB_PHY0		28
> +#define RST_USB_PHY1		29
> +#define RST_BUS_OHCI0		30
> +#define RST_BUS_OHCI1		31
> +#define RST_BUS_EHCI0		32
> +#define RST_BUS_OTG		33
> +#define RST_BUS_LEDC		34
> +
> +#endif /* _DT_BINDINGS_RST_SUN50I_R329_CCU_H_ */
> 




More information about the linux-arm-kernel mailing list