diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2020-03-18 08:48:16 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2020-03-18 08:48:16 +0100 |
commit | fcad6e3394943e32bc9e08d971f2c6511a99a151 (patch) | |
tree | f8f4ad6996ef81492d49c36273ef78aac75bd781 /drivers | |
parent | 394b88e6872c607e5317cf57c61c641288555311 (diff) | |
parent | e6e12e9368f730d5e50c9495c2332abe2ad6abf0 (diff) | |
download | barebox-fcad6e3394943e32bc9e08d971f2c6511a99a151.tar.gz barebox-fcad6e3394943e32bc9e08d971f2c6511a99a151.tar.xz |
Merge branch 'for-next/imx'
Diffstat (limited to 'drivers')
26 files changed, 2813 insertions, 394 deletions
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 97ae97a2a9..e627ef4a09 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_COMMON_CLK) += \ clk-pllv1.o \ clk-pllv2.o \ clk-pllv3.o \ + clk-pll14xx.o \ clk-pfd.o \ clk-gate2.o \ clk-gate-exclusive.o \ @@ -25,5 +26,7 @@ obj-$(CONFIG_ARCH_IMX6SX) += clk-imx6sx.o obj-$(CONFIG_ARCH_IMX6SL) += clk-imx6sl.o obj-$(CONFIG_ARCH_IMX6UL) += clk-imx6ul.o obj-$(CONFIG_ARCH_IMX7) += clk-imx7.o +pbl-$(CONFIG_ARCH_IMX8MM) += clk-pll14xx.o +obj-$(CONFIG_ARCH_IMX8MM) += clk-imx8mm.o obj-$(CONFIG_ARCH_IMX8MQ) += clk-imx8mq.o obj-$(CONFIG_ARCH_VF610) += clk-vf610.o diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c new file mode 100644 index 0000000000..a31741af88 --- /dev/null +++ b/drivers/clk/imx/clk-imx8mm.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP. + */ + +#include <dt-bindings/clock/imx8mm-clock.h> +#include <io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/types.h> +#include <of_address.h> + +#include "clk.h" + +static const char *pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; +static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; + +/* CCM ROOT */ +static const char *imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_m4_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "sys_pll1_266m", + "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_vpu_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "vpu_pll_out", }; + +static const char *imx8mm_gpu3d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_gpu2d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "sys_pll1_100m",}; + +static const char *imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", + "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; + +static const char *imx8mm_vpu_bus_sels[] = {"osc_24m", "sys_pll1_800m", "vpu_pll_out", "audio_pll2_out", + "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_200m", "sys_pll1_100m", }; + +static const char *imx8mm_disp_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext4", }; + +static const char *imx8mm_disp_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", }; + +static const char *imx8mm_disp_rtrm_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_noc_apb_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll3_out", "sys_pll2_333m", "sys_pll2_200m", + "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", + "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll2_166m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", "sys_pll2_500m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll1_out", "sys_pll1_266m", }; + +static const char *imx8mm_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_vpu_g1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_vpu_g2_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_disp_dtrc_sels[] = {"osc_24m", "dummy", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", }; + +static const char *imx8mm_disp_dc8000_sels[] = {"osc_24m", "dummy", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", }; + +static const char *imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; + +static const char *imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "sys_pll1_400m", }; + +static const char *imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", + "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; + +static const char *imx8mm_dc_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char *imx8mm_lcdif_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char *imx8mm_sai1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", }; + +static const char *imx8mm_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_sai4_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", }; + +static const char *imx8mm_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_spdif2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", + "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; + +static const char *imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "video_pll1_out", }; + +static const char *imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", + "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m", + "audio_pll2_out", "sys_pll3_out", "sys_pll2_250m", "video_pll1_out", }; + +static const char *imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", + "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; + +static const char *imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll1_800m", "clk_ext2", "clk_ext4", "audio_pll2_out" }; + +static const char *imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", "sys_pll1_40m", + "video_pll1_out", "sys_pll1_80m", "audio_pll1_out", "clk_ext1" }; + +static const char *imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", + "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; + +static const char *imx8mm_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out", "sys_pll3_out", "sys_pll2_200m", + "sys_pll1_266m", "sys_pll2_500m", "sys_pll1_100m", }; + +static const char *imx8mm_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_csi1_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi1_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_csi2_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_pcie2_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; + +static const char *imx8mm_pcie2_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", + "clk_ext2", "clk_ext3", "clk_ext4", "sys_pll1_400m", }; + +static const char *imx8mm_pcie2_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", + "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; + +static const char *imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_vpu_h1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "audio_pll2_out", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char *imx8mm_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "osc_27m", "sys_pll1_200m", "audio_pll2_out", + "vpu_pll", "sys_pll1_80m", }; + +static struct clk *clks[IMX8MM_CLK_END]; +static struct clk_onecell_data clk_data; + +static int imx8mm_clocks_init(struct device_node *ccm_np) +{ + struct device_node *anatop_np; + void __iomem *ccm, *ana; + int ret; + + anatop_np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop"); + ana = of_iomap(anatop_np, 0); + if (WARN_ON(!ana)) + return -ENOMEM; + + ccm = of_iomap(ccm_np, 0); + if (WARN_ON(!ccm)) + return -ENOMEM; + + clks[IMX8MM_CLK_DUMMY] = clk_fixed("dummy", 0); + clks[IMX8MM_CLK_24M] = of_clk_get_by_name(ccm_np, "osc_24m"); + clks[IMX8MM_CLK_32K] = of_clk_get_by_name(ccm_np, "osc_32k"); + clks[IMX8MM_CLK_EXT1] = of_clk_get_by_name(ccm_np, "clk_ext1"); + clks[IMX8MM_CLK_EXT2] = of_clk_get_by_name(ccm_np, "clk_ext2"); + clks[IMX8MM_CLK_EXT3] = of_clk_get_by_name(ccm_np, "clk_ext3"); + clks[IMX8MM_CLK_EXT4] = of_clk_get_by_name(ccm_np, "clk_ext4"); + + clks[IMX8MM_AUDIO_PLL1_REF_SEL] = imx_clk_mux("audio_pll1_ref_sel", ana + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_AUDIO_PLL2_REF_SEL] = imx_clk_mux("audio_pll2_ref_sel", ana + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_VIDEO_PLL1_REF_SEL] = imx_clk_mux("video_pll1_ref_sel", ana + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_DRAM_PLL_REF_SEL] = imx_clk_mux("dram_pll_ref_sel", ana + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", ana + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", ana + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", ana + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", ana + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", ana, &imx_1443x_pll); + clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", ana + 0x14, &imx_1443x_pll); + clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", ana + 0x28, &imx_1443x_pll); + clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", ana + 0x50, &imx_1443x_pll); + clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", ana + 0x64, &imx_1416x_pll); + clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", ana + 0x74, &imx_1416x_pll); + clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", ana + 0x84, &imx_1416x_pll); + clks[IMX8MM_SYS_PLL1] = clk_fixed("sys_pll1", 800000000); + clks[IMX8MM_SYS_PLL2] = clk_fixed("sys_pll2", 1000000000); + clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", ana + 0x114, &imx_1416x_pll); + + /* PLL bypass out */ + clks[IMX8MM_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", ana, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_AUDIO_PLL2_BYPASS] = imx_clk_mux_flags("audio_pll2_bypass", ana + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_VIDEO_PLL1_BYPASS] = imx_clk_mux_flags("video_pll1_bypass", ana + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_DRAM_PLL_BYPASS] = imx_clk_mux_flags("dram_pll_bypass", ana + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_GPU_PLL_BYPASS] = imx_clk_mux_flags("gpu_pll_bypass", ana + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_VPU_PLL_BYPASS] = imx_clk_mux_flags("vpu_pll_bypass", ana + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", ana + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MM_SYS_PLL3_BYPASS] = imx_clk_mux_flags("sys_pll3_bypass", ana + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); + + /* PLL out gate */ + clks[IMX8MM_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", ana, 13); + clks[IMX8MM_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", ana + 0x14, 13); + clks[IMX8MM_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", ana + 0x28, 13); + clks[IMX8MM_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll_bypass", ana + 0x50, 13); + clks[IMX8MM_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", ana + 0x64, 11); + clks[IMX8MM_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", ana + 0x74, 11); + clks[IMX8MM_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", ana + 0x84, 11); + clks[IMX8MM_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", ana + 0x114, 11); + + /* SYS PLL1 fixed output */ + clks[IMX8MM_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1", ana + 0x94, 27); + clks[IMX8MM_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1", ana + 0x94, 25); + clks[IMX8MM_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1", ana + 0x94, 23); + clks[IMX8MM_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1", ana + 0x94, 21); + clks[IMX8MM_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1", ana + 0x94, 19); + clks[IMX8MM_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1", ana + 0x94, 17); + clks[IMX8MM_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1", ana + 0x94, 15); + clks[IMX8MM_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1", ana + 0x94, 13); + clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1", ana + 0x94, 11); + + clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20); + clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10); + clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8); + clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6); + clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5); + clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4); + clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3); + clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2); + clks[IMX8MM_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); + + /* SYS PLL2 fixed output */ + clks[IMX8MM_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2", ana + 0x104, 27); + clks[IMX8MM_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2", ana + 0x104, 25); + clks[IMX8MM_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2", ana + 0x104, 23); + clks[IMX8MM_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2", ana + 0x104, 21); + clks[IMX8MM_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2", ana + 0x104, 19); + clks[IMX8MM_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2", ana + 0x104, 17); + clks[IMX8MM_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2", ana + 0x104, 15); + clks[IMX8MM_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2", ana + 0x104, 13); + clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2", ana + 0x104, 11); + + clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20); + clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10); + clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8); + clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6); + clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5); + clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4); + clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3); + clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2); + clks[IMX8MM_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); + + /* Core Slice */ + clks[IMX8MM_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", ccm + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels)); + clks[IMX8MM_CLK_M4_SRC] = imx_clk_mux2("arm_m4_src", ccm + 0x8080, 24, 3, imx8mm_m4_sels, ARRAY_SIZE(imx8mm_m4_sels)); + clks[IMX8MM_CLK_VPU_SRC] = imx_clk_mux2("vpu_src", ccm + 0x8100, 24, 3, imx8mm_vpu_sels, ARRAY_SIZE(imx8mm_vpu_sels)); + clks[IMX8MM_CLK_GPU3D_SRC] = imx_clk_mux2("gpu3d_src", ccm + 0x8180, 24, 3, imx8mm_gpu3d_sels, ARRAY_SIZE(imx8mm_gpu3d_sels)); + clks[IMX8MM_CLK_GPU2D_SRC] = imx_clk_mux2("gpu2d_src", ccm + 0x8200, 24, 3, imx8mm_gpu2d_sels, ARRAY_SIZE(imx8mm_gpu2d_sels)); + clks[IMX8MM_CLK_A53_CG] = imx_clk_gate3("arm_a53_cg", "arm_a53_src", ccm + 0x8000, 28); + clks[IMX8MM_CLK_M4_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", ccm + 0x8080, 28); + clks[IMX8MM_CLK_VPU_CG] = imx_clk_gate3("vpu_cg", "vpu_src", ccm + 0x8100, 28); + clks[IMX8MM_CLK_GPU3D_CG] = imx_clk_gate3("gpu3d_cg", "gpu3d_src", ccm + 0x8180, 28); + clks[IMX8MM_CLK_GPU2D_CG] = imx_clk_gate3("gpu2d_cg", "gpu2d_src", ccm + 0x8200, 28); + clks[IMX8MM_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", ccm + 0x8000, 0, 3); + clks[IMX8MM_CLK_M4_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", ccm + 0x8080, 0, 3); + clks[IMX8MM_CLK_VPU_DIV] = imx_clk_divider2("vpu_div", "vpu_cg", ccm + 0x8100, 0, 3); + clks[IMX8MM_CLK_GPU3D_DIV] = imx_clk_divider2("gpu3d_div", "gpu3d_cg", ccm + 0x8180, 0, 3); + clks[IMX8MM_CLK_GPU2D_DIV] = imx_clk_divider2("gpu2d_div", "gpu2d_cg", ccm + 0x8200, 0, 3); + + /* BUS */ + clks[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_composite_critical("main_axi", imx8mm_main_axi_sels, ccm + 0x8800); + clks[IMX8MM_CLK_ENET_AXI] = imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, ccm + 0x8880); + clks[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_composite_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, ccm + 0x8900); + clks[IMX8MM_CLK_VPU_BUS] = imx8m_clk_composite("vpu_bus", imx8mm_vpu_bus_sels, ccm + 0x8980); + clks[IMX8MM_CLK_DISP_AXI] = imx8m_clk_composite("disp_axi", imx8mm_disp_axi_sels, ccm + 0x8a00); + clks[IMX8MM_CLK_DISP_APB] = imx8m_clk_composite("disp_apb", imx8mm_disp_apb_sels, ccm + 0x8a80); + clks[IMX8MM_CLK_DISP_RTRM] = imx8m_clk_composite("disp_rtrm", imx8mm_disp_rtrm_sels, ccm + 0x8b00); + clks[IMX8MM_CLK_USB_BUS] = imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, ccm + 0x8b80); + clks[IMX8MM_CLK_GPU_AXI] = imx8m_clk_composite("gpu_axi", imx8mm_gpu_axi_sels, ccm + 0x8c00); + clks[IMX8MM_CLK_GPU_AHB] = imx8m_clk_composite("gpu_ahb", imx8mm_gpu_ahb_sels, ccm + 0x8c80); + clks[IMX8MM_CLK_NOC] = imx8m_clk_composite_critical("noc", imx8mm_noc_sels, ccm + 0x8d00); + clks[IMX8MM_CLK_NOC_APB] = imx8m_clk_composite_critical("noc_apb", imx8mm_noc_apb_sels, ccm + 0x8d80); + + /* AHB */ + clks[IMX8MM_CLK_AHB] = imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, ccm + 0x9000); + clks[IMX8MM_CLK_AUDIO_AHB] = imx8m_clk_composite("audio_ahb", imx8mm_audio_ahb_sels, ccm + 0x9100); + + /* IPG */ + clks[IMX8MM_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", ccm + 0x9080, 0, 1); + clks[IMX8MM_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", ccm + 0x9180, 0, 1); + + /* IP */ + clks[IMX8MM_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, ccm + 0xa000); + clks[IMX8MM_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mm_dram_apb_sels, ccm + 0xa080); + clks[IMX8MM_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mm_vpu_g1_sels, ccm + 0xa100); + clks[IMX8MM_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mm_vpu_g2_sels, ccm + 0xa180); + clks[IMX8MM_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mm_disp_dtrc_sels, ccm + 0xa200); + clks[IMX8MM_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mm_disp_dc8000_sels, ccm + 0xa280); + clks[IMX8MM_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, ccm + 0xa300); + clks[IMX8MM_CLK_PCIE1_PHY] = imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels, ccm + 0xa380); + clks[IMX8MM_CLK_PCIE1_AUX] = imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels, ccm + 0xa400); + clks[IMX8MM_CLK_DC_PIXEL] = imx8m_clk_composite("dc_pixel", imx8mm_dc_pixel_sels, ccm + 0xa480); + clks[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_composite("lcdif_pixel", imx8mm_lcdif_pixel_sels, ccm + 0xa500); + clks[IMX8MM_CLK_SAI1] = imx8m_clk_composite("sai1", imx8mm_sai1_sels, ccm + 0xa580); + clks[IMX8MM_CLK_SAI2] = imx8m_clk_composite("sai2", imx8mm_sai2_sels, ccm + 0xa600); + clks[IMX8MM_CLK_SAI3] = imx8m_clk_composite("sai3", imx8mm_sai3_sels, ccm + 0xa680); + clks[IMX8MM_CLK_SAI4] = imx8m_clk_composite("sai4", imx8mm_sai4_sels, ccm + 0xa700); + clks[IMX8MM_CLK_SAI5] = imx8m_clk_composite("sai5", imx8mm_sai5_sels, ccm + 0xa780); + clks[IMX8MM_CLK_SAI6] = imx8m_clk_composite("sai6", imx8mm_sai6_sels, ccm + 0xa800); + clks[IMX8MM_CLK_SPDIF1] = imx8m_clk_composite("spdif1", imx8mm_spdif1_sels, ccm + 0xa880); + clks[IMX8MM_CLK_SPDIF2] = imx8m_clk_composite("spdif2", imx8mm_spdif2_sels, ccm + 0xa900); + clks[IMX8MM_CLK_ENET_REF] = imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels, ccm + 0xa980); + clks[IMX8MM_CLK_ENET_TIMER] = imx8m_clk_composite("enet_timer", imx8mm_enet_timer_sels, ccm + 0xaa00); + clks[IMX8MM_CLK_ENET_PHY_REF] = imx8m_clk_composite("enet_phy", imx8mm_enet_phy_sels, ccm + 0xaa80); + clks[IMX8MM_CLK_NAND] = imx8m_clk_composite("nand", imx8mm_nand_sels, ccm + 0xab00); + clks[IMX8MM_CLK_QSPI] = imx8m_clk_composite("qspi", imx8mm_qspi_sels, ccm + 0xab80); + clks[IMX8MM_CLK_USDHC1] = imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels, ccm + 0xac00); + clks[IMX8MM_CLK_USDHC2] = imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels, ccm + 0xac80); + clks[IMX8MM_CLK_I2C1] = imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, ccm + 0xad00); + clks[IMX8MM_CLK_I2C2] = imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, ccm + 0xad80); + clks[IMX8MM_CLK_I2C3] = imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, ccm + 0xae00); + clks[IMX8MM_CLK_I2C4] = imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, ccm + 0xae80); + clks[IMX8MM_CLK_UART1] = imx8m_clk_composite("uart1", imx8mm_uart1_sels, ccm + 0xaf00); + clks[IMX8MM_CLK_UART2] = imx8m_clk_composite("uart2", imx8mm_uart2_sels, ccm + 0xaf80); + clks[IMX8MM_CLK_UART3] = imx8m_clk_composite("uart3", imx8mm_uart3_sels, ccm + 0xb000); + clks[IMX8MM_CLK_UART4] = imx8m_clk_composite("uart4", imx8mm_uart4_sels, ccm + 0xb080); + clks[IMX8MM_CLK_USB_CORE_REF] = imx8m_clk_composite("usb_core_ref", imx8mm_usb_core_sels, ccm + 0xb100); + clks[IMX8MM_CLK_USB_PHY_REF] = imx8m_clk_composite("usb_phy_ref", imx8mm_usb_phy_sels, ccm + 0xb180); + clks[IMX8MM_CLK_GIC] = imx8m_clk_composite_critical("gic", imx8mm_gic_sels, ccm + 0xb200); + clks[IMX8MM_CLK_ECSPI1] = imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, ccm + 0xb280); + clks[IMX8MM_CLK_ECSPI2] = imx8m_clk_composite("ecspi2", imx8mm_ecspi2_sels, ccm + 0xb300); + clks[IMX8MM_CLK_PWM1] = imx8m_clk_composite("pwm1", imx8mm_pwm1_sels, ccm + 0xb380); + clks[IMX8MM_CLK_PWM2] = imx8m_clk_composite("pwm2", imx8mm_pwm2_sels, ccm + 0xb400); + clks[IMX8MM_CLK_PWM3] = imx8m_clk_composite("pwm3", imx8mm_pwm3_sels, ccm + 0xb480); + clks[IMX8MM_CLK_PWM4] = imx8m_clk_composite("pwm4", imx8mm_pwm4_sels, ccm + 0xb500); + clks[IMX8MM_CLK_GPT1] = imx8m_clk_composite("gpt1", imx8mm_gpt1_sels, ccm + 0xb580); + clks[IMX8MM_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mm_wdog_sels, ccm + 0xb900); + clks[IMX8MM_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mm_wrclk_sels, ccm + 0xb980); + clks[IMX8MM_CLK_CLKO1] = imx8m_clk_composite("clko1", imx8mm_clko1_sels, ccm + 0xba00); + clks[IMX8MM_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mm_dsi_core_sels, ccm + 0xbb00); + clks[IMX8MM_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mm_dsi_phy_sels, ccm + 0xbb80); + clks[IMX8MM_CLK_DSI_DBI] = imx8m_clk_composite("dsi_dbi", imx8mm_dsi_dbi_sels, ccm + 0xbc00); + clks[IMX8MM_CLK_USDHC3] = imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels, ccm + 0xbc80); + clks[IMX8MM_CLK_CSI1_CORE] = imx8m_clk_composite("csi1_core", imx8mm_csi1_core_sels, ccm + 0xbd00); + clks[IMX8MM_CLK_CSI1_PHY_REF] = imx8m_clk_composite("csi1_phy_ref", imx8mm_csi1_phy_sels, ccm + 0xbd80); + clks[IMX8MM_CLK_CSI1_ESC] = imx8m_clk_composite("csi1_esc", imx8mm_csi1_esc_sels, ccm + 0xbe00); + clks[IMX8MM_CLK_CSI2_CORE] = imx8m_clk_composite("csi2_core", imx8mm_csi2_core_sels, ccm + 0xbe80); + clks[IMX8MM_CLK_CSI2_PHY_REF] = imx8m_clk_composite("csi2_phy_ref", imx8mm_csi2_phy_sels, ccm + 0xbf00); + clks[IMX8MM_CLK_CSI2_ESC] = imx8m_clk_composite("csi2_esc", imx8mm_csi2_esc_sels, ccm + 0xbf80); + clks[IMX8MM_CLK_PCIE2_CTRL] = imx8m_clk_composite("pcie2_ctrl", imx8mm_pcie2_ctrl_sels, ccm + 0xc000); + clks[IMX8MM_CLK_PCIE2_PHY] = imx8m_clk_composite("pcie2_phy", imx8mm_pcie2_phy_sels, ccm + 0xc080); + clks[IMX8MM_CLK_PCIE2_AUX] = imx8m_clk_composite("pcie2_aux", imx8mm_pcie2_aux_sels, ccm + 0xc100); + clks[IMX8MM_CLK_ECSPI3] = imx8m_clk_composite("ecspi3", imx8mm_ecspi3_sels, ccm + 0xc180); + clks[IMX8MM_CLK_PDM] = imx8m_clk_composite("pdm", imx8mm_pdm_sels, ccm + 0xc200); + clks[IMX8MM_CLK_VPU_H1] = imx8m_clk_composite("vpu_h1", imx8mm_vpu_h1_sels, ccm + 0xc280); + + /* CCGR */ + clks[IMX8MM_CLK_ECSPI1_ROOT] = imx_clk_gate4("ecspi1_root_clk", "ecspi1", ccm + 0x4070, 0); + clks[IMX8MM_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", ccm + 0x4080, 0); + clks[IMX8MM_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", ccm + 0x4090, 0); + clks[IMX8MM_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", ccm + 0x40a0, 0); + clks[IMX8MM_CLK_GPIO1_ROOT] = imx_clk_gate4("gpio1_root_clk", "ipg_root", ccm + 0x40b0, 0); + clks[IMX8MM_CLK_GPIO2_ROOT] = imx_clk_gate4("gpio2_root_clk", "ipg_root", ccm + 0x40c0, 0); + clks[IMX8MM_CLK_GPIO3_ROOT] = imx_clk_gate4("gpio3_root_clk", "ipg_root", ccm + 0x40d0, 0); + clks[IMX8MM_CLK_GPIO4_ROOT] = imx_clk_gate4("gpio4_root_clk", "ipg_root", ccm + 0x40e0, 0); + clks[IMX8MM_CLK_GPIO5_ROOT] = imx_clk_gate4("gpio5_root_clk", "ipg_root", ccm + 0x40f0, 0); + clks[IMX8MM_CLK_GPT1_ROOT] = imx_clk_gate4("gpt1_root_clk", "gpt1", ccm + 0x4100, 0); + clks[IMX8MM_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", ccm + 0x4170, 0); + clks[IMX8MM_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", ccm + 0x4180, 0); + clks[IMX8MM_CLK_I2C3_ROOT] = imx_clk_gate4("i2c3_root_clk", "i2c3", ccm + 0x4190, 0); + clks[IMX8MM_CLK_I2C4_ROOT] = imx_clk_gate4("i2c4_root_clk", "i2c4", ccm + 0x41a0, 0); + clks[IMX8MM_CLK_MU_ROOT] = imx_clk_gate4("mu_root_clk", "ipg_root", ccm + 0x4210, 0); + clks[IMX8MM_CLK_OCOTP_ROOT] = imx_clk_gate4("ocotp_root_clk", "ipg_root", ccm + 0x4220, 0); + clks[IMX8MM_CLK_PCIE1_ROOT] = imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", ccm + 0x4250, 0); + clks[IMX8MM_CLK_PWM1_ROOT] = imx_clk_gate4("pwm1_root_clk", "pwm1", ccm + 0x4280, 0); + clks[IMX8MM_CLK_PWM2_ROOT] = imx_clk_gate4("pwm2_root_clk", "pwm2", ccm + 0x4290, 0); + clks[IMX8MM_CLK_PWM3_ROOT] = imx_clk_gate4("pwm3_root_clk", "pwm3", ccm + 0x42a0, 0); + clks[IMX8MM_CLK_PWM4_ROOT] = imx_clk_gate4("pwm4_root_clk", "pwm4", ccm + 0x42b0, 0); + clks[IMX8MM_CLK_QSPI_ROOT] = imx_clk_gate4("qspi_root_clk", "qspi", ccm + 0x42f0, 0); + clks[IMX8MM_CLK_NAND_ROOT] = imx_clk_gate2_shared2("nand_root_clk", "nand", ccm + 0x4300, 0); + clks[IMX8MM_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", ccm + 0x4300, 0); + clks[IMX8MM_CLK_SAI1_ROOT] = imx_clk_gate2_shared2("sai1_root_clk", "sai1", ccm + 0x4330, 0); + clks[IMX8MM_CLK_SAI1_IPG] = imx_clk_gate2_shared2("sai1_ipg_clk", "ipg_audio_root", ccm + 0x4330, 0); + clks[IMX8MM_CLK_SAI2_ROOT] = imx_clk_gate2_shared2("sai2_root_clk", "sai2", ccm + 0x4340, 0); + clks[IMX8MM_CLK_SAI2_IPG] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_audio_root", ccm + 0x4340, 0); + clks[IMX8MM_CLK_SAI3_ROOT] = imx_clk_gate2_shared2("sai3_root_clk", "sai3", ccm + 0x4350, 0); + clks[IMX8MM_CLK_SAI3_IPG] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_audio_root", ccm + 0x4350, 0); + clks[IMX8MM_CLK_SAI4_ROOT] = imx_clk_gate2_shared2("sai4_root_clk", "sai4", ccm + 0x4360, 0); + clks[IMX8MM_CLK_SAI4_IPG] = imx_clk_gate2_shared2("sai4_ipg_clk", "ipg_audio_root", ccm + 0x4360, 0); + clks[IMX8MM_CLK_SAI5_ROOT] = imx_clk_gate2_shared2("sai5_root_clk", "sai5", ccm + 0x4370, 0); + clks[IMX8MM_CLK_SAI5_IPG] = imx_clk_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", ccm + 0x4370, 0); + clks[IMX8MM_CLK_SAI6_ROOT] = imx_clk_gate2_shared2("sai6_root_clk", "sai6", ccm + 0x4380, 0); + clks[IMX8MM_CLK_SAI6_IPG] = imx_clk_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", ccm + 0x4380, 0); + clks[IMX8MM_CLK_SNVS_ROOT] = imx_clk_gate4("snvs_root_clk", "ipg_root", ccm + 0x4470, 0); + clks[IMX8MM_CLK_UART1_ROOT] = imx_clk_gate4("uart1_root_clk", "uart1", ccm + 0x4490, 0); + clks[IMX8MM_CLK_UART2_ROOT] = imx_clk_gate4("uart2_root_clk", "uart2", ccm + 0x44a0, 0); + clks[IMX8MM_CLK_UART3_ROOT] = imx_clk_gate4("uart3_root_clk", "uart3", ccm + 0x44b0, 0); + clks[IMX8MM_CLK_UART4_ROOT] = imx_clk_gate4("uart4_root_clk", "uart4", ccm + 0x44c0, 0); + clks[IMX8MM_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", ccm + 0x44d0, 0); + clks[IMX8MM_CLK_GPU3D_ROOT] = imx_clk_gate4("gpu3d_root_clk", "gpu3d_div", ccm + 0x44f0, 0); + clks[IMX8MM_CLK_USDHC1_ROOT] = imx_clk_gate4("usdhc1_root_clk", "usdhc1", ccm + 0x4510, 0); + clks[IMX8MM_CLK_USDHC2_ROOT] = imx_clk_gate4("usdhc2_root_clk", "usdhc2", ccm + 0x4520, 0); + clks[IMX8MM_CLK_WDOG1_ROOT] = imx_clk_gate4("wdog1_root_clk", "wdog", ccm + 0x4530, 0); + clks[IMX8MM_CLK_WDOG2_ROOT] = imx_clk_gate4("wdog2_root_clk", "wdog", ccm + 0x4540, 0); + clks[IMX8MM_CLK_WDOG3_ROOT] = imx_clk_gate4("wdog3_root_clk", "wdog", ccm + 0x4550, 0); + clks[IMX8MM_CLK_VPU_G1_ROOT] = imx_clk_gate4("vpu_g1_root_clk", "vpu_g1", ccm + 0x4560, 0); + clks[IMX8MM_CLK_GPU_BUS_ROOT] = imx_clk_gate4("gpu_root_clk", "gpu_axi", ccm + 0x4570, 0); + clks[IMX8MM_CLK_VPU_H1_ROOT] = imx_clk_gate4("vpu_h1_root_clk", "vpu_h1", ccm + 0x4590, 0); + clks[IMX8MM_CLK_VPU_G2_ROOT] = imx_clk_gate4("vpu_g2_root_clk", "vpu_g2", ccm + 0x45a0, 0); + clks[IMX8MM_CLK_PDM_ROOT] = imx_clk_gate2_shared2("pdm_root_clk", "pdm", ccm + 0x45b0, 0); + clks[IMX8MM_CLK_PDM_IPG] = imx_clk_gate2_shared2("pdm_ipg_clk", "ipg_audio_root", ccm + 0x45b0, 0); + clks[IMX8MM_CLK_DISP_ROOT] = imx_clk_gate2_shared2("disp_root_clk", "disp_dc8000", ccm + 0x45d0, 0); + clks[IMX8MM_CLK_DISP_AXI_ROOT] = imx_clk_gate2_shared2("disp_axi_root_clk", "disp_axi", ccm + 0x45d0, 0); + clks[IMX8MM_CLK_DISP_APB_ROOT] = imx_clk_gate2_shared2("disp_apb_root_clk", "disp_apb", ccm + 0x45d0, 0); + clks[IMX8MM_CLK_DISP_RTRM_ROOT] = imx_clk_gate2_shared2("disp_rtrm_root_clk", "disp_rtrm", ccm + 0x45d0, 0); + clks[IMX8MM_CLK_USDHC3_ROOT] = imx_clk_gate4("usdhc3_root_clk", "usdhc3", ccm + 0x45e0, 0); + clks[IMX8MM_CLK_TMU_ROOT] = imx_clk_gate4("tmu_root_clk", "ipg_root", ccm + 0x4620, 0); + clks[IMX8MM_CLK_VPU_DEC_ROOT] = imx_clk_gate4("vpu_dec_root_clk", "vpu_bus", ccm + 0x4630, 0); + clks[IMX8MM_CLK_SDMA1_ROOT] = imx_clk_gate4("sdma1_clk", "ipg_root", ccm + 0x43a0, 0); + clks[IMX8MM_CLK_SDMA2_ROOT] = imx_clk_gate4("sdma2_clk", "ipg_audio_root", ccm + 0x43b0, 0); + clks[IMX8MM_CLK_SDMA3_ROOT] = imx_clk_gate4("sdma3_clk", "ipg_audio_root", ccm + 0x45f0, 0); + clks[IMX8MM_CLK_GPU2D_ROOT] = imx_clk_gate4("gpu2d_root_clk", "gpu2d_div", ccm + 0x4660, 0); + clks[IMX8MM_CLK_CSI1_ROOT] = imx_clk_gate4("csi1_root_clk", "csi1_core", ccm + 0x4650, 0); + + clks[IMX8MM_CLK_GPT_3M] = imx_clk_fixed_factor("gpt_3m", "osc_24m", 1, 8); + + clks[IMX8MM_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + clks[IMX8MM_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", ccm + 0x9800, 24, 1, imx8mm_dram_core_sels, ARRAY_SIZE(imx8mm_dram_core_sels), CLK_IS_CRITICAL); + + clks[IMX8MM_CLK_ARM] = imx_clk_cpu("arm", "arm_a53_div", + clks[IMX8MM_CLK_A53_DIV], + clks[IMX8MM_CLK_A53_SRC], + clks[IMX8MM_ARM_PLL_OUT], + clks[IMX8MM_SYS_PLL1_800M]); + + imx_check_clocks(clks, ARRAY_SIZE(clks)); + + clk_enable(clks[IMX8MM_SYS_PLL3_OUT]); + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + + ret = of_clk_add_provider(ccm_np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) + pr_err("failed to register clks for i.MX8MM\n"); + + return ret; +} +CLK_OF_DECLARE(imx8mm, "fsl,imx8mm-ccm", imx8mm_clocks_init); diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c new file mode 100644 index 0000000000..91e9624a60 --- /dev/null +++ b/drivers/clk/imx/clk-pll14xx.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP. + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <linux/clk.h> +#include <io.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <malloc.h> +#include <clock.h> +#include <soc/imx8m/clk-early.h> +#include <asm-generic/div64.h> + +#include "clk.h" + +#define GNRL_CTL 0x0 +#define DIV_CTL 0x4 +#define LOCK_STATUS BIT(31) +#define LOCK_SEL_MASK BIT(29) +#define CLKE_MASK BIT(11) +#define RST_MASK BIT(9) +#define BYPASS_MASK BIT(4) +#define MDIV_SHIFT 12 +#define MDIV_MASK GENMASK(21, 12) +#define PDIV_SHIFT 4 +#define PDIV_MASK GENMASK(9, 4) +#define SDIV_SHIFT 0 +#define SDIV_MASK GENMASK(2, 0) +#define KDIV_SHIFT 0 +#define KDIV_MASK GENMASK(15, 0) + +#define LOCK_TIMEOUT_US 10000 + +struct clk_pll14xx { + struct clk clk; + void __iomem *base; + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; + int rate_count; + const char *parent; +}; + +#define to_clk_pll14xx(clk) container_of(clk, struct clk_pll14xx, clk) + +static const struct imx_pll14xx_rate_table imx_pll1416x_tbl[] = { + PLL_1416X_RATE(1800000000U, 225, 3, 0), + PLL_1416X_RATE(1600000000U, 200, 3, 0), + PLL_1416X_RATE(1500000000U, 375, 3, 1), + PLL_1416X_RATE(1400000000U, 350, 3, 1), + PLL_1416X_RATE(1200000000U, 300, 3, 1), + PLL_1416X_RATE(1000000000U, 250, 3, 1), + PLL_1416X_RATE(800000000U, 200, 3, 1), + PLL_1416X_RATE(750000000U, 250, 2, 2), + PLL_1416X_RATE(700000000U, 350, 3, 2), + PLL_1416X_RATE(600000000U, 300, 3, 2), +}; + +static const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(594000000U, 198, 2, 2, 0), + PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), + PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), +}; + +struct imx_pll14xx_clk imx_1443x_pll = { + .type = PLL_1443X, + .rate_table = imx_pll1443x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), +}; + +struct imx_pll14xx_clk imx_1416x_pll = { + .type = PLL_1416X, + .rate_table = imx_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1416x_tbl), +}; + +static const struct imx_pll14xx_rate_table *imx_get_pll_settings( + struct clk_pll14xx *pll, unsigned long rate) +{ + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) + if (rate == rate_table[i].rate) + return &rate_table[i]; + + return NULL; +} + +static long clk_pll14xx_round_rate(struct clk *clk, unsigned long rate, + unsigned long *prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; + int i; + + /* Assumming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) + if (rate >= rate_table[i].rate) + return rate_table[i].rate; + + /* return minimum supported value */ + return rate_table[i - 1].rate; +} + +static unsigned long clk_pll1416x_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + u32 mdiv, pdiv, sdiv, pll_div; + u64 fvco = parent_rate; + + pll_div = readl(pll->base + 4); + mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT; + pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT; + sdiv = (pll_div & SDIV_MASK) >> SDIV_SHIFT; + + fvco *= mdiv; + do_div(fvco, pdiv << sdiv); + + return fvco; +} + +static unsigned long clk_pll1443x_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + u32 mdiv, pdiv, sdiv, pll_div_ctl0, pll_div_ctl1; + short int kdiv; + u64 fvco = parent_rate; + + pll_div_ctl0 = readl(pll->base + 4); + pll_div_ctl1 = readl(pll->base + 8); + mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT; + pdiv = (pll_div_ctl0 & PDIV_MASK) >> PDIV_SHIFT; + sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT; + kdiv = pll_div_ctl1 & KDIV_MASK; + + /* fvco = (m * 65536 + k) * Fin / (p * 65536) */ + fvco *= (mdiv * 65536 + kdiv); + pdiv *= 65536; + + do_div(fvco, pdiv << sdiv); + + return fvco; +} + +static inline bool clk_pll14xx_mp_change(const struct imx_pll14xx_rate_table *rate, + u32 pll_div) +{ + u32 old_mdiv, old_pdiv; + + old_mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT; + old_pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT; + + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv; +} + +static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll) +{ + u32 val; + + return readl_poll_timeout(pll->base, val, val & LOCK_STATUS, + LOCK_TIMEOUT_US); +} + +static int clk_pll1416x_set_rate(struct clk *clk, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + const struct imx_pll14xx_rate_table *rate; + u32 tmp, div_val; + int ret; + + rate = imx_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk->name); + return -EINVAL; + } + + tmp = readl(pll->base + 4); + + if (!clk_pll14xx_mp_change(rate, tmp)) { + tmp &= ~(SDIV_MASK) << SDIV_SHIFT; + tmp |= rate->sdiv << SDIV_SHIFT; + writel(tmp, pll->base + 4); + + return 0; + } + + /* Bypass clock and set lock to pll output lock */ + tmp = readl(pll->base); + tmp |= LOCK_SEL_MASK; + writel(tmp, pll->base); + + /* Enable RST */ + tmp &= ~RST_MASK; + writel(tmp, pll->base); + + /* Enable BYPASS */ + tmp |= BYPASS_MASK; + writel(tmp, pll->base); + + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) | + (rate->sdiv << SDIV_SHIFT); + writel(div_val, pll->base + 0x4); + + /* + * According to SPEC, t3 - t2 need to be greater than + * 1us and 1/FREF, respectively. + * FREF is FIN / Prediv, the prediv is [1, 63], so choose + * 3us. + */ + udelay(3); + + /* Disable RST */ + tmp |= RST_MASK; + writel(tmp, pll->base); + + /* Wait Lock */ + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel(tmp, pll->base); + + return 0; +} + +int clk_pll1416x_early_set_rate(void __iomem *base, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx pll = { + .clk = { + .name = "pll1416x", + }, + .base = base, + .rate_table = imx_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1416x_tbl), + }; + + return clk_pll1416x_set_rate(&pll.clk, drate, prate); +} + +static int clk_pll1443x_set_rate(struct clk *clk, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + const struct imx_pll14xx_rate_table *rate; + u32 tmp, div_val; + int ret; + + rate = imx_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk->name); + return -EINVAL; + } + + tmp = readl(pll->base + 4); + + if (!clk_pll14xx_mp_change(rate, tmp)) { + tmp &= ~(SDIV_MASK) << SDIV_SHIFT; + tmp |= rate->sdiv << SDIV_SHIFT; + writel(tmp, pll->base + 4); + + tmp = rate->kdiv << KDIV_SHIFT; + writel(tmp, pll->base + 8); + + return 0; + } + + /* Enable RST */ + tmp = readl(pll->base); + tmp &= ~RST_MASK; + writel(tmp, pll->base); + + /* Enable BYPASS */ + tmp |= BYPASS_MASK; + writel(tmp, pll->base); + + div_val = (rate->mdiv << MDIV_SHIFT) | (rate->pdiv << PDIV_SHIFT) | + (rate->sdiv << SDIV_SHIFT); + writel(div_val, pll->base + 0x4); + writel(rate->kdiv << KDIV_SHIFT, pll->base + 0x8); + + /* + * According to SPEC, t3 - t2 need to be greater than + * 1us and 1/FREF, respectively. + * FREF is FIN / Prediv, the prediv is [1, 63], so choose + * 3us. + */ + udelay(3); + + /* Disable RST */ + tmp |= RST_MASK; + writel(tmp, pll->base); + + /* Wait Lock*/ + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel(tmp, pll->base); + + return 0; +} + +static int clk_pll14xx_prepare(struct clk *clk) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + u32 val; + int ret; + + /* + * RESETB = 1 from 0, PLL starts its normal + * operation after lock time + */ + val = readl(pll->base + GNRL_CTL); + if (val & RST_MASK) + return 0; + val |= BYPASS_MASK; + writel(val, pll->base + GNRL_CTL); + val |= RST_MASK; + writel(val, pll->base + GNRL_CTL); + + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + val &= ~BYPASS_MASK; + writel(val, pll->base + GNRL_CTL); + + return 0; +} + +static int clk_pll14xx_is_prepared(struct clk *clk) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + u32 val; + + val = readl(pll->base + GNRL_CTL); + + return (val & RST_MASK) ? 1 : 0; +} + +static void clk_pll14xx_unprepare(struct clk *clk) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(clk); + u32 val; +printf("%s %p\n", __func__, pll); +printf("%s %p\n", __func__, pll->base); + /* + * Set RST to 0, power down mode is enabled and + * every digital block is reset + */ + val = readl(pll->base + GNRL_CTL); + val &= ~RST_MASK; + writel(val, pll->base + GNRL_CTL); +} + +static const struct clk_ops clk_pll1416x_ops = { + .enable = clk_pll14xx_prepare, + .disable = clk_pll14xx_unprepare, + .is_enabled = clk_pll14xx_is_prepared, + .recalc_rate = clk_pll1416x_recalc_rate, + .round_rate = clk_pll14xx_round_rate, + .set_rate = clk_pll1416x_set_rate, +}; + +static const struct clk_ops clk_pll1416x_min_ops = { + .recalc_rate = clk_pll1416x_recalc_rate, +}; + +static const struct clk_ops clk_pll1443x_ops = { + .enable = clk_pll14xx_prepare, + .disable = clk_pll14xx_unprepare, + .is_enabled = clk_pll14xx_is_prepared, + .recalc_rate = clk_pll1443x_recalc_rate, + .round_rate = clk_pll14xx_round_rate, + .set_rate = clk_pll1443x_set_rate, +}; + +struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, + void __iomem *base, + const struct imx_pll14xx_clk *pll_clk) +{ + struct clk_pll14xx *pll; + struct clk *clk; + u32 val; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + clk = &pll->clk; + + pll->parent = parent_name; + clk->name = name; + clk->flags = pll_clk->flags; + clk->parent_names = &pll->parent; + clk->num_parents = 1; + + switch (pll_clk->type) { + case PLL_1416X: + if (!pll_clk->rate_table) + clk->ops = &clk_pll1416x_min_ops; + else + clk->ops = &clk_pll1416x_ops; + break; + case PLL_1443X: + clk->ops = &clk_pll1443x_ops; + break; + default: + pr_err("%s: Unknown pll type for pll clk %s\n", + __func__, name); + }; + + pll->base = base; + pll->type = pll_clk->type; + pll->rate_table = pll_clk->rate_table; + pll->rate_count = pll_clk->rate_count; + + val = readl(pll->base + GNRL_CTL); + val &= ~BYPASS_MASK; + writel(val, pll->base + GNRL_CTL); + + ret = clk_register(clk); + if (ret) { + free(pll); + return ERR_PTR(ret); + } + + return clk; +} diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 04286f03f7..39eff9ee74 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -168,6 +168,50 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, const char *parent, void __iomem *base, u32 div_mask); +enum imx_pll14xx_type { + PLL_1416X, + PLL_1443X, +}; + +/* NOTE: Rate table should be kept sorted in descending order. */ +struct imx_pll14xx_rate_table { + unsigned int rate; + unsigned int pdiv; + unsigned int mdiv; + unsigned int sdiv; + unsigned int kdiv; +}; + +#define PLL_1416X_RATE(_rate, _m, _p, _s) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + } + +#define PLL_1443X_RATE(_rate, _m, _p, _s, _k) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + } + +struct imx_pll14xx_clk { + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; + int rate_count; + int flags; +}; + +extern struct imx_pll14xx_clk imx_1443x_pll; +extern struct imx_pll14xx_clk imx_1416x_pll; + +struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, + void __iomem *base, const struct imx_pll14xx_clk *pll_clk); + struct clk *imx_clk_frac_pll(const char *name, const char *parent_name, void __iomem *base); @@ -226,4 +270,7 @@ struct clk *imx8m_clk_composite_flags(const char *name, #define imx8m_clk_composite(name, parent_names, reg) \ __imx8m_clk_composite(name, parent_names, reg, 0) +#define imx8m_clk_composite_critical(name, parent_names, reg) \ + __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL) + #endif /* __IMX_CLK_H */ diff --git a/drivers/ddr/Kconfig b/drivers/ddr/Kconfig index 4ea71598af..d92c272b58 100644 --- a/drivers/ddr/Kconfig +++ b/drivers/ddr/Kconfig @@ -1 +1,2 @@ source "drivers/ddr/fsl/Kconfig" +source "drivers/ddr/imx8m/Kconfig" diff --git a/drivers/ddr/Makefile b/drivers/ddr/Makefile index faf2f9e1d6..7e33182cbc 100644 --- a/drivers/ddr/Makefile +++ b/drivers/ddr/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_DDR_FSL) += fsl/ +obj-$(CONFIG_IMX8M_DRAM) += imx8m/ diff --git a/drivers/ddr/imx8m/Kconfig b/drivers/ddr/imx8m/Kconfig new file mode 100644 index 0000000000..e8bce8c49d --- /dev/null +++ b/drivers/ddr/imx8m/Kconfig @@ -0,0 +1,7 @@ +menu "i.MX8M DDR controllers" + depends on ARCH_IMX8MQ || ARCH_IMX8MM + +config IMX8M_DRAM + bool "imx8m dram controller support" + +endmenu diff --git a/drivers/ddr/imx8m/Makefile b/drivers/ddr/imx8m/Makefile new file mode 100644 index 0000000000..2be313900f --- /dev/null +++ b/drivers/ddr/imx8m/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2018 NXP +# +# SPDX-License-Identifier: GPL-2.0+ +# + +pbl-$(CONFIG_IMX8M_DRAM) += helper.o ddrphy_utils.o ddrphy_train.o ddrphy_csr.o ddr_init.o diff --git a/drivers/ddr/imx8m/ddr_init.c b/drivers/ddr/imx8m/ddr_init.c new file mode 100644 index 0000000000..374601b786 --- /dev/null +++ b/drivers/ddr/imx8m/ddr_init.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018-2019 NXP + */ +#define DEBUG +#include <common.h> +#include <errno.h> +#include <io.h> +#include <soc/imx8m/ddr.h> +#include <mach/generic.h> +#include <mach/imx8m-regs.h> +#include <mach/imx8m-ccm-regs.h> + +#define SRC_DDRC_RCR_ADDR MX8MQ_SRC_DDRC_RCR_ADDR + +static void ddr_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num) +{ + int i = 0; + + for (i = 0; i < num; i++) { + reg32_write((unsigned long)ddrc_cfg->reg, ddrc_cfg->val); + ddrc_cfg++; + } +} + +static int imx8m_ddr_init(unsigned long src_ddrc_rcr, + struct dram_timing_info *dram_timing) +{ + unsigned int tmp, initial_drate, target_freq; + int ret; + + debug("DDRINFO: start DRAM init\n"); + + debug("DDRINFO: cfg clk\n"); + + /* disable iso */ + reg32_write(0x303A00EC, 0x0000ffff); /* PGC_CPU_MAPPING */ + reg32setbit(0x303A00F8, 5); /* PU_PGC_SW_PUP_REQ */ + + initial_drate = dram_timing->fsp_msg[0].drate; + /* default to the frequency point 0 clock */ + ddrphy_init_set_dfi_clk(initial_drate); + + /* D-aasert the presetn */ + reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000006); + + /* Step2: Program the dwc_ddr_umctl2 registers */ + debug("DDRINFO: ddrc config start\n"); + ddr_cfg_umctl2(dram_timing->ddrc_cfg, dram_timing->ddrc_cfg_num); + debug("DDRINFO: ddrc config done\n"); + + /* Step3: De-assert reset signal(core_ddrc_rstn & aresetn_n) */ + reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000004); + reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000000); + + /* + * Step4: Disable auto-refreshes, self-refresh, powerdown, and + * assertion of dfi_dram_clk_disable by setting RFSHCTL3.dis_auto_refresh = 1, + * PWRCTL.powerdown_en = 0, and PWRCTL.selfref_en = 0, + * PWRCTL.en_dfi_dram_clk_disable = 0 + */ + reg32_write(DDRC_DBG1(0), 0x00000000); + reg32_write(DDRC_RFSHCTL3(0), 0x0000001); + reg32_write(DDRC_PWRCTL(0), 0xa0); + + /* if ddr type is LPDDR4, do it */ + tmp = reg32_read(DDRC_MSTR(0)); + if (tmp & (0x1 << 5)) + reg32_write(DDRC_DDR_SS_GPR0, 0x01); /* LPDDR4 mode */ + + /* determine the initial boot frequency */ + target_freq = reg32_read(DDRC_MSTR2(0)) & 0x3; + target_freq = (tmp & (0x1 << 29)) ? target_freq : 0x0; + + /* Step5: Set SWCT.sw_done to 0 */ + reg32_write(DDRC_SWCTL(0), 0x00000000); + + /* Set the default boot frequency point */ + clrsetbits_le32(DDRC_DFIMISC(0), (0x1f << 8), target_freq << 8); + /* Step6: Set DFIMISC.dfi_init_complete_en to 0 */ + clrbits_le32(DDRC_DFIMISC(0), 0x1); + + /* Step7: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */ + reg32_write(DDRC_SWCTL(0), 0x00000001); + do { + tmp = reg32_read(DDRC_SWSTAT(0)); + } while ((tmp & 0x1) == 0x0); + + /* + * Step8 ~ Step13: Start PHY initialization and training by + * accessing relevant PUB registers + */ + debug("DDRINFO:ddrphy config start\n"); + + ret = ddr_cfg_phy(dram_timing); + if (ret) + return ret; + + debug("DDRINFO: ddrphy config done\n"); + + /* + * step14 CalBusy.0 =1, indicates the calibrator is actively + * calibrating. Wait Calibrating done. + */ + do { + tmp = reg32_read(DDRPHY_CalBusy(0)); + } while ((tmp & 0x1)); + + debug("DDRINFO:ddrphy calibration done\n"); + + /* Step15: Set SWCTL.sw_done to 0 */ + reg32_write(DDRC_SWCTL(0), 0x00000000); + + /* Step16: Set DFIMISC.dfi_init_start to 1 */ + setbits_le32(DDRC_DFIMISC(0), (0x1 << 5)); + + /* Step17: Set SWCTL.sw_done to 1; need to polling SWSTAT.sw_done_ack */ + reg32_write(DDRC_SWCTL(0), 0x00000001); + do { + tmp = reg32_read(DDRC_SWSTAT(0)); + } while ((tmp & 0x1) == 0x0); + + /* Step18: Polling DFISTAT.dfi_init_complete = 1 */ + do { + tmp = reg32_read(DDRC_DFISTAT(0)); + } while ((tmp & 0x1) == 0x0); + + /* Step19: Set SWCTL.sw_done to 0 */ + reg32_write(DDRC_SWCTL(0), 0x00000000); + + /* Step20: Set DFIMISC.dfi_init_start to 0 */ + clrbits_le32(DDRC_DFIMISC(0), (0x1 << 5)); + + /* Step21: optional */ + + /* Step22: Set DFIMISC.dfi_init_complete_en to 1 */ + setbits_le32(DDRC_DFIMISC(0), 0x1); + + /* Step23: Set PWRCTL.selfref_sw to 0 */ + clrbits_le32(DDRC_PWRCTL(0), (0x1 << 5)); + + /* Step24: Set SWCTL.sw_done to 1; need polling SWSTAT.sw_done_ack */ + reg32_write(DDRC_SWCTL(0), 0x00000001); + do { + tmp = reg32_read(DDRC_SWSTAT(0)); + } while ((tmp & 0x1) == 0x0); + + /* Step25: Wait for dwc_ddr_umctl2 to move to normal operating mode by monitoring + * STAT.operating_mode signal */ + do { + tmp = reg32_read(DDRC_STAT(0)); + } while ((tmp & 0x3) != 0x1); + + /* Step26: Set back register in Step4 to the original values if desired */ + reg32_write(DDRC_RFSHCTL3(0), 0x0000000); + /* enable selfref_en by default */ + setbits_le32(DDRC_PWRCTL(0), 0x1 << 3); + + /* enable port 0 */ + reg32_write(DDRC_PCTRL_0(0), 0x00000001); + debug("DDRINFO: ddrmix config done\n"); + + return 0; +} + +/* + * We store the timing parameters here. the TF-A will pick these up. + * Note that the timing used we leave the driver with is a PLL bypass 25MHz + * mode. So if your board runs horribly slow you'll likely have to provide a + * TF-A binary. + */ +#define IMX8M_SAVED_DRAM_TIMING_BASE 0x180000 + +int imx8mm_ddr_init(struct dram_timing_info *dram_timing) +{ + unsigned long src_ddrc_rcr = MX8M_SRC_DDRC_RCR_ADDR; + int ret; + + /* Step1: Follow the power up procedure */ + reg32_write(src_ddrc_rcr, 0x8f00001f); + reg32_write(src_ddrc_rcr, 0x8f00000f); + + ret = imx8m_ddr_init(src_ddrc_rcr, dram_timing); + if (ret) + return ret; + + /* save the dram timing config into memory */ + dram_config_save(dram_timing, IMX8M_SAVED_DRAM_TIMING_BASE); + + return 0; +} + +int imx8mq_ddr_init(struct dram_timing_info *dram_timing) +{ + unsigned long src_ddrc_rcr = MX8MQ_SRC_DDRC_RCR_ADDR; + int ret; + + /* Step1: Follow the power up procedure */ + reg32_write(src_ddrc_rcr + 0x04, 0x8f00000f); + reg32_write(src_ddrc_rcr, 0x8f00000f); + reg32_write(src_ddrc_rcr + 0x04, 0x8f000000); + + ret = imx8m_ddr_init(src_ddrc_rcr, dram_timing); + if (ret) + return ret; + + /* save the dram timing config into memory */ + dram_config_save(dram_timing, IMX8M_SAVED_DRAM_TIMING_BASE); + + return 0; +} diff --git a/drivers/ddr/imx8m/ddrphy_csr.c b/drivers/ddr/imx8m/ddrphy_csr.c new file mode 100644 index 0000000000..98ac5db3c0 --- /dev/null +++ b/drivers/ddr/imx8m/ddrphy_csr.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <linux/kernel.h> +#include <soc/imx8m/ddr.h> + +/* ddr phy trained csr */ +struct dram_cfg_param ddrphy_trained_csr[] = { + { 0x200b2, 0x0 }, + { 0x1200b2, 0x0 }, + { 0x2200b2, 0x0 }, + { 0x200cb, 0x0 }, + { 0x10043, 0x0 }, + { 0x110043, 0x0 }, + { 0x210043, 0x0 }, + { 0x10143, 0x0 }, + { 0x110143, 0x0 }, + { 0x210143, 0x0 }, + { 0x11043, 0x0 }, + { 0x111043, 0x0 }, + { 0x211043, 0x0 }, + { 0x11143, 0x0 }, + { 0x111143, 0x0 }, + { 0x211143, 0x0 }, + { 0x12043, 0x0 }, + { 0x112043, 0x0 }, + { 0x212043, 0x0 }, + { 0x12143, 0x0 }, + { 0x112143, 0x0 }, + { 0x212143, 0x0 }, + { 0x13043, 0x0 }, + { 0x113043, 0x0 }, + { 0x213043, 0x0 }, + { 0x13143, 0x0 }, + { 0x113143, 0x0 }, + { 0x213143, 0x0 }, + { 0x80, 0x0 }, + { 0x100080, 0x0 }, + { 0x200080, 0x0 }, + { 0x1080, 0x0 }, + { 0x101080, 0x0 }, + { 0x201080, 0x0 }, + { 0x2080, 0x0 }, + { 0x102080, 0x0 }, + { 0x202080, 0x0 }, + { 0x3080, 0x0 }, + { 0x103080, 0x0 }, + { 0x203080, 0x0 }, + { 0x4080, 0x0 }, + { 0x104080, 0x0 }, + { 0x204080, 0x0 }, + { 0x5080, 0x0 }, + { 0x105080, 0x0 }, + { 0x205080, 0x0 }, + { 0x6080, 0x0 }, + { 0x106080, 0x0 }, + { 0x206080, 0x0 }, + { 0x7080, 0x0 }, + { 0x107080, 0x0 }, + { 0x207080, 0x0 }, + { 0x8080, 0x0 }, + { 0x108080, 0x0 }, + { 0x208080, 0x0 }, + { 0x9080, 0x0 }, + { 0x109080, 0x0 }, + { 0x209080, 0x0 }, + { 0x10080, 0x0 }, + { 0x110080, 0x0 }, + { 0x210080, 0x0 }, + { 0x10180, 0x0 }, + { 0x110180, 0x0 }, + { 0x210180, 0x0 }, + { 0x11080, 0x0 }, + { 0x111080, 0x0 }, + { 0x211080, 0x0 }, + { 0x11180, 0x0 }, + { 0x111180, 0x0 }, + { 0x211180, 0x0 }, + { 0x12080, 0x0 }, + { 0x112080, 0x0 }, + { 0x212080, 0x0 }, + { 0x12180, 0x0 }, + { 0x112180, 0x0 }, + { 0x212180, 0x0 }, + { 0x13080, 0x0 }, + { 0x113080, 0x0 }, + { 0x213080, 0x0 }, + { 0x13180, 0x0 }, + { 0x113180, 0x0 }, + { 0x213180, 0x0 }, + { 0x10081, 0x0 }, + { 0x110081, 0x0 }, + { 0x210081, 0x0 }, + { 0x10181, 0x0 }, + { 0x110181, 0x0 }, + { 0x210181, 0x0 }, + { 0x11081, 0x0 }, + { 0x111081, 0x0 }, + { 0x211081, 0x0 }, + { 0x11181, 0x0 }, + { 0x111181, 0x0 }, + { 0x211181, 0x0 }, + { 0x12081, 0x0 }, + { 0x112081, 0x0 }, + { 0x212081, 0x0 }, + { 0x12181, 0x0 }, + { 0x112181, 0x0 }, + { 0x212181, 0x0 }, + { 0x13081, 0x0 }, + { 0x113081, 0x0 }, + { 0x213081, 0x0 }, + { 0x13181, 0x0 }, + { 0x113181, 0x0 }, + { 0x213181, 0x0 }, + { 0x100d0, 0x0 }, + { 0x1100d0, 0x0 }, + { 0x2100d0, 0x0 }, + { 0x101d0, 0x0 }, + { 0x1101d0, 0x0 }, + { 0x2101d0, 0x0 }, + { 0x110d0, 0x0 }, + { 0x1110d0, 0x0 }, + { 0x2110d0, 0x0 }, + { 0x111d0, 0x0 }, + { 0x1111d0, 0x0 }, + { 0x2111d0, 0x0 }, + { 0x120d0, 0x0 }, + { 0x1120d0, 0x0 }, + { 0x2120d0, 0x0 }, + { 0x121d0, 0x0 }, + { 0x1121d0, 0x0 }, + { 0x2121d0, 0x0 }, + { 0x130d0, 0x0 }, + { 0x1130d0, 0x0 }, + { 0x2130d0, 0x0 }, + { 0x131d0, 0x0 }, + { 0x1131d0, 0x0 }, + { 0x2131d0, 0x0 }, + { 0x100d1, 0x0 }, + { 0x1100d1, 0x0 }, + { 0x2100d1, 0x0 }, + { 0x101d1, 0x0 }, + { 0x1101d1, 0x0 }, + { 0x2101d1, 0x0 }, + { 0x110d1, 0x0 }, + { 0x1110d1, 0x0 }, + { 0x2110d1, 0x0 }, + { 0x111d1, 0x0 }, + { 0x1111d1, 0x0 }, + { 0x2111d1, 0x0 }, + { 0x120d1, 0x0 }, + { 0x1120d1, 0x0 }, + { 0x2120d1, 0x0 }, + { 0x121d1, 0x0 }, + { 0x1121d1, 0x0 }, + { 0x2121d1, 0x0 }, + { 0x130d1, 0x0 }, + { 0x1130d1, 0x0 }, + { 0x2130d1, 0x0 }, + { 0x131d1, 0x0 }, + { 0x1131d1, 0x0 }, + { 0x2131d1, 0x0 }, + { 0x10068, 0x0 }, + { 0x10168, 0x0 }, + { 0x10268, 0x0 }, + { 0x10368, 0x0 }, + { 0x10468, 0x0 }, + { 0x10568, 0x0 }, + { 0x10668, 0x0 }, + { 0x10768, 0x0 }, + { 0x10868, 0x0 }, + { 0x11068, 0x0 }, + { 0x11168, 0x0 }, + { 0x11268, 0x0 }, + { 0x11368, 0x0 }, + { 0x11468, 0x0 }, + { 0x11568, 0x0 }, + { 0x11668, 0x0 }, + { 0x11768, 0x0 }, + { 0x11868, 0x0 }, + { 0x12068, 0x0 }, + { 0x12168, 0x0 }, + { 0x12268, 0x0 }, + { 0x12368, 0x0 }, + { 0x12468, 0x0 }, + { 0x12568, 0x0 }, + { 0x12668, 0x0 }, + { 0x12768, 0x0 }, + { 0x12868, 0x0 }, + { 0x13068, 0x0 }, + { 0x13168, 0x0 }, + { 0x13268, 0x0 }, + { 0x13368, 0x0 }, + { 0x13468, 0x0 }, + { 0x13568, 0x0 }, + { 0x13668, 0x0 }, + { 0x13768, 0x0 }, + { 0x13868, 0x0 }, + { 0x10069, 0x0 }, + { 0x10169, 0x0 }, + { 0x10269, 0x0 }, + { 0x10369, 0x0 }, + { 0x10469, 0x0 }, + { 0x10569, 0x0 }, + { 0x10669, 0x0 }, + { 0x10769, 0x0 }, + { 0x10869, 0x0 }, + { 0x11069, 0x0 }, + { 0x11169, 0x0 }, + { 0x11269, 0x0 }, + { 0x11369, 0x0 }, + { 0x11469, 0x0 }, + { 0x11569, 0x0 }, + { 0x11669, 0x0 }, + { 0x11769, 0x0 }, + { 0x11869, 0x0 }, + { 0x12069, 0x0 }, + { 0x12169, 0x0 }, + { 0x12269, 0x0 }, + { 0x12369, 0x0 }, + { 0x12469, 0x0 }, + { 0x12569, 0x0 }, + { 0x12669, 0x0 }, + { 0x12769, 0x0 }, + { 0x12869, 0x0 }, + { 0x13069, 0x0 }, + { 0x13169, 0x0 }, + { 0x13269, 0x0 }, + { 0x13369, 0x0 }, + { 0x13469, 0x0 }, + { 0x13569, 0x0 }, + { 0x13669, 0x0 }, + { 0x13769, 0x0 }, + { 0x13869, 0x0 }, + { 0x1008c, 0x0 }, + { 0x11008c, 0x0 }, + { 0x21008c, 0x0 }, + { 0x1018c, 0x0 }, + { 0x11018c, 0x0 }, + { 0x21018c, 0x0 }, + { 0x1108c, 0x0 }, + { 0x11108c, 0x0 }, + { 0x21108c, 0x0 }, + { 0x1118c, 0x0 }, + { 0x11118c, 0x0 }, + { 0x21118c, 0x0 }, + { 0x1208c, 0x0 }, + { 0x11208c, 0x0 }, + { 0x21208c, 0x0 }, + { 0x1218c, 0x0 }, + { 0x11218c, 0x0 }, + { 0x21218c, 0x0 }, + { 0x1308c, 0x0 }, + { 0x11308c, 0x0 }, + { 0x21308c, 0x0 }, + { 0x1318c, 0x0 }, + { 0x11318c, 0x0 }, + { 0x21318c, 0x0 }, + { 0x1008d, 0x0 }, + { 0x11008d, 0x0 }, + { 0x21008d, 0x0 }, + { 0x1018d, 0x0 }, + { 0x11018d, 0x0 }, + { 0x21018d, 0x0 }, + { 0x1108d, 0x0 }, + { 0x11108d, 0x0 }, + { 0x21108d, 0x0 }, + { 0x1118d, 0x0 }, + { 0x11118d, 0x0 }, + { 0x21118d, 0x0 }, + { 0x1208d, 0x0 }, + { 0x11208d, 0x0 }, + { 0x21208d, 0x0 }, + { 0x1218d, 0x0 }, + { 0x11218d, 0x0 }, + { 0x21218d, 0x0 }, + { 0x1308d, 0x0 }, + { 0x11308d, 0x0 }, + { 0x21308d, 0x0 }, + { 0x1318d, 0x0 }, + { 0x11318d, 0x0 }, + { 0x21318d, 0x0 }, + { 0x100c0, 0x0 }, + { 0x1100c0, 0x0 }, + { 0x2100c0, 0x0 }, + { 0x101c0, 0x0 }, + { 0x1101c0, 0x0 }, + { 0x2101c0, 0x0 }, + { 0x102c0, 0x0 }, + { 0x1102c0, 0x0 }, + { 0x2102c0, 0x0 }, + { 0x103c0, 0x0 }, + { 0x1103c0, 0x0 }, + { 0x2103c0, 0x0 }, + { 0x104c0, 0x0 }, + { 0x1104c0, 0x0 }, + { 0x2104c0, 0x0 }, + { 0x105c0, 0x0 }, + { 0x1105c0, 0x0 }, + { 0x2105c0, 0x0 }, + { 0x106c0, 0x0 }, + { 0x1106c0, 0x0 }, + { 0x2106c0, 0x0 }, + { 0x107c0, 0x0 }, + { 0x1107c0, 0x0 }, + { 0x2107c0, 0x0 }, + { 0x108c0, 0x0 }, + { 0x1108c0, 0x0 }, + { 0x2108c0, 0x0 }, + { 0x110c0, 0x0 }, + { 0x1110c0, 0x0 }, + { 0x2110c0, 0x0 }, + { 0x111c0, 0x0 }, + { 0x1111c0, 0x0 }, + { 0x2111c0, 0x0 }, + { 0x112c0, 0x0 }, + { 0x1112c0, 0x0 }, + { 0x2112c0, 0x0 }, + { 0x113c0, 0x0 }, + { 0x1113c0, 0x0 }, + { 0x2113c0, 0x0 }, + { 0x114c0, 0x0 }, + { 0x1114c0, 0x0 }, + { 0x2114c0, 0x0 }, + { 0x115c0, 0x0 }, + { 0x1115c0, 0x0 }, + { 0x2115c0, 0x0 }, + { 0x116c0, 0x0 }, + { 0x1116c0, 0x0 }, + { 0x2116c0, 0x0 }, + { 0x117c0, 0x0 }, + { 0x1117c0, 0x0 }, + { 0x2117c0, 0x0 }, + { 0x118c0, 0x0 }, + { 0x1118c0, 0x0 }, + { 0x2118c0, 0x0 }, + { 0x120c0, 0x0 }, + { 0x1120c0, 0x0 }, + { 0x2120c0, 0x0 }, + { 0x121c0, 0x0 }, + { 0x1121c0, 0x0 }, + { 0x2121c0, 0x0 }, + { 0x122c0, 0x0 }, + { 0x1122c0, 0x0 }, + { 0x2122c0, 0x0 }, + { 0x123c0, 0x0 }, + { 0x1123c0, 0x0 }, + { 0x2123c0, 0x0 }, + { 0x124c0, 0x0 }, + { 0x1124c0, 0x0 }, + { 0x2124c0, 0x0 }, + { 0x125c0, 0x0 }, + { 0x1125c0, 0x0 }, + { 0x2125c0, 0x0 }, + { 0x126c0, 0x0 }, + { 0x1126c0, 0x0 }, + { 0x2126c0, 0x0 }, + { 0x127c0, 0x0 }, + { 0x1127c0, 0x0 }, + { 0x2127c0, 0x0 }, + { 0x128c0, 0x0 }, + { 0x1128c0, 0x0 }, + { 0x2128c0, 0x0 }, + { 0x130c0, 0x0 }, + { 0x1130c0, 0x0 }, + { 0x2130c0, 0x0 }, + { 0x131c0, 0x0 }, + { 0x1131c0, 0x0 }, + { 0x2131c0, 0x0 }, + { 0x132c0, 0x0 }, + { 0x1132c0, 0x0 }, + { 0x2132c0, 0x0 }, + { 0x133c0, 0x0 }, + { 0x1133c0, 0x0 }, + { 0x2133c0, 0x0 }, + { 0x134c0, 0x0 }, + { 0x1134c0, 0x0 }, + { 0x2134c0, 0x0 }, + { 0x135c0, 0x0 }, + { 0x1135c0, 0x0 }, + { 0x2135c0, 0x0 }, + { 0x136c0, 0x0 }, + { 0x1136c0, 0x0 }, + { 0x2136c0, 0x0 }, + { 0x137c0, 0x0 }, + { 0x1137c0, 0x0 }, + { 0x2137c0, 0x0 }, + { 0x138c0, 0x0 }, + { 0x1138c0, 0x0 }, + { 0x2138c0, 0x0 }, + { 0x100c1, 0x0 }, + { 0x1100c1, 0x0 }, + { 0x2100c1, 0x0 }, + { 0x101c1, 0x0 }, + { 0x1101c1, 0x0 }, + { 0x2101c1, 0x0 }, + { 0x102c1, 0x0 }, + { 0x1102c1, 0x0 }, + { 0x2102c1, 0x0 }, + { 0x103c1, 0x0 }, + { 0x1103c1, 0x0 }, + { 0x2103c1, 0x0 }, + { 0x104c1, 0x0 }, + { 0x1104c1, 0x0 }, + { 0x2104c1, 0x0 }, + { 0x105c1, 0x0 }, + { 0x1105c1, 0x0 }, + { 0x2105c1, 0x0 }, + { 0x106c1, 0x0 }, + { 0x1106c1, 0x0 }, + { 0x2106c1, 0x0 }, + { 0x107c1, 0x0 }, + { 0x1107c1, 0x0 }, + { 0x2107c1, 0x0 }, + { 0x108c1, 0x0 }, + { 0x1108c1, 0x0 }, + { 0x2108c1, 0x0 }, + { 0x110c1, 0x0 }, + { 0x1110c1, 0x0 }, + { 0x2110c1, 0x0 }, + { 0x111c1, 0x0 }, + { 0x1111c1, 0x0 }, + { 0x2111c1, 0x0 }, + { 0x112c1, 0x0 }, + { 0x1112c1, 0x0 }, + { 0x2112c1, 0x0 }, + { 0x113c1, 0x0 }, + { 0x1113c1, 0x0 }, + { 0x2113c1, 0x0 }, + { 0x114c1, 0x0 }, + { 0x1114c1, 0x0 }, + { 0x2114c1, 0x0 }, + { 0x115c1, 0x0 }, + { 0x1115c1, 0x0 }, + { 0x2115c1, 0x0 }, + { 0x116c1, 0x0 }, + { 0x1116c1, 0x0 }, + { 0x2116c1, 0x0 }, + { 0x117c1, 0x0 }, + { 0x1117c1, 0x0 }, + { 0x2117c1, 0x0 }, + { 0x118c1, 0x0 }, + { 0x1118c1, 0x0 }, + { 0x2118c1, 0x0 }, + { 0x120c1, 0x0 }, + { 0x1120c1, 0x0 }, + { 0x2120c1, 0x0 }, + { 0x121c1, 0x0 }, + { 0x1121c1, 0x0 }, + { 0x2121c1, 0x0 }, + { 0x122c1, 0x0 }, + { 0x1122c1, 0x0 }, + { 0x2122c1, 0x0 }, + { 0x123c1, 0x0 }, + { 0x1123c1, 0x0 }, + { 0x2123c1, 0x0 }, + { 0x124c1, 0x0 }, + { 0x1124c1, 0x0 }, + { 0x2124c1, 0x0 }, + { 0x125c1, 0x0 }, + { 0x1125c1, 0x0 }, + { 0x2125c1, 0x0 }, + { 0x126c1, 0x0 }, + { 0x1126c1, 0x0 }, + { 0x2126c1, 0x0 }, + { 0x127c1, 0x0 }, + { 0x1127c1, 0x0 }, + { 0x2127c1, 0x0 }, + { 0x128c1, 0x0 }, + { 0x1128c1, 0x0 }, + { 0x2128c1, 0x0 }, + { 0x130c1, 0x0 }, + { 0x1130c1, 0x0 }, + { 0x2130c1, 0x0 }, + { 0x131c1, 0x0 }, + { 0x1131c1, 0x0 }, + { 0x2131c1, 0x0 }, + { 0x132c1, 0x0 }, + { 0x1132c1, 0x0 }, + { 0x2132c1, 0x0 }, + { 0x133c1, 0x0 }, + { 0x1133c1, 0x0 }, + { 0x2133c1, 0x0 }, + { 0x134c1, 0x0 }, + { 0x1134c1, 0x0 }, + { 0x2134c1, 0x0 }, + { 0x135c1, 0x0 }, + { 0x1135c1, 0x0 }, + { 0x2135c1, 0x0 }, + { 0x136c1, 0x0 }, + { 0x1136c1, 0x0 }, + { 0x2136c1, 0x0 }, + { 0x137c1, 0x0 }, + { 0x1137c1, 0x0 }, + { 0x2137c1, 0x0 }, + { 0x138c1, 0x0 }, + { 0x1138c1, 0x0 }, + { 0x2138c1, 0x0 }, + { 0x10020, 0x0 }, + { 0x110020, 0x0 }, + { 0x210020, 0x0 }, + { 0x11020, 0x0 }, + { 0x111020, 0x0 }, + { 0x211020, 0x0 }, + { 0x12020, 0x0 }, + { 0x112020, 0x0 }, + { 0x212020, 0x0 }, + { 0x13020, 0x0 }, + { 0x113020, 0x0 }, + { 0x213020, 0x0 }, + { 0x20072, 0x0 }, + { 0x20073, 0x0 }, + { 0x20074, 0x0 }, + { 0x100aa, 0x0 }, + { 0x110aa, 0x0 }, + { 0x120aa, 0x0 }, + { 0x130aa, 0x0 }, + { 0x20010, 0x0 }, + { 0x120010, 0x0 }, + { 0x220010, 0x0 }, + { 0x20011, 0x0 }, + { 0x120011, 0x0 }, + { 0x220011, 0x0 }, + { 0x100ae, 0x0 }, + { 0x1100ae, 0x0 }, + { 0x2100ae, 0x0 }, + { 0x100af, 0x0 }, + { 0x1100af, 0x0 }, + { 0x2100af, 0x0 }, + { 0x110ae, 0x0 }, + { 0x1110ae, 0x0 }, + { 0x2110ae, 0x0 }, + { 0x110af, 0x0 }, + { 0x1110af, 0x0 }, + { 0x2110af, 0x0 }, + { 0x120ae, 0x0 }, + { 0x1120ae, 0x0 }, + { 0x2120ae, 0x0 }, + { 0x120af, 0x0 }, + { 0x1120af, 0x0 }, + { 0x2120af, 0x0 }, + { 0x130ae, 0x0 }, + { 0x1130ae, 0x0 }, + { 0x2130ae, 0x0 }, + { 0x130af, 0x0 }, + { 0x1130af, 0x0 }, + { 0x2130af, 0x0 }, + { 0x20020, 0x0 }, + { 0x120020, 0x0 }, + { 0x220020, 0x0 }, + { 0x100a0, 0x0 }, + { 0x100a1, 0x0 }, + { 0x100a2, 0x0 }, + { 0x100a3, 0x0 }, + { 0x100a4, 0x0 }, + { 0x100a5, 0x0 }, + { 0x100a6, 0x0 }, + { 0x100a7, 0x0 }, + { 0x110a0, 0x0 }, + { 0x110a1, 0x0 }, + { 0x110a2, 0x0 }, + { 0x110a3, 0x0 }, + { 0x110a4, 0x0 }, + { 0x110a5, 0x0 }, + { 0x110a6, 0x0 }, + { 0x110a7, 0x0 }, + { 0x120a0, 0x0 }, + { 0x120a1, 0x0 }, + { 0x120a2, 0x0 }, + { 0x120a3, 0x0 }, + { 0x120a4, 0x0 }, + { 0x120a5, 0x0 }, + { 0x120a6, 0x0 }, + { 0x120a7, 0x0 }, + { 0x130a0, 0x0 }, + { 0x130a1, 0x0 }, + { 0x130a2, 0x0 }, + { 0x130a3, 0x0 }, + { 0x130a4, 0x0 }, + { 0x130a5, 0x0 }, + { 0x130a6, 0x0 }, + { 0x130a7, 0x0 }, + { 0x2007c, 0x0 }, + { 0x12007c, 0x0 }, + { 0x22007c, 0x0 }, + { 0x2007d, 0x0 }, + { 0x12007d, 0x0 }, + { 0x22007d, 0x0 }, + { 0x400fd, 0x0 }, + { 0x400c0, 0x0 }, + { 0x90201, 0x0 }, + { 0x190201, 0x0 }, + { 0x290201, 0x0 }, + { 0x90202, 0x0 }, + { 0x190202, 0x0 }, + { 0x290202, 0x0 }, + { 0x90203, 0x0 }, + { 0x190203, 0x0 }, + { 0x290203, 0x0 }, + { 0x90204, 0x0 }, + { 0x190204, 0x0 }, + { 0x290204, 0x0 }, + { 0x90205, 0x0 }, + { 0x190205, 0x0 }, + { 0x290205, 0x0 }, + { 0x90206, 0x0 }, + { 0x190206, 0x0 }, + { 0x290206, 0x0 }, + { 0x90207, 0x0 }, + { 0x190207, 0x0 }, + { 0x290207, 0x0 }, + { 0x90208, 0x0 }, + { 0x190208, 0x0 }, + { 0x290208, 0x0 }, + { 0x10062, 0x0 }, + { 0x10162, 0x0 }, + { 0x10262, 0x0 }, + { 0x10362, 0x0 }, + { 0x10462, 0x0 }, + { 0x10562, 0x0 }, + { 0x10662, 0x0 }, + { 0x10762, 0x0 }, + { 0x10862, 0x0 }, + { 0x11062, 0x0 }, + { 0x11162, 0x0 }, + { 0x11262, 0x0 }, + { 0x11362, 0x0 }, + { 0x11462, 0x0 }, + { 0x11562, 0x0 }, + { 0x11662, 0x0 }, + { 0x11762, 0x0 }, + { 0x11862, 0x0 }, + { 0x12062, 0x0 }, + { 0x12162, 0x0 }, + { 0x12262, 0x0 }, + { 0x12362, 0x0 }, + { 0x12462, 0x0 }, + { 0x12562, 0x0 }, + { 0x12662, 0x0 }, + { 0x12762, 0x0 }, + { 0x12862, 0x0 }, + { 0x13062, 0x0 }, + { 0x13162, 0x0 }, + { 0x13262, 0x0 }, + { 0x13362, 0x0 }, + { 0x13462, 0x0 }, + { 0x13562, 0x0 }, + { 0x13662, 0x0 }, + { 0x13762, 0x0 }, + { 0x13862, 0x0 }, + { 0x20077, 0x0 }, + { 0x10001, 0x0 }, + { 0x11001, 0x0 }, + { 0x12001, 0x0 }, + { 0x13001, 0x0 }, + { 0x10040, 0x0 }, + { 0x10140, 0x0 }, + { 0x10240, 0x0 }, + { 0x10340, 0x0 }, + { 0x10440, 0x0 }, + { 0x10540, 0x0 }, + { 0x10640, 0x0 }, + { 0x10740, 0x0 }, + { 0x10840, 0x0 }, + { 0x10030, 0x0 }, + { 0x10130, 0x0 }, + { 0x10230, 0x0 }, + { 0x10330, 0x0 }, + { 0x10430, 0x0 }, + { 0x10530, 0x0 }, + { 0x10630, 0x0 }, + { 0x10730, 0x0 }, + { 0x10830, 0x0 }, + { 0x11040, 0x0 }, + { 0x11140, 0x0 }, + { 0x11240, 0x0 }, + { 0x11340, 0x0 }, + { 0x11440, 0x0 }, + { 0x11540, 0x0 }, + { 0x11640, 0x0 }, + { 0x11740, 0x0 }, + { 0x11840, 0x0 }, + { 0x11030, 0x0 }, + { 0x11130, 0x0 }, + { 0x11230, 0x0 }, + { 0x11330, 0x0 }, + { 0x11430, 0x0 }, + { 0x11530, 0x0 }, + { 0x11630, 0x0 }, + { 0x11730, 0x0 }, + { 0x11830, 0x0 }, + { 0x12040, 0x0 }, + { 0x12140, 0x0 }, + { 0x12240, 0x0 }, + { 0x12340, 0x0 }, + { 0x12440, 0x0 }, + { 0x12540, 0x0 }, + { 0x12640, 0x0 }, + { 0x12740, 0x0 }, + { 0x12840, 0x0 }, + { 0x12030, 0x0 }, + { 0x12130, 0x0 }, + { 0x12230, 0x0 }, + { 0x12330, 0x0 }, + { 0x12430, 0x0 }, + { 0x12530, 0x0 }, + { 0x12630, 0x0 }, + { 0x12730, 0x0 }, + { 0x12830, 0x0 }, + { 0x13040, 0x0 }, + { 0x13140, 0x0 }, + { 0x13240, 0x0 }, + { 0x13340, 0x0 }, + { 0x13440, 0x0 }, + { 0x13540, 0x0 }, + { 0x13640, 0x0 }, + { 0x13740, 0x0 }, + { 0x13840, 0x0 }, + { 0x13030, 0x0 }, + { 0x13130, 0x0 }, + { 0x13230, 0x0 }, + { 0x13330, 0x0 }, + { 0x13430, 0x0 }, + { 0x13530, 0x0 }, + { 0x13630, 0x0 }, + { 0x13730, 0x0 }, + { 0x13830, 0x0 }, +}; + +uint32_t ddrphy_trained_csr_num = ARRAY_SIZE(ddrphy_trained_csr); diff --git a/drivers/ddr/imx8m/ddrphy_train.c b/drivers/ddr/imx8m/ddrphy_train.c new file mode 100644 index 0000000000..c2238cc66b --- /dev/null +++ b/drivers/ddr/imx8m/ddrphy_train.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ +#define DEBUG +#include <common.h> +#include <linux/kernel.h> +#include <soc/imx8m/ddr.h> +#include <firmware.h> +#include <mach/imx8m-regs.h> + +void ddr_load_train_code(enum fw_type type) +{ + const u16 *imem, *dmem; + size_t isize, dsize; + + if (type == FW_1D_IMAGE) { + get_builtin_firmware(lpddr4_pmu_train_1d_imem_bin, &imem, &isize); + get_builtin_firmware(lpddr4_pmu_train_1d_dmem_bin, &dmem, &dsize); + } else { + get_builtin_firmware(lpddr4_pmu_train_2d_imem_bin, &imem, &isize); + get_builtin_firmware(lpddr4_pmu_train_2d_dmem_bin, &dmem, &dsize); + } + + ddrc_phy_load_firmware(IOMEM(MX8M_DDRC_PHY_BASE_ADDR), + DDRC_PHY_IMEM, imem, isize); + + ddrc_phy_load_firmware(IOMEM(MX8M_DDRC_PHY_BASE_ADDR), + DDRC_PHY_DMEM, dmem, dsize); +} + +int ddr_cfg_phy(struct dram_timing_info *dram_timing) +{ + struct dram_cfg_param *dram_cfg; + struct dram_fsp_msg *fsp_msg; + unsigned int num; + int i = 0; + int j = 0; + int ret; + + /* initialize PHY configuration */ + dram_cfg = dram_timing->ddrphy_cfg; + num = dram_timing->ddrphy_cfg_num; + for (i = 0; i < num; i++) { + /* config phy reg */ + dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val); + dram_cfg++; + } + + /* load the frequency setpoint message block config */ + fsp_msg = dram_timing->fsp_msg; + for (i = 0; i < dram_timing->fsp_msg_num; i++) { + debug("DRAM PHY training for %dMTS\n", fsp_msg->drate); + /* set dram PHY input clocks to desired frequency */ + ddrphy_init_set_dfi_clk(fsp_msg->drate); + + /* load the dram training firmware image */ + dwc_ddrphy_apb_wr(0xd0000, 0x0); + ddr_load_train_code(fsp_msg->fw_type); + + /* load the frequency set point message block parameter */ + dram_cfg = fsp_msg->fsp_cfg; + num = fsp_msg->fsp_cfg_num; + for (j = 0; j < num; j++) { + dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val); + dram_cfg++; + } + + /* + * -------------------- excute the firmware -------------------- + * Running the firmware is a simply process to taking the + * PMU out of reset and stall, then the firwmare will be run + * 1. reset the PMU; + * 2. begin the excution; + * 3. wait for the training done; + * 4. read the message block result. + * ------------------------------------------------------------- + */ + dwc_ddrphy_apb_wr(0xd0000, 0x1); + dwc_ddrphy_apb_wr(0xd0099, 0x9); + dwc_ddrphy_apb_wr(0xd0099, 0x1); + dwc_ddrphy_apb_wr(0xd0099, 0x0); + + /* Wait for the training firmware to complete */ + ret = wait_ddrphy_training_complete(); + if (ret) + return ret; + + /* Halt the microcontroller. */ + dwc_ddrphy_apb_wr(0xd0099, 0x1); + + /* Read the Message Block results */ + dwc_ddrphy_apb_wr(0xd0000, 0x0); + ddrphy_init_read_msg_block(fsp_msg->fw_type); + dwc_ddrphy_apb_wr(0xd0000, 0x1); + + fsp_msg++; + } + + /* Load PHY Init Engine Image */ + dram_cfg = dram_timing->ddrphy_pie; + num = dram_timing->ddrphy_pie_num; + for (i = 0; i < num; i++) { + dwc_ddrphy_apb_wr(dram_cfg->reg, dram_cfg->val); + dram_cfg++; + } + + /* save the ddr PHY trained CSR in memory for low power use */ + ddrphy_trained_csr_save(ddrphy_trained_csr, ddrphy_trained_csr_num); + + return 0; +} diff --git a/drivers/ddr/imx8m/ddrphy_utils.c b/drivers/ddr/imx8m/ddrphy_utils.c new file mode 100644 index 0000000000..651bb4b698 --- /dev/null +++ b/drivers/ddr/imx8m/ddrphy_utils.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Copyright 2018 NXP +*/ + +#include <common.h> +#include <errno.h> +#include <io.h> +#include <linux/iopoll.h> +#include <soc/imx8m/ddr.h> +#include <mach/imx8m-regs.h> +#include <mach/imx8m-ccm-regs.h> + +void ddrc_phy_load_firmware(void __iomem *phy, + enum ddrc_phy_firmware_offset offset, + const u16 *blob, size_t size) +{ + while (size) { + writew(*blob++, phy + DDRC_PHY_REG(offset)); + offset++; + size -= sizeof(*blob); + } +} + +enum pmc_constants { + PMC_MESSAGE_ID, + PMC_MESSAGE_STREAM, + + PMC_TRAIN_SUCCESS = 0x07, + PMC_TRAIN_STREAM_START = 0x08, + PMC_TRAIN_FAIL = 0xff, +}; + +static u32 ddrc_phy_get_message(void __iomem *phy, int type) +{ + u32 r, message; + + /* + * When BIT0 set to 0, the PMU has a message for the user + * Wait for it indefinitely. + */ + readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004), + r, !(r & BIT(0)), 0); + + switch (type) { + case PMC_MESSAGE_ID: + /* + * Get the major message ID + */ + message = readl(phy + DDRC_PHY_REG(0xd0032)); + break; + case PMC_MESSAGE_STREAM: + message = readl(phy + DDRC_PHY_REG(0xd0034)); + message <<= 16; + message |= readl(phy + DDRC_PHY_REG(0xd0032)); + break; + } + + /* + * By setting this register to 0, the user acknowledges the + * receipt of the message. + */ + writel(0x00000000, phy + DDRC_PHY_REG(0xd0031)); + /* + * When BIT0 set to 0, the PMU has a message for the user + */ + readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004), + r, r & BIT(0), 0); + + writel(0x00000001, phy + DDRC_PHY_REG(0xd0031)); + + return message; +} + +static void ddrc_phy_fetch_streaming_message(void __iomem *phy) +{ + const u16 index = ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM); + u16 i; + + for (i = 0; i < index; i++) + ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM); +} + +int wait_ddrphy_training_complete(void) +{ + void __iomem *phy = IOMEM(MX8M_DDRC_PHY_BASE_ADDR); + + for (;;) { + const u32 m = ddrc_phy_get_message(phy, PMC_MESSAGE_ID); + + switch (m) { + case PMC_TRAIN_STREAM_START: + ddrc_phy_fetch_streaming_message(phy); + break; + case PMC_TRAIN_SUCCESS: + return 0; + case PMC_TRAIN_FAIL: + hang(); + } + } +} + +struct dram_bypass_clk_setting { + ulong clk; + int alt_root_sel; + int alt_pre_div; + int apb_root_sel; + int apb_pre_div; +}; + +#define MHZ(x) (1000000UL * (x)) + +static struct dram_bypass_clk_setting imx8mq_dram_bypass_tbl[] = { + { + .clk = MHZ(100), + .alt_root_sel = 2, + .alt_pre_div = 1 - 1, + .apb_root_sel = 2, + .apb_pre_div = 2 - 1, + } , { + .clk = MHZ(250), + .alt_root_sel = 3, + .alt_pre_div = 2 - 1, + .apb_root_sel = 2, + .apb_pre_div = 2 - 1, + }, { + .clk = MHZ(400), + .alt_root_sel = 1, + .alt_pre_div = 2 - 1, + .apb_root_sel = 3, + .apb_pre_div = 2 - 1, + }, +}; + +static void dram_enable_bypass(ulong clk_val) +{ + int i; + struct dram_bypass_clk_setting *config; + + for (i = 0; i < ARRAY_SIZE(imx8mq_dram_bypass_tbl); i++) { + if (clk_val == imx8mq_dram_bypass_tbl[i].clk) + break; + } + + if (i == ARRAY_SIZE(imx8mq_dram_bypass_tbl)) { + printf("No matched freq table %lu\n", clk_val); + return; + } + + config = &imx8mq_dram_bypass_tbl[i]; + + imx8m_clock_set_target_val(IMX8M_DRAM_ALT_CLK_ROOT, + IMX8M_CCM_TARGET_ROOTn_ENABLE | + IMX8M_CCM_TARGET_ROOTn_MUX(config->alt_root_sel) | + IMX8M_CCM_TARGET_ROOTn_PRE_DIV(config->alt_pre_div)); + imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT, + IMX8M_CCM_TARGET_ROOTn_ENABLE | + IMX8M_CCM_TARGET_ROOTn_MUX(config->apb_root_sel) | + IMX8M_CCM_TARGET_ROOTn_PRE_DIV(config->apb_pre_div)); + imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG, IMX8M_CCM_TARGET_ROOTn_ENABLE | + IMX8M_CCM_TARGET_ROOTn_MUX(1)); +} + +static void dram_disable_bypass(void) +{ + imx8m_clock_set_target_val(IMX8M_DRAM_SEL_CFG, + IMX8M_CCM_TARGET_ROOTn_ENABLE | + IMX8M_CCM_TARGET_ROOTn_MUX(0)); + imx8m_clock_set_target_val(IMX8M_DRAM_APB_CLK_ROOT, + IMX8M_CCM_TARGET_ROOTn_ENABLE | + IMX8M_CCM_TARGET_ROOTn_MUX(4) | + IMX8M_CCM_TARGET_ROOTn_PRE_DIV(5 - 1)); +} + +struct imx_int_pll_rate_table { + u32 rate; + u32 r1; + u32 r2; +}; + +#define MDIV(x) ((x) << 12) +#define PDIV(x) ((x) << 4) +#define SDIV(x) ((x) << 0) + +#define LOCK_STATUS BIT(31) +#define LOCK_SEL_MASK BIT(29) +#define CLKE_MASK BIT(11) +#define RST_MASK BIT(9) +#define BYPASS_MASK BIT(4) + +static struct imx_int_pll_rate_table imx8mm_fracpll_tbl[] = { + { .rate = 1000000000U, .r1 = MDIV(250) | PDIV(3) | SDIV(1), .r2 = 0 }, + { .rate = 800000000U, .r1 = MDIV(300) | PDIV(9) | SDIV(0), .r2 = 0 }, + { .rate = 750000000U, .r1 = MDIV(250) | PDIV(8) | SDIV(0), .r2 = 0 }, + { .rate = 650000000U, .r1 = MDIV(325) | PDIV(3) | SDIV(2), .r2 = 0 }, + { .rate = 600000000U, .r1 = MDIV(300) | PDIV(3) | SDIV(2), .r2 = 0 }, + { .rate = 594000000U, .r1 = MDIV( 99) | PDIV(1) | SDIV(2), .r2 = 0 }, + { .rate = 400000000U, .r1 = MDIV(300) | PDIV(9) | SDIV(1), .r2 = 0 }, + { .rate = 266666667U, .r1 = MDIV(400) | PDIV(9) | SDIV(2), .r2 = 0 }, + { .rate = 167000000U, .r1 = MDIV(334) | PDIV(3) | SDIV(4), .r2 = 0 }, + { .rate = 100000000U, .r1 = MDIV(300) | PDIV(9) | SDIV(3), .r2 = 0 }, +}; + +static struct imx_int_pll_rate_table *fracpll(u32 freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(imx8mm_fracpll_tbl); i++) + if (freq == imx8mm_fracpll_tbl[i].rate) + return &imx8mm_fracpll_tbl[i]; + + return NULL; +} + +static int dram_pll_init(u32 freq) +{ + volatile int i; + u32 tmp; + void *pll_base; + struct imx_int_pll_rate_table *rate; + + rate = fracpll(freq); + if (!rate) { + printf("No matched freq table %u\n", freq); + return -EINVAL; + } + + setbits_le32(MX8M_GPC_BASE_ADDR + 0xec, 1 << 7); + setbits_le32(MX8M_GPC_BASE_ADDR + 0xf8, 1 << 5); + writel(0x8F000000UL, MX8M_SRC_BASE_ADDR + 0x1004); + + pll_base = IOMEM(MX8M_ANATOP_BASE_ADDR) + 0x50; + + /* Bypass clock and set lock to pll output lock */ + tmp = readl(pll_base); + tmp |= BYPASS_MASK; + writel(tmp, pll_base); + + /* Enable RST */ + tmp &= ~RST_MASK; + writel(tmp, pll_base); + + writel(rate->r1, pll_base + 4); + writel(rate->r2, pll_base + 8); + + for (i = 0; i < 1000; i++); + + /* Disable RST */ + tmp |= RST_MASK; + writel(tmp, pll_base); + + /* Wait Lock*/ + while (!(readl(pll_base) & LOCK_STATUS)); + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel(tmp, pll_base); + + return 0; +} + +void ddrphy_init_set_dfi_clk(unsigned int drate) +{ + switch (drate) { + case 4000: + dram_pll_init(MHZ(1000)); + dram_disable_bypass(); + break; + case 3200: + dram_pll_init(MHZ(800)); + dram_disable_bypass(); + break; + case 3000: + dram_pll_init(MHZ(750)); + dram_disable_bypass(); + break; + case 2400: + dram_pll_init(MHZ(600)); + dram_disable_bypass(); + break; + case 1600: + dram_pll_init(MHZ(400)); + dram_disable_bypass(); + break; + case 1066: + dram_pll_init(MHZ(266)); + dram_disable_bypass(); + break; + case 667: + dram_pll_init(MHZ(167)); + dram_disable_bypass(); + break; + case 400: + dram_enable_bypass(MHZ(400)); + break; + case 100: + dram_enable_bypass(MHZ(100)); + break; + default: + return; + } +} + +void ddrphy_init_read_msg_block(enum fw_type type) +{ +} diff --git a/drivers/ddr/imx8m/helper.c b/drivers/ddr/imx8m/helper.c new file mode 100644 index 0000000000..9e32ef9376 --- /dev/null +++ b/drivers/ddr/imx8m/helper.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +#include <common.h> +#include <io.h> +#include <errno.h> +#include <soc/imx8m/ddr.h> + +#define IMEM_LEN 32768 /* byte */ +#define DMEM_LEN 16384 /* byte */ +#define IMEM_2D_OFFSET 49152 + +#define IMEM_OFFSET_ADDR 0x00050000 +#define DMEM_OFFSET_ADDR 0x00054000 +#define DDR_TRAIN_CODE_BASE_ADDR IP2APB_DDRPHY_IPS_BASE_ADDR(0) + +void ddrphy_trained_csr_save(struct dram_cfg_param *ddrphy_csr, + unsigned int num) +{ + int i = 0; + + /* enable the ddrphy apb */ + dwc_ddrphy_apb_wr(0xd0000, 0x0); + dwc_ddrphy_apb_wr(0xc0080, 0x3); + for (i = 0; i < num; i++) { + ddrphy_csr->val = dwc_ddrphy_apb_rd(ddrphy_csr->reg); + ddrphy_csr++; + } + /* disable the ddrphy apb */ + dwc_ddrphy_apb_wr(0xc0080, 0x2); + dwc_ddrphy_apb_wr(0xd0000, 0x1); +} + +void dram_config_save(struct dram_timing_info *timing_info, + unsigned long saved_timing_base) +{ + int i = 0; + struct dram_timing_info *saved_timing = (void *)saved_timing_base; + struct dram_cfg_param *cfg; + + saved_timing->ddrc_cfg_num = timing_info->ddrc_cfg_num; + saved_timing->ddrphy_cfg_num = timing_info->ddrphy_cfg_num; + saved_timing->ddrphy_trained_csr_num = ddrphy_trained_csr_num; + saved_timing->ddrphy_pie_num = timing_info->ddrphy_pie_num; + + /* save the fsp table */ + for (i = 0; i < 4; i++) + saved_timing->fsp_table[i] = timing_info->fsp_table[i]; + + cfg = (struct dram_cfg_param *)(saved_timing_base + + sizeof(*timing_info)); + + /* save ddrc config */ + saved_timing->ddrc_cfg = cfg; + for (i = 0; i < timing_info->ddrc_cfg_num; i++) { + cfg->reg = timing_info->ddrc_cfg[i].reg; + cfg->val = timing_info->ddrc_cfg[i].val; + cfg++; + } + + /* save ddrphy config */ + saved_timing->ddrphy_cfg = cfg; + for (i = 0; i < timing_info->ddrphy_cfg_num; i++) { + cfg->reg = timing_info->ddrphy_cfg[i].reg; + cfg->val = timing_info->ddrphy_cfg[i].val; + cfg++; + } + + /* save the ddrphy csr */ + saved_timing->ddrphy_trained_csr = cfg; + for (i = 0; i < ddrphy_trained_csr_num; i++) { + cfg->reg = ddrphy_trained_csr[i].reg; + cfg->val = ddrphy_trained_csr[i].val; + cfg++; + } + + /* save the ddrphy pie */ + saved_timing->ddrphy_pie = cfg; + for (i = 0; i < timing_info->ddrphy_pie_num; i++) { + cfg->reg = timing_info->ddrphy_pie[i].reg; + cfg->val = timing_info->ddrphy_pie[i].val; + cfg++; + } +} diff --git a/drivers/hab/habv4.c b/drivers/hab/habv4.c index e3c1de1a4d..f0a087d1a8 100644 --- a/drivers/hab/habv4.c +++ b/drivers/hab/habv4.c @@ -213,7 +213,7 @@ static enum hab_status hab_sip_report_status(enum hab_config *config, return (enum hab_status)res.a0; } -static enum hab_status imx8_read_sram_events(enum hab_status status, +static enum hab_status imx8m_read_sram_events(enum hab_status status, uint32_t index, void *event, uint32_t *bytes) { @@ -262,7 +262,7 @@ static enum hab_status imx8_read_sram_events(enum hab_status status, struct habv4_rvt hab_smc_ops = { .header = { .tag = 0xdd }, - .report_event = imx8_read_sram_events, + .report_event = imx8m_read_sram_events, .report_status = hab_sip_report_status, }; @@ -585,12 +585,12 @@ int imx6_hab_get_status(void) return -EINVAL; } -static int imx8_hab_get_status(void) +static int imx8m_hab_get_status(void) { return habv4_get_status(&hab_smc_ops); } -static int init_imx8_hab_get_status(void) +static int init_imx8m_hab_get_status(void) { if (!cpu_is_mx8mq()) /* can happen in multi-image builds and is not an error */ @@ -600,7 +600,7 @@ static int init_imx8_hab_get_status(void) * Nobody will check the return value if there were HAB errors, but the * initcall will fail spectaculously with a strange error message. */ - imx8_hab_get_status(); + imx8m_hab_get_status(); return 0; } @@ -610,7 +610,7 @@ static int init_imx8_hab_get_status(void) * * */ -postmmu_initcall(init_imx8_hab_get_status); +postmmu_initcall(init_imx8m_hab_get_status); static int init_imx6_hab_get_status(void) { diff --git a/drivers/i2c/busses/i2c-imx-early.c b/drivers/i2c/busses/i2c-imx-early.c index d67226441e..26922c1044 100644 --- a/drivers/i2c/busses/i2c-imx-early.c +++ b/drivers/i2c/busses/i2c-imx-early.c @@ -308,3 +308,15 @@ void *ls1046_i2c_init(void __iomem *regs) return &fsl_i2c; } + +void *imx8m_i2c_early_init(void __iomem *regs) +{ + fsl_i2c.regs = regs; + fsl_i2c.regshift = 2; + fsl_i2c.i2cr_ien_opcode = I2CR_IEN_OPCODE_1; + fsl_i2c.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C; + /* Divider for ~100kHz when coming from the ROM */ + fsl_i2c.ifdr = 0x0f; + + return &fsl_i2c; +} diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c index 2579cfd9d1..caaf1ac9b5 100644 --- a/drivers/mci/imx-esdhc-pbl.c +++ b/drivers/mci/imx-esdhc-pbl.c @@ -22,6 +22,7 @@ #include <mach/atf.h> #include <mach/imx6-regs.h> #include <mach/imx8mq-regs.h> +#include <mach/imx8mm-regs.h> #include <mach/imx-header.h> #endif #include "sdhci.h" @@ -201,16 +202,20 @@ static void imx_esdhc_init(struct fsl_esdhc_host *host, FIELD_PREP(WML_RD_WML_MASK, SECTOR_WML)); } -static int imx8_esdhc_init(struct fsl_esdhc_host *host, - struct esdhc_soc_data *data, - int instance) +static int imx8m_esdhc_init(struct fsl_esdhc_host *host, + struct esdhc_soc_data *data, + int instance) { switch (instance) { case 0: - host->regs = IOMEM(MX8MQ_USDHC1_BASE_ADDR); + host->regs = IOMEM(MX8M_USDHC1_BASE_ADDR); break; case 1: - host->regs = IOMEM(MX8MQ_USDHC2_BASE_ADDR); + host->regs = IOMEM(MX8M_USDHC2_BASE_ADDR); + break; + case 2: + /* Only exists on i.MX8MM, not on i.MX8MQ */ + host->regs = IOMEM(MX8MM_USDHC3_BASE_ADDR); break; default: return -EINVAL; @@ -261,7 +266,7 @@ int imx6_esdhc_start_image(int instance) } /** - * imx8_esdhc_load_image - Load and optionally start an image from USDHC controller + * imx8m_esdhc_load_image - Load and optionally start an image from USDHC controller * @instance: The USDHC controller instance (0..2) * @start: Whether to directly start the loaded image * @@ -273,17 +278,17 @@ int imx6_esdhc_start_image(int instance) * Return: If successful, this function does not return (if directly started) * or 0. A negative error code is returned when this function fails. */ -int imx8_esdhc_load_image(int instance, bool start) +int imx8m_esdhc_load_image(int instance, bool start) { struct esdhc_soc_data data; struct fsl_esdhc_host host; int ret; - ret = imx8_esdhc_init(&host, &data, instance); + ret = imx8m_esdhc_init(&host, &data, instance); if (ret) return ret; - return esdhc_load_image(&host, MX8MQ_DDR_CSD1_BASE_ADDR, + return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR, MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K, start); } #endif diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index 6a9f1196cb..dc8fab73d3 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -389,6 +389,7 @@ static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = { { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data }, { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data }, { .compatible = "fsl,imx8mq-usdhc", .data = &usdhc_imx6sx_data }, + { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx6sx_data }, { .compatible = "fsl,ls1046a-esdhc",.data = &esdhc_ls_data }, { /* sentinel */ } }; diff --git a/drivers/pinctrl/imx-iomux-v3.c b/drivers/pinctrl/imx-iomux-v3.c index 1d2b76d632..dccf7d10df 100644 --- a/drivers/pinctrl/imx-iomux-v3.c +++ b/drivers/pinctrl/imx-iomux-v3.c @@ -252,6 +252,8 @@ static __maybe_unused struct of_device_id imx_iomux_v3_dt_ids[] = { .compatible = "fsl,imx7d-iomuxc-lpsr", .data = &imx_iomux_imx7_lpsr_data, }, { + .compatible = "fsl,imx8mm-iomuxc", + }, { .compatible = "fsl,imx8mq-iomuxc", }, { /* sentinel */ diff --git a/drivers/serial/serial_imx.c b/drivers/serial/serial_imx.c index 09341af874..e5280ac3e9 100644 --- a/drivers/serial/serial_imx.c +++ b/drivers/serial/serial_imx.c @@ -288,6 +288,9 @@ static __maybe_unused struct of_device_id imx_serial_dt_ids[] = { .compatible = "fsl,imx8mq-uart", .data = &imx21_data, }, { + .compatible = "fsl,imx8mm-uart", + .data = &imx21_data, + }, { /* sentinel */ } }; diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 9e98099502..64d4bddad4 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -2,9 +2,9 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MUSB) += musb/ -obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_STORAGE) += storage/ obj-y += host/ obj-y += otg/ +obj-y += gadget/ obj-$(CONFIG_USB) += misc/ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9d6a262038..3c1d7e6f18 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -15,6 +15,9 @@ config USB_GADGET_DRIVER_ARC default y select USB_GADGET_DUALSPEED +config USB_GADGET_DRIVER_ARC_PBL + bool + config USB_GADGET_DRIVER_AT91 bool prompt "at91 gadget driver" diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9ef594575b..27673fcf0e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o obj-$(CONFIG_USB_GADGET_DFU) += dfu.o obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o obj-$(CONFIG_USB_GADGET_DRIVER_ARC) += fsl_udc.o +pbl-$(CONFIG_USB_GADGET_DRIVER_ARC_PBL) += fsl_udc_pbl.o obj-$(CONFIG_USB_GADGET_DRIVER_AT91) += at91_udc.o obj-$(CONFIG_USB_GADGET_DRIVER_PXA27X) += pxa27x_udc.o diff --git a/drivers/usb/gadget/fsl_udc.c b/drivers/usb/gadget/fsl_udc.c index c7160cdbc7..cffe9bdab7 100644 --- a/drivers/usb/gadget/fsl_udc.c +++ b/drivers/usb/gadget/fsl_udc.c @@ -10,384 +10,9 @@ #include <io.h> #include <asm/byteorder.h> #include <linux/err.h> - +#include <soc/fsl/fsl_udc.h> #include <asm/mmu.h> -/* ### define USB registers here - */ -#define USB_MAX_CTRL_PAYLOAD 64 -#define USB_DR_SYS_OFFSET 0x400 - - /* USB DR device mode registers (Little Endian) */ -struct usb_dr_device { - /* Capability register */ - u8 res1[256]; - u16 caplength; /* Capability Register Length */ - u16 hciversion; /* Host Controller Interface Version */ - u32 hcsparams; /* Host Controller Structual Parameters */ - u32 hccparams; /* Host Controller Capability Parameters */ - u8 res2[20]; - u32 dciversion; /* Device Controller Interface Version */ - u32 dccparams; /* Device Controller Capability Parameters */ - u8 res3[24]; - /* Operation register */ - u32 usbcmd; /* USB Command Register */ - u32 usbsts; /* USB Status Register */ - u32 usbintr; /* USB Interrupt Enable Register */ - u32 frindex; /* Frame Index Register */ - u8 res4[4]; - u32 deviceaddr; /* Device Address */ - u32 endpointlistaddr; /* Endpoint List Address Register */ - u8 res5[4]; - u32 burstsize; /* Master Interface Data Burst Size Register */ - u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ - u8 res6[24]; - u32 configflag; /* Configure Flag Register */ - u32 portsc1; /* Port 1 Status and Control Register */ - u8 res7[28]; - u32 otgsc; /* On-The-Go Status and Control */ - u32 usbmode; /* USB Mode Register */ - u32 endptsetupstat; /* Endpoint Setup Status Register */ - u32 endpointprime; /* Endpoint Initialization Register */ - u32 endptflush; /* Endpoint Flush Register */ - u32 endptstatus; /* Endpoint Status Register */ - u32 endptcomplete; /* Endpoint Complete Register */ - u32 endptctrl[6]; /* Endpoint Control Registers */ -}; - -/* ep0 transfer state */ -#define WAIT_FOR_SETUP 0 -#define DATA_STATE_XMIT 1 -#define DATA_STATE_NEED_ZLP 2 -#define WAIT_FOR_OUT_STATUS 3 -#define DATA_STATE_RECV 4 - -/* Device Controller Capability Parameter register */ -#define DCCPARAMS_DC 0x00000080 -#define DCCPARAMS_DEN_MASK 0x0000001f - -/* Frame Index Register Bit Masks */ -#define USB_FRINDEX_MASKS 0x3fff -/* USB CMD Register Bit Masks */ -#define USB_CMD_RUN_STOP 0x00000001 -#define USB_CMD_CTRL_RESET 0x00000002 -#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 -#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 -#define USB_CMD_INT_AA_DOORBELL 0x00000040 -#define USB_CMD_ASP 0x00000300 -#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 -#define USB_CMD_SUTW 0x00002000 -#define USB_CMD_ATDTW 0x00004000 -#define USB_CMD_ITC 0x00FF0000 - -/* bit 15,3,2 are frame list size */ -#define USB_CMD_FRAME_SIZE_1024 0x00000000 -#define USB_CMD_FRAME_SIZE_512 0x00000004 -#define USB_CMD_FRAME_SIZE_256 0x00000008 -#define USB_CMD_FRAME_SIZE_128 0x0000000C -#define USB_CMD_FRAME_SIZE_64 0x00008000 -#define USB_CMD_FRAME_SIZE_32 0x00008004 -#define USB_CMD_FRAME_SIZE_16 0x00008008 -#define USB_CMD_FRAME_SIZE_8 0x0000800C - -/* bit 9-8 are async schedule park mode count */ -#define USB_CMD_ASP_00 0x00000000 -#define USB_CMD_ASP_01 0x00000100 -#define USB_CMD_ASP_10 0x00000200 -#define USB_CMD_ASP_11 0x00000300 -#define USB_CMD_ASP_BIT_POS 8 - -/* bit 23-16 are interrupt threshold control */ -#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 -#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 -#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 -#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 -#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 -#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 -#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 -#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 -#define USB_CMD_ITC_BIT_POS 16 - -/* USB STS Register Bit Masks */ -#define USB_STS_INT 0x00000001 -#define USB_STS_ERR 0x00000002 -#define USB_STS_PORT_CHANGE 0x00000004 -#define USB_STS_FRM_LST_ROLL 0x00000008 -#define USB_STS_SYS_ERR 0x00000010 -#define USB_STS_IAA 0x00000020 -#define USB_STS_RESET 0x00000040 -#define USB_STS_SOF 0x00000080 -#define USB_STS_SUSPEND 0x00000100 -#define USB_STS_HC_HALTED 0x00001000 -#define USB_STS_RCL 0x00002000 -#define USB_STS_PERIODIC_SCHEDULE 0x00004000 -#define USB_STS_ASYNC_SCHEDULE 0x00008000 - -/* USB INTR Register Bit Masks */ -#define USB_INTR_INT_EN 0x00000001 -#define USB_INTR_ERR_INT_EN 0x00000002 -#define USB_INTR_PTC_DETECT_EN 0x00000004 -#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 -#define USB_INTR_SYS_ERR_EN 0x00000010 -#define USB_INTR_ASYN_ADV_EN 0x00000020 -#define USB_INTR_RESET_EN 0x00000040 -#define USB_INTR_SOF_EN 0x00000080 -#define USB_INTR_DEVICE_SUSPEND 0x00000100 - -/* Device Address bit masks */ -#define USB_DEVICE_ADDRESS_MASK 0xFE000000 -#define USB_DEVICE_ADDRESS_BIT_POS 25 - -/* endpoint list address bit masks */ -#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 - -/* PORTSCX Register Bit Masks */ -#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001 -#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002 -#define PORTSCX_PORT_ENABLE 0x00000004 -#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008 -#define PORTSCX_OVER_CURRENT_ACT 0x00000010 -#define PORTSCX_OVER_CURRENT_CHG 0x00000020 -#define PORTSCX_PORT_FORCE_RESUME 0x00000040 -#define PORTSCX_PORT_SUSPEND 0x00000080 -#define PORTSCX_PORT_RESET 0x00000100 -#define PORTSCX_LINE_STATUS_BITS 0x00000C00 -#define PORTSCX_PORT_POWER 0x00001000 -#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000 -#define PORTSCX_PORT_TEST_CTRL 0x000F0000 -#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000 -#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000 -#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000 -#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000 -#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000 -#define PORTSCX_PORT_SPEED_MASK 0x0C000000 -#define PORTSCX_PORT_WIDTH 0x10000000 -#define PORTSCX_PHY_TYPE_SEL 0xC0000000 - -/* bit 11-10 are line status */ -#define PORTSCX_LINE_STATUS_SE0 0x00000000 -#define PORTSCX_LINE_STATUS_JSTATE 0x00000400 -#define PORTSCX_LINE_STATUS_KSTATE 0x00000800 -#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00 -#define PORTSCX_LINE_STATUS_BIT_POS 10 - -/* bit 15-14 are port indicator control */ -#define PORTSCX_PIC_OFF 0x00000000 -#define PORTSCX_PIC_AMBER 0x00004000 -#define PORTSCX_PIC_GREEN 0x00008000 -#define PORTSCX_PIC_UNDEF 0x0000C000 -#define PORTSCX_PIC_BIT_POS 14 - -/* bit 19-16 are port test control */ -#define PORTSCX_PTC_DISABLE 0x00000000 -#define PORTSCX_PTC_JSTATE 0x00010000 -#define PORTSCX_PTC_KSTATE 0x00020000 -#define PORTSCX_PTC_SEQNAK 0x00030000 -#define PORTSCX_PTC_PACKET 0x00040000 -#define PORTSCX_PTC_FORCE_EN 0x00050000 -#define PORTSCX_PTC_BIT_POS 16 - -/* bit 27-26 are port speed */ -#define PORTSCX_PORT_SPEED_FULL 0x00000000 -#define PORTSCX_PORT_SPEED_LOW 0x04000000 -#define PORTSCX_PORT_SPEED_HIGH 0x08000000 -#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000 -#define PORTSCX_SPEED_BIT_POS 26 - -/* bit 28 is parallel transceiver width for UTMI interface */ -#define PORTSCX_PTW 0x10000000 -#define PORTSCX_PTW_8BIT 0x00000000 -#define PORTSCX_PTW_16BIT 0x10000000 - -/* bit 31-30 are port transceiver select */ -#define PORTSCX_PTS_UTMI 0x00000000 -#define PORTSCX_PTS_ULPI 0x80000000 -#define PORTSCX_PTS_FSLS 0xC0000000 -#define PORTSCX_PTS_BIT_POS 30 - -/* otgsc Register Bit Masks */ -#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 -#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 -#define OTGSC_CTRL_OTG_TERM 0x00000008 -#define OTGSC_CTRL_DATA_PULSING 0x00000010 -#define OTGSC_STS_USB_ID 0x00000100 -#define OTGSC_STS_A_VBUS_VALID 0x00000200 -#define OTGSC_STS_A_SESSION_VALID 0x00000400 -#define OTGSC_STS_B_SESSION_VALID 0x00000800 -#define OTGSC_STS_B_SESSION_END 0x00001000 -#define OTGSC_STS_1MS_TOGGLE 0x00002000 -#define OTGSC_STS_DATA_PULSING 0x00004000 -#define OTGSC_INTSTS_USB_ID 0x00010000 -#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 -#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 -#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 -#define OTGSC_INTSTS_B_SESSION_END 0x00100000 -#define OTGSC_INTSTS_1MS 0x00200000 -#define OTGSC_INTSTS_DATA_PULSING 0x00400000 -#define OTGSC_INTR_USB_ID 0x01000000 -#define OTGSC_INTR_A_VBUS_VALID 0x02000000 -#define OTGSC_INTR_A_SESSION_VALID 0x04000000 -#define OTGSC_INTR_B_SESSION_VALID 0x08000000 -#define OTGSC_INTR_B_SESSION_END 0x10000000 -#define OTGSC_INTR_1MS_TIMER 0x20000000 -#define OTGSC_INTR_DATA_PULSING 0x40000000 - -/* USB MODE Register Bit Masks */ -#define USB_MODE_CTRL_MODE_IDLE 0x00000000 -#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 -#define USB_MODE_CTRL_MODE_HOST 0x00000003 -#define USB_MODE_CTRL_MODE_RSV 0x00000001 -#define USB_MODE_CTRL_MODE_MASK 0x00000003 -#define USB_MODE_SETUP_LOCK_OFF 0x00000008 -#define USB_MODE_STREAM_DISABLE 0x00000010 -/* Endpoint Flush Register */ -#define EPFLUSH_TX_OFFSET 0x00010000 -#define EPFLUSH_RX_OFFSET 0x00000000 - -/* Endpoint Setup Status bit masks */ -#define EP_SETUP_STATUS_MASK 0x0000003F -#define EP_SETUP_STATUS_EP0 0x00000001 - -/* ENDPOINTCTRLx Register Bit Masks */ -#define EPCTRL_TX_ENABLE 0x00800000 -#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ -#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ -#define EPCTRL_TX_TYPE 0x000C0000 -#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ -#define EPCTRL_TX_EP_STALL 0x00010000 -#define EPCTRL_RX_ENABLE 0x00000080 -#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ -#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ -#define EPCTRL_RX_TYPE 0x0000000C -#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ -#define EPCTRL_RX_EP_STALL 0x00000001 - -/* bit 19-18 and 3-2 are endpoint type */ -#define EPCTRL_EP_TYPE_CONTROL 0 -#define EPCTRL_EP_TYPE_ISO 1 -#define EPCTRL_EP_TYPE_BULK 2 -#define EPCTRL_EP_TYPE_INTERRUPT 3 -#define EPCTRL_TX_EP_TYPE_SHIFT 18 -#define EPCTRL_RX_EP_TYPE_SHIFT 2 - -/* SNOOPn Register Bit Masks */ -#define SNOOP_ADDRESS_MASK 0xFFFFF000 -#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */ -#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */ -#define SNOOP_SIZE_8KB 0x0C -#define SNOOP_SIZE_16KB 0x0D -#define SNOOP_SIZE_32KB 0x0E -#define SNOOP_SIZE_64KB 0x0F -#define SNOOP_SIZE_128KB 0x10 -#define SNOOP_SIZE_256KB 0x11 -#define SNOOP_SIZE_512KB 0x12 -#define SNOOP_SIZE_1MB 0x13 -#define SNOOP_SIZE_2MB 0x14 -#define SNOOP_SIZE_4MB 0x15 -#define SNOOP_SIZE_8MB 0x16 -#define SNOOP_SIZE_16MB 0x17 -#define SNOOP_SIZE_32MB 0x18 -#define SNOOP_SIZE_64MB 0x19 -#define SNOOP_SIZE_128MB 0x1A -#define SNOOP_SIZE_256MB 0x1B -#define SNOOP_SIZE_512MB 0x1C -#define SNOOP_SIZE_1GB 0x1D -#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */ - -/* pri_ctrl Register Bit Masks */ -#define PRI_CTRL_PRI_LVL1 0x0000000C -#define PRI_CTRL_PRI_LVL0 0x00000003 - -/* si_ctrl Register Bit Masks */ -#define SI_CTRL_ERR_DISABLE 0x00000010 -#define SI_CTRL_IDRC_DISABLE 0x00000008 -#define SI_CTRL_RD_SAFE_EN 0x00000004 -#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002 -#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001 - -/* control Register Bit Masks */ -#define USB_CTRL_IOENB 0x00000004 -#define USB_CTRL_ULPI_INT0EN 0x00000001 - -/* Endpoint Queue Head data struct - * Rem: all the variables of qh are LittleEndian Mode - * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr - */ -struct ep_queue_head { - u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len - and IOS(15) */ - u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ - u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ - u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), - MultO(11-10), STS (7-0) */ - u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ - u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ - u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ - u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ - u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ - u32 res1; - u8 setup_buffer[8]; /* Setup data 8 bytes */ - u32 res2[4]; -}; - -/* Endpoint Queue Head Bit Masks */ -#define EP_QUEUE_HEAD_MULT_POS 30 -#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 -#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 -#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) -#define EP_QUEUE_HEAD_IOS 0x00008000 -#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 -#define EP_QUEUE_HEAD_IOC 0x00008000 -#define EP_QUEUE_HEAD_MULTO 0x00000C00 -#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 -#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 -#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF -#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 -#define EP_QUEUE_FRINDEX_MASK 0x000007FF -#define EP_MAX_LENGTH_TRANSFER 0x4000 - -/* Endpoint Transfer Descriptor data struct */ -/* Rem: all the variables of td are LittleEndian Mode */ -struct ep_td_struct { - u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set - indicate invalid */ - u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), - MultO(11-10), STS (7-0) */ - u32 buff_ptr0; /* Buffer pointer Page 0 */ - u32 buff_ptr1; /* Buffer pointer Page 1 */ - u32 buff_ptr2; /* Buffer pointer Page 2 */ - u32 buff_ptr3; /* Buffer pointer Page 3 */ - u32 buff_ptr4; /* Buffer pointer Page 4 */ - u32 res; - /* 32 bytes */ - dma_addr_t td_dma; /* dma address for this td */ - /* virtual address of next td specified in next_td_ptr */ - struct ep_td_struct *next_td_virt; -}; - -/* Endpoint Transfer Descriptor bit Masks */ -#define DTD_NEXT_TERMINATE 0x00000001 -#define DTD_IOC 0x00008000 -#define DTD_STATUS_ACTIVE 0x00000080 -#define DTD_STATUS_HALTED 0x00000040 -#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 -#define DTD_STATUS_TRANSACTION_ERR 0x00000008 -#define DTD_RESERVED_FIELDS 0x80007300 -#define DTD_ADDR_MASK 0xFFFFFFE0 -#define DTD_PACKET_SIZE 0x7FFF0000 -#define DTD_LENGTH_BIT_POS 16 -#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ - DTD_STATUS_DATA_BUFF_ERR | \ - DTD_STATUS_TRANSACTION_ERR) -/* Alignment requirements; must be a power of two */ -#define DTD_ALIGNMENT 0x20 -#define QH_ALIGNMENT 2048 - -/* Controller dma boundary */ -#define UDC_DMA_BOUNDARY 0x1000 - -/*-------------------------------------------------------------------------*/ - /* ### driver private data */ struct fsl_req { @@ -1137,6 +762,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned length; u32 swap_temp; struct ep_td_struct *dtd; + unsigned long buf; /* how big will this transfer be? */ length = min(req->req.length - req->req.actual, @@ -1154,7 +780,13 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, dtd->size_ioc_sts = cpu_to_le32(swap_temp); /* Init all of buffer page pointers */ - swap_temp = (u32) (req->req.buf + req->req.actual); + buf = (unsigned long)req->req.buf; + if (buf > 0xffffffff) { + pr_err("Only 32bit supported\n"); + return NULL; + } + + swap_temp = (u32)(buf + req->req.actual); dtd->buff_ptr0 = cpu_to_le32(swap_temp); dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); @@ -1320,13 +952,19 @@ static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) if (req->queue.next != &ep->queue) { struct ep_queue_head *qh; struct fsl_req *next_req; + unsigned long next_req_head; qh = ep->qh; next_req = list_entry(req->queue.next, struct fsl_req, queue); /* Point the QH to the first TD of next request */ - writel((u32) next_req->head, &qh->curr_dtd_ptr); + next_req_head = (unsigned long)next_req->head; + if (next_req_head > 0xffffffff) { + pr_err("Only 32bit supported\n"); + goto out; + } + writel((u32)next_req_head, &qh->curr_dtd_ptr); } /* The request hasn't been processed, patch up the TD chain */ diff --git a/drivers/usb/gadget/fsl_udc_pbl.c b/drivers/usb/gadget/fsl_udc_pbl.c new file mode 100644 index 0000000000..978adf0667 --- /dev/null +++ b/drivers/usb/gadget/fsl_udc_pbl.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <common.h> +#include <usb/ch9.h> +#include <soc/fsl/fsl_udc.h> +#include <mach/imx8mm-regs.h> + +static void fsl_queue_td(struct usb_dr_device *dr, struct ep_td_struct *dtd, + int ep_is_in) +{ + int ep_index = 0; + int i = ep_index * 2 + ep_is_in; + u32 bitmask; + volatile struct ep_queue_head *dQH = + (void *)(unsigned long)readl(&dr->endpointlistaddr); + unsigned long td_dma = (unsigned long)dtd; + + dQH = &dQH[i]; + + bitmask = ep_is_in ? (1 << (ep_index + 16)) : (1 << (ep_index)); + + dQH->next_dtd_ptr = cpu_to_le32(td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK); + + dQH->size_ioc_int_sts &= cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); + + writel(bitmask, &dr->endpointprime); +} + +static struct ep_td_struct dtd_data __attribute__((aligned(64))); +static struct ep_td_struct dtd_status __attribute__((aligned(64))); + +static int fsl_ep_queue(struct usb_dr_device *dr, struct ep_td_struct *dtd, + void *buf, int len) +{ + u32 swap_temp; + + memset(dtd, 0, sizeof(*dtd)); + + /* Clear reserved field */ + swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp &= ~DTD_RESERVED_FIELDS; + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + swap_temp = (unsigned long)buf; + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + + /* Fill in the transfer size; set active bit */ + swap_temp = ((len << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE) | DTD_IOC; + + writel(cpu_to_le32(swap_temp), &dtd->size_ioc_sts); + + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + + fsl_queue_td(dr, dtd, len ? 0 : 1); + + return 0; +} + +enum state { + state_init = 0, + state_expect_command, + state_transfer_data, + state_complete, +}; + +#define MAX_TRANSFER_SIZE 2048 + +static enum state state; +static uint8_t databuf[MAX_TRANSFER_SIZE] __attribute__((aligned(64))); +static int actual; +static int to_transfer; +static void *image; + +static void tripwire_handler(struct usb_dr_device *dr, u8 ep_num) +{ + uint32_t val; + struct ep_queue_head *qh; + struct ep_queue_head *dQH = (void *)(unsigned long)readl(&dr->endpointlistaddr); + struct usb_ctrlrequest *ctrl; + + qh = &dQH[ep_num * 2]; + + val = readl(&dr->endptsetupstat); + val |= 1 << ep_num; + writel(val, &dr->endptsetupstat); + + do { + val = readl(&dr->usbcmd); + val |= USB_CMD_SUTW; + writel(val, &dr->usbcmd); + + ctrl = (void *)qh->setup_buffer; + if ((ctrl->wValue & 0xff) == 1) + state = state_expect_command; + + } while (!(readl(&dr->usbcmd) & USB_CMD_SUTW)); + + val = readl(&dr->usbcmd); + val &= ~USB_CMD_SUTW; + writel(val, &dr->usbcmd); + + fsl_ep_queue(dr, &dtd_data, databuf, MAX_TRANSFER_SIZE); +} + +static void dtd_complete_irq(struct usb_dr_device *dr) +{ + struct ep_td_struct *dtd = &dtd_data; + u32 bit_pos; + int len; + + /* Clear the bits in the register */ + bit_pos = readl(&dr->endptcomplete); + writel(bit_pos, &dr->endptcomplete); + + if (!(bit_pos & 1)) + return; + + len = MAX_TRANSFER_SIZE - + (le32_to_cpu(dtd->size_ioc_sts) >> DTD_LENGTH_BIT_POS); + + if (state == state_expect_command) { + state = state_transfer_data; + to_transfer = databuf[8] << 24 | + databuf[9] << 16 | + databuf[10] << 8 | + databuf[11]; + } else { + memcpy(image + actual, &databuf[1], len - 1); + actual += len - 1; + to_transfer -= len - 1; + + if (to_transfer == 0) + state = state_complete; + } + + fsl_ep_queue(dr, &dtd_status, NULL, 0); +} + +static int usb_irq(struct usb_dr_device *dr) +{ + uint32_t irq_src = readl(&dr->usbsts); + + irq_src &= ~0x80; + + if (!irq_src) + return -EAGAIN; + + /* Clear notification bits */ + writel(irq_src, &dr->usbsts); + + /* USB Interrupt */ + if (irq_src & USB_STS_INT) { + /* Setup package, we only support ep0 as control ep */ + if (readl(&dr->endptsetupstat) & EP_SETUP_STATUS_EP0) + tripwire_handler(dr, 0); + + /* completion of dtd */ + if (readl(&dr->endptcomplete)) + dtd_complete_irq(dr); + } + + if (state == state_complete) + return 0; + else + return -EAGAIN; +} + +int imx_barebox_load_usb(void __iomem *dr, void *dest) +{ + int ret; + + image = dest; + + while (1) { + ret = usb_irq(dr); + if (!ret) + break; + } + + return 0; +} + +int imx_barebox_start_usb(void __iomem *dr, void *dest) +{ + void __noreturn (*bb)(void); + int ret; + + ret = imx_barebox_load_usb(dr, dest); + if (ret) + return ret; + + printf("Downloading complete, start barebox\n"); + bb = dest; + bb(); +} + +int imx8mm_barebox_load_usb(void *dest) +{ + return imx_barebox_load_usb(IOMEM(MX8MM_USB1_BASE_ADDR), dest); +} + +int imx8mm_barebox_start_usb(void *dest) +{ + return imx_barebox_start_usb(IOMEM(MX8MM_USB1_BASE_ADDR), dest); +} diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index 3cf5d26dcd..03301d9c3e 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -323,6 +323,8 @@ static __maybe_unused struct of_device_id imx_chipidea_dt_ids[] = { { .compatible = "fsl,imx27-usb", }, { + .compatible = "fsl,imx7d-usb", + }, { /* sentinel */ }, }; diff --git a/drivers/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c index ef3892c220..aa4485ccba 100644 --- a/drivers/usb/imx/imx-usb-misc.c +++ b/drivers/usb/imx/imx-usb-misc.c @@ -599,6 +599,12 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { .data = &mx7_data, }, #endif +#ifdef CONFIG_ARCH_IMX8M + { + .compatible = "fsl,imx8mm-usbmisc", + .data = &mx7_data, + }, +#endif #ifdef CONFIG_ARCH_VF610 { .compatible = "fsl,vf610-usbmisc", |