diff options
Diffstat (limited to 'drivers')
133 files changed, 6813 insertions, 369 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 0e1f58c515b..3c0ad17138f 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -34,15 +34,15 @@ obj-$(CONFIG_$(PHASE_)SERIAL) += serial/ obj-$(CONFIG_$(PHASE_)SPI) += spi/ obj-$(CONFIG_$(PHASE_)TIMER) += timer/ obj-$(CONFIG_$(PHASE_)VIRTIO) += virtio/ -obj-$(CONFIG_$(XPL_)DM_MAILBOX) += mailbox/ -obj-$(CONFIG_$(XPL_)REMOTEPROC) += remoteproc/ -obj-$(CONFIG_$(XPL_)SYSINFO) += sysinfo/ +obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox/ +obj-$(CONFIG_$(PHASE_)REMOTEPROC) += remoteproc/ +obj-$(CONFIG_$(PHASE_)SYSINFO) += sysinfo/ obj-$(CONFIG_$(PHASE_)SM) += sm/ obj-$(CONFIG_$(PHASE_)TPM) += tpm/ -obj-$(CONFIG_$(XPL_)NVME) += nvme/ +obj-$(CONFIG_$(PHASE_)NVME) += nvme/ obj-$(CONFIG_XEN) += xen/ -obj-$(CONFIG_$(XPL_)FPGA) += fpga/ -obj-$(CONFIG_$(XPL_)VIDEO) += video/ +obj-$(CONFIG_$(PHASE_)FPGA) += fpga/ +obj-$(CONFIG_$(PHASE_)VIDEO) += video/ obj-y += bus/ @@ -55,7 +55,7 @@ obj-$(CONFIG_SPL_CRYPTO) += crypto/ obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR) += ddr/fsl/ obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/ obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/ -obj-$(CONFIG_$(XPL_)ALTERA_SDRAM) += ddr/altera/ +obj-$(CONFIG_$(PHASE_)ALTERA_SDRAM) += ddr/altera/ obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/ obj-$(CONFIG_IMX8ULP_DRAM) += ddr/imx/imx8ulp/ obj-$(CONFIG_ARCH_IMX9) += ddr/imx/imx9/ diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 69fa9b707e0..20e33f2f27a 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o obj-$(CONFIG_AHCI) += ahci-uclass.o -obj-$(CONFIG_$(XPL_)AHCI_PCI) += ahci-pci.o +obj-$(CONFIG_$(PHASE_)AHCI_PCI) += ahci-pci.o obj-$(CONFIG_SCSI_AHCI) += ahci.o obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o obj-$(CONFIG_FSL_SATA) += fsl_sata.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e593e228685..b532b3b7339 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -735,17 +735,6 @@ static int ata_scsiop_read_write(struct ahci_uc_priv *uc_priv, is_write ? "WRITE" : "READ"); return -EIO; } - - /* If this transaction is a write, do a following flush. - * Writes in u-boot are so rare, and the logic to know when is - * the last write and do a flush only there is sufficiently - * difficult. Just do a flush after every write. This incurs, - * usually, one extra flush when the rare writes do happen. - */ - if (is_write) { - if (-EIO == ata_io_flush(uc_priv, pccb->target)) - return -EIO; - } user_buffer += transfer_size; user_buffer_size -= transfer_size; blocks -= now_blocks; @@ -845,6 +834,9 @@ static int ahci_scsi_exec(struct udevice *dev, struct scsi_cmd *pccb) case SCSI_INQUIRY: ret = ata_scsiop_inquiry(uc_priv, pccb); break; + case SCSI_SYNC_CACHE: + ret = ata_io_flush(uc_priv, pccb->target); + break; default: printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]); return -ENOTSUPP; diff --git a/drivers/block/Makefile b/drivers/block/Makefile index ee290620545..f5a9d8637a3 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_$(PHASE_)BLK) += blk-uclass.o -ifndef CONFIG_$(XPL_)BLK +ifndef CONFIG_$(PHASE_)BLK obj-$(CONFIG_SPL_LEGACY_BLOCK) += blk_legacy.o endif diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 7daf8247247..2d242bc2886 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -8,4 +8,4 @@ obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o endif -obj-$(CONFIG_$(XPL_)TI_SYSC) += ti-sysc.o +obj-$(CONFIG_$(PHASE_)TI_SYSC) += ti-sysc.o diff --git a/drivers/button/button-qcom-pmic.c b/drivers/button/button-qcom-pmic.c index f9f0948ae09..e3bb9bd758a 100644 --- a/drivers/button/button-qcom-pmic.c +++ b/drivers/button/button-qcom-pmic.c @@ -73,25 +73,25 @@ static const struct qcom_pmic_btn_data qcom_pmic_btn_data_table[] = { .compatible = "qcom,pm8941-pwrkey", .status_bit = PON_KPDPWR_N_SET, .code = KEY_ENTER, - .label = "pwrkey", + .label = "Power Button", }, { .compatible = "qcom,pm8941-resin", .status_bit = PON_RESIN_N_SET, .code = KEY_DOWN, - .label = "vol_down", + .label = "Volume Down", }, { .compatible = "qcom,pmk8350-pwrkey", .status_bit = PON_GEN3_KPDPWR_N_SET, .code = KEY_ENTER, - .label = "pwrkey", + .label = "Power Button", }, { .compatible = "qcom,pmk8350-resin", .status_bit = PON_GEN3_RESIN_N_SET, .code = KEY_DOWN, - .label = "vol_down", + .label = "Volume Down", }, }; diff --git a/drivers/button/button-uclass.c b/drivers/button/button-uclass.c index 729983d5870..025917887e8 100644 --- a/drivers/button/button-uclass.c +++ b/drivers/button/button-uclass.c @@ -21,7 +21,7 @@ int button_get_by_label(const char *label, struct udevice **devp) struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); /* Ignore the top-level button node */ - if (uc_plat->label && !strcmp(label, uc_plat->label)) + if (uc_plat->label && !strcasecmp(label, uc_plat->label)) return uclass_get_device_tail(dev, 0, devp); } diff --git a/drivers/clk/clk-stub.c b/drivers/clk/clk-stub.c index 343fa5cd3fe..5f5aca41d5b 100644 --- a/drivers/clk/clk-stub.c +++ b/drivers/clk/clk-stub.c @@ -14,7 +14,7 @@ static const struct udevice_id nop_parent_ids[] = { { .compatible = "qcom,rpm-proc" }, { .compatible = "qcom,glink-rpm" }, - { .compatible = "qcom,rpm-sm6115" }, + { .compatible = "qcom,glink-smd-rpm" }, { } }; @@ -50,6 +50,7 @@ static struct clk_ops stub_clk_ops = { static const struct udevice_id stub_clk_ids[] = { { .compatible = "qcom,rpmcc" }, + { .compatible = "qcom,sdm845-rpmh-clk" }, { .compatible = "qcom,sc7280-rpmh-clk" }, { .compatible = "qcom,sm8150-rpmh-clk" }, { .compatible = "qcom,sm8250-rpmh-clk" }, diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 90b70529a47..4b3d812f9c6 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -623,14 +623,27 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!ops->set_parent) return -ENOSYS; + ret = clk_enable(parent); + if (ret) { + printf("Cannot enable parent %s\n", parent->dev->name); + return ret; + } + ret = ops->set_parent(clk, parent); - if (ret) + if (ret) { + clk_disable(parent); return ret; + } - if (CONFIG_IS_ENABLED(CLK_CCF)) + if (CONFIG_IS_ENABLED(CLK_CCF)) { ret = device_reparent(clk->dev, parent->dev); + if (ret) { + clk_disable(parent); + return ret; + } + } - return ret; + return 0; } int clk_enable(struct clk *clk) diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index bad579f8d5e..3d8ed21d3b9 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -14,7 +14,14 @@ #include "clk.h" +#if CONFIG_IS_ENABLED(VIDEO) +static u32 share_count_media; +#endif + static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +#endif static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -31,6 +38,12 @@ static const char * const imx8mp_hsio_axi_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const imx8mp_media_isp_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_400m", "audio_pll2_out", + "clk_ext1", "sys_pll2_500m", }; +#endif + static const char * const imx8mp_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",}; @@ -43,6 +56,16 @@ static const char * const imx8mp_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m" "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const imx8mp_media_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "sys_pll2_500m", }; + +static const char * const imx8mp_media_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "sys_pll1_133m", }; +#endif + static const char * const imx8mp_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", }; @@ -175,6 +198,17 @@ static const char * const imx8mp_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "s "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; +#if CONFIG_IS_ENABLED(VIDEO) +static const char * const imx8mp_media_disp_pix_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 * const imx8mp_media_ldb_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", + "clk_ext2", "audio_pll2_out", + "video_pll1_out", }; +#endif + static const char * const imx8mp_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", }; @@ -199,12 +233,19 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_CLK_DUMMY, clk_register_fixed_rate(NULL, "dummy", 0)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1_REF_SEL, imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); +#endif clk_dm(IMX8MP_DRAM_PLL_REF_SEL, imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MP_ARM_PLL_REF_SEL, imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MP_SYS_PLL1_REF_SEL, imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MP_SYS_PLL2_REF_SEL, imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MP_SYS_PLL3_REF_SEL, imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1, imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, + &imx_1443x_pll)); +#endif clk_dm(IMX8MP_DRAM_PLL, imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll)); clk_dm(IMX8MP_ARM_PLL, imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, @@ -216,12 +257,18 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_SYS_PLL3, imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1_BYPASS, imx_clk_mux_flags(dev, "video_pll1_bypass", base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT)); +#endif clk_dm(IMX8MP_DRAM_PLL_BYPASS, imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MP_ARM_PLL_BYPASS, imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MP_SYS_PLL1_BYPASS, imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MP_SYS_PLL2_BYPASS, imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MP_SYS_PLL3_BYPASS, imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_VIDEO_PLL1_OUT, imx_clk_gate(dev, "video_pll1_out", "video_pll1_bypass", base + 0x28, 13)); +#endif clk_dm(IMX8MP_DRAM_PLL_OUT, imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); clk_dm(IMX8MP_ARM_PLL_OUT, imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); clk_dm(IMX8MP_SYS_PLL1_OUT, imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); @@ -267,13 +314,23 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_CLK_A53_DIV, imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MP_CLK_HSIO_AXI, imx8m_clk_composite(dev, "hsio_axi", imx8mp_hsio_axi_sels, base + 0x8380)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_ISP, imx8m_clk_composite(dev, "media_isp", imx8mp_media_isp_sels, base + 0x8400)); +#endif clk_dm(IMX8MP_CLK_MAIN_AXI, imx8m_clk_composite_critical(dev, "main_axi", imx8mp_main_axi_sels, base + 0x8800)); clk_dm(IMX8MP_CLK_ENET_AXI, imx8m_clk_composite_critical(dev, "enet_axi", imx8mp_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MP_CLK_NAND_USDHC_BUS, imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mp_nand_usdhc_sels, base + 0x8900)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_AXI, imx8m_clk_composite(dev, "media_axi", imx8mp_media_axi_sels, base + 0x8a00)); + clk_dm(IMX8MP_CLK_MEDIA_APB, imx8m_clk_composite(dev, "media_apb", imx8mp_media_apb_sels, base + 0x8a80)); +#endif clk_dm(IMX8MP_CLK_NOC, imx8m_clk_composite_critical(dev, "noc", imx8mp_noc_sels, base + 0x8d00)); clk_dm(IMX8MP_CLK_NOC_IO, imx8m_clk_composite_critical(dev, "noc_io", imx8mp_noc_io_sels, base + 0x8d80)); clk_dm(IMX8MP_CLK_AHB, imx8m_clk_composite_critical(dev, "ahb_root", imx8mp_ahb_sels, base + 0x9000)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_DISP2_PIX, imx8m_clk_composite(dev, "media_disp2_pix", imx8mp_media_disp_pix_sels, base + 0x9300)); +#endif clk_dm(IMX8MP_CLK_IPG_ROOT, imx_clk_divider2(dev, "ipg_root", "ahb_root", base + 0x9080, 0, 1)); @@ -312,6 +369,10 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_CLK_WDOG, imx8m_clk_composite(dev, "wdog", imx8mp_wdog_sels, base + 0xb900)); clk_dm(IMX8MP_CLK_USDHC3, imx8m_clk_composite(dev, "usdhc3", imx8mp_usdhc3_sels, base + 0xbc80)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_DISP1_PIX, imx8m_clk_composite(dev, "media_disp1_pix", imx8mp_media_disp_pix_sels, base + 0xbe00)); + clk_dm(IMX8MP_CLK_MEDIA_LDB, imx8m_clk_composite(dev, "media_ldb", imx8mp_media_ldb_sels, base + 0xbf00)); +#endif clk_dm(IMX8MP_CLK_DRAM_ALT_ROOT, imx_clk_fixed_factor(dev, "dram_alt_root", "dram_alt", 1, 4)); clk_dm(IMX8MP_CLK_DRAM_CORE, imx_clk_mux2_flags(dev, "dram_core_clk", base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL)); @@ -355,6 +416,14 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_CLK_WDOG2_ROOT, imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MP_CLK_WDOG3_ROOT, imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MP_CLK_HSIO_ROOT, imx_clk_gate4(dev, "hsio_root_clk", "ipg_root", base + 0x45c0, 0)); +#if CONFIG_IS_ENABLED(VIDEO) + clk_dm(IMX8MP_CLK_MEDIA_APB_ROOT, imx_clk_gate2_shared2(dev, "media_apb_root_clk", "media_apb", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_AXI_ROOT, imx_clk_gate2_shared2(dev, "media_axi_root_clk", "media_axi", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT, imx_clk_gate2_shared2(dev, "media_disp1_pix_root_clk", "media_disp1_pix", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT, imx_clk_gate2_shared2(dev, "media_disp2_pix_root_clk", "media_disp2_pix", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_LDB_ROOT, imx_clk_gate2_shared2(dev, "media_ldb_root_clk", "media_ldb", base + 0x45d0, 0, &share_count_media)); + clk_dm(IMX8MP_CLK_MEDIA_ISP_ROOT, imx_clk_gate2_shared2(dev, "media_isp_root_clk", "media_isp", base + 0x45d0, 0, &share_count_media)); +#endif clk_dm(IMX8MP_CLK_USDHC3_ROOT, imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); diff --git a/drivers/clk/qcom/clock-apq8096.c b/drivers/clk/qcom/clock-apq8096.c index c77d69128b0..551f52d5197 100644 --- a/drivers/clk/qcom/clock-apq8096.c +++ b/drivers/clk/qcom/clock-apq8096.c @@ -83,11 +83,12 @@ static ulong apq8096_clk_set_rate(struct clk *clk, ulong rate) struct msm_clk_priv *priv = dev_get_priv(clk->dev); switch (clk->id) { - case GCC_SDCC1_APPS_CLK: /* SDC1 */ + case GCC_SDCC2_APPS_CLK: /* SDC2 */ return clk_init_sdc(priv, rate); break; case GCC_BLSP2_UART2_APPS_CLK: /*UART2*/ - return clk_init_uart(priv); + clk_init_uart(priv); + return 7372800; default: return 0; } diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c index 8691f08109b..9aff8a847ad 100644 --- a/drivers/clk/qcom/clock-sc7280.c +++ b/drivers/clk/qcom/clock-sc7280.c @@ -107,6 +107,17 @@ static const struct gate_clk sc7280_clks[] = { GATE_CLK(GCC_QUPV3_WRAP0_S0_CLK, 0x52008, BIT(10)), GATE_CLK(GCC_QUPV3_WRAP0_S1_CLK, 0x52008, BIT(11)), GATE_CLK(GCC_QUPV3_WRAP0_S3_CLK, 0x52008, BIT(13)), + GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x77010, BIT(0)), + GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x770cc, BIT(0)), + GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77018, BIT(0)), + GATE_CLK(GCC_UFS_PHY_UNIPRO_CORE_CLK, 0x7705c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_PHY_AUX_CLK, 0x7709c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_TX_SYMBOL_0_CLK, 0x7701c, BIT(0)), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_0_CLK, 0x77020, BIT(0)), + GATE_CLK(GCC_UFS_PHY_RX_SYMBOL_1_CLK, 0x770b8, BIT(0)), + GATE_CLK(GCC_UFS_1_CLKREF_EN, 0x8c000, BIT(0)), + GATE_CLK(GCC_SDCC2_AHB_CLK, 0x14008, BIT(0)), + GATE_CLK(GCC_SDCC2_APPS_CLK, 0x14004, BIT(0)), }; static int sc7280_enable(struct clk *clk) diff --git a/drivers/clk/qcom/clock-sdm845.c b/drivers/clk/qcom/clock-sdm845.c index 6a0bf16ba2d..5c8702ef2fe 100644 --- a/drivers/clk/qcom/clock-sdm845.c +++ b/drivers/clk/qcom/clock-sdm845.c @@ -77,7 +77,9 @@ static ulong sdm845_clk_set_rate(struct clk *clk, ulong rate) } static const struct gate_clk sdm845_clks[] = { + GATE_CLK(GCC_AGGRE_USB3_PRIM_AXI_CLK, 0x8201c, 0x00000001), GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x82020, 0x00000001), + GATE_CLK(GCC_CFG_NOC_USB3_PRIM_AXI_CLK, 0x0502c, 0x00000001), GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x05030, 0x00000001), GATE_CLK(GCC_QUPV3_WRAP0_S0_CLK, 0x5200c, 0x00000400), GATE_CLK(GCC_QUPV3_WRAP0_S1_CLK, 0x5200c, 0x00000800), @@ -112,6 +114,7 @@ static const struct gate_clk sdm845_clks[] = { GATE_CLK(GCC_UFS_CARD_TX_SYMBOL_0_CLK, 0x75014, 0x00000001), GATE_CLK(GCC_UFS_CARD_UNIPRO_CORE_CLK, 0x75054, 0x00000001), GATE_CLK(GCC_UFS_MEM_CLKREF_CLK, 0x8c000, 0x00000001), + GATE_CLK(GCC_AGGRE_UFS_PHY_AXI_CLK, 0x82024, 0x00000001), GATE_CLK(GCC_UFS_PHY_AHB_CLK, 0x77010, 0x00000001), GATE_CLK(GCC_UFS_PHY_AXI_CLK, 0x7700c, 0x00000001), GATE_CLK(GCC_UFS_PHY_ICE_CORE_CLK, 0x77058, 0x00000001), diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 657e589c286..a549890c22b 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -6,16 +6,16 @@ obj-y += device.o fdtaddr.o lists.o root.o uclass.o util.o tag.o obj-$(CONFIG_$(PHASE_)ACPIGEN) += acpi.o obj-$(CONFIG_$(PHASE_)DEVRES) += devres.o obj-$(CONFIG_$(PHASE_)DM_DEVICE_REMOVE) += device-remove.o -obj-$(CONFIG_$(XPL_)SIMPLE_BUS) += simple-bus.o +obj-$(CONFIG_$(PHASE_)SIMPLE_BUS) += simple-bus.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_DM) += dump.o obj-$(CONFIG_$(PHASE_)REGMAP) += regmap.o obj-$(CONFIG_$(PHASE_)SYSCON) += syscon-uclass.o -obj-$(CONFIG_$(XPL_)OF_LIVE) += of_access.o of_addr.o +obj-$(CONFIG_$(PHASE_)OF_LIVE) += of_access.o of_addr.o ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o endif -obj-$(CONFIG_$(XPL_)OF_PLATDATA) += read.o +obj-$(CONFIG_$(PHASE_)OF_PLATDATA) += read.o obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o ofnode_graph.o ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/drivers/core/ofnode_graph.c b/drivers/core/ofnode_graph.c index 90c92af3258..175ac768771 100644 --- a/drivers/core/ofnode_graph.c +++ b/drivers/core/ofnode_graph.c @@ -98,7 +98,7 @@ ofnode ofnode_graph_get_port_by_id(ofnode parent, u32 id) * @id: id for the endpoint * * Return: ofnode in given endpoint or ofnode_null() if not found. - * reg and port_reg are ignored when they are -1. + * reg_id and id are ignored when they are -1. */ ofnode ofnode_graph_get_endpoint_by_regs(ofnode parent, int reg_id, int id) { diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index f846a35d6b2..ce5e61bbaa6 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -16,6 +16,7 @@ #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/ofnode_graph.h> #include <dm/uclass.h> #include <dm/uclass-internal.h> #include <dm/util.h> @@ -582,6 +583,24 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, ret = uclass_find_device_by_phandle(id, parent, name, &dev); return uclass_get_device_tail(dev, ret, devp); } + +int uclass_get_device_by_endpoint(enum uclass_id class_id, struct udevice *dev, + int port_idx, int ep_idx, struct udevice **devp) +{ + ofnode node_source = dev_ofnode(dev); + ofnode node_dest = ofnode_graph_get_remote_node(node_source, port_idx, ep_idx); + struct udevice *target = NULL; + int ret; + + if (!ofnode_valid(node_dest)) + return -EINVAL; + + ret = uclass_find_device_by_ofnode(class_id, node_dest, &target); + if (ret) + return -ENODEV; + + return uclass_get_device_tail(target, 0, devp); +} #endif /* diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile index b19f3601813..c627f16711e 100644 --- a/drivers/ddr/altera/Makefile +++ b/drivers/ddr/altera/Makefile @@ -6,7 +6,7 @@ # (C) Copyright 2010, Thomas Chou <thomas@wytron.com.tw> # Copyright (C) 2014-2025 Altera Corporation <www.altera.com> -ifdef CONFIG_$(XPL_)ALTERA_SDRAM +ifdef CONFIG_$(PHASE_)ALTERA_SDRAM obj-$(CONFIG_TARGET_SOCFPGA_GEN5) += sdram_gen5.o sequencer.o obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += sdram_arria10.o obj-$(CONFIG_TARGET_SOCFPGA_STRATIX10) += sdram_soc64.o sdram_s10.o diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 6e1ab1c2ea5..eb4ae5e2074 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -3,12 +3,12 @@ # Copyright (C) 2012 Samsung Electronics # Lukasz Majewski <l.majewski@samsung.com> -obj-$(CONFIG_$(XPL_)DFU) += dfu.o -obj-$(CONFIG_$(XPL_)DFU_MMC) += dfu_mmc.o -obj-$(CONFIG_$(XPL_)DFU_MTD) += dfu_mtd.o -obj-$(CONFIG_$(XPL_)DFU_NAND) += dfu_nand.o -obj-$(CONFIG_$(XPL_)DFU_RAM) += dfu_ram.o -obj-$(CONFIG_$(XPL_)DFU_SF) += dfu_sf.o -obj-$(CONFIG_$(XPL_)DFU_WRITE_ALT) += dfu_alt.o -obj-$(CONFIG_$(XPL_)DFU_VIRT) += dfu_virt.o -obj-$(CONFIG_$(XPL_)DFU_SCSI) += dfu_scsi.o +obj-$(CONFIG_$(PHASE_)DFU) += dfu.o +obj-$(CONFIG_$(PHASE_)DFU_MMC) += dfu_mmc.o +obj-$(CONFIG_$(PHASE_)DFU_MTD) += dfu_mtd.o +obj-$(CONFIG_$(PHASE_)DFU_NAND) += dfu_nand.o +obj-$(CONFIG_$(PHASE_)DFU_RAM) += dfu_ram.o +obj-$(CONFIG_$(PHASE_)DFU_SF) += dfu_sf.o +obj-$(CONFIG_$(PHASE_)DFU_WRITE_ALT) += dfu_alt.o +obj-$(CONFIG_$(PHASE_)DFU_VIRT) += dfu_virt.o +obj-$(CONFIG_$(PHASE_)DFU_SCSI) += dfu_scsi.o diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index 1eb460f5a02..70207573de2 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -1,6 +1,5 @@ menu "Fastboot support" depends on CMDLINE - depends on !NET_LWIP config FASTBOOT bool diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c index 12ffb463deb..68f92c4b887 100644 --- a/drivers/fastboot/fb_common.c +++ b/drivers/fastboot/fb_common.c @@ -183,11 +183,15 @@ void fastboot_handle_boot(int command, bool success) switch (command) { case FASTBOOT_COMMAND_BOOT: fastboot_boot(); +#if CONFIG_IS_ENABLED(NET) net_set_state(NETLOOP_SUCCESS); +#endif break; case FASTBOOT_COMMAND_CONTINUE: +#if CONFIG_IS_ENABLED(NET) net_set_state(NETLOOP_SUCCESS); +#endif break; case FASTBOOT_COMMAND_REBOOT: diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 8b979f69ed9..9e7b118b074 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_FIRMWARE) += firmware-uclass.o -obj-$(CONFIG_$(XPL_)ARM_PSCI_FW) += psci.o +obj-$(CONFIG_$(PHASE_)ARM_PSCI_FW) += psci.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_SANDBOX) += firmware-sandbox.o obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c index 4b1b80d7abe..2940181e83e 100644 --- a/drivers/firmware/firmware-zynqmp.c +++ b/drivers/firmware/firmware-zynqmp.c @@ -5,6 +5,8 @@ * Copyright (C) 2018-2019 Xilinx, Inc. */ +#include <asm/arch/hardware.h> +#include <asm/io.h> #include <cpu_func.h> #include <dm.h> #include <dm/device_compat.h> @@ -169,6 +171,32 @@ unsigned int zynqmp_firmware_version(void) return pm_api_version; }; +#if defined(CONFIG_ARCH_VERSAL2) +int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value) +{ + *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY); + return 0; +} + +int zynqmp_pm_ufs_sram_csr_read(u32 *value) +{ + *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; +} + +int zynqmp_pm_ufs_sram_csr_write(u32 *value) +{ + writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR); + return 0; +} + +int zynqmp_pm_ufs_cal_reg(u32 *value) +{ + *value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET); + return 0; +} +#endif + int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, u32 value) { int ret; @@ -195,6 +223,52 @@ int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) return ret; } +u32 zynqmp_pm_get_bootmode_reg(void) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG); + if (ret) { + printf("%s: IOCTL_READ_REG is not supported failed with error code: %d\n" + , __func__, ret); + return 0; + } + + ret = xilinx_pm_request(PM_IOCTL, CRP_BOOT_MODE_REG_NODE, IOCTL_READ_REG, + CRP_BOOT_MODE_REG_OFFSET, 0, ret_payload); + if (ret) { + printf("%s: node 0x%x: get_bootmode 0x%x failed\n", + __func__, CRP_BOOT_MODE_REG_NODE, CRP_BOOT_MODE_REG_OFFSET); + return 0; + } + + return ret_payload[1]; +} + +u32 zynqmp_pm_get_pmc_multi_boot_reg(void) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG); + if (ret) { + printf("%s: IOCTL_READ_REG is not supported failed with error code: %d\n" + , __func__, ret); + return 0; + } + + ret = xilinx_pm_request(PM_IOCTL, PM_REG_PMC_GLOBAL_NODE, IOCTL_READ_REG, + PMC_MULTI_BOOT_MODE_REG_OFFSET, 0, ret_payload); + if (ret) { + printf("%s: node 0x%x: get_bootmode 0x%x failed\n", + __func__, PM_REG_PMC_GLOBAL_NODE, PMC_MULTI_BOOT_MODE_REG_OFFSET); + return 0; + } + + return ret_payload[1]; +} + int zynqmp_pm_feature(const u32 api_id) { int ret; diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 67d2f450024..3253f4211d6 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -20,6 +20,16 @@ #include "smt.h" +static void scmi_smt_enable_intr(struct scmi_smt *smt, bool enable) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + if (enable) + hdr->flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; + else + hdr->flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED; +} + /** * Get shared memory configuration defined by the referred DT phandle * Return with a errno compliant value. @@ -48,6 +58,9 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (!smt->buf) return -ENOMEM; + if (device_is_compatible(dev, "arm,scmi") && ofnode_has_property(dev_ofnode(dev), "mboxes")) + scmi_smt_enable_intr(smt, true); + #ifdef CONFIG_ARM if (dcache_status()) mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE), diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 190a1e3f5fc..54d6689ce78 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -696,20 +696,25 @@ static int ti_sci_cmd_put_device(const struct ti_sci_handle *handle, u32 id) MSG_DEVICE_SW_STATE_AUTO_OFF); } -static -int ti_sci_cmd_release_exclusive_devices(const struct ti_sci_handle *handle) +static int ti_sci_cmd_release_exclusive_devices(void) { struct ti_sci_exclusive_dev *dev, *tmp; struct ti_sci_info *info; int i, cnt; - info = handle_to_ti_sci_info(handle); - - list_for_each_entry_safe(dev, tmp, &info->dev_list, list) { - cnt = dev->count; - debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt); - for (i = 0; i < cnt; i++) - ti_sci_cmd_put_device(handle, dev->id); + /* + * Scan all ti_sci_list registrations, since with FIT images, we could + * have started with one device tree registration and switched over + * to a final version. This prevents exclusive devices identified + * during the first probe to be left orphan. + */ + list_for_each_entry(info, &ti_sci_list, list) { + list_for_each_entry_safe(dev, tmp, &info->dev_list, list) { + cnt = dev->count; + debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt); + for (i = 0; i < cnt; i++) + ti_sci_cmd_put_device(&info->handle, dev->id); + } } return 0; diff --git a/drivers/fpga/altera.c b/drivers/fpga/altera.c index ae06f0123a0..64fda3a307c 100644 --- a/drivers/fpga/altera.c +++ b/drivers/fpga/altera.c @@ -12,6 +12,10 @@ /* * Altera FPGA support */ +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX) || \ + IS_ENABLED(CONFIG_TARGET_SOCFPGA_STRATIX10) +#include <asm/arch/misc.h> +#endif #include <errno.h> #include <ACEX1K.h> #include <log.h> @@ -47,6 +51,43 @@ static const struct altera_fpga { #endif }; +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX) || \ + IS_ENABLED(CONFIG_TARGET_SOCFPGA_STRATIX10) +int fpga_is_partial_data(int devnum, size_t img_len) +{ + /* + * The FPGA data (full or partial) is checked by + * the SDM hardware, for Intel SDM Mailbox based + * devices. Hence always return full bitstream. + * + * For Cyclone V and Arria 10 family, the bitstream + * type parameter is not handled by the driver. + */ + return 0; +} + +int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, + bitstream_type bstype) +{ + int ret_val; + int flags = 0; + + ret_val = fpga_load(devnum, (void *)fpgadata, size, bstype, flags); + + /* + * Enable the HPS to FPGA bridges when FPGA load is completed + * successfully. This is to ensure the FPGA is accessible + * by the HPS. + */ + if (!ret_val) { + printf("Enable FPGA bridges\n"); + do_bridge_reset(1, ~0); + } + + return ret_val; +} +#endif + static int altera_validate(Altera_desc *desc, const char *fn) { if (!desc) { diff --git a/drivers/fpga/versalpl.c b/drivers/fpga/versalpl.c index 1957e8dcaca..d691f135e89 100644 --- a/drivers/fpga/versalpl.c +++ b/drivers/fpga/versalpl.c @@ -41,8 +41,15 @@ static int versal_load(xilinx_desc *desc, const void *buf, size_t bsize, buf_lo = lower_32_bits(bin_buf); buf_hi = upper_32_bits(bin_buf); - ret = xilinx_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_lo, - buf_hi, 0, ret_payload); + + if (desc->family == xilinx_versal2) { + ret = xilinx_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_hi, + buf_lo, 0, ret_payload); + } else { + ret = xilinx_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_lo, + buf_hi, 0, ret_payload); + } + if (ret) printf("PL FPGA LOAD failed with err: 0x%08x\n", ret); diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d426d3a2d7b..d64c14db5cf 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_DM_74X164) += 74x164_gpio.o endif obj-$(CONFIG_$(PHASE_)DM_GPIO) += gpio-uclass.o -obj-$(CONFIG_$(XPL_)DM_PCA953X) += pca953x_gpio.o +obj-$(CONFIG_$(PHASE_)DM_PCA953X) += pca953x_gpio.o obj-$(CONFIG_ADI_GPIO) += gpio-adi-adsp.o obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o @@ -59,12 +59,12 @@ obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o obj-$(CONFIG_HSDK_CREG_GPIO) += hsdk-creg-gpio.o obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o -obj-$(CONFIG_$(XPL_)PALMAS_GPIO) += palmas_gpio.o +obj-$(CONFIG_$(PHASE_)PALMAS_GPIO) += palmas_gpio.o obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o obj-$(CONFIG_OCTEON_GPIO) += octeon_gpio.o obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o obj-$(CONFIG_MSM_GPIO) += msm_gpio.o -obj-$(CONFIG_$(XPL_)PCF8575_GPIO) += pcf8575_gpio.o +obj-$(CONFIG_$(PHASE_)PCF8575_GPIO) += pcf8575_gpio.o obj-$(CONFIG_$(PHASE_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o @@ -73,11 +73,11 @@ obj-$(CONFIG_NX_GPIO) += nx_gpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o -obj-$(CONFIG_$(XPL_)MAX77663_GPIO) += max77663_gpio.o +obj-$(CONFIG_$(PHASE_)MAX77663_GPIO) += max77663_gpio.o obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o obj-$(CONFIG_FTGPIO010) += ftgpio010.o -obj-$(CONFIG_$(SPL_)ADP5585_GPIO) += adp5585_gpio.o +obj-$(CONFIG_$(PHASE_)ADP5585_GPIO) += adp5585_gpio.o obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c index cea073b3297..6783fc756f4 100644 --- a/drivers/gpio/msm_gpio.c +++ b/drivers/gpio/msm_gpio.c @@ -151,6 +151,9 @@ static int msm_gpio_direction_output(struct udevice *dev, unsigned int gpio, static int msm_gpio_set_flags(struct udevice *dev, unsigned int gpio, ulong flags) { + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return -EPERM; + if (flags & GPIOD_IS_OUT_ACTIVE) { return msm_gpio_direction_output(dev, gpio, 1); } else if (flags & GPIOD_IS_OUT) { @@ -172,12 +175,19 @@ static int msm_gpio_get_value_special(struct msm_gpio_bank *priv, unsigned int g const struct msm_special_pin_data *data; if (!priv->pin_data->special_pins_data) - return 0; + return -EINVAL; data = &priv->pin_data->special_pins_data[offset]; - if (!data->io_reg || data->in_bit >= 31) - return 0; + if (!data->io_reg) + return -EINVAL; + + if (data->in_bit >= 31) { + if (data->out_bit >= 31) + return -EINVAL; + + return !!(readl(priv->base + data->io_reg) >> data->out_bit); + } return !!(readl(priv->base + data->io_reg) >> data->in_bit); } @@ -186,19 +196,54 @@ static int msm_gpio_get_value(struct udevice *dev, unsigned int gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return -EPERM; + if (qcom_is_special_pin(priv->pin_data, gpio)) return msm_gpio_get_value_special(priv, gpio); return !!(readl(priv->base + GPIO_IN_OUT_REG(dev, gpio)) >> GPIO_IN); } +static int msm_gpio_get_function_special(struct msm_gpio_bank *priv, + unsigned int gpio) +{ + unsigned int offset = gpio - priv->pin_data->special_pins_start; + const struct msm_special_pin_data *data; + + if (!priv->pin_data->special_pins_data) + return GPIOF_UNKNOWN; + + data = &priv->pin_data->special_pins_data[offset]; + + /* No I/O fields, cannot control/read the I/O value */ + if (!data->io_reg || (data->out_bit >= 31 && data->in_bit >= 31)) + return GPIOF_FUNC; + + /* No Output-Enable register, cannot control I/O direction */ + if (!data->ctl_reg || data->oe_bit >= 31) { + if (data->out_bit >= 31) + return GPIOF_INPUT; + else + return GPIOF_OUTPUT; + } + + if (readl(priv->base + data->ctl_reg) & BIT(data->oe_bit)) + return GPIOF_OUTPUT; + + return GPIOF_INPUT; +} + static int msm_gpio_get_function(struct udevice *dev, unsigned int gpio) { struct msm_gpio_bank *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev_get_parent(dev), gpio)) + return GPIOF_UNKNOWN; + /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(priv->pin_data, gpio)) - return 0; + return msm_gpio_get_function_special(priv, gpio); if (readl(priv->base + GPIO_CONFIG_REG(dev, gpio)) & GPIO_OE_ENABLE) return GPIOF_OUTPUT; diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 2713289f7db..42326db85f6 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -2,15 +2,15 @@ # # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-$(CONFIG_$(XPL_)DM_I2C) += i2c-uclass.o -ifdef CONFIG_$(XPL_)ACPIGEN -obj-$(CONFIG_$(XPL_)DM_I2C) += acpi_i2c.o +obj-$(CONFIG_$(PHASE_)DM_I2C) += i2c-uclass.o +ifdef CONFIG_$(PHASE_)ACPIGEN +obj-$(CONFIG_$(PHASE_)DM_I2C) += acpi_i2c.o endif -obj-$(CONFIG_$(XPL_)DM_I2C_GPIO) += i2c-gpio.o -obj-$(CONFIG_$(XPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o -obj-$(CONFIG_$(XPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o +obj-$(CONFIG_$(PHASE_)DM_I2C_GPIO) += i2c-gpio.o +obj-$(CONFIG_$(PHASE_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o +obj-$(CONFIG_$(PHASE_)I2C_CROS_EC_LDO) += cros_ec_ldo.o -obj-$(CONFIG_$(XPL_)SYS_I2C_LEGACY) += i2c_core.o +obj-$(CONFIG_$(PHASE_)SYS_I2C_LEGACY) += i2c_core.o obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AST2600) += ast2600_i2c.o @@ -58,4 +58,4 @@ obj-$(CONFIG_SYS_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_SYS_I2C_XILINX_XIIC) += xilinx_xiic.o obj-$(CONFIG_TEGRA186_BPMP_I2C) += tegra186_bpmp_i2c.o -obj-$(CONFIG_$(XPL_)I2C_MUX) += muxes/ +obj-$(CONFIG_$(PHASE_)I2C_MUX) += muxes/ diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index c2b365af11d..c09f0ae795e 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -55,6 +55,12 @@ config BUTTON_KEYBOARD dt node to define button-event mapping. For example, an arrows and enter may be implemented to navigate boot menu. +config CPCAP_POWER_BUTTON + bool "Enable power button of CPCAP PMIC support" + depends on DM_KEYBOARD && DM_PMIC_CPCAP + help + Enable support for a dedicated power button of the CPCAP PMIC. + config CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support" depends on INPUT diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 8d4107b8848..1303fcdb0b7 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(PHASE_)CROS_EC_KEYB) += cros_ec_keyb.o obj-$(CONFIG_$(PHASE_)OF_CONTROL) += key_matrix.o obj-$(CONFIG_$(PHASE_)DM_KEYBOARD) += input.o keyboard-uclass.o obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o +obj-$(CONFIG_CPCAP_POWER_BUTTON) += cpcap_pwrbutton.o ifndef CONFIG_XPL_BUILD diff --git a/drivers/input/cpcap_pwrbutton.c b/drivers/input/cpcap_pwrbutton.c new file mode 100644 index 00000000000..c8ad39d33ca --- /dev/null +++ b/drivers/input/cpcap_pwrbutton.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <stdlib.h> +#include <dm.h> +#include <input.h> +#include <keyboard.h> +#include <power/pmic.h> +#include <power/cpcap.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/input.h> + +static const unsigned int cpcpap_to_reg[] = { + CPCAP_REG_INT1, + CPCAP_REG_INT2, + CPCAP_REG_INT3, + CPCAP_REG_INT4, +}; + +/** + * struct cpcap_pwrbutton_priv + * + * @bank: id of interrupt bank co-responding to an IRQ register + * @id: id of interrupt pin co-responding to the bit in IRQ register + * @keycode: linux key code + * @old_state: holder of last button state + * @skip: holder of keycode skip state. This is required since both pressing + * and releasing generate same event and cause key send duplication. + */ +struct cpcap_pwrbutton_priv { + u32 bank; + u32 id; + + u32 keycode; + + bool old_state; + bool skip; +}; + +static int cpcap_pwrbutton_read_keys(struct input_config *input) +{ + struct udevice *dev = input->dev; + struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev); + u32 value, state_changed; + bool state; + + value = pmic_reg_read(dev->parent, cpcpap_to_reg[priv->bank]) & + BIT(priv->id); + + /* Interrupt bit is cleared by writing it to interrupt reg */ + pmic_reg_write(dev->parent, cpcpap_to_reg[priv->bank], BIT(priv->id)); + + state = value >> priv->id; + state_changed = state != priv->old_state; + + if (state_changed && !priv->skip) { + priv->old_state = state; + input_add_keycode(input, priv->keycode, state); + } + + if (state) + priv->skip = !priv->skip; + + return 0; +} + +static int cpcap_pwrbutton_of_to_plat(struct udevice *dev) +{ + struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev); + ofnode irq_parent; + u32 irq_desc; + int ret; + + /* Check interrupt parent, driver supports only CPCAP as parent */ + irq_parent = ofnode_parse_phandle(dev_ofnode(dev), "interrupt-parent", 0); + if (!ofnode_device_is_compatible(irq_parent, "motorola,cpcap")) + return -EINVAL; + + ret = dev_read_u32(dev, "interrupts", &irq_desc); + if (ret) + return ret; + + /* IRQ registers are 16 bit wide */ + priv->bank = irq_desc / 16; + priv->id = irq_desc % 16; + + ret = dev_read_u32(dev, "linux,code", &priv->keycode); + if (ret) + return ret; + + priv->old_state = false; + priv->skip = false; + return 0; +} + +static int cpcap_pwrbutton_probe(struct udevice *dev) +{ + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret; + + input_init(input, false); + input_add_tables(input, false); + + /* Register the device */ + input->dev = dev; + input->read_keys = cpcap_pwrbutton_read_keys; + strcpy(sdev->name, "cpcap-pwrbutton"); + ret = input_stdio_register(sdev); + if (ret) { + log_debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct udevice_id cpcap_pwrbutton_ids[] = { + { .compatible = "motorola,cpcap-pwrbutton" }, + { } +}; + +U_BOOT_DRIVER(cpcap_pwrbutton) = { + .name = "cpcap_pwrbutton", + .id = UCLASS_KEYBOARD, + .of_match = cpcap_pwrbutton_ids, + .of_to_plat = cpcap_pwrbutton_of_to_plat, + .probe = cpcap_pwrbutton_probe, + .priv_auto = sizeof(struct cpcap_pwrbutton_priv), +}; diff --git a/drivers/led/Makefile b/drivers/led/Makefile index aa64a38b4e1..996753b88ae 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -10,6 +10,6 @@ obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_LED_BCM6753) += led_bcm6753.o obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o obj-$(CONFIG_LED_PWM) += led_pwm.o -obj-$(CONFIG_$(XPL_)LED_GPIO) += led_gpio.o +obj-$(CONFIG_$(PHASE_)LED_GPIO) += led_gpio.o obj-$(CONFIG_LED_CORTINA) += led_cortina.o obj-$(CONFIG_LED_LP5562) += led_lp5562.o diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 574add60005..e8c745f7d79 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -3,7 +3,7 @@ # Copyright (c) 2016, NVIDIA CORPORATION. # -obj-$(CONFIG_$(XPL_)DM_MAILBOX) += mailbox-uclass.o +obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox-uclass.o obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 834e0285097..0911d2fc0cc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -576,6 +576,7 @@ config QFW_SMBIOS bool default y depends on QFW && SMBIOS && !SANDBOX && !SYSINFO_SMBIOS + select BLOBLIST help Hidden option to read SMBIOS tables from QEMU. diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0b81ba2604f..248068d5b43 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_SANDBOX) += p2sb_sandbox.o p2sb_emul.o obj-$(CONFIG_SANDBOX) += swap_case.o endif -ifdef CONFIG_$(XPL_)DM_I2C +ifdef CONFIG_$(PHASE_)DM_I2C ifndef CONFIG_XPL_BUILD obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o @@ -37,14 +37,14 @@ obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_IIM) += fsl_iim.o obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o -obj-$(CONFIG_$(XPL_)FS_LOADER) += fs_loader.o +obj-$(CONFIG_$(PHASE_)FS_LOADER) += fs_loader.o obj-$(CONFIG_GATEWORKS_SC) += gsc.o obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o obj-$(CONFIG_GDSYS_SOC) += gdsys_soc.o obj-$(CONFIG_IRQ) += irq-uclass.o obj-$(CONFIG_SANDBOX) += irq_sandbox.o irq_sandbox_test.o -obj-$(CONFIG_$(XPL_)I2C_EEPROM) += i2c_eeprom.o +obj-$(CONFIG_$(PHASE_)I2C_EEPROM) += i2c_eeprom.o obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o obj-$(CONFIG_IMX8) += imx8/ obj-$(CONFIG_IMX_ELE) += imx_ele/ @@ -53,14 +53,14 @@ obj-$(CONFIG_LED_STATUS) += status_led.o obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o obj-$(CONFIG_$(PHASE_)LS2_SFP) += ls2_sfp.o -obj-$(CONFIG_$(XPL_)MXC_OCOTP) += mxc_ocotp.o +obj-$(CONFIG_$(PHASE_)MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NPCM_OTP) += npcm_otp.o obj-$(CONFIG_NPCM_HOST) += npcm_host_intf.o obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o -obj-$(CONFIG_$(XPL_)PWRSEQ) += pwrseq-uclass.o +obj-$(CONFIG_$(PHASE_)PWRSEQ) += pwrseq-uclass.o ifdef CONFIG_QFW obj-y += qfw.o obj-$(CONFIG_QFW_ACPI) += qfw_acpi.o diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 6740591a653..38867f30a7e 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -528,6 +528,7 @@ config SPL_MMC_SDHCI_ADMA config MMC_SDHCI_ADMA_FORCE_32BIT bool "Force 32 bit mode for ADMA on 64 bit platforms" + depends on MMC_SDHCI_ADMA || SPL_MMC_SDHCI_ADMA help This forces SDHCI ADMA to be built for 32 bit descriptors, even on a 64 bit platform where they would otherwise be assumed to @@ -537,6 +538,7 @@ config MMC_SDHCI_ADMA_FORCE_32BIT config MMC_SDHCI_ADMA_64BIT bool "Use SHDCI ADMA with 64 bit descriptors" + depends on MMC_SDHCI_ADMA || SPL_MMC_SDHCI_ADMA depends on !MMC_SDHCI_ADMA_FORCE_32BIT default y if DMA_ADDR_T_64BIT help @@ -869,7 +871,7 @@ config FTSDC010_SDIO config MMC_MTK bool "MediaTek SD/MMC Card Interface support" - depends on ARCH_MEDIATEK || ARCH_MTMIPS + depends on ARCH_MEDIATEK || ARCH_MTMIPS || ARCH_AIROHA depends on OF_CONTROL help This selects the MediaTek(R) Secure digital and Multimedia card Interface. diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 94ed28ead71..360706f53d2 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -4,17 +4,17 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. obj-y += mmc.o -obj-$(CONFIG_$(XPL_)DM_MMC) += mmc-uclass.o +obj-$(CONFIG_$(PHASE_)DM_MMC) += mmc-uclass.o ifdef CONFIG_$(PHASE_)DM_MMC obj-$(CONFIG_$(PHASE_)BOOTSTD) += mmc_bootdev.o endif obj-$(CONFIG_$(PHASE_)MMC_WRITE) += mmc_write.o -obj-$(CONFIG_$(XPL_)MMC_PWRSEQ) += mmc-pwrseq.o +obj-$(CONFIG_$(PHASE_)MMC_PWRSEQ) += mmc-pwrseq.o obj-$(CONFIG_MMC_SDHCI_ADMA_HELPERS) += sdhci-adma.o -ifndef CONFIG_$(XPL_)BLK +ifndef CONFIG_$(PHASE_)BLK obj-y += mmc_legacy.o endif diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index c8db4f811c2..9af84da1599 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -498,22 +498,12 @@ static int mmc_blk_probe(struct udevice *dev) return ret; } - ret = device_probe(dev); - if (ret) { - debug("Probing %s failed (err=%d)\n", dev->name, ret); - - mmc_deinit(mmc); - - return ret; - } - return 0; } -static int mmc_blk_remove(struct udevice *dev) +static int mmc_remove(struct udevice *dev) { - struct udevice *mmc_dev = dev_get_parent(dev); - struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc_dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; return mmc_deinit(mmc); @@ -533,7 +523,6 @@ U_BOOT_DRIVER(mmc_blk) = { .id = UCLASS_BLK, .ops = &mmc_blk_ops, .probe = mmc_blk_probe, - .remove = mmc_blk_remove, .flags = DM_FLAG_OS_PREPARE, }; #endif /* CONFIG_BLK */ @@ -543,4 +532,5 @@ UCLASS_DRIVER(mmc) = { .name = "mmc", .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_auto = sizeof(struct mmc_uclass_priv), + .pre_remove = mmc_remove, }; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 31a72366206..47139e0a911 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -3040,9 +3040,9 @@ static int mmc_complete_init(struct mmc *mmc) return err; } -static void __maybe_unused mmc_cyclic_cd_poll(struct cyclic_info *c) +static void mmc_cyclic_cd_poll(struct cyclic_info *c) { - struct mmc *m = CONFIG_IS_ENABLED(CYCLIC, (container_of(c, struct mmc, cyclic)), (NULL)); + struct mmc *m = container_of(c, struct mmc, cyclic); if (!m->has_init) return; @@ -3073,15 +3073,15 @@ int mmc_init(struct mmc *mmc) if (!err) err = mmc_complete_init(mmc); - if (err) + if (err) { pr_info("%s: %d, time %lu\n", __func__, err, get_timer(start)); + return err; + } if (CONFIG_IS_ENABLED(CYCLIC, (!mmc->cyclic.func), (NULL))) { /* Register cyclic function for card detect polling */ - CONFIG_IS_ENABLED(CYCLIC, (cyclic_register(&mmc->cyclic, - mmc_cyclic_cd_poll, - 100 * 1000, - mmc->cfg->name))); + cyclic_register(&mmc->cyclic, mmc_cyclic_cd_poll, 100 * 1000, + mmc->cfg->name); } return err; @@ -3092,7 +3092,7 @@ int mmc_deinit(struct mmc *mmc) u32 caps_filtered; if (CONFIG_IS_ENABLED(CYCLIC, (mmc->cyclic.func), (NULL))) - CONFIG_IS_ENABLED(CYCLIC, (cyclic_unregister(&mmc->cyclic))); + cyclic_unregister(&mmc->cyclic); if (!CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) && !CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) && diff --git a/drivers/mmc/mmc_boot.c b/drivers/mmc/mmc_boot.c index 367c957b518..986e6c500b1 100644 --- a/drivers/mmc/mmc_boot.c +++ b/drivers/mmc/mmc_boot.c @@ -8,20 +8,107 @@ #include <mmc.h> #include "mmc_private.h" -/* - * This function changes the size of boot partition and the size of rpmb - * partition present on EMMC devices. - * - * Input Parameters: - * struct *mmc: pointer for the mmc device strcuture - * bootsize: size of boot partition - * rpmbsize: size of rpmb partition - * - * Returns 0 on success. - */ +static int mmc_resize_boot_micron(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; -int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, - unsigned long rpmbsize) + /* Micron eMMC doesn't support resizing RPMB partition */ + (void)rpmbsize; + + /* BOOT partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + + if (bootsize > 0xff) + bootsize = 0xff; + + /* Set EXT_CSD[175] ERASE_GROUP_DEF to 0x01 */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 0x01); + if (err) + goto error; + + /* Set EXT_CSD[127:125] for BOOT partition size, [125] is low byte */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_SIZE_MULT_MICRON, bootsize); + if (err) + goto error; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_SIZE_MULT_MICRON + 1, 0x00); + if (err) + goto error; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_SIZE_MULT_MICRON + 2, 0x00); + if (err) + goto error; + + /* Set EXT_CSD[155] PARTITION_SETTING_COMPLETE to 0x01 */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PARTITION_SETTING, 0x01); + if (err) + goto error; + + return 0; + +error: + debug("%s: Error = %d\n", __func__, err); + return err; +} + +static int mmc_resize_boot_sandisk(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; + struct mmc_cmd cmd; + + /* BOOT/RPMB partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + rpmbsize = (rpmbsize * 1024) / 128; + + if (bootsize > 0xff) + bootsize = 0xff; + + if (rpmbsize > 0xff) + rpmbsize = 0xff; + + /* Send BOOT/RPMB resize op code */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG_SANDISK; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto error; + + /* Arg: BOOT partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = bootsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto error; + + /* Arg: RPMB partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = rpmbsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto error; + + return 0; + +error: + debug("%s: Error = %d\n", __func__, err); + return err; +} + +static int mmc_resize_boot_samsung(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) { int err; struct mmc_cmd cmd; @@ -32,10 +119,8 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = MMC_CMD62_ARG1; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error1 = %d\n", err); - return err; - } + if (err) + goto error; /* Boot partition changing mode */ cmd.cmdidx = MMC_CMD_RES_MAN; @@ -43,10 +128,9 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = MMC_CMD62_ARG2; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error2 = %d\n", err); - return err; - } + if (err) + goto error; + /* boot partition size is multiple of 128KB */ bootsize = (bootsize * 1024) / 128; @@ -56,10 +140,9 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = bootsize; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error3 = %d\n", err); - return err; - } + if (err) + goto error; + /* RPMB partition size is multiple of 128KB */ rpmbsize = (rpmbsize * 1024) / 128; /* Arg: RPMB partition size */ @@ -68,11 +151,43 @@ int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, cmd.cmdarg = rpmbsize; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) { - debug("mmc_boot_partition_size_change: Error4 = %d\n", err); - return err; - } + if (err) + goto error; + return 0; + +error: + debug("%s: Error = %d\n", __func__, err); + return err; +} + +/* + * This function changes the size of BOOT partition and the size of RPMB + * partition present on eMMC devices. + * + * Input Parameters: + * struct *mmc: pointer for the mmc device strcuture + * bootsize: size of BOOT partition + * rpmbsize: size of RPMB partition + * + * Returns 0 on success. + */ + +int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + switch (mmc->cid[0] >> 24) { + case CID_MANFID_MICRON: + return mmc_resize_boot_micron(mmc, bootsize, rpmbsize); + case CID_MANFID_SAMSUNG: + return mmc_resize_boot_samsung(mmc, bootsize, rpmbsize); + case CID_MANFID_SANDISK: + return mmc_resize_boot_sandisk(mmc, bootsize, rpmbsize); + default: + printf("Unsupported manufacturer id 0x%02x\n", + mmc->cid[0] >> 24); + return -EPERM; + } } /* diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c index c023d15e52a..90fcf2679bb 100644 --- a/drivers/mmc/mmc_write.c +++ b/drivers/mmc/mmc_write.c @@ -80,6 +80,8 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt) struct mmc *mmc = find_mmc_device(dev_num); lbaint_t blk = 0, blk_r = 0; int timeout_ms = 1000; + u32 grpcnt; + if (!mmc) return -1; @@ -123,6 +125,15 @@ ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt) } else { blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? mmc->erase_grp_size : (blkcnt - blk); + + grpcnt = (blkcnt - blk) / mmc->erase_grp_size; + /* Max 2GB per spec */ + if ((blkcnt - blk) > 0x400000) + blk_r = 0x400000; + else if (grpcnt) + blk_r = grpcnt * mmc->erase_grp_size; + else + blk_r = blkcnt - blk; } err = mmc_erase_t(mmc, start + blk, blk_r, erase_args); if (err) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4833b5158c7..dc7f0724a7b 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -177,8 +177,10 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) } while (!(stat & SDHCI_INT_DATA_END)); #if (CONFIG_IS_ENABLED(MMC_SDHCI_SDMA) || CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)) - dma_unmap_single(host->start_addr, data->blocks * data->blocksize, - mmc_get_dma_dir(data)); + if (host->flags & USE_DMA) { + dma_unmap_single(host->start_addr, data->blocks * data->blocksize, + mmc_get_dma_dir(data)); + } #endif return 0; diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f5ddfbf4b83..3a1e7e18736 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -941,6 +941,19 @@ spinand_select_op_variant(struct spinand_device *spinand, return NULL; } +static int spinand_setup_slave(struct spinand_device *spinand, + const struct spinand_info *spinand_info) +{ + struct spi_slave *slave = spinand->slave; + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (!ops->setup_for_spinand) + return 0; + + return ops->setup_for_spinand(slave, spinand_info); +} + /** * spinand_match_and_init() - Try to find a match between a device ID and an * entry in a spinand_info table @@ -964,6 +977,7 @@ int spinand_match_and_init(struct spinand_device *spinand, u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; + int ret; for (i = 0; i < table_size; i++) { const struct spinand_info *info = &table[i]; @@ -975,6 +989,10 @@ int spinand_match_and_init(struct spinand_device *spinand, if (memcmp(id + 1, info->devid.id, info->devid.len)) continue; + ret = spinand_setup_slave(spinand, info); + if (ret) + return ret; + nand->memorg = table[i].memorg; nand->eccreq = table[i].eccreq; spinand->eccinfo = table[i].eccinfo; diff --git a/drivers/mux/Makefile b/drivers/mux/Makefile index 63770e12bd1..b61f713c301 100644 --- a/drivers/mux/Makefile +++ b/drivers/mux/Makefile @@ -4,4 +4,4 @@ # Jean-Jacques Hiblot <jjhiblot@ti.com> obj-$(CONFIG_MULTIPLEXER) += mux-uclass.o -obj-$(CONFIG_$(XPL_)MUX_MMIO) += mmio.o +obj-$(CONFIG_$(PHASE_)MUX_MMIO) += mmio.o diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3db784faedd..a0a7890bd26 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -122,6 +122,14 @@ config AG7XXX This driver supports the Atheros AG7xxx Ethernet MAC. This MAC is present in the Atheros AR7xxx, AR9xxx and QCA9xxx MIPS chips. +config AIROHA_ETH + bool "Airoha Ethernet QDMA Driver" + depends on ARCH_AIROHA + select PHYLIB + select DM_RESET + help + This Driver support Airoha Ethernet QDMA Driver + Say Y to enable support for the Airoha Ethernet QDMA. config ALTERA_TSE bool "Altera Triple-Speed Ethernet MAC support" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d919d437c08..3244d39036d 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_AG7XXX) += ag7xxx.o +obj-$(CONFIG_AIROHA_ETH) += airoha_eth.o obj-$(CONFIG_ALTERA_TSE) += altera_tse.o obj-$(CONFIG_ASPEED_MDIO) += aspeed_mdio.o obj-$(CONFIG_BCM6348_ETH) += bcm6348-eth.o diff --git a/drivers/net/airoha_eth.c b/drivers/net/airoha_eth.c new file mode 100644 index 00000000000..7e35e1fd41d --- /dev/null +++ b/drivers/net/airoha_eth.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Based on Linux airoha_eth.c majorly rewritten + * and simplified for U-Boot usage for single TX/RX ring. + * + * Copyright (c) 2024 AIROHA Inc + * Author: Lorenzo Bianconi <lorenzo@kernel.org> + * Christian Marangi <ansuelsmth@gmail.org> + */ + +#include <dm.h> +#include <dm/devres.h> +#include <mapmem.h> +#include <net.h> +#include <regmap.h> +#include <reset.h> +#include <syscon.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/time.h> + +#define AIROHA_MAX_NUM_GDM_PORTS 1 +#define AIROHA_MAX_NUM_QDMA 1 +#define AIROHA_MAX_NUM_RSTS 3 +#define AIROHA_MAX_NUM_XSI_RSTS 4 + +#define AIROHA_MAX_PACKET_SIZE 2048 +#define AIROHA_NUM_TX_RING 1 +#define AIROHA_NUM_RX_RING 1 +#define AIROHA_NUM_TX_IRQ 1 +#define HW_DSCP_NUM 32 +#define IRQ_QUEUE_LEN 1 +#define TX_DSCP_NUM 16 +#define RX_DSCP_NUM PKTBUFSRX + +/* SCU */ +#define SCU_SHARE_FEMEM_SEL 0x958 + +/* SWITCH */ +#define SWITCH_MFC 0x10 +#define SWITCH_BC_FFP GENMASK(31, 24) +#define SWITCH_UNM_FFP GENMASK(23, 16) +#define SWITCH_UNU_FFP GENMASK(15, 8) +#define SWITCH_PMCR(_n) 0x3000 + ((_n) * 0x100) +#define SWITCH_IPG_CFG GENMASK(19, 18) +#define SWITCH_IPG_CFG_NORMAL FIELD_PREP(SWITCH_IPG_CFG, 0x0) +#define SWITCH_IPG_CFG_SHORT FIELD_PREP(SWITCH_IPG_CFG, 0x1) +#define SWITCH_IPG_CFG_SHRINK FIELD_PREP(SWITCH_IPG_CFG, 0x2) +#define SWITCH_MAC_MODE BIT(16) +#define SWITCH_FORCE_MODE BIT(15) +#define SWITCH_MAC_TX_EN BIT(14) +#define SWITCH_MAC_RX_EN BIT(13) +#define SWITCH_BKOFF_EN BIT(9) +#define SWITCH_BKPR_EN BIT(8) +#define SWITCH_FORCE_RX_FC BIT(5) +#define SWITCH_FORCE_TX_FC BIT(4) +#define SWITCH_FORCE_SPD GENMASK(3, 2) +#define SWITCH_FORCE_SPD_10 FIELD_PREP(SWITCH_FORCE_SPD, 0x0) +#define SWITCH_FORCE_SPD_100 FIELD_PREP(SWITCH_FORCE_SPD, 0x1) +#define SWITCH_FORCE_SPD_1000 FIELD_PREP(SWITCH_FORCE_SPD, 0x2) +#define SWITCH_FORCE_DPX BIT(1) +#define SWITCH_FORCE_LNK BIT(0) +#define SWITCH_SMACCR0 0x30e4 +#define SMACCR0_MAC2 GENMASK(31, 24) +#define SMACCR0_MAC3 GENMASK(23, 16) +#define SMACCR0_MAC4 GENMASK(15, 8) +#define SMACCR0_MAC5 GENMASK(7, 0) +#define SWITCH_SMACCR1 0x30e8 +#define SMACCR1_MAC0 GENMASK(15, 8) +#define SMACCR1_MAC1 GENMASK(7, 0) +#define SWITCH_PHY_POLL 0x7018 +#define SWITCH_PHY_AP_EN GENMASK(30, 24) +#define SWITCH_EEE_POLL_EN GENMASK(22, 16) +#define SWITCH_PHY_PRE_EN BIT(15) +#define SWITCH_PHY_END_ADDR GENMASK(12, 8) +#define SWITCH_PHY_ST_ADDR GENMASK(4, 0) + +/* FE */ +#define PSE_BASE 0x0100 +#define CSR_IFC_BASE 0x0200 +#define CDM1_BASE 0x0400 +#define GDM1_BASE 0x0500 +#define PPE1_BASE 0x0c00 + +#define CDM2_BASE 0x1400 +#define GDM2_BASE 0x1500 + +#define GDM3_BASE 0x1100 +#define GDM4_BASE 0x2500 + +#define GDM_BASE(_n) \ + ((_n) == 4 ? GDM4_BASE : \ + (_n) == 3 ? GDM3_BASE : \ + (_n) == 2 ? GDM2_BASE : GDM1_BASE) + +#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n) +#define GDM_DROP_CRC_ERR BIT(23) +#define GDM_IP4_CKSUM BIT(22) +#define GDM_TCP_CKSUM BIT(21) +#define GDM_UDP_CKSUM BIT(20) +#define GDM_UCFQ_MASK GENMASK(15, 12) +#define GDM_BCFQ_MASK GENMASK(11, 8) +#define GDM_MCFQ_MASK GENMASK(7, 4) +#define GDM_OCFQ_MASK GENMASK(3, 0) + +/* QDMA */ +#define REG_QDMA_GLOBAL_CFG 0x0004 +#define GLOBAL_CFG_RX_2B_OFFSET_MASK BIT(31) +#define GLOBAL_CFG_DMA_PREFERENCE_MASK GENMASK(30, 29) +#define GLOBAL_CFG_CPU_TXR_RR_MASK BIT(28) +#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK BIT(27) +#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK BIT(26) +#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK BIT(25) +#define GLOBAL_CFG_OAM_MODIFY_MASK BIT(24) +#define GLOBAL_CFG_RESET_MASK BIT(23) +#define GLOBAL_CFG_RESET_DONE_MASK BIT(22) +#define GLOBAL_CFG_MULTICAST_EN_MASK BIT(21) +#define GLOBAL_CFG_IRQ1_EN_MASK BIT(20) +#define GLOBAL_CFG_IRQ0_EN_MASK BIT(19) +#define GLOBAL_CFG_LOOPCNT_EN_MASK BIT(18) +#define GLOBAL_CFG_RD_BYPASS_WR_MASK BIT(17) +#define GLOBAL_CFG_QDMA_LOOPBACK_MASK BIT(16) +#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK GENMASK(13, 8) +#define GLOBAL_CFG_CHECK_DONE_MASK BIT(7) +#define GLOBAL_CFG_TX_WB_DONE_MASK BIT(6) +#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK GENMASK(5, 4) +#define GLOBAL_CFG_RX_DMA_BUSY_MASK BIT(3) +#define GLOBAL_CFG_RX_DMA_EN_MASK BIT(2) +#define GLOBAL_CFG_TX_DMA_BUSY_MASK BIT(1) +#define GLOBAL_CFG_TX_DMA_EN_MASK BIT(0) + +#define REG_FWD_DSCP_BASE 0x0010 +#define REG_FWD_BUF_BASE 0x0014 + +#define REG_HW_FWD_DSCP_CFG 0x0018 +#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK GENMASK(29, 28) +#define HW_FWD_DSCP_SCATTER_LEN_MASK GENMASK(17, 16) +#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK GENMASK(15, 0) + +#define REG_INT_STATUS(_n) \ + (((_n) == 4) ? 0x0730 : \ + ((_n) == 3) ? 0x0724 : \ + ((_n) == 2) ? 0x0720 : \ + ((_n) == 1) ? 0x0024 : 0x0020) + +#define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) + +#define REG_TX_IRQ_CFG(_n) ((_n) ? 0x004c : 0x0054) +#define TX_IRQ_THR_MASK GENMASK(27, 16) +#define TX_IRQ_DEPTH_MASK GENMASK(11, 0) + +#define REG_IRQ_CLEAR_LEN(_n) ((_n) ? 0x0064 : 0x0058) +#define IRQ_CLEAR_LEN_MASK GENMASK(7, 0) + +#define REG_TX_RING_BASE(_n) \ + (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5)) + +#define REG_TX_CPU_IDX(_n) \ + (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5)) + +#define TX_RING_CPU_IDX_MASK GENMASK(15, 0) + +#define REG_TX_DMA_IDX(_n) \ + (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5)) + +#define TX_RING_DMA_IDX_MASK GENMASK(15, 0) + +#define IRQ_RING_IDX_MASK GENMASK(20, 16) +#define IRQ_DESC_IDX_MASK GENMASK(15, 0) + +#define REG_RX_RING_BASE(_n) \ + (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5)) + +#define REG_RX_RING_SIZE(_n) \ + (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5)) + +#define RX_RING_THR_MASK GENMASK(31, 16) +#define RX_RING_SIZE_MASK GENMASK(15, 0) + +#define REG_RX_CPU_IDX(_n) \ + (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5)) + +#define RX_RING_CPU_IDX_MASK GENMASK(15, 0) + +#define REG_RX_DMA_IDX(_n) \ + (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5)) + +#define REG_RX_DELAY_INT_IDX(_n) \ + (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) + +#define RX_DELAY_INT_MASK GENMASK(15, 0) + +#define RX_RING_DMA_IDX_MASK GENMASK(15, 0) + +#define REG_LMGR_INIT_CFG 0x1000 +#define LMGR_INIT_START BIT(31) +#define LMGR_SRAM_MODE_MASK BIT(30) +#define HW_FWD_PKTSIZE_OVERHEAD_MASK GENMASK(27, 20) +#define HW_FWD_DESC_NUM_MASK GENMASK(16, 0) + +/* CTRL */ +#define QDMA_DESC_DONE_MASK BIT(31) +#define QDMA_DESC_DROP_MASK BIT(30) /* tx: drop - rx: overflow */ +#define QDMA_DESC_MORE_MASK BIT(29) /* more SG elements */ +#define QDMA_DESC_DEI_MASK BIT(25) +#define QDMA_DESC_NO_DROP_MASK BIT(24) +#define QDMA_DESC_LEN_MASK GENMASK(15, 0) +/* DATA */ +#define QDMA_DESC_NEXT_ID_MASK GENMASK(15, 0) +/* TX MSG0 */ +#define QDMA_ETH_TXMSG_MIC_IDX_MASK BIT(30) +#define QDMA_ETH_TXMSG_SP_TAG_MASK GENMASK(29, 14) +#define QDMA_ETH_TXMSG_ICO_MASK BIT(13) +#define QDMA_ETH_TXMSG_UCO_MASK BIT(12) +#define QDMA_ETH_TXMSG_TCO_MASK BIT(11) +#define QDMA_ETH_TXMSG_TSO_MASK BIT(10) +#define QDMA_ETH_TXMSG_FAST_MASK BIT(9) +#define QDMA_ETH_TXMSG_OAM_MASK BIT(8) +#define QDMA_ETH_TXMSG_CHAN_MASK GENMASK(7, 3) +#define QDMA_ETH_TXMSG_QUEUE_MASK GENMASK(2, 0) +/* TX MSG1 */ +#define QDMA_ETH_TXMSG_NO_DROP BIT(31) +#define QDMA_ETH_TXMSG_METER_MASK GENMASK(30, 24) /* 0x7f no meters */ +#define QDMA_ETH_TXMSG_FPORT_MASK GENMASK(23, 20) +#define QDMA_ETH_TXMSG_NBOQ_MASK GENMASK(19, 15) +#define QDMA_ETH_TXMSG_HWF_MASK BIT(14) +#define QDMA_ETH_TXMSG_HOP_MASK BIT(13) +#define QDMA_ETH_TXMSG_PTP_MASK BIT(12) +#define QDMA_ETH_TXMSG_ACNT_G1_MASK GENMASK(10, 6) /* 0x1f do not count */ +#define QDMA_ETH_TXMSG_ACNT_G0_MASK GENMASK(5, 0) /* 0x3f do not count */ + +/* RX MSG1 */ +#define QDMA_ETH_RXMSG_DEI_MASK BIT(31) +#define QDMA_ETH_RXMSG_IP6_MASK BIT(30) +#define QDMA_ETH_RXMSG_IP4_MASK BIT(29) +#define QDMA_ETH_RXMSG_IP4F_MASK BIT(28) +#define QDMA_ETH_RXMSG_L4_VALID_MASK BIT(27) +#define QDMA_ETH_RXMSG_L4F_MASK BIT(26) +#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21) +#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16) +#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0) + +struct airoha_qdma_desc { + __le32 rsv; + __le32 ctrl; + __le32 addr; + __le32 data; + __le32 msg0; + __le32 msg1; + __le32 msg2; + __le32 msg3; +}; + +struct airoha_qdma_fwd_desc { + __le32 addr; + __le32 ctrl0; + __le32 ctrl1; + __le32 ctrl2; + __le32 msg0; + __le32 msg1; + __le32 rsv0; + __le32 rsv1; +}; + +struct airoha_queue { + struct airoha_qdma_desc *desc; + u16 head; + + int ndesc; +}; + +struct airoha_tx_irq_queue { + struct airoha_qdma *qdma; + + int size; + u32 *q; +}; + +struct airoha_qdma { + struct airoha_eth *eth; + void __iomem *regs; + + struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; + struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; + + /* descriptor and packet buffers for qdma hw forward */ + struct { + void *desc; + void *q; + } hfwd; +}; + +struct airoha_gdm_port { + struct airoha_qdma *qdma; + int id; +}; + +struct airoha_eth { + void __iomem *fe_regs; + void __iomem *switch_regs; + + struct reset_ctl_bulk rsts; + struct reset_ctl_bulk xsi_rsts; + + struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA]; + struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS]; +}; + +static u32 airoha_rr(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static void airoha_wr(void __iomem *base, u32 offset, u32 val) +{ + writel(val, base + offset); +} + +static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) +{ + val |= (airoha_rr(base, offset) & ~mask); + airoha_wr(base, offset, val); + + return val; +} + +#define airoha_fe_rr(eth, offset) \ + airoha_rr((eth)->fe_regs, (offset)) +#define airoha_fe_wr(eth, offset, val) \ + airoha_wr((eth)->fe_regs, (offset), (val)) +#define airoha_fe_rmw(eth, offset, mask, val) \ + airoha_rmw((eth)->fe_regs, (offset), (mask), (val)) +#define airoha_fe_set(eth, offset, val) \ + airoha_rmw((eth)->fe_regs, (offset), 0, (val)) +#define airoha_fe_clear(eth, offset, val) \ + airoha_rmw((eth)->fe_regs, (offset), (val), 0) + +#define airoha_qdma_rr(qdma, offset) \ + airoha_rr((qdma)->regs, (offset)) +#define airoha_qdma_wr(qdma, offset, val) \ + airoha_wr((qdma)->regs, (offset), (val)) +#define airoha_qdma_rmw(qdma, offset, mask, val) \ + airoha_rmw((qdma)->regs, (offset), (mask), (val)) +#define airoha_qdma_set(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), 0, (val)) +#define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + +#define airoha_switch_wr(eth, offset, val) \ + airoha_wr((eth)->switch_regs, (offset), (val)) + +static void airoha_fe_maccr_init(struct airoha_eth *eth) +{ + int p; + + for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { + /* Disable any kind of CRC drop or offload */ + airoha_fe_wr(eth, REG_GDM_FWD_CFG(p), 0); + } +} + +static int airoha_fe_init(struct airoha_eth *eth) +{ + airoha_fe_maccr_init(eth); + + return 0; +} + +static void airoha_qdma_reset_rx_desc(struct airoha_queue *q, int index, + uchar *rx_packet) +{ + struct airoha_qdma_desc *desc; + u32 val; + + desc = &q->desc[index]; + index = (index + 1) % q->ndesc; + + dma_map_single(rx_packet, PKTSIZE_ALIGN, DMA_TO_DEVICE); + + WRITE_ONCE(desc->msg0, cpu_to_le32(0)); + WRITE_ONCE(desc->msg1, cpu_to_le32(0)); + WRITE_ONCE(desc->msg2, cpu_to_le32(0)); + WRITE_ONCE(desc->msg3, cpu_to_le32(0)); + WRITE_ONCE(desc->addr, cpu_to_le32(virt_to_phys(rx_packet))); + WRITE_ONCE(desc->data, cpu_to_le32(index)); + val = FIELD_PREP(QDMA_DESC_LEN_MASK, PKTSIZE_ALIGN); + WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); + + dma_map_single(desc, sizeof(*desc), DMA_TO_DEVICE); +} + +static void airoha_qdma_init_rx_desc(struct airoha_queue *q) +{ + int i; + + for (i = 0; i < q->ndesc; i++) + airoha_qdma_reset_rx_desc(q, i, net_rx_packets[i]); +} + +static int airoha_qdma_init_rx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int ndesc) +{ + int qid = q - &qdma->q_rx[0]; + unsigned long dma_addr; + + q->ndesc = ndesc; + q->head = 0; + + q->desc = dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + if (!q->desc) + return -ENOMEM; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + dma_map_single(q->desc, q->ndesc * sizeof(*q->desc), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); + airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), + RX_RING_SIZE_MASK, + FIELD_PREP(RX_RING_SIZE_MASK, ndesc)); + + airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK, + FIELD_PREP(RX_RING_THR_MASK, 0)); + airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, + FIELD_PREP(RX_RING_CPU_IDX_MASK, q->ndesc - 1)); + airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); + + return 0; +} + +static int airoha_qdma_init_rx(struct airoha_qdma *qdma) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { + int err; + + err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma, + RX_DSCP_NUM); + if (err) + return err; + } + + return 0; +} + +static int airoha_qdma_init_tx_queue(struct airoha_queue *q, + struct airoha_qdma *qdma, int size) +{ + int qid = q - &qdma->q_tx[0]; + unsigned long dma_addr; + + q->ndesc = size; + q->head = 0; + + q->desc = dma_alloc_coherent(q->ndesc * sizeof(*q->desc), &dma_addr); + if (!q->desc) + return -ENOMEM; + + memset(q->desc, 0, q->ndesc * sizeof(*q->desc)); + dma_map_single(q->desc, q->ndesc * sizeof(*q->desc), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head)); + airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, + FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head)); + + return 0; +} + +static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, + struct airoha_qdma *qdma, int size) +{ + int id = irq_q - &qdma->q_tx_irq[0]; + unsigned long dma_addr; + + irq_q->q = dma_alloc_coherent(size * sizeof(u32), &dma_addr); + if (!irq_q->q) + return -ENOMEM; + + memset(irq_q->q, 0xffffffff, size * sizeof(u32)); + irq_q->size = size; + irq_q->qdma = qdma; + + dma_map_single(irq_q->q, size * sizeof(u32), DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); + airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, + FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); + + return 0; +} + +static int airoha_qdma_init_tx(struct airoha_qdma *qdma) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { + err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma, + IRQ_QUEUE_LEN); + if (err) + return err; + } + + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { + err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma, + TX_DSCP_NUM); + if (err) + return err; + } + + return 0; +} + +static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) +{ + unsigned long dma_addr; + u32 status; + int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); + qdma->hfwd.desc = dma_alloc_coherent(size, &dma_addr); + if (!qdma->hfwd.desc) + return -ENOMEM; + + memset(qdma->hfwd.desc, 0, size); + dma_map_single(qdma->hfwd.desc, size, DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; + qdma->hfwd.q = dma_alloc_coherent(size, &dma_addr); + if (!qdma->hfwd.q) + return -ENOMEM; + + memset(qdma->hfwd.q, 0, size); + dma_map_single(qdma->hfwd.q, size, DMA_TO_DEVICE); + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK | + HW_FWD_DSCP_MIN_SCATTER_LEN_MASK, + FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0) | + FIELD_PREP(HW_FWD_DSCP_MIN_SCATTER_LEN_MASK, 1)); + airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, + FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | + LMGR_INIT_START); + + udelay(1000); + return read_poll_timeout(airoha_qdma_rr, status, + !(status & LMGR_INIT_START), USEC_PER_MSEC, + 30 * USEC_PER_MSEC, qdma, + REG_LMGR_INIT_CFG); +} + +static int airoha_qdma_hw_init(struct airoha_qdma *qdma) +{ + int i; + + /* clear pending irqs */ + for (i = 0; i < 2; i++) + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); + + airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_CPU_TXR_RR_MASK | + GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | + GLOBAL_CFG_IRQ0_EN_MASK | + GLOBAL_CFG_TX_WB_DONE_MASK | + FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 3)); + + /* disable qdma rx delay interrupt */ + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { + if (!qdma->q_rx[i].ndesc) + continue; + + airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i), + RX_DELAY_INT_MASK); + } + + return 0; +} + +static int airoha_qdma_init(struct udevice *dev, + struct airoha_eth *eth, + struct airoha_qdma *qdma) +{ + int err; + + qdma->eth = eth; + qdma->regs = dev_remap_addr_name(dev, "qdma0"); + if (IS_ERR(qdma->regs)) + return PTR_ERR(qdma->regs); + + err = airoha_qdma_init_rx(qdma); + if (err) + return err; + + err = airoha_qdma_init_tx(qdma); + if (err) + return err; + + err = airoha_qdma_init_hfwd_queues(qdma); + if (err) + return err; + + return airoha_qdma_hw_init(qdma); +} + +static int airoha_hw_init(struct udevice *dev, + struct airoha_eth *eth) +{ + int ret, i; + + /* disable xsi */ + ret = reset_assert_bulk(ð->xsi_rsts); + if (ret) + return ret; + + ret = reset_assert_bulk(ð->rsts); + if (ret) + return ret; + + mdelay(20); + + ret = reset_deassert_bulk(ð->rsts); + if (ret) + return ret; + + mdelay(20); + + ret = airoha_fe_init(eth); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) { + ret = airoha_qdma_init(dev, eth, ð->qdma[i]); + if (ret) + return ret; + } + + return 0; +} + +static int airoha_switch_init(struct udevice *dev, struct airoha_eth *eth) +{ + ofnode switch_node; + fdt_addr_t addr; + + switch_node = ofnode_by_compatible(ofnode_null(), "airoha,en7581-switch"); + if (!ofnode_valid(switch_node)) + return -EINVAL; + + addr = ofnode_get_addr(switch_node); + if (addr == FDT_ADDR_T_NONE) + return -ENOMEM; + + /* Switch doesn't have a DEV, gets address and setup Flood and CPU port */ + eth->switch_regs = map_sysmem(addr, 0); + + /* Set FLOOD, no CPU switch register */ + airoha_switch_wr(eth, SWITCH_MFC, SWITCH_BC_FFP | SWITCH_UNM_FFP | + SWITCH_UNU_FFP); + + /* Set CPU 6 PMCR */ + airoha_switch_wr(eth, SWITCH_PMCR(6), + SWITCH_IPG_CFG_SHORT | SWITCH_MAC_MODE | + SWITCH_FORCE_MODE | SWITCH_MAC_TX_EN | + SWITCH_MAC_RX_EN | SWITCH_BKOFF_EN | SWITCH_BKPR_EN | + SWITCH_FORCE_RX_FC | SWITCH_FORCE_TX_FC | + SWITCH_FORCE_SPD_1000 | SWITCH_FORCE_DPX | + SWITCH_FORCE_LNK); + + /* Sideband signal error for Port 3, which need the auto polling */ + airoha_switch_wr(eth, SWITCH_PHY_POLL, + FIELD_PREP(SWITCH_PHY_AP_EN, 0x7f) | + FIELD_PREP(SWITCH_EEE_POLL_EN, 0x7f) | + SWITCH_PHY_PRE_EN | + FIELD_PREP(SWITCH_PHY_END_ADDR, 0xc) | + FIELD_PREP(SWITCH_PHY_ST_ADDR, 0x8)); + + return 0; +} + +static int airoha_eth_probe(struct udevice *dev) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct regmap *scu_regmap; + ofnode scu_node; + int ret; + + scu_node = ofnode_by_compatible(ofnode_null(), "airoha,en7581-scu"); + if (!ofnode_valid(scu_node)) + return -EINVAL; + + scu_regmap = syscon_node_to_regmap(scu_node); + if (IS_ERR(scu_regmap)) + return PTR_ERR(scu_regmap); + + /* It seems by default the FEMEM_SEL is set to Memory (0x1) + * preventing any access to any QDMA and FrameEngine register + * reporting all 0xdeadbeef (poor cow :( ) + */ + regmap_write(scu_regmap, SCU_SHARE_FEMEM_SEL, 0x0); + + eth->fe_regs = dev_remap_addr_name(dev, "fe"); + if (!eth->fe_regs) + return -ENOMEM; + + eth->rsts.resets = devm_kcalloc(dev, AIROHA_MAX_NUM_RSTS, + sizeof(struct reset_ctl), GFP_KERNEL); + if (!eth->rsts.resets) + return -ENOMEM; + eth->rsts.count = AIROHA_MAX_NUM_RSTS; + + eth->xsi_rsts.resets = devm_kcalloc(dev, AIROHA_MAX_NUM_XSI_RSTS, + sizeof(struct reset_ctl), GFP_KERNEL); + if (!eth->xsi_rsts.resets) + return -ENOMEM; + eth->xsi_rsts.count = AIROHA_MAX_NUM_XSI_RSTS; + + ret = reset_get_by_name(dev, "fe", ð->rsts.resets[0]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "pdma", ð->rsts.resets[1]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "qdma", ð->rsts.resets[2]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "hsi0-mac", ð->xsi_rsts.resets[0]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "hsi1-mac", ð->xsi_rsts.resets[1]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "hsi-mac", ð->xsi_rsts.resets[2]); + if (ret) + return ret; + + ret = reset_get_by_name(dev, "xfp-mac", ð->xsi_rsts.resets[3]); + if (ret) + return ret; + + ret = airoha_hw_init(dev, eth); + if (ret) + return ret; + + return airoha_switch_init(dev, eth); +} + +static int airoha_eth_init(struct udevice *dev) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_queue *q; + int qid; + + qid = 0; + q = &qdma->q_rx[qid]; + + airoha_qdma_init_rx_desc(q); + + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); + + return 0; +} + +static void airoha_eth_stop(struct udevice *dev) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + + airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); +} + +static int airoha_eth_send(struct udevice *dev, void *packet, int length) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_qdma_desc *desc; + struct airoha_queue *q; + dma_addr_t dma_addr; + u32 msg0, msg1; + int qid, index; + u8 fport; + u32 val; + int i; + + dma_addr = dma_map_single(packet, length, DMA_TO_DEVICE); + + qid = 0; + q = &qdma->q_tx[qid]; + desc = &q->desc[q->head]; + index = (q->head + 1) % q->ndesc; + + fport = 1; + + msg0 = 0; + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); + + val = FIELD_PREP(QDMA_DESC_LEN_MASK, length); + WRITE_ONCE(desc->ctrl, cpu_to_le32(val)); + WRITE_ONCE(desc->addr, cpu_to_le32(dma_addr)); + val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index); + WRITE_ONCE(desc->data, cpu_to_le32(val)); + WRITE_ONCE(desc->msg0, cpu_to_le32(msg0)); + WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); + WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); + + dma_map_single(desc, sizeof(*desc), DMA_TO_DEVICE); + + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + + for (i = 0; i < 100; i++) { + dma_unmap_single(virt_to_phys(desc), sizeof(*desc), + DMA_FROM_DEVICE); + if (desc->ctrl & QDMA_DESC_DONE_MASK) + break; + + udelay(1); + } + + /* Return error if for some reason the descriptor never ACK */ + if (!(desc->ctrl & QDMA_DESC_DONE_MASK)) + return -EAGAIN; + + q->head = index; + airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(0), + IRQ_CLEAR_LEN_MASK, 1); + + return 0; +} + +static int airoha_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_qdma_desc *desc; + struct airoha_queue *q; + u16 length; + int qid; + + qid = 0; + q = &qdma->q_rx[qid]; + desc = &q->desc[q->head]; + + dma_unmap_single(virt_to_phys(desc), sizeof(*desc), + DMA_FROM_DEVICE); + + if (!(desc->ctrl & QDMA_DESC_DONE_MASK)) + return -EAGAIN; + + length = FIELD_GET(QDMA_DESC_LEN_MASK, desc->ctrl); + dma_unmap_single(desc->addr, length, + DMA_FROM_DEVICE); + + *packetp = phys_to_virt(desc->addr); + + return length; +} + +static int arht_eth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct airoha_eth *eth = dev_get_priv(dev); + struct airoha_qdma *qdma = ð->qdma[0]; + struct airoha_queue *q; + int qid; + + if (!packet) + return 0; + + qid = 0; + q = &qdma->q_rx[qid]; + + dma_map_single(packet, length, DMA_TO_DEVICE); + + airoha_qdma_reset_rx_desc(q, q->head, packet); + + airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid), RX_RING_CPU_IDX_MASK, + FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head)); + q->head = (q->head + 1) % q->ndesc; + + return 0; +} + +static int arht_eth_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct airoha_eth *eth = dev_get_priv(dev); + unsigned char *mac = pdata->enetaddr; + u32 macaddr_lsb, macaddr_msb; + + macaddr_lsb = FIELD_PREP(SMACCR0_MAC2, mac[2]) | + FIELD_PREP(SMACCR0_MAC3, mac[3]) | + FIELD_PREP(SMACCR0_MAC4, mac[4]) | + FIELD_PREP(SMACCR0_MAC5, mac[5]); + macaddr_msb = FIELD_PREP(SMACCR1_MAC1, mac[1]) | + FIELD_PREP(SMACCR1_MAC0, mac[0]); + + /* Set MAC for Switch */ + airoha_switch_wr(eth, SWITCH_SMACCR0, macaddr_lsb); + airoha_switch_wr(eth, SWITCH_SMACCR1, macaddr_msb); + + return 0; +} + +static const struct udevice_id airoha_eth_ids[] = { + { .compatible = "airoha,en7581-eth" }, +}; + +static const struct eth_ops airoha_eth_ops = { + .start = airoha_eth_init, + .stop = airoha_eth_stop, + .send = airoha_eth_send, + .recv = airoha_eth_recv, + .free_pkt = arht_eth_free_pkt, + .write_hwaddr = arht_eth_write_hwaddr, +}; + +U_BOOT_DRIVER(airoha_eth) = { + .name = "airoha-eth", + .id = UCLASS_ETH, + .of_match = airoha_eth_ids, + .probe = airoha_eth_probe, + .ops = &airoha_eth_ops, + .priv_auto = sizeof(struct airoha_eth), + .plat_auto = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 3132718e4f8..5b4cf30b0a3 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -79,6 +79,40 @@ config PHY_ADIN help Add support for configuring RGMII on Analog Devices ADIN PHYs. +menuconfig PHY_AIROHA + bool "Airoha Ethernet PHYs support" + +config PHY_AIROHA_EN8811H + bool "Airoha Ethernet EN8811H support" + depends on PHY_AIROHA + help + AIROHA EN8811H supported. + +choice + prompt "Location of the Airoha PHY firmware" + default PHY_AIROHA_FW_IN_MMC + depends on PHY_AIROHA_EN8811H + +config PHY_AIROHA_FW_IN_MMC + bool "Airoha firmware in MMC boot1 partition" + +endchoice + +config AIROHA_FW_ADDR + hex "Airoha Firmware Address" + depends on PHY_AIROHA_EN8811H + default 0x0 + +config AIROHA_MD32_DM_SIZE + hex "Airoha Firmware MD32 DM Size" + depends on PHY_AIROHA_EN8811H + default 0x4000 + +config AIROHA_MD32_DSP_SIZE + hex "Airoha Firmware MD32 DSP Size" + depends on PHY_AIROHA_EN8811H + default 0x20000 + menuconfig PHY_AQUANTIA bool "Aquantia Ethernet PHYs support" select PHY_GIGE diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 2487f366e1c..87dee3c15b9 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6352.o obj-$(CONFIG_PHYLIB) += phy.o obj-$(CONFIG_PHYLIB_10G) += generic_10g.o obj-$(CONFIG_PHY_ADIN) += adin.o +obj-$(CONFIG_PHY_AIROHA_EN8811H) += air_en8811h.o obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o obj-$(CONFIG_PHY_ATHEROS) += atheros.o obj-$(CONFIG_PHY_BROADCOM) += broadcom.o diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c new file mode 100644 index 00000000000..96bb24418a0 --- /dev/null +++ b/drivers/net/phy/air_en8811h.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * + * Limitations of the EN8811H: + * - Only full duplex supported + * - Forced speed (AN off) is not supported by hardware (100Mbps) + * + * Source originated from linux air_en8811h.c + * + * Copyright (C) 2025 Airoha Technology Corp. + */ +#include <phy.h> +#include <errno.h> +#include <malloc.h> +#include <asm/unaligned.h> +#include <linux/iopoll.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <mmc.h> + +#define EN8811H_PHY_ID 0x03a2a411 + +#define AIR_FW_ADDR_DM 0x00000000 +#define AIR_FW_ADDR_DSP 0x00100000 + +#define EN8811H_MD32_DM_SIZE 0x4000 +#define EN8811H_MD32_DSP_SIZE 0x20000 + + #define EN8811H_FW_CTRL_1 0x0f0018 + #define EN8811H_FW_CTRL_1_START 0x0 + #define EN8811H_FW_CTRL_1_FINISH 0x1 + #define EN8811H_FW_CTRL_2 0x800000 + #define EN8811H_FW_CTRL_2_LOADING BIT(11) + + /* MII Registers */ + #define AIR_AUX_CTRL_STATUS 0x1d + #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) + #define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 + #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 + #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc + +#define AIR_EXT_PAGE_ACCESS 0x1f +#define AIR_PHY_PAGE_STANDARD 0x0000 +#define AIR_PHY_PAGE_EXTENDED_4 0x0004 + +/* MII Registers Page 4*/ +#define AIR_BPBUS_MODE 0x10 +#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 +#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) +#define AIR_BPBUS_WR_ADDR_HIGH 0x11 +#define AIR_BPBUS_WR_ADDR_LOW 0x12 +#define AIR_BPBUS_WR_DATA_HIGH 0x13 +#define AIR_BPBUS_WR_DATA_LOW 0x14 +#define AIR_BPBUS_RD_ADDR_HIGH 0x15 +#define AIR_BPBUS_RD_ADDR_LOW 0x16 +#define AIR_BPBUS_RD_DATA_HIGH 0x17 +#define AIR_BPBUS_RD_DATA_LOW 0x18 + +/* Registers on MDIO_MMD_VEND1 */ +#define EN8811H_PHY_FW_STATUS 0x8009 +#define EN8811H_PHY_READY 0x02 + +/* Registers on MDIO_MMD_VEND2 */ +#define AIR_PHY_LED_BCR 0x021 +#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0) +#define AIR_PHY_LED_BCR_TIME_TEST BIT(2) +#define AIR_PHY_LED_BCR_CLK_EN BIT(3) +#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15) + +#define AIR_PHY_LED_DUR_ON 0x022 + +#define AIR_PHY_LED_DUR_BLINK 0x023 + +#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) +#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8)) +#define AIR_PHY_LED_ON_LINK1000 BIT(0) +#define AIR_PHY_LED_ON_LINK100 BIT(1) +#define AIR_PHY_LED_ON_LINK10 BIT(2) +#define AIR_PHY_LED_ON_LINKDOWN BIT(3) +#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +#define AIR_PHY_LED_ON_FORCE_ON BIT(6) +#define AIR_PHY_LED_ON_LINK2500 BIT(8) +#define AIR_PHY_LED_ON_POLARITY BIT(14) +#define AIR_PHY_LED_ON_ENABLE BIT(15) + +#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) +#define AIR_PHY_LED_BLINK_1000TX BIT(0) +#define AIR_PHY_LED_BLINK_1000RX BIT(1) +#define AIR_PHY_LED_BLINK_100TX BIT(2) +#define AIR_PHY_LED_BLINK_100RX BIT(3) +#define AIR_PHY_LED_BLINK_10TX BIT(4) +#define AIR_PHY_LED_BLINK_10RX BIT(5) +#define AIR_PHY_LED_BLINK_COLLISION BIT(6) +#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9) +#define AIR_PHY_LED_BLINK_2500TX BIT(10) +#define AIR_PHY_LED_BLINK_2500RX BIT(11) + +#define EN8811H_FW_VERSION 0x3b3c + +#define EN8811H_POLARITY 0xca0f8 +#define EN8811H_POLARITY_TX_NORMAL BIT(0) +#define EN8811H_POLARITY_RX_REVERSE BIT(1) + +#define EN8811H_CLK_CGM 0xcf958 +#define EN8811H_CLK_CGM_CKO BIT(26) +#define EN8811H_HWTRAP1 0xcf914 +#define EN8811H_HWTRAP1_CKO BIT(12) + +#define air_upper_16_bits(n) ((u16)((n) >> 16)) +#define air_lower_16_bits(n) ((u16)((n) & 0xffff)) + +/* Led definitions */ +#define EN8811H_LED_COUNT 3 + +/* Default LED setup: + * GPIO5 <-> LED0 On: Link detected + * GPIO4 <-> LED1 On: Link detected at 2500 and 1000 Mbps + * GPIO3 <-> LED2 On: Link detected at 2500 and 100 Mbps + */ +#define AIR_DEFAULT_TRIGGER_LED0 (AIR_PHY_LED_ON_LINK2500 | \ + AIR_PHY_LED_ON_LINK1000 | \ + AIR_PHY_LED_ON_LINK100) +#define AIR_DEFAULT_TRIGGER_LED1 (AIR_PHY_LED_ON_LINK2500 | \ + AIR_PHY_LED_ON_LINK1000 | \ + AIR_PHY_LED_BLINK_2500TX | \ + AIR_PHY_LED_BLINK_2500RX | \ + AIR_PHY_LED_BLINK_1000TX | \ + AIR_PHY_LED_BLINK_1000RX) +#define AIR_DEFAULT_TRIGGER_LED2 (AIR_PHY_LED_ON_LINK2500 | \ + AIR_PHY_LED_ON_LINK100 | \ + AIR_PHY_LED_BLINK_2500TX | \ + AIR_PHY_LED_BLINK_2500RX | \ + AIR_PHY_LED_BLINK_100TX | \ + AIR_PHY_LED_BLINK_100RX) + +struct led { + unsigned long rules; +}; + +enum { + AIR_PHY_LED_DUR_BLINK_32MS, + AIR_PHY_LED_DUR_BLINK_64MS, + AIR_PHY_LED_DUR_BLINK_128MS, + AIR_PHY_LED_DUR_BLINK_256MS, + AIR_PHY_LED_DUR_BLINK_512MS, + AIR_PHY_LED_DUR_BLINK_1024MS, +}; + +enum { + AIR_LED_DISABLE, + AIR_LED_ENABLE, +}; + +enum { + AIR_ACTIVE_LOW, + AIR_ACTIVE_HIGH, +}; + +enum { + AIR_LED_MODE_DISABLE, + AIR_LED_MODE_USER_DEFINE, +}; + +#define AIR_PHY_LED_DUR_UNIT 781 +#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) + +struct en8811h_priv { + int firmware_version; + bool mcu_needs_restart; + struct led led[EN8811H_LED_COUNT]; +}; + +static int air_phy_read_page(struct phy_device *phydev) +{ + return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS); +} + +static int air_phy_write_page(struct phy_device *phydev, int page) +{ + return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page); +} + +int air_phy_select_page(struct phy_device *phydev, int page) +{ + int ret, oldpage; + + oldpage = air_phy_read_page(phydev); + if (oldpage < 0) + return oldpage; + + if (oldpage != page) { + ret = air_phy_write_page(phydev, page); + if (ret < 0) + return ret; + } + + return oldpage; +} + +int air_phy_restore_page(struct phy_device *phydev, int oldpage, int ret) +{ + int r; + + if (oldpage >= 0) { + r = air_phy_write_page(phydev, oldpage); + + if (ret >= 0 && r < 0) + ret = r; + } else { + ret = oldpage; + } + + return ret; +} + +static int air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ + int ret, saved_page; + + saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH, + air_upper_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW, + air_lower_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, + air_upper_16_bits(pbus_data)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, + air_lower_16_bits(pbus_data)); + if (ret < 0) + goto restore_page; + } + +restore_page: + if (ret < 0) + printf("%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + + return air_phy_restore_page(phydev, saved_page, ret); +} + +static int air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ + int pbus_data_low, pbus_data_high; + int ret = 0, saved_page; + + saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH, + air_upper_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW, + air_lower_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) { + ret = pbus_data_high; + goto restore_page; + } + + pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) { + ret = pbus_data_low; + goto restore_page; + } + + *pbus_data = pbus_data_low | (pbus_data_high << 16); + } + +restore_page: + if (ret < 0) + printf("%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + + return air_phy_restore_page(phydev, saved_page, ret); +} + +static int air_buckpbus_reg_modify(struct phy_device *phydev, + u32 pbus_address, u32 mask, u32 set) +{ + int pbus_data_low, pbus_data_high; + u32 pbus_data_old, pbus_data_new; + int ret = 0, saved_page; + + saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH, + air_upper_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW, + air_lower_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return pbus_data_high; + + pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return pbus_data_low; + + pbus_data_old = pbus_data_low | (pbus_data_high << 16); + pbus_data_new = (pbus_data_old & ~mask) | set; + if (pbus_data_new == pbus_data_old) + return 0; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH, + air_upper_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW, + air_lower_16_bits(pbus_address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, + air_upper_16_bits(pbus_data_new)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, + air_lower_16_bits(pbus_data_new)); + if (ret < 0) + goto restore_page; + } + +restore_page: + if (ret < 0) + printf("%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + + return air_phy_restore_page(phydev, saved_page, ret); +} + +static int air_write_buf(struct phy_device *phydev, unsigned long address, + unsigned long array_size, const unsigned char *buffer) +{ + unsigned int offset; + int ret, saved_page; + u16 val; + + saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >= 0) { + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH, + air_upper_16_bits(address)); + if (ret < 0) + goto restore_page; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW, + air_lower_16_bits(address)); + if (ret < 0) + goto restore_page; + + for (offset = 0; offset < array_size; offset += 4) { + val = get_unaligned_le16(&buffer[offset + 2]); + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, val); + if (ret < 0) + goto restore_page; + + val = get_unaligned_le16(&buffer[offset]); + ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, val); + if (ret < 0) + goto restore_page; + } + } + +restore_page: + if (ret < 0) + printf("%s 0x%08lx failed: %d\n", __func__, + address, ret); + + return air_phy_restore_page(phydev, saved_page, ret); +} + +__weak ulong *en8811h_get_fw_addr(void) +{ + return (ulong *)CONFIG_AIROHA_FW_ADDR; +} + +static int en8811h_wait_mcu_ready(struct phy_device *phydev) +{ + int ret, reg_value; + + /* Because of mdio-lock, may have to wait for multiple loads */ + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + EN8811H_PHY_FW_STATUS, reg_value, + reg_value == EN8811H_PHY_READY, + 20000, 7500000, true); + if (ret) { + printf("MCU not ready: 0x%x\n", reg_value); + return -ENODEV; + } + + return 0; +} + +static int en8811h_load_firmware(struct phy_device *phydev) +{ + int ret; + char *addr = NULL; + struct en8811h_priv *priv = phydev->priv; + int dev = CONFIG_SYS_MMC_ENV_DEV; + u32 cnt = (CONFIG_AIROHA_MD32_DM_SIZE + + CONFIG_AIROHA_MD32_DSP_SIZE) / 512; + ulong airoha_fw_addr = (ulong)en8811h_get_fw_addr(); + u32 blk = airoha_fw_addr / 512; + + addr = malloc(CONFIG_AIROHA_MD32_DM_SIZE + CONFIG_AIROHA_MD32_DSP_SIZE); + if (!addr) { + puts("cannot allocated buffer for firmware.\n"); + return -ENOMEM; + } + + if (IS_ENABLED(CONFIG_PHY_AIROHA_FW_IN_MMC)) { + struct mmc *mmc = find_mmc_device(dev); + + if (!mmc) { + puts("Failed to find MMC device for Airoha ucode\n"); + goto en8811h_load_firmware_out; + } + + printf("MMC read: dev # %u, block # %u, count %u ...\n", + dev, blk, cnt); + + if (mmc_init(mmc)) { + puts("initializing MMC device failed.\n"); + goto en8811h_load_firmware_out; + } + + ret = mmc_set_part_conf(mmc, 1, 2, 2); + if (ret) { + puts("cannot access eMMC boot1 hw partition.\n"); + goto en8811h_load_firmware_out; + } + + (void)blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr); + + mmc_set_part_conf(mmc, 1, 1, 0); + + } else { + puts("EN8811H firmware loading not implemented"); + free(addr); + addr = NULL; + return -EOPNOTSUPP; + } + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); + if (ret < 0) + return ret; + + ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, + EN8811H_FW_CTRL_2_LOADING); + if (ret < 0) + return ret; + + ret = air_write_buf(phydev, AIR_FW_ADDR_DM, CONFIG_AIROHA_MD32_DM_SIZE, addr); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, CONFIG_AIROHA_MD32_DSP_SIZE, + addr + CONFIG_AIROHA_MD32_DM_SIZE); + if (ret < 0) + goto en8811h_load_firmware_out; + + ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, 0); + if (ret < 0) + return ret; + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); + if (ret < 0) + return ret; + + ret = en8811h_wait_mcu_ready(phydev); + + air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, + &priv->firmware_version); + printf("MD32 firmware version: %08x\n", + priv->firmware_version); + +en8811h_load_firmware_out: + free(addr); + if (ret < 0) + printf("Firmware loading failed: %d\n", ret); + + return ret; +} + +static int en8811h_restart_mcu(struct phy_device *phydev) +{ + int ret; + + ret = phy_write_mmd(phydev, 0x1e, 0x8009, 0x0); + if (ret < 0) + return ret; + + ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); + if (ret < 0) + return ret; + + return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); +} + +static int air_led_hw_control_set(struct phy_device *phydev, + u8 index, unsigned long rules) +{ + struct en8811h_priv *priv = phydev->priv; + u16 on = 0, blink = 0; + int ret; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + on |= rules & (AIR_PHY_LED_ON_LINK100 | + AIR_PHY_LED_ON_LINK1000 | + AIR_PHY_LED_ON_LINK2500); + + blink |= rules & (AIR_PHY_LED_BLINK_100TX | + AIR_PHY_LED_BLINK_100RX | + AIR_PHY_LED_BLINK_1000TX | + AIR_PHY_LED_BLINK_1000RX | + AIR_PHY_LED_BLINK_2500TX | + AIR_PHY_LED_BLINK_2500RX); + + if (blink || on) { + on &= ~AIR_PHY_LED_ON_FORCE_ON; + blink &= ~AIR_PHY_LED_BLINK_FORCE_BLINK; + } else { + priv->led[index].rules = 0; + } + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), + AIR_PHY_LED_ON_MASK, on); + if (ret < 0) + return ret; + + return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index), + blink); +} + +static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) +{ + int val = 0; + int err; + + if (index >= EN8811H_LED_COUNT) + return -EINVAL; + + if (state == AIR_LED_ENABLE) + val |= AIR_PHY_LED_ON_ENABLE; + else + val &= ~AIR_PHY_LED_ON_ENABLE; + + if (pol == AIR_ACTIVE_HIGH) + val |= AIR_PHY_LED_ON_POLARITY; + else + val &= ~AIR_PHY_LED_ON_POLARITY; + + err = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_ON(index), val); + if (err < 0) + return err; + + return 0; +} + +static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) +{ + int ret, i; + struct en8811h_priv *priv = phydev->priv; + + ret = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_DUR_BLINK, dur); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_DUR_ON, dur >> 1); + if (ret < 0) + return ret; + + switch (mode) { + case AIR_LED_MODE_DISABLE: + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, + AIR_PHY_LED_BCR_EXT_CTRL | + AIR_PHY_LED_BCR_MODE_MASK, 0); + break; + case AIR_LED_MODE_USER_DEFINE: + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, + AIR_PHY_LED_BCR_EXT_CTRL | + AIR_PHY_LED_BCR_CLK_EN, + AIR_PHY_LED_BCR_EXT_CTRL | + AIR_PHY_LED_BCR_CLK_EN); + if (ret < 0) + return ret; + break; + default: + printf("LED mode %d is not supported\n", mode); + return -EINVAL; + } + + for (i = 0; i < num; ++i) { + ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); + if (ret < 0) { + printf("LED%d init failed: %d\n", i, ret); + return ret; + } + air_led_hw_control_set(phydev, i, priv->led[i].rules); + } + + return 0; +} + +static int en8811h_config(struct phy_device *phydev) +{ + ofnode node = phy_get_ofnode(phydev); + struct en8811h_priv *priv = phydev->priv; + int ret = 0; + u32 pbus_value = 0; + + /* If restart happened in .probe(), no need to restart now */ + if (priv->mcu_needs_restart) { + ret = en8811h_restart_mcu(phydev); + if (ret < 0) + return ret; + } else { + ret = en8811h_load_firmware(phydev); + if (ret) { + printf("Load firmware fail.\n"); + return ret; + } + /* Next calls to .config() mcu needs to restart */ + priv->mcu_needs_restart = true; + } + + ret = phy_write_mmd(phydev, 0x1e, 0x800c, 0x0); + ret |= phy_write_mmd(phydev, 0x1e, 0x800d, 0x0); + ret |= phy_write_mmd(phydev, 0x1e, 0x800e, 0x1101); + ret |= phy_write_mmd(phydev, 0x1e, 0x800f, 0x0002); + if (ret < 0) + return ret; + + /* Serdes polarity */ + pbus_value = 0; + if (ofnode_read_bool(node, "airoha,pnswap-rx")) + pbus_value |= EN8811H_POLARITY_RX_REVERSE; + else + pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; + if (ofnode_read_bool(node, "airoha,pnswap-tx")) + pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; + else + pbus_value |= EN8811H_POLARITY_TX_NORMAL; + ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, + EN8811H_POLARITY_RX_REVERSE | + EN8811H_POLARITY_TX_NORMAL, pbus_value); + if (ret < 0) + return ret; + + ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, + AIR_LED_MODE_USER_DEFINE); + if (ret < 0) { + printf("Failed to disable leds: %d\n", ret); + return ret; + } + + return 0; +} + +static int en8811h_parse_status(struct phy_device *phydev) +{ + int ret = 0, reg_value; + + phydev->duplex = DUPLEX_FULL; + + reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS); + if (reg_value < 0) + return reg_value; + + switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) { + case AIR_AUX_CTRL_STATUS_SPEED_2500: + phydev->speed = SPEED_2500; + break; + case AIR_AUX_CTRL_STATUS_SPEED_1000: + phydev->speed = SPEED_1000; + break; + case AIR_AUX_CTRL_STATUS_SPEED_100: + phydev->speed = SPEED_100; + break; + default: + printf("Auto-neg error, defaulting to 100M/FD\n"); + phydev->speed = SPEED_100; + break; + } + + return ret; +} + +static int en8811h_startup(struct phy_device *phydev) +{ + int ret = 0; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + return en8811h_parse_status(phydev); +} + +static int en8811h_probe(struct phy_device *phydev) +{ + struct en8811h_priv *priv; + + priv = malloc(sizeof(*priv)); + if (!priv) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + + priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; + priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; + priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; + + /* mcu has just restarted after firmware load */ + priv->mcu_needs_restart = false; + + phydev->priv = priv; + + return 0; +} + +U_BOOT_PHY_DRIVER(en8811h) = { + .name = "Airoha EN8811H", + .uid = EN8811H_PHY_ID, + .mask = 0x0ffffff0, + .config = &en8811h_config, + .probe = &en8811h_probe, + .startup = &en8811h_startup, + .shutdown = &genphy_shutdown, +}; diff --git a/drivers/nvme/Makefile b/drivers/nvme/Makefile index 8c32cfbfc0f..5f8ed79077d 100644 --- a/drivers/nvme/Makefile +++ b/drivers/nvme/Makefile @@ -4,4 +4,4 @@ obj-y += nvme-uclass.o nvme.o nvme_show.o obj-$(CONFIG_NVME_APPLE) += nvme_apple.o -obj-$(CONFIG_$(XPL_)NVME_PCI) += nvme_pci.o +obj-$(CONFIG_$(PHASE_)NVME_PCI) += nvme_pci.o diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index ce4ea28b299..b4d01fc700d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,8 +8,8 @@ obj-y += marvell/ obj-y += rockchip/ obj-y += socionext/ -obj-$(CONFIG_$(XPL_)PHY) += phy-uclass.o -obj-$(CONFIG_$(XPL_)NOP_PHY) += nop-phy.o +obj-$(CONFIG_$(PHASE_)PHY) += phy-uclass.o +obj-$(CONFIG_$(PHASE_)NOP_PHY) += nop-phy.o obj-$(CONFIG_MIPI_DPHY_HELPERS) += phy-core-mipi-dphy.o obj-$(CONFIG_AB8500_USB_PHY) += phy-ab8500-usb.o obj-$(CONFIG_APPLE_ATCPHY) += phy-apple-atc.o @@ -19,7 +19,7 @@ obj-$(CONFIG_BCM6358_USBH_PHY) += bcm6358-usbh-phy.o obj-$(CONFIG_BCM6368_USBH_PHY) += bcm6368-usbh-phy.o obj-$(CONFIG_BCM_SR_PCIE_PHY) += phy-bcm-sr-pcie.o obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o -obj-$(CONFIG_$(XPL_)PIPE3_PHY) += ti-pipe3-phy.o +obj-$(CONFIG_$(PHASE_)PIPE3_PHY) += ti-pipe3-phy.o obj-$(CONFIG_AM654_PHY) += phy-ti-am654.o obj-$(CONFIG_STI_USB_PHY) += sti_usb_phy.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index e0da41cd7eb..2b1e51768da 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_$(XPL_)PHY_CADENCE_SIERRA) += phy-cadence-sierra.o -obj-$(CONFIG_$(XPL_)PHY_CADENCE_TORRENT) += phy-cadence-torrent.o +obj-$(CONFIG_$(PHASE_)PHY_CADENCE_SIERRA) += phy-cadence-sierra.o +obj-$(CONFIG_$(PHASE_)PHY_CADENCE_TORRENT) += phy-cadence-torrent.o diff --git a/drivers/phy/qcom/phy-qcom-qusb2.c b/drivers/phy/qcom/phy-qcom-qusb2.c index c91ba18c4ab..d98f6108e69 100644 --- a/drivers/phy/qcom/phy-qcom-qusb2.c +++ b/drivers/phy/qcom/phy-qcom-qusb2.c @@ -122,6 +122,9 @@ struct qusb2_phy_cfg { /* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */ bool has_pll_override; + + /* true if PHY default clk scheme is single-ended */ + bool se_clk_scheme_default; }; /* set of registers with offsets different per-PHY */ @@ -173,6 +176,19 @@ static const unsigned int sm6115_regs_layout[] = { [QUSB2PHY_PORT_POWERDOWN] = 0xb4, [QUSB2PHY_INTR_CTRL] = 0xbc, }; +static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), +}; + static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), @@ -214,6 +230,19 @@ static const struct qusb2_phy_cfg sm6115_phy_cfg = { .regs = sm6115_regs_layout, .has_pll_test = true, + .se_clk_scheme_default = true, + .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), + .mask_core_ready = PLL_LOCKED, + .autoresume_en = BIT(3), +}; + +static const struct qusb2_phy_cfg sdm660_phy_cfg = { + .tbl = msm8996_init_tbl, + .tbl_num = ARRAY_SIZE(msm8996_init_tbl), + .regs = sm6115_regs_layout, + + .has_pll_test = true, + .se_clk_scheme_default = false, .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), .mask_core_ready = PLL_LOCKED, .autoresume_en = BIT(3), @@ -228,6 +257,7 @@ static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = { POWER_DOWN), .mask_core_ready = CORE_READY_STATUS, .has_pll_override = true, + .se_clk_scheme_default = true, .autoresume_en = BIT(0), .update_tune1_with_efuse = true, }; @@ -316,8 +346,18 @@ static int qusb2phy_power_on(struct phy *phy) /* Required to get phy pll lock successfully */ udelay(150); + /* + * Not all the SoCs have got a readable TCSR_PHY_CLK_SCHEME + * register in the TCSR so, if there's none, use the default + * value hardcoded in the configuration. + */ + qphy->has_se_clk_scheme = cfg->se_clk_scheme_default; + if (cfg->has_pll_test) { - val |= CLK_REF_SEL; + if (!qphy->has_se_clk_scheme) + val &= ~CLK_REF_SEL; + else + val |= CLK_REF_SEL; writel(val, qphy->base + QUSB2PHY_PLL_TEST); @@ -413,6 +453,8 @@ static const struct udevice_id qusb2phy_ids[] = { { .compatible = "qcom,qusb2-phy" }, { .compatible = "qcom,qcm2290-qusb2-phy", .data = (ulong)&sm6115_phy_cfg }, + { .compatible = "qcom,sdm660-qusb2-phy", + .data = (ulong)&sdm660_phy_cfg }, { .compatible = "qcom,sm6115-qusb2-phy", .data = (ulong)&sm6115_phy_cfg }, { .compatible = "qcom,qusb2-v2-phy", .data = (ulong)&qusb2_v2_phy_cfg }, diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index 699901fd15e..169036a08d7 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -1 +1 @@ -obj-$(CONFIG_$(XPL_)PHY_J721E_WIZ) += phy-j721e-wiz.o +obj-$(CONFIG_$(PHASE_)PHY_J721E_WIZ) += phy-j721e-wiz.o diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 6deb6aaf6eb..a8eba656843 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -1,14 +1,14 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += pinctrl-uclass.o -obj-$(CONFIG_$(XPL_)PINCTRL_GENERIC) += pinctrl-generic.o +obj-$(CONFIG_$(PHASE_)PINCTRL_GENERIC) += pinctrl-generic.o obj-$(CONFIG_PINCTRL_ADI) += pinctrl-adi-adsp.o obj-$(CONFIG_PINCTRL_APPLE) += pinctrl-apple.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o obj-y += nxp/ -obj-$(CONFIG_$(XPL_)PINCTRL_ROCKCHIP) += rockchip/ +obj-$(CONFIG_$(PHASE_)PINCTRL_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-$(CONFIG_ARCH_ATH79) += ath79/ obj-$(CONFIG_PINCTRL_INTEL) += intel/ @@ -18,7 +18,7 @@ obj-$(CONFIG_PINCTRL_QCOM) += qcom/ obj-$(CONFIG_ARCH_RENESAS) += renesas/ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ -obj-$(CONFIG_$(XPL_)PINCTRL_TEGRA) += tegra/ +obj-$(CONFIG_$(PHASE_)PINCTRL_TEGRA) += tegra/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl_pic32.o obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/ @@ -32,7 +32,7 @@ obj-$(CONFIG_PINCTRL_QE) += pinctrl-qe-io.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o -obj-$(CONFIG_$(XPL_)PINCTRL_STMFX) += pinctrl-stmfx.o +obj-$(CONFIG_$(PHASE_)PINCTRL_STMFX) += pinctrl-stmfx.o obj-y += broadcom/ obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o obj-$(CONFIG_PINCTRL_STARFIVE) += starfive/ diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index f4a3942ee2f..e567cb113a3 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -48,12 +48,25 @@ config PINCTRL_QCOM_QCS404 Say Y here to enable support for pinctrl on the Snapdragon QCS404 SoC, as well as the associated GPIO driver. +config PINCTRL_QCOM_SA8775P + bool "Qualcomm SA8775P Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the Snapdragon SA8775P SoC, + as well as the associated GPIO driver. + config PINCTRL_QCOM_SC7280 bool "Qualcomm SC7280/QCM6490 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SC7280 SoC, - as well as the associated GPIO driver. + +config PINCTRL_QCOM_SDM660 + bool "Qualcomm SDM630/660 Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the Snapdragon 630/636/660 + SoCs, as well as the associated GPIO driver. config PINCTRL_QCOM_SDM845 bool "Qualcomm SDM845 Pinctrl" diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 94cdc6e4a62..6ffc6d48c15 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -9,7 +9,9 @@ obj-$(CONFIG_PINCTRL_QCOM_IPQ9574) += pinctrl-ipq9574.o obj-$(CONFIG_PINCTRL_QCOM_APQ8096) += pinctrl-apq8096.o obj-$(CONFIG_PINCTRL_QCOM_QCM2290) += pinctrl-qcm2290.o obj-$(CONFIG_PINCTRL_QCOM_QCS404) += pinctrl-qcs404.o +obj-$(CONFIG_PINCTRL_QCOM_SA8775P) += pinctrl-sa8775p.o obj-$(CONFIG_PINCTRL_QCOM_SC7280) += pinctrl-sc7280.o +obj-$(CONFIG_PINCTRL_QCOM_SDM660) += pinctrl-sdm660.o obj-$(CONFIG_PINCTRL_QCOM_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_QCOM_SM6115) += pinctrl-sm6115.o obj-$(CONFIG_PINCTRL_QCOM_SM8150) += pinctrl-sm8150.o diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c index 24d031947a3..c95db56bc47 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcom.c +++ b/drivers/pinctrl/qcom/pinctrl-qcom.c @@ -15,14 +15,18 @@ #include <asm/gpio.h> #include <dm/pinctrl.h> #include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/bug.h> #include <mach/gpio.h> #include "pinctrl-qcom.h" +#define MSM_PINCTRL_MAX_PINS 256 + struct msm_pinctrl_priv { phys_addr_t base; struct msm_pinctrl_data *data; + DECLARE_BITMAP(reserved_map, MSM_PINCTRL_MAX_PINS); }; #define GPIO_CONFIG_REG(priv, x) \ @@ -71,13 +75,60 @@ static const char *msm_get_function_name(struct udevice *dev, return priv->data->get_function_name(dev, selector); } +static int msm_pinctrl_parse_ranges(struct udevice *dev) +{ + struct msm_pinctrl_priv *priv = dev_get_priv(dev); + ofnode node = dev_ofnode(dev); + int ret, count, i; + u32 *ranges; + + if (ofnode_read_prop(node, "gpio-reserved-ranges", &count)) { + if (count % 2 == 1) { + dev_err(dev, "gpio-reserved-ranges must be a multiple of 2\n"); + return -EINVAL; + } + + ranges = malloc(count); + if (!ranges) + return -ENOMEM; + + ret = ofnode_read_u32_array(node, "gpio-reserved-ranges", ranges, count / 4); + if (ret) { + dev_err(dev, "failed to read gpio-reserved-ranges array (%d)\n", ret); + return ret; + } + + for (i = 0; i < count / 4; i += 2) { + if (ranges[i] >= MSM_PINCTRL_MAX_PINS || + (ranges[i] + ranges[i + 1]) >= MSM_PINCTRL_MAX_PINS) { + dev_err(dev, "invalid reserved-range (%d;%d)\n", + ranges[i], ranges[i + 1]); + return -EINVAL; + } + + bitmap_set(priv->reserved_map, ranges[i], ranges[i + 1]); + } + + free(ranges); + } + + return 0; +} + static int msm_pinctrl_probe(struct udevice *dev) { struct msm_pinctrl_priv *priv = dev_get_priv(dev); + int ret; priv->base = dev_read_addr(dev); priv->data = (struct msm_pinctrl_data *)dev_get_driver_data(dev); + ret = msm_pinctrl_parse_ranges(dev); + if (ret) { + printf("Couldn't parse reserved GPIO ranges!\n"); + return ret; + } + return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0; } @@ -97,6 +148,9 @@ static int msm_pinmux_set(struct udevice *dev, unsigned int pin_selector, if (func < 0) return func; + if (msm_pinctrl_is_reserved(dev, pin_selector)) + return -EPERM; + /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) return 0; @@ -145,6 +199,9 @@ static int msm_pinconf_set(struct udevice *dev, unsigned int pin_selector, { struct msm_pinctrl_priv *priv = dev_get_priv(dev); + if (msm_pinctrl_is_reserved(dev, pin_selector)) + return -EPERM; + if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) return msm_pinconf_set_special(priv, pin_selector, param, argument); @@ -241,3 +298,13 @@ U_BOOT_DRIVER(pinctrl_qcom) = { .ops = &msm_pinctrl_ops, .probe = msm_pinctrl_probe, }; + +bool msm_pinctrl_is_reserved(struct udevice *dev, unsigned int pin) +{ + struct msm_pinctrl_priv *priv = dev_get_priv(dev); + + if (pin >= MSM_PINCTRL_MAX_PINS) + return false; + + return test_bit(pin, priv->reserved_map); +} diff --git a/drivers/pinctrl/qcom/pinctrl-sa8775p.c b/drivers/pinctrl/qcom/pinctrl-sa8775p.c new file mode 100644 index 00000000000..cb2496ff1fb --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sa8775p.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include <dm.h> + +#include "pinctrl-qcom.h" + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +typedef unsigned int msm_pin_function[10]; +#define SA8775_PIN_OFFSET 0x100000 + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)\ + { \ + msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9 \ + } + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + } + +#define UFS_RESET(pg_name, ctl) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = ctl + 0x4, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + } + +enum sa8775p_functions { + msm_mux_gpio, + msm_mux_atest_char, + msm_mux_atest_usb2, + msm_mux_audio_ref, + msm_mux_cam_mclk, + msm_mux_cci_async, + msm_mux_cci_i2c, + msm_mux_cci_timer0, + msm_mux_cci_timer1, + msm_mux_cci_timer2, + msm_mux_cci_timer3, + msm_mux_cci_timer4, + msm_mux_cci_timer5, + msm_mux_cci_timer6, + msm_mux_cci_timer7, + msm_mux_cci_timer8, + msm_mux_cci_timer9, + msm_mux_cri_trng, + msm_mux_cri_trng0, + msm_mux_cri_trng1, + msm_mux_dbg_out, + msm_mux_ddr_bist, + msm_mux_ddr_pxi0, + msm_mux_ddr_pxi1, + msm_mux_ddr_pxi2, + msm_mux_ddr_pxi3, + msm_mux_ddr_pxi4, + msm_mux_ddr_pxi5, + msm_mux_edp0_hot, + msm_mux_edp0_lcd, + msm_mux_edp1_hot, + msm_mux_edp1_lcd, + msm_mux_edp2_hot, + msm_mux_edp2_lcd, + msm_mux_edp3_hot, + msm_mux_edp3_lcd, + msm_mux_emac0_mcg0, + msm_mux_emac0_mcg1, + msm_mux_emac0_mcg2, + msm_mux_emac0_mcg3, + msm_mux_emac0_mdc, + msm_mux_emac0_mdio, + msm_mux_emac0_ptp_aux, + msm_mux_emac0_ptp_pps, + msm_mux_emac1_mcg0, + msm_mux_emac1_mcg1, + msm_mux_emac1_mcg2, + msm_mux_emac1_mcg3, + msm_mux_emac1_mdc, + msm_mux_emac1_mdio, + msm_mux_emac1_ptp_aux, + msm_mux_emac1_ptp_pps, + msm_mux_gcc_gp1, + msm_mux_gcc_gp2, + msm_mux_gcc_gp3, + msm_mux_gcc_gp4, + msm_mux_gcc_gp5, + msm_mux_hs0_mi2s, + msm_mux_hs1_mi2s, + msm_mux_hs2_mi2s, + msm_mux_ibi_i3c, + msm_mux_jitter_bist, + msm_mux_mdp0_vsync0, + msm_mux_mdp0_vsync1, + msm_mux_mdp0_vsync2, + msm_mux_mdp0_vsync3, + msm_mux_mdp0_vsync4, + msm_mux_mdp0_vsync5, + msm_mux_mdp0_vsync6, + msm_mux_mdp0_vsync7, + msm_mux_mdp0_vsync8, + msm_mux_mdp1_vsync0, + msm_mux_mdp1_vsync1, + msm_mux_mdp1_vsync2, + msm_mux_mdp1_vsync3, + msm_mux_mdp1_vsync4, + msm_mux_mdp1_vsync5, + msm_mux_mdp1_vsync6, + msm_mux_mdp1_vsync7, + msm_mux_mdp1_vsync8, + msm_mux_mdp_vsync, + msm_mux_mi2s1_data0, + msm_mux_mi2s1_data1, + msm_mux_mi2s1_sck, + msm_mux_mi2s1_ws, + msm_mux_mi2s2_data0, + msm_mux_mi2s2_data1, + msm_mux_mi2s2_sck, + msm_mux_mi2s2_ws, + msm_mux_mi2s_mclk0, + msm_mux_mi2s_mclk1, + msm_mux_pcie0_clkreq, + msm_mux_pcie1_clkreq, + msm_mux_phase_flag, + msm_mux_pll_bist, + msm_mux_pll_clk, + msm_mux_prng_rosc0, + msm_mux_prng_rosc1, + msm_mux_prng_rosc2, + msm_mux_prng_rosc3, + msm_mux_qdss_cti, + msm_mux_qdss_gpio, + msm_mux_qup0_se0, + msm_mux_qup0_se1, + msm_mux_qup0_se2, + msm_mux_qup0_se3, + msm_mux_qup0_se4, + msm_mux_qup0_se5, + msm_mux_qup1_se0, + msm_mux_qup1_se1, + msm_mux_qup1_se2, + msm_mux_qup1_se3, + msm_mux_qup1_se4, + msm_mux_qup1_se5, + msm_mux_qup1_se6, + msm_mux_qup2_se0, + msm_mux_qup2_se1, + msm_mux_qup2_se2, + msm_mux_qup2_se3, + msm_mux_qup2_se4, + msm_mux_qup2_se5, + msm_mux_qup2_se6, + msm_mux_qup3_se0, + msm_mux_sail_top, + msm_mux_sailss_emac0, + msm_mux_sailss_ospi, + msm_mux_sgmii_phy, + msm_mux_tb_trig, + msm_mux_tgu_ch0, + msm_mux_tgu_ch1, + msm_mux_tgu_ch2, + msm_mux_tgu_ch3, + msm_mux_tgu_ch4, + msm_mux_tgu_ch5, + msm_mux_tsense_pwm1, + msm_mux_tsense_pwm2, + msm_mux_tsense_pwm3, + msm_mux_tsense_pwm4, + msm_mux_usb2phy_ac, + msm_mux_vsense_trigger, + msm_mux__, +}; + +#define MSM_PIN_FUNCTION(fname) \ + [msm_mux_##fname] = {#fname, msm_mux_##fname} + +static const struct pinctrl_function msm_pinctrl_functions[] = { + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(atest_char), + MSM_PIN_FUNCTION(atest_usb2), + MSM_PIN_FUNCTION(audio_ref), + MSM_PIN_FUNCTION(cam_mclk), + MSM_PIN_FUNCTION(cci_async), + MSM_PIN_FUNCTION(cci_i2c), + MSM_PIN_FUNCTION(cci_timer0), + MSM_PIN_FUNCTION(cci_timer1), + MSM_PIN_FUNCTION(cci_timer2), + MSM_PIN_FUNCTION(cci_timer3), + MSM_PIN_FUNCTION(cci_timer4), + MSM_PIN_FUNCTION(cci_timer5), + MSM_PIN_FUNCTION(cci_timer6), + MSM_PIN_FUNCTION(cci_timer7), + MSM_PIN_FUNCTION(cci_timer8), + MSM_PIN_FUNCTION(cci_timer9), + MSM_PIN_FUNCTION(cri_trng), + MSM_PIN_FUNCTION(cri_trng0), + MSM_PIN_FUNCTION(cri_trng1), + MSM_PIN_FUNCTION(dbg_out), + MSM_PIN_FUNCTION(ddr_bist), + MSM_PIN_FUNCTION(ddr_pxi0), + MSM_PIN_FUNCTION(ddr_pxi1), + MSM_PIN_FUNCTION(ddr_pxi2), + MSM_PIN_FUNCTION(ddr_pxi3), + MSM_PIN_FUNCTION(ddr_pxi4), + MSM_PIN_FUNCTION(ddr_pxi5), + MSM_PIN_FUNCTION(edp0_hot), + MSM_PIN_FUNCTION(edp0_lcd), + MSM_PIN_FUNCTION(edp1_hot), + MSM_PIN_FUNCTION(edp1_lcd), + MSM_PIN_FUNCTION(edp2_hot), + MSM_PIN_FUNCTION(edp2_lcd), + MSM_PIN_FUNCTION(edp3_hot), + MSM_PIN_FUNCTION(edp3_lcd), + MSM_PIN_FUNCTION(emac0_mcg0), + MSM_PIN_FUNCTION(emac0_mcg1), + MSM_PIN_FUNCTION(emac0_mcg2), + MSM_PIN_FUNCTION(emac0_mcg3), + MSM_PIN_FUNCTION(emac0_mdc), + MSM_PIN_FUNCTION(emac0_mdio), + MSM_PIN_FUNCTION(emac0_ptp_aux), + MSM_PIN_FUNCTION(emac0_ptp_pps), + MSM_PIN_FUNCTION(emac1_mcg0), + MSM_PIN_FUNCTION(emac1_mcg1), + MSM_PIN_FUNCTION(emac1_mcg2), + MSM_PIN_FUNCTION(emac1_mcg3), + MSM_PIN_FUNCTION(emac1_mdc), + MSM_PIN_FUNCTION(emac1_mdio), + MSM_PIN_FUNCTION(emac1_ptp_aux), + MSM_PIN_FUNCTION(emac1_ptp_pps), + MSM_PIN_FUNCTION(gcc_gp1), + MSM_PIN_FUNCTION(gcc_gp2), + MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gcc_gp4), + MSM_PIN_FUNCTION(gcc_gp5), + MSM_PIN_FUNCTION(hs0_mi2s), + MSM_PIN_FUNCTION(hs1_mi2s), + MSM_PIN_FUNCTION(hs2_mi2s), + MSM_PIN_FUNCTION(ibi_i3c), + MSM_PIN_FUNCTION(jitter_bist), + MSM_PIN_FUNCTION(mdp0_vsync0), + MSM_PIN_FUNCTION(mdp0_vsync1), + MSM_PIN_FUNCTION(mdp0_vsync2), + MSM_PIN_FUNCTION(mdp0_vsync3), + MSM_PIN_FUNCTION(mdp0_vsync4), + MSM_PIN_FUNCTION(mdp0_vsync5), + MSM_PIN_FUNCTION(mdp0_vsync6), + MSM_PIN_FUNCTION(mdp0_vsync7), + MSM_PIN_FUNCTION(mdp0_vsync8), + MSM_PIN_FUNCTION(mdp1_vsync0), + MSM_PIN_FUNCTION(mdp1_vsync1), + MSM_PIN_FUNCTION(mdp1_vsync2), + MSM_PIN_FUNCTION(mdp1_vsync3), + MSM_PIN_FUNCTION(mdp1_vsync4), + MSM_PIN_FUNCTION(mdp1_vsync5), + MSM_PIN_FUNCTION(mdp1_vsync6), + MSM_PIN_FUNCTION(mdp1_vsync7), + MSM_PIN_FUNCTION(mdp1_vsync8), + MSM_PIN_FUNCTION(mdp_vsync), + MSM_PIN_FUNCTION(mi2s1_data0), + MSM_PIN_FUNCTION(mi2s1_data1), + MSM_PIN_FUNCTION(mi2s1_sck), + MSM_PIN_FUNCTION(mi2s1_ws), + MSM_PIN_FUNCTION(mi2s2_data0), + MSM_PIN_FUNCTION(mi2s2_data1), + MSM_PIN_FUNCTION(mi2s2_sck), + MSM_PIN_FUNCTION(mi2s2_ws), + MSM_PIN_FUNCTION(mi2s_mclk0), + MSM_PIN_FUNCTION(mi2s_mclk1), + MSM_PIN_FUNCTION(pcie0_clkreq), + MSM_PIN_FUNCTION(pcie1_clkreq), + MSM_PIN_FUNCTION(phase_flag), + MSM_PIN_FUNCTION(pll_bist), + MSM_PIN_FUNCTION(pll_clk), + MSM_PIN_FUNCTION(prng_rosc0), + MSM_PIN_FUNCTION(prng_rosc1), + MSM_PIN_FUNCTION(prng_rosc2), + MSM_PIN_FUNCTION(prng_rosc3), + MSM_PIN_FUNCTION(qdss_cti), + MSM_PIN_FUNCTION(qdss_gpio), + MSM_PIN_FUNCTION(qup0_se0), + MSM_PIN_FUNCTION(qup0_se1), + MSM_PIN_FUNCTION(qup0_se2), + MSM_PIN_FUNCTION(qup0_se3), + MSM_PIN_FUNCTION(qup0_se4), + MSM_PIN_FUNCTION(qup0_se5), + MSM_PIN_FUNCTION(qup1_se0), + MSM_PIN_FUNCTION(qup1_se1), + MSM_PIN_FUNCTION(qup1_se2), + MSM_PIN_FUNCTION(qup1_se3), + MSM_PIN_FUNCTION(qup1_se4), + MSM_PIN_FUNCTION(qup1_se5), + MSM_PIN_FUNCTION(qup1_se6), + MSM_PIN_FUNCTION(qup2_se0), + MSM_PIN_FUNCTION(qup2_se1), + MSM_PIN_FUNCTION(qup2_se2), + MSM_PIN_FUNCTION(qup2_se3), + MSM_PIN_FUNCTION(qup2_se4), + MSM_PIN_FUNCTION(qup2_se5), + MSM_PIN_FUNCTION(qup2_se6), + MSM_PIN_FUNCTION(qup3_se0), + MSM_PIN_FUNCTION(sail_top), + MSM_PIN_FUNCTION(sailss_emac0), + MSM_PIN_FUNCTION(sailss_ospi), + MSM_PIN_FUNCTION(sgmii_phy), + MSM_PIN_FUNCTION(tb_trig), + MSM_PIN_FUNCTION(tgu_ch0), + MSM_PIN_FUNCTION(tgu_ch1), + MSM_PIN_FUNCTION(tgu_ch2), + MSM_PIN_FUNCTION(tgu_ch3), + MSM_PIN_FUNCTION(tgu_ch4), + MSM_PIN_FUNCTION(tgu_ch5), + MSM_PIN_FUNCTION(tsense_pwm1), + MSM_PIN_FUNCTION(tsense_pwm2), + MSM_PIN_FUNCTION(tsense_pwm3), + MSM_PIN_FUNCTION(tsense_pwm4), + MSM_PIN_FUNCTION(usb2phy_ac), + MSM_PIN_FUNCTION(vsense_trigger), +}; + +static const msm_pin_function sa8775p_pin_functions[] = { + [0] = PINGROUP(0, _, _, _, _, _, _, _, _, _), + [1] = PINGROUP(1, pcie0_clkreq, _, _, _, _, _, _, _, _), + [2] = PINGROUP(2, _, _, _, _, _, _, _, _, _), + [3] = PINGROUP(3, pcie1_clkreq, _, _, _, _, _, _, _, _), + [4] = PINGROUP(4, _, _, _, _, _, _, _, _, _), + [5] = PINGROUP(5, _, _, _, _, _, _, _, _, _), + [6] = PINGROUP(6, emac0_ptp_aux, emac0_ptp_pps, emac1_ptp_aux, + emac1_ptp_pps, _, _, _, _, _), + [7] = PINGROUP(7, sgmii_phy, _, _, _, _, _, _, _, _), + [8] = PINGROUP(8, emac0_mdc, _, _, _, _, _, _, _, _), + [9] = PINGROUP(9, emac0_mdio, _, _, _, _, _, _, _, _), + [10] = PINGROUP(10, usb2phy_ac, emac0_ptp_aux, emac0_ptp_pps, + emac1_ptp_aux, emac1_ptp_pps, _, _, _, _), + [11] = PINGROUP(11, usb2phy_ac, emac0_ptp_aux, emac0_ptp_pps, + emac1_ptp_aux, emac1_ptp_pps, _, _, _, _), + [12] = PINGROUP(12, usb2phy_ac, emac0_ptp_aux, emac0_ptp_pps, + emac1_ptp_aux, emac1_ptp_pps, emac0_mcg0, _, _, _), + [13] = PINGROUP(13, qup3_se0, emac0_mcg1, _, _, sail_top, _, _, _, _), + [14] = PINGROUP(14, qup3_se0, emac0_mcg2, _, _, sail_top, _, _, _, _), + [15] = PINGROUP(15, qup3_se0, emac0_mcg3, _, _, sail_top, _, _, _, _), + [16] = PINGROUP(16, qup3_se0, emac1_mcg0, _, _, sail_top, _, _, _, _), + [17] = PINGROUP(17, qup3_se0, tb_trig, tb_trig, emac1_mcg1, _, _, _, _, _), + [18] = PINGROUP(18, qup3_se0, emac1_mcg2, _, _, sailss_ospi, sailss_emac0, _, _, _), + [19] = PINGROUP(19, qup3_se0, emac1_mcg3, _, _, sailss_ospi, sailss_emac0, _, _, _), + [20] = PINGROUP(20, qup0_se0, emac1_mdc, qdss_gpio, _, _, _, _, _, _), + [21] = PINGROUP(21, qup0_se0, emac1_mdio, qdss_gpio, _, _, _, _, _, _), + [22] = PINGROUP(22, qup0_se0, qdss_gpio, _, _, _, _, _, _, _), + [23] = PINGROUP(23, qup0_se0, qdss_gpio, _, _, _, _, _, _, _), + [24] = PINGROUP(24, qup0_se1, qdss_gpio, _, _, _, _, _, _, _), + [25] = PINGROUP(25, qup0_se1, phase_flag, _, qdss_gpio, _, _, _, _, _), + [26] = PINGROUP(26, sgmii_phy, qup0_se1, qdss_cti, phase_flag, _, _, _, _, _), + [27] = PINGROUP(27, qup0_se1, qdss_cti, phase_flag, _, atest_char, _, _, _, _), + [28] = PINGROUP(28, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [29] = PINGROUP(29, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [30] = PINGROUP(30, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [31] = PINGROUP(31, qup0_se3, phase_flag, _, qdss_gpio, _, _, _, _, _), + [32] = PINGROUP(32, qup0_se4, phase_flag, _, _, _, _, _, _, _), + [33] = PINGROUP(33, qup0_se4, gcc_gp4, _, ddr_pxi0, _, _, _, _, _), + [34] = PINGROUP(34, qup0_se4, gcc_gp5, _, ddr_pxi0, _, _, _, _, _), + [35] = PINGROUP(35, qup0_se4, phase_flag, _, _, _, _, _, _, _), + [36] = PINGROUP(36, qup0_se2, qup0_se5, phase_flag, tgu_ch2, _, _, _, _, _), + [37] = PINGROUP(37, qup0_se2, qup0_se5, phase_flag, tgu_ch3, _, _, _, _, _), + [38] = PINGROUP(38, qup0_se5, qup0_se2, qdss_cti, phase_flag, tgu_ch4, _, _, _, _), + [39] = PINGROUP(39, qup0_se5, qup0_se2, qdss_cti, phase_flag, tgu_ch5, _, _, _, _), + [40] = PINGROUP(40, qup1_se0, qup1_se1, ibi_i3c, mdp1_vsync0, _, _, _, _, _), + [41] = PINGROUP(41, qup1_se0, qup1_se1, ibi_i3c, mdp1_vsync1, _, _, _, _, _), + [42] = PINGROUP(42, qup1_se1, qup1_se0, ibi_i3c, mdp1_vsync2, gcc_gp5, _, _, _, _), + [43] = PINGROUP(43, qup1_se1, qup1_se0, ibi_i3c, mdp1_vsync3, _, _, _, _, _), + [44] = PINGROUP(44, qup1_se2, qup1_se3, edp0_lcd, _, _, _, _, _, _), + [45] = PINGROUP(45, qup1_se2, qup1_se3, edp1_lcd, _, _, _, _, _, _), + [46] = PINGROUP(46, qup1_se3, qup1_se2, mdp1_vsync4, tgu_ch0, _, _, _, _, _), + [47] = PINGROUP(47, qup1_se3, qup1_se2, mdp1_vsync5, tgu_ch1, _, _, _, _, _), + [48] = PINGROUP(48, qup1_se4, qdss_cti, edp2_lcd, _, _, _, _, _, _), + [49] = PINGROUP(49, qup1_se4, qdss_cti, edp3_lcd, _, _, _, _, _, _), + [50] = PINGROUP(50, qup1_se4, cci_async, qdss_cti, mdp1_vsync8, _, _, _, _, _), + [51] = PINGROUP(51, qup1_se4, qdss_cti, mdp1_vsync6, gcc_gp1, _, _, _, _, _), + [52] = PINGROUP(52, qup1_se5, cci_timer4, cci_i2c, mdp1_vsync7, gcc_gp2, _, ddr_pxi1, _, _), + [53] = PINGROUP(53, qup1_se5, cci_timer5, cci_i2c, gcc_gp3, _, ddr_pxi1, _, _, _), + [54] = PINGROUP(54, qup1_se5, cci_timer6, cci_i2c, _, _, _, _, _, _), + [55] = PINGROUP(55, qup1_se5, cci_timer7, cci_i2c, gcc_gp4, _, ddr_pxi2, _, _, _), + [56] = PINGROUP(56, qup1_se6, qup1_se6, cci_timer8, cci_i2c, phase_flag, ddr_bist, _, _, _), + [57] = PINGROUP(57, qup1_se6, qup1_se6, cci_timer9, cci_i2c, + mdp0_vsync0, phase_flag, ddr_bist, _, _), + [58] = PINGROUP(58, cci_i2c, mdp0_vsync1, ddr_bist, _, atest_usb2, atest_char, _, _, _), + [59] = PINGROUP(59, cci_i2c, mdp0_vsync2, ddr_bist, _, atest_usb2, atest_char, _, _, _), + [60] = PINGROUP(60, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [61] = PINGROUP(61, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [62] = PINGROUP(62, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [63] = PINGROUP(63, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [64] = PINGROUP(64, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [65] = PINGROUP(65, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [66] = PINGROUP(66, cci_i2c, cci_async, qdss_gpio, _, _, _, _, _, _), + [67] = PINGROUP(67, cci_i2c, qdss_gpio, _, _, _, _, _, _, _), + [68] = PINGROUP(68, cci_timer0, cci_async, _, _, _, _, _, _, _), + [69] = PINGROUP(69, cci_timer1, cci_async, _, _, _, _, _, _, _), + [70] = PINGROUP(70, cci_timer2, cci_async, _, _, _, _, _, _, _), + [71] = PINGROUP(71, cci_timer3, cci_async, _, _, _, _, _, _, _), + [72] = PINGROUP(72, cam_mclk, _, _, _, _, _, _, _, _), + [73] = PINGROUP(73, cam_mclk, _, _, _, _, _, _, _, _), + [74] = PINGROUP(74, cam_mclk, _, _, _, _, _, _, _, _), + [75] = PINGROUP(75, cam_mclk, _, _, _, _, _, _, _, _), + [76] = PINGROUP(76, _, _, _, _, _, _, _, _, _), + [77] = PINGROUP(77, _, _, _, _, _, _, _, _, _), + [78] = PINGROUP(78, _, _, _, _, _, _, _, _, _), + [79] = PINGROUP(79, _, _, _, _, _, _, _, _, _), + [80] = PINGROUP(80, qup2_se0, ibi_i3c, mdp0_vsync3, _, _, _, _, _, _), + [81] = PINGROUP(81, qup2_se0, ibi_i3c, mdp0_vsync4, _, _, _, _, _, _), + [82] = PINGROUP(82, qup2_se0, mdp_vsync, gcc_gp1, _, _, _, _, _, _), + [83] = PINGROUP(83, qup2_se0, mdp_vsync, gcc_gp2, _, _, _, _, _, _), + [84] = PINGROUP(84, qup2_se1, qup2_se5, ibi_i3c, mdp_vsync, gcc_gp3, _, _, _, _), + [85] = PINGROUP(85, qup2_se1, qup2_se5, ibi_i3c, _, _, _, _, _, _), + [86] = PINGROUP(86, qup2_se2, jitter_bist, atest_usb2, ddr_pxi2, _, _, _, _, _), + [87] = PINGROUP(87, qup2_se2, pll_clk, atest_usb2, ddr_pxi3, _, _, _, _, _), + [88] = PINGROUP(88, qup2_se2, _, atest_usb2, ddr_pxi3, _, _, _, _, _), + [89] = PINGROUP(89, qup2_se2, _, atest_usb2, ddr_pxi4, atest_char, _, _, _, _), + [90] = PINGROUP(90, qup2_se2, _, atest_usb2, ddr_pxi4, atest_char, _, _, _, _), + [91] = PINGROUP(91, qup2_se3, mdp0_vsync5, _, atest_usb2, _, _, _, _, _), + [92] = PINGROUP(92, qup2_se3, mdp0_vsync6, _, atest_usb2, _, _, _, _, _), + [93] = PINGROUP(93, qup2_se3, mdp0_vsync7, _, atest_usb2, _, _, _, _, _), + [94] = PINGROUP(94, qup2_se3, mdp0_vsync8, _, atest_usb2, _, _, _, _, _), + [95] = PINGROUP(95, qup2_se4, qup2_se6, _, atest_usb2, _, _, _, _, _), + [96] = PINGROUP(96, qup2_se4, qup2_se6, _, atest_usb2, _, _, _, _, _), + [97] = PINGROUP(97, qup2_se6, qup2_se4, cri_trng0, _, atest_usb2, _, _, _, _), + [98] = PINGROUP(98, qup2_se6, qup2_se4, phase_flag, cri_trng1, _, _, _, _, _), + [99] = PINGROUP(99, qup2_se5, qup2_se1, phase_flag, cri_trng, _, _, _, _, _), + [100] = PINGROUP(100, qup2_se5, qup2_se1, _, _, _, _, _, _, _), + [101] = PINGROUP(101, edp0_hot, prng_rosc0, tsense_pwm4, _, _, _, _, _, _), + [102] = PINGROUP(102, edp1_hot, prng_rosc1, tsense_pwm3, _, _, _, _, _, _), + [103] = PINGROUP(103, edp3_hot, prng_rosc2, tsense_pwm2, _, _, _, _, _, _), + [104] = PINGROUP(104, edp2_hot, prng_rosc3, tsense_pwm1, _, _, _, _, _, _), + [105] = PINGROUP(105, mi2s_mclk0, _, qdss_gpio, atest_usb2, _, _, _, _, _), + [106] = PINGROUP(106, mi2s1_sck, phase_flag, _, qdss_gpio, _, _, _, _, _), + [107] = PINGROUP(107, mi2s1_ws, phase_flag, _, qdss_gpio, _, _, _, _, _), + [108] = PINGROUP(108, mi2s1_data0, phase_flag, _, qdss_gpio, _, _, _, _, _), + [109] = PINGROUP(109, mi2s1_data1, phase_flag, _, qdss_gpio, _, _, _, _, _), + [110] = PINGROUP(110, mi2s2_sck, phase_flag, _, qdss_gpio, _, _, _, _, _), + [111] = PINGROUP(111, mi2s2_ws, phase_flag, _, qdss_gpio, vsense_trigger, _, _, _, _), + [112] = PINGROUP(112, mi2s2_data0, phase_flag, _, qdss_gpio, _, _, _, _, _), + [113] = PINGROUP(113, mi2s2_data1, audio_ref, phase_flag, _, qdss_gpio, _, _, _, _), + [114] = PINGROUP(114, hs0_mi2s, pll_bist, phase_flag, _, qdss_gpio, _, _, _, _), + [115] = PINGROUP(115, hs0_mi2s, _, qdss_gpio, _, _, _, _, _, _), + [116] = PINGROUP(116, hs0_mi2s, _, qdss_gpio, _, _, _, _, _, _), + [117] = PINGROUP(117, hs0_mi2s, mi2s_mclk1, _, qdss_gpio, _, _, _, _, _), + [118] = PINGROUP(118, hs1_mi2s, _, qdss_gpio, ddr_pxi5, _, _, _, _, _), + [119] = PINGROUP(119, hs1_mi2s, _, qdss_gpio, ddr_pxi5, _, _, _, _, _), + [120] = PINGROUP(120, hs1_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _), + [121] = PINGROUP(121, hs1_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _), + [122] = PINGROUP(122, hs2_mi2s, phase_flag, _, qdss_gpio, _, _, _, _, _), + [123] = PINGROUP(123, hs2_mi2s, phase_flag, _, _, _, _, _, _, _), + [124] = PINGROUP(124, hs2_mi2s, phase_flag, _, _, _, _, _, _, _), + [125] = PINGROUP(125, hs2_mi2s, phase_flag, _, _, _, _, _, _, _), + [126] = PINGROUP(126, _, _, _, _, _, _, _, _, _), + [127] = PINGROUP(127, _, _, _, _, _, _, _, _, _), + [128] = PINGROUP(128, _, _, _, _, _, _, _, _, _), + [129] = PINGROUP(129, _, _, _, _, _, _, _, _, _), + [130] = PINGROUP(130, _, _, _, _, _, _, _, _, _), + [131] = PINGROUP(131, _, _, _, _, _, _, _, _, _), + [132] = PINGROUP(132, _, _, _, _, _, _, _, _, _), + [133] = PINGROUP(133, _, _, _, _, _, _, _, _, _), + [134] = PINGROUP(134, _, _, _, _, _, _, _, _, _), + [135] = PINGROUP(135, _, _, _, _, _, _, _, _, _), + [136] = PINGROUP(136, _, _, _, _, _, _, _, _, _), + [137] = PINGROUP(137, _, _, _, _, _, _, _, _, _), + [138] = PINGROUP(138, _, _, _, _, _, _, _, _, _), + [139] = PINGROUP(139, _, _, _, _, _, _, _, _, _), + [140] = PINGROUP(140, _, _, _, _, _, _, _, _, _), + [141] = PINGROUP(141, _, _, _, _, _, _, _, _, _), + [142] = PINGROUP(142, _, _, _, _, _, _, _, _, _), + [143] = PINGROUP(143, _, _, _, _, _, _, _, _, _), + [144] = PINGROUP(144, dbg_out, _, _, _, _, _, _, _, _), + [145] = PINGROUP(145, _, _, _, _, _, _, _, _, _), + [146] = PINGROUP(146, _, _, _, _, _, _, _, _, _), + [147] = PINGROUP(147, _, _, _, _, _, _, _, _, _), + [148] = PINGROUP(148, _, _, _, _, _, _, _, _, _), +}; + +static const struct msm_special_pin_data msm_special_pins_data[] = { + [0] = UFS_RESET("ufs_reset", 0x1a2000), + [1] = SDC_QDSD_PINGROUP("sdc1_rclk", 0x199000, 15, 0), + [2] = SDC_QDSD_PINGROUP("sdc1_clk", 0x199000, 13, 6), + [3] = SDC_QDSD_PINGROUP("sdc1_cmd", 0x199000, 11, 3), + [4] = SDC_QDSD_PINGROUP("sdc1_data", 0x199000, 9, 0), +}; + +static const char *sa8775p_get_function_name(struct udevice *dev, + unsigned int selector) +{ + return msm_pinctrl_functions[selector].name; +} + +static const char *sa8775p_get_pin_name(struct udevice *dev, + unsigned int selector) +{ + if (selector >= 149 && selector <= 155) + snprintf(pin_name, MAX_PIN_NAME_LEN, + msm_special_pins_data[selector - 149].name); + else + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + + return pin_name; +} + +static int sa8775p_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) +{ + unsigned int i; + const msm_pin_function *func = sa8775p_pin_functions + pin; + + for (i = 0; i < 10; i++) + if ((*func)[i] == selector) + return i; + + pr_err("Can't find requested function for pin %u pin\n", pin); + + return -EINVAL; +} + +static const unsigned int sa8775p_pin_offsets[] = { + [0] = SA8775_PIN_OFFSET, [1] = SA8775_PIN_OFFSET, [2] = SA8775_PIN_OFFSET, + [3] = SA8775_PIN_OFFSET, [4] = SA8775_PIN_OFFSET, [5] = SA8775_PIN_OFFSET, + [6] = SA8775_PIN_OFFSET, [7] = SA8775_PIN_OFFSET, [8] = SA8775_PIN_OFFSET, + [9] = SA8775_PIN_OFFSET, [10] = SA8775_PIN_OFFSET, [11] = SA8775_PIN_OFFSET, + [12] = SA8775_PIN_OFFSET, [13] = SA8775_PIN_OFFSET, [14] = SA8775_PIN_OFFSET, + [15] = SA8775_PIN_OFFSET, [16] = SA8775_PIN_OFFSET, [17] = SA8775_PIN_OFFSET, + [18] = SA8775_PIN_OFFSET, [19] = SA8775_PIN_OFFSET, [20] = SA8775_PIN_OFFSET, + [21] = SA8775_PIN_OFFSET, [22] = SA8775_PIN_OFFSET, [23] = SA8775_PIN_OFFSET, + [24] = SA8775_PIN_OFFSET, [25] = SA8775_PIN_OFFSET, [26] = SA8775_PIN_OFFSET, + [27] = SA8775_PIN_OFFSET, [28] = SA8775_PIN_OFFSET, [29] = SA8775_PIN_OFFSET, + [30] = SA8775_PIN_OFFSET, [31] = SA8775_PIN_OFFSET, [32] = SA8775_PIN_OFFSET, + [33] = SA8775_PIN_OFFSET, [34] = SA8775_PIN_OFFSET, [35] = SA8775_PIN_OFFSET, + [36] = SA8775_PIN_OFFSET, [37] = SA8775_PIN_OFFSET, [38] = SA8775_PIN_OFFSET, + [39] = SA8775_PIN_OFFSET, [40] = SA8775_PIN_OFFSET, [41] = SA8775_PIN_OFFSET, + [42] = SA8775_PIN_OFFSET, [43] = SA8775_PIN_OFFSET, [44] = SA8775_PIN_OFFSET, + [45] = SA8775_PIN_OFFSET, [46] = SA8775_PIN_OFFSET, [47] = SA8775_PIN_OFFSET, + [48] = SA8775_PIN_OFFSET, [49] = SA8775_PIN_OFFSET, [50] = SA8775_PIN_OFFSET, + [51] = SA8775_PIN_OFFSET, [52] = SA8775_PIN_OFFSET, [53] = SA8775_PIN_OFFSET, + [54] = SA8775_PIN_OFFSET, [55] = SA8775_PIN_OFFSET, [56] = SA8775_PIN_OFFSET, + [57] = SA8775_PIN_OFFSET, [58] = SA8775_PIN_OFFSET, [59] = SA8775_PIN_OFFSET, + [60] = SA8775_PIN_OFFSET, [61] = SA8775_PIN_OFFSET, [62] = SA8775_PIN_OFFSET, + [63] = SA8775_PIN_OFFSET, [64] = SA8775_PIN_OFFSET, [65] = SA8775_PIN_OFFSET, + [66] = SA8775_PIN_OFFSET, [67] = SA8775_PIN_OFFSET, [68] = SA8775_PIN_OFFSET, + [69] = SA8775_PIN_OFFSET, [70] = SA8775_PIN_OFFSET, [71] = SA8775_PIN_OFFSET, + [72] = SA8775_PIN_OFFSET, [73] = SA8775_PIN_OFFSET, [74] = SA8775_PIN_OFFSET, + [75] = SA8775_PIN_OFFSET, [76] = SA8775_PIN_OFFSET, [77] = SA8775_PIN_OFFSET, + [78] = SA8775_PIN_OFFSET, [79] = SA8775_PIN_OFFSET, [80] = SA8775_PIN_OFFSET, + [81] = SA8775_PIN_OFFSET, [82] = SA8775_PIN_OFFSET, [83] = SA8775_PIN_OFFSET, + [84] = SA8775_PIN_OFFSET, [85] = SA8775_PIN_OFFSET, [86] = SA8775_PIN_OFFSET, + [87] = SA8775_PIN_OFFSET, [88] = SA8775_PIN_OFFSET, [89] = SA8775_PIN_OFFSET, + [90] = SA8775_PIN_OFFSET, [91] = SA8775_PIN_OFFSET, [92] = SA8775_PIN_OFFSET, + [93] = SA8775_PIN_OFFSET, [94] = SA8775_PIN_OFFSET, [95] = SA8775_PIN_OFFSET, + [96] = SA8775_PIN_OFFSET, [97] = SA8775_PIN_OFFSET, [98] = SA8775_PIN_OFFSET, + [99] = SA8775_PIN_OFFSET, [100] = SA8775_PIN_OFFSET, [101] = SA8775_PIN_OFFSET, + [102] = SA8775_PIN_OFFSET, [103] = SA8775_PIN_OFFSET, [104] = SA8775_PIN_OFFSET, + [105] = SA8775_PIN_OFFSET, [106] = SA8775_PIN_OFFSET, [107] = SA8775_PIN_OFFSET, + [108] = SA8775_PIN_OFFSET, [109] = SA8775_PIN_OFFSET, [110] = SA8775_PIN_OFFSET, + [111] = SA8775_PIN_OFFSET, [112] = SA8775_PIN_OFFSET, [113] = SA8775_PIN_OFFSET, + [114] = SA8775_PIN_OFFSET, [115] = SA8775_PIN_OFFSET, [116] = SA8775_PIN_OFFSET, + [117] = SA8775_PIN_OFFSET, [118] = SA8775_PIN_OFFSET, [119] = SA8775_PIN_OFFSET, + [120] = SA8775_PIN_OFFSET, [121] = SA8775_PIN_OFFSET, [122] = SA8775_PIN_OFFSET, + [123] = SA8775_PIN_OFFSET, [124] = SA8775_PIN_OFFSET, [125] = SA8775_PIN_OFFSET, + [126] = SA8775_PIN_OFFSET, [127] = SA8775_PIN_OFFSET, [128] = SA8775_PIN_OFFSET, + [129] = SA8775_PIN_OFFSET, [130] = SA8775_PIN_OFFSET, [131] = SA8775_PIN_OFFSET, + [132] = SA8775_PIN_OFFSET, [133] = SA8775_PIN_OFFSET, [134] = SA8775_PIN_OFFSET, + [135] = SA8775_PIN_OFFSET, [136] = SA8775_PIN_OFFSET, [137] = SA8775_PIN_OFFSET, + [138] = SA8775_PIN_OFFSET, [139] = SA8775_PIN_OFFSET, [140] = SA8775_PIN_OFFSET, + [141] = SA8775_PIN_OFFSET, [142] = SA8775_PIN_OFFSET, [143] = SA8775_PIN_OFFSET, + [144] = SA8775_PIN_OFFSET, [145] = SA8775_PIN_OFFSET, [146] = SA8775_PIN_OFFSET, + [147] = SA8775_PIN_OFFSET, [148] = SA8775_PIN_OFFSET, [148] = SA8775_PIN_OFFSET, + [149] = SA8775_PIN_OFFSET, [150] = SA8775_PIN_OFFSET, [151] = SA8775_PIN_OFFSET, + [152] = SA8775_PIN_OFFSET, [153] = SA8775_PIN_OFFSET, [154] = SA8775_PIN_OFFSET, +}; + +static const struct msm_pinctrl_data sa8775p_data = { + .pin_data = { + .pin_count = 155, + .special_pins_start = 149, + .special_pins_data = msm_special_pins_data, + .pin_offsets = sa8775p_pin_offsets, + }, + .functions_count = ARRAY_SIZE(msm_pinctrl_functions), + .get_function_name = sa8775p_get_function_name, + .get_function_mux = sa8775p_get_function_mux, + .get_pin_name = sa8775p_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { .compatible = "qcom,sa8775p-tlmm", .data = (ulong)&sa8775p_data }, + { /* Sentinal */ } +}; + +U_BOOT_DRIVER(pinctrl_sa8775p) = { + .name = "pinctrl_sa8775p", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-sdm660.c b/drivers/pinctrl/qcom/pinctrl-sdm660.c new file mode 100644 index 00000000000..646d848ffa4 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sdm660.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm SDM630/660 TLMM pinctrl + * + */ + +#include <dm.h> +#include "pinctrl-qcom.h" + +#define TLMM_BASE 0x03100000 +#define SOUTH (0x03100000 - TLMM_BASE) /* 0x0 */ +#define CENTER (0x03500000 - TLMM_BASE) /* 0x400000 */ +#define NORTH (0x03900000 - TLMM_BASE) /* 0x800000 */ + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +static const struct pinctrl_function sdm660_pinctrl_functions[] = { + { "gpio", 0 }, + { "blsp_uart2", 3 }, /* gpio 4 and 5, used for debug uart */ +}; + +static const unsigned int sdm660_pin_offsets[] = { + [0] = SOUTH, + [1] = SOUTH, + [2] = SOUTH, + [3] = SOUTH, + [4] = NORTH, + [5] = SOUTH, + [6] = SOUTH, + [7] = SOUTH, + [8] = NORTH, + [9] = NORTH, + [10] = NORTH, + [11] = NORTH, + [12] = NORTH, + [13] = NORTH, + [14] = NORTH, + [15] = NORTH, + [16] = CENTER, + [17] = CENTER, + [18] = CENTER, + [19] = CENTER, + [20] = SOUTH, + [21] = SOUTH, + [22] = CENTER, + [23] = CENTER, + [24] = NORTH, + [25] = NORTH, + [26] = NORTH, + [27] = NORTH, + [28] = CENTER, + [29] = CENTER, + [30] = CENTER, + [31] = CENTER, + [32] = SOUTH, + [33] = SOUTH, + [34] = SOUTH, + [35] = SOUTH, + [36] = SOUTH, + [37] = SOUTH, + [38] = SOUTH, + [39] = SOUTH, + [40] = SOUTH, + [41] = SOUTH, + [42] = SOUTH, + [43] = SOUTH, + [44] = SOUTH, + [45] = SOUTH, + [46] = SOUTH, + [47] = SOUTH, + [48] = SOUTH, + [49] = SOUTH, + [50] = SOUTH, + [51] = SOUTH, + [52] = SOUTH, + [53] = NORTH, + [54] = NORTH, + [55] = SOUTH, + [56] = SOUTH, + [57] = SOUTH, + [58] = SOUTH, + [59] = NORTH, + [60] = NORTH, + [61] = NORTH, + [62] = NORTH, + [63] = NORTH, + [64] = SOUTH, + [65] = SOUTH, + [66] = NORTH, + [67] = NORTH, + [68] = NORTH, + [69] = NORTH, + [70] = NORTH, + [71] = NORTH, + [72] = NORTH, + [73] = NORTH, + [74] = NORTH, + [75] = NORTH, + [76] = NORTH, + [77] = NORTH, + [78] = NORTH, + [79] = SOUTH, + [80] = SOUTH, + [81] = CENTER, + [82] = CENTER, + [83] = SOUTH, + [84] = SOUTH, + [85] = SOUTH, + [86] = SOUTH, + [87] = SOUTH, + [88] = SOUTH, + [89] = SOUTH, + [90] = SOUTH, + [91] = SOUTH, + [92] = SOUTH, + [93] = SOUTH, + [94] = SOUTH, + [95] = SOUTH, + [96] = SOUTH, + [97] = SOUTH, + [98] = SOUTH, + [99] = SOUTH, + [100] = SOUTH, + [101] = SOUTH, + [102] = SOUTH, + [103] = SOUTH, + [104] = SOUTH, + [105] = SOUTH, + [106] = SOUTH, + [107] = SOUTH, + [108] = SOUTH, + [109] = SOUTH, + [110] = SOUTH, + [111] = SOUTH, + [112] = SOUTH, + [113] = SOUTH, +}; + +/* + * Special pins - eMMC/SD related: [114..120], in total 7 special pins + */ + +#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + } + +/* All SDC pins are in the NORTH tile */ +static const struct msm_special_pin_data sdm660_special_pins_data[] = { + SDC_QDSD_PINGROUP("sdc1_clk", NORTH + 0x9a000, 13, 6), + SDC_QDSD_PINGROUP("sdc1_cmd", NORTH + 0x9a000, 11, 3), + SDC_QDSD_PINGROUP("sdc1_data", NORTH + 0x9a000, 9, 0), + SDC_QDSD_PINGROUP("sdc2_clk", NORTH + 0x9b000, 14, 6), + SDC_QDSD_PINGROUP("sdc2_cmd", NORTH + 0x9b000, 11, 3), + SDC_QDSD_PINGROUP("sdc2_data", NORTH + 0x9b000, 9, 0), + SDC_QDSD_PINGROUP("sdc1_rclk", NORTH + 0x9a000, 15, 0), +}; + +static const char *sdm660_get_function_name(struct udevice *dev, unsigned int selector) +{ + return sdm660_pinctrl_functions[selector].name; +} + +static const char *sdm660_get_pin_name(struct udevice *dev, unsigned int selector) +{ + static const char * const special_pins_names[] = { + "sdc1_clk", "sdc1_cmd", "sdc1_data", + "sdc2_clk", "sdc2_cmd", "sdc2_data", + "sdc1_rclk" + }; + + if (selector >= 114 && selector <= 120) + snprintf(pin_name, MAX_PIN_NAME_LEN, special_pins_names[selector - 114]); + else + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + + return pin_name; +} + +static int sdm660_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +{ + if (selector >= 0 && selector < ARRAY_SIZE(sdm660_pinctrl_functions)) + return sdm660_pinctrl_functions[selector].val; + return -EINVAL; +} + +struct msm_pinctrl_data sdm660_data = { + .pin_data = { + .pin_offsets = sdm660_pin_offsets, + .pin_count = ARRAY_SIZE(sdm660_pin_offsets) + ARRAY_SIZE(sdm660_special_pins_data), + .special_pins_start = 114, + .special_pins_data = sdm660_special_pins_data, + }, + .functions_count = ARRAY_SIZE(sdm660_pinctrl_functions), + .get_function_name = sdm660_get_function_name, + .get_function_mux = sdm660_get_function_mux, + .get_pin_name = sdm660_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { + .compatible = "qcom,sdm630-pinctrl", + .data = (ulong)&sdm660_data + }, + { + .compatible = "qcom,sdm660-pinctrl", + .data = (ulong)&sdm660_data + }, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(pinctrl_ssdm660) = { + .name = "pinctrl_sdm660", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, +}; diff --git a/drivers/pinctrl/tegra/funcmux-tegra20.c b/drivers/pinctrl/tegra/funcmux-tegra20.c index 951ae196161..f60d5aad3a4 100644 --- a/drivers/pinctrl/tegra/funcmux-tegra20.c +++ b/drivers/pinctrl/tegra/funcmux-tegra20.c @@ -62,8 +62,15 @@ int funcmux_select(enum periph_id id, int config) pinmux_tristate_disable(PMUX_PINGRP_SDIO1); bad_config = 0; break; + case FUNCMUX_UART1_SDB_SDD: + pinmux_set_func(PMUX_PINGRP_SDB, PMUX_FUNC_UARTA); + pinmux_set_func(PMUX_PINGRP_SDD, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PMUX_PINGRP_SDB); + pinmux_tristate_disable(PMUX_PINGRP_SDD); + bad_config = 0; + break; } - if (!bad_config) { + if (!bad_config && config != FUNCMUX_UART1_SDB_SDD) { /* * Tegra appears to boot with function UARTA pre- * selected on mux group SDB. If two mux groups are diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index b04be168bc8..bc02825ee1f 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -14,7 +14,7 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) { struct pmux_drvgrp_config *drive_group; - int i, ret, pad_id; + int i, ret, pad_id, count = 0; const char **pads; drive_group = kmalloc_array(drvcnt, sizeof(*drive_group), GFP_KERNEL); @@ -46,21 +46,32 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) goto exit; } + /* + * i goes through all drive instances defined, while + * count is increased only if a valid configuration is found + */ for (i = 0; i < drvcnt; i++) { for (pad_id = 0; pad_id < PMUX_DRVGRP_COUNT; pad_id++) if (tegra_pinctrl_to_drvgrp[pad_id]) if (!strcmp(pads[i], tegra_pinctrl_to_drvgrp[pad_id])) { - drive_group[i].drvgrp = pad_id; + drive_group[count].drvgrp = pad_id; break; } - debug("%s drvmap: %d, %d, %d, %d, %d\n", pads[i], - drive_group[i].drvgrp, drive_group[i].slwf, - drive_group[i].slwr, drive_group[i].drvup, - drive_group[i].drvdn); + if (pad_id == PMUX_DRVGRP_COUNT) { + log_debug("%s: drive %s is not valid\n", __func__, pads[i]); + continue; + } + + log_debug("%s(%d) drvmap: %d, %d, %d, %d, %d\n", pads[count], count, + drive_group[count].drvgrp, drive_group[count].slwf, + drive_group[count].slwr, drive_group[count].drvup, + drive_group[count].drvdn); + + count++; } - pinmux_config_drvgrp_table(drive_group, drvcnt); + pinmux_config_drvgrp_table(drive_group, count); free(pads); exit: @@ -71,7 +82,7 @@ exit: static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) { struct pmux_mipipadctrlgrp_config *mipipad_group; - int i, ret, pad_id; + int i, ret, pad_id, count = 0; const char *function; const char **pads; @@ -89,6 +100,11 @@ static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) if (!strcmp(function, tegra_pinctrl_to_func[i])) break; + if (!function || i == PMUX_FUNC_COUNT) { + log_debug("%s: pin function is not defined or is not valid\n", __func__); + goto exit; + } + mipipad_group[0].func = i; for (i = 1; i < padcnt; i++) @@ -100,16 +116,27 @@ static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) goto exit; } + /* + * i goes through all pin instances defined, while + * count is increased only if a valid configuration is found + */ for (i = 0; i < padcnt; i++) { for (pad_id = 0; pad_id < PMUX_MIPIPADCTRLGRP_COUNT; pad_id++) if (tegra_pinctrl_to_mipipadgrp[pad_id]) if (!strcmp(pads[i], tegra_pinctrl_to_mipipadgrp[pad_id])) { - mipipad_group[i].grp = pad_id; + mipipad_group[count].grp = pad_id; break; } + + if (pad_id == PMUX_MIPIPADCTRLGRP_COUNT) { + log_debug("%s: drive %s is not valid\n", __func__, pads[i]); + continue; + } + + count++; } - pinmux_config_mipipadctrlgrp_table(mipipad_group, padcnt); + pinmux_config_mipipadctrlgrp_table(mipipad_group, count); free(pads); exit: @@ -122,7 +149,7 @@ static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) { } static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) { struct pmux_pingrp_config *pinmux_group; - int i, ret, pin_id; + int i, ret, pin_id, count = 0; const char *function; const char **pins; @@ -140,6 +167,11 @@ static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) if (!strcmp(function, tegra_pinctrl_to_func[i])) break; + if (!function || i == PMUX_FUNC_COUNT) { + log_debug("%s: pin function is not defined or is not valid\n", __func__); + goto exit; + } + pinmux_group[0].func = i; pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", PMUX_PULL_NORMAL); @@ -178,20 +210,31 @@ static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) goto exit; } + /* + * i goes through all pin instances defined, while + * count is increased only if a valid configuration is found + */ for (i = 0; i < pincnt; i++) { for (pin_id = 0; pin_id < PMUX_PINGRP_COUNT; pin_id++) if (tegra_pinctrl_to_pingrp[pin_id]) if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) { - pinmux_group[i].pingrp = pin_id; + pinmux_group[count].pingrp = pin_id; break; } - debug("%s pinmap: %d, %d, %d, %d\n", pins[i], - pinmux_group[i].pingrp, pinmux_group[i].func, - pinmux_group[i].pull, pinmux_group[i].tristate); + if (pin_id == PMUX_PINGRP_COUNT) { + log_debug("%s: pin %s is not valid\n", __func__, pins[i]); + continue; + } + + log_debug("%s(%d) pinmap: %d, %d, %d, %d\n", pins[count], count, + pinmux_group[count].pingrp, pinmux_group[count].func, + pinmux_group[count].pull, pinmux_group[count].tristate); + + count++; } - pinmux_config_pingrp_table(pinmux_group, pincnt); + pinmux_config_pingrp_table(pinmux_group, count); free(pins); exit: diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c index d59b3ec7b5d..c32d590a7e0 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra20.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c @@ -37,6 +37,11 @@ static void tegra_pinctrl_set_pin(struct udevice *config) if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) break; + if (pin_id == PMUX_PINGRP_COUNT) { + log_debug("%s: %s(%d) is not valid\n", __func__, pins[i], pin_id); + continue; + } + if (pull >= 0) pinmux_set_pullupdown(pin_id, pull); @@ -58,13 +63,16 @@ static void tegra_pinctrl_set_func(struct udevice *config) const char **pins; function = dev_read_string(config, "nvidia,function"); - if (function) + if (function) { for (i = 0; i < PMUX_FUNC_COUNT; i++) if (tegra_pinctrl_to_func[i]) if (!strcmp(function, tegra_pinctrl_to_func[i])) break; - func_id = i; + func_id = i; + } else { + func_id = PMUX_FUNC_COUNT; + } count = dev_read_string_list(config, "nvidia,pins", &pins); if (count < 0) { @@ -78,6 +86,12 @@ static void tegra_pinctrl_set_func(struct udevice *config) if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) break; + if (func_id == PMUX_FUNC_COUNT || pin_id == PMUX_PINGRP_COUNT) { + log_debug("%s: pin %s(%d) or function %s(%d) is not valid\n", + __func__, pins[i], pin_id, function, func_id); + continue; + } + debug("%s(%d) muxed to %s(%d)\n", pins[i], pin_id, function, func_id); pinmux_set_func(pin_id, func_id); diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index bd82d2f7044..5f5218bd8b5 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -47,6 +47,13 @@ config IMX8MP_HSIOMIX_BLKCTRL help Enable support for manipulating NXP i.MX8MP on-SoC HSIOMIX block controller. +config IMX8MP_MEDIAMIX_BLKCTRL + bool "Enable i.MX8MP MEDIAMIX domain driver" + depends on POWER_DOMAIN && IMX8MP + select CLK + help + Enable support for manipulating NXP i.MX8MP on-SoC MEDIAMIX block controller. + config MTK_POWER_DOMAIN bool "Enable the MediaTek power domain driver" depends on POWER_DOMAIN && ARCH_MEDIATEK diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index 110646c503a..4d20c97d26c 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -3,12 +3,13 @@ # Copyright (c) 2016, NVIDIA CORPORATION. # -obj-$(CONFIG_$(XPL_)POWER_DOMAIN) += power-domain-uclass.o +obj-$(CONFIG_$(PHASE_)POWER_DOMAIN) += power-domain-uclass.o obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o +obj-$(CONFIG_IMX8MP_MEDIAMIX_BLKCTRL) += imx8mp-mediamix.o obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c index c22fbe60675..e54ba5d9a54 100644 --- a/drivers/power/domain/imx8m-power-domain.c +++ b/drivers/power/domain/imx8m-power-domain.c @@ -40,6 +40,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MN_MIPI_A53_DOMAIN BIT(2) #define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19) +#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12) #define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5) #define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4) #define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3) @@ -63,6 +64,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MN_MIPI_SW_Pxx_REQ BIT(0) #define IMX8MP_HSIOMIX_Pxx_REQ BIT(17) +#define IMX8MP_MEDIAMIX_Pxx_REQ BIT(10) #define IMX8MP_USB2_PHY_Pxx_REQ BIT(3) #define IMX8MP_USB1_PHY_Pxx_REQ BIT(2) #define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1) @@ -81,6 +83,9 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MP_HSIOMIX_PWRDNACKN BIT(28) #define IMX8MP_HSIOMIX_PWRDNREQN BIT(12) +#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30) +#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14) + /* * The PGC offset values in Reference Manual * (Rev. 1, 01/2018 and the older ones) GPC chapter's @@ -101,6 +106,7 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX8MP_PGC_PCIE 13 #define IMX8MP_PGC_USB1 14 #define IMX8MP_PGC_USB2 15 +#define IMX8MP_PGC_MEDIAMIX 22 #define IMX8MP_PGC_HSIOMIX 29 #define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) @@ -303,6 +309,17 @@ static const struct imx_pgc_domain imx8mp_pgc_domains[] = { .pgc = BIT(IMX8MP_PGC_HSIOMIX), .keep_clocks = true, }, + + [IMX8MP_POWER_DOMAIN_MEDIAMIX] = { + .bits = { + .pxx = IMX8MP_MEDIAMIX_Pxx_REQ, + .map = IMX8MP_MEDIAMIX_A53_DOMAIN, + .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN, + .hskack = IMX8MP_MEDIAMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIAMIX), + .keep_clocks = true, + }, }; static const struct imx_pgc_regs imx8mp_pgc_regs = { diff --git a/drivers/power/domain/imx8mp-mediamix.c b/drivers/power/domain/imx8mp-mediamix.c new file mode 100644 index 00000000000..78c32ca3d3a --- /dev/null +++ b/drivers/power/domain/imx8mp-mediamix.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i.MX8 MEDIAMIX control block driver + * Copyright (C) 2024 Miquel Raynal <miquel.raynal@bootlin.com> + * Inspired from Marek Vasut <marex@denx.de> work on the hsiomix driver. + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <power-domain-uclass.h> +#include <linux/delay.h> + +#include <dt-bindings/power/imx8mp-power.h> + +#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 + +struct imx8mp_mediamix_priv { + void __iomem *base; + struct clk clk_apb; + struct clk clk_axi; + struct clk clk_disp2; + struct power_domain pd_bus; + struct power_domain pd_lcdif2; +}; + +static int imx8mp_mediamix_on(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + struct power_domain *domain; + struct clk *clk; + u32 reset; + int ret; + + switch (power_domain->id) { + case IMX8MP_MEDIABLK_PD_LCDIF_2: + domain = &priv->pd_lcdif2; + clk = &priv->clk_disp2; + reset = BIT(11) | BIT(12) | BIT(24); + break; + default: + return -EINVAL; + } + + /* Make sure bus domain is awake */ + ret = power_domain_on(&priv->pd_bus); + if (ret) + return ret; + + /* Put devices into reset */ + clrbits_le32(priv->base + BLK_SFT_RSTN, reset); + + /* Enable upstream clocks */ + ret = clk_enable(&priv->clk_apb); + if (ret) + goto dis_bus_pd; + + ret = clk_enable(&priv->clk_axi); + if (ret) + goto dis_apb_clk; + + /* Enable blk-ctrl clock to allow reset to propagate */ + ret = clk_enable(clk); + if (ret) + goto dis_axi_clk; + setbits_le32(priv->base + BLK_CLK_EN, reset); + + /* Power up upstream GPC domain */ + ret = power_domain_on(domain); + if (ret) + goto dis_lcdif_clk; + + /* Wait for reset to propagate */ + udelay(5); + + /* Release reset */ + setbits_le32(priv->base + BLK_SFT_RSTN, reset); + + return 0; + +dis_lcdif_clk: + clk_disable(clk); +dis_axi_clk: + clk_disable(&priv->clk_axi); +dis_apb_clk: + clk_disable(&priv->clk_apb); +dis_bus_pd: + power_domain_off(&priv->pd_bus); + + return ret; +} + +static int imx8mp_mediamix_off(struct power_domain *power_domain) +{ + struct udevice *dev = power_domain->dev; + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + struct power_domain *domain; + struct clk *clk; + u32 reset; + + switch (power_domain->id) { + case IMX8MP_MEDIABLK_PD_LCDIF_2: + domain = &priv->pd_lcdif2; + clk = &priv->clk_disp2; + reset = BIT(11) | BIT(12) | BIT(24); + break; + default: + return -EINVAL; + } + + /* Put devices into reset and disable clocks */ + clrbits_le32(priv->base + BLK_SFT_RSTN, reset); + clrbits_le32(priv->base + BLK_CLK_EN, reset); + + /* Power down upstream GPC domain */ + power_domain_off(domain); + + clk_disable(clk); + clk_disable(&priv->clk_axi); + clk_disable(&priv->clk_apb); + + /* Allow bus domain to suspend */ + power_domain_off(&priv->pd_bus); + + return 0; +} + +static int imx8mp_mediamix_of_xlate(struct power_domain *power_domain, + struct ofnode_phandle_args *args) +{ + power_domain->id = args->args[0]; + + return 0; +} + +static int imx8mp_mediamix_bind(struct udevice *dev) +{ + /* Bind child lcdif */ + return dm_scan_fdt_dev(dev); +} + +static int imx8mp_mediamix_probe(struct udevice *dev) +{ + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr_ptr(dev); + + ret = clk_get_by_name(dev, "apb", &priv->clk_apb); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "axi", &priv->clk_axi); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "disp2", &priv->clk_disp2); + if (ret < 0) + return ret; + + ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus"); + if (ret < 0) + return ret; + + ret = power_domain_get_by_name(dev, &priv->pd_lcdif2, "lcdif2"); + if (ret < 0) + goto free_bus_pd; + + return 0; + +free_bus_pd: + power_domain_free(&priv->pd_bus); + return ret; +} + +static int imx8mp_mediamix_remove(struct udevice *dev) +{ + struct imx8mp_mediamix_priv *priv = dev_get_priv(dev); + + power_domain_free(&priv->pd_lcdif2); + power_domain_free(&priv->pd_bus); + + return 0; +} + +static const struct udevice_id imx8mp_mediamix_ids[] = { + { .compatible = "fsl,imx8mp-media-blk-ctrl" }, + { } +}; + +struct power_domain_ops imx8mp_mediamix_ops = { + .on = imx8mp_mediamix_on, + .off = imx8mp_mediamix_off, + .of_xlate = imx8mp_mediamix_of_xlate, +}; + +U_BOOT_DRIVER(imx8mp_mediamix) = { + .name = "imx8mp_mediamix", + .id = UCLASS_POWER_DOMAIN, + .of_match = imx8mp_mediamix_ids, + .bind = imx8mp_mediamix_bind, + .probe = imx8mp_mediamix_probe, + .remove = imx8mp_mediamix_remove, + .priv_auto = sizeof(struct imx8mp_mediamix_priv), + .ops = &imx8mp_mediamix_ops, +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index bbcbcee4c35..5a61cd45b8c 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -295,6 +295,16 @@ config DM_PMIC_SANDBOX Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt +config DM_PMIC_CPCAP + bool "Enable Driver Model for Motorola CPCAP" + help + The CPCAP is a Motorola/ST-Ericsson creation, a multifunctional IC + whose main purpose is power control. It was used in a wide variety of + Motorola products, both Tegra and OMAP based. The most notable devices + using this PMIC are the Motorola Droid 4, Atrix 4G, and Droid X2. + Unlike most PMICs, this one is not I2C based; it uses the SPI bus. The + core driver provides both read and write access to the device registers. + config PMIC_S5M8767 bool "Enable Driver Model for the Samsung S5M8767 PMIC" ---help--- diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index bc138f563ff..2210b1a64ae 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -4,41 +4,42 @@ # Lukasz Majewski <l.majewski@samsung.com> obj-$(CONFIG_$(PHASE_)DM_PMIC) += pmic-uclass.o -obj-$(CONFIG_$(XPL_)DM_PMIC_FAN53555) += fan53555.o -obj-$(CONFIG_$(XPL_)DM_PMIC_DA9063) += da9063.o -obj-$(CONFIG_$(XPL_)DM_PMIC_MAX77663) += max77663.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_FAN53555) += fan53555.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_DA9063) += da9063.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_MAX77663) += max77663.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o -obj-$(CONFIG_$(XPL_)DM_PMIC_BD71837) += bd71837.o -obj-$(CONFIG_$(XPL_)DM_PMIC_MP5416) += mp5416.o -obj-$(CONFIG_$(XPL_)DM_PMIC_PFUZE100) += pfuze100.o -obj-$(CONFIG_$(XPL_)DM_PMIC_PCA9450) += pca9450.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_BD71837) += bd71837.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_MP5416) += mp5416.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_PCA9450) += pca9450.o obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_PMIC_AB8500) += ab8500.o obj-$(CONFIG_PMIC_ACT8846) += act8846.o obj-$(CONFIG_PMIC_AS3722) += as3722.o as3722_gpio.o -obj-$(CONFIG_$(XPL_)PMIC_AXP) += axp.o +obj-$(CONFIG_$(PHASE_)PMIC_AXP) += axp.o obj-$(CONFIG_PMIC_MAX8997) += max8997.o obj-$(CONFIG_PMIC_QCOM) += pmic_qcom.o obj-$(CONFIG_$(PHASE_)PMIC_RK8XX) += rk8xx.o -obj-$(CONFIG_$(XPL_)PMIC_RN5T567) += rn5t567.o +obj-$(CONFIG_$(PHASE_)PMIC_RN5T567) += rn5t567.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o -obj-$(CONFIG_$(XPL_)DM_PMIC_TPS80031) += tps80031.o -obj-$(CONFIG_$(XPL_)PMIC_PALMAS) += palmas.o -obj-$(CONFIG_$(XPL_)PMIC_LP873X) += lp873x.o -obj-$(CONFIG_$(XPL_)PMIC_LP87565) += lp87565.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_TPS80031) += tps80031.o +obj-$(CONFIG_$(PHASE_)PMIC_PALMAS) += palmas.o +obj-$(CONFIG_$(PHASE_)PMIC_LP873X) += lp873x.o +obj-$(CONFIG_$(PHASE_)PMIC_LP87565) += lp87565.o obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o obj-$(CONFIG_PMIC_TPS65217) += pmic_tps65217.o obj-$(CONFIG_PMIC_TPS65219) += tps65219.o obj-$(CONFIG_PMIC_TPS65941) += tps65941.o obj-$(CONFIG_PMIC_RAA215300) += raa215300.o obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_CPCAP) += cpcap.o -ifeq ($(CONFIG_$(XPL_)POWER_LEGACY),y) +ifeq ($(CONFIG_$(PHASE_)POWER_LEGACY),y) obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_PCA9450) += pmic_pca9450.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o @@ -47,5 +48,5 @@ obj-$(CONFIG_POWER_HI6553) += pmic_hi6553.o obj-$(CONFIG_POWER_MC34VR500) += pmic_mc34vr500.o endif -obj-$(CONFIG_$(XPL_)POWER_TPS62362) += pmic_tps62362.o +obj-$(CONFIG_$(PHASE_)POWER_TPS62362) += pmic_tps62362.o obj-$(CONFIG_SPL_POWER_TPS65910) += pmic_tps65910.o diff --git a/drivers/power/pmic/cpcap.c b/drivers/power/pmic/cpcap.c new file mode 100644 index 00000000000..f2076afff43 --- /dev/null +++ b/drivers/power/pmic/cpcap.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <dm/lists.h> +#include <log.h> +#include <power/pmic.h> +#include <power/cpcap.h> +#include <spi.h> +#include <linux/delay.h> +#include <linux/err.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "sw", .driver = CPCAP_SW_DRIVER }, + { .prefix = "v", .driver = CPCAP_LDO_DRIVER }, + { }, +}; + +static int cpcap_write(struct udevice *dev, uint reg, const uint8_t *buff, int len) +{ + u8 buf[4]; + u16 data = *(u16 *)buff; + int ret; + + buf[0] = ((reg >> 8) & 0xff) | 0x80; + buf[1] = reg & 0xff; + buf[2] = data >> 8 & 0xff; + buf[3] = data & 0xff; + + ret = dm_spi_xfer(dev, 32, buf, NULL, SPI_XFER_ONCE); + + log_debug("%s: reg 0x%x, data 0x%04x, ret %d\n", __func__, reg, data, ret); + + return ret; +} + +static int cpcap_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + u8 buf[4]; + int ret; + + buf[0] = (reg >> 8) & 0xff; + buf[1] = reg & 0xff; + buf[2] = 0; + buf[3] = 0; + + ret = dm_spi_xfer(dev, 32, buf, buf, SPI_XFER_ONCE); + *buff = (buf[2] << 8) | buf[3]; + + log_debug("%s: reg 0x%x, data 0x%04x, ret %d\n", __func__, reg, *buff, ret); + return ret; +} + +static int cpcap_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + /* Regulator device node of PMIC */ + regulators_node = dev_read_subnode(dev, "regulator"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulator subnode not found!\n", dev->name); + return -ENXIO; + } + + /* Actual regulators container */ + regulators_node = ofnode_find_subnode(regulators_node, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + return dm_scan_fdt_dev(dev); +} + +static int cpcap_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parent_priv(dev); + int ret; + + ret = spi_claim_bus(slave); + if (ret) { + log_err("SPI bus allocation failed (%d)\n", ret); + return ret; + } + + u16 id = pmic_reg_read(dev, CPCAP_REG_VERSC1); + + u16 ven = (id >> 6) & 0x7; + u16 rev = ((id >> 3) & 0x7) | ((id << 3) & 0x38); + + log_debug("%s: vendor %s rev: %i.%i (%x)\n", __func__, + ven == CPCAP_VENDOR_ST ? "ST" : "TI", + CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev), + rev); + return 0; +} + +static struct dm_pmic_ops cpcap_ops = { + .read = cpcap_read, + .write = cpcap_write, +}; + +static const struct udevice_id cpcap_ids[] = { + { .compatible = "motorola,cpcap" }, + { .compatible = "st,6556002" }, + { } +}; + +U_BOOT_DRIVER(pmic_cpcap) = { + .name = "cpcap_pmic", + .id = UCLASS_PMIC, + .of_match = cpcap_ids, + .bind = cpcap_bind, + .probe = cpcap_probe, + .ops = &cpcap_ops, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 8f102a92c23..bec2d2d7d49 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -224,6 +224,13 @@ config DM_REGULATOR_QCOM_RPMH implements get/set api for a limited set of regulators used by u-boot. +config DM_REGULATOR_QCOM_USB_VBUS + bool "Enable driver model for Qualcomm USB vbus regulator" + depends on DM_REGULATOR + ---help--- + Enable support for the Qualcomm USB Vbus regulator. The driver + implements get/set api for the regulator to be used by u-boot. + config SPL_DM_REGULATOR_GPIO bool "Enable Driver Model for GPIO REGULATOR in SPL" depends on DM_REGULATOR_GPIO && SPL_GPIO @@ -486,3 +493,12 @@ config REGULATOR_RZG2L_USBPHY Enable this option to support controlling the VBUS supply in the USB PHY peripheral of the Renesas RZ/G2L SoC. This option is required in order to use the USB OTG port. + +config DM_REGULATOR_CPCAP + bool "Enable driver for CPCAP PMIC regulators" + depends on DM_REGULATOR && DM_PMIC_CPCAP + ---help--- + Enable implementation of driver-model regulator uclass features for + REGULATOR CPCAP. The driver supports both DC-to-DC Step-Down Switching + (SW) Regulators and Low-Dropout Linear (LDO) Regulators found in CPCAP + PMIC and implements get/set api for voltage and state. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 4382d4b3ab9..99affa235f3 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -4,42 +4,44 @@ # Przemyslaw Marczak <p.marczak@samsung.com> # -obj-$(CONFIG_$(XPL_)DM_REGULATOR) += regulator-uclass.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o -obj-$(CONFIG_$(XPL_)REGULATOR_AXP) += axp_regulator.o -obj-$(CONFIG_$(XPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_DA9063) += da9063.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o +obj-$(CONFIG_$(PHASE_)REGULATOR_AXP) += axp_regulator.o +obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX77663) += max77663_regulator.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o -obj-$(CONFIG_$(XPL_)DM_PMIC_PFUZE100) += pfuze100.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_BD71837) += bd71837.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_PCA9450) += pca9450.o -obj-$(CONFIG_$(XPL_)REGULATOR_PWM) += pwm_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_FAN53555) += fan53555.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_COMMON) += regulator_common.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_FIXED) += fixed.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_GPIO) += gpio-regulator.o +obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_BD71837) += bd71837.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PCA9450) += pca9450.o +obj-$(CONFIG_$(PHASE_)REGULATOR_PWM) += pwm_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FAN53555) += fan53555.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_COMMON) += regulator_common.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_DM_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o +obj-$(CONFIG_DM_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus_regulator.o obj-$(CONFIG_$(PHASE_)REGULATOR_RK8XX) += rk8xx.o obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_PBIAS) += pbias_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PALMAS) += palmas_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PBIAS) += pbias_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_LP873X) += lp873x_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_LP87565) += lp87565_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS65911) += tps65911_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS65911) += tps65911_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS6287X) += tps6287x_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_STPMIC1) += stpmic1.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS6287X) += tps6287x_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS80031) += tps80031_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o -obj-$(CONFIG_$(XPL_)DM_REGULATOR_ANATOP) += anatop_regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_ANATOP) += anatop_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS65219) += tps65219_regulator.o obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o +obj-$(CONFIG_$(PHASE_)DM_REGULATOR_CPCAP) += cpcap_regulator.o diff --git a/drivers/power/regulator/cpcap_regulator.c b/drivers/power/regulator/cpcap_regulator.c new file mode 100644 index 00000000000..04cd6651374 --- /dev/null +++ b/drivers/power/regulator/cpcap_regulator.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/cpcap.h> +#include <linux/delay.h> +#include <linux/err.h> + +/* CPCAP_REG_ASSIGN2 bits - Resource Assignment 2 */ +#define CPCAP_BIT_VSDIO_SEL BIT(15) +#define CPCAP_BIT_VDIG_SEL BIT(14) +#define CPCAP_BIT_VCAM_SEL BIT(13) +#define CPCAP_BIT_SW6_SEL BIT(12) +#define CPCAP_BIT_SW5_SEL BIT(11) +#define CPCAP_BIT_SW4_SEL BIT(10) +#define CPCAP_BIT_SW3_SEL BIT(9) +#define CPCAP_BIT_SW2_SEL BIT(8) +#define CPCAP_BIT_SW1_SEL BIT(7) + +/* CPCAP_REG_ASSIGN3 bits - Resource Assignment 3 */ +#define CPCAP_BIT_VUSBINT2_SEL BIT(15) +#define CPCAP_BIT_VUSBINT1_SEL BIT(14) +#define CPCAP_BIT_VVIB_SEL BIT(13) +#define CPCAP_BIT_VWLAN1_SEL BIT(12) +#define CPCAP_BIT_VRF1_SEL BIT(11) +#define CPCAP_BIT_VHVIO_SEL BIT(10) +#define CPCAP_BIT_VDAC_SEL BIT(9) +#define CPCAP_BIT_VUSB_SEL BIT(8) +#define CPCAP_BIT_VSIM_SEL BIT(7) +#define CPCAP_BIT_VRFREF_SEL BIT(6) +#define CPCAP_BIT_VPLL_SEL BIT(5) +#define CPCAP_BIT_VFUSE_SEL BIT(4) +#define CPCAP_BIT_VCSI_SEL BIT(3) +#define CPCAP_BIT_SPARE_14_2 BIT(2) +#define CPCAP_BIT_VWLAN2_SEL BIT(1) +#define CPCAP_BIT_VRF2_SEL BIT(0) +#define CPCAP_BIT_NONE 0 + +/* CPCAP_REG_ASSIGN4 bits - Resource Assignment 4 */ +#define CPCAP_BIT_VAUDIO_SEL BIT(0) + +/* + * Off mode configuration bit. Used currently only by SW5 on omap4. There's + * the following comment in Motorola Linux kernel tree for it: + * + * When set in the regulator mode, the regulator assignment will be changed + * to secondary when the regulator is disabled. The mode will be set back to + * primary when the regulator is turned on. + */ +#define CPCAP_REG_OFF_MODE_SEC BIT(15) + +#define CPCAP_REG(_reg, _assignment_reg, _assignment_mask, _mode_mask, \ + _volt_mask, _volt_shft, _mode_val, _off_mode_val, _val_tbl, \ + _mode_cntr, _volt_trans_time, _turn_on_time, _bit_offset) { \ + .reg = CPCAP_REG_##_reg, \ + .assignment_reg = CPCAP_REG_##_assignment_reg, \ + .assignment_mask = CPCAP_BIT_##_assignment_mask, \ + .mode_mask = _mode_mask, \ + .volt_mask = _volt_mask, \ + .volt_shft = _volt_shft, \ + .mode_val = _mode_val, \ + .off_mode_val = _off_mode_val, \ + .val_tbl_sz = ARRAY_SIZE(_val_tbl), \ + .val_tbl = _val_tbl, \ + .mode_cntr = _mode_cntr, \ + .volt_trans_time = _volt_trans_time, \ + .turn_on_time = _turn_on_time, \ + .bit_offset_from_cpcap_lowest_voltage = _bit_offset, \ +} + +static const struct cpcap_regulator_data tegra20_regulators[CPCAP_REGULATORS_COUNT] = { + /* BUCK */ + [CPCAP_SW1] = CPCAP_REG(S1C1, ASSIGN2, SW1_SEL, 0x6f00, 0x007f, + 0, 0x6800, 0, sw1_val_tbl, 0, 0, 1500, 0x0c), + [CPCAP_SW2] = CPCAP_REG(S2C1, ASSIGN2, SW2_SEL, 0x6f00, 0x007f, + 0, 0x4804, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18), + [CPCAP_SW3] = CPCAP_REG(S3C, ASSIGN2, SW3_SEL, 0x0578, 0x0003, + 0, 0x043c, 0, sw3_val_tbl, 0, 0, 0, 0), + [CPCAP_SW4] = CPCAP_REG(S4C1, ASSIGN2, SW4_SEL, 0x6f00, 0x007f, + 0, 0x4909, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18), + [CPCAP_SW5] = CPCAP_REG(S5C, ASSIGN2, SW5_SEL, 0x0028, 0x0000, + 0, 0x0020, 0, sw5_val_tbl, 0, 0, 1500, 0), + [CPCAP_SW6] = CPCAP_REG(S6C, ASSIGN2, SW6_SEL, 0x0000, 0x0000, + 0, 0, 0, unknown_val_tbl, 0, 0, 0, 0), + /* LDO */ + [CPCAP_VCAM] = CPCAP_REG(VCAMC, ASSIGN2, VCAM_SEL, 0x0087, 0x0030, + 4, 0x7, 0, vcam_val_tbl, 0, 420, 1000, 0), + [CPCAP_VCSI] = CPCAP_REG(VCSIC, ASSIGN3, VCSI_SEL, 0x0047, 0x0010, + 4, 0x7, 0, vcsi_val_tbl, 0, 350, 1000, 0), + [CPCAP_VDAC] = CPCAP_REG(VDACC, ASSIGN3, VDAC_SEL, 0x0087, 0x0030, + 4, 0x0, 0, vdac_val_tbl, 0, 420, 1000, 0), + [CPCAP_VDIG] = CPCAP_REG(VDIGC, ASSIGN2, VDIG_SEL, 0x0087, 0x0030, + 4, 0x0, 0, vdig_val_tbl, 0, 420, 1000, 0), + [CPCAP_VFUSE] = CPCAP_REG(VFUSEC, ASSIGN3, VFUSE_SEL, 0x00a0, 0x000f, + 0, 0x0, 0, vfuse_val_tbl, 0, 420, 1000, 0), + [CPCAP_VHVIO] = CPCAP_REG(VHVIOC, ASSIGN3, VHVIO_SEL, 0x0017, 0x0000, + 0, 0x2, 0, vhvio_val_tbl, 0, 0, 1000, 0), + [CPCAP_VSDIO] = CPCAP_REG(VSDIOC, ASSIGN2, VSDIO_SEL, 0x0087, 0x0038, + 3, 0x2, 0, vsdio_val_tbl, 0, 420, 1000, 0), + [CPCAP_VPLL] = CPCAP_REG(VPLLC, ASSIGN3, VPLL_SEL, 0x0047, 0x0018, + 3, 0x1, 0, vpll_val_tbl, 0, 420, 100, 0), + [CPCAP_VRF1] = CPCAP_REG(VRF1C, ASSIGN3, VRF1_SEL, 0x00ac, 0x0002, + 1, 0x0, 0, vrf1_val_tbl, 0, 10, 1000, 0), + [CPCAP_VRF2] = CPCAP_REG(VRF2C, ASSIGN3, VRF2_SEL, 0x0023, 0x0008, + 3, 0x0, 0, vrf2_val_tbl, 0, 10, 1000, 0), + [CPCAP_VRFREF] = CPCAP_REG(VRFREFC, ASSIGN3, VRFREF_SEL, 0x0023, 0x0008, + 3, 0x0, 0, vrfref_val_tbl, 0, 420, 100, 0), + [CPCAP_VWLAN1] = CPCAP_REG(VWLAN1C, ASSIGN3, VWLAN1_SEL, 0x0047, 0x0010, + 4, 0x0, 0, vwlan1_val_tbl, 0, 420, 1000, 0), + [CPCAP_VWLAN2] = CPCAP_REG(VWLAN2C, ASSIGN3, VWLAN2_SEL, 0x020c, 0x00c0, + 6, 0xd, 0, vwlan2_val_tbl, 0, 420, 1000, 0), + [CPCAP_VSIM] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x0023, 0x0008, + 3, 0x0, 0, vsim_val_tbl, 0, 420, 1000, 0), + [CPCAP_VSIMCARD] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x1e80, 0x0008, + 3, 0x1E00, 0, vsimcard_val_tbl, 0, 420, 1000, 0), + [CPCAP_VVIB] = CPCAP_REG(VVIBC, ASSIGN3, VVIB_SEL, 0x0001, 0x000c, + 2, 0x1, 0, vvib_val_tbl, 0, 500, 500, 0), + [CPCAP_VUSB] = CPCAP_REG(VUSBC, ASSIGN3, VUSB_SEL, 0x011c, 0x0040, + 6, 0xc, 0, vusb_val_tbl, 0, 0, 1000, 0), + [CPCAP_VAUDIO] = CPCAP_REG(VAUDIOC, ASSIGN4, VAUDIO_SEL, 0x0016, 0x0001, + 0, 0x5, 0, vaudio_val_tbl, 0, 0, 1000, 0), +}; + +static int cpcap_regulator_get_value(struct udevice *dev) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int value, volt_shift = regulator->volt_shft; + + value = pmic_reg_read(dev->parent, regulator->reg); + if (value < 0) + return value; + + if (!(value & regulator->mode_mask)) + return 0; + + value &= regulator->volt_mask; + value -= regulator->bit_offset_from_cpcap_lowest_voltage; + + return regulator->val_tbl[value >> volt_shift]; +} + +static int cpcap_regulator_set_value(struct udevice *dev, int uV) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int value, ret, volt_shift = regulator->volt_shft; + + if (dev->driver_data == CPCAP_VRF1) { + if (uV > 2500000) + value = 0; + else + value = regulator->volt_mask; + } else { + for (value = 0; value < regulator->val_tbl_sz; value++) + if (regulator->val_tbl[value] >= uV) + break; + + if (value >= regulator->val_tbl_sz) + value = regulator->val_tbl_sz; + + value <<= volt_shift; + value += regulator->bit_offset_from_cpcap_lowest_voltage; + } + + ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->volt_mask, + value); + if (ret) + return ret; + + if (regulator->volt_trans_time) + udelay(regulator->volt_trans_time); + + return 0; +} + +static int cpcap_regulator_get_enable(struct udevice *dev) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int value; + + value = pmic_reg_read(dev->parent, regulator->reg); + if (value < 0) + return value; + + return (value & regulator->mode_mask) ? 1 : 0; +} + +static int cpcap_regulator_set_enable(struct udevice *dev, bool enable) +{ + const struct cpcap_regulator_data *regulator = + &tegra20_regulators[dev->driver_data]; + int ret; + + if (enable) { + ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask, + regulator->mode_val); + if (ret) + return ret; + } + + if (regulator->mode_val & CPCAP_REG_OFF_MODE_SEC) { + ret = pmic_clrsetbits(dev->parent, regulator->assignment_reg, + regulator->assignment_mask, + enable ? 0 : regulator->assignment_mask); + if (ret) + return ret; + } + + if (!enable) { + ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask, + regulator->off_mode_val); + if (ret) + return ret; + } + + if (regulator->turn_on_time) + udelay(regulator->turn_on_time); + + return 0; +} + +static int cpcap_regulator_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = dev_get_uclass_plat(dev); + int id; + + for (id = 0; id < CPCAP_REGULATORS_COUNT; id++) + if (cpcap_regulator_to_name[id]) + if (!strcmp(dev->name, cpcap_regulator_to_name[id])) + break; + + switch (id) { + case CPCAP_SW1 ... CPCAP_SW6: + uc_pdata->type = REGULATOR_TYPE_BUCK; + break; + + case CPCAP_VCAM ... CPCAP_VAUDIO: + uc_pdata->type = REGULATOR_TYPE_LDO; + break; + + default: + log_err("CPCAP: Invalid regulator ID\n"); + return -ENODEV; + } + + dev->driver_data = id; + return 0; +} + +static const struct dm_regulator_ops cpcap_regulator_ops = { + .get_value = cpcap_regulator_get_value, + .set_value = cpcap_regulator_set_value, + .get_enable = cpcap_regulator_get_enable, + .set_enable = cpcap_regulator_set_enable, +}; + +U_BOOT_DRIVER(cpcap_sw) = { + .name = CPCAP_SW_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &cpcap_regulator_ops, + .probe = cpcap_regulator_probe, +}; + +U_BOOT_DRIVER(cpcap_ldo) = { + .name = CPCAP_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &cpcap_regulator_ops, + .probe = cpcap_regulator_probe, +}; diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index cd2b1a654c1..954deca5ed7 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -466,6 +466,25 @@ static const struct rpmh_vreg_hw_data pmic5_nldo515 = { .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), }; +static const struct rpmh_vreg_hw_data pmic5_ftsmps527 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + .n_voltages = 215, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_smps), +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo515_mv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1800000, 0, 187, 8000), + .n_voltages = 188, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), +}; + #define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ { \ .name = _name, \ @@ -558,6 +577,28 @@ static const struct rpmh_vreg_init_data pmc8380_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmm8654au_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps527, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps527, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps527, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps527, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps527, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps527, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps527, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps527, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps527, "vdd-s9"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l6-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo515_mv, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), + {} +}; + /* probe an individual regulator */ static int rpmh_regulator_probe(struct udevice *dev) { @@ -688,6 +729,10 @@ static const struct udevice_id rpmh_regulator_ids[] = { .compatible = "qcom,pmc8380-rpmh-regulators", .data = (ulong)pmc8380_vreg_data, }, + { + .compatible = "qcom,pmm8654au-rpmh-regulators", + .data = (ulong)pmm8654au_vreg_data, + }, { /* sentinal */ }, }; diff --git a/drivers/power/regulator/qcom_usb_vbus_regulator.c b/drivers/power/regulator/qcom_usb_vbus_regulator.c new file mode 100644 index 00000000000..2d58ef5e111 --- /dev/null +++ b/drivers/power/regulator/qcom_usb_vbus_regulator.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2025, Linaro Limited + */ +#define pr_fmt(fmt) "qcom_usb_vbus: " fmt + +#include <bitfield.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <log.h> +#include <asm/gpio.h> +#include <linux/bitops.h> +#include <linux/printk.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define CMD_OTG 0x50 +#define OTG_EN BIT(0) +// The 0 bit in this register's bit field is undocumented +#define OTG_CFG 0x56 +#define OTG_EN_SRC_CFG BIT(1) + +struct qcom_usb_vbus_priv { + phys_addr_t base; +}; + +static int qcom_usb_vbus_regulator_of_to_plat(struct udevice *dev) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static int qcom_usb_vbus_regulator_get_enable(struct udevice *dev) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + int otg_en_reg = priv->base + CMD_OTG; + int ret; + + ret = pmic_reg_read(dev->parent, otg_en_reg); + if (ret < 0) + log_err("failed to read usb vbus: %d\n", ret); + else + ret &= OTG_EN; + + return ret; +} + +static int qcom_usb_vbus_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + int otg_en_reg = priv->base + CMD_OTG; + int ret; + + if (enable) { + ret = pmic_clrsetbits(dev->parent, otg_en_reg, 0, OTG_EN); + if (ret < 0) { + log_err("error enabling: %d\n", ret); + return ret; + } + } else { + ret = pmic_clrsetbits(dev->parent, otg_en_reg, OTG_EN, 0); + if (ret < 0) { + log_err("error disabling: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int qcom_usb_vbus_regulator_probe(struct udevice *dev) +{ + struct qcom_usb_vbus_priv *priv = dev_get_priv(dev); + int otg_cfg_reg = priv->base + OTG_CFG; + int ret; + + /* Disable HW logic for VBUS enable */ + ret = pmic_clrsetbits(dev->parent, otg_cfg_reg, OTG_EN_SRC_CFG, 0); + if (ret < 0) { + log_err("error setting EN_SRC_CFG: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct dm_regulator_ops qcom_usb_vbus_regulator_ops = { + .get_enable = qcom_usb_vbus_regulator_get_enable, + .set_enable = qcom_usb_vbus_regulator_set_enable, +}; + +static const struct udevice_id qcom_usb_vbus_regulator_ids[] = { + { .compatible = "qcom,pm8150b-vbus-reg"}, + { }, +}; + +U_BOOT_DRIVER(qcom_usb_vbus_regulator) = { + .name = "qcom-usb-vbus-regulator", + .id = UCLASS_REGULATOR, + .of_match = qcom_usb_vbus_regulator_ids, + .of_to_plat = qcom_usb_vbus_regulator_of_to_plat, + .ops = &qcom_usb_vbus_regulator_ops, + .probe = qcom_usb_vbus_regulator_probe, + .priv_auto = sizeof(struct qcom_usb_vbus_priv), +}; diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index bd94ea771be..47bd57c7890 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -4,7 +4,7 @@ # Texas Instruments Incorporated - https://www.ti.com/ # -obj-$(CONFIG_$(XPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o +obj-$(CONFIG_$(PHASE_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o # Remote proc drivers - Please keep this list alphabetically sorted. obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index cd0b84c0622..7d022ce718a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -28,6 +28,10 @@ DEFINE_CACHE_ALIGN_BUFFER(u8, tempbuff, 512); /* temporary data buffer */ #define SCSI_MAX_BLK 0xFFFF #define SCSI_LBA48_READ 0xFFFFFFF +#define SCSI_UNMAP_PARAM_RESERVED 0 +#define SCSI_UNMAP_PARAM_LEN 22 +#define SCSI_UNMAP_PARAM_DATA_LEN 16 + static void scsi_print_error(struct scsi_cmd *pccb) { /* Dummy function that could print an error for debugging */ @@ -78,6 +82,23 @@ static void scsi_setup_inquiry(struct scsi_cmd *pccb) pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ } +static void scsi_setup_sync_cache(struct scsi_cmd *pccb, lbaint_t start, + unsigned short blocks) +{ + pccb->cmd[0] = SCSI_SYNC_CACHE; + pccb->cmd[1] = 0; + pccb->cmd[2] = (unsigned char)(start >> 24) & 0xff; + pccb->cmd[3] = (unsigned char)(start >> 16) & 0xff; + pccb->cmd[4] = (unsigned char)(start >> 8) & 0xff; + pccb->cmd[5] = (unsigned char)start & 0xff; + pccb->cmd[6] = 0; + pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff; + pccb->cmd[8] = (unsigned char)blocks & 0xff; + pccb->cmd[9] = 0; + pccb->cmdlen = 10; + pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ +} + static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, unsigned short blocks) { @@ -90,7 +111,7 @@ static void scsi_setup_read_ext(struct scsi_cmd *pccb, lbaint_t start, pccb->cmd[6] = 0; pccb->cmd[7] = (unsigned char)(blocks >> 8) & 0xff; pccb->cmd[8] = (unsigned char)blocks & 0xff; - pccb->cmd[6] = 0; + pccb->cmd[9] = 0; pccb->cmdlen = 10; pccb->msgout[0] = SCSI_IDENTIFY; /* NOT USED */ debug("scsi_setup_read_ext: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", @@ -121,6 +142,51 @@ static void scsi_setup_write_ext(struct scsi_cmd *pccb, lbaint_t start, pccb->cmd[7], pccb->cmd[8]); } +static void scsi_setup_erase_ext(struct scsi_cmd *pccb, lbaint_t start, + unsigned short blocks) +{ + u8 *param = tempbuff; + const u8 param_size = 24; + + memset(param, 0, param_size); + param[0] = SCSI_UNMAP_PARAM_RESERVED; + param[1] = SCSI_UNMAP_PARAM_LEN; + param[2] = SCSI_UNMAP_PARAM_RESERVED; + param[3] = SCSI_UNMAP_PARAM_DATA_LEN; + + param[8] = 0x0; + param[9] = 0x0; + param[10] = 0x0; + param[11] = 0x0; + param[12] = (start >> 24) & 0xff; + param[13] = (start >> 16) & 0xff; + param[14] = (start >> 8) & 0xff; + param[15] = (start) & 0xff; + param[16] = (blocks >> 24) & 0xff; + param[17] = (blocks >> 16) & 0xff; + param[18] = (blocks >> 8) & 0xff; + param[19] = (blocks) & 0xff; + + memset(pccb->cmd, 0, sizeof(pccb->cmd)); + pccb->cmd[0] = SCSI_UNMAP; + pccb->cmd[1] = 0; + pccb->cmd[6] = 0; + pccb->cmd[7] = 0; + pccb->cmd[8] = param_size; + pccb->cmd[9] = 0; + pccb->cmdlen = 10; + + pccb->pdata = param; + pccb->datalen = param_size; + pccb->dma_dir = DMA_TO_DEVICE; + + debug("%s: cmd: %02X %02X startblk %02X%02X%02X%02X blccnt %02X%02X\n", + __func__, + pccb->cmd[0], pccb->cmd[1], + pccb->cmd[2], pccb->cmd[3], pccb->cmd[4], pccb->cmd[5], + pccb->cmd[7], pccb->cmd[8]); +} + static ulong scsi_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *buffer) { @@ -240,11 +306,59 @@ static ulong scsi_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, } buf_addr += pccb->datalen; } while (blks != 0); + + /* Flush the SCSI cache so we don't lose data on board reset. */ + scsi_setup_sync_cache(pccb, 0, 0); + if (scsi_exec(bdev, pccb)) + scsi_print_error(pccb); + debug("%s: end startblk " LBAF ", blccnt %x buffer %lX\n", __func__, start, smallblks, buf_addr); return blkcnt; } +/******************************************************************************* + * scsi_erase + */ +static ulong scsi_erase(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt) +{ + struct blk_desc *block_dev = dev_get_uclass_plat(dev); + struct udevice *bdev = dev->parent; + struct scsi_plat *uc_plat = dev_get_uclass_plat(bdev); + lbaint_t start, blks, max_blks; + struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + + /* Setup device */ + pccb->target = block_dev->target; + pccb->lun = block_dev->lun; + start = blknr; + blks = blkcnt; + if (uc_plat->max_bytes_per_req) + max_blks = uc_plat->max_bytes_per_req / block_dev->blksz; + else + max_blks = SCSI_MAX_BLK; + + debug("\n%s: dev %d startblk " LBAF ", blccnt " LBAF "\n", + __func__, block_dev->devnum, start, blks); + do { + if (blks > max_blks) { + scsi_setup_erase_ext(pccb, start, max_blks); + start += max_blks; + blks -= max_blks; + } else { + scsi_setup_erase_ext(pccb, start, blks); + start += blks; + blks = 0; + } + if (scsi_exec(bdev, pccb)) { + scsi_print_error(pccb); + blkcnt -= blks; + break; + } + } while (blks != 0); + return blkcnt; +} + #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) static int scsi_buffer_aligned(struct udevice *dev, struct bounce_buffer *state) { @@ -592,6 +706,7 @@ int scsi_scan(bool verbose) static const struct blk_ops scsi_blk_ops = { .read = scsi_read, .write = scsi_write, + .erase = scsi_erase, #if IS_ENABLED(CONFIG_BOUNCE_BUFFER) .buffer_aligned = scsi_buffer_aligned, #endif /* CONFIG_BOUNCE_BUFFER */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 84130524c2d..589b526381f 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -519,6 +519,8 @@ config DEBUG_UART_BASE default 0x0 if DEBUG_UART_SANDBOX default 0xff000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQMP default 0xe0000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQ + default 0xff000000 if DEBUG_UART_PL011 && ARCH_VERSAL + default 0xf1920000 if DEBUG_UART_PL011 && (ARCH_VERSAL_NET || ARCH_VERSAL2) help This is the base address of your UART for memory-mapped UARTs. @@ -554,6 +556,7 @@ config DEBUG_UART_CLOCK default 0 if DEBUG_MVEBU_A3700_UART default 100000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQMP default 50000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQ + default 100000000 if DEBUG_UART_PL011 && (ARCH_VERSAL || ARCH_VERSAL_NET || ARCH_VERSAL2) help The UART input clock determines the speed of the internal UART circuitry. The baud rate is derived from this by dividing the input diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 2ef8ba20cf5..c32e3fcd439 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -11,7 +11,7 @@ endif obj-$(CONFIG_PL01X_SERIAL) += serial_pl01x.o obj-$(CONFIG_PL011_SERIAL) += serial_pl01x.o -obj-$(CONFIG_$(XPL_)SYS_NS16550_SERIAL) += serial_ns16550.o +obj-$(CONFIG_$(PHASE_)SYS_NS16550_SERIAL) += serial_ns16550.o obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f475f341c9c..a3513f0a3ef 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -59,6 +59,15 @@ config ADI_SPI3 Enable the ADI (Analog Devices) SPI controller driver. This driver enables the support for SC5XX spi controller. +config AIROHA_SNFI_SPI + bool "Airoha SPI memory controller driver" + depends on SPI_MEM + help + Enable the Airoha SPI memory controller driver. This driver is + originally based on the Airoha SNFI IP core. It can only be + used to access SPI memory devices like SPI-NOR or SPI-NAND on + platforms embedding this IP core, like AN7581. + config ALTERA_SPI bool "Altera SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 21895d46429..da91b18b6ed 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_MEM) += spi-mem-nodm.o endif obj-$(CONFIG_ADI_SPI3) += adi_spi3.o +obj-$(CONFIG_AIROHA_SNFI_SPI) += airoha_snfi_spi.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_APPLE_SPI) += apple_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o diff --git a/drivers/spi/airoha_snfi_spi.c b/drivers/spi/airoha_snfi_spi.c new file mode 100644 index 00000000000..3ea25b293d1 --- /dev/null +++ b/drivers/spi/airoha_snfi_spi.c @@ -0,0 +1,718 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 AIROHA Inc + * + * Based on spi-airoha-snfi.c on Linux + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> + * Author: Ray Liu <ray.liu@airoha.com> + */ + +#include <asm/unaligned.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <linux/bitfield.h> +#include <linux/dma-mapping.h> +#include <linux/mtd/spinand.h> +#include <linux/time.h> +#include <regmap.h> +#include <spi.h> +#include <spi-mem.h> + +/* SPI */ +#define REG_SPI_CTRL_READ_MODE 0x0000 +#define REG_SPI_CTRL_READ_IDLE_EN 0x0004 +#define REG_SPI_CTRL_SIDLY 0x0008 +#define REG_SPI_CTRL_CSHEXT 0x000c +#define REG_SPI_CTRL_CSLEXT 0x0010 + +#define REG_SPI_CTRL_MTX_MODE_TOG 0x0014 +#define SPI_CTRL_MTX_MODE_TOG GENMASK(3, 0) + +#define REG_SPI_CTRL_RDCTL_FSM 0x0018 +#define SPI_CTRL_RDCTL_FSM GENMASK(3, 0) + +#define REG_SPI_CTRL_MACMUX_SEL 0x001c + +#define REG_SPI_CTRL_MANUAL_EN 0x0020 +#define SPI_CTRL_MANUAL_EN BIT(0) + +#define REG_SPI_CTRL_OPFIFO_EMPTY 0x0024 +#define SPI_CTRL_OPFIFO_EMPTY BIT(0) + +#define REG_SPI_CTRL_OPFIFO_WDATA 0x0028 +#define SPI_CTRL_OPFIFO_LEN GENMASK(8, 0) +#define SPI_CTRL_OPFIFO_OP GENMASK(13, 9) + +#define REG_SPI_CTRL_OPFIFO_FULL 0x002c +#define SPI_CTRL_OPFIFO_FULL BIT(0) + +#define REG_SPI_CTRL_OPFIFO_WR 0x0030 +#define SPI_CTRL_OPFIFO_WR BIT(0) + +#define REG_SPI_CTRL_DFIFO_FULL 0x0034 +#define SPI_CTRL_DFIFO_FULL BIT(0) + +#define REG_SPI_CTRL_DFIFO_WDATA 0x0038 +#define SPI_CTRL_DFIFO_WDATA GENMASK(7, 0) + +#define REG_SPI_CTRL_DFIFO_EMPTY 0x003c +#define SPI_CTRL_DFIFO_EMPTY BIT(0) + +#define REG_SPI_CTRL_DFIFO_RD 0x0040 +#define SPI_CTRL_DFIFO_RD BIT(0) + +#define REG_SPI_CTRL_DFIFO_RDATA 0x0044 +#define SPI_CTRL_DFIFO_RDATA GENMASK(7, 0) + +#define REG_SPI_CTRL_DUMMY 0x0080 +#define SPI_CTRL_CTRL_DUMMY GENMASK(3, 0) + +#define REG_SPI_CTRL_PROBE_SEL 0x0088 +#define REG_SPI_CTRL_INTERRUPT 0x0090 +#define REG_SPI_CTRL_INTERRUPT_EN 0x0094 +#define REG_SPI_CTRL_SI_CK_SEL 0x009c +#define REG_SPI_CTRL_SW_CFGNANDADDR_VAL 0x010c +#define REG_SPI_CTRL_SW_CFGNANDADDR_EN 0x0110 +#define REG_SPI_CTRL_SFC_STRAP 0x0114 + +#define REG_SPI_CTRL_NFI2SPI_EN 0x0130 +#define SPI_CTRL_NFI2SPI_EN BIT(0) + +/* NFI2SPI */ +#define REG_SPI_NFI_CNFG 0x0000 +#define SPI_NFI_DMA_MODE BIT(0) +#define SPI_NFI_READ_MODE BIT(1) +#define SPI_NFI_DMA_BURST_EN BIT(2) +#define SPI_NFI_HW_ECC_EN BIT(8) +#define SPI_NFI_AUTO_FDM_EN BIT(9) +#define SPI_NFI_OPMODE GENMASK(14, 12) + +#define REG_SPI_NFI_PAGEFMT 0x0004 +#define SPI_NFI_PAGE_SIZE GENMASK(1, 0) +#define SPI_NFI_SPARE_SIZE GENMASK(5, 4) + +#define REG_SPI_NFI_CON 0x0008 +#define SPI_NFI_FIFO_FLUSH BIT(0) +#define SPI_NFI_RST BIT(1) +#define SPI_NFI_RD_TRIG BIT(8) +#define SPI_NFI_WR_TRIG BIT(9) +#define SPI_NFI_SEC_NUM GENMASK(15, 12) + +#define REG_SPI_NFI_INTR_EN 0x0010 +#define SPI_NFI_RD_DONE_EN BIT(0) +#define SPI_NFI_WR_DONE_EN BIT(1) +#define SPI_NFI_RST_DONE_EN BIT(2) +#define SPI_NFI_ERASE_DONE_EN BIT(3) +#define SPI_NFI_BUSY_RETURN_EN BIT(4) +#define SPI_NFI_ACCESS_LOCK_EN BIT(5) +#define SPI_NFI_AHB_DONE_EN BIT(6) +#define SPI_NFI_ALL_IRQ_EN \ + (SPI_NFI_RD_DONE_EN | SPI_NFI_WR_DONE_EN | \ + SPI_NFI_RST_DONE_EN | SPI_NFI_ERASE_DONE_EN | \ + SPI_NFI_BUSY_RETURN_EN | SPI_NFI_ACCESS_LOCK_EN | \ + SPI_NFI_AHB_DONE_EN) + +#define REG_SPI_NFI_INTR 0x0014 +#define SPI_NFI_AHB_DONE BIT(6) + +#define REG_SPI_NFI_CMD 0x0020 + +#define REG_SPI_NFI_ADDR_NOB 0x0030 +#define SPI_NFI_ROW_ADDR_NOB GENMASK(6, 4) + +#define REG_SPI_NFI_STA 0x0060 +#define REG_SPI_NFI_FIFOSTA 0x0064 +#define REG_SPI_NFI_STRADDR 0x0080 +#define REG_SPI_NFI_FDM0L 0x00a0 +#define REG_SPI_NFI_FDM0M 0x00a4 +#define REG_SPI_NFI_FDM7L 0x00d8 +#define REG_SPI_NFI_FDM7M 0x00dc +#define REG_SPI_NFI_FIFODATA0 0x0190 +#define REG_SPI_NFI_FIFODATA1 0x0194 +#define REG_SPI_NFI_FIFODATA2 0x0198 +#define REG_SPI_NFI_FIFODATA3 0x019c +#define REG_SPI_NFI_MASTERSTA 0x0224 + +#define REG_SPI_NFI_SECCUS_SIZE 0x022c +#define SPI_NFI_CUS_SEC_SIZE GENMASK(12, 0) +#define SPI_NFI_CUS_SEC_SIZE_EN BIT(16) + +#define REG_SPI_NFI_RD_CTL2 0x0510 +#define REG_SPI_NFI_RD_CTL3 0x0514 + +#define REG_SPI_NFI_PG_CTL1 0x0524 +#define SPI_NFI_PG_LOAD_CMD GENMASK(15, 8) + +#define REG_SPI_NFI_PG_CTL2 0x0528 +#define REG_SPI_NFI_NOR_PROG_ADDR 0x052c +#define REG_SPI_NFI_NOR_RD_ADDR 0x0534 + +#define REG_SPI_NFI_SNF_MISC_CTL 0x0538 +#define SPI_NFI_DATA_READ_WR_MODE GENMASK(18, 16) + +#define REG_SPI_NFI_SNF_MISC_CTL2 0x053c +#define SPI_NFI_READ_DATA_BYTE_NUM GENMASK(12, 0) +#define SPI_NFI_PROG_LOAD_BYTE_NUM GENMASK(28, 16) + +#define REG_SPI_NFI_SNF_STA_CTL1 0x0550 +#define SPI_NFI_READ_FROM_CACHE_DONE BIT(25) +#define SPI_NFI_LOAD_TO_CACHE_DONE BIT(26) + +#define REG_SPI_NFI_SNF_STA_CTL2 0x0554 + +#define REG_SPI_NFI_SNF_NFI_CNFG 0x055c +#define SPI_NFI_SPI_MODE BIT(0) + +/* SPI NAND Protocol OP */ +#define SPI_NAND_OP_GET_FEATURE 0x0f +#define SPI_NAND_OP_SET_FEATURE 0x1f +#define SPI_NAND_OP_PAGE_READ 0x13 +#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 +#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b +#define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b +#define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b +#define SPI_NAND_OP_WRITE_ENABLE 0x06 +#define SPI_NAND_OP_WRITE_DISABLE 0x04 +#define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 +#define SPI_NAND_OP_PROGRAM_LOAD_QUAD 0x32 +#define SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE 0x84 +#define SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD 0x34 +#define SPI_NAND_OP_PROGRAM_EXECUTE 0x10 +#define SPI_NAND_OP_READ_ID 0x9f +#define SPI_NAND_OP_BLOCK_ERASE 0xd8 +#define SPI_NAND_OP_RESET 0xff +#define SPI_NAND_OP_DIE_SELECT 0xc2 + +#define SPI_NAND_CACHE_SIZE (SZ_4K + SZ_256) +#define SPI_MAX_TRANSFER_SIZE 511 + +enum airoha_snand_mode { + SPI_MODE_AUTO, + SPI_MODE_MANUAL, + SPI_MODE_DMA, +}; + +enum airoha_snand_cs { + SPI_CHIP_SEL_HIGH, + SPI_CHIP_SEL_LOW, +}; + +struct airoha_snand_priv { + struct regmap *regmap_ctrl; + struct regmap *regmap_nfi; + struct clk *spi_clk; + + struct { + size_t page_size; + size_t sec_size; + u8 sec_num; + u8 spare_size; + } nfi_cfg; +}; + +static int airoha_snand_set_fifo_op(struct airoha_snand_priv *priv, + u8 op_cmd, int op_len) +{ + int err; + u32 val; + + err = regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WDATA, + FIELD_PREP(SPI_CTRL_OPFIFO_LEN, op_len) | + FIELD_PREP(SPI_CTRL_OPFIFO_OP, op_cmd)); + if (err) + return err; + + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_OPFIFO_FULL, + val, !(val & SPI_CTRL_OPFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WR, + SPI_CTRL_OPFIFO_WR); + if (err) + return err; + + return regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_OPFIFO_EMPTY, + val, (val & SPI_CTRL_OPFIFO_EMPTY), + 0, 250 * USEC_PER_MSEC); +} + +static int airoha_snand_set_cs(struct airoha_snand_priv *priv, u8 cs) +{ + return airoha_snand_set_fifo_op(priv, cs, sizeof(cs)); +} + +static int airoha_snand_write_data_to_fifo(struct airoha_snand_priv *priv, + const u8 *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + u32 val; + + /* 1. Wait until dfifo is not full */ + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_FULL, val, + !(val & SPI_CTRL_DFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + /* 2. Write data to register DFIFO_WDATA */ + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_WDATA, + FIELD_PREP(SPI_CTRL_DFIFO_WDATA, data[i])); + if (err) + return err; + + /* 3. Wait until dfifo is not full */ + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_FULL, val, + !(val & SPI_CTRL_DFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + } + + return 0; +} + +static int airoha_snand_read_data_from_fifo(struct airoha_snand_priv *priv, + u8 *ptr, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + u32 val; + + /* 1. wait until dfifo is not empty */ + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_EMPTY, val, + !(val & SPI_CTRL_DFIFO_EMPTY), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + /* 2. read from dfifo to register DFIFO_RDATA */ + err = regmap_read(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_RDATA, &val); + if (err) + return err; + + ptr[i] = FIELD_GET(SPI_CTRL_DFIFO_RDATA, val); + /* 3. enable register DFIFO_RD to read next byte */ + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_DFIFO_RD, SPI_CTRL_DFIFO_RD); + if (err) + return err; + } + + return 0; +} + +static int airoha_snand_set_mode(struct airoha_snand_priv *priv, + enum airoha_snand_mode mode) +{ + int err; + + switch (mode) { + case SPI_MODE_MANUAL: { + u32 val; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_NFI2SPI_EN, 0); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_READ_IDLE_EN, 0); + if (err) + return err; + + err = regmap_read_poll_timeout(priv->regmap_ctrl, + REG_SPI_CTRL_RDCTL_FSM, val, + !(val & SPI_CTRL_RDCTL_FSM), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MTX_MODE_TOG, 9); + if (err) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MANUAL_EN, SPI_CTRL_MANUAL_EN); + if (err) + return err; + break; + } + case SPI_MODE_DMA: + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_NFI2SPI_EN, + SPI_CTRL_MANUAL_EN); + if (err < 0) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MTX_MODE_TOG, 0x0); + if (err < 0) + return err; + + err = regmap_write(priv->regmap_ctrl, + REG_SPI_CTRL_MANUAL_EN, 0x0); + if (err < 0) + return err; + break; + case SPI_MODE_AUTO: + default: + break; + } + + return regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_DUMMY, 0); +} + +static int airoha_snand_write_data(struct airoha_snand_priv *priv, u8 cmd, + const u8 *data, int len) +{ + int i, data_len; + + for (i = 0; i < len; i += data_len) { + int err; + + data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); + err = airoha_snand_set_fifo_op(priv, cmd, data_len); + if (err) + return err; + + err = airoha_snand_write_data_to_fifo(priv, &data[i], + data_len); + if (err < 0) + return err; + } + + return 0; +} + +static int airoha_snand_read_data(struct airoha_snand_priv *priv, u8 *data, + int len) +{ + int i, data_len; + + for (i = 0; i < len; i += data_len) { + int err; + + data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); + err = airoha_snand_set_fifo_op(priv, 0xc, data_len); + if (err) + return err; + + err = airoha_snand_read_data_from_fifo(priv, &data[i], + data_len); + if (err < 0) + return err; + } + + return 0; +} + +static int airoha_snand_nfi_init(struct airoha_snand_priv *priv) +{ + int err; + + /* switch to SNFI mode */ + err = regmap_write(priv->regmap_nfi, REG_SPI_NFI_SNF_NFI_CNFG, + SPI_NFI_SPI_MODE); + if (err) + return err; + + /* Enable DMA */ + return regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_INTR_EN, + SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN); +} + +static int airoha_snand_nfi_config(struct airoha_snand_priv *priv) +{ + int err; + u32 val; + + err = regmap_write(priv->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); + if (err) + return err; + + /* auto FDM */ + err = regmap_clear_bits(priv->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_AUTO_FDM_EN); + if (err) + return err; + + /* HW ECC */ + err = regmap_clear_bits(priv->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_HW_ECC_EN); + if (err) + return err; + + /* DMA Burst */ + err = regmap_set_bits(priv->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_BURST_EN); + if (err) + return err; + + /* page format */ + switch (priv->nfi_cfg.spare_size) { + case 26: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1); + break; + case 27: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2); + break; + case 28: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3); + break; + default: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0); + break; + } + + err = regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_PAGEFMT, + SPI_NFI_SPARE_SIZE, val); + if (err) + return err; + + switch (priv->nfi_cfg.page_size) { + case 2048: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1); + break; + case 4096: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2); + break; + default: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0); + break; + } + + err = regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_PAGEFMT, + SPI_NFI_PAGE_SIZE, val); + if (err) + return err; + + /* sec num */ + val = FIELD_PREP(SPI_NFI_SEC_NUM, priv->nfi_cfg.sec_num); + err = regmap_update_bits(priv->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_SEC_NUM, val); + if (err) + return err; + + /* enable cust sec size */ + err = regmap_set_bits(priv->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + return err; + + /* set cust sec size */ + val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, priv->nfi_cfg.sec_size); + return regmap_update_bits(priv->regmap_nfi, + REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE, val); +} + +static int airoha_snand_adjust_op_size(struct spi_slave *slave, + struct spi_mem_op *op) +{ + size_t max_len; + + max_len = 1 + op->addr.nbytes + op->dummy.nbytes; + if (max_len >= 160) + return -EOPNOTSUPP; + + if (op->data.nbytes > 160 - max_len) + op->data.nbytes = 160 - max_len; + + return 0; +} + +static bool airoha_snand_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(slave, op)) + return false; + + if (op->cmd.buswidth != 1) + return false; + + return (!op->addr.nbytes || op->addr.buswidth == 1) && + (!op->dummy.nbytes || op->dummy.buswidth == 1) && + (!op->data.nbytes || op->data.buswidth == 1); +} + +static int airoha_snand_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + u8 data[8], cmd, opcode = op->cmd.opcode; + struct udevice *bus = slave->dev->parent; + struct airoha_snand_priv *priv; + int i, err; + + priv = dev_get_priv(bus); + + /* switch to manual mode */ + err = airoha_snand_set_mode(priv, SPI_MODE_MANUAL); + if (err < 0) + return err; + + err = airoha_snand_set_cs(priv, SPI_CHIP_SEL_LOW); + if (err < 0) + return err; + + /* opcode */ + err = airoha_snand_write_data(priv, 0x8, &opcode, sizeof(opcode)); + if (err) + return err; + + /* addr part */ + cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; + put_unaligned_be64(op->addr.val, data); + + for (i = ARRAY_SIZE(data) - op->addr.nbytes; + i < ARRAY_SIZE(data); i++) { + err = airoha_snand_write_data(priv, cmd, &data[i], + sizeof(data[0])); + if (err) + return err; + } + + /* dummy */ + data[0] = 0xff; + for (i = 0; i < op->dummy.nbytes; i++) { + err = airoha_snand_write_data(priv, 0x8, &data[0], + sizeof(data[0])); + if (err) + return err; + } + + /* data */ + if (op->data.dir == SPI_MEM_DATA_IN) { + err = airoha_snand_read_data(priv, op->data.buf.in, + op->data.nbytes); + if (err) + return err; + } else { + err = airoha_snand_write_data(priv, 0x8, op->data.buf.out, + op->data.nbytes); + if (err) + return err; + } + + return airoha_snand_set_cs(priv, SPI_CHIP_SEL_HIGH); +} + +static int airoha_snand_probe(struct udevice *dev) +{ + struct airoha_snand_priv *priv = dev_get_priv(dev); + int ret; + + ret = regmap_init_mem_index(dev_ofnode(dev), &priv->regmap_ctrl, 0); + if (ret) { + dev_err(dev, "failed to init spi ctrl regmap\n"); + return ret; + } + + ret = regmap_init_mem_index(dev_ofnode(dev), &priv->regmap_nfi, 1); + if (ret) { + dev_err(dev, "failed to init spi nfi regmap\n"); + return ret; + } + + priv->spi_clk = devm_clk_get(dev, "spi"); + if (IS_ERR(priv->spi_clk)) { + dev_err(dev, "unable to get spi clk\n"); + return PTR_ERR(priv->regmap_ctrl); + } + clk_enable(priv->spi_clk); + + return airoha_snand_nfi_init(priv); +} + +static int airoha_snand_nfi_set_speed(struct udevice *bus, uint speed) +{ + struct airoha_snand_priv *priv = dev_get_priv(bus); + int ret; + + ret = clk_set_rate(priv->spi_clk, speed); + if (ret < 0) + return ret; + + return 0; +} + +static int airoha_snand_nfi_set_mode(struct udevice *bus, uint mode) +{ + return 0; +} + +static int airoha_snand_nfi_setup(struct spi_slave *slave, + const struct spinand_info *spinand_info) +{ + struct udevice *bus = slave->dev->parent; + struct airoha_snand_priv *priv; + u32 sec_size, sec_num; + int pagesize, oobsize; + + priv = dev_get_priv(bus); + + pagesize = spinand_info->memorg.pagesize; + oobsize = spinand_info->memorg.oobsize; + + if (pagesize == 2 * 1024) + sec_num = 4; + else if (pagesize == 4 * 1024) + sec_num = 8; + else + sec_num = 1; + + sec_size = (pagesize + oobsize) / sec_num; + + /* init default value */ + priv->nfi_cfg.sec_size = sec_size; + priv->nfi_cfg.sec_num = sec_num; + priv->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024); + priv->nfi_cfg.spare_size = 16; + + return airoha_snand_nfi_config(priv); +} + +static const struct spi_controller_mem_ops airoha_snand_mem_ops = { + .adjust_op_size = airoha_snand_adjust_op_size, + .supports_op = airoha_snand_supports_op, + .exec_op = airoha_snand_exec_op, +}; + +static const struct dm_spi_ops airoha_snfi_spi_ops = { + .mem_ops = &airoha_snand_mem_ops, + .set_speed = airoha_snand_nfi_set_speed, + .set_mode = airoha_snand_nfi_set_mode, + .setup_for_spinand = airoha_snand_nfi_setup, +}; + +static const struct udevice_id airoha_snand_ids[] = { + { .compatible = "airoha,en7581-snand" }, + { } +}; + +U_BOOT_DRIVER(airoha_snfi_spi) = { + .name = "airoha-snfi-spi", + .id = UCLASS_SPI, + .of_match = airoha_snand_ids, + .ops = &airoha_snfi_spi_ops, + .priv_auto = sizeof(struct airoha_snand_priv), + .probe = airoha_snand_probe, +}; diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c index 816916de16d..fbeb0c6a85c 100644 --- a/drivers/spi/cadence_ospi_versal.c +++ b/drivers/spi/cadence_ospi_versal.c @@ -204,3 +204,22 @@ void cadence_qspi_apb_enable_linear_mode(bool enable) ~VERSAL_OSPI_LINEAR_MODE, VERSAL_AXI_MUX_SEL); } } + +int cadence_device_reset(struct udevice *bus) +{ + struct cadence_spi_priv *priv = dev_get_priv(bus); + u32 reg; + + reg = readl(priv->regbase + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK; + writel(reg, priv->regbase + CQSPI_REG_CONFIG); + + writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, priv->regbase + CQSPI_REG_CONFIG); + udelay(5); + writel(reg | CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, priv->regbase + CQSPI_REG_CONFIG); + udelay(150); + writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, priv->regbase + CQSPI_REG_CONFIG); + udelay(1200); + + return 0; +} diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 623904ecdad..a78c00db4ff 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -33,6 +33,11 @@ __weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, return 0; } +__weak int cadence_device_reset(struct udevice *dev) +{ + return 0; +} + __weak int cadence_qspi_flash_reset(struct udevice *dev) { return 0; @@ -251,6 +256,9 @@ static int cadence_spi_probe(struct udevice *bus) priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz); + if (device_is_compatible(bus, "amd,versal2-ospi")) + return cadence_device_reset(bus); + /* Reset ospi flash device */ return cadence_qspi_flash_reset(bus); @@ -452,6 +460,7 @@ static const struct dm_spi_ops cadence_spi_ops = { static const struct udevice_id cadence_spi_ids[] = { { .compatible = "cdns,qspi-nor" }, { .compatible = "ti,am654-ospi" }, + { .compatible = "amd,versal2-ospi" }, { } }; diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 1f9125cd239..731b6527cf3 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -45,6 +45,8 @@ #define CQSPI_REG_CONFIG_CLK_POL BIT(1) #define CQSPI_REG_CONFIG_CLK_PHA BIT(2) #define CQSPI_REG_CONFIG_PHY_ENABLE_MASK BIT(3) +#define CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK BIT(5) +#define CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK BIT(6) #define CQSPI_REG_CONFIG_DIRECT BIT(7) #define CQSPI_REG_CONFIG_DECODE BIT(9) #define CQSPI_REG_CONFIG_ENBL_DMA BIT(15) @@ -310,5 +312,6 @@ int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg); int cadence_qspi_flash_reset(struct udevice *dev); ofnode cadence_qspi_get_subnode(struct udevice *dev); void cadence_qspi_apb_enable_linear_mode(bool enable); +int cadence_device_reset(struct udevice *dev); #endif /* __CADENCE_QSPI_H__ */ diff --git a/drivers/spi/tegra20_slink.c b/drivers/spi/tegra20_slink.c index d54a5049205..097d9164175 100644 --- a/drivers/spi/tegra20_slink.c +++ b/drivers/spi/tegra20_slink.c @@ -29,7 +29,10 @@ DECLARE_GLOBAL_DATA_PTR; #define SLINK_CMD_IDLE_SCLK_PULL_LOW (2 << 24) #define SLINK_CMD_IDLE_SCLK_PULL_HIGH (3 << 24) #define SLINK_CMD_IDLE_SCLK_MASK (3 << 24) +#define SLINK_CMD_CS_POL3 BIT(23) +#define SLINK_CMD_CS_POL2 BIT(22) #define SLINK_CMD_CK_SDA BIT(21) +#define SLINK_CMD_CS_POL1 BIT(20) #define SLINK_CMD_CS_POL BIT(13) #define SLINK_CMD_CS_VAL BIT(12) #define SLINK_CMD_CS_SOFT BIT(11) @@ -64,6 +67,13 @@ DECLARE_GLOBAL_DATA_PTR; #define SPI_TIMEOUT 1000 #define TEGRA_SPI_MAX_FREQ 52000000 +unsigned int cmd_cs_pol_bit[] = { + SLINK_CMD_CS_POL, + SLINK_CMD_CS_POL1, + SLINK_CMD_CS_POL2, + SLINK_CMD_CS_POL3, +}; + struct spi_regs { u32 command; /* SLINK_COMMAND_0 register */ u32 command2; /* SLINK_COMMAND2_0 reg */ @@ -155,6 +165,14 @@ static int tegra30_spi_claim_bus(struct udevice *dev) writel(reg, ®s->status); debug("%s: STATUS = %08x\n", __func__, readl(®s->status)); + /* Update the polarity bits */ + if (priv->mode & SPI_CS_HIGH) + setbits_le32(&priv->regs->command, + cmd_cs_pol_bit[spi_chip_select(dev)]); + else + clrbits_le32(&priv->regs->command, + cmd_cs_pol_bit[spi_chip_select(dev)]); + /* Set master mode and sw controlled CS */ reg = readl(®s->command); reg |= SLINK_CMD_M_S | SLINK_CMD_CS_SOFT; diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c index 5cc5a9e654c..faae54e9fef 100644 --- a/drivers/spmi/spmi-msm.c +++ b/drivers/spmi/spmi-msm.c @@ -24,6 +24,9 @@ DECLARE_GLOBAL_DATA_PTR; #define PMIC_ARB_VERSION_V5_MIN 0x50000000 #define PMIC_ARB_VERSION_V7_MIN 0x70000000 +#define PMIC_ARB_FEATURES 0x0004 +#define PMIC_ARB_FEATURES_PERIPH_MASK GENMASK(10, 0) + #define APID_MAP_OFFSET_V1_V2_V3 (0x800) #define APID_MAP_OFFSET_V5 (0x900) #define APID_MAP_OFFSET_V7 (0x2000) @@ -60,6 +63,7 @@ DECLARE_GLOBAL_DATA_PTR; #define SPMI_MAX_PERIPH 256 #define SPMI_CHANNEL_READ_ONLY BIT(31) +#define SPMI_CHANNEL_VALID BIT(30) #define SPMI_CHANNEL_MASK 0xffff enum arb_ver { @@ -114,6 +118,8 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, return -EIO; if (pid >= SPMI_MAX_PERIPH) return -EIO; + if (!(priv->channel_map[usid][pid] & SPMI_CHANNEL_VALID)) + return -EINVAL; if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY) return -EPERM; @@ -183,6 +189,8 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) return -EIO; if (pid >= SPMI_MAX_PERIPH) return -EIO; + if (!(priv->channel_map[usid][pid] & SPMI_CHANNEL_VALID)) + return -EINVAL; channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK; @@ -246,6 +254,32 @@ static struct dm_spmi_ops msm_spmi_ops = { .write = msm_spmi_write, }; +/* + * In order to allow multiple EEs to write to a single PPID in arbiter + * version 5 and 7, there is more than one APID mapped to each PPID. + * The owner field for each of these mappings specifies the EE which is + * allowed to write to the APID. + */ +static void msm_spmi_channel_map_v5(struct msm_spmi_priv *priv, unsigned int i, + uint8_t slave_id, uint8_t pid) +{ + /* Mark channels read-only when from different owner */ + uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i)); + uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg); + bool prev_valid = priv->channel_map[slave_id][pid] & SPMI_CHANNEL_VALID; + uint32_t prev_read_only = priv->channel_map[slave_id][pid] & SPMI_CHANNEL_READ_ONLY; + + if (!prev_valid) { + /* First PPID mapping */ + priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID; + if (owner != priv->owner) + priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY; + } else if ((owner == priv->owner) && prev_read_only) { + /* Read only and we found one we own, switch */ + priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID; + } +} + static int msm_spmi_probe(struct udevice *dev) { struct msm_spmi_priv *priv = dev_get_priv(dev); @@ -271,13 +305,17 @@ static int msm_spmi_probe(struct udevice *dev) } else if (hw_ver < PMIC_ARB_VERSION_V7_MIN) { priv->arb_ver = V5; priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5; - priv->max_channels = SPMI_MAX_CHANNELS_V5; + priv->max_channels = min_t(u32, readl(core_addr + PMIC_ARB_FEATURES) & + PMIC_ARB_FEATURES_PERIPH_MASK, + SPMI_MAX_CHANNELS_V5); priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg"); } else { /* TOFIX: handle second bus */ priv->arb_ver = V7; priv->arb_chnl = core_addr + APID_MAP_OFFSET_V7; - priv->max_channels = SPMI_MAX_CHANNELS_V7; + priv->max_channels = min_t(u32, readl(core_addr + PMIC_ARB_FEATURES) & + PMIC_ARB_FEATURES_PERIPH_MASK, + SPMI_MAX_CHANNELS_V7); priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg"); } @@ -297,15 +335,16 @@ static int msm_spmi_probe(struct udevice *dev) uint8_t slave_id = (periph & 0xf0000) >> 16; uint8_t pid = (periph & 0xff00) >> 8; - priv->channel_map[slave_id][pid] = i; - - /* Mark channels read-only when from different owner */ - if (priv->arb_ver == V5 || priv->arb_ver == V7) { - uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i)); - uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg); + switch (priv->arb_ver) { + case V2: + case V3: + priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID; + break; - if (owner != priv->owner) - priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY; + case V5: + case V7: + msm_spmi_channel_map_v5(priv, i, slave_id, pid); + break; } } return 0; diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 475540ffac7..4972905482a 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -71,6 +71,27 @@ config POWEROFF_GPIO Support for system poweroff using a GPIO pin. This can be used for systems having a single GPIO to trigger a system poweroff. +config SPL_POWEROFF_GPIO + bool "Enable support for GPIO poweroff driver in SPL" + depends on DM_GPIO && SPL + help + Support for system poweroff using a GPIO pin in SPL. This can be used + for systems having a single GPIO to trigger a system poweroff. + +config TPL_POWEROFF_GPIO + bool "Enable support for GPIO poweroff driver in TPL" + depends on DM_GPIO && TPL + help + Support for system poweroff using a GPIO pin in TPL. This can be used + for systems having a single GPIO to trigger a system poweroff. + +config VPL_POWEROFF_GPIO + bool "Enable support for GPIO poweroff driver in VPL" + depends on DM_GPIO && VPL + help + Support for system poweroff using a GPIO pin in VPL. This can be used + for systems having a single GPIO to trigger a system poweroff. + config SYSRESET_GPIO bool "Enable support for GPIO reset driver" depends on DM_GPIO @@ -79,6 +100,30 @@ config SYSRESET_GPIO example on Microblaze where reset logic can be controlled via GPIO pin which triggers cpu reset. +config SPL_SYSRESET_GPIO + bool "Enable support for GPIO reset driver in SPL" + depends on DM_GPIO && SPL + help + Reset support via GPIO pin connected reset logic in SPL. This is used + for example on Microblaze where reset logic can be controlled via + GPIO pin which triggers cpu reset. + +config TPL_SYSRESET_GPIO + bool "Enable support for GPIO reset driver in TPL" + depends on DM_GPIO && TPL + help + Reset support via GPIO pin connected reset logic in TPL. This is used + for example on Microblaze where reset logic can be controlled via + GPIO pin which triggers cpu reset. + +config VPL_SYSRESET_GPIO + bool "Enable support for GPIO reset driver in VPL" + depends on DM_GPIO && VPL + help + Reset support via GPIO pin connected reset logic in VPL. This is used + for example on Microblaze where reset logic can be controlled via + GPIO pin which triggers cpu reset. + config SYSRESET_MAX77663 bool "Enable support for MAX77663 PMIC System Reset" depends on DM_PMIC_MAX77663 diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index 796fc9effa5..ded91a4d325 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -8,8 +8,8 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o obj-$(CONFIG_ARCH_STI) += sysreset_sti.o obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o obj-$(CONFIG_SYSRESET_CV1800B) += sysreset_cv1800b.o -obj-$(CONFIG_POWEROFF_GPIO) += poweroff_gpio.o -obj-$(CONFIG_SYSRESET_GPIO) += sysreset_gpio.o +obj-$(CONFIG_$(PHASE_)POWEROFF_GPIO) += poweroff_gpio.o +obj-$(CONFIG_$(PHASE_)SYSRESET_GPIO) += sysreset_gpio.o obj-$(CONFIG_$(PHASE_)SYSRESET_MAX77663) += sysreset_max77663.o obj-$(CONFIG_SYSRESET_MPC83XX) += sysreset_mpc83xx.o obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index 7a847e8388b..21db0d317fe 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -5,14 +5,14 @@ obj-y += timer-uclass.o obj-$(CONFIG_ADI_SC5XX_TIMER) += adi_sc5xx_timer.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o -obj-$(CONFIG_$(XPL_)ANDES_PLMT_TIMER) += andes_plmt_timer.o +obj-$(CONFIG_$(PHASE_)ANDES_PLMT_TIMER) += andes_plmt_timer.o obj-$(CONFIG_ARC_TIMER) += arc_timer.o obj-$(CONFIG_ARM_TWD_TIMER) += arm_twd_timer.o obj-$(CONFIG_AST_TIMER) += ast_timer.o obj-$(CONFIG_AST_IBEX_TIMER) += ast_ibex_timer.o obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o -obj-$(CONFIG_$(XPL_)ATMEL_PIT_TIMER) += atmel_pit_timer.o -obj-$(CONFIG_$(XPL_)ATMEL_TCB_TIMER) += atmel_tcb_timer.o +obj-$(CONFIG_$(PHASE_)ATMEL_PIT_TIMER) += atmel_pit_timer.o +obj-$(CONFIG_$(PHASE_)ATMEL_TCB_TIMER) += atmel_tcb_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o obj-$(CONFIG_FTTMR010_TIMER) += fttmr010_timer.o @@ -27,7 +27,7 @@ obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o obj-$(CONFIG_SP804_TIMER) += sp804_timer.o -obj-$(CONFIG_$(XPL_)RISCV_ACLINT) += riscv_aclint_timer.o +obj-$(CONFIG_$(PHASE_)RISCV_ACLINT) += riscv_aclint_timer.o obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_STM32_TIMER) += stm32_timer.o obj-$(CONFIG_TEGRA_TIMER) += tegra-timer.o diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index 08ec179346e..5b2d5ccb146 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -737,9 +737,13 @@ static int cr50_i2c_report_state(struct udevice *dev, char *str, int str_max) static int cr50_i2c_open(struct udevice *dev) { + struct cr50_priv *priv = dev_get_priv(dev); char buf[80]; int ret; + if (priv->locality != -1) + return -EBUSY; + ret = process_reset(dev); if (ret) return log_msg_ret("reset", ret); diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c index bfd844e4193..1c5ed538370 100644 --- a/drivers/ufs/ufs-amd-versal2.c +++ b/drivers/ufs/ufs-amd-versal2.c @@ -19,8 +19,6 @@ #include "ufshcd-dwc.h" #include "ufshci-dwc.h" -#define VERSAL2_UFS_DEVICE_ID 4 - #define SRAM_CSR_INIT_DONE_MASK BIT(0) #define SRAM_CSR_EXT_LD_DONE_MASK BIT(1) #define SRAM_CSR_BYPASS_MASK BIT(2) @@ -32,19 +30,12 @@ #define TIMEOUT_MICROSEC 1000000L -#define IOCTL_UFS_TXRX_CFGRDY_GET 40 -#define IOCTL_UFS_SRAM_CSR_SEL 41 - -#define PM_UFS_SRAM_CSR_WRITE 0 -#define PM_UFS_SRAM_CSR_READ 1 - struct ufs_versal2_priv { struct ufs_hba *hba; struct reset_ctl *rstc; struct reset_ctl *rstphy; u32 phy_mode; u32 host_clk; - u32 pd_dev_id; u8 attcompval0; u8 attcompval1; u8 ctlecompval0; @@ -102,41 +93,6 @@ static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val) return 0; } -int versal2_pm_ufs_get_txrx_cfgrdy(u32 node_id, u32 *value) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - int ret; - - if (!value) - return -EINVAL; - - ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_TXRX_CFGRDY_GET, - 0, 0, ret_payload); - *value = ret_payload[1]; - - return ret; -} - -int versal2_pm_ufs_sram_csr_sel(u32 node_id, u32 type, u32 *value) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - int ret; - - if (!value) - return -EINVAL; - - if (type == PM_UFS_SRAM_CSR_READ) { - ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_SRAM_CSR_SEL, - type, 0, ret_payload); - *value = ret_payload[1]; - } else { - ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_SRAM_CSR_SEL, - type, *value, 0); - } - - return ret; -} - static int ufs_versal2_enable_phy(struct ufs_hba *hba) { u32 offset, reg; @@ -281,7 +237,7 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) time_left = TIMEOUT_MICROSEC; do { time_left--; - ret = versal2_pm_ufs_get_txrx_cfgrdy(priv->pd_dev_id, ®); + ret = zynqmp_pm_ufs_get_txrx_cfgrdy(®); if (ret) return ret; @@ -312,8 +268,7 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) time_left = TIMEOUT_MICROSEC; do { time_left--; - ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, - PM_UFS_SRAM_CSR_READ, ®); + ret = zynqmp_pm_ufs_sram_csr_read(®); if (ret) return ret; @@ -341,10 +296,10 @@ static int ufs_versal2_init(struct ufs_hba *hba) struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); struct clk clk; unsigned long core_clk_rate = 0; + u32 cal; int ret = 0; priv->phy_mode = UFSHCD_DWC_PHY_MODE_ROM; - priv->pd_dev_id = VERSAL2_UFS_DEVICE_ID; ret = clk_get_by_name(hba->dev, "core_clk", &clk); if (ret) { @@ -371,6 +326,15 @@ static int ufs_versal2_init(struct ufs_hba *hba) return PTR_ERR(priv->rstphy); } + ret = zynqmp_pm_ufs_cal_reg(&cal); + if (ret) + return ret; + + priv->attcompval0 = (u8)cal; + priv->attcompval1 = (u8)(cal >> 8); + priv->ctlecompval0 = (u8)(cal >> 16); + priv->ctlecompval1 = (u8)(cal >> 24); + return ret; } @@ -397,8 +361,7 @@ static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba, return ret; } - ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, - PM_UFS_SRAM_CSR_READ, &sram_csr); + ret = zynqmp_pm_ufs_sram_csr_read(&sram_csr); if (ret) return ret; @@ -410,8 +373,7 @@ static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba, return -EINVAL; } - ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, - PM_UFS_SRAM_CSR_WRITE, &sram_csr); + ret = zynqmp_pm_ufs_sram_csr_write(&sram_csr); if (ret) return ret; diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 1f00f23f704..dea5316599b 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -4,9 +4,9 @@ cdns3-y := core.o drd.o obj-$(CONFIG_USB_CDNS3) += cdns3.o -cdns3-$(CONFIG_$(XPL_)USB_CDNS3_GADGET) += gadget.o ep0.o +cdns3-$(CONFIG_$(PHASE_)USB_CDNS3_GADGET) += gadget.o ep0.o -cdns3-$(CONFIG_$(XPL_)USB_CDNS3_HOST) += host.o +cdns3-$(CONFIG_$(PHASE_)USB_CDNS3_HOST) += host.o obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 11cc4657a0f..4c597c166c6 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2016 Freescale Semiconductor, Inc. # -obj-$(CONFIG_$(XPL_)DM_USB) += common.o +obj-$(CONFIG_$(PHASE_)DM_USB) += common.o obj-$(CONFIG_USB_ISP1760) += usb_urb.o obj-$(CONFIG_USB_MUSB_HOST) += usb_urb.o obj-$(CONFIG_USB_MUSB_GADGET) += usb_urb.o diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 985206eafe4..a619cd374fb 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -6,11 +6,11 @@ dwc3-y := core.o obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o -obj-$(CONFIG_$(XPL_)USB_DWC3_AM62) += dwc3-am62.o +obj-$(CONFIG_$(PHASE_)USB_DWC3_AM62) += dwc3-am62.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o -obj-$(CONFIG_$(XPL_)USB_DWC3_GENERIC) += dwc3-generic.o +obj-$(CONFIG_$(PHASE_)USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_LAYERSCAPE) += dwc3-layerscape.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 477ecd02098..2b01113d54c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1635,7 +1635,7 @@ usb_ep *dwc3_gadget_match_ep(struct usb_gadget *gadget, /* * Special workaround for NXP UUU tool in SPL. * - * The tool excepts the interrupt-in endpoint to be ep1in, + * The tool expects the interrupt-in endpoint to be ep1in, * otherwise it crashes. This is a result of the previous * hard-coded EP setup in drivers/usb/gadget/epautoconf.c * which did special-case EP allocation for SPL builds, diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index a77037a7094..f9326f0a7e7 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1443,6 +1443,7 @@ static const struct udevice_id usba_udc_ids[] = { { .compatible = "atmel,at91sam9rl-udc" }, { .compatible = "atmel,at91sam9g45-udc" }, { .compatible = "atmel,sama5d3-udc" }, + { .compatible = "microchip,sam9x60-udc" }, {} }; diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index d3fc4acb401..71dc58da3f0 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -284,7 +284,6 @@ static const char fsg_string_interface[] = "Mass Storage"; #define kthread_create(...) __builtin_return_address(0) #define wait_for_completion(...) do {} while (0) -struct kref {int x; }; struct completion {int x; }; struct fsg_dev; @@ -345,8 +344,6 @@ struct fsg_common { /* Vendor (8 chars), product (16 chars), release (4 * hexadecimal digits) and NUL byte */ char inquiry_string[8 + 16 + 4 + 1]; - - struct kref ref; }; struct fsg_config { @@ -2436,7 +2433,7 @@ int fsg_main_thread(void *common_) return 0; } -static void fsg_common_release(struct kref *ref); +static void fsg_common_release(struct fsg_common *common); static struct fsg_common *fsg_common_init(struct fsg_common *common, struct usb_composite_dev *cdev) @@ -2548,16 +2545,12 @@ error_luns: common->nluns = i + 1; error_release: common->state = FSG_STATE_TERMINATED; /* The thread is dead */ - /* Call fsg_common_release() directly, ref might be not - * initialised */ - fsg_common_release(&common->ref); + fsg_common_release(common); return ERR_PTR(rc); } -static void fsg_common_release(struct kref *ref) +static void fsg_common_release(struct fsg_common *common) { - struct fsg_common *common = container_of(ref, struct fsg_common, ref); - /* If the thread isn't already dead, tell it to exit now */ if (common->state != FSG_STATE_TERMINATED) { raise_exception(common, FSG_STATE_EXIT); @@ -2571,8 +2564,6 @@ static void fsg_common_release(struct kref *ref) /* In error recovery common->nluns may be zero. */ for (; i; --i, ++lun) fsg_lun_close(lun); - - kfree(common->luns); } { @@ -2648,6 +2639,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); } + fsg_common_release(fsg->common); free(fsg->function.descriptors); free(fsg->function.hs_descriptors); kfree(fsg); @@ -2751,6 +2743,8 @@ int fsg_add(struct usb_configuration *c) struct fsg_common *fsg_common; fsg_common = fsg_common_init(NULL, c->cdev); + if (IS_ERR(fsg_common)) + return PTR_ERR(fsg_common); fsg_common->vendor_name = 0; fsg_common->product_name = 0; diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 4b6a8fdfeee..56b9b123fa1 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -2,9 +2,9 @@ # # USB peripheral controller drivers -ifndef CONFIG_$(XPL_)DM_USB_GADGET +ifndef CONFIG_$(PHASE_)DM_USB_GADGET obj-$(CONFIG_USB_DWC3_GADGET) += udc-core.o endif -obj-$(CONFIG_$(XPL_)DM_USB_GADGET) += udc-core.o +obj-$(CONFIG_$(PHASE_)DM_USB_GADGET) += udc-core.o obj-y += udc-uclass.o diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 902d68d0378..ef4ce62a680 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -ifdef CONFIG_$(XPL_)DM_USB +ifdef CONFIG_$(PHASE_)DM_USB obj-y += usb-uclass.o obj-$(CONFIG_SANDBOX) += usb-sandbox.o endif diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index df607303616..73353944971 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -529,6 +529,16 @@ config VIDEO_LCD_HIMAX_HX8394 Say Y here if you want to enable support for Himax HX8394 dsi 4dl panel. +config VIDEO_LCD_MOT + tristate "Atrix 4G and Droid X2 540x960 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for the LCD panel module for + Motorola Atrix 4G or Droid X2. Exact panel vendor and model are + unknown. The panel has a 540x960 resolution and uses 24 bit RGB per + pixel. + config VIDEO_LCD_ORISETECH_OTM8009A bool "OTM8009A DSI LCD panel support" select VIDEO_MIPI_DSI @@ -728,6 +738,15 @@ config ATMEL_HLCD help HLCDC supports video output to an attached LCD panel. +config BACKLIGHT_LM3532 + bool "Backlight Driver for LM3532" + depends on BACKLIGHT + select DM_I2C + help + Say Y to enable the backlight driver for National Semiconductor / TI + LM3532 Lighting Power chip. Only backlight functions is supported as + for now. Supported backlight level range is from 1 to 255. + config BACKLIGHT_LM3533 bool "Backlight Driver for LM3533" depends on BACKLIGHT @@ -809,17 +828,9 @@ source "drivers/video/stm32/Kconfig" source "drivers/video/tidss/Kconfig" -config VIDEO_TEGRA124 - bool "Enable video support on Tegra124" - help - Tegra124 supports many video output options including eDP and - HDMI. At present only eDP is supported by U-Boot. This option - enables this support which can be used on devices which - have an eDP display connected. - source "drivers/video/bridge/Kconfig" -source "drivers/video/tegra20/Kconfig" +source "drivers/video/tegra/Kconfig" source "drivers/video/imx/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6073bc5234a..fbdb058647a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,13 +29,13 @@ obj-$(CONFIG_$(PHASE_)BMP) += bmp.o endif +obj-$(CONFIG_BACKLIGHT_LM3532) += lm3532_backlight.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ -obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ -obj-${CONFIG_$(XPL_)VIDEO_TIDSS} += tidss/ +obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} += tidss/ obj-y += ti/ obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o @@ -53,13 +53,14 @@ obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o obj-$(CONFIG_VIDEO_EFI) += efi.o -obj-$(CONFIG_VIDEO_IPUV3) += imx/ +obj-y += imx/ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o obj-$(CONFIG_VIDEO_LCD_LG_LD070WX3) += lg-ld070wx3.o +obj-$(CONFIG_VIDEO_LCD_MOT) += mot-panel.o obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o @@ -85,4 +86,4 @@ obj-$(CONFIG_VIDEO_ZYNQMP_DPSUB) += zynqmp/ obj-y += bridge/ obj-y += sunxi/ -obj-y += tegra20/ +obj-y += tegra/ diff --git a/drivers/video/imx/Kconfig b/drivers/video/imx/Kconfig index 34e8b640595..12f11c2eea8 100644 --- a/drivers/video/imx/Kconfig +++ b/drivers/video/imx/Kconfig @@ -13,3 +13,12 @@ config IMX_VIDEO_SKIP config IMX_HDMI bool "Enable HDMI support in IPUv3" depends on VIDEO_IPUV3 + +config IMX_LDB + bool "Freescale i.MX8MP LDB bridge" + depends on VIDEO_BRIDGE + help + Support for i.MX8MP DPI-to-LVDS on-SoC encoder. + +config IMX_LCDIF + bool "i.MX LCDIFv3 LCD controller" diff --git a/drivers/video/imx/Makefile b/drivers/video/imx/Makefile index 179ea651fe8..1edf5a6bdf0 100644 --- a/drivers/video/imx/Makefile +++ b/drivers/video/imx/Makefile @@ -3,4 +3,6 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -obj-y += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_IMX_LDB) += ldb.o +obj-$(CONFIG_IMX_LCDIF) += lcdif.o diff --git a/drivers/video/imx/lcdif.c b/drivers/video/imx/lcdif.c new file mode 100644 index 00000000000..9f4fc7f5152 --- /dev/null +++ b/drivers/video/imx/lcdif.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * i.MX8 LCD interface driver inspired from the Linux driver + * Copyright 2019 NXP + * Copyright 2024 Bootlin + * Adapted by Miquel Raynal <miquel.raynal@bootlin.com> + */ + +#include <asm/io.h> +#include <asm/mach-imx/dma.h> +#include <clk.h> +#include <dm.h> +#include <panel.h> +#include <power-domain.h> +#include <video.h> +#include <video_bridge.h> +#include <linux/delay.h> + +#include "../videomodes.h" + +#define LCDIFV3_CTRL 0x0 +#define LCDIFV3_CTRL_SET 0x4 +#define LCDIFV3_CTRL_CLR 0x8 +#define CTRL_INV_HS BIT(0) +#define CTRL_INV_VS BIT(1) +#define CTRL_INV_DE BIT(2) +#define CTRL_INV_PXCK BIT(3) +#define CTRL_CLK_GATE BIT(30) +#define CTRL_SW_RESET BIT(31) + +#define LCDIFV3_DISP_PARA 0x10 +#define DISP_PARA_DISP_MODE_NORMAL 0 +#define DISP_PARA_LINE_PATTERN_RGB_YUV 0 +#define DISP_PARA_DISP_ON BIT(31) + +#define LCDIFV3_DISP_SIZE 0x14 +#define DISP_SIZE_DELTA_X(x) ((x) & 0xffff) +#define DISP_SIZE_DELTA_Y(x) ((x) << 16) + +#define LCDIFV3_HSYN_PARA 0x18 +#define HSYN_PARA_FP_H(x) ((x) & 0xffff) +#define HSYN_PARA_BP_H(x) ((x) << 16) + +#define LCDIFV3_VSYN_PARA 0x1C +#define VSYN_PARA_FP_V(x) ((x) & 0xffff) +#define VSYN_PARA_BP_V(x) ((x) << 16) + +#define LCDIFV3_VSYN_HSYN_WIDTH 0x20 +#define VSYN_HSYN_PW_H(x) ((x) & 0xffff) +#define VSYN_HSYN_PW_V(x) ((x) << 16) + +#define LCDIFV3_CTRLDESCL0_1 0x200 +#define CTRLDESCL0_1_WIDTH(x) ((x) & 0xffff) +#define CTRLDESCL0_1_HEIGHT(x) ((x) << 16) + +#define LCDIFV3_CTRLDESCL0_3 0x208 +#define CTRLDESCL0_3_PITCH(x) ((x) & 0xFFFF) + +#define LCDIFV3_CTRLDESCL_LOW0_4 0x20C +#define LCDIFV3_CTRLDESCL_HIGH0_4 0x210 + +#define LCDIFV3_CTRLDESCL0_5 0x214 +#define CTRLDESCL0_5_YUV_FORMAT(x) (((x) & 0x3) << 14) +#define CTRLDESCL0_5_BPP(x) (((x) & 0xf) << 24) +#define BPP32_ARGB8888 0x9 +#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30) +#define CTRLDESCL0_5_EN BIT(31) + +struct lcdifv3_priv { + void __iomem *base; + struct clk pix_clk; + struct power_domain pd; + struct udevice *panel; + struct udevice *bridge; +}; + +static void lcdifv3_set_mode(struct lcdifv3_priv *priv, + struct display_timing *timings) +{ + u32 reg; + + writel(DISP_SIZE_DELTA_X(timings->hactive.typ) | + DISP_SIZE_DELTA_Y(timings->vactive.typ), + priv->base + LCDIFV3_DISP_SIZE); + + writel(HSYN_PARA_FP_H(timings->hfront_porch.typ) | + HSYN_PARA_BP_H(timings->hback_porch.typ), + priv->base + LCDIFV3_HSYN_PARA); + + writel(VSYN_PARA_BP_V(timings->vback_porch.typ) | + VSYN_PARA_FP_V(timings->vfront_porch.typ), + priv->base + LCDIFV3_VSYN_PARA); + + writel(VSYN_HSYN_PW_H(timings->hsync_len.typ) | + VSYN_HSYN_PW_V(timings->vsync_len.typ), + priv->base + LCDIFV3_VSYN_HSYN_WIDTH); + + writel(CTRLDESCL0_1_WIDTH(timings->hactive.typ) | + CTRLDESCL0_1_HEIGHT(timings->vactive.typ), + priv->base + LCDIFV3_CTRLDESCL0_1); + + if (timings->flags & DISPLAY_FLAGS_HSYNC_LOW) + writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_CLR); + + if (timings->flags & DISPLAY_FLAGS_VSYNC_LOW) + writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_CLR); + + if (timings->flags & DISPLAY_FLAGS_DE_LOW) + writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_CLR); + + if (timings->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) + writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_SET); + else + writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_CLR); + + writel(0, priv->base + LCDIFV3_DISP_PARA); + + reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5); + reg &= ~(CTRLDESCL0_5_BPP(0xf) | CTRLDESCL0_5_YUV_FORMAT(0x3)); + reg |= CTRLDESCL0_5_BPP(BPP32_ARGB8888); + writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5); +} + +static void lcdifv3_enable_controller(struct lcdifv3_priv *priv) +{ + u32 reg; + + reg = readl(priv->base + LCDIFV3_DISP_PARA); + reg |= DISP_PARA_DISP_ON; + writel(reg, priv->base + LCDIFV3_DISP_PARA); + + reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5); + reg |= CTRLDESCL0_5_EN; + writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5); +} + +static int lcdifv3_video_sync(struct udevice *dev) +{ + struct lcdifv3_priv *priv = dev_get_priv(dev); + u32 reg; + + reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5); + reg |= CTRLDESCL0_5_SHADOW_LOAD_EN; + writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5); + + return 0; +} + +static void lcdifv3_init(struct udevice *dev, struct display_timing *timings) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct lcdifv3_priv *priv = dev_get_priv(dev); + + clk_set_rate(&priv->pix_clk, timings->pixelclock.typ); + + writel(CTRL_SW_RESET | CTRL_CLK_GATE, priv->base + LCDIFV3_CTRL_CLR); + udelay(10); + + lcdifv3_set_mode(priv, timings); + + writel(plat->base & 0xFFFFFFFF, priv->base + LCDIFV3_CTRLDESCL_LOW0_4); + writel(plat->base >> 32, priv->base + LCDIFV3_CTRLDESCL_HIGH0_4); + + writel(CTRLDESCL0_3_PITCH(timings->hactive.typ * 4), /* 32bpp */ + priv->base + LCDIFV3_CTRLDESCL0_3); + + lcdifv3_enable_controller(priv); +} + +static int lcdifv3_video_probe(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct lcdifv3_priv *priv = dev_get_priv(dev); + struct clk axi_clk, disp_axi_clk; + struct display_timing timings; + u32 fb_start, fb_end; + int ret; + + ret = power_domain_get(dev, &priv->pd); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "pix", &priv->pix_clk); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "axi", &axi_clk); + if (ret < 0) + return ret; + + ret = clk_get_by_name(dev, "disp_axi", &disp_axi_clk); + if (ret < 0) + return ret; + + ret = power_domain_on(&priv->pd); + if (ret) + return ret; + + ret = clk_enable(&priv->pix_clk); + if (ret) + goto dis_pd; + + ret = clk_enable(&axi_clk); + if (ret) + goto dis_pix_clk; + + ret = clk_enable(&disp_axi_clk); + if (ret) + goto dis_axi_clk; + + priv->base = dev_remap_addr(dev); + if (!priv->base) { + ret = -EINVAL; + goto dis_clks; + } + + /* Attach bridge */ + ret = uclass_get_device_by_endpoint(UCLASS_VIDEO_BRIDGE, dev, + -1, -1, &priv->bridge); + if (ret) + goto dis_clks; + + ret = video_bridge_attach(priv->bridge); + if (ret) + goto dis_clks; + + ret = video_bridge_set_backlight(priv->bridge, 80); + if (ret) + goto dis_clks; + + /* Attach panels */ + ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge, + 1, -1, &priv->panel); + if (ret) { + ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge, + 2, -1, &priv->panel); + if (ret) + goto dis_clks; + } + + ret = panel_get_display_timing(priv->panel, &timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(priv->panel), + 0, &timings); + if (ret) { + printf("Cannot decode panel timings (%d)\n", ret); + goto dis_clks; + } + } + + lcdifv3_init(dev, &timings); + + /* Only support 32bpp for now */ + uc_priv->bpix = VIDEO_BPP32; + uc_priv->xsize = timings.hactive.typ; + uc_priv->ysize = timings.vactive.typ; + + /* Enable dcache for the frame buffer */ + fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); + fb_end = ALIGN(plat->base + plat->size, 1 << MMU_SECTION_SHIFT); + mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, + DCACHE_WRITEBACK); + video_set_flush_dcache(dev, true); + + return 0; + +dis_clks: + clk_disable(&disp_axi_clk); +dis_axi_clk: + clk_disable(&axi_clk); +dis_pix_clk: + clk_disable(&priv->pix_clk); +dis_pd: + power_domain_off(&priv->pd); + + return ret; +} + +static int lcdifv3_video_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + /* Max size supported by LCDIF */ + plat->size = 1920 * 1080 * VNBYTES(VIDEO_BPP32); + + return 0; +} + +static const struct udevice_id lcdifv3_video_ids[] = { + { .compatible = "fsl,imx8mp-lcdif" }, + { } +}; + +static struct video_ops lcdifv3_video_ops = { + .video_sync = lcdifv3_video_sync, +}; + +U_BOOT_DRIVER(lcdifv3_video) = { + .name = "lcdif", + .id = UCLASS_VIDEO, + .of_match = lcdifv3_video_ids, + .bind = lcdifv3_video_bind, + .ops = &lcdifv3_video_ops, + .probe = lcdifv3_video_probe, + .priv_auto = sizeof(struct lcdifv3_priv), + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE, +}; diff --git a/drivers/video/imx/ldb.c b/drivers/video/imx/ldb.c new file mode 100644 index 00000000000..e918341c0a3 --- /dev/null +++ b/drivers/video/imx/ldb.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Derived work from: + * Philippe Cornu <philippe.cornu@st.com> + * Yannick Fertre <yannick.fertre@st.com> + * Adapted by Miquel Raynal <miquel.raynal@bootlin.com> + */ + +#define LOG_CATEGORY UCLASS_VIDEO_BRIDGE + +#include <clk.h> +#include <dm.h> +#include <log.h> +#include <panel.h> +#include <video_bridge.h> +#include <asm/io.h> +#include <linux/delay.h> + +#define LDB_CTRL_CH0_ENABLE BIT(0) +#define LDB_CTRL_CH1_ENABLE BIT(2) +#define LDB_CTRL_CH0_DATA_WIDTH BIT(5) +#define LDB_CTRL_CH0_BIT_MAPPING BIT(6) +#define LDB_CTRL_CH1_DATA_WIDTH BIT(7) +#define LDB_CTRL_CH1_BIT_MAPPING BIT(8) +#define LDB_CTRL_DI0_VSYNC_POLARITY BIT(9) +#define LDB_CTRL_DI1_VSYNC_POLARITY BIT(10) + +#define LVDS_CTRL_CH0_EN BIT(0) +#define LVDS_CTRL_CH1_EN BIT(1) +#define LVDS_CTRL_VBG_EN BIT(2) +#define LVDS_CTRL_PRE_EMPH_EN BIT(4) +#define LVDS_CTRL_PRE_EMPH_ADJ(n) (((n) & 0x7) << 5) +#define LVDS_CTRL_CC_ADJ(n) (((n) & 0x7) << 11) + +struct imx_ldb_priv { + struct clk ldb_clk; + void __iomem *ldb_ctrl; + void __iomem *lvds_ctrl; + struct udevice *lvds1; + struct udevice *lvds2; +}; + +static int imx_ldb_set_backlight(struct udevice *dev, int percent) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->lvds1) { + ret = panel_enable_backlight(priv->lvds1); + if (ret) { + debug("ldb: Cannot enable lvds1 backlight\n"); + return ret; + } + + ret = panel_set_backlight(priv->lvds1, percent); + if (ret) + return ret; + } + + if (priv->lvds2) { + ret = panel_enable_backlight(priv->lvds2); + if (ret) { + debug("ldb: Cannot enable lvds2 backlight\n"); + return ret; + } + + ret = panel_set_backlight(priv->lvds2, percent); + if (ret) + return ret; + } + + return 0; +} + +static int imx_ldb_of_to_plat(struct udevice *dev) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + int ret; + + uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 1, -1, &priv->lvds1); + uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 2, -1, &priv->lvds2); + if (!priv->lvds1 && !priv->lvds2) { + debug("ldb: No remote panel for '%s' (ret=%d)\n", + dev_read_name(dev), ret); + return ret; + } + + return 0; +} + +/* The block has a mysterious x7 internal divisor (x3.5 in dual configuration) */ +#define IMX_LDB_INTERNAL_DIVISOR(x) (((x) * 70) / 10) +#define IMX_LDB_INTERNAL_DIVISOR_DUAL(x) (((x) * 35) / 10) + +static ulong imx_ldb_input_rate(struct imx_ldb_priv *priv, + struct display_timing *timings) +{ + ulong target_rate = timings->pixelclock.typ; + + if (priv->lvds1 && priv->lvds2) + return IMX_LDB_INTERNAL_DIVISOR_DUAL(target_rate); + + return IMX_LDB_INTERNAL_DIVISOR(target_rate); +} + +static int imx_ldb_attach(struct udevice *dev) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + struct display_timing timings; + bool format_jeida = false; + bool format_24bpp = true; + u32 ldb_ctrl = 0, lvds_ctrl; + ulong ldb_rate; + int ret; + + /* TODO: update the 24bpp/jeida booleans with proper checks when they + * will be supported. + */ + if (priv->lvds1) { + ret = panel_get_display_timing(priv->lvds1, &timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds1), + 0, &timings); + if (ret) { + printf("Cannot decode lvds1 timings (%d)\n", ret); + return ret; + } + } + + ldb_ctrl |= LDB_CTRL_CH0_ENABLE; + if (format_24bpp) + ldb_ctrl |= LDB_CTRL_CH0_DATA_WIDTH; + if (format_jeida) + ldb_ctrl |= LDB_CTRL_CH0_BIT_MAPPING; + if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH) + ldb_ctrl |= LDB_CTRL_DI0_VSYNC_POLARITY; + } + + if (priv->lvds2) { + ret = panel_get_display_timing(priv->lvds2, &timings); + if (ret) { + ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds2), + 0, &timings); + if (ret) { + printf("Cannot decode lvds2 timings (%d)\n", ret); + return ret; + } + } + + ldb_ctrl |= LDB_CTRL_CH1_ENABLE; + if (format_24bpp) + ldb_ctrl |= LDB_CTRL_CH1_DATA_WIDTH; + if (format_jeida) + ldb_ctrl |= LDB_CTRL_CH1_BIT_MAPPING; + if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH) + ldb_ctrl |= LDB_CTRL_DI1_VSYNC_POLARITY; + } + + /* + * Not all pixel clocks will work, as the final rate (after internal + * integer division) should be identical to the LCDIF clock, otherwise + * the rendering will appear resized/shimmering. + */ + ldb_rate = imx_ldb_input_rate(priv, &timings); + clk_set_rate(&priv->ldb_clk, ldb_rate); + + writel(ldb_ctrl, priv->ldb_ctrl); + + lvds_ctrl = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN | + LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN; + writel(lvds_ctrl, priv->lvds_ctrl); + + /* Wait for VBG to stabilize. */ + udelay(15); + + if (priv->lvds1) + lvds_ctrl |= LVDS_CTRL_CH0_EN; + if (priv->lvds2) + lvds_ctrl |= LVDS_CTRL_CH1_EN; + + writel(lvds_ctrl, priv->lvds_ctrl); + + return 0; +} + +static int imx_ldb_probe(struct udevice *dev) +{ + struct imx_ldb_priv *priv = dev_get_priv(dev); + struct udevice *parent = dev_get_parent(dev); + fdt_addr_t parent_addr, child_addr; + int ret; + + ret = clk_get_by_name(dev, "ldb", &priv->ldb_clk); + if (ret < 0) + return ret; + + parent_addr = dev_read_addr(parent); + if (parent_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + child_addr = dev_read_addr_name(dev, "ldb"); + if (child_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->ldb_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE); + if (!priv->ldb_ctrl) + return -EINVAL; + + child_addr = dev_read_addr_name(dev, "lvds"); + if (child_addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->lvds_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE); + if (!priv->lvds_ctrl) + return -EINVAL; + + ret = clk_enable(&priv->ldb_clk); + if (ret) + return ret; + + ret = video_bridge_set_active(dev, true); + if (ret) + goto dis_clk; + + return 0; + +dis_clk: + clk_disable(&priv->ldb_clk); + + return ret; +} + +struct video_bridge_ops imx_ldb_ops = { + .attach = imx_ldb_attach, + .set_backlight = imx_ldb_set_backlight, +}; + +static const struct udevice_id imx_ldb_ids[] = { + { .compatible = "fsl,imx8mp-ldb"}, + { } +}; + +U_BOOT_DRIVER(imx_ldb) = { + .name = "imx-lvds-display-bridge", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = imx_ldb_ids, + .probe = imx_ldb_probe, + .of_to_plat = imx_ldb_of_to_plat, + .ops = &imx_ldb_ops, + .priv_auto = sizeof(struct imx_ldb_priv), +}; diff --git a/drivers/video/lm3532_backlight.c b/drivers/video/lm3532_backlight.c new file mode 100644 index 00000000000..81b3b910196 --- /dev/null +++ b/drivers/video/lm3532_backlight.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI LM3532 LED driver + * + * Copyright (c) 2019 Texas Instruments Incorporated + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include <backlight.h> +#include <dm.h> +#include <dm/ofnode.h> +#include <i2c.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +#define LM3532_BL_MODE_MANUAL 0x00 +#define LM3532_BL_MODE_ALS 0x01 + +#define LM3532_REG_OUTPUT_CFG 0x10 +#define LM3532_REG_STARTSHUT_RAMP 0x11 +#define LM3532_REG_RT_RAMP 0x12 +#define LM3532_REG_PWM_A_CFG 0x13 +#define LM3532_REG_PWM_B_CFG 0x14 +#define LM3532_REG_PWM_C_CFG 0x15 +#define LM3532_REG_ZONE_CFG_A 0x16 +#define LM3532_REG_CTRL_A_FS_CURR 0x17 +#define LM3532_REG_ZONE_CFG_B 0x18 +#define LM3532_REG_CTRL_B_FS_CURR 0x19 +#define LM3532_REG_ZONE_CFG_C 0x1a +#define LM3532_REG_CTRL_C_FS_CURR 0x1b +#define LM3532_REG_ENABLE 0x1d +#define LM3532_ALS_CONFIG 0x23 +#define LM3532_REG_ZN_0_HI 0x60 +#define LM3532_REG_ZN_0_LO 0x61 +#define LM3532_REG_ZN_1_HI 0x62 +#define LM3532_REG_ZN_1_LO 0x63 +#define LM3532_REG_ZN_2_HI 0x64 +#define LM3532_REG_ZN_2_LO 0x65 +#define LM3532_REG_ZN_3_HI 0x66 +#define LM3532_REG_ZN_3_LO 0x67 +#define LM3532_REG_ZONE_TRGT_A 0x70 +#define LM3532_REG_ZONE_TRGT_B 0x75 +#define LM3532_REG_ZONE_TRGT_C 0x7a +#define LM3532_REG_MAX 0x7e + +/* Control Enable */ +#define LM3532_CTRL_A_ENABLE BIT(0) +#define LM3532_CTRL_B_ENABLE BIT(1) +#define LM3532_CTRL_C_ENABLE BIT(2) + +/* PWM Zone Control */ +#define LM3532_PWM_ZONE_MASK 0x7c +#define LM3532_PWM_ZONE_0_EN BIT(2) +#define LM3532_PWM_ZONE_1_EN BIT(3) +#define LM3532_PWM_ZONE_2_EN BIT(4) +#define LM3532_PWM_ZONE_3_EN BIT(5) +#define LM3532_PWM_ZONE_4_EN BIT(6) + +/* Brightness Configuration */ +#define LM3532_I2C_CTRL BIT(0) +#define LM3532_ALS_CTRL 0 +#define LM3532_LINEAR_MAP BIT(1) +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) +#define LM3532_ZONE_0 0 +#define LM3532_ZONE_1 BIT(2) +#define LM3532_ZONE_2 BIT(3) +#define LM3532_ZONE_3 (BIT(2) | BIT(3)) +#define LM3532_ZONE_4 BIT(4) + +#define LM3532_ENABLE_ALS BIT(3) +#define LM3532_ALS_SEL_SHIFT 6 + +/* Zone Boundary Register */ +#define LM3532_ALS_WINDOW_mV 2000 +#define LM3532_ALS_ZB_MAX 4 +#define LM3532_ALS_OFFSET_mV 2 + +#define LM3532_CONTROL_A 0 +#define LM3532_CONTROL_B 1 +#define LM3532_CONTROL_C 2 +#define LM3532_MAX_CONTROL_BANKS 3 +#define LM3532_MAX_LED_STRINGS 3 + +#define LM3532_OUTPUT_CFG_MASK 0x3 +#define LM3532_BRT_VAL_ADJUST 8 +#define LM3532_RAMP_DOWN_SHIFT 3 + +#define LM3532_NUM_RAMP_VALS 8 +#define LM3532_NUM_AVG_VALS 8 +#define LM3532_NUM_IMP_VALS 32 + +#define LM3532_FS_CURR_MIN 5000 +#define LM3532_FS_CURR_MAX 29800 +#define LM3532_FS_CURR_STEP 800 + +struct lm3532_bank_data { + int control_bank; + int mode; + int ctrl_brt_pointer; + int num_leds; + int full_scale_current; + u32 present:1; + u32 led_strings[LM3532_MAX_CONTROL_BANKS]; +}; + +struct lm3532_backlight_priv { + struct gpio_desc enable_gpio; + struct udevice *regulator; + + u32 runtime_ramp_up; + u32 runtime_ramp_down; + + struct lm3532_bank_data bank[LM3532_MAX_CONTROL_BANKS]; +}; + +/* This device does not like i2c md so use this instead */ +static void __maybe_unused dump_i2c(struct udevice *dev) +{ + int i, c; + + for (i = 0; i < 0x10; i++) { + printf("00%02x: %02x", i * 0x10, dm_i2c_reg_read(dev, i * 0x10)); + for (c = 1; c < 0xf; c++) + printf(" %02x", dm_i2c_reg_read(dev, i * 0x10 + c)); + printf(" %02x\n", dm_i2c_reg_read(dev, i * 0x10 + 0xf)); + } +} + +static int lm3532_backlight_enable(struct udevice *dev) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + int ret, i; + + for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) { + if (priv->bank[i].present) { + u32 ctrl_en_val = BIT(priv->bank[i].control_bank); + + ret = dm_i2c_reg_clrset(dev, LM3532_REG_ENABLE, + ctrl_en_val, ctrl_en_val); + if (ret) { + log_debug("%s: failed to set ctrl: %d\n", + __func__, ret); + return ret; + } + } + } + + regulator_set_enable_if_allowed(priv->regulator, 1); + + return 0; +} + +static int lm3532_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + struct lm3532_bank_data *bank; + int ret, i; + + for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) { + if (priv->bank[i].present) { + bank = &priv->bank[i]; + u32 brightness_reg = LM3532_REG_ZONE_TRGT_A + + bank->control_bank * 5 + + (bank->ctrl_brt_pointer >> 2); + + ret = dm_i2c_reg_write(dev, brightness_reg, percent); + if (ret) { + log_debug("%s: failed to set brightness: %d\n", + __func__, ret); + return ret; + } + } + } + + return 0; +} + +static const int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192, + 16384, 32768, 65536 }; +static int lm3532_get_ramp_index(int ramp_time) +{ + int i; + + if (ramp_time <= ramp_table[0]) + return 0; + + if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1]) + return LM3532_NUM_RAMP_VALS - 1; + + for (i = 1; i < LM3532_NUM_RAMP_VALS; i++) { + if (ramp_time == ramp_table[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (ramp_time > ramp_table[i - 1] && + ramp_time < ramp_table[i]) { + if (ramp_time - ramp_table[i - 1] < ramp_table[i] - ramp_time) + return i - 1; + else + return i; + } + } + + return 0; +} + +static int lm3532_backlight_of_to_plat(struct udevice *dev) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + u32 ramp_time, reg; + ofnode child; + int ret; + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not decode enable-gpios (%d)\n", __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vin-supply", &priv->regulator); + if (ret) { + log_debug("%s: vin regulator not defined: %d\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ramp_time = dev_read_u32_default(dev, "ramp-up-us", 0); + priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time); + + ramp_time = dev_read_u32_default(dev, "ramp-down-us", 0); + priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); + + /* Backlight is one of children but has no dedicated driver */ + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + ret = ofnode_read_u32(child, "reg", ®); + if (ret || reg > LM3532_CONTROL_C) { + log_debug("%s: control bank invalid %d\n", __func__, reg); + continue; + } + + struct lm3532_bank_data *bank = &priv->bank[reg]; + + bank->control_bank = reg; + bank->present = 1; + bank->mode = ofnode_read_u32_default(child, "ti,led-mode", + LM3532_BL_MODE_MANUAL); + bank->mode = LM3532_BL_MODE_ALS ? LM3532_ALS_CTRL : LM3532_I2C_CTRL; + + if (ofnode_read_bool(child, "ti,linear-mapping-mode")) + bank->mode |= LM3532_LINEAR_MAP; + + bank->num_leds = ofnode_read_size(child, "led-sources"); + bank->num_leds /= sizeof(u32); + if (bank->num_leds > LM3532_MAX_LED_STRINGS) { + log_debug("%s: too many LED string defined %d\n", + __func__, bank->num_leds); + continue; + } + + ret = ofnode_read_u32_array(child, "led-sources", + bank->led_strings, + bank->num_leds); + if (ret) { + log_debug("%s: led-sources property missing %d\n", + __func__, ret); + continue; + } + + ret = ofnode_read_u32(child, "led-max-microamp", + &bank->full_scale_current); + if (ret) + log_debug("%s: failed getting led-max-microamp %d\n", + __func__, ret); + else + bank->full_scale_current = min(bank->full_scale_current, + LM3532_FS_CURR_MAX); + } + + return 0; +} + +static int lm3532_backlight_init_registers(struct udevice *dev, + struct lm3532_bank_data *bank) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + u32 brightness_config_val, runtime_ramp_val; + u32 output_cfg_val = 0, output_cfg_shift = 0, output_cfg_mask = 0; + int fs_current_reg, fs_current_val; + int ret, i; + + if (!bank->present) + return 0; + + u32 brightness_config_reg = LM3532_REG_ZONE_CFG_A + bank->control_bank * 2; + /* + * This could be hard coded to the default value but the control + * brightness register may have changed during boot. + */ + ret = dm_i2c_reg_read(dev, brightness_config_reg); + if (ret < 0) + return ret; + + bank->ctrl_brt_pointer = ret & ~LM3532_ZONE_MASK; + brightness_config_val = bank->ctrl_brt_pointer | bank->mode; + + ret = dm_i2c_reg_write(dev, brightness_config_reg, brightness_config_val); + if (ret) + return ret; + + if (bank->full_scale_current) { + fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + bank->control_bank * 2; + fs_current_val = (bank->full_scale_current - LM3532_FS_CURR_MIN) / + LM3532_FS_CURR_STEP; + + ret = dm_i2c_reg_write(dev, fs_current_reg, fs_current_val); + if (ret) + return ret; + } + + for (i = 0; i < bank->num_leds; i++) { + output_cfg_shift = bank->led_strings[i] * 2; + output_cfg_val |= (bank->control_bank << output_cfg_shift); + output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift; + } + + ret = dm_i2c_reg_clrset(dev, LM3532_REG_OUTPUT_CFG, output_cfg_mask, + output_cfg_val); + if (ret) + return ret; + + runtime_ramp_val = priv->runtime_ramp_up | + (priv->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT); + + return dm_i2c_reg_write(dev, LM3532_REG_RT_RAMP, runtime_ramp_val); +} + +static int lm3532_backlight_probe(struct udevice *dev) +{ + struct lm3532_backlight_priv *priv = dev_get_priv(dev); + int ret, i; + + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + dm_gpio_set_value(&priv->enable_gpio, 1); + + for (i = 0; i < LM3532_MAX_CONTROL_BANKS; i++) { + ret = lm3532_backlight_init_registers(dev, &priv->bank[i]); + if (ret) + return ret; + } + + return 0; +} + +static const struct backlight_ops lm3532_backlight_ops = { + .enable = lm3532_backlight_enable, + .set_brightness = lm3532_backlight_set_brightness, +}; + +static const struct udevice_id lm3532_backlight_ids[] = { + { .compatible = "ti,lm3532" }, + { } +}; + +U_BOOT_DRIVER(lm3532_backlight) = { + .name = "lm3532_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = lm3532_backlight_ids, + .of_to_plat = lm3532_backlight_of_to_plat, + .probe = lm3532_backlight_probe, + .ops = &lm3532_backlight_ops, + .priv_auto = sizeof(struct lm3532_backlight_priv), +}; diff --git a/drivers/video/mot-panel.c b/drivers/video/mot-panel.c new file mode 100644 index 00000000000..a9114957867 --- /dev/null +++ b/drivers/video/mot-panel.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Motorola ATRIX 4G and DROID X2 DSI panel driver + * Exact manufacturer and model unknown + * + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <backlight.h> +#include <dm.h> +#include <panel.h> +#include <log.h> +#include <misc.h> +#include <mipi_display.h> +#include <mipi_dsi.h> +#include <asm/gpio.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <power/regulator.h> + +struct mot_panel_priv { + struct udevice *vdd; + struct udevice *vddio; + + struct udevice *backlight; + + struct gpio_desc reset_gpio; +}; + +#define dsi_generic_write_seq(dsi, cmd, seq...) do { \ + static const u8 b[] = { cmd, seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static struct display_timing default_timing = { + .pixelclock.typ = 38250000, + .hactive.typ = 540, + .hfront_porch.typ = 32, + .hback_porch.typ = 32, + .hsync_len.typ = 16, + .vactive.typ = 960, + .vfront_porch.typ = 12, + .vback_porch.typ = 12, + .vsync_len.typ = 8, +}; + +static int mot_es2(struct mipi_dsi_device *dsi) +{ + int ret; + + dsi_generic_write_seq(dsi, 0x55, 0x01); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret); + return ret; + } + mdelay(120); + + dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49, + 0x74, 0x29, 0x12, 0x15, 0x2f, 0x2f, 0x04); + dsi_generic_write_seq(dsi, 0xf8, 0x4b, 0x04, 0x10, 0x1a, 0x2c, 0x2c, + 0x2c, 0x2c, 0x14, 0x12); + + dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x00, 0x80, 0xc7, 0x00); + dsi_generic_write_seq(dsi, 0xb7, 0x66, 0xf6, 0x46, 0x9f, 0x90, 0x99, + 0xff, 0x80, 0x6d, 0x01); + + /* Gamma R */ + dsi_generic_write_seq(dsi, 0xf9, 0x04); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c, + 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x30, 0x12, 0x0e, 0x0c, + 0x22, 0x27, 0x31, 0x2e, 0x07, 0x0f); + + /* Gamma G */ + dsi_generic_write_seq(dsi, 0xf9, 0x02); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11, + 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x37, 0x15, 0x15, 0x11, + 0x1f, 0x25, 0x2d, 0x2a, 0x05, 0x0f); + + /* Gamma B */ + dsi_generic_write_seq(dsi, 0xf9, 0x01); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15, + 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x3f, 0x16, 0x1f, 0x15, + 0x1f, 0x25, 0x2d, 0x2b, 0x06, 0x0b); + + /* Gamma W */ + dsi_generic_write_seq(dsi, 0xf9, 0x20); + dsi_generic_write_seq(dsi, 0xfa, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11, + 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08); + dsi_generic_write_seq(dsi, 0xfb, 0x00, 0x2f, 0x34, 0x15, 0x1a, 0x11, + 0x1f, 0x23, 0x2d, 0x29, 0x02, 0x08); + + dsi_generic_write_seq(dsi, 0x53, 0x2c); + dsi_generic_write_seq(dsi, 0x35, 0x00); + + return 0; +} + +static int __maybe_unused mot_es4(struct mipi_dsi_device *dsi) +{ + int ret; + + dsi_generic_write_seq(dsi, 0xd2, 0x04, 0x53); + dsi_generic_write_seq(dsi, 0xd2, 0x05, 0x53); + dsi_generic_write_seq(dsi, 0x55, 0x01); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret); + return ret; + } + mdelay(120); + + dsi_generic_write_seq(dsi, 0xb5, 0x03, 0x7f, 0x0a, 0x80, 0xff, 0x00); + dsi_generic_write_seq(dsi, 0xb7, 0x7a, 0xf7, 0x4d, 0x91, 0x90, 0xb3, + 0xff, 0x80, 0x6d, 0x01); + dsi_generic_write_seq(dsi, 0xf4, 0x00, 0xbb, 0x46, 0x53, 0x0c, 0x49, + 0x74, 0x29, 0x12, 0x15, 0x37, 0x37, 0x04); + dsi_generic_write_seq(dsi, 0xf8, 0x0a, 0x04, 0x10, 0x2a, 0x35, 0x35, + 0x35, 0x35, 0x21, 0x1a); + + /* Gamma R */ + dsi_generic_write_seq(dsi, 0xf9, 0x04); + dsi_generic_write_seq(dsi, 0xfa, 0x08, 0x1c, 0x1b, 0x0f, 0x0f, 0x0a, + 0x1e, 0x22, 0x27, 0x26, 0x07, 0x0d); + dsi_generic_write_seq(dsi, 0xfb, 0x08, 0x3c, 0x27, 0x0f, 0x0f, 0x0a, + 0x1e, 0x26, 0x31, 0x2f, 0x07, 0x0b); + + /* Gamma G */ + dsi_generic_write_seq(dsi, 0xf9, 0x02); + dsi_generic_write_seq(dsi, 0xfa, 0x30, 0x14, 0x0f, 0x00, 0x06, 0x02, + 0x1e, 0x22, 0x27, 0x27, 0x08, 0x10); + dsi_generic_write_seq(dsi, 0xfb, 0x30, 0x35, 0x0f, 0x00, 0x0a, 0x02, + 0x1c, 0x23, 0x31, 0x2f, 0x08, 0x0e); + + /* Gamma B */ + dsi_generic_write_seq(dsi, 0xf9, 0x01); + dsi_generic_write_seq(dsi, 0xfa, 0x12, 0x1b, 0x26, 0x0e, 0x12, 0x0b, + 0x1e, 0x22, 0x27, 0x27, 0x06, 0x0c); + dsi_generic_write_seq(dsi, 0xfb, 0x12, 0x3b, 0x2c, 0x12, 0x12, 0x0e, + 0x1e, 0x26, 0x31, 0x2f, 0x06, 0x0d); + + /* Gamma W */ + dsi_generic_write_seq(dsi, 0xf9, 0x20); + dsi_generic_write_seq(dsi, 0xfa, 0x37, 0x1b, 0x09, 0x01, 0x06, 0x04, + 0x19, 0x19, 0x22, 0x24, 0x04, 0x15); + dsi_generic_write_seq(dsi, 0xfb, 0x37, 0x3b, 0x17, 0x01, 0x0a, 0x04, + 0x19, 0x1d, 0x2c, 0x2c, 0x04, 0x13); + + dsi_generic_write_seq(dsi, 0x53, 0x2c); + dsi_generic_write_seq(dsi, 0x35, 0x00); + dsi_generic_write_seq(dsi, 0xc3, 0x01, 0x4e); + + return 0; +} + +static int mot_panel_enable_backlight(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + dsi_generic_write_seq(dsi, 0xf0, 0x5a, 0x5a); + dsi_generic_write_seq(dsi, 0xf1, 0x5a, 0x5a); + dsi_generic_write_seq(dsi, 0xd0, 0x8e); + + ret = mot_es2(dsi); + if (ret) + return ret; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_debug("%s: failed to set display on: %d\n", __func__, ret); + return ret; + } + mdelay(20); + + return 0; +} + +static int mot_panel_set_backlight(struct udevice *dev, int percent) +{ + struct mot_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + mdelay(5); + + return backlight_set_brightness(priv->backlight, percent); +} + +static int mot_panel_timings(struct udevice *dev, struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int mot_panel_of_to_plat(struct udevice *dev) +{ + struct mot_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_debug("%s: cannot get backlight: ret = %d\n", __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd); + if (ret) { + log_debug("%s: cannot get vdd-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); + if (ret) { + log_debug("%s: cannot get vddio-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not decode reser-gpios (%d)\n", __func__, ret); + return ret; + } + + return 0; +} + +static int mot_panel_hw_init(struct udevice *dev) +{ + struct mot_panel_priv *priv = dev_get_priv(dev); + int ret; + + ret = regulator_set_enable_if_allowed(priv->vddio, 1); + if (ret) { + log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->vdd, 1); + if (ret) { + log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret); + return ret; + } + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_debug("%s: error entering reset (%d)\n", __func__, ret); + return ret; + } + mdelay(50); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error exiting reset (%d)\n", __func__, ret); + return ret; + } + mdelay(10); + + return 0; +} + +static int mot_panel_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 2; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_LPM; + + return mot_panel_hw_init(dev); +} + +static const struct panel_ops mot_panel_ops = { + .enable_backlight = mot_panel_enable_backlight, + .set_backlight = mot_panel_set_backlight, + .get_display_timing = mot_panel_timings, +}; + +static const struct udevice_id mot_panel_ids[] = { + { .compatible = "motorola,mot-panel" }, + { } +}; + +U_BOOT_DRIVER(mot_panel) = { + .name = "mot_panel", + .id = UCLASS_PANEL, + .of_match = mot_panel_ids, + .ops = &mot_panel_ops, + .of_to_plat = mot_panel_of_to_plat, + .probe = mot_panel_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct mot_panel_priv), +}; diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig new file mode 100644 index 00000000000..d3b8dbb2826 --- /dev/null +++ b/drivers/video/tegra/Kconfig @@ -0,0 +1,51 @@ +config HOST1X_TEGRA + bool "NVIDIA Tegra host1x BUS support" + depends on SIMPLE_BUS + +config VIDEO_TEGRA + bool "Enable Display Controller support on Tegra devices" + depends on OF_CONTROL + select HOST1X_TEGRA + help + Enable support for Display Controller found in Tegra SoC. The + Display Controller Complex integrates two independent display + controllers. Each display controller is capable of interfacing + to an external display device, which can be a parallel interface + or SPI LCD, DVI, an HDMI HDTV, RGB monitor or a MIPI DSI LCD. + Direct interface is supported directly to most LCD displays with + TFT or TFT-like interface. + +config VIDEO_DSI_TEGRA + bool "Enable DSI controller support on Tegra devices" + depends on VIDEO_BRIDGE && PANEL && DM_GPIO + select VIDEO_TEGRA + select VIDEO_MIPI_DSI + help + Enable support for the Display Serial Interface (DSI) found in + Tegra SoC. It is a MIPI standard serial bitstream, intended to + provide a low pin count interface to a display panel. + +config VIDEO_HDMI_TEGRA + bool "Enable HDMI support on Tegra devices" + depends on VIDEO_BRIDGE && DM_I2C + select I2C_EDID + select VIDEO_TEGRA + help + Enable support for the High-Definition Multimedia Interface (HDMI) + found in Tegra SoC. + +config TEGRA_BACKLIGHT_PWM + bool "Enable Tegra DC PWM backlight support" + depends on BACKLIGHT + select VIDEO_TEGRA + help + Enable support for the Display Controller dependent PWM backlight + found in the Tegra SoC and usually used with DSI panels. + +config VIDEO_TEGRA124 + bool "Enable video support on Tegra124" + help + Tegra124 supports many video output options including eDP and + HDMI. At present only eDP is supported by U-Boot. This option + enables this support which can be used on devices which + have an eDP display connected. diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile new file mode 100644 index 00000000000..3c50a0ba3c3 --- /dev/null +++ b/drivers/video/tegra/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_HOST1X_TEGRA) += host1x.o +obj-$(CONFIG_VIDEO_TEGRA) += dc.o +obj-$(CONFIG_VIDEO_DSI_TEGRA) += dsi.o mipi.o mipi-phy.o +obj-$(CONFIG_VIDEO_HDMI_TEGRA) += hdmi.o +obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += dc-pwm-backlight.o + +obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ diff --git a/drivers/video/tegra/TODO b/drivers/video/tegra/TODO new file mode 100644 index 00000000000..1c8c2389a18 --- /dev/null +++ b/drivers/video/tegra/TODO @@ -0,0 +1,5 @@ +Existence of separate Tegra124 video implementations is not an ideal solution +since generic video setup for Tegra already has Tegra124 support of some degree. +It is not possible at the time of this note is written to integrate T124 SOR +and DP without possible regressions. Tegra124 setup for SOR and DP should be +incorporated into existing setup once such opportunity occurs. diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra/dc-pwm-backlight.c index 998f0df1991..eff10b5563e 100644 --- a/drivers/video/tegra20/tegra-pwm-backlight.c +++ b/drivers/video/tegra/dc-pwm-backlight.c @@ -15,7 +15,7 @@ #include <asm/io.h> #include <asm/gpio.h> -#include "tegra-dc.h" +#include "dc.h" #define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 #define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra/dc.c index 1f43153ff27..f0e3d2c993f 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra/dc.c @@ -19,7 +19,7 @@ #include <asm/arch/clock.h> #include <asm/arch/powergate.h> -#include "tegra-dc.h" +#include "dc.h" /* Holder of Tegra per-SOC DC differences */ struct tegra_dc_soc_info { diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra/dc.h index 2a4013b3355..2a4013b3355 100644 --- a/drivers/video/tegra20/tegra-dc.h +++ b/drivers/video/tegra/dc.h diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra/dsi.c index a2a22fa0fe2..bc308869f4e 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra/dsi.c @@ -24,8 +24,8 @@ #include <asm/arch/clock.h> #include <asm/arch-tegra/clk_rst.h> -#include "tegra-dc.h" -#include "tegra-dsi.h" +#include "dc.h" +#include "dsi.h" #include "mipi-phy.h" /* List of supported DSI bridges */ @@ -1129,6 +1129,7 @@ static const struct video_bridge_ops tegra_dsi_bridge_ops = { }; static const struct udevice_id tegra_dsi_bridge_ids[] = { + { .compatible = "nvidia,tegra20-dsi", .data = DSI_V0 }, { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, { .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 }, diff --git a/drivers/video/tegra20/tegra-dsi.h b/drivers/video/tegra/dsi.h index 683c5e31a34..683c5e31a34 100644 --- a/drivers/video/tegra20/tegra-dsi.h +++ b/drivers/video/tegra/dsi.h diff --git a/drivers/video/tegra20/tegra-hdmi.c b/drivers/video/tegra/hdmi.c index bda69919d92..bfb48b25187 100644 --- a/drivers/video/tegra20/tegra-hdmi.c +++ b/drivers/video/tegra/hdmi.c @@ -22,8 +22,8 @@ #include <asm/io.h> #include <asm/arch/clock.h> -#include "tegra-dc.h" -#include "tegra-hdmi.h" +#include "dc.h" +#include "hdmi.h" #define DDCCI_ENTRY_ADDR 0x37 #define DDCCI_SOURSE_ADDR 0x51 diff --git a/drivers/video/tegra20/tegra-hdmi.h b/drivers/video/tegra/hdmi.h index d17655973e3..d17655973e3 100644 --- a/drivers/video/tegra20/tegra-hdmi.h +++ b/drivers/video/tegra/hdmi.h diff --git a/drivers/video/tegra20/tegra-host1x.c b/drivers/video/tegra/host1x.c index 58ab871a3b4..58ab871a3b4 100644 --- a/drivers/video/tegra20/tegra-host1x.c +++ b/drivers/video/tegra/host1x.c diff --git a/drivers/video/tegra20/mipi-phy.c b/drivers/video/tegra/mipi-phy.c index 576262e405d..576262e405d 100644 --- a/drivers/video/tegra20/mipi-phy.c +++ b/drivers/video/tegra/mipi-phy.c diff --git a/drivers/video/tegra20/mipi-phy.h b/drivers/video/tegra/mipi-phy.h index 41889a75035..41889a75035 100644 --- a/drivers/video/tegra20/mipi-phy.h +++ b/drivers/video/tegra/mipi-phy.h diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra/mipi.c index a4f4343d008..a4f4343d008 100644 --- a/drivers/video/tegra20/tegra-mipi.c +++ b/drivers/video/tegra/mipi.c diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra/tegra124/Makefile index a378382628c..a378382628c 100644 --- a/drivers/video/tegra124/Makefile +++ b/drivers/video/tegra/tegra124/Makefile diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra/tegra124/display.c index abe31e27d84..abe31e27d84 100644 --- a/drivers/video/tegra124/display.c +++ b/drivers/video/tegra/tegra124/display.c diff --git a/drivers/video/tegra124/displayport.h b/drivers/video/tegra/tegra124/displayport.h index a3044475aeb..a3044475aeb 100644 --- a/drivers/video/tegra124/displayport.h +++ b/drivers/video/tegra/tegra124/displayport.h diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra/tegra124/dp.c index b95b14da77d..b95b14da77d 100644 --- a/drivers/video/tegra124/dp.c +++ b/drivers/video/tegra/tegra124/dp.c diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra/tegra124/sor.c index 1ce5330c6bc..1ce5330c6bc 100644 --- a/drivers/video/tegra124/sor.c +++ b/drivers/video/tegra/tegra124/sor.c diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra/tegra124/sor.h index 2fc9a38267d..2fc9a38267d 100644 --- a/drivers/video/tegra124/sor.h +++ b/drivers/video/tegra/tegra124/sor.h diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig deleted file mode 100644 index 598f9ea1f21..00000000000 --- a/drivers/video/tegra20/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -config HOST1X_TEGRA - bool "NVIDIA Tegra host1x BUS support" - depends on SIMPLE_BUS - -config VIDEO_TEGRA20 - bool "Enable Display Controller support on Tegra20 and Tegra 30" - depends on OF_CONTROL - select HOST1X_TEGRA - help - T20/T30 support video output to an attached LCD panel as well as - other options such as HDMI. Only the LCD is supported in U-Boot. - This option enables this support which can be used on devices which - have an LCD display connected. - -config VIDEO_DSI_TEGRA30 - bool "Enable Tegra 30 DSI support" - depends on VIDEO_BRIDGE && PANEL && DM_GPIO - select VIDEO_TEGRA20 - select VIDEO_MIPI_DSI - help - T30 has native support for DSI panels. This option enables support - for such panels which can be used on endeavoru and tf600t. - -config VIDEO_HDMI_TEGRA - bool "Enable Tegra HDMI support" - depends on VIDEO_BRIDGE && DM_I2C - select I2C_EDID - select VIDEO_TEGRA20 - help - Tegra has native support for HDMI. This option enables support - for such connection and can be used for any supported device. - -config TEGRA_BACKLIGHT_PWM - bool "Enable Tegra DC PWM backlight support" - depends on BACKLIGHT - select VIDEO_TEGRA20 - help - Tegra DC dependent backlight. diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile deleted file mode 100644 index 78521405749..00000000000 --- a/drivers/video/tegra20/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ - -obj-$(CONFIG_HOST1X_TEGRA) += tegra-host1x.o -obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o -obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o -obj-$(CONFIG_VIDEO_HDMI_TEGRA) += tegra-hdmi.o -obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o diff --git a/drivers/video/tidss/Makefile b/drivers/video/tidss/Makefile index f0cbe1d4ed1..3381a5fec57 100644 --- a/drivers/video/tidss/Makefile +++ b/drivers/video/tidss/Makefile @@ -9,4 +9,4 @@ # Author: Tomi Valkeinen <tomi.valkeinen@ti.com> -obj-${CONFIG_$(XPL_)VIDEO_TIDSS} = tidss_drv.o +obj-${CONFIG_$(PHASE_)VIDEO_TIDSS} = tidss_drv.o |
