summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile14
-rw-r--r--drivers/adc/rockchip-saradc.c10
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/ahci.c47
-rw-r--r--drivers/ata/dwc_ahsata.c82
-rw-r--r--drivers/ata/dwc_ahsata_priv.h2
-rw-r--r--drivers/block/Makefile2
-rw-r--r--drivers/block/blkmap.c82
-rw-r--r--drivers/block/blkmap_helper.c2
-rw-r--r--drivers/bootcount/Kconfig3
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/button/button-qcom-pmic.c8
-rw-r--r--drivers/button/button-uclass.c2
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/airoha/Makefile3
-rw-r--r--drivers/clk/airoha/clk-airoha.c454
-rw-r--r--drivers/clk/altera/clk-agilex5.c53
-rw-r--r--drivers/clk/clk-composite.c4
-rw-r--r--drivers/clk/clk-divider.c7
-rw-r--r--drivers/clk/clk-fixed-factor.c6
-rw-r--r--drivers/clk/clk-gate.c5
-rw-r--r--drivers/clk/clk-mux.c47
-rw-r--r--drivers/clk/clk-stub.c4
-rw-r--r--drivers/clk/clk-uclass.c37
-rw-r--r--drivers/clk/clk_boston.c19
-rw-r--r--drivers/clk/imx/Kconfig1
-rw-r--r--drivers/clk/imx/clk-composite-8m.c4
-rw-r--r--drivers/clk/imx/clk-gate2.c5
-rw-r--r--drivers/clk/imx/clk-imx6q.c92
-rw-r--r--drivers/clk/imx/clk-imx8mm.c268
-rw-r--r--drivers/clk/imx/clk-imx8mn.c262
-rw-r--r--drivers/clk/imx/clk-imx8mp.c419
-rw-r--r--drivers/clk/imx/clk-imx8mq.c226
-rw-r--r--drivers/clk/imx/clk-imx93.c8
-rw-r--r--drivers/clk/imx/clk-imxrt1020.c42
-rw-r--r--drivers/clk/imx/clk-imxrt1050.c78
-rw-r--r--drivers/clk/imx/clk-imxrt1170.c30
-rw-r--r--drivers/clk/imx/clk-pllv3.c9
-rw-r--r--drivers/clk/imx/clk.h116
-rw-r--r--drivers/clk/mediatek/Makefile2
-rw-r--r--drivers/clk/qcom/Kconfig8
-rw-r--r--drivers/clk/qcom/Makefile1
-rw-r--r--drivers/clk/qcom/clock-apq8016.c12
-rw-r--r--drivers/clk/qcom/clock-apq8096.c5
-rw-r--r--drivers/clk/qcom/clock-ipq9574.c94
-rw-r--r--drivers/clk/qcom/clock-qcm2290.c4
-rw-r--r--drivers/clk/qcom/clock-qcom.h13
-rw-r--r--drivers/clk/qcom/clock-sa8775p.c4
-rw-r--r--drivers/clk/qcom/clock-sc7280.c135
-rw-r--r--drivers/clk/qcom/clock-sdm845.c7
-rw-r--r--drivers/clk/qcom/clock-sm6115.c4
-rw-r--r--drivers/clk/qcom/clock-sm8150.c4
-rw-r--r--drivers/clk/qcom/clock-sm8250.c4
-rw-r--r--drivers/clk/qcom/clock-sm8550.c4
-rw-r--r--drivers/clk/qcom/clock-sm8650.c4
-rw-r--r--drivers/clk/qcom/clock-x1e80100.c4
-rw-r--r--drivers/clk/renesas/rzg2l-cpg.c17
-rw-r--r--drivers/clk/rockchip/Makefile2
-rw-r--r--drivers/clk/rockchip/clk_pll.c23
-rw-r--r--drivers/clk/rockchip/clk_rk3528.c1754
-rw-r--r--drivers/clk/rockchip/clk_rk3576.c2513
-rw-r--r--drivers/clk/stm32/clk-stm32mp1.c1
-rw-r--r--drivers/clk/sunxi/Kconfig7
-rw-r--r--drivers/clk/sunxi/Makefile1
-rw-r--r--drivers/clk/sunxi/clk_a100.c102
-rw-r--r--drivers/clk/sunxi/clk_sunxi.c5
-rw-r--r--drivers/clk/ti/clk-k3.c6
-rw-r--r--drivers/core/Makefile8
-rw-r--r--drivers/core/ofnode_graph.c217
-rw-r--r--drivers/core/uclass.c19
-rw-r--r--drivers/cpu/imx8_cpu.c54
-rw-r--r--drivers/ddr/altera/Makefile5
-rw-r--r--drivers/ddr/altera/iossm_mailbox.c923
-rw-r--r--drivers/ddr/altera/iossm_mailbox.h141
-rw-r--r--drivers/ddr/altera/sdram_agilex5.c423
-rw-r--r--drivers/ddr/altera/sdram_soc64.c132
-rw-r--r--drivers/ddr/altera/sdram_soc64.h10
-rw-r--r--drivers/dfu/Makefile18
-rw-r--r--drivers/dma/Kconfig7
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/adi_dma.c253
-rw-r--r--drivers/dma/ti/Makefile1
-rw-r--r--drivers/dma/ti/k3-psil.c2
-rw-r--r--drivers/dma/ti/k3-udma.c20
-rw-r--r--drivers/fastboot/Kconfig1
-rw-r--r--drivers/fastboot/fb_command.c1
-rw-r--r--drivers/fastboot/fb_common.c4
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/firmware-zynqmp.c74
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c8
-rw-r--r--drivers/firmware/scmi/smt.c13
-rw-r--r--drivers/firmware/ti_sci.c23
-rw-r--r--drivers/firmware/ti_sci_static_data.h2
-rw-r--r--drivers/fpga/altera.c41
-rw-r--r--drivers/fpga/versalpl.c11
-rw-r--r--drivers/gpio/Kconfig17
-rw-r--r--drivers/gpio/Makefile12
-rw-r--r--drivers/gpio/adp5588_gpio.c208
-rw-r--r--drivers/gpio/axp_gpio.c75
-rw-r--r--drivers/gpio/gpio-adi-adsp.c179
-rw-r--r--drivers/gpio/msm_gpio.c53
-rw-r--r--drivers/gpio/pca953x_gpio.c2
-rw-r--r--drivers/gpio/sunxi_gpio.c12
-rw-r--r--drivers/i2c/Kconfig16
-rw-r--r--drivers/i2c/Makefile17
-rw-r--r--drivers/i2c/adi_i2c.c386
-rw-r--r--drivers/i2c/mtk_i2c.c3
-rw-r--r--drivers/i2c/omap24xx_i2c.c165
-rw-r--r--drivers/input/Kconfig6
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/cpcap_pwrbutton.c134
-rw-r--r--drivers/led/Makefile2
-rw-r--r--drivers/mailbox/Kconfig7
-rw-r--r--drivers/mailbox/Makefile3
-rw-r--r--drivers/mailbox/imx-mailbox.c443
-rw-r--r--drivers/mailbox/zynqmp-ipi.c1
-rw-r--r--drivers/memory/ti-gpmc.c1
-rw-r--r--drivers/misc/Kconfig8
-rw-r--r--drivers/misc/Makefile11
-rw-r--r--drivers/misc/k3_fuse.c78
-rw-r--r--drivers/misc/qfw_acpi.c34
-rw-r--r--drivers/misc/rockchip-otp.c15
-rw-r--r--drivers/mmc/Kconfig19
-rw-r--r--drivers/mmc/Makefile7
-rw-r--r--drivers/mmc/adi_sdhci.c148
-rw-r--r--drivers/mmc/am654_sdhci.c25
-rw-r--r--drivers/mmc/fsl_esdhc_imx.c8
-rw-r--r--drivers/mmc/mmc-uclass.c29
-rw-r--r--drivers/mmc/mmc.c24
-rw-r--r--drivers/mmc/mmc_boot.c173
-rw-r--r--drivers/mmc/mmc_write.c11
-rw-r--r--drivers/mmc/msm_sdhci.c10
-rw-r--r--drivers/mmc/omap_hsmmc.c13
-rw-r--r--drivers/mmc/rockchip_dw_mmc.c1
-rw-r--r--drivers/mmc/rockchip_sdhci.c54
-rw-r--r--drivers/mmc/sdhci.c8
-rw-r--r--drivers/mmc/sunxi_mmc.c55
-rw-r--r--drivers/mmc/sunxi_mmc.h18
-rw-r--r--drivers/mtd/mtdpart.c5
-rw-r--r--drivers/mtd/nand/raw/Kconfig24
-rw-r--r--drivers/mtd/nand/raw/Makefile2
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c13
-rw-r--r--drivers/mtd/nand/raw/cadence_nand.c2423
-rw-r--r--drivers/mtd/nand/raw/cadence_spl.c59
-rw-r--r--drivers/mtd/nand/raw/meson_nand.c4
-rw-r--r--drivers/mtd/nand/raw/nand_base.c73
-rw-r--r--drivers/mtd/nand/spi/core.c18
-rw-r--r--drivers/mtd/spi/spi-nor-tiny.c1
-rw-r--r--drivers/mtd/ubi/attach.c1
-rw-r--r--drivers/mtd/ubi/build.c3
-rw-r--r--drivers/mtd/ubi/part.c2
-rw-r--r--drivers/mux/Kconfig10
-rw-r--r--drivers/mux/Makefile2
-rw-r--r--drivers/net/Kconfig30
-rw-r--r--drivers/net/Makefile3
-rw-r--r--drivers/net/airoha_eth.c948
-rw-r--r--drivers/net/designware.c294
-rw-r--r--drivers/net/dwc_eth_qos.c16
-rw-r--r--drivers/net/dwc_eth_qos.h2
-rw-r--r--drivers/net/dwc_eth_qos_adi.c103
-rw-r--r--drivers/net/dwc_eth_qos_rockchip.c292
-rw-r--r--drivers/net/e1000.c2
-rw-r--r--drivers/net/phy/micrel_ksz90x1.c169
-rw-r--r--drivers/net/phy/miiphybb.c216
-rw-r--r--drivers/net/ravb.c194
-rw-r--r--drivers/net/sandbox-lwip.c85
-rw-r--r--drivers/net/sandbox.c250
-rw-r--r--drivers/net/sh_eth.c189
-rw-r--r--drivers/net/sun8i_emac.c1
-rw-r--r--drivers/net/ti/am65-cpsw-nuss.c2
-rw-r--r--drivers/nvme/Makefile2
-rw-r--r--drivers/pci/Kconfig10
-rw-r--r--drivers/pci/pci_auto.c19
-rw-r--r--drivers/pci/pcie_cdns_ti.c11
-rw-r--r--drivers/pci/pcie_xilinx.c53
-rw-r--r--drivers/phy/Kconfig5
-rw-r--r--drivers/phy/Makefile7
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c96
-rw-r--r--drivers/phy/cadence/Makefile4
-rw-r--r--drivers/phy/phy-rcar-gen3.c79
-rw-r--r--drivers/phy/qcom/phy-qcom-qusb2.c44
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c63
-rw-r--r--drivers/phy/starfive/Kconfig21
-rw-r--r--drivers/phy/starfive/Makefile7
-rw-r--r--drivers/phy/starfive/phy-jh7110-pcie.c239
-rw-r--r--drivers/phy/starfive/phy-jh7110-usb-syscon.h9
-rw-r--r--drivers/phy/starfive/phy-jh7110-usb2.c162
-rw-r--r--drivers/phy/ti/Makefile2
-rw-r--r--drivers/pinctrl/Kconfig8
-rw-r--r--drivers/pinctrl/Makefile10
-rw-r--r--drivers/pinctrl/intel/Kconfig3
-rw-r--r--drivers/pinctrl/pinctrl-adi-adsp.c161
-rw-r--r--drivers/pinctrl/pinctrl-at91.c52
-rw-r--r--drivers/pinctrl/qcom/Kconfig51
-rw-r--r--drivers/pinctrl/qcom/Makefile4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-apq8016.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-apq8096.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq4019.c3
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq9574.c226
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcm2290.c2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcom.c72
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcom.h3
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qcs404.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sa8775p.c623
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sc7280.c106
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm660.c226
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sdm845.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm6115.c2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8150.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8250.c2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8550.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8650.c4
-rw-r--r--drivers/pinctrl/qcom/pinctrl-x1e80100.c4
-rw-r--r--drivers/pinctrl/rockchip/Makefile2
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3528.c273
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rk3576.c278
-rw-r--r--drivers/pinctrl/rockchip/pinctrl-rockchip.h3
-rw-r--r--drivers/pinctrl/sunxi/Kconfig10
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c47
-rw-r--r--drivers/pinctrl/tegra/funcmux-tegra20.c9
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra.c159
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra20.c18
-rw-r--r--drivers/power/Kconfig25
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/axp_spl.c17
-rw-r--r--drivers/power/domain/Kconfig7
-rw-r--r--drivers/power/domain/Makefile3
-rw-r--r--drivers/power/domain/imx8m-power-domain.c17
-rw-r--r--drivers/power/domain/imx8mp-mediamix.c208
-rw-r--r--drivers/power/domain/ti-power-domain.c6
-rw-r--r--drivers/power/pmic/Kconfig10
-rw-r--r--drivers/power/pmic/Makefile31
-rw-r--r--drivers/power/pmic/axp.c1
-rw-r--r--drivers/power/pmic/cpcap.c125
-rw-r--r--drivers/power/regulator/Kconfig31
-rw-r--r--drivers/power/regulator/Makefile50
-rw-r--r--drivers/power/regulator/axp_drivevbus.c57
-rw-r--r--drivers/power/regulator/cpcap_regulator.c275
-rw-r--r--drivers/power/regulator/qcom-rpmh-regulator.c45
-rw-r--r--drivers/power/regulator/qcom_usb_vbus_regulator.c111
-rw-r--r--drivers/power/regulator/rzg2l-usbphy-regulator.c42
-rw-r--r--drivers/power/regulator/scmi_regulator.c9
-rw-r--r--drivers/pwm/Kconfig8
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-mtk.c16
-rw-r--r--drivers/pwm/pwm-stm32.c205
-rw-r--r--drivers/ram/Kconfig2
-rw-r--r--drivers/ram/renesas/dbsc5/dram.c92
-rw-r--r--drivers/ram/rockchip/Makefile2
-rw-r--r--drivers/ram/rockchip/sdram_rk3528.c33
-rw-r--r--drivers/ram/rockchip/sdram_rk3576.c35
-rw-r--r--drivers/remoteproc/Kconfig22
-rw-r--r--drivers/remoteproc/Makefile4
-rw-r--r--drivers/remoteproc/adi_sc5xx_rproc.c277
-rw-r--r--drivers/remoteproc/ti_k3_dsp_rproc.c36
-rw-r--r--drivers/remoteproc/ti_k3_m4_rproc.c371
-rw-r--r--drivers/remoteproc/ti_k3_r5f_rproc.c2
-rw-r--r--drivers/reset/Kconfig23
-rw-r--r--drivers/reset/Makefile5
-rw-r--r--drivers/reset/reset-airoha.c173
-rw-r--r--drivers/reset/reset-rzg2l-usbphy-ctrl.c142
-rw-r--r--drivers/reset/reset-socfpga.c3
-rw-r--r--drivers/reset/reset-spacemit-k1.c548
-rw-r--r--drivers/reset/rst-rk3528.c302
-rw-r--r--drivers/reset/rst-rk3576.c647
-rw-r--r--drivers/rng/msm_rng.c13
-rw-r--r--drivers/rng/rockchip_rng.c79
-rw-r--r--drivers/scsi/scsi.c117
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile5
-rw-r--r--drivers/serial/ns16550.c16
-rw-r--r--drivers/serial/sandbox.c10
-rw-r--r--drivers/serial/serial_mxc.c11
-rw-r--r--drivers/serial/serial_stm32.c18
-rw-r--r--drivers/serial/usbtty.c983
-rw-r--r--drivers/serial/usbtty.h49
-rw-r--r--drivers/soc/soc_ti_k3.c16
-rw-r--r--drivers/spi/Kconfig20
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/adi_spi3.c679
-rw-r--r--drivers/spi/airoha_snfi_spi.c718
-rw-r--r--drivers/spi/atmel-quadspi.c294
-rw-r--r--drivers/spi/cadence_ospi_versal.c19
-rw-r--r--drivers/spi/cadence_qspi.c40
-rw-r--r--drivers/spi/cadence_qspi.h10
-rw-r--r--drivers/spi/soft_spi.c19
-rw-r--r--drivers/spi/tegra20_slink.c18
-rw-r--r--drivers/spmi/spmi-msm.c59
-rw-r--r--drivers/sysinfo/Kconfig7
-rw-r--r--drivers/sysinfo/Makefile1
-rw-r--r--drivers/sysinfo/iot2050.c202
-rw-r--r--drivers/sysinfo/iot2050.h14
-rw-r--r--drivers/sysinfo/sysinfo-uclass.c29
-rw-r--r--drivers/sysreset/Kconfig46
-rw-r--r--drivers/sysreset/Makefile4
-rw-r--r--drivers/timer/Makefile8
-rw-r--r--drivers/timer/sandbox_timer.c5
-rw-r--r--drivers/tpm/cr50_i2c.c4
-rw-r--r--drivers/ufs/ufs-amd-versal2.c66
-rw-r--r--drivers/ufs/ufs.c44
-rw-r--r--drivers/ufs/ufs.h335
-rw-r--r--drivers/ufs/ufshci.h469
-rw-r--r--drivers/ufs/unipro.h128
-rw-r--r--drivers/usb/cdns3/Kconfig7
-rw-r--r--drivers/usb/cdns3/Makefile5
-rw-r--r--drivers/usb/cdns3/cdns3-starfive.c182
-rw-r--r--drivers/usb/cdns3/core.c3
-rw-r--r--drivers/usb/cdns3/drd.c14
-rw-r--r--drivers/usb/common/Makefile2
-rw-r--r--drivers/usb/dwc3/Makefile4
-rw-r--r--drivers/usb/dwc3/gadget.c2
-rw-r--r--drivers/usb/emul/sandbox_keyb.c8
-rw-r--r--drivers/usb/gadget/Kconfig1
-rw-r--r--drivers/usb/gadget/Makefile4
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c1
-rw-r--r--drivers/usb/gadget/core.c621
-rw-r--r--drivers/usb/gadget/ep0.c619
-rw-r--r--drivers/usb/gadget/f_acm.c4
-rw-r--r--drivers/usb/gadget/f_mass_storage.c18
-rw-r--r--drivers/usb/gadget/f_thor.c1
-rw-r--r--drivers/usb/gadget/udc/Makefile4
-rw-r--r--drivers/usb/host/Makefile2
-rw-r--r--drivers/usb/host/ohci-hcd.c3
-rw-r--r--drivers/usb/host/usb-uclass.c190
-rw-r--r--drivers/usb/host/xhci.c2
-rw-r--r--drivers/usb/musb-new/Kconfig7
-rw-r--r--drivers/usb/musb-new/Makefile1
-rw-r--r--drivers/usb/musb-new/sc5xx.c202
-rw-r--r--drivers/video/Kconfig75
-rw-r--r--drivers/video/Makefile11
-rw-r--r--drivers/video/bridge/Kconfig15
-rw-r--r--drivers/video/bridge/Makefile1
-rw-r--r--drivers/video/bridge/dp501.c27
-rw-r--r--drivers/video/bridge/lvds-codec.c128
-rw-r--r--drivers/video/bridge/ssd2825.c159
-rw-r--r--drivers/video/bridge/tc358768.c101
-rw-r--r--drivers/video/bridge/video-bridge-uclass.c11
-rw-r--r--drivers/video/endeavoru-panel.c21
-rw-r--r--drivers/video/imx/Kconfig9
-rw-r--r--drivers/video/imx/Makefile4
-rw-r--r--drivers/video/imx/lcdif.c314
-rw-r--r--drivers/video/imx/ldb.c251
-rw-r--r--drivers/video/lg-ld070wx3.c2
-rw-r--r--drivers/video/lm3532_backlight.c380
-rw-r--r--drivers/video/lm3533_backlight.c142
-rw-r--r--drivers/video/lp855x_backlight.c302
-rw-r--r--drivers/video/mot-panel.c308
-rw-r--r--drivers/video/renesas-r61307.c13
-rw-r--r--drivers/video/renesas-r69328.c37
-rw-r--r--drivers/video/samsung-ltl106hl02.c2
-rw-r--r--drivers/video/sharp-lq079l1sx01.c288
-rw-r--r--drivers/video/sharp-lq101r1sx01.c1
-rw-r--r--drivers/video/tegra/Kconfig51
-rw-r--r--drivers/video/tegra/Makefile9
-rw-r--r--drivers/video/tegra/TODO5
-rw-r--r--drivers/video/tegra/dc-pwm-backlight.c (renamed from drivers/video/tegra20/tegra-pwm-backlight.c)14
-rw-r--r--drivers/video/tegra/dc.c (renamed from drivers/video/tegra20/tegra-dc.c)328
-rw-r--r--drivers/video/tegra/dc.h (renamed from drivers/video/tegra20/tegra-dc.h)3
-rw-r--r--drivers/video/tegra/dsi.c (renamed from drivers/video/tegra20/tegra-dsi.c)248
-rw-r--r--drivers/video/tegra/dsi.h (renamed from drivers/video/tegra20/tegra-dsi.h)0
-rw-r--r--drivers/video/tegra/hdmi.c623
-rw-r--r--drivers/video/tegra/hdmi.h648
-rw-r--r--drivers/video/tegra/host1x.c86
-rw-r--r--drivers/video/tegra/mipi-phy.c (renamed from drivers/video/tegra20/mipi-phy.c)0
-rw-r--r--drivers/video/tegra/mipi-phy.h (renamed from drivers/video/tegra20/mipi-phy.h)0
-rw-r--r--drivers/video/tegra/mipi.c (renamed from drivers/video/tegra20/tegra-mipi.c)134
-rw-r--r--drivers/video/tegra/tegra124/Makefile (renamed from drivers/video/tegra124/Makefile)0
-rw-r--r--drivers/video/tegra/tegra124/display.c (renamed from drivers/video/tegra124/display.c)0
-rw-r--r--drivers/video/tegra/tegra124/displayport.h (renamed from drivers/video/tegra124/displayport.h)0
-rw-r--r--drivers/video/tegra/tegra124/dp.c (renamed from drivers/video/tegra124/dp.c)0
-rw-r--r--drivers/video/tegra/tegra124/sor.c (renamed from drivers/video/tegra124/sor.c)0
-rw-r--r--drivers/video/tegra/tegra124/sor.h (renamed from drivers/video/tegra124/sor.h)0
-rw-r--r--drivers/video/tegra20/Kconfig24
-rw-r--r--drivers/video/tegra20/Makefile5
-rw-r--r--drivers/video/ti/tilcdc.c4
-rw-r--r--drivers/video/tidss/Makefile2
-rw-r--r--drivers/video/video-uclass.c2
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/adi_wdt.c143
-rw-r--r--drivers/watchdog/da9063-wdt.c1
-rw-r--r--drivers/watchdog/sunxi_wdt.c11
382 files changed, 30648 insertions, 5556 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/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c
index 7cf9735f60d..1515951403c 100644
--- a/drivers/adc/rockchip-saradc.c
+++ b/drivers/adc/rockchip-saradc.c
@@ -339,6 +339,14 @@ static const struct rockchip_saradc_data rk3399_saradc_data = {
.stop = rockchip_saradc_stop_v1,
};
+static const struct rockchip_saradc_data rk3528_saradc_data = {
+ .num_bits = 10,
+ .num_channels = 4,
+ .clk_rate = 1000000,
+ .channel_data = rockchip_saradc_channel_data_v2,
+ .start_channel = rockchip_saradc_start_channel_v2,
+};
+
static const struct rockchip_saradc_data rk3588_saradc_data = {
.num_bits = 12,
.num_channels = 8,
@@ -354,6 +362,8 @@ static const struct udevice_id rockchip_saradc_ids[] = {
.data = (ulong)&rk3066_tsadc_data },
{ .compatible = "rockchip,rk3399-saradc",
.data = (ulong)&rk3399_saradc_data },
+ { .compatible = "rockchip,rk3528-saradc",
+ .data = (ulong)&rk3528_saradc_data },
{ .compatible = "rockchip,rk3588-saradc",
.data = (ulong)&rk3588_saradc_data },
{ }
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 8058d5ff1c3..38e953ee79c 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -420,7 +420,7 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port,
static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts)
{
- phys_addr_t pa = virt_to_phys((void *)pp->cmd_tbl);
+ phys_addr_t pa = virt_to_phys(pp->cmd_tbl);
pp->cmd_slot->opts = cpu_to_le32(opts);
pp->cmd_slot->status = 0;
@@ -449,7 +449,7 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
{
struct ahci_ioports *pp = &(uc_priv->port[port]);
void __iomem *port_mmio = pp->port_mmio;
- u64 dma_addr;
+ phys_addr_t dma_addr;
u32 port_status;
void __iomem *mem;
@@ -463,7 +463,6 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ);
if (!mem) {
- free(pp);
printf("%s: No mem for table!\n", __func__);
return -ENOMEM;
}
@@ -473,34 +472,32 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
- pp->cmd_slot =
- (struct ahci_cmd_hdr *)(uintptr_t)virt_to_phys((void *)mem);
- debug("cmd_slot = %p\n", pp->cmd_slot);
- mem += (AHCI_CMD_SLOT_SZ + 224);
+ pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
+ mem += AHCI_CMD_SLOT_SZ * AHCI_MAX_CMD_SLOT;
/*
* Second item: Received-FIS area
*/
- pp->rx_fis = virt_to_phys((void *)mem);
+ pp->rx_fis = mem;
mem += AHCI_RX_FIS_SZ;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
- pp->cmd_tbl = virt_to_phys((void *)mem);
- debug("cmd_tbl_dma = %lx\n", pp->cmd_tbl);
+ pp->cmd_tbl = mem;
mem += AHCI_CMD_TBL_HDR;
- pp->cmd_tbl_sg =
- (struct ahci_sg *)(uintptr_t)virt_to_phys((void *)mem);
+ pp->cmd_tbl_sg = (struct ahci_sg *)(mem);
- dma_addr = (ulong)pp->cmd_slot;
- writel_with_flush(dma_addr, port_mmio + PORT_LST_ADDR);
- writel_with_flush(dma_addr >> 32, port_mmio + PORT_LST_ADDR_HI);
- dma_addr = (ulong)pp->rx_fis;
- writel_with_flush(dma_addr, port_mmio + PORT_FIS_ADDR);
- writel_with_flush(dma_addr >> 32, port_mmio + PORT_FIS_ADDR_HI);
+ dma_addr = virt_to_phys(pp->cmd_slot);
+ debug("cmd_slot_dma = 0x%08llx\n", (u64)dma_addr);
+ writel_with_flush(lower_32_bits(dma_addr), port_mmio + PORT_LST_ADDR);
+ writel_with_flush(upper_32_bits(dma_addr), port_mmio + PORT_LST_ADDR_HI);
+ dma_addr = virt_to_phys(pp->rx_fis);
+ debug("rx_fis_dma = 0x%08llx\n", (u64)dma_addr);
+ writel_with_flush(lower_32_bits(dma_addr), port_mmio + PORT_FIS_ADDR);
+ writel_with_flush(upper_32_bits(dma_addr), port_mmio + PORT_FIS_ADDR_HI);
#ifdef CONFIG_SUNXI_AHCI
sunxi_dma_init(port_mmio);
@@ -736,17 +733,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;
@@ -846,6 +832,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/ata/dwc_ahsata.c b/drivers/ata/dwc_ahsata.c
index 203f98edffc..d225289fe6e 100644
--- a/drivers/ata/dwc_ahsata.c
+++ b/drivers/ata/dwc_ahsata.c
@@ -7,6 +7,7 @@
#include <ahci.h>
#include <blk.h>
#include <bootdev.h>
+#include <clk.h>
#include <cpu_func.h>
#include <dm.h>
#include <dwc_ahsata.h>
@@ -19,9 +20,11 @@
#include <sata.h>
#include <asm/cache.h>
#include <asm/io.h>
+#if IS_ENABLED(CONFIG_ARCH_MX5) || IS_ENABLED(CONFIG_ARCH_MX6)
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <asm/mach-imx/sata.h>
+#endif
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/delay.h>
@@ -116,13 +119,12 @@ static int ahci_setup_oobr(struct ahci_uc_priv *uc_priv, int clk)
return 0;
}
-static int ahci_host_init(struct ahci_uc_priv *uc_priv)
+static int ahci_host_init(struct ahci_uc_priv *uc_priv, int clk)
{
u32 tmp, cap_save, num_ports;
int i, j, timeout = 1000;
struct sata_port_regs *port_mmio = NULL;
struct sata_host_regs *host_mmio = uc_priv->mmio_base;
- int clk = mxc_get_clock(MXC_SATA_CLK);
cap_save = readl(&host_mmio->cap);
cap_save |= SATA_HOST_CAP_SSS;
@@ -330,6 +332,7 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port,
{
struct ahci_ioports *pp = &uc_priv->port[port];
struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
+ phys_addr_t pa = virt_to_phys(buf);
u32 sg_count, max_bytes;
int i;
@@ -341,9 +344,8 @@ static int ahci_fill_sg(struct ahci_uc_priv *uc_priv, u8 port,
}
for (i = 0; i < sg_count; i++) {
- ahci_sg->addr =
- cpu_to_le32((u32)buf + i * max_bytes);
- ahci_sg->addr_hi = 0;
+ ahci_sg->addr = cpu_to_le32(lower_32_bits(pa));
+ ahci_sg->addr_hi = cpu_to_le32(upper_32_bits(pa));
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
(buf_len < max_bytes
? (buf_len - 1)
@@ -359,14 +361,14 @@ static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts)
{
struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot +
AHCI_CMD_SLOT_SZ * cmd_slot);
+ phys_addr_t pa = virt_to_phys(pp->cmd_tbl);
memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ);
cmd_hdr->opts = cpu_to_le32(opts);
cmd_hdr->status = 0;
- pp->cmd_slot->tbl_addr = cpu_to_le32((u32)pp->cmd_tbl & 0xffffffff);
+ pp->cmd_slot->tbl_addr = cpu_to_le32(lower_32_bits(pa));
#ifdef CONFIG_PHYS_64BIT
- pp->cmd_slot->tbl_addr_hi =
- cpu_to_le32((u32)(((pp->cmd_tbl) >> 16) >> 16));
+ pp->cmd_slot->tbl_addr_hi = cpu_to_le32(upper_32_bits(pa));
#endif
}
@@ -404,7 +406,7 @@ static int ahci_exec_ata_cmd(struct ahci_uc_priv *uc_priv, u8 port,
}
ahci_fill_cmd_slot(pp, cmd_slot, opts);
- flush_cache((int)(pp->cmd_slot), AHCI_PORT_PRIV_DMA_SZ);
+ flush_cache((ulong)(pp->cmd_slot), AHCI_PORT_PRIV_DMA_SZ);
writel_with_flush(1 << cmd_slot, &port_mmio->ci);
if (waiting_for_cmd_completed((u8 *)&port_mmio->ci, 10000,
@@ -412,8 +414,8 @@ static int ahci_exec_ata_cmd(struct ahci_uc_priv *uc_priv, u8 port,
printf("timeout exit!\n");
return -1;
}
- invalidate_dcache_range((int)(pp->cmd_slot),
- (int)(pp->cmd_slot)+AHCI_PORT_PRIV_DMA_SZ);
+ invalidate_dcache_range((ulong)(pp->cmd_slot),
+ (ulong)(pp->cmd_slot) + AHCI_PORT_PRIV_DMA_SZ);
debug("ahci_exec_ata_cmd: %d byte transferred.\n",
pp->cmd_slot->status);
if (!is_write)
@@ -441,8 +443,9 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
{
struct ahci_ioports *pp = &uc_priv->port[port];
struct sata_port_regs *port_mmio = pp->port_mmio;
+ phys_addr_t dma_addr;
u32 port_status;
- u32 mem;
+ void *mem;
int timeout = 10000000;
debug("Enter start port: %d\n", port);
@@ -453,22 +456,20 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
return -1;
}
- mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024);
+ mem = memalign(2048, AHCI_PORT_PRIV_DMA_SZ);
if (!mem) {
printf("No mem for table!\n");
return -ENOMEM;
}
- mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */
- memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+ memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
- debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot);
- mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS);
+ mem += AHCI_CMD_SLOT_SZ * AHCI_MAX_CMD_SLOT;
/*
* Second item: Received-FIS area, 256-Byte aligned
@@ -481,14 +482,19 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port)
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
- debug("cmd_tbl_dma = 0x%lx\n", pp->cmd_tbl);
-
mem += AHCI_CMD_TBL_HDR;
+ pp->cmd_tbl_sg = (struct ahci_sg *)mem;
writel_with_flush(0x00004444, &port_mmio->dmacr);
- pp->cmd_tbl_sg = (struct ahci_sg *)mem;
- writel_with_flush((u32)pp->cmd_slot, &port_mmio->clb);
- writel_with_flush(pp->rx_fis, &port_mmio->fb);
+ dma_addr = virt_to_phys(pp->cmd_slot);
+ debug("cmd_slot_dma = 0x%08llx\n", (u64)dma_addr);
+ writel_with_flush(lower_32_bits(dma_addr), &port_mmio->clb);
+ writel_with_flush(upper_32_bits(dma_addr), &port_mmio->clbu);
+ dma_addr = virt_to_phys(pp->cmd_slot);
+ debug("rx_fis_slot_dma = 0x%08llx\n", (u64)dma_addr);
+ writel_with_flush(lower_32_bits(dma_addr), &port_mmio->fb);
+ writel_with_flush(upper_32_bits(dma_addr), &port_mmio->fbu);
+
/* Enable FRE */
writel_with_flush((SATA_PORT_CMD_FRE | readl(&port_mmio->cmd)),
@@ -910,17 +916,41 @@ int dwc_ahsata_scan(struct udevice *dev)
int dwc_ahsata_probe(struct udevice *dev)
{
struct ahci_uc_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk_bulk clk_bulk __maybe_unused;
+ struct clk clk __maybe_unused;
+ int sataclk;
int ret;
-#if defined(CONFIG_MX6)
+#if IS_ENABLED(CONFIG_MX6)
setup_sata();
#endif
+#if IS_ENABLED(CONFIG_MX5) || IS_ENABLED(CONFIG_MX6)
+ sataclk = mxc_get_clock(MXC_SATA_CLK);
+#else
+ ret = clk_get_bulk(dev, &clk_bulk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable_bulk(&clk_bulk);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_name(dev, "sata", &clk);
+ if (ret)
+ return ret;
+
+ sataclk = clk_get_rate(&clk);
+#endif
+ if (IS_ERR_VALUE(sataclk)) {
+ log_err("Unable to get SATA clock rate\n");
+ return -EINVAL;
+ }
uc_priv->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NO_ATAPI;
uc_priv->mmio_base = dev_read_addr_ptr(dev);
/* initialize adapter */
- ret = ahci_host_init(uc_priv);
+ ret = ahci_host_init(uc_priv, sataclk);
if (ret)
return ret;
@@ -962,7 +992,6 @@ U_BOOT_DRIVER(dwc_ahsata_blk) = {
.ops = &dwc_ahsata_blk_ops,
};
-#if CONFIG_IS_ENABLED(DWC_AHSATA_AHCI)
struct ahci_ops dwc_ahsata_ahci_ops = {
.port_status = dwc_ahsata_port_status,
.reset = dwc_ahsata_bus_reset,
@@ -970,7 +999,9 @@ struct ahci_ops dwc_ahsata_ahci_ops = {
};
static const struct udevice_id dwc_ahsata_ahci_ids[] = {
+ { .compatible = "fsl,imx53-ahci" },
{ .compatible = "fsl,imx6q-ahci" },
+ { .compatible = "fsl,imx6qp-ahci" },
{ }
};
@@ -981,4 +1012,3 @@ U_BOOT_DRIVER(dwc_ahsata_ahci) = {
.ops = &dwc_ahsata_ahci_ops,
.probe = dwc_ahsata_probe,
};
-#endif
diff --git a/drivers/ata/dwc_ahsata_priv.h b/drivers/ata/dwc_ahsata_priv.h
index 5b0579ae115..0c2cd5446b5 100644
--- a/drivers/ata/dwc_ahsata_priv.h
+++ b/drivers/ata/dwc_ahsata_priv.h
@@ -7,8 +7,6 @@
#ifndef __DWC_AHSATA_PRIV_H__
#define __DWC_AHSATA_PRIV_H__
-#define DWC_AHSATA_MAX_CMD_SLOTS 32
-
/* Max host controller numbers */
#define SATA_HC_MAX_NUM 4
/* Max command queue depth per host controller */
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/block/blkmap.c b/drivers/block/blkmap.c
index 34eed1380dc..473c65b5911 100644
--- a/drivers/block/blkmap.c
+++ b/drivers/block/blkmap.c
@@ -17,6 +17,30 @@
struct blkmap;
/**
+ * define BLKMAP_SLICE_LINEAR - Linear mapping to another block device
+ *
+ * This blkmap slice type is used for mapping to other existing block
+ * devices.
+ */
+#define BLKMAP_SLICE_LINEAR BIT(0)
+
+/**
+ * define BLKMAP_SLICE_MEM - Linear mapping to memory based block device
+ *
+ * This blkmap slice type is used for mapping to memory based block
+ * devices, like ramdisks.
+ */
+#define BLKMAP_SLICE_MEM BIT(1)
+
+/**
+ * define BLKMAP_SLICE_PRESERVE - Preserved blkmap slice
+ *
+ * This blkmap slice is intended to be preserved, and it's
+ * information passed on to a later stage, like OS.
+ */
+#define BLKMAP_SLICE_PRESERVE BIT(2)
+
+/**
* struct blkmap_slice - Region mapped to a blkmap
*
* Common data for a region mapped to a blkmap, specialized by each
@@ -25,12 +49,14 @@ struct blkmap;
* @node: List node used to associate this slice with a blkmap
* @blknr: Start block number of the mapping
* @blkcnt: Number of blocks covered by this mapping
+ * @attr: Attributes of blkmap slice
*/
struct blkmap_slice {
struct list_head node;
lbaint_t blknr;
lbaint_t blkcnt;
+ uint attr;
/**
* @read: - Read from slice
@@ -169,6 +195,7 @@ int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
.slice = {
.blknr = blknr,
.blkcnt = blkcnt,
+ .attr = BLKMAP_SLICE_LINEAR,
.read = blkmap_linear_read,
.write = blkmap_linear_write,
@@ -234,7 +261,7 @@ static void blkmap_mem_destroy(struct blkmap *bm, struct blkmap_slice *bms)
}
int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
- void *addr, bool remapped)
+ void *addr, bool remapped, bool preserve)
{
struct blkmap *bm = dev_get_plat(dev);
struct blkmap_mem *bmm;
@@ -248,6 +275,7 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
.slice = {
.blknr = blknr,
.blkcnt = blkcnt,
+ .attr = BLKMAP_SLICE_MEM,
.read = blkmap_mem_read,
.write = blkmap_mem_write,
@@ -258,6 +286,9 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
.remapped = remapped,
};
+ if (preserve)
+ bmm->slice.attr |= BLKMAP_SLICE_PRESERVE;
+
err = blkmap_slice_add(bm, &bmm->slice);
if (err)
free(bmm);
@@ -268,11 +299,11 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
void *addr)
{
- return __blkmap_map_mem(dev, blknr, blkcnt, addr, false);
+ return __blkmap_map_mem(dev, blknr, blkcnt, addr, false, false);
}
int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
- phys_addr_t paddr)
+ phys_addr_t paddr, bool preserve)
{
struct blkmap *bm = dev_get_plat(dev);
struct blk_desc *bd = dev_get_uclass_plat(bm->blk);
@@ -283,7 +314,7 @@ int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
if (!addr)
return -ENOMEM;
- err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true);
+ err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true, preserve);
if (err)
unmap_sysmem(addr);
@@ -486,6 +517,49 @@ err:
return err;
}
+static bool blkmap_mem_preserve_slice(struct blkmap_slice *bms)
+{
+ return (bms->attr & (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE)) ==
+ (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE);
+}
+
+int blkmap_get_preserved_pmem_slices(int (*cb)(void *ctx, u64 addr,
+ u64 size), void *ctx)
+{
+ int ret;
+ u64 addr, size;
+ struct udevice *dev;
+ struct uclass *uc;
+ struct blkmap *bm;
+ struct blkmap_mem *bmm;
+ struct blkmap_slice *bms;
+ struct blk_desc *bd;
+
+ if (!cb) {
+ log_debug("%s: No callback passed to the function\n", __func__);
+ return 0;
+ }
+
+ uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) {
+ bm = dev_get_plat(dev);
+ bd = dev_get_uclass_plat(bm->blk);
+
+ list_for_each_entry(bms, &bm->slices, node) {
+ if (!blkmap_mem_preserve_slice(bms))
+ continue;
+
+ bmm = container_of(bms, struct blkmap_mem, slice);
+ addr = (u64)(uintptr_t)bmm->addr;
+ size = (u64)bms->blkcnt << bd->log2blksz;
+ ret = cb(ctx, addr, size);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int blkmap_destroy(struct udevice *dev)
{
int err;
diff --git a/drivers/block/blkmap_helper.c b/drivers/block/blkmap_helper.c
index bfba14110d2..2f1bc28ee5d 100644
--- a/drivers/block/blkmap_helper.c
+++ b/drivers/block/blkmap_helper.c
@@ -28,7 +28,7 @@ int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size,
bm = dev_get_plat(bm_dev);
desc = dev_get_uclass_plat(bm->blk);
blknum = image_size >> desc->log2blksz;
- ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr);
+ ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr, true);
if (ret) {
log_err("Unable to map %#llx at block %d : %d\n",
(unsigned long long)image_addr, 0, ret);
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig
index 0080d2a165c..99b6c7534fd 100644
--- a/drivers/bootcount/Kconfig
+++ b/drivers/bootcount/Kconfig
@@ -183,6 +183,9 @@ config BOOTCOUNT_BOOTLIMIT
counter being cleared.
If set to 0, do not set a boot limit in the environment.
+config BOOTCOUNT_ALTBOOTCMD
+ string "Alternative boot command when BOOTLIMIT is reached"
+
config SYS_BOOTCOUNT_SINGLEWORD
bool "Use single word to pack boot count and magic value"
depends on BOOTCOUNT_GENERIC
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/Makefile b/drivers/clk/Makefile
index fe0e49f6112..8411205ee04 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_$(PHASE_)CLK_GPIO) += clk-gpio.o
obj-$(CONFIG_$(PHASE_)CLK_STUB) += clk-stub.o
obj-y += adi/
+obj-y += airoha/
obj-y += analogbits/
obj-y += imx/
obj-$(CONFIG_CLK_JH7110) += starfive/
diff --git a/drivers/clk/airoha/Makefile b/drivers/clk/airoha/Makefile
new file mode 100644
index 00000000000..1744c5f7236
--- /dev/null
+++ b/drivers/clk/airoha/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+# Core
+obj-$(CONFIG_ARCH_AIROHA) += clk-airoha.o
diff --git a/drivers/clk/airoha/clk-airoha.c b/drivers/clk/airoha/clk-airoha.c
new file mode 100644
index 00000000000..1b2c4c98de5
--- /dev/null
+++ b/drivers/clk/airoha/clk-airoha.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based on the Linux clk-en7523.c but majorly reworked
+ * for U-Boot that doesn't require CCF subsystem.
+ *
+ * Major modification, support for set_rate, realtime
+ * get_rate and split for reset part to a different driver.
+ *
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver)
+ * Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/devres.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include <dt-bindings/clock/en7523-clk.h>
+
+#define REG_GSW_CLK_DIV_SEL 0x1b4
+#define REG_EMI_CLK_DIV_SEL 0x1b8
+#define REG_BUS_CLK_DIV_SEL 0x1bc
+#define REG_SPI_CLK_DIV_SEL 0x1c4
+#define REG_SPI_CLK_FREQ_SEL 0x1c8
+#define REG_NPU_CLK_DIV_SEL 0x1fc
+
+#define REG_NP_SCU_PCIC 0x88
+#define REG_NP_SCU_SSTR 0x9c
+#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13)
+#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11)
+#define REG_CRYPTO_CLKSRC2 0x20c
+
+#define EN7581_MAX_CLKS 9
+
+struct airoha_clk_desc {
+ int id;
+ const char *name;
+ u32 base_reg;
+ u8 base_bits;
+ u8 base_shift;
+ union {
+ const unsigned int *base_values;
+ unsigned int base_value;
+ };
+ size_t n_base_values;
+
+ u16 div_reg;
+ u8 div_bits;
+ u8 div_shift;
+ u16 div_val0;
+ u8 div_step;
+ u8 div_offset;
+};
+
+struct airoha_clk_priv {
+ struct regmap *chip_scu_map;
+ struct airoha_clk_soc_data *data;
+};
+
+struct airoha_clk_soc_data {
+ u32 num_clocks;
+ const struct airoha_clk_desc *descs;
+};
+
+static const u32 gsw_base[] = { 400000000, 500000000 };
+static const u32 slic_base[] = { 100000000, 3125000 };
+
+static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 };
+static const u32 bus7581_base[] = { 600000000, 540000000 };
+static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 };
+static const u32 crypto_base[] = { 540000000, 480000000 };
+static const u32 emmc7581_base[] = { 200000000, 150000000 };
+
+static const struct airoha_clk_desc en7581_base_clks[EN7581_MAX_CLKS] = {
+ [EN7523_CLK_GSW] = {
+ .id = EN7523_CLK_GSW,
+ .name = "gsw",
+
+ .base_reg = REG_GSW_CLK_DIV_SEL,
+ .base_bits = 1,
+ .base_shift = 8,
+ .base_values = gsw_base,
+ .n_base_values = ARRAY_SIZE(gsw_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ },
+ [EN7523_CLK_EMI] = {
+ .id = EN7523_CLK_EMI,
+ .name = "emi",
+
+ .base_reg = REG_EMI_CLK_DIV_SEL,
+ .base_bits = 2,
+ .base_shift = 8,
+ .base_values = emi7581_base,
+ .n_base_values = ARRAY_SIZE(emi7581_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ },
+ [EN7523_CLK_BUS] = {
+ .id = EN7523_CLK_BUS,
+ .name = "bus",
+
+ .base_reg = REG_BUS_CLK_DIV_SEL,
+ .base_bits = 1,
+ .base_shift = 8,
+ .base_values = bus7581_base,
+ .n_base_values = ARRAY_SIZE(bus7581_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ },
+ [EN7523_CLK_SLIC] = {
+ .id = EN7523_CLK_SLIC,
+ .name = "slic",
+
+ .base_reg = REG_SPI_CLK_FREQ_SEL,
+ .base_bits = 1,
+ .base_shift = 0,
+ .base_values = slic_base,
+ .n_base_values = ARRAY_SIZE(slic_base),
+
+ .div_reg = REG_SPI_CLK_DIV_SEL,
+ .div_bits = 5,
+ .div_shift = 24,
+ .div_val0 = 20,
+ .div_step = 2,
+ },
+ [EN7523_CLK_SPI] = {
+ .id = EN7523_CLK_SPI,
+ .name = "spi",
+
+ .base_reg = REG_SPI_CLK_DIV_SEL,
+
+ .base_value = 400000000,
+
+ .div_bits = 5,
+ .div_shift = 8,
+ .div_val0 = 40,
+ .div_step = 2,
+ },
+ [EN7523_CLK_NPU] = {
+ .id = EN7523_CLK_NPU,
+ .name = "npu",
+
+ .base_reg = REG_NPU_CLK_DIV_SEL,
+ .base_bits = 2,
+ .base_shift = 8,
+ .base_values = npu7581_base,
+ .n_base_values = ARRAY_SIZE(npu7581_base),
+
+ .div_bits = 3,
+ .div_shift = 0,
+ .div_step = 1,
+ .div_offset = 1,
+ },
+ [EN7523_CLK_CRYPTO] = {
+ .id = EN7523_CLK_CRYPTO,
+ .name = "crypto",
+
+ .base_reg = REG_CRYPTO_CLKSRC2,
+ .base_bits = 1,
+ .base_shift = 0,
+ .base_values = crypto_base,
+ .n_base_values = ARRAY_SIZE(crypto_base),
+ },
+ [EN7581_CLK_EMMC] = {
+ .id = EN7581_CLK_EMMC,
+ .name = "emmc",
+
+ .base_reg = REG_CRYPTO_CLKSRC2,
+ .base_bits = 1,
+ .base_shift = 12,
+ .base_values = emmc7581_base,
+ .n_base_values = ARRAY_SIZE(emmc7581_base),
+ }
+};
+
+static u32 airoha_clk_get_base_rate(const struct airoha_clk_desc *desc, u32 val)
+{
+ if (!desc->base_bits)
+ return desc->base_value;
+
+ val >>= desc->base_shift;
+ val &= (1 << desc->base_bits) - 1;
+
+ if (val >= desc->n_base_values)
+ return 0;
+
+ return desc->base_values[val];
+}
+
+static u32 airoha_clk_get_div(const struct airoha_clk_desc *desc, u32 val)
+{
+ if (!desc->div_bits)
+ return 1;
+
+ val >>= desc->div_shift;
+ val &= (1 << desc->div_bits) - 1;
+
+ if (!val && desc->div_val0)
+ return desc->div_val0;
+
+ return (val + desc->div_offset) * desc->div_step;
+}
+
+static int airoha_clk_enable(struct clk *clk)
+{
+ struct airoha_clk_priv *priv = dev_get_priv(clk->dev);
+ struct airoha_clk_soc_data *data = priv->data;
+ int id = clk->id;
+
+ if (id > data->num_clocks)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int airoha_clk_disable(struct clk *clk)
+{
+ return 0;
+}
+
+static ulong airoha_clk_get_rate(struct clk *clk)
+{
+ struct airoha_clk_priv *priv = dev_get_priv(clk->dev);
+ struct airoha_clk_soc_data *data = priv->data;
+ const struct airoha_clk_desc *desc;
+ struct regmap *map = priv->chip_scu_map;
+ int id = clk->id;
+ u32 reg, val;
+ ulong rate;
+ int ret;
+
+ if (id > data->num_clocks) {
+ dev_err(clk->dev, "Invalid clk ID %d\n", id);
+ return 0;
+ }
+
+ desc = &data->descs[id];
+
+ ret = regmap_read(map, desc->base_reg, &val);
+ if (ret) {
+ dev_err(clk->dev, "Failed to read reg for clock %s\n",
+ desc->name);
+ return 0;
+ }
+
+ rate = airoha_clk_get_base_rate(desc, val);
+
+ reg = desc->div_reg ? desc->div_reg : desc->base_reg;
+ ret = regmap_read(map, reg, &val);
+ if (ret) {
+ dev_err(clk->dev, "Failed to read reg for clock %s\n",
+ desc->name);
+ return 0;
+ }
+
+ rate /= airoha_clk_get_div(desc, val);
+
+ return rate;
+}
+
+static int airoha_clk_search_rate(const struct airoha_clk_desc *desc, int div,
+ ulong rate)
+{
+ int i;
+
+ /* Single base rate */
+ if (!desc->base_bits) {
+ if (rate != desc->base_value / div)
+ goto err;
+
+ return 0;
+ }
+
+ /* Check every base rate with provided divisor */
+ for (i = 0; i < desc->n_base_values; i++)
+ if (rate == desc->base_values[i] / div)
+ return i;
+
+err:
+ return -EINVAL;
+}
+
+static ulong airoha_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct airoha_clk_priv *priv = dev_get_priv(clk->dev);
+ struct airoha_clk_soc_data *data = priv->data;
+ const struct airoha_clk_desc *desc;
+ struct regmap *map = priv->chip_scu_map;
+ int div_val, base_val;
+ u32 reg, val, mask;
+ int id = clk->id;
+ int div;
+ int ret;
+
+ if (id > data->num_clocks) {
+ dev_err(clk->dev, "Invalid clk ID %d\n", id);
+ return 0;
+ }
+
+ desc = &data->descs[id];
+
+ if (!desc->base_bits && !desc->div_bits) {
+ dev_err(clk->dev, "Can't set rate for fixed clock %s\n",
+ desc->name);
+ return 0;
+ }
+
+ if (!desc->div_bits) {
+ /* Divisor not supported, just search in base rate */
+ div_val = 0;
+ base_val = airoha_clk_search_rate(desc, 1, rate);
+ if (base_val < 0) {
+ dev_err(clk->dev, "Invalid rate for clock %s\n",
+ desc->name);
+ return 0;
+ }
+ } else {
+ div_val = 0;
+
+ /* Check if div0 satisfy the request */
+ if (desc->div_val0) {
+ base_val = airoha_clk_search_rate(desc, desc->div_val0,
+ rate);
+ if (base_val >= 0) {
+ div_val = 0;
+ goto apply;
+ }
+
+ /* Skip checking first divisor val */
+ div_val = 1;
+ }
+
+ /* Simulate rate with every divisor supported */
+ for (div_val = div_val + desc->div_offset;
+ div_val < BIT(desc->div_bits) - 1; div_val++) {
+ div = div_val * desc->div_step;
+
+ base_val = airoha_clk_search_rate(desc, div, rate);
+ if (base_val >= 0)
+ break;
+ }
+
+ if (div_val == BIT(desc->div_bits) - 1) {
+ dev_err(clk->dev, "Invalid rate for clock %s\n",
+ desc->name);
+ return 0;
+ }
+ }
+
+apply:
+ if (desc->div_bits) {
+ reg = desc->div_reg ? desc->div_reg : desc->base_reg;
+
+ mask = (BIT(desc->div_bits) - 1) << desc->div_shift;
+ val = div_val << desc->div_shift;
+
+ ret = regmap_update_bits(map, reg, mask, val);
+ if (ret) {
+ dev_err(clk->dev, "Failed to update div reg for clock %s\n",
+ desc->name);
+ return 0;
+ }
+ }
+
+ if (desc->base_bits) {
+ mask = (BIT(desc->base_bits) - 1) << desc->base_shift;
+ val = base_val << desc->base_shift;
+
+ ret = regmap_update_bits(map, desc->base_reg, mask, val);
+ if (ret) {
+ dev_err(clk->dev, "Failed to update reg for clock %s\n",
+ desc->name);
+ return 0;
+ }
+ }
+
+ return rate;
+}
+
+const struct clk_ops airoha_clk_ops = {
+ .enable = airoha_clk_enable,
+ .disable = airoha_clk_disable,
+ .get_rate = airoha_clk_get_rate,
+ .set_rate = airoha_clk_set_rate,
+};
+
+static int airoha_clk_probe(struct udevice *dev)
+{
+ struct airoha_clk_priv *priv = dev_get_priv(dev);
+ ofnode chip_scu_node;
+
+ chip_scu_node = ofnode_by_compatible(ofnode_null(),
+ "airoha,en7581-chip-scu");
+ if (!ofnode_valid(chip_scu_node))
+ return -EINVAL;
+
+ priv->chip_scu_map = syscon_node_to_regmap(chip_scu_node);
+ if (IS_ERR(priv->chip_scu_map))
+ return PTR_ERR(priv->chip_scu_map);
+
+ priv->data = (void *)dev_get_driver_data(dev);
+
+ return 0;
+}
+
+static int airoha_clk_bind(struct udevice *dev)
+{
+ struct udevice *rst_dev;
+ int ret = 0;
+
+ if (CONFIG_IS_ENABLED(RESET_AIROHA)) {
+ ret = device_bind_driver_to_node(dev, "airoha-reset", "reset",
+ dev_ofnode(dev), &rst_dev);
+ if (ret)
+ debug("Warning: failed to bind reset controller\n");
+ }
+
+ return ret;
+}
+
+static const struct airoha_clk_soc_data en7581_data = {
+ .num_clocks = ARRAY_SIZE(en7581_base_clks),
+ .descs = en7581_base_clks,
+};
+
+static const struct udevice_id airoha_clk_ids[] = {
+ { .compatible = "airoha,en7581-scu",
+ .data = (ulong)&en7581_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(airoha_clk) = {
+ .name = "clk-airoha",
+ .id = UCLASS_CLK,
+ .of_match = airoha_clk_ids,
+ .probe = airoha_clk_probe,
+ .bind = airoha_clk_bind,
+ .priv_auto = sizeof(struct airoha_clk_priv),
+ .ops = &airoha_clk_ops,
+};
diff --git a/drivers/clk/altera/clk-agilex5.c b/drivers/clk/altera/clk-agilex5.c
index 716c71598bc..fb1e72ffc5c 100644
--- a/drivers/clk/altera/clk-agilex5.c
+++ b/drivers/clk/altera/clk-agilex5.c
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2024 Intel Corporation <www.intel.com>
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
*/
-#include <clk-uclass.h>
#include <config.h>
-#include <errno.h>
-#include <dm.h>
#include <log.h>
+#include <dm.h>
+#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
@@ -23,9 +23,14 @@
#include <linux/types.h>
#include <asm/arch/clock_manager.h>
#include <dt-bindings/clock/agilex5-clock.h>
+#include <wait_bit.h>
+#include <clk-uclass.h>
DECLARE_GLOBAL_DATA_PTR;
+#define CLKMGR_CTRL_SWCTRLBTCLKEN_MASK BIT(8)
+#define CLKMGR_CTRL_SWCTRLBTCLKSEL_MASK BIT(9)
+
struct socfpga_clk_plat {
void __iomem *regs;
};
@@ -36,21 +41,30 @@ struct socfpga_clk_plat {
*/
static void clk_write_bypass_mainpll(struct socfpga_clk_plat *plat, u32 val)
{
+ uintptr_t base_addr = (uintptr_t)plat->regs;
+
CM_REG_WRITEL(plat, val, CLKMGR_MAINPLL_BYPASS);
- cm_wait_for_fsm();
+ wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY,
+ false, 20000, false);
}
static void clk_write_bypass_perpll(struct socfpga_clk_plat *plat, u32 val)
{
+ uintptr_t base_addr = (uintptr_t)plat->regs;
+
CM_REG_WRITEL(plat, val, CLKMGR_PERPLL_BYPASS);
- cm_wait_for_fsm();
+ wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY,
+ false, 20000, false);
}
/* function to write the ctrl register which requires a poll of the busy bit */
static void clk_write_ctrl(struct socfpga_clk_plat *plat, u32 val)
{
+ uintptr_t base_addr = (uintptr_t)plat->regs;
+
CM_REG_WRITEL(plat, val, CLKMGR_CTRL);
- cm_wait_for_fsm();
+ wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY,
+ false, 20000, false);
}
static const struct {
@@ -59,15 +73,6 @@ static const struct {
u32 mask;
} membus_pll[] = {
{
- MEMBUS_CLKSLICE_REG,
- /*
- * BIT[7:7]
- * Enable source synchronous mode
- */
- BIT(7),
- BIT(7)
- },
- {
MEMBUS_SYNTHCALFOSC_INIT_CENTERFREQ_REG,
/*
* BIT[0:0]
@@ -238,6 +243,7 @@ static void clk_basic_init(struct udevice *dev,
{
struct socfpga_clk_plat *plat = dev_get_plat(dev);
u32 vcocalib;
+ uintptr_t base_addr = (uintptr_t)plat->regs;
if (!cfg)
return;
@@ -249,7 +255,8 @@ static void clk_basic_init(struct udevice *dev,
CM_REG_SETBITS(plat, CLKMGR_PERPLL_PLLGLOB,
CLKMGR_PLLGLOB_PD_MASK | CLKMGR_PLLGLOB_RST_MASK);
- cm_wait_for_lock(CLKMGR_STAT_ALLPLL_LOCKED_MASK);
+ wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT),
+ CLKMGR_STAT_ALLPLL_LOCKED_MASK, true, 20000, false);
/* Put both PLLs in bypass */
clk_write_bypass_mainpll(plat, CLKMGR_BYPASS_MAINPLL_ALL);
@@ -264,9 +271,14 @@ static void clk_basic_init(struct udevice *dev,
CM_REG_READL(plat, CLKMGR_CTRL) & ~CLKMGR_CTRL_BOOTMODE);
} else {
#ifdef CONFIG_XPL_BUILD
- /* Always force clock manager into boot mode before any configuration */
- clk_write_ctrl(plat,
- CM_REG_READL(plat, CLKMGR_CTRL) | CLKMGR_CTRL_BOOTMODE);
+ /*
+ * Configure HPS Internal Oscillator as default boot_clk source,
+ * always force clock manager into boot mode before any configuration
+ */
+ clk_write_ctrl(plat, CM_REG_READL(plat, CLKMGR_CTRL) |
+ CLKMGR_CTRL_BOOTMODE |
+ CLKMGR_CTRL_SWCTRLBTCLKEN_MASK |
+ CLKMGR_CTRL_SWCTRLBTCLKSEL_MASK);
#else
/* Skip clock configuration in SSBL if it's not in boot mode */
if (!(CM_REG_READL(plat, CLKMGR_CTRL) & CLKMGR_CTRL_BOOTMODE))
@@ -365,7 +377,8 @@ static void clk_basic_init(struct udevice *dev,
CLKMGR_PLLCX_EN_SET_MSK,
CLKMGR_PERPLL_PLLC3);
- cm_wait_for_lock(CLKMGR_STAT_ALLPLL_LOCKED_MASK);
+ wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT),
+ CLKMGR_STAT_ALLPLL_LOCKED_MASK, true, 20000, false);
CM_REG_WRITEL(plat, CLKMGR_LOSTLOCK_SET_MASK, CLKMGR_MAINPLL_LOSTLOCK);
CM_REG_WRITEL(plat, CLKMGR_LOSTLOCK_SET_MASK, CLKMGR_PERPLL_LOSTLOCK);
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 199ca6eaa37..9e3b5191767 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -97,7 +97,7 @@ static int clk_composite_disable(struct clk *clk)
return 0;
}
-struct clk *clk_register_composite(struct device *dev, const char *name,
+struct clk *clk_register_composite(struct udevice *dev, const char *name,
const char * const *parent_names,
int num_parents, struct clk *mux,
const struct clk_ops *mux_ops,
@@ -149,7 +149,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
clk = &composite->clk;
clk->flags = flags;
ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name,
- parent_names[clk_composite_get_parent(clk)]);
+ clk_resolve_parent_clk(dev, parent_names[clk_composite_get_parent(clk)]));
if (ret) {
clk = ERR_PTR(ret);
goto err;
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index aa210e3d15f..e692b9c2167 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -183,7 +183,7 @@ const struct clk_ops clk_divider_ops = {
.set_rate = clk_divider_set_rate,
};
-static struct clk *_register_divider(struct device *dev, const char *name,
+static struct clk *_register_divider(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, const struct clk_div_table *table)
@@ -218,7 +218,8 @@ static struct clk *_register_divider(struct device *dev, const char *name,
clk = &div->clk;
clk->flags = flags;
- ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
+ ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name,
+ clk_resolve_parent_clk(dev, parent_name));
if (ret) {
kfree(div);
return ERR_PTR(ret);
@@ -227,7 +228,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
return clk;
}
-struct clk *clk_register_divider(struct device *dev, const char *name,
+struct clk *clk_register_divider(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags)
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index 068798cf9b0..4eb8be728e6 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -37,7 +37,7 @@ const struct clk_ops ccf_clk_fixed_factor_ops = {
.get_rate = clk_factor_recalc_rate,
};
-struct clk *clk_hw_register_fixed_factor(struct device *dev,
+struct clk *clk_hw_register_fixed_factor(struct udevice *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
@@ -56,7 +56,7 @@ struct clk *clk_hw_register_fixed_factor(struct device *dev,
clk->flags = flags;
ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name,
- parent_name);
+ clk_resolve_parent_clk(dev, parent_name));
if (ret) {
kfree(fix);
return ERR_PTR(ret);
@@ -65,7 +65,7 @@ struct clk *clk_hw_register_fixed_factor(struct device *dev,
return clk;
}
-struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+struct clk *clk_register_fixed_factor(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index bf1c6a93b46..256ff108991 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -117,7 +117,7 @@ const struct clk_ops clk_gate_ops = {
.get_rate = clk_generic_get_rate,
};
-struct clk *clk_register_gate(struct device *dev, const char *name,
+struct clk *clk_register_gate(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock)
@@ -149,7 +149,8 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
clk = &gate->clk;
clk->flags = flags;
- ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name);
+ ret = clk_register(clk, UBOOT_DM_CLK_GATE, name,
+ clk_resolve_parent_clk(dev, parent_name));
if (ret) {
kfree(gate);
return ERR_PTR(ret);
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 62477e15d27..d7411f8f282 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -113,6 +113,11 @@ int clk_mux_fetch_parent_index(struct clk *clk, struct clk *parent)
for (i = 0; i < mux->num_parents; i++) {
if (!strcmp(parent->dev->name, mux->parent_names[i]))
return i;
+ if (!strcmp(parent->dev->name,
+ clk_resolve_parent_clk(clk->dev,
+ mux->parent_names[i])))
+ return i;
+
}
return -EINVAL;
@@ -159,15 +164,15 @@ const struct clk_ops clk_mux_ops = {
.set_parent = clk_mux_set_parent,
};
-struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
+struct clk *clk_register_mux(struct udevice *dev, const char *name,
const char * const *parent_names, u8 num_parents,
unsigned long flags,
- void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table)
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags)
{
+ u32 mask = BIT(width) - 1;
struct clk_mux *mux;
struct clk *clk;
- u8 width = 0;
int ret;
if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
@@ -192,7 +197,7 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
mux->shift = shift;
mux->mask = mask;
mux->flags = clk_mux_flags;
- mux->table = table;
+ mux->table = NULL;
#if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF)
mux->io_mux_val = *(u32 *)reg;
#endif
@@ -207,7 +212,8 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
* for the corresponding clock (to do that define .set_parent() method).
*/
ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name,
- parent_names[clk_mux_get_parent(clk)]);
+ clk_resolve_parent_clk(dev,
+ parent_names[clk_mux_get_parent(clk)]));
if (ret) {
kfree(mux);
return ERR_PTR(ret);
@@ -216,35 +222,6 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
return clk;
}
-struct clk *clk_register_mux_table(struct device *dev, const char *name,
- const char * const *parent_names, u8 num_parents,
- unsigned long flags,
- void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table)
-{
- struct clk *clk;
-
- clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
- flags, reg, shift, mask, clk_mux_flags,
- table);
- if (IS_ERR(clk))
- return ERR_CAST(clk);
- return clk;
-}
-
-struct clk *clk_register_mux(struct device *dev, const char *name,
- const char * const *parent_names, u8 num_parents,
- unsigned long flags,
- void __iomem *reg, u8 shift, u8 width,
- u8 clk_mux_flags)
-{
- u32 mask = BIT(width) - 1;
-
- return clk_register_mux_table(dev, name, parent_names, num_parents,
- flags, reg, shift, mask, clk_mux_flags,
- NULL);
-}
-
U_BOOT_DRIVER(ccf_clk_mux) = {
.name = UBOOT_DM_CLK_CCF_MUX,
.id = UCLASS_CLK,
diff --git a/drivers/clk/clk-stub.c b/drivers/clk/clk-stub.c
index 5fbbb07b7f7..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,8 @@ 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" },
{ .compatible = "qcom,sm8550-rpmh-clk" },
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 353ae476068..4b3d812f9c6 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -420,6 +420,24 @@ int clk_get_by_name_nodev(ofnode node, const char *name, struct clk *clk)
return clk_get_by_index_nodev(node, index, clk);
}
+const char *
+clk_resolve_parent_clk(struct udevice *dev, const char *name)
+{
+ struct udevice *parent;
+ struct clk clk;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, name, &parent);
+ if (!ret)
+ return name;
+
+ ret = clk_get_by_name(dev, name, &clk);
+ if (!clk.dev)
+ return name;
+
+ return clk.dev->name;
+}
+
int clk_release_all(struct clk *clk, unsigned int count)
{
unsigned int i;
@@ -605,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/clk_boston.c b/drivers/clk/clk_boston.c
index 030ff7cc58e..71e030f463e 100644
--- a/drivers/clk/clk_boston.c
+++ b/drivers/clk/clk_boston.c
@@ -58,17 +58,21 @@ const struct clk_ops clk_boston_ops = {
.get_rate = clk_boston_get_rate,
};
-static int clk_boston_of_to_plat(struct udevice *dev)
+static int clk_boston_probe(struct udevice *dev)
{
struct clk_boston *state = dev_get_plat(dev);
struct udevice *syscon;
int err;
- err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
- "regmap", &syscon);
- if (err) {
- pr_err("unable to find syscon device\n");
- return err;
+ if (dev->parent && device_get_uclass_id(dev->parent) == UCLASS_SYSCON) {
+ syscon = dev->parent;
+ } else {
+ err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+ "regmap", &syscon);
+ if (err) {
+ pr_err("unable to find syscon device\n");
+ return err;
+ }
}
state->regmap = syscon_get_regmap(syscon);
@@ -91,7 +95,8 @@ U_BOOT_DRIVER(clk_boston) = {
.name = "boston_clock",
.id = UCLASS_CLK,
.of_match = clk_boston_match,
- .of_to_plat = clk_boston_of_to_plat,
+ .probe = clk_boston_probe,
.plat_auto = sizeof(struct clk_boston),
.ops = &clk_boston_ops,
+ .flags = DM_FLAG_PRE_RELOC,
};
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index 56d893e0579..d17a54fb9b3 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -60,6 +60,7 @@ config SPL_CLK_IMX8MP
depends on ARCH_IMX8M && SPL
select SPL_CLK
select SPL_CLK_CCF
+ select SPL_CLK_COMPOSITE_CCF
help
This enables SPL DM/DTS support for clock driver in i.MX8MP
diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c
index 64bffa3b181..14c5b92939c 100644
--- a/drivers/clk/imx/clk-composite-8m.c
+++ b/drivers/clk/imx/clk-composite-8m.c
@@ -151,7 +151,7 @@ const struct clk_ops imx8m_clk_mux_ops = {
.set_parent = imx8m_clk_mux_set_parent,
};
-struct clk *imx8m_clk_composite_flags(const char *name,
+struct clk *imx8m_clk_composite_flags(struct udevice *dev, const char *name,
const char * const *parent_names,
int num_parents, void __iomem *reg,
unsigned long flags)
@@ -187,7 +187,7 @@ struct clk *imx8m_clk_composite_flags(const char *name,
gate->reg = reg;
gate->bit_idx = PCG_CGC_SHIFT;
- clk = clk_register_composite(NULL, name,
+ clk = clk_register_composite(dev, name,
parent_names, num_parents,
&mux->clk, &imx8m_clk_mux_ops, &div->clk,
&imx8m_clk_composite_divider_ops,
diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c
index 65fa6b5b139..fa07b13249b 100644
--- a/drivers/clk/imx/clk-gate2.c
+++ b/drivers/clk/imx/clk-gate2.c
@@ -90,7 +90,7 @@ static const struct clk_ops clk_gate2_ops = {
.get_rate = clk_generic_get_rate,
};
-struct clk *clk_register_gate2(struct device *dev, const char *name,
+struct clk *clk_register_gate2(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 cgr_val,
u8 clk_gate2_flags, unsigned int *share_count)
@@ -111,7 +111,8 @@ struct clk *clk_register_gate2(struct device *dev, const char *name,
clk = &gate->clk;
- ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name);
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name,
+ clk_resolve_parent_clk(dev, parent_name));
if (ret) {
kfree(gate);
return ERR_PTR(ret);
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index df9f0285e1e..13239f2f64d 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -35,6 +35,8 @@ static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
static const char *const periph_sels[] = { "periph_pre", "periph_clk2", };
static const char *const periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m",
"pll2_pfd0_352m", "pll2_198m", };
+static const char *const uart_sels[] = { "pll3_80m", "osc", };
+static const char *const ecspi_sels[] = { "pll3_60m", "osc", };
static int imx6q_clk_probe(struct udevice *dev)
{
@@ -44,21 +46,21 @@ static int imx6q_clk_probe(struct udevice *dev)
base = (void *)ANATOP_BASE_ADDR;
clk_dm(IMX6QDL_CLK_PLL2,
- imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_bus", "osc",
base + 0x30, 0x1));
clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
- imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc",
base + 0x10, 0x3));
clk_dm(IMX6QDL_CLK_PLL3_60M,
- imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
+ imx_clk_fixed_factor(dev, "pll3_60m", "pll3_usb_otg", 1, 8));
clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
clk_dm(IMX6QDL_CLK_PLL6,
- imx_clk_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3));
+ imx_clk_pllv3(dev, IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3));
clk_dm(IMX6QDL_CLK_PLL6_ENET,
- imx_clk_gate("pll6_enet", "pll6", base + 0xe0, 13));
+ imx_clk_gate(dev, "pll6_enet", "pll6", base + 0xe0, 13));
/* CCM clocks */
base = dev_read_addr_ptr(dev);
@@ -66,76 +68,98 @@ static int imx6q_clk_probe(struct udevice *dev)
return -EINVAL;
clk_dm(IMX6QDL_CLK_USDHC1_SEL,
- imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
+ imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC2_SEL,
- imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
+ imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC3_SEL,
- imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
+ imx_clk_mux(dev, "usdhc3_sel", base + 0x1c, 18, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMX6QDL_CLK_USDHC4_SEL,
- imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
+ imx_clk_mux(dev, "usdhc4_sel", base + 0x1c, 19, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ if (of_machine_is_compatible("fsl,imx6qp")) {
+ clk_dm(IMX6QDL_CLK_UART_SEL,
+ imx_clk_mux(dev, "uart_sel", base + 0x24, 6, 1, uart_sels,
+ ARRAY_SIZE(uart_sels)));
+ clk_dm(IMX6QDL_CLK_ECSPI_SEL,
+ imx_clk_mux(dev, "ecspi_sel", base + 0x38, 18, 1, ecspi_sels,
+ ARRAY_SIZE(ecspi_sels)));
+ }
+
clk_dm(IMX6QDL_CLK_USDHC1_PODF,
- imx_clk_divider("usdhc1_podf", "usdhc1_sel",
+ imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel",
base + 0x24, 11, 3));
clk_dm(IMX6QDL_CLK_USDHC2_PODF,
- imx_clk_divider("usdhc2_podf", "usdhc2_sel",
+ imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel",
base + 0x24, 16, 3));
clk_dm(IMX6QDL_CLK_USDHC3_PODF,
- imx_clk_divider("usdhc3_podf", "usdhc3_sel",
+ imx_clk_divider(dev, "usdhc3_podf", "usdhc3_sel",
base + 0x24, 19, 3));
clk_dm(IMX6QDL_CLK_USDHC4_PODF,
- imx_clk_divider("usdhc4_podf", "usdhc4_sel",
+ imx_clk_divider(dev, "usdhc4_podf", "usdhc4_sel",
base + 0x24, 22, 3));
- clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
- imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
+ if (of_machine_is_compatible("fsl,imx6qp")) {
+ clk_dm(IMX6QDL_CLK_UART_SERIAL_PODF,
+ imx_clk_divider(dev, "uart_serial_podf", "uart_sel", base + 0x24, 0, 6));
+ clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
+ imx_clk_divider(dev, "ecspi_root", "ecspi_sel", base + 0x38, 19, 6));
+ } else {
+ clk_dm(IMX6QDL_CLK_UART_SERIAL_PODF,
+ imx_clk_divider(dev, "uart_serial_podf", "pll3_80m", base + 0x24, 0, 6));
+ clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
+ imx_clk_divider(dev, "ecspi_root", "pll3_60m", base + 0x38, 19, 6));
+ }
clk_dm(IMX6QDL_CLK_ECSPI1,
- imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
+ imx_clk_gate2(dev, "ecspi1", "ecspi_root", base + 0x6c, 0));
clk_dm(IMX6QDL_CLK_ECSPI2,
- imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
+ imx_clk_gate2(dev, "ecspi2", "ecspi_root", base + 0x6c, 2));
clk_dm(IMX6QDL_CLK_ECSPI3,
- imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
+ imx_clk_gate2(dev, "ecspi3", "ecspi_root", base + 0x6c, 4));
clk_dm(IMX6QDL_CLK_ECSPI4,
- imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
+ imx_clk_gate2(dev, "ecspi4", "ecspi_root", base + 0x6c, 6));
+ clk_dm(IMX6QDL_CLK_UART_IPG,
+ imx_clk_gate2(dev, "uart_ipg", "ipg", base + 0x7c, 24));
+ clk_dm(IMX6QDL_CLK_UART_SERIAL,
+ imx_clk_gate2(dev, "uart_serial", "uart_serial_podf", base + 0x7c, 26));
clk_dm(IMX6QDL_CLK_USDHC1,
- imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
+ imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2));
clk_dm(IMX6QDL_CLK_USDHC2,
- imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
+ imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4));
clk_dm(IMX6QDL_CLK_USDHC3,
- imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
+ imx_clk_gate2(dev, "usdhc3", "usdhc3_podf", base + 0x80, 6));
clk_dm(IMX6QDL_CLK_USDHC4,
- imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
+ imx_clk_gate2(dev, "usdhc4", "usdhc4_podf", base + 0x80, 8));
clk_dm(IMX6QDL_CLK_PERIPH_PRE,
- imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels,
+ imx_clk_mux(dev, "periph_pre", base + 0x18, 18, 2, periph_pre_sels,
ARRAY_SIZE(periph_pre_sels)));
clk_dm(IMX6QDL_CLK_PERIPH,
- imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48,
+ imx_clk_busy_mux(dev, "periph", base + 0x14, 25, 1, base + 0x48,
5, periph_sels, ARRAY_SIZE(periph_sels)));
clk_dm(IMX6QDL_CLK_AHB,
- imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3,
+ imx_clk_busy_divider(dev, "ahb", "periph", base + 0x14, 10, 3,
base + 0x48, 1));
clk_dm(IMX6QDL_CLK_IPG,
- imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2));
+ imx_clk_divider(dev, "ipg", "ahb", base + 0x14, 8, 2));
clk_dm(IMX6QDL_CLK_IPG_PER,
- imx_clk_divider("ipg_per", "ipg", base + 0x1c, 0, 6));
+ imx_clk_divider(dev, "ipg_per", "ipg", base + 0x1c, 0, 6));
clk_dm(IMX6QDL_CLK_I2C1,
- imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6));
+ imx_clk_gate2(dev, "i2c1", "ipg_per", base + 0x70, 6));
clk_dm(IMX6QDL_CLK_I2C2,
- imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8));
+ imx_clk_gate2(dev, "i2c2", "ipg_per", base + 0x70, 8));
clk_dm(IMX6QDL_CLK_I2C3,
- imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10));
+ imx_clk_gate2(dev, "i2c3", "ipg_per", base + 0x70, 10));
clk_dm(IMX6QDL_CLK_PWM1,
- imx_clk_gate2("pwm1", "ipg_per", base + 0x78, 16));
+ imx_clk_gate2(dev, "pwm1", "ipg_per", base + 0x78, 16));
- clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2("enet", "ipg", base + 0x6c, 10));
+ clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2(dev, "enet", "ipg", base + 0x6c, 10));
clk_dm(IMX6QDL_CLK_ENET_REF,
- imx_clk_fixed_factor("enet_ref", "pll6_enet", 1, 1));
+ imx_clk_fixed_factor(dev, "enet_ref", "pll6_enet", 1, 1));
return 0;
}
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
index bb6958f0ec2..b81db516a69 100644
--- a/drivers/clk/imx/clk-imx8mm.c
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -14,7 +14,7 @@
#include "clk.h"
-static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", };
+static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", };
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", };
@@ -23,128 +23,144 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se
static const char * const imx8mm_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", };
-static const char * const imx8mm_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m",
+static const char * const imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m",
"sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m",
"audio_pll1_out", "sys_pll3_out", };
-static const char * const imx8mm_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m",
+static const char * const imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m",
"sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out",
"audio_pll1_out", "video_pll1_out", };
#ifndef CONFIG_XPL_BUILD
-static const char * const imx8mm_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m",
+static const char * const imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out",
"video_pll1_out", "sys_pll3_out", };
-static const char * const imx8mm_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m",
+static const char * const imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m",
"sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out",
"video_pll1_out", "clk_ext4", };
-static const char * const imx8mm_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out",
+static const char * const imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out",
"clk_ext1", "clk_ext2", "clk_ext3",
"clk_ext4", "video_pll1_out", };
-static const char * const imx8mm_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m",
+static const char * const imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m",
"sys_pll2_200m", "sys_pll2_500m", "video_pll1_out",
"audio_pll2_out", };
#endif
-static const char * const imx8mm_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m",
+static const char * const imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll1_out", };
-static const char * const imx8mm_usb_bus_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m",
+static const char * const imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext4", "audio_pll2_out", };
-static const char * const imx8mm_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
-static const char * const imx8mm_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
-static const char * const imx8mm_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mm_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mm_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mm_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
+static const char * const imx8mm_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4",
+ "audio_pll2_out", };
+
+static const char * const imx8mm_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3",
+ "audio_pll2_out", };
+
+static const char * const imx8mm_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4",
+ "audio_pll2_out", };
+
+static const char * const imx8mm_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3",
+ "audio_pll2_out", };
+
#if CONFIG_IS_ENABLED(PCIE_DW_IMX)
-static const char * const imx8mm_pcie1_ctrl_sels[] = {"clock-osc-24m", "sys_pll2_250m", "sys_pll2_200m",
+static const char * const imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m",
"sys_pll1_266m", "sys_pll1_800m", "sys_pll2_500m",
"sys_pll2_333m", "sys_pll3_out", };
-static const char * const imx8mm_pcie1_phy_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll2_500m",
+static const char * const imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m",
"clk_ext1", "clk_ext2", "clk_ext3",
"clk_ext4", "sys_pll1_400m", };
-static const char * const imx8mm_pcie1_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m",
+static const char * const imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m",
"sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m",
"sys_pll1_160m", "sys_pll1_200m", };
#endif
#ifndef CONFIG_XPL_BUILD
-static const char * const imx8mm_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mm_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mm_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mm_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
"sys_pll1_80m", "video_pll1_out", };
#endif
-static const char * const imx8mm_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m",
+static const char * const imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m",
"vpu_pll_out", "sys_pll2_125m", "sys_pll3_out",
"sys_pll1_80m", "sys_pll2_166m", };
-static const char * const imx8mm_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_clk", "sys_pll1_100m", };
#if CONFIG_IS_ENABLED(NXP_FSPI)
-static const char * const imx8mm_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m",
+static const char * const imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m",
"sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m",
"sys_pll3_out", "sys_pll1_100m", };
#endif
-static const char * const imx8mm_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m",
+static const char * const imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
-static const char * const imx8mm_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m",
+static const char * const imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
#if CONFIG_IS_ENABLED(DM_SPI)
-static const char * const imx8mm_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mm_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mm_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
#endif
@@ -156,19 +172,19 @@ static int imx8mm_clk_probe(struct udevice *dev)
base = (void *)ANATOP_BASE_ADDR;
clk_dm(IMX8MM_DRAM_PLL_REF_SEL,
- imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2,
+ imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MM_ARM_PLL_REF_SEL,
- imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2,
+ imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MM_SYS_PLL1_REF_SEL,
- imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2,
+ imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MM_SYS_PLL2_REF_SEL,
- imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2,
+ imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MM_SYS_PLL3_REF_SEL,
- imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2,
+ imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MM_DRAM_PLL,
@@ -189,238 +205,254 @@ static int imx8mm_clk_probe(struct udevice *dev)
/* PLL bypass out */
clk_dm(IMX8MM_DRAM_PLL_BYPASS,
- imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1,
+ 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(IMX8MM_ARM_PLL_BYPASS,
- imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1,
+ 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(IMX8MM_SYS_PLL1_BYPASS,
- imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1,
+ 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(IMX8MM_SYS_PLL2_BYPASS,
- imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1,
+ 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(IMX8MM_SYS_PLL3_BYPASS,
- imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1,
+ 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));
/* PLL out gate */
clk_dm(IMX8MM_DRAM_PLL_OUT,
- imx_clk_gate("dram_pll_out", "dram_pll_bypass",
+ imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass",
base + 0x50, 13));
clk_dm(IMX8MM_ARM_PLL_OUT,
- imx_clk_gate("arm_pll_out", "arm_pll_bypass",
+ imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass",
base + 0x84, 11));
clk_dm(IMX8MM_SYS_PLL1_OUT,
- imx_clk_gate("sys_pll1_out", "sys_pll1_bypass",
+ imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass",
base + 0x94, 11));
clk_dm(IMX8MM_SYS_PLL2_OUT,
- imx_clk_gate("sys_pll2_out", "sys_pll2_bypass",
+ imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass",
base + 0x104, 11));
clk_dm(IMX8MM_SYS_PLL3_OUT,
- imx_clk_gate("sys_pll3_out", "sys_pll3_bypass",
+ imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass",
base + 0x114, 11));
/* SYS PLL fixed output */
clk_dm(IMX8MM_SYS_PLL1_40M,
- imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20));
+ imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20));
clk_dm(IMX8MM_SYS_PLL1_80M,
- imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10));
+ imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10));
clk_dm(IMX8MM_SYS_PLL1_100M,
- imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8));
+ imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8));
clk_dm(IMX8MM_SYS_PLL1_133M,
- imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6));
+ imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6));
clk_dm(IMX8MM_SYS_PLL1_160M,
- imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5));
+ imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5));
clk_dm(IMX8MM_SYS_PLL1_200M,
- imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4));
+ imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4));
clk_dm(IMX8MM_SYS_PLL1_266M,
- imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3));
+ imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3));
clk_dm(IMX8MM_SYS_PLL1_400M,
- imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2));
clk_dm(IMX8MM_SYS_PLL1_800M,
- imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1));
+ imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1));
clk_dm(IMX8MM_SYS_PLL2_50M,
- imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20));
+ imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20));
clk_dm(IMX8MM_SYS_PLL2_100M,
- imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10));
+ imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10));
clk_dm(IMX8MM_SYS_PLL2_125M,
- imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8));
+ imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8));
clk_dm(IMX8MM_SYS_PLL2_166M,
- imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6));
+ imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6));
clk_dm(IMX8MM_SYS_PLL2_200M,
- imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5));
+ imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5));
clk_dm(IMX8MM_SYS_PLL2_250M,
- imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4));
+ imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4));
clk_dm(IMX8MM_SYS_PLL2_333M,
- imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3));
+ imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3));
clk_dm(IMX8MM_SYS_PLL2_500M,
- imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2));
clk_dm(IMX8MM_SYS_PLL2_1000M,
- imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
+ imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1));
base = dev_read_addr_ptr(dev);
if (!base)
return -EINVAL;
clk_dm(IMX8MM_CLK_A53_SRC,
- imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3,
+ imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3,
imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels)));
clk_dm(IMX8MM_CLK_A53_CG,
- imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
+ imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
clk_dm(IMX8MM_CLK_A53_DIV,
- imx_clk_divider2("arm_a53_div", "arm_a53_cg",
+ imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg",
base + 0x8000, 0, 3));
clk_dm(IMX8MM_CLK_AHB,
- imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels,
+ imx8m_clk_composite_critical(dev, "ahb", imx8mm_ahb_sels,
base + 0x9000));
clk_dm(IMX8MM_CLK_IPG_ROOT,
- imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1));
+ imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1));
clk_dm(IMX8MM_CLK_NAND_USDHC_BUS,
- imx8m_clk_composite_critical("nand_usdhc_bus",
+ imx8m_clk_composite_critical(dev, "nand_usdhc_bus",
imx8mm_nand_usdhc_sels,
base + 0x8900));
clk_dm(IMX8MM_CLK_USB_BUS,
- imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80));
+ imx8m_clk_composite(dev, "usb_bus", imx8mm_usb_bus_sels, base + 0x8b80));
/* IP */
#if CONFIG_IS_ENABLED(PCIE_DW_IMX)
clk_dm(IMX8MM_CLK_PCIE1_CTRL,
- imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels,
+ imx8m_clk_composite(dev, "pcie1_ctrl", imx8mm_pcie1_ctrl_sels,
base + 0xa300));
clk_dm(IMX8MM_CLK_PCIE1_PHY,
- imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels,
+ imx8m_clk_composite(dev, "pcie1_phy", imx8mm_pcie1_phy_sels,
base + 0xa380));
clk_dm(IMX8MM_CLK_PCIE1_AUX,
- imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels,
+ imx8m_clk_composite(dev, "pcie1_aux", imx8mm_pcie1_aux_sels,
base + 0xa400));
#endif
clk_dm(IMX8MM_CLK_USDHC1,
- imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels,
+ imx8m_clk_composite(dev, "usdhc1", imx8mm_usdhc1_sels,
base + 0xac00));
clk_dm(IMX8MM_CLK_USDHC2,
- imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels,
+ imx8m_clk_composite(dev, "usdhc2", imx8mm_usdhc2_sels,
base + 0xac80));
clk_dm(IMX8MM_CLK_I2C1,
- imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00));
+ imx8m_clk_composite(dev, "i2c1", imx8mm_i2c1_sels, base + 0xad00));
clk_dm(IMX8MM_CLK_I2C2,
- imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80));
+ imx8m_clk_composite(dev, "i2c2", imx8mm_i2c2_sels, base + 0xad80));
clk_dm(IMX8MM_CLK_I2C3,
- imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00));
+ imx8m_clk_composite(dev, "i2c3", imx8mm_i2c3_sels, base + 0xae00));
clk_dm(IMX8MM_CLK_I2C4,
- imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80));
+ imx8m_clk_composite(dev, "i2c4", imx8mm_i2c4_sels, base + 0xae80));
+ clk_dm(IMX8MM_CLK_UART1,
+ imx8m_clk_composite(dev, "uart1", imx8mm_uart1_sels, base + 0xaf00));
+ clk_dm(IMX8MM_CLK_UART2,
+ imx8m_clk_composite(dev, "uart2", imx8mm_uart2_sels, base + 0xaf80));
+ clk_dm(IMX8MM_CLK_UART3,
+ imx8m_clk_composite(dev, "uart3", imx8mm_uart3_sels, base + 0xb000));
+ clk_dm(IMX8MM_CLK_UART4,
+ imx8m_clk_composite(dev, "uart4", imx8mm_uart4_sels, base + 0xb080));
+ clk_dm(IMX8MM_CLK_UART1_ROOT,
+ imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0));
+ clk_dm(IMX8MM_CLK_UART2_ROOT,
+ imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0));
+ clk_dm(IMX8MM_CLK_UART3_ROOT,
+ imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0));
+ clk_dm(IMX8MM_CLK_UART4_ROOT,
+ imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0));
clk_dm(IMX8MM_CLK_WDOG,
- imx8m_clk_composite("wdog", imx8mm_wdog_sels, base + 0xb900));
+ imx8m_clk_composite(dev, "wdog", imx8mm_wdog_sels, base + 0xb900));
clk_dm(IMX8MM_CLK_USDHC3,
- imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels,
+ imx8m_clk_composite(dev, "usdhc3", imx8mm_usdhc3_sels,
base + 0xbc80));
clk_dm(IMX8MM_CLK_USB_CORE_REF,
- imx8m_clk_composite("usb_core_ref", imx8mm_usb_core_sels, base + 0xb100));
+ imx8m_clk_composite(dev, "usb_core_ref", imx8mm_usb_core_sels, base + 0xb100));
clk_dm(IMX8MM_CLK_USB_PHY_REF,
- imx8m_clk_composite("usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180));
+ imx8m_clk_composite(dev, "usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180));
clk_dm(IMX8MM_CLK_I2C1_ROOT,
- imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0));
+ imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0));
clk_dm(IMX8MM_CLK_I2C2_ROOT,
- imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0));
+ imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0));
clk_dm(IMX8MM_CLK_I2C3_ROOT,
- imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0));
+ imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0));
clk_dm(IMX8MM_CLK_I2C4_ROOT,
- imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0));
+ imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0));
clk_dm(IMX8MM_CLK_OCOTP_ROOT,
- imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0));
+ imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0));
clk_dm(IMX8MM_CLK_USDHC1_ROOT,
- imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
+ imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
clk_dm(IMX8MM_CLK_USDHC2_ROOT,
- imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
+ imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
clk_dm(IMX8MM_CLK_WDOG1_ROOT,
- imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0));
+ imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0));
clk_dm(IMX8MM_CLK_WDOG2_ROOT,
- imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0));
+ imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0));
clk_dm(IMX8MM_CLK_WDOG3_ROOT,
- imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0));
+ imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0));
clk_dm(IMX8MM_CLK_USDHC3_ROOT,
- imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0));
+ imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0));
clk_dm(IMX8MM_CLK_USB1_CTRL_ROOT,
- imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));
+ imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));
/* clks not needed in SPL stage */
#ifndef CONFIG_XPL_BUILD
clk_dm(IMX8MM_CLK_ENET_AXI,
- imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels,
+ imx8m_clk_composite(dev, "enet_axi", imx8mm_enet_axi_sels,
base + 0x8880));
clk_dm(IMX8MM_CLK_ENET_REF,
- imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels,
+ imx8m_clk_composite(dev, "enet_ref", imx8mm_enet_ref_sels,
base + 0xa980));
clk_dm(IMX8MM_CLK_ENET_TIMER,
- imx8m_clk_composite("enet_timer", imx8mm_enet_timer_sels,
+ imx8m_clk_composite(dev, "enet_timer", imx8mm_enet_timer_sels,
base + 0xaa00));
clk_dm(IMX8MM_CLK_ENET_PHY_REF,
- imx8m_clk_composite("enet_phy", imx8mm_enet_phy_sels,
+ imx8m_clk_composite(dev, "enet_phy", imx8mm_enet_phy_sels,
base + 0xaa80));
clk_dm(IMX8MM_CLK_ENET1_ROOT,
- imx_clk_gate4("enet1_root_clk", "enet_axi",
+ imx_clk_gate4(dev, "enet1_root_clk", "enet_axi",
base + 0x40a0, 0));
clk_dm(IMX8MM_CLK_PWM1,
- imx8m_clk_composite("pwm1", imx8mm_pwm1_sels, base + 0xb380));
+ imx8m_clk_composite(dev, "pwm1", imx8mm_pwm1_sels, base + 0xb380));
clk_dm(IMX8MM_CLK_PWM2,
- imx8m_clk_composite("pwm2", imx8mm_pwm2_sels, base + 0xb400));
+ imx8m_clk_composite(dev, "pwm2", imx8mm_pwm2_sels, base + 0xb400));
clk_dm(IMX8MM_CLK_PWM3,
- imx8m_clk_composite("pwm3", imx8mm_pwm3_sels, base + 0xb480));
+ imx8m_clk_composite(dev, "pwm3", imx8mm_pwm3_sels, base + 0xb480));
clk_dm(IMX8MM_CLK_PWM4,
- imx8m_clk_composite("pwm4", imx8mm_pwm4_sels, base + 0xb500));
+ imx8m_clk_composite(dev, "pwm4", imx8mm_pwm4_sels, base + 0xb500));
clk_dm(IMX8MM_CLK_PWM1_ROOT,
- imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0));
+ imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0));
clk_dm(IMX8MM_CLK_PWM2_ROOT,
- imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0));
+ imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0));
clk_dm(IMX8MM_CLK_PWM3_ROOT,
- imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0));
+ imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0));
clk_dm(IMX8MM_CLK_PWM4_ROOT,
- imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0));
+ imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0));
#endif
#if CONFIG_IS_ENABLED(PCIE_DW_IMX)
clk_dm(IMX8MM_CLK_PCIE1_ROOT,
- imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0));
+ imx_clk_gate4(dev, "pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0));
#endif
#if CONFIG_IS_ENABLED(DM_SPI)
clk_dm(IMX8MM_CLK_ECSPI1,
- imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280));
+ imx8m_clk_composite(dev, "ecspi1", imx8mm_ecspi1_sels, base + 0xb280));
clk_dm(IMX8MM_CLK_ECSPI2,
- imx8m_clk_composite("ecspi2", imx8mm_ecspi2_sels, base + 0xb300));
+ imx8m_clk_composite(dev, "ecspi2", imx8mm_ecspi2_sels, base + 0xb300));
clk_dm(IMX8MM_CLK_ECSPI3,
- imx8m_clk_composite("ecspi3", imx8mm_ecspi3_sels, base + 0xc180));
+ imx8m_clk_composite(dev, "ecspi3", imx8mm_ecspi3_sels, base + 0xc180));
clk_dm(IMX8MM_CLK_ECSPI1_ROOT,
- imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
+ imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
clk_dm(IMX8MM_CLK_ECSPI2_ROOT,
- imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
+ imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
clk_dm(IMX8MM_CLK_ECSPI3_ROOT,
- imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
+ imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
#endif
#if CONFIG_IS_ENABLED(NXP_FSPI)
clk_dm(IMX8MM_CLK_QSPI,
- imx8m_clk_composite("qspi", imx8mm_qspi_sels, base + 0xab80));
+ imx8m_clk_composite(dev, "qspi", imx8mm_qspi_sels, base + 0xab80));
clk_dm(IMX8MM_CLK_QSPI_ROOT,
- imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0));
+ imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0));
#endif
clk_dm(IMX8MM_CLK_ARM,
- imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1,
+ imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1,
imx8mm_arm_core_sels,
ARRAY_SIZE(imx8mm_arm_core_sels),
CLK_IS_CRITICAL));
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c
index be15ebd0e25..be5b7933a8d 100644
--- a/drivers/clk/imx/clk-imx8mn.c
+++ b/drivers/clk/imx/clk-imx8mn.c
@@ -16,7 +16,7 @@
static u32 share_count_nand;
-static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", };
+static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", };
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", };
@@ -25,117 +25,133 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se
static const char * const imx8mn_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", };
-static const char * const imx8mn_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m",
+static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m",
"sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m",
"audio_pll1_out", "sys_pll3_out", };
-static const char * const imx8mn_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m",
+static const char * const imx8mn_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m",
"sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out",
"audio_pll1_out", "video_pll_out", };
-static const char * const imx8mn_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m",
+static const char * const imx8mn_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out",
"video_pll_out", "sys_pll3_out", };
#ifndef CONFIG_XPL_BUILD
-static const char * const imx8mn_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m",
+static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m",
"sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out",
"video_pll_out", "clk_ext4", };
-static const char * const imx8mn_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out",
+static const char * const imx8mn_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out",
"clk_ext1", "clk_ext2", "clk_ext3",
"clk_ext4", "video_pll_out", };
-static const char * const imx8mn_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m",
+static const char * const imx8mn_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m",
"sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out",
"video_pll_out", "audio_pll2_out", };
#endif
-static const char * const imx8mn_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m",
+static const char * const imx8mn_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll1_out", };
-static const char * const imx8mn_usb_bus_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m",
+static const char * const imx8mn_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext4", "audio_pll2_out", };
-static const char * const imx8mn_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mn_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
-static const char * const imx8mn_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mn_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
#if CONFIG_IS_ENABLED(DM_SPI)
-static const char * const imx8mn_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mn_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mn_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mn_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mn_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mn_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
#endif
-static const char * const imx8mn_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mn_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mn_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mn_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mn_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mn_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mn_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mn_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll_out",
"audio_pll2_out", "sys_pll1_133m", };
+static const char * const imx8mn_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2",
+ "clk_ext4", "audio_pll2_out", };
+
+static const char * const imx8mn_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2",
+ "clk_ext3", "audio_pll2_out", };
+
+static const char * const imx8mn_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2",
+ "clk_ext4", "audio_pll2_out", };
+
+static const char * const imx8mn_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+ "sys_pll2_100m", "sys_pll3_out", "clk_ext2",
+ "clk_ext3", "audio_pll2_out", };
+
#ifndef CONFIG_XPL_BUILD
-static const char * const imx8mn_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mn_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
"sys_pll1_80m", "video_pll_out", };
-static const char * const imx8mn_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mn_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
"sys_pll1_80m", "video_pll_out", };
-static const char * const imx8mn_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mn_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
"sys_pll1_80m", "video_pll_out", };
-static const char * const imx8mn_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mn_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
"sys_pll1_80m", "video_pll_out", };
#endif
-static const char * const imx8mn_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m",
+static const char * const imx8mn_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m",
"m7_alt_pll", "sys_pll2_125m", "sys_pll3_out",
"sys_pll1_80m", "sys_pll2_166m", };
-static const char * const imx8mn_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_clk", "sys_pll1_100m", };
-static const char * const imx8mn_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m",
+static const char * const imx8mn_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m",
"sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m",
"sys_pll3_out", "sys_pll1_100m", };
-static const char * const imx8mn_nand_sels[] = {"clock-osc-24m", "sys_pll2_500m", "audio_pll1_out",
+static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out",
"sys_pll1_400m", "audio_pll2_out", "sys_pll3_out",
"sys_pll2_250m", "video_pll_out", };
-static const char * const imx8mn_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m",
+static const char * const imx8mn_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
-static const char * const imx8mn_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m",
+static const char * const imx8mn_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
@@ -148,19 +164,19 @@ static int imx8mn_clk_probe(struct udevice *dev)
base = (void *)ANATOP_BASE_ADDR;
clk_dm(IMX8MN_DRAM_PLL_REF_SEL,
- imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2,
+ imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MN_ARM_PLL_REF_SEL,
- imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2,
+ imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MN_SYS_PLL1_REF_SEL,
- imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2,
+ imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MN_SYS_PLL2_REF_SEL,
- imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2,
+ imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MN_SYS_PLL3_REF_SEL,
- imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2,
+ imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MN_DRAM_PLL,
@@ -181,86 +197,86 @@ static int imx8mn_clk_probe(struct udevice *dev)
/* PLL bypass out */
clk_dm(IMX8MN_DRAM_PLL_BYPASS,
- imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1,
+ 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(IMX8MN_ARM_PLL_BYPASS,
- imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1,
+ 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(IMX8MN_SYS_PLL1_BYPASS,
- imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1,
+ 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(IMX8MN_SYS_PLL2_BYPASS,
- imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1,
+ 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(IMX8MN_SYS_PLL3_BYPASS,
- imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1,
+ 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));
/* PLL out gate */
clk_dm(IMX8MN_DRAM_PLL_OUT,
- imx_clk_gate("dram_pll_out", "dram_pll_bypass",
+ imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass",
base + 0x50, 13));
clk_dm(IMX8MN_ARM_PLL_OUT,
- imx_clk_gate("arm_pll_out", "arm_pll_bypass",
+ imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass",
base + 0x84, 11));
clk_dm(IMX8MN_SYS_PLL1_OUT,
- imx_clk_gate("sys_pll1_out", "sys_pll1_bypass",
+ imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass",
base + 0x94, 11));
clk_dm(IMX8MN_SYS_PLL2_OUT,
- imx_clk_gate("sys_pll2_out", "sys_pll2_bypass",
+ imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass",
base + 0x104, 11));
clk_dm(IMX8MN_SYS_PLL3_OUT,
- imx_clk_gate("sys_pll3_out", "sys_pll3_bypass",
+ imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass",
base + 0x114, 11));
/* SYS PLL fixed output */
clk_dm(IMX8MN_SYS_PLL1_40M,
- imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20));
+ imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20));
clk_dm(IMX8MN_SYS_PLL1_80M,
- imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10));
+ imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10));
clk_dm(IMX8MN_SYS_PLL1_100M,
- imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8));
+ imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8));
clk_dm(IMX8MN_SYS_PLL1_133M,
- imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6));
+ imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6));
clk_dm(IMX8MN_SYS_PLL1_160M,
- imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5));
+ imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5));
clk_dm(IMX8MN_SYS_PLL1_200M,
- imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4));
+ imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4));
clk_dm(IMX8MN_SYS_PLL1_266M,
- imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3));
+ imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3));
clk_dm(IMX8MN_SYS_PLL1_400M,
- imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2));
clk_dm(IMX8MN_SYS_PLL1_800M,
- imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1));
+ imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1));
clk_dm(IMX8MN_SYS_PLL2_50M,
- imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20));
+ imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20));
clk_dm(IMX8MN_SYS_PLL2_100M,
- imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10));
+ imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10));
clk_dm(IMX8MN_SYS_PLL2_125M,
- imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8));
+ imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8));
clk_dm(IMX8MN_SYS_PLL2_166M,
- imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6));
+ imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6));
clk_dm(IMX8MN_SYS_PLL2_200M,
- imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5));
+ imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5));
clk_dm(IMX8MN_SYS_PLL2_250M,
- imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4));
+ imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4));
clk_dm(IMX8MN_SYS_PLL2_333M,
- imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3));
+ imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3));
clk_dm(IMX8MN_SYS_PLL2_500M,
- imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2));
clk_dm(IMX8MN_SYS_PLL2_1000M,
- imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
+ imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1));
ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk);
if (ret)
@@ -272,141 +288,157 @@ static int imx8mn_clk_probe(struct udevice *dev)
return -EINVAL;
clk_dm(IMX8MN_CLK_A53_SRC,
- imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3,
+ imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3,
imx8mn_a53_sels, ARRAY_SIZE(imx8mn_a53_sels)));
clk_dm(IMX8MN_CLK_A53_CG,
- imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
+ imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
clk_dm(IMX8MN_CLK_A53_DIV,
- imx_clk_divider2("arm_a53_div", "arm_a53_cg",
+ imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg",
base + 0x8000, 0, 3));
clk_dm(IMX8MN_CLK_AHB,
- imx8m_clk_composite_critical("ahb", imx8mn_ahb_sels,
+ imx8m_clk_composite_critical(dev, "ahb", imx8mn_ahb_sels,
base + 0x9000));
clk_dm(IMX8MN_CLK_IPG_ROOT,
- imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1));
+ imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1));
clk_dm(IMX8MN_CLK_ENET_AXI,
- imx8m_clk_composite("enet_axi", imx8mn_enet_axi_sels,
+ imx8m_clk_composite(dev, "enet_axi", imx8mn_enet_axi_sels,
base + 0x8880));
clk_dm(IMX8MN_CLK_NAND_USDHC_BUS,
- imx8m_clk_composite_critical("nand_usdhc_bus",
+ imx8m_clk_composite_critical(dev, "nand_usdhc_bus",
imx8mn_nand_usdhc_sels,
base + 0x8900));
clk_dm(IMX8MN_CLK_USB_BUS,
- imx8m_clk_composite("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80));
+ imx8m_clk_composite(dev, "usb_bus", imx8mn_usb_bus_sels, base + 0x8b80));
/* IP */
clk_dm(IMX8MN_CLK_USDHC1,
- imx8m_clk_composite("usdhc1", imx8mn_usdhc1_sels,
+ imx8m_clk_composite(dev, "usdhc1", imx8mn_usdhc1_sels,
base + 0xac00));
clk_dm(IMX8MN_CLK_USDHC2,
- imx8m_clk_composite("usdhc2", imx8mn_usdhc2_sels,
+ imx8m_clk_composite(dev, "usdhc2", imx8mn_usdhc2_sels,
base + 0xac80));
clk_dm(IMX8MN_CLK_I2C1,
- imx8m_clk_composite("i2c1", imx8mn_i2c1_sels, base + 0xad00));
+ imx8m_clk_composite(dev, "i2c1", imx8mn_i2c1_sels, base + 0xad00));
clk_dm(IMX8MN_CLK_I2C2,
- imx8m_clk_composite("i2c2", imx8mn_i2c2_sels, base + 0xad80));
+ imx8m_clk_composite(dev, "i2c2", imx8mn_i2c2_sels, base + 0xad80));
clk_dm(IMX8MN_CLK_I2C3,
- imx8m_clk_composite("i2c3", imx8mn_i2c3_sels, base + 0xae00));
+ imx8m_clk_composite(dev, "i2c3", imx8mn_i2c3_sels, base + 0xae00));
clk_dm(IMX8MN_CLK_I2C4,
- imx8m_clk_composite("i2c4", imx8mn_i2c4_sels, base + 0xae80));
+ imx8m_clk_composite(dev, "i2c4", imx8mn_i2c4_sels, base + 0xae80));
+ clk_dm(IMX8MN_CLK_UART1,
+ imx8m_clk_composite(dev, "uart1", imx8mn_uart1_sels, base + 0xaf00));
+ clk_dm(IMX8MN_CLK_UART2,
+ imx8m_clk_composite(dev, "uart2", imx8mn_uart2_sels, base + 0xaf80));
+ clk_dm(IMX8MN_CLK_UART3,
+ imx8m_clk_composite(dev, "uart3", imx8mn_uart3_sels, base + 0xb000));
+ clk_dm(IMX8MN_CLK_UART4,
+ imx8m_clk_composite(dev, "uart4", imx8mn_uart4_sels, base + 0xb080));
clk_dm(IMX8MN_CLK_WDOG,
- imx8m_clk_composite("wdog", imx8mn_wdog_sels, base + 0xb900));
+ imx8m_clk_composite(dev, "wdog", imx8mn_wdog_sels, base + 0xb900));
clk_dm(IMX8MN_CLK_USDHC3,
- imx8m_clk_composite("usdhc3", imx8mn_usdhc3_sels,
+ imx8m_clk_composite(dev, "usdhc3", imx8mn_usdhc3_sels,
base + 0xbc80));
clk_dm(IMX8MN_CLK_NAND,
- imx8m_clk_composite("nand", imx8mn_nand_sels, base + 0xab00));
+ imx8m_clk_composite(dev, "nand", imx8mn_nand_sels, base + 0xab00));
clk_dm(IMX8MN_CLK_QSPI,
- imx8m_clk_composite("qspi", imx8mn_qspi_sels, base + 0xab80));
+ imx8m_clk_composite(dev, "qspi", imx8mn_qspi_sels, base + 0xab80));
clk_dm(IMX8MN_CLK_USB_CORE_REF,
- imx8m_clk_composite("usb_core_ref", imx8mn_usb_core_sels, base + 0xb100));
+ imx8m_clk_composite(dev, "usb_core_ref", imx8mn_usb_core_sels, base + 0xb100));
clk_dm(IMX8MN_CLK_USB_PHY_REF,
- imx8m_clk_composite("usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180));
+ imx8m_clk_composite(dev, "usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180));
clk_dm(IMX8MN_CLK_I2C1_ROOT,
- imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0));
+ imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0));
clk_dm(IMX8MN_CLK_I2C2_ROOT,
- imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0));
+ imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0));
clk_dm(IMX8MN_CLK_I2C3_ROOT,
- imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0));
+ imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0));
clk_dm(IMX8MN_CLK_I2C4_ROOT,
- imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0));
+ imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0));
clk_dm(IMX8MN_CLK_OCOTP_ROOT,
- imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0));
+ imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0));
clk_dm(IMX8MN_CLK_USDHC1_ROOT,
- imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
+ imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
clk_dm(IMX8MN_CLK_USDHC2_ROOT,
- imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
+ imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
clk_dm(IMX8MN_CLK_WDOG1_ROOT,
- imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0));
+ imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0));
clk_dm(IMX8MN_CLK_WDOG2_ROOT,
- imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0));
+ imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0));
clk_dm(IMX8MN_CLK_WDOG3_ROOT,
- imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0));
+ imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0));
clk_dm(IMX8MN_CLK_USDHC3_ROOT,
- imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0));
+ imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0));
clk_dm(IMX8MN_CLK_QSPI_ROOT,
- imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0));
+ imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0));
clk_dm(IMX8MN_CLK_NAND_ROOT,
- imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand));
+ imx_clk_gate2_shared2(dev, "nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand));
clk_dm(IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK,
- imx_clk_gate2_shared2("nand_usdhc_rawnand_clk",
+ imx_clk_gate2_shared2(dev, "nand_usdhc_rawnand_clk",
"nand_usdhc_bus", base + 0x4300, 0,
&share_count_nand));
+ clk_dm(IMX8MN_CLK_UART1_ROOT,
+ imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0));
+ clk_dm(IMX8MN_CLK_UART2_ROOT,
+ imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0));
+ clk_dm(IMX8MN_CLK_UART3_ROOT,
+ imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0));
+ clk_dm(IMX8MN_CLK_UART4_ROOT,
+ imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0));
clk_dm(IMX8MN_CLK_USB1_CTRL_ROOT,
- imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));
+ imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));
/* clks not needed in SPL stage */
#ifndef CONFIG_XPL_BUILD
clk_dm(IMX8MN_CLK_ENET_REF,
- imx8m_clk_composite("enet_ref", imx8mn_enet_ref_sels,
+ imx8m_clk_composite(dev, "enet_ref", imx8mn_enet_ref_sels,
base + 0xa980));
clk_dm(IMX8MN_CLK_ENET_TIMER,
- imx8m_clk_composite("enet_timer", imx8mn_enet_timer_sels,
+ imx8m_clk_composite(dev, "enet_timer", imx8mn_enet_timer_sels,
base + 0xaa00));
clk_dm(IMX8MN_CLK_ENET_PHY_REF,
- imx8m_clk_composite("enet_phy", imx8mn_enet_phy_sels,
+ imx8m_clk_composite(dev, "enet_phy", imx8mn_enet_phy_sels,
base + 0xaa80));
clk_dm(IMX8MN_CLK_ENET1_ROOT,
- imx_clk_gate4("enet1_root_clk", "enet_axi",
+ imx_clk_gate4(dev, "enet1_root_clk", "enet_axi",
base + 0x40a0, 0));
clk_dm(IMX8MN_CLK_PWM1,
- imx8m_clk_composite("pwm1", imx8mn_pwm1_sels, base + 0xb380));
+ imx8m_clk_composite(dev, "pwm1", imx8mn_pwm1_sels, base + 0xb380));
clk_dm(IMX8MN_CLK_PWM2,
- imx8m_clk_composite("pwm2", imx8mn_pwm2_sels, base + 0xb400));
+ imx8m_clk_composite(dev, "pwm2", imx8mn_pwm2_sels, base + 0xb400));
clk_dm(IMX8MN_CLK_PWM3,
- imx8m_clk_composite("pwm3", imx8mn_pwm3_sels, base + 0xb480));
+ imx8m_clk_composite(dev, "pwm3", imx8mn_pwm3_sels, base + 0xb480));
clk_dm(IMX8MN_CLK_PWM4,
- imx8m_clk_composite("pwm4", imx8mn_pwm4_sels, base + 0xb500));
+ imx8m_clk_composite(dev, "pwm4", imx8mn_pwm4_sels, base + 0xb500));
clk_dm(IMX8MN_CLK_PWM1_ROOT,
- imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0));
+ imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0));
clk_dm(IMX8MN_CLK_PWM2_ROOT,
- imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0));
+ imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0));
clk_dm(IMX8MN_CLK_PWM3_ROOT,
- imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0));
+ imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0));
clk_dm(IMX8MN_CLK_PWM4_ROOT,
- imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0));
+ imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0));
#endif
#if CONFIG_IS_ENABLED(DM_SPI)
clk_dm(IMX8MN_CLK_ECSPI1,
- imx8m_clk_composite("ecspi1", imx8mn_ecspi1_sels, base + 0xb280));
+ imx8m_clk_composite(dev, "ecspi1", imx8mn_ecspi1_sels, base + 0xb280));
clk_dm(IMX8MN_CLK_ECSPI2,
- imx8m_clk_composite("ecspi2", imx8mn_ecspi2_sels, base + 0xb300));
+ imx8m_clk_composite(dev, "ecspi2", imx8mn_ecspi2_sels, base + 0xb300));
clk_dm(IMX8MN_CLK_ECSPI3,
- imx8m_clk_composite("ecspi3", imx8mn_ecspi3_sels, base + 0xc180));
+ imx8m_clk_composite(dev, "ecspi3", imx8mn_ecspi3_sels, base + 0xc180));
clk_dm(IMX8MN_CLK_ECSPI1_ROOT,
- imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
+ imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
clk_dm(IMX8MN_CLK_ECSPI2_ROOT,
- imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
+ imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
clk_dm(IMX8MN_CLK_ECSPI3_ROOT,
- imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
+ imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
#endif
clk_dm(IMX8MN_CLK_ARM,
- imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1,
+ imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1,
imx8mn_arm_core_sels,
ARRAY_SIZE(imx8mn_arm_core_sels),
CLK_IS_CRITICAL));
diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c
index 1d04090ca00..3d8ed21d3b9 100644
--- a/drivers/clk/imx/clk-imx8mp.c
+++ b/drivers/clk/imx/clk-imx8mp.c
@@ -14,7 +14,14 @@
#include "clk.h"
-static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", };
+#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", };
@@ -23,167 +30,194 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se
static const char * const imx8mp_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", };
-static const char * const imx8mp_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m",
+static const char * const imx8mp_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m",
"sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m",
"audio_pll1_out", "sys_pll3_out", };
-static const char * const imx8mp_hsio_axi_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m",
+static const char * const imx8mp_hsio_axi_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext4", "audio_pll2_out", };
-static const char * const imx8mp_main_axi_sels[] = {"clock-osc-24m", "sys_pll2_333m", "sys_pll1_800m",
+#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",};
-static const char * const imx8mp_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m",
+static const char * const imx8mp_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out",
"video_pll1_out", "sys_pll3_out", };
-static const char * const imx8mp_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m",
+static const char * const imx8mp_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m",
"sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll1_out", };
-static const char * const imx8mp_noc_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_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", };
-static const char * const imx8mp_noc_io_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out",
+static const char * const imx8mp_noc_io_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", };
-static const char * const imx8mp_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m",
+static const char * const imx8mp_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m",
"sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out",
"audio_pll1_out", "video_pll1_out", };
-static const char * const imx8mp_dram_alt_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll1_100m",
+static const char * const imx8mp_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m",
"sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out",
"audio_pll1_out", "sys_pll1_266m", };
-static const char * const imx8mp_dram_apb_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mp_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mp_pcie_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m",
+static const char * const imx8mp_pcie_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m",
"sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m",
"sys_pll1_160m", "sys_pll1_200m", };
-static const char * const imx8mp_i2c5_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mp_i2c5_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mp_i2c6_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mp_i2c6_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mp_enet_qos_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m",
+static const char * const imx8mp_enet_qos_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m",
"sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out",
"video_pll1_out", "clk_ext4", };
-static const char * const imx8mp_enet_qos_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out",
+static const char * const imx8mp_enet_qos_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out",
"clk_ext1", "clk_ext2", "clk_ext3",
"clk_ext4", "video_pll1_out", };
-static const char * const imx8mp_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mp_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
-static const char * const imx8mp_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mp_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
-static const char * const imx8mp_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mp_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mp_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mp_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mp_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mp_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mp_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m",
+static const char * const imx8mp_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m",
"sys_pll3_out", "audio_pll1_out", "video_pll1_out",
"audio_pll2_out", "sys_pll1_133m", };
-static const char * const imx8mp_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+static const char * const imx8mp_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m",
"sys_pll2_100m", "sys_pll3_out", "clk_ext2",
"clk_ext4", "audio_pll2_out", };
-static const char * const imx8mp_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+static const char * const imx8mp_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m",
"sys_pll2_100m", "sys_pll3_out", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
-static const char * const imx8mp_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+static const char * const imx8mp_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m",
"sys_pll2_100m", "sys_pll3_out", "clk_ext2",
"clk_ext4", "audio_pll2_out", };
-static const char * const imx8mp_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m",
+static const char * const imx8mp_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m",
"sys_pll2_100m", "sys_pll3_out", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
-static const char * const imx8mp_usb_core_ref_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m",
+static const char * const imx8mp_usb_core_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
-static const char * const imx8mp_usb_phy_ref_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m",
+static const char * const imx8mp_usb_phy_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll2_200m", "clk_ext2",
"clk_ext3", "audio_pll2_out", };
-static const char * const imx8mp_gic_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mp_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll2_100m", "sys_pll1_800m",
"sys_pll2_500m", "clk_ext4", "audio_pll2_out" };
-static const char * const imx8mp_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mp_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mp_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mp_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext1",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mp_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mp_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mp_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m",
+static const char * const imx8mp_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m",
"sys_pll1_40m", "sys_pll3_out", "clk_ext2",
"sys_pll1_80m", "video_pll1_out", };
-static const char * const imx8mp_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mp_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mp_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mp_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mp_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m",
+static const char * const imx8mp_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m",
"sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out",
"sys_pll2_250m", "audio_pll2_out", };
-static const char * const imx8mp_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m",
+static const char * const imx8mp_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m",
"vpu_pll_out", "sys_pll2_125m", "sys_pll3_out",
"sys_pll1_80m", "sys_pll2_166m" };
-static const char * const imx8mp_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m",
+static const char * const imx8mp_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m",
"sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m",
"sys_pll3_out", "sys_pll1_100m", };
-static const char * const imx8mp_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m",
+static const char * const imx8mp_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m",
"sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m",
"audio_pll2_out", "sys_pll1_100m", };
-static const char * const imx8mp_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m",
+#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", };
-static const char * const imx8mp_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out",
+static const char * const imx8mp_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out",
"clk_ext1", "clk_ext2", "clk_ext3",
"clk_ext4", "video_pll1_out", };
-static const char * const imx8mp_enet_phy_ref_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m",
+static const char * const imx8mp_enet_phy_ref_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m",
"sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out",
"video_pll1_out", "audio_pll2_out", };
@@ -199,12 +233,19 @@ static int imx8mp_clk_probe(struct udevice *dev)
clk_dm(IMX8MP_CLK_DUMMY, clk_register_fixed_rate(NULL, "dummy", 0));
- clk_dm(IMX8MP_DRAM_PLL_REF_SEL, imx_clk_mux("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("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("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("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("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_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,37 +257,43 @@ 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));
- clk_dm(IMX8MP_DRAM_PLL_BYPASS, imx_clk_mux_flags("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("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("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("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("sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT));
-
- clk_dm(IMX8MP_DRAM_PLL_OUT, imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13));
- clk_dm(IMX8MP_ARM_PLL_OUT, imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11));
- clk_dm(IMX8MP_SYS_PLL1_OUT, imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11));
- clk_dm(IMX8MP_SYS_PLL2_OUT, imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11));
- clk_dm(IMX8MP_SYS_PLL3_OUT, imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11));
-
- clk_dm(IMX8MP_SYS_PLL1_40M, imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20));
- clk_dm(IMX8MP_SYS_PLL1_80M, imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10));
- clk_dm(IMX8MP_SYS_PLL1_100M, imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8));
- clk_dm(IMX8MP_SYS_PLL1_133M, imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6));
- clk_dm(IMX8MP_SYS_PLL1_160M, imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5));
- clk_dm(IMX8MP_SYS_PLL1_200M, imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4));
- clk_dm(IMX8MP_SYS_PLL1_266M, imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3));
- clk_dm(IMX8MP_SYS_PLL1_400M, imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2));
- clk_dm(IMX8MP_SYS_PLL1_800M, imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1));
-
- clk_dm(IMX8MP_SYS_PLL2_50M, imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20));
- clk_dm(IMX8MP_SYS_PLL2_100M, imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10));
- clk_dm(IMX8MP_SYS_PLL2_125M, imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8));
- clk_dm(IMX8MP_SYS_PLL2_166M, imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6));
- clk_dm(IMX8MP_SYS_PLL2_200M, imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5));
- clk_dm(IMX8MP_SYS_PLL2_250M, imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4));
- clk_dm(IMX8MP_SYS_PLL2_333M, imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3));
- clk_dm(IMX8MP_SYS_PLL2_500M, imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2));
- clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
+#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));
+ clk_dm(IMX8MP_SYS_PLL2_OUT, imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11));
+ clk_dm(IMX8MP_SYS_PLL3_OUT, imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11));
+
+ clk_dm(IMX8MP_SYS_PLL1_40M, imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20));
+ clk_dm(IMX8MP_SYS_PLL1_80M, imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10));
+ clk_dm(IMX8MP_SYS_PLL1_100M, imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8));
+ clk_dm(IMX8MP_SYS_PLL1_133M, imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6));
+ clk_dm(IMX8MP_SYS_PLL1_160M, imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5));
+ clk_dm(IMX8MP_SYS_PLL1_200M, imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4));
+ clk_dm(IMX8MP_SYS_PLL1_266M, imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3));
+ clk_dm(IMX8MP_SYS_PLL1_400M, imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2));
+ clk_dm(IMX8MP_SYS_PLL1_800M, imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1));
+
+ clk_dm(IMX8MP_SYS_PLL2_50M, imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20));
+ clk_dm(IMX8MP_SYS_PLL2_100M, imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10));
+ clk_dm(IMX8MP_SYS_PLL2_125M, imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8));
+ clk_dm(IMX8MP_SYS_PLL2_166M, imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6));
+ clk_dm(IMX8MP_SYS_PLL2_200M, imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5));
+ clk_dm(IMX8MP_SYS_PLL2_250M, imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4));
+ clk_dm(IMX8MP_SYS_PLL2_333M, imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3));
+ clk_dm(IMX8MP_SYS_PLL2_500M, imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2));
+ clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1));
ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk);
if (ret)
@@ -262,104 +309,126 @@ static int imx8mp_clk_probe(struct udevice *dev)
if (!base)
return -EINVAL;
- clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels)));
- clk_dm(IMX8MP_CLK_A53_CG, imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
- clk_dm(IMX8MP_CLK_A53_DIV, imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3));
-
- clk_dm(IMX8MP_CLK_HSIO_AXI, imx8m_clk_composite("hsio_axi", imx8mp_hsio_axi_sels, base + 0x8380));
- clk_dm(IMX8MP_CLK_MAIN_AXI, imx8m_clk_composite_critical("main_axi", imx8mp_main_axi_sels, base + 0x8800));
- clk_dm(IMX8MP_CLK_ENET_AXI, imx8m_clk_composite_critical("enet_axi", imx8mp_enet_axi_sels, base + 0x8880));
- clk_dm(IMX8MP_CLK_NAND_USDHC_BUS, imx8m_clk_composite_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, base + 0x8900));
- clk_dm(IMX8MP_CLK_NOC, imx8m_clk_composite_critical("noc", imx8mp_noc_sels, base + 0x8d00));
- clk_dm(IMX8MP_CLK_NOC_IO, imx8m_clk_composite_critical("noc_io", imx8mp_noc_io_sels, base + 0x8d80));
-
- clk_dm(IMX8MP_CLK_AHB, imx8m_clk_composite_critical("ahb_root", imx8mp_ahb_sels, base + 0x9000));
-
- clk_dm(IMX8MP_CLK_IPG_ROOT, imx_clk_divider2("ipg_root", "ahb_root", base + 0x9080, 0, 1));
-
- clk_dm(IMX8MP_CLK_DRAM_ALT, imx8m_clk_composite("dram_alt", imx8mp_dram_alt_sels, base + 0xa000));
- clk_dm(IMX8MP_CLK_DRAM_APB, imx8m_clk_composite_critical("dram_apb", imx8mp_dram_apb_sels, base + 0xa080));
- clk_dm(IMX8MP_CLK_PCIE_AUX, imx8m_clk_composite("pcie_aux", imx8mp_pcie_aux_sels, base + 0xa400));
- clk_dm(IMX8MP_CLK_I2C5, imx8m_clk_composite("i2c5", imx8mp_i2c5_sels, base + 0xa480));
- clk_dm(IMX8MP_CLK_I2C6, imx8m_clk_composite("i2c6", imx8mp_i2c6_sels, base + 0xa500));
- clk_dm(IMX8MP_CLK_ENET_QOS, imx8m_clk_composite("enet_qos", imx8mp_enet_qos_sels, base + 0xa880));
- clk_dm(IMX8MP_CLK_ENET_QOS_TIMER, imx8m_clk_composite("enet_qos_timer", imx8mp_enet_qos_timer_sels, base + 0xa900));
- clk_dm(IMX8MP_CLK_ENET_REF, imx8m_clk_composite("enet_ref", imx8mp_enet_ref_sels, base + 0xa980));
- clk_dm(IMX8MP_CLK_ENET_TIMER, imx8m_clk_composite("enet_timer", imx8mp_enet_timer_sels, base + 0xaa00));
- clk_dm(IMX8MP_CLK_ENET_PHY_REF, imx8m_clk_composite("enet_phy_ref", imx8mp_enet_phy_ref_sels, base + 0xaa80));
- clk_dm(IMX8MP_CLK_QSPI, imx8m_clk_composite("qspi", imx8mp_qspi_sels, base + 0xab80));
- clk_dm(IMX8MP_CLK_USDHC1, imx8m_clk_composite("usdhc1", imx8mp_usdhc1_sels, base + 0xac00));
- clk_dm(IMX8MP_CLK_USDHC2, imx8m_clk_composite("usdhc2", imx8mp_usdhc2_sels, base + 0xac80));
- clk_dm(IMX8MP_CLK_I2C1, imx8m_clk_composite("i2c1", imx8mp_i2c1_sels, base + 0xad00));
- clk_dm(IMX8MP_CLK_I2C2, imx8m_clk_composite("i2c2", imx8mp_i2c2_sels, base + 0xad80));
- clk_dm(IMX8MP_CLK_I2C3, imx8m_clk_composite("i2c3", imx8mp_i2c3_sels, base + 0xae00));
- clk_dm(IMX8MP_CLK_I2C4, imx8m_clk_composite("i2c4", imx8mp_i2c4_sels, base + 0xae80));
-
- clk_dm(IMX8MP_CLK_UART1, imx8m_clk_composite("uart1", imx8mp_uart1_sels, base + 0xaf00));
- clk_dm(IMX8MP_CLK_UART2, imx8m_clk_composite("uart2", imx8mp_uart2_sels, base + 0xaf80));
- clk_dm(IMX8MP_CLK_UART3, imx8m_clk_composite("uart3", imx8mp_uart3_sels, base + 0xb000));
- clk_dm(IMX8MP_CLK_UART4, imx8m_clk_composite("uart4", imx8mp_uart4_sels, base + 0xb080));
- clk_dm(IMX8MP_CLK_USB_CORE_REF, imx8m_clk_composite("usb_core_ref", imx8mp_usb_core_ref_sels, base + 0xb100));
- clk_dm(IMX8MP_CLK_USB_PHY_REF, imx8m_clk_composite("usb_phy_ref", imx8mp_usb_phy_ref_sels, base + 0xb180));
- clk_dm(IMX8MP_CLK_GIC, imx8m_clk_composite_critical("gic", imx8mp_gic_sels, base + 0xb200));
- clk_dm(IMX8MP_CLK_ECSPI1, imx8m_clk_composite("ecspi1", imx8mp_ecspi1_sels, base + 0xb280));
- clk_dm(IMX8MP_CLK_ECSPI2, imx8m_clk_composite("ecspi2", imx8mp_ecspi2_sels, base + 0xb300));
- clk_dm(IMX8MP_CLK_PWM1, imx8m_clk_composite_critical("pwm1", imx8mp_pwm1_sels, base + 0xb380));
- clk_dm(IMX8MP_CLK_PWM2, imx8m_clk_composite_critical("pwm2", imx8mp_pwm2_sels, base + 0xb400));
- clk_dm(IMX8MP_CLK_PWM3, imx8m_clk_composite_critical("pwm3", imx8mp_pwm3_sels, base + 0xb480));
- clk_dm(IMX8MP_CLK_PWM4, imx8m_clk_composite_critical("pwm4", imx8mp_pwm4_sels, base + 0xb500));
- clk_dm(IMX8MP_CLK_ECSPI3, imx8m_clk_composite("ecspi3", imx8mp_ecspi3_sels, base + 0xc180));
-
- clk_dm(IMX8MP_CLK_WDOG, imx8m_clk_composite("wdog", imx8mp_wdog_sels, base + 0xb900));
- clk_dm(IMX8MP_CLK_USDHC3, imx8m_clk_composite("usdhc3", imx8mp_usdhc3_sels, base + 0xbc80));
-
- clk_dm(IMX8MP_CLK_DRAM_ALT_ROOT, imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4));
- clk_dm(IMX8MP_CLK_DRAM_CORE, imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL));
-
- clk_dm(IMX8MP_CLK_DRAM1_ROOT, imx_clk_gate4_flags("dram1_root_clk", "dram_core_clk", base + 0x4050, 0, CLK_IS_CRITICAL));
- clk_dm(IMX8MP_CLK_ECSPI1_ROOT, imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
- clk_dm(IMX8MP_CLK_ECSPI2_ROOT, imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
- clk_dm(IMX8MP_CLK_ECSPI3_ROOT, imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
- clk_dm(IMX8MP_CLK_ENET1_ROOT, imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0));
- clk_dm(IMX8MP_CLK_GPIO1_ROOT, imx_clk_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0));
- clk_dm(IMX8MP_CLK_GPIO2_ROOT, imx_clk_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0));
- clk_dm(IMX8MP_CLK_GPIO3_ROOT, imx_clk_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0));
- clk_dm(IMX8MP_CLK_GPIO4_ROOT, imx_clk_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0));
- clk_dm(IMX8MP_CLK_GPIO5_ROOT, imx_clk_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0));
- clk_dm(IMX8MP_CLK_I2C1_ROOT, imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0));
- clk_dm(IMX8MP_CLK_I2C2_ROOT, imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0));
- clk_dm(IMX8MP_CLK_I2C3_ROOT, imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0));
- clk_dm(IMX8MP_CLK_I2C4_ROOT, imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0));
- clk_dm(IMX8MP_CLK_PCIE_ROOT, imx_clk_gate4("pcie_root_clk", "pcie_aux", base + 0x4250, 0));
- clk_dm(IMX8MP_CLK_PWM1_ROOT, imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0));
- clk_dm(IMX8MP_CLK_PWM2_ROOT, imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0));
- clk_dm(IMX8MP_CLK_PWM3_ROOT, imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0));
- clk_dm(IMX8MP_CLK_PWM4_ROOT, imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0));
- clk_dm(IMX8MP_CLK_QOS_ROOT, imx_clk_gate4("qos_root_clk", "ipg_root", base + 0x42c0, 0));
- clk_dm(IMX8MP_CLK_QOS_ENET_ROOT, imx_clk_gate4("qos_enet_root_clk", "ipg_root", base + 0x42e0, 0));
- clk_dm(IMX8MP_CLK_QSPI_ROOT, imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0));
- clk_dm(IMX8MP_CLK_I2C5_ROOT, imx_clk_gate2("i2c5_root_clk", "i2c5", base + 0x4330, 0));
- clk_dm(IMX8MP_CLK_I2C6_ROOT, imx_clk_gate2("i2c6_root_clk", "i2c6", base + 0x4340, 0));
- clk_dm(IMX8MP_CLK_SIM_ENET_ROOT, imx_clk_gate4("sim_enet_root_clk", "enet_axi", base + 0x4400, 0));
- clk_dm(IMX8MP_CLK_ENET_QOS_ROOT, imx_clk_gate4("enet_qos_root_clk", "sim_enet_root_clk", base + 0x43b0, 0));
- clk_dm(IMX8MP_CLK_UART1_ROOT, imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0));
- clk_dm(IMX8MP_CLK_UART2_ROOT, imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0));
- clk_dm(IMX8MP_CLK_UART3_ROOT, imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0));
- clk_dm(IMX8MP_CLK_UART4_ROOT, imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0));
- clk_dm(IMX8MP_CLK_USB_ROOT, imx_clk_gate2("usb_root_clk", "hsio_axi", base + 0x44d0, 0));
- clk_dm(IMX8MP_CLK_USB_SUSP, imx_clk_gate2("usb_suspend_clk", "clock-osc-24m", base + 0x44d0, 0));
- clk_dm(IMX8MP_CLK_USB_PHY_ROOT, imx_clk_gate4("usb_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0));
- clk_dm(IMX8MP_CLK_USDHC1_ROOT, imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
- clk_dm(IMX8MP_CLK_USDHC2_ROOT, imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
- clk_dm(IMX8MP_CLK_WDOG1_ROOT, imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0));
- clk_dm(IMX8MP_CLK_WDOG2_ROOT, imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0));
- clk_dm(IMX8MP_CLK_WDOG3_ROOT, imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0));
- clk_dm(IMX8MP_CLK_HSIO_ROOT, imx_clk_gate4("hsio_root_clk", "ipg_root", base + 0x45c0, 0));
-
- clk_dm(IMX8MP_CLK_USDHC3_ROOT, imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0));
+ clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels)));
+ clk_dm(IMX8MP_CLK_A53_CG, imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
+ 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));
+
+ clk_dm(IMX8MP_CLK_DRAM_ALT, imx8m_clk_composite(dev, "dram_alt", imx8mp_dram_alt_sels, base + 0xa000));
+ clk_dm(IMX8MP_CLK_DRAM_APB, imx8m_clk_composite_critical(dev, "dram_apb", imx8mp_dram_apb_sels, base + 0xa080));
+ clk_dm(IMX8MP_CLK_PCIE_AUX, imx8m_clk_composite(dev, "pcie_aux", imx8mp_pcie_aux_sels, base + 0xa400));
+ clk_dm(IMX8MP_CLK_I2C5, imx8m_clk_composite(dev, "i2c5", imx8mp_i2c5_sels, base + 0xa480));
+ clk_dm(IMX8MP_CLK_I2C6, imx8m_clk_composite(dev, "i2c6", imx8mp_i2c6_sels, base + 0xa500));
+ clk_dm(IMX8MP_CLK_ENET_QOS, imx8m_clk_composite(dev, "enet_qos", imx8mp_enet_qos_sels, base + 0xa880));
+ clk_dm(IMX8MP_CLK_ENET_QOS_TIMER, imx8m_clk_composite(dev, "enet_qos_timer", imx8mp_enet_qos_timer_sels, base + 0xa900));
+ clk_dm(IMX8MP_CLK_ENET_REF, imx8m_clk_composite(dev, "enet_ref", imx8mp_enet_ref_sels, base + 0xa980));
+ clk_dm(IMX8MP_CLK_ENET_TIMER, imx8m_clk_composite(dev, "enet_timer", imx8mp_enet_timer_sels, base + 0xaa00));
+ clk_dm(IMX8MP_CLK_ENET_PHY_REF, imx8m_clk_composite(dev, "enet_phy_ref", imx8mp_enet_phy_ref_sels, base + 0xaa80));
+ clk_dm(IMX8MP_CLK_QSPI, imx8m_clk_composite(dev, "qspi", imx8mp_qspi_sels, base + 0xab80));
+ clk_dm(IMX8MP_CLK_USDHC1, imx8m_clk_composite(dev, "usdhc1", imx8mp_usdhc1_sels, base + 0xac00));
+ clk_dm(IMX8MP_CLK_USDHC2, imx8m_clk_composite(dev, "usdhc2", imx8mp_usdhc2_sels, base + 0xac80));
+ clk_dm(IMX8MP_CLK_I2C1, imx8m_clk_composite(dev, "i2c1", imx8mp_i2c1_sels, base + 0xad00));
+ clk_dm(IMX8MP_CLK_I2C2, imx8m_clk_composite(dev, "i2c2", imx8mp_i2c2_sels, base + 0xad80));
+ clk_dm(IMX8MP_CLK_I2C3, imx8m_clk_composite(dev, "i2c3", imx8mp_i2c3_sels, base + 0xae00));
+ clk_dm(IMX8MP_CLK_I2C4, imx8m_clk_composite(dev, "i2c4", imx8mp_i2c4_sels, base + 0xae80));
+
+ clk_dm(IMX8MP_CLK_UART1, imx8m_clk_composite(dev, "uart1", imx8mp_uart1_sels, base + 0xaf00));
+ clk_dm(IMX8MP_CLK_UART2, imx8m_clk_composite(dev, "uart2", imx8mp_uart2_sels, base + 0xaf80));
+ clk_dm(IMX8MP_CLK_UART3, imx8m_clk_composite(dev, "uart3", imx8mp_uart3_sels, base + 0xb000));
+ clk_dm(IMX8MP_CLK_UART4, imx8m_clk_composite(dev, "uart4", imx8mp_uart4_sels, base + 0xb080));
+ clk_dm(IMX8MP_CLK_USB_CORE_REF, imx8m_clk_composite(dev, "usb_core_ref", imx8mp_usb_core_ref_sels, base + 0xb100));
+ clk_dm(IMX8MP_CLK_USB_PHY_REF, imx8m_clk_composite(dev, "usb_phy_ref", imx8mp_usb_phy_ref_sels, base + 0xb180));
+ clk_dm(IMX8MP_CLK_GIC, imx8m_clk_composite_critical(dev, "gic", imx8mp_gic_sels, base + 0xb200));
+ clk_dm(IMX8MP_CLK_ECSPI1, imx8m_clk_composite(dev, "ecspi1", imx8mp_ecspi1_sels, base + 0xb280));
+ clk_dm(IMX8MP_CLK_ECSPI2, imx8m_clk_composite(dev, "ecspi2", imx8mp_ecspi2_sels, base + 0xb300));
+ clk_dm(IMX8MP_CLK_PWM1, imx8m_clk_composite_critical(dev, "pwm1", imx8mp_pwm1_sels, base + 0xb380));
+ clk_dm(IMX8MP_CLK_PWM2, imx8m_clk_composite_critical(dev, "pwm2", imx8mp_pwm2_sels, base + 0xb400));
+ clk_dm(IMX8MP_CLK_PWM3, imx8m_clk_composite_critical(dev, "pwm3", imx8mp_pwm3_sels, base + 0xb480));
+ clk_dm(IMX8MP_CLK_PWM4, imx8m_clk_composite_critical(dev, "pwm4", imx8mp_pwm4_sels, base + 0xb500));
+ clk_dm(IMX8MP_CLK_ECSPI3, imx8m_clk_composite(dev, "ecspi3", imx8mp_ecspi3_sels, base + 0xc180));
+
+ 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));
+
+ clk_dm(IMX8MP_CLK_DRAM1_ROOT, imx_clk_gate4_flags(dev, "dram1_root_clk", "dram_core_clk", base + 0x4050, 0, CLK_IS_CRITICAL));
+ clk_dm(IMX8MP_CLK_ECSPI1_ROOT, imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
+ clk_dm(IMX8MP_CLK_ECSPI2_ROOT, imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
+ clk_dm(IMX8MP_CLK_ECSPI3_ROOT, imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
+ clk_dm(IMX8MP_CLK_ENET1_ROOT, imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0));
+ clk_dm(IMX8MP_CLK_GPIO1_ROOT, imx_clk_gate4(dev, "gpio1_root_clk", "ipg_root", base + 0x40b0, 0));
+ clk_dm(IMX8MP_CLK_GPIO2_ROOT, imx_clk_gate4(dev, "gpio2_root_clk", "ipg_root", base + 0x40c0, 0));
+ clk_dm(IMX8MP_CLK_GPIO3_ROOT, imx_clk_gate4(dev, "gpio3_root_clk", "ipg_root", base + 0x40d0, 0));
+ clk_dm(IMX8MP_CLK_GPIO4_ROOT, imx_clk_gate4(dev, "gpio4_root_clk", "ipg_root", base + 0x40e0, 0));
+ clk_dm(IMX8MP_CLK_GPIO5_ROOT, imx_clk_gate4(dev, "gpio5_root_clk", "ipg_root", base + 0x40f0, 0));
+ clk_dm(IMX8MP_CLK_I2C1_ROOT, imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0));
+ clk_dm(IMX8MP_CLK_I2C2_ROOT, imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0));
+ clk_dm(IMX8MP_CLK_I2C3_ROOT, imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0));
+ clk_dm(IMX8MP_CLK_I2C4_ROOT, imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0));
+ clk_dm(IMX8MP_CLK_PCIE_ROOT, imx_clk_gate4(dev, "pcie_root_clk", "pcie_aux", base + 0x4250, 0));
+ clk_dm(IMX8MP_CLK_PWM1_ROOT, imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0));
+ clk_dm(IMX8MP_CLK_PWM2_ROOT, imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0));
+ clk_dm(IMX8MP_CLK_PWM3_ROOT, imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0));
+ clk_dm(IMX8MP_CLK_PWM4_ROOT, imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0));
+ clk_dm(IMX8MP_CLK_QOS_ROOT, imx_clk_gate4(dev, "qos_root_clk", "ipg_root", base + 0x42c0, 0));
+ clk_dm(IMX8MP_CLK_QOS_ENET_ROOT, imx_clk_gate4(dev, "qos_enet_root_clk", "ipg_root", base + 0x42e0, 0));
+ clk_dm(IMX8MP_CLK_QSPI_ROOT, imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0));
+ clk_dm(IMX8MP_CLK_I2C5_ROOT, imx_clk_gate2(dev, "i2c5_root_clk", "i2c5", base + 0x4330, 0));
+ clk_dm(IMX8MP_CLK_I2C6_ROOT, imx_clk_gate2(dev, "i2c6_root_clk", "i2c6", base + 0x4340, 0));
+ clk_dm(IMX8MP_CLK_SIM_ENET_ROOT, imx_clk_gate4(dev, "sim_enet_root_clk", "enet_axi", base + 0x4400, 0));
+ clk_dm(IMX8MP_CLK_ENET_QOS_ROOT, imx_clk_gate4(dev, "enet_qos_root_clk", "sim_enet_root_clk", base + 0x43b0, 0));
+ clk_dm(IMX8MP_CLK_UART1_ROOT, imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0));
+ clk_dm(IMX8MP_CLK_UART2_ROOT, imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0));
+ clk_dm(IMX8MP_CLK_UART3_ROOT, imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0));
+ clk_dm(IMX8MP_CLK_UART4_ROOT, imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0));
+ clk_dm(IMX8MP_CLK_USB_ROOT, imx_clk_gate2(dev, "usb_root_clk", "hsio_axi", base + 0x44d0, 0));
+ clk_dm(IMX8MP_CLK_USB_SUSP, imx_clk_gate2(dev, "usb_suspend_clk", "osc_24m", base + 0x44d0, 0));
+ clk_dm(IMX8MP_CLK_USB_PHY_ROOT, imx_clk_gate4(dev, "usb_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0));
+ clk_dm(IMX8MP_CLK_USDHC1_ROOT, imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
+ clk_dm(IMX8MP_CLK_USDHC2_ROOT, imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
+ clk_dm(IMX8MP_CLK_WDOG1_ROOT, imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0));
+ 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));
clk_dm(IMX8MP_CLK_ARM,
- imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1,
+ imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1,
imx8mp_arm_core_sels,
ARRAY_SIZE(imx8mp_arm_core_sels),
CLK_IS_CRITICAL));
diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
index ed4acd79ef7..fe6cba19758 100644
--- a/drivers/clk/imx/clk-imx8mq.c
+++ b/drivers/clk/imx/clk-imx8mq.c
@@ -152,31 +152,31 @@ static int imx8mq_clk_probe(struct udevice *dev)
clk_dm(IMX8MQ_CLK_27M, clk_register_fixed_rate(NULL, "clock-osc-27m", 27000000));
clk_dm(IMX8MQ_DRAM_PLL1_REF_SEL,
- imx_clk_mux("dram_pll_ref_sel", base + 0x60, 0, 2,
+ imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x60, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_ARM_PLL_REF_SEL,
- imx_clk_mux("arm_pll_ref_sel", base + 0x28, 0, 2,
+ imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x28, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_GPU_PLL_REF_SEL,
- imx_clk_mux("gpu_pll_ref_sel", base + 0x18, 0, 2,
+ imx_clk_mux(dev, "gpu_pll_ref_sel", base + 0x18, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_VPU_PLL_REF_SEL,
- imx_clk_mux("vpu_pll_ref_sel", base + 0x20, 0, 2,
+ imx_clk_mux(dev, "vpu_pll_ref_sel", base + 0x20, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_SYS3_PLL1_REF_SEL,
- imx_clk_mux("sys3_pll_ref_sel", base + 0x48, 0, 2,
+ imx_clk_mux(dev, "sys3_pll_ref_sel", base + 0x48, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_AUDIO_PLL1_REF_SEL,
- imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 0, 2,
+ imx_clk_mux(dev, "audio_pll1_ref_sel", base + 0x0, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_AUDIO_PLL2_REF_SEL,
- imx_clk_mux("audio_pll2_ref_sel", base + 0x8, 0, 2,
+ imx_clk_mux(dev, "audio_pll2_ref_sel", base + 0x8, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_VIDEO_PLL1_REF_SEL,
- imx_clk_mux("video_pll1_ref_sel", base + 0x10, 0, 2,
+ imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x10, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_VIDEO2_PLL1_REF_SEL,
- imx_clk_mux("video_pll2_ref_sel", base + 0x54, 0, 2,
+ imx_clk_mux(dev, "video_pll2_ref_sel", base + 0x54, 0, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMX8MQ_ARM_PLL,
@@ -207,140 +207,140 @@ static int imx8mq_clk_probe(struct udevice *dev)
/* PLL bypass out */
clk_dm(IMX8MQ_ARM_PLL_BYPASS,
- imx_clk_mux_flags("arm_pll_bypass", base + 0x28, 4, 1,
+ imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x28, 4, 1,
arm_pll_bypass_sels,
ARRAY_SIZE(arm_pll_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMX8MQ_GPU_PLL_BYPASS,
- imx_clk_mux_flags("gpu_pll_bypass", base + 0x18, 4, 1,
+ imx_clk_mux_flags(dev, "gpu_pll_bypass", base + 0x18, 4, 1,
gpu_pll_bypass_sels,
ARRAY_SIZE(gpu_pll_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMX8MQ_VPU_PLL_BYPASS,
- imx_clk_mux_flags("vpu_pll_bypass", base + 0x20, 4, 1,
+ imx_clk_mux_flags(dev, "vpu_pll_bypass", base + 0x20, 4, 1,
vpu_pll_bypass_sels,
ARRAY_SIZE(vpu_pll_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMX8MQ_AUDIO_PLL1_BYPASS,
- imx_clk_mux_flags("audio_pll1_bypass", base + 0x0, 4, 1,
+ imx_clk_mux_flags(dev, "audio_pll1_bypass", base + 0x0, 4, 1,
audio_pll1_bypass_sels,
ARRAY_SIZE(audio_pll1_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMX8MQ_AUDIO_PLL2_BYPASS,
- imx_clk_mux_flags("audio_pll2_bypass", base + 0x8, 4, 1,
+ imx_clk_mux_flags(dev, "audio_pll2_bypass", base + 0x8, 4, 1,
audio_pll2_bypass_sels,
ARRAY_SIZE(audio_pll2_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMX8MQ_VIDEO_PLL1_BYPASS,
- imx_clk_mux_flags("video_pll1_bypass", base + 0x10, 4, 1,
+ imx_clk_mux_flags(dev, "video_pll1_bypass", base + 0x10, 4, 1,
video_pll1_bypass_sels,
ARRAY_SIZE(video_pll1_bypass_sels),
CLK_SET_RATE_PARENT));
/* PLL out gate */
clk_dm(IMX8MQ_DRAM_PLL_OUT,
- imx_clk_gate("dram_pll_out", "dram_pll_ref_sel",
+ imx_clk_gate(dev, "dram_pll_out", "dram_pll_ref_sel",
base + 0x60, 13));
clk_dm(IMX8MQ_ARM_PLL_OUT,
- imx_clk_gate("arm_pll_out", "arm_pll_bypass",
+ imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass",
base + 0x28, 11));
clk_dm(IMX8MQ_GPU_PLL_OUT,
- imx_clk_gate("gpu_pll_out", "gpu_pll_bypass",
+ imx_clk_gate(dev, "gpu_pll_out", "gpu_pll_bypass",
base + 0x18, 11));
clk_dm(IMX8MQ_VPU_PLL_OUT,
- imx_clk_gate("vpu_pll_out", "vpu_pll_bypass",
+ imx_clk_gate(dev, "vpu_pll_out", "vpu_pll_bypass",
base + 0x20, 11));
clk_dm(IMX8MQ_AUDIO_PLL1_OUT,
- imx_clk_gate("audio_pll1_out", "audio_pll1_bypass",
+ imx_clk_gate(dev, "audio_pll1_out", "audio_pll1_bypass",
base + 0x0, 11));
clk_dm(IMX8MQ_AUDIO_PLL2_OUT,
- imx_clk_gate("audio_pll2_out", "audio_pll2_bypass",
+ imx_clk_gate(dev, "audio_pll2_out", "audio_pll2_bypass",
base + 0x8, 11));
clk_dm(IMX8MQ_VIDEO_PLL1_OUT,
- imx_clk_gate("video_pll1_out", "video_pll1_bypass",
+ imx_clk_gate(dev, "video_pll1_out", "video_pll1_bypass",
base + 0x10, 11));
clk_dm(IMX8MQ_SYS1_PLL_OUT,
- imx_clk_gate("sys_pll1_out", "sys1_pll",
+ imx_clk_gate(dev, "sys_pll1_out", "sys1_pll",
base + 0x30, 11));
clk_dm(IMX8MQ_SYS2_PLL_OUT,
- imx_clk_gate("sys_pll2_out", "sys2_pll",
+ imx_clk_gate(dev, "sys_pll2_out", "sys2_pll",
base + 0x3c, 11));
clk_dm(IMX8MQ_SYS3_PLL_OUT,
- imx_clk_gate("sys_pll3_out", "sys3_pll",
+ imx_clk_gate(dev, "sys_pll3_out", "sys3_pll",
base + 0x48, 11));
clk_dm(IMX8MQ_VIDEO2_PLL_OUT,
- imx_clk_gate("video_pll2_out", "video_pll2_ref_sel",
+ imx_clk_gate(dev, "video_pll2_out", "video_pll2_ref_sel",
base + 0x54, 11));
/* SYS PLL fixed output */
clk_dm(IMX8MQ_SYS1_PLL_40M,
- imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20));
+ imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20));
clk_dm(IMX8MQ_SYS1_PLL_80M,
- imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10));
+ imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10));
clk_dm(IMX8MQ_SYS1_PLL_100M,
- imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8));
+ imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8));
clk_dm(IMX8MQ_SYS1_PLL_133M,
- imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6));
+ imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6));
clk_dm(IMX8MQ_SYS1_PLL_160M,
- imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5));
+ imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5));
clk_dm(IMX8MQ_SYS1_PLL_200M,
- imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4));
+ imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4));
clk_dm(IMX8MQ_SYS1_PLL_266M,
- imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3));
+ imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3));
clk_dm(IMX8MQ_SYS1_PLL_400M,
- imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2));
clk_dm(IMX8MQ_SYS1_PLL_800M,
- imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1));
+ imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1));
clk_dm(IMX8MQ_SYS2_PLL_50M,
- imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20));
+ imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20));
clk_dm(IMX8MQ_SYS2_PLL_100M,
- imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10));
+ imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10));
clk_dm(IMX8MQ_SYS2_PLL_125M,
- imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8));
+ imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8));
clk_dm(IMX8MQ_SYS2_PLL_166M,
- imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6));
+ imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6));
clk_dm(IMX8MQ_SYS2_PLL_200M,
- imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5));
+ imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5));
clk_dm(IMX8MQ_SYS2_PLL_250M,
- imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4));
+ imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4));
clk_dm(IMX8MQ_SYS2_PLL_333M,
- imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3));
+ imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3));
clk_dm(IMX8MQ_SYS2_PLL_500M,
- imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2));
clk_dm(IMX8MQ_SYS2_PLL_1000M,
- imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1));
+ imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1));
clk_dm(IMX8MQ_CLK_MON_AUDIO_PLL1_DIV,
- imx_clk_divider("audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3));
+ imx_clk_divider(dev, "audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3));
clk_dm(IMX8MQ_CLK_MON_AUDIO_PLL2_DIV,
- imx_clk_divider("audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3));
+ imx_clk_divider(dev, "audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3));
clk_dm(IMX8MQ_CLK_MON_VIDEO_PLL1_DIV,
- imx_clk_divider("video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3));
+ imx_clk_divider(dev, "video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3));
clk_dm(IMX8MQ_CLK_MON_GPU_PLL_DIV,
- imx_clk_divider("gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3));
+ imx_clk_divider(dev, "gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3));
clk_dm(IMX8MQ_CLK_MON_VPU_PLL_DIV,
- imx_clk_divider("vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3));
+ imx_clk_divider(dev, "vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3));
clk_dm(IMX8MQ_CLK_MON_ARM_PLL_DIV,
- imx_clk_divider("arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3));
+ imx_clk_divider(dev, "arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3));
clk_dm(IMX8MQ_CLK_MON_SYS_PLL1_DIV,
- imx_clk_divider("sys_pll1_out_monitor", "sys_pll1_out", base + 0x7c, 0, 3));
+ imx_clk_divider(dev, "sys_pll1_out_monitor", "sys_pll1_out", base + 0x7c, 0, 3));
clk_dm(IMX8MQ_CLK_MON_SYS_PLL2_DIV,
- imx_clk_divider("sys_pll2_out_monitor", "sys_pll2_out", base + 0x7c, 4, 3));
+ imx_clk_divider(dev, "sys_pll2_out_monitor", "sys_pll2_out", base + 0x7c, 4, 3));
clk_dm(IMX8MQ_CLK_MON_SYS_PLL3_DIV,
- imx_clk_divider("sys_pll3_out_monitor", "sys_pll3_out", base + 0x7c, 8, 3));
+ imx_clk_divider(dev, "sys_pll3_out_monitor", "sys_pll3_out", base + 0x7c, 8, 3));
clk_dm(IMX8MQ_CLK_MON_DRAM_PLL_DIV,
- imx_clk_divider("dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3));
+ imx_clk_divider(dev, "dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3));
clk_dm(IMX8MQ_CLK_MON_VIDEO_PLL2_DIV,
- imx_clk_divider("video_pll2_out_monitor", "video_pll2_out", base + 0x7c, 16, 3));
+ imx_clk_divider(dev, "video_pll2_out_monitor", "video_pll2_out", base + 0x7c, 16, 3));
clk_dm(IMX8MQ_CLK_MON_SEL,
- imx_clk_mux_flags("pllout_monitor_sel", base + 0x74, 0, 4,
+ imx_clk_mux_flags(dev, "pllout_monitor_sel", base + 0x74, 0, 4,
pllout_monitor_sels,
ARRAY_SIZE(pllout_monitor_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMX8MQ_CLK_MON_CLK2_OUT,
- imx_clk_gate4("pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4));
+ imx_clk_gate4(dev, "pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4));
base = dev_read_addr_ptr(dev);
if (!base) {
@@ -349,140 +349,140 @@ static int imx8mq_clk_probe(struct udevice *dev)
}
clk_dm(IMX8MQ_CLK_A53_SRC,
- imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3,
+ imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3,
imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels)));
clk_dm(IMX8MQ_CLK_A53_CG,
- imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
+ imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28));
clk_dm(IMX8MQ_CLK_A53_DIV,
- imx_clk_divider2("arm_a53_div", "arm_a53_cg",
+ imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg",
base + 0x8000, 0, 3));
clk_dm(IMX8MQ_CLK_A53_CORE,
- imx_clk_mux2("arm_a53_src", base + 0x9880, 24, 1,
+ imx_clk_mux2(dev, "arm_a53_src", base + 0x9880, 24, 1,
imx8mq_a53_core_sels, ARRAY_SIZE(imx8mq_a53_core_sels)));
clk_dm(IMX8MQ_CLK_AHB,
- imx8m_clk_composite_critical("ahb", imx8mq_ahb_sels,
+ imx8m_clk_composite_critical(dev, "ahb", imx8mq_ahb_sels,
base + 0x9000));
clk_dm(IMX8MQ_CLK_IPG_ROOT,
- imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1));
+ imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1));
clk_dm(IMX8MQ_CLK_ENET_AXI,
- imx8m_clk_composite("enet_axi", imx8mq_enet_axi_sels,
+ imx8m_clk_composite(dev, "enet_axi", imx8mq_enet_axi_sels,
base + 0x8880));
clk_dm(IMX8MQ_CLK_NAND_USDHC_BUS,
- imx8m_clk_composite_critical("nand_usdhc_bus",
+ imx8m_clk_composite_critical(dev, "nand_usdhc_bus",
imx8mq_nand_usdhc_sels,
base + 0x8900));
clk_dm(IMX8MQ_CLK_USB_BUS,
- imx8m_clk_composite("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80));
+ imx8m_clk_composite(dev, "usb_bus", imx8mq_usb_bus_sels, base + 0x8b80));
/* DRAM */
clk_dm(IMX8MQ_CLK_DRAM_CORE,
- imx_clk_mux2("dram_core_clk", base + 0x9800, 24, 1,
+ imx_clk_mux2(dev, "dram_core_clk", base + 0x9800, 24, 1,
imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels)));
clk_dm(IMX8MQ_CLK_DRAM_ALT,
- imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000));
+ imx8m_clk_composite(dev, "dram_alt", imx8mq_dram_alt_sels, base + 0xa000));
clk_dm(IMX8MQ_CLK_DRAM_APB,
- imx8m_clk_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080));
+ imx8m_clk_composite_critical(dev, "dram_apb", imx8mq_dram_apb_sels, base + 0xa080));
/* IP */
clk_dm(IMX8MQ_CLK_USDHC1,
- imx8m_clk_composite("usdhc1", imx8mq_usdhc1_sels,
+ imx8m_clk_composite(dev, "usdhc1", imx8mq_usdhc1_sels,
base + 0xac00));
clk_dm(IMX8MQ_CLK_USDHC2,
- imx8m_clk_composite("usdhc2", imx8mq_usdhc2_sels,
+ imx8m_clk_composite(dev, "usdhc2", imx8mq_usdhc2_sels,
base + 0xac80));
clk_dm(IMX8MQ_CLK_I2C1,
- imx8m_clk_composite("i2c1", imx8mq_i2c1_sels, base + 0xad00));
+ imx8m_clk_composite(dev, "i2c1", imx8mq_i2c1_sels, base + 0xad00));
clk_dm(IMX8MQ_CLK_I2C2,
- imx8m_clk_composite("i2c2", imx8mq_i2c2_sels, base + 0xad80));
+ imx8m_clk_composite(dev, "i2c2", imx8mq_i2c2_sels, base + 0xad80));
clk_dm(IMX8MQ_CLK_I2C3,
- imx8m_clk_composite("i2c3", imx8mq_i2c3_sels, base + 0xae00));
+ imx8m_clk_composite(dev, "i2c3", imx8mq_i2c3_sels, base + 0xae00));
clk_dm(IMX8MQ_CLK_I2C4,
- imx8m_clk_composite("i2c4", imx8mq_i2c4_sels, base + 0xae80));
+ imx8m_clk_composite(dev, "i2c4", imx8mq_i2c4_sels, base + 0xae80));
clk_dm(IMX8MQ_CLK_WDOG,
- imx8m_clk_composite("wdog", imx8mq_wdog_sels, base + 0xb900));
+ imx8m_clk_composite(dev, "wdog", imx8mq_wdog_sels, base + 0xb900));
clk_dm(IMX8MQ_CLK_UART1,
- imx8m_clk_composite("uart1", imx8mq_uart1_sels, base + 0xaf00));
+ imx8m_clk_composite(dev, "uart1", imx8mq_uart1_sels, base + 0xaf00));
clk_dm(IMX8MQ_CLK_UART2,
- imx8m_clk_composite("uart2", imx8mq_uart2_sels, base + 0xaf80));
+ imx8m_clk_composite(dev, "uart2", imx8mq_uart2_sels, base + 0xaf80));
clk_dm(IMX8MQ_CLK_UART3,
- imx8m_clk_composite("uart3", imx8mq_uart3_sels, base + 0xb000));
+ imx8m_clk_composite(dev, "uart3", imx8mq_uart3_sels, base + 0xb000));
clk_dm(IMX8MQ_CLK_UART4,
- imx8m_clk_composite("uart4", imx8mq_uart4_sels, base + 0xb080));
+ imx8m_clk_composite(dev, "uart4", imx8mq_uart4_sels, base + 0xb080));
clk_dm(IMX8MQ_CLK_QSPI,
- imx8m_clk_composite("qspi", imx8mq_qspi_sels, base + 0xab80));
+ imx8m_clk_composite(dev, "qspi", imx8mq_qspi_sels, base + 0xab80));
clk_dm(IMX8MQ_CLK_USB_CORE_REF,
- imx8m_clk_composite("usb_core_ref", imx8mq_usb_core_sels, base + 0xb100));
+ imx8m_clk_composite(dev, "usb_core_ref", imx8mq_usb_core_sels, base + 0xb100));
clk_dm(IMX8MQ_CLK_USB_PHY_REF,
- imx8m_clk_composite("usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180));
+ imx8m_clk_composite(dev, "usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180));
clk_dm(IMX8MQ_CLK_ECSPI1,
- imx8m_clk_composite("ecspi1", imx8mq_ecspi1_sels, base + 0xb280));
+ imx8m_clk_composite(dev, "ecspi1", imx8mq_ecspi1_sels, base + 0xb280));
clk_dm(IMX8MQ_CLK_ECSPI2,
- imx8m_clk_composite("ecspi2", imx8mq_ecspi2_sels, base + 0xb300));
+ imx8m_clk_composite(dev, "ecspi2", imx8mq_ecspi2_sels, base + 0xb300));
clk_dm(IMX8MQ_CLK_ECSPI3,
- imx8m_clk_composite("ecspi3", imx8mq_ecspi3_sels, base + 0xc180));
+ imx8m_clk_composite(dev, "ecspi3", imx8mq_ecspi3_sels, base + 0xc180));
clk_dm(IMX8MQ_CLK_ECSPI1_ROOT,
- imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
+ imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0));
clk_dm(IMX8MQ_CLK_ECSPI2_ROOT,
- imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
+ imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0));
clk_dm(IMX8MQ_CLK_ECSPI3_ROOT,
- imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
+ imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0));
clk_dm(IMX8MQ_CLK_I2C1_ROOT,
- imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0));
+ imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0));
clk_dm(IMX8MQ_CLK_I2C2_ROOT,
- imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0));
+ imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0));
clk_dm(IMX8MQ_CLK_I2C3_ROOT,
- imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0));
+ imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0));
clk_dm(IMX8MQ_CLK_I2C4_ROOT,
- imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0));
+ imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0));
clk_dm(IMX8MQ_CLK_UART1_ROOT,
- imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0));
+ imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0));
clk_dm(IMX8MQ_CLK_UART2_ROOT,
- imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0));
+ imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0));
clk_dm(IMX8MQ_CLK_UART3_ROOT,
- imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0));
+ imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0));
clk_dm(IMX8MQ_CLK_UART4_ROOT,
- imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0));
+ imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0));
clk_dm(IMX8MQ_CLK_OCOTP_ROOT,
- imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0));
+ imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0));
clk_dm(IMX8MQ_CLK_USDHC1_ROOT,
- imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
+ imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0));
clk_dm(IMX8MQ_CLK_USDHC2_ROOT,
- imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
+ imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0));
clk_dm(IMX8MQ_CLK_WDOG1_ROOT,
- imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0));
+ imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0));
clk_dm(IMX8MQ_CLK_WDOG2_ROOT,
- imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0));
+ imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0));
clk_dm(IMX8MQ_CLK_WDOG3_ROOT,
- imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0));
+ imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0));
clk_dm(IMX8MQ_CLK_QSPI_ROOT,
- imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0));
+ imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0));
clk_dm(IMX8MQ_CLK_USB1_CTRL_ROOT,
- imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));
+ imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));
clk_dm(IMX8MQ_CLK_USB2_CTRL_ROOT,
- imx_clk_gate4("usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0));
+ imx_clk_gate4(dev, "usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0));
clk_dm(IMX8MQ_CLK_USB1_PHY_ROOT,
- imx_clk_gate4("usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0));
+ imx_clk_gate4(dev, "usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0));
clk_dm(IMX8MQ_CLK_USB2_PHY_ROOT,
- imx_clk_gate4("usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0));
+ imx_clk_gate4(dev, "usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0));
clk_dm(IMX8MQ_CLK_ENET_REF,
- imx8m_clk_composite("enet_ref", imx8mq_enet_ref_sels,
+ imx8m_clk_composite(dev, "enet_ref", imx8mq_enet_ref_sels,
base + 0xa980));
clk_dm(IMX8MQ_CLK_ENET_TIMER,
- imx8m_clk_composite("enet_timer", imx8mq_enet_timer_sels,
+ imx8m_clk_composite(dev, "enet_timer", imx8mq_enet_timer_sels,
base + 0xaa00));
clk_dm(IMX8MQ_CLK_ENET_PHY_REF,
- imx8m_clk_composite("enet_phy", imx8mq_enet_phy_sels,
+ imx8m_clk_composite(dev, "enet_phy", imx8mq_enet_phy_sels,
base + 0xaa80));
clk_dm(IMX8MQ_CLK_ENET1_ROOT,
- imx_clk_gate4("enet1_root_clk", "enet_axi",
+ imx_clk_gate4(dev, "enet1_root_clk", "enet_axi",
base + 0x40a0, 0));
clk_dm(IMX8MQ_CLK_DRAM_ALT_ROOT,
- imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4));
+ imx_clk_fixed_factor(dev, "dram_alt_root", "dram_alt", 1, 4));
return 0;
}
diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c
index b31e57a4a01..c3112968c17 100644
--- a/drivers/clk/imx/clk-imx93.c
+++ b/drivers/clk/imx/clk-imx93.c
@@ -291,15 +291,15 @@ static int imx93_clk_probe(struct udevice *dev)
clk_dm(IMX93_CLK_SYS_PLL_PFD0,
clk_register_fixed_rate(NULL, "sys_pll_pfd0", 1000000000));
clk_dm(IMX93_CLK_SYS_PLL_PFD0_DIV2,
- imx_clk_fixed_factor("sys_pll_pfd0_div2", "sys_pll_pfd0", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll_pfd0_div2", "sys_pll_pfd0", 1, 2));
clk_dm(IMX93_CLK_SYS_PLL_PFD1,
clk_register_fixed_rate(NULL, "sys_pll_pfd1", 800000000));
clk_dm(IMX93_CLK_SYS_PLL_PFD1_DIV2,
- imx_clk_fixed_factor("sys_pll_pfd1_div2", "sys_pll_pfd1", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll_pfd1_div2", "sys_pll_pfd1", 1, 2));
clk_dm(IMX93_CLK_SYS_PLL_PFD2,
clk_register_fixed_rate(NULL, "sys_pll_pfd2", 625000000));
clk_dm(IMX93_CLK_SYS_PLL_PFD2_DIV2,
- imx_clk_fixed_factor("sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2));
+ imx_clk_fixed_factor(dev, "sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2));
anatop_base = (void *)ANATOP_BASE_ADDR;
@@ -338,7 +338,7 @@ static int imx93_clk_probe(struct udevice *dev)
}
clk_dm(IMX93_CLK_A55_SEL,
- imx_clk_mux2("a55_sel", base + 0x4820, 0, 1,
+ imx_clk_mux2(dev, "a55_sel", base + 0x4820, 0, 1,
a55_core_sels, ARRAY_SIZE(a55_core_sels)));
return 0;
diff --git a/drivers/clk/imx/clk-imxrt1020.c b/drivers/clk/imx/clk-imxrt1020.c
index 752434cb0ad..c14afdaf236 100644
--- a/drivers/clk/imx/clk-imxrt1020.c
+++ b/drivers/clk/imx/clk-imxrt1020.c
@@ -38,26 +38,26 @@ static int imxrt1020_clk_probe(struct udevice *dev)
base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop"));
clk_dm(IMXRT1020_CLK_PLL2_SYS,
- imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_sys", "osc",
base + 0x30, 0x1));
clk_dm(IMXRT1020_CLK_PLL3_USB_OTG,
- imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc",
base + 0x10, 0x1));
/* PLL bypass out */
clk_dm(IMXRT1020_CLK_PLL2_BYPASS,
- imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1,
+ imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1,
pll2_bypass_sels,
ARRAY_SIZE(pll2_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMXRT1020_CLK_PLL3_BYPASS,
- imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1,
+ imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1,
pll3_bypass_sels,
ARRAY_SIZE(pll3_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMXRT1020_CLK_PLL3_80M,
- imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6));
+ imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6));
clk_dm(IMXRT1020_CLK_PLL2_PFD0_352M,
imx_clk_pfd("pll2_pfd0_352m", "pll2_sys", base + 0x100, 0));
@@ -78,51 +78,51 @@ static int imxrt1020_clk_probe(struct udevice *dev)
return -EINVAL;
clk_dm(IMXRT1020_CLK_PRE_PERIPH_SEL,
- imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2,
+ imx_clk_mux(dev, "pre_periph_sel", base + 0x18, 18, 2,
pre_periph_sels, ARRAY_SIZE(pre_periph_sels)));
clk_dm(IMXRT1020_CLK_PERIPH_SEL,
- imx_clk_mux("periph_sel", base + 0x14, 25, 1,
+ imx_clk_mux(dev, "periph_sel", base + 0x14, 25, 1,
periph_sels, ARRAY_SIZE(periph_sels)));
clk_dm(IMXRT1020_CLK_USDHC1_SEL,
- imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
+ imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMXRT1020_CLK_USDHC2_SEL,
- imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
+ imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMXRT1020_CLK_LPUART_SEL,
- imx_clk_mux("lpuart_sel", base + 0x24, 6, 1,
+ imx_clk_mux(dev, "lpuart_sel", base + 0x24, 6, 1,
lpuart_sels, ARRAY_SIZE(lpuart_sels)));
clk_dm(IMXRT1020_CLK_SEMC_ALT_SEL,
- imx_clk_mux("semc_alt_sel", base + 0x14, 7, 1,
+ imx_clk_mux(dev, "semc_alt_sel", base + 0x14, 7, 1,
semc_alt_sels, ARRAY_SIZE(semc_alt_sels)));
clk_dm(IMXRT1020_CLK_SEMC_SEL,
- imx_clk_mux("semc_sel", base + 0x14, 6, 1,
+ imx_clk_mux(dev, "semc_sel", base + 0x14, 6, 1,
semc_sels, ARRAY_SIZE(semc_sels)));
clk_dm(IMXRT1020_CLK_AHB_PODF,
- imx_clk_divider("ahb_podf", "periph_sel",
+ imx_clk_divider(dev, "ahb_podf", "periph_sel",
base + 0x14, 10, 3));
clk_dm(IMXRT1020_CLK_USDHC1_PODF,
- imx_clk_divider("usdhc1_podf", "usdhc1_sel",
+ imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel",
base + 0x24, 11, 3));
clk_dm(IMXRT1020_CLK_USDHC2_PODF,
- imx_clk_divider("usdhc2_podf", "usdhc2_sel",
+ imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel",
base + 0x24, 16, 3));
clk_dm(IMXRT1020_CLK_LPUART_PODF,
- imx_clk_divider("lpuart_podf", "lpuart_sel",
+ imx_clk_divider(dev, "lpuart_podf", "lpuart_sel",
base + 0x24, 0, 6));
clk_dm(IMXRT1020_CLK_SEMC_PODF,
- imx_clk_divider("semc_podf", "semc_sel",
+ imx_clk_divider(dev, "semc_podf", "semc_sel",
base + 0x14, 16, 3));
clk_dm(IMXRT1020_CLK_USDHC1,
- imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
+ imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2));
clk_dm(IMXRT1020_CLK_USDHC2,
- imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
+ imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4));
clk_dm(IMXRT1020_CLK_LPUART1,
- imx_clk_gate2("lpuart1", "lpuart_podf", base + 0x7c, 24));
+ imx_clk_gate2(dev, "lpuart1", "lpuart_podf", base + 0x7c, 24));
clk_dm(IMXRT1020_CLK_SEMC,
- imx_clk_gate2("semc", "semc_podf", base + 0x74, 4));
+ imx_clk_gate2(dev, "semc", "semc_podf", base + 0x74, 4));
#ifdef CONFIG_XPL_BUILD
struct clk *clk, *clk1;
diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c
index 2c029ec5a6e..ba5b48748ef 100644
--- a/drivers/clk/imx/clk-imxrt1050.c
+++ b/drivers/clk/imx/clk-imxrt1050.c
@@ -36,63 +36,63 @@ static int imxrt1050_clk_probe(struct udevice *dev)
base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop"));
clk_dm(IMXRT1050_CLK_PLL1_REF_SEL,
- imx_clk_mux("pll1_arm_ref_sel", base + 0x0, 14, 2,
+ imx_clk_mux(dev, "pll1_arm_ref_sel", base + 0x0, 14, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMXRT1050_CLK_PLL2_REF_SEL,
- imx_clk_mux("pll2_sys_ref_sel", base + 0x30, 14, 2,
+ imx_clk_mux(dev, "pll2_sys_ref_sel", base + 0x30, 14, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMXRT1050_CLK_PLL3_REF_SEL,
- imx_clk_mux("pll3_usb_otg_ref_sel", base + 0x10, 14, 2,
+ imx_clk_mux(dev, "pll3_usb_otg_ref_sel", base + 0x10, 14, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMXRT1050_CLK_PLL5_REF_SEL,
- imx_clk_mux("pll5_video_ref_sel", base + 0xa0, 14, 2,
+ imx_clk_mux(dev, "pll5_video_ref_sel", base + 0xa0, 14, 2,
pll_ref_sels, ARRAY_SIZE(pll_ref_sels)));
clk_dm(IMXRT1050_CLK_PLL1_ARM,
- imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_arm", "pll1_arm_ref_sel",
+ imx_clk_pllv3(dev, IMX_PLLV3_SYS, "pll1_arm", "pll1_arm_ref_sel",
base + 0x0, 0x7f));
clk_dm(IMXRT1050_CLK_PLL2_SYS,
- imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", "pll2_sys_ref_sel",
+ imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_sys", "pll2_sys_ref_sel",
base + 0x30, 0x1));
clk_dm(IMXRT1050_CLK_PLL3_USB_OTG,
- imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg",
+ imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg",
"pll3_usb_otg_ref_sel",
base + 0x10, 0x1));
clk_dm(IMXRT1050_CLK_PLL5_VIDEO,
- imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "pll5_video_ref_sel",
+ imx_clk_pllv3(dev, IMX_PLLV3_AV, "pll5_video", "pll5_video_ref_sel",
base + 0xa0, 0x7f));
/* PLL bypass out */
clk_dm(IMXRT1050_CLK_PLL1_BYPASS,
- imx_clk_mux_flags("pll1_bypass", base + 0x0, 16, 1,
+ imx_clk_mux_flags(dev, "pll1_bypass", base + 0x0, 16, 1,
pll1_bypass_sels,
ARRAY_SIZE(pll1_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMXRT1050_CLK_PLL2_BYPASS,
- imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1,
+ imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1,
pll2_bypass_sels,
ARRAY_SIZE(pll2_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMXRT1050_CLK_PLL3_BYPASS,
- imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1,
+ imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1,
pll3_bypass_sels,
ARRAY_SIZE(pll3_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMXRT1050_CLK_PLL5_BYPASS,
- imx_clk_mux_flags("pll5_bypass", base + 0xa0, 16, 1,
+ imx_clk_mux_flags(dev, "pll5_bypass", base + 0xa0, 16, 1,
pll5_bypass_sels,
ARRAY_SIZE(pll5_bypass_sels),
CLK_SET_RATE_PARENT));
clk_dm(IMXRT1050_CLK_VIDEO_POST_DIV_SEL,
- imx_clk_divider("video_post_div_sel", "pll5_video",
+ imx_clk_divider(dev, "video_post_div_sel", "pll5_video",
base + 0xa0, 19, 2));
clk_dm(IMXRT1050_CLK_VIDEO_DIV,
- imx_clk_divider("video_div", "video_post_div_sel",
+ imx_clk_divider(dev, "video_div", "video_post_div_sel",
base + 0x170, 30, 2));
clk_dm(IMXRT1050_CLK_PLL3_80M,
- imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6));
+ imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6));
clk_dm(IMXRT1050_CLK_PLL2_PFD0_352M,
imx_clk_pfd("pll2_pfd0_352m", "pll2_sys", base + 0x100, 0));
@@ -113,73 +113,73 @@ static int imxrt1050_clk_probe(struct udevice *dev)
return -EINVAL;
clk_dm(IMXRT1050_CLK_ARM_PODF,
- imx_clk_divider("arm_podf", "pll1_arm",
+ imx_clk_divider(dev, "arm_podf", "pll1_arm",
base + 0x10, 0, 3));
clk_dm(IMXRT1050_CLK_PRE_PERIPH_SEL,
- imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2,
+ imx_clk_mux(dev, "pre_periph_sel", base + 0x18, 18, 2,
pre_periph_sels, ARRAY_SIZE(pre_periph_sels)));
clk_dm(IMXRT1050_CLK_PERIPH_SEL,
- imx_clk_mux("periph_sel", base + 0x14, 25, 1,
+ imx_clk_mux(dev, "periph_sel", base + 0x14, 25, 1,
periph_sels, ARRAY_SIZE(periph_sels)));
clk_dm(IMXRT1050_CLK_USDHC1_SEL,
- imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
+ imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMXRT1050_CLK_USDHC2_SEL,
- imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
+ imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1,
usdhc_sels, ARRAY_SIZE(usdhc_sels)));
clk_dm(IMXRT1050_CLK_LPUART_SEL,
- imx_clk_mux("lpuart_sel", base + 0x24, 6, 1,
+ imx_clk_mux(dev, "lpuart_sel", base + 0x24, 6, 1,
lpuart_sels, ARRAY_SIZE(lpuart_sels)));
clk_dm(IMXRT1050_CLK_SEMC_ALT_SEL,
- imx_clk_mux("semc_alt_sel", base + 0x14, 7, 1,
+ imx_clk_mux(dev, "semc_alt_sel", base + 0x14, 7, 1,
semc_alt_sels, ARRAY_SIZE(semc_alt_sels)));
clk_dm(IMXRT1050_CLK_SEMC_SEL,
- imx_clk_mux("semc_sel", base + 0x14, 6, 1,
+ imx_clk_mux(dev, "semc_sel", base + 0x14, 6, 1,
semc_sels, ARRAY_SIZE(semc_sels)));
clk_dm(IMXRT1050_CLK_LCDIF_SEL,
- imx_clk_mux("lcdif_sel", base + 0x38, 15, 3,
+ imx_clk_mux(dev, "lcdif_sel", base + 0x38, 15, 3,
lcdif_sels, ARRAY_SIZE(lcdif_sels)));
clk_dm(IMXRT1050_CLK_AHB_PODF,
- imx_clk_divider("ahb_podf", "periph_sel",
+ imx_clk_divider(dev, "ahb_podf", "periph_sel",
base + 0x14, 10, 3));
clk_dm(IMXRT1050_CLK_IPG_PDOF,
- imx_clk_divider("ipg_podf", "ahb_podf",
+ imx_clk_divider(dev, "ipg_podf", "ahb_podf",
base + 0x14, 8, 2));
clk_dm(IMXRT1050_CLK_USDHC1_PODF,
- imx_clk_divider("usdhc1_podf", "usdhc1_sel",
+ imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel",
base + 0x24, 11, 3));
clk_dm(IMXRT1050_CLK_USDHC2_PODF,
- imx_clk_divider("usdhc2_podf", "usdhc2_sel",
+ imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel",
base + 0x24, 16, 3));
clk_dm(IMXRT1050_CLK_LPUART_PODF,
- imx_clk_divider("lpuart_podf", "lpuart_sel",
+ imx_clk_divider(dev, "lpuart_podf", "lpuart_sel",
base + 0x24, 0, 6));
clk_dm(IMXRT1050_CLK_SEMC_PODF,
- imx_clk_divider("semc_podf", "semc_sel",
+ imx_clk_divider(dev, "semc_podf", "semc_sel",
base + 0x14, 16, 3));
clk_dm(IMXRT1050_CLK_LCDIF_PRED,
- imx_clk_divider("lcdif_pred", "lcdif_sel",
+ imx_clk_divider(dev, "lcdif_pred", "lcdif_sel",
base + 0x38, 12, 3));
clk_dm(IMXRT1050_CLK_LCDIF_PODF,
- imx_clk_divider("lcdif_podf", "lcdif_pred",
+ imx_clk_divider(dev, "lcdif_podf", "lcdif_pred",
base + 0x18, 23, 3));
clk_dm(IMXRT1050_CLK_USDHC1,
- imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
+ imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2));
clk_dm(IMXRT1050_CLK_USDHC2,
- imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
+ imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4));
clk_dm(IMXRT1050_CLK_LPUART1,
- imx_clk_gate2("lpuart1", "lpuart_podf", base + 0x7c, 24));
+ imx_clk_gate2(dev, "lpuart1", "lpuart_podf", base + 0x7c, 24));
clk_dm(IMXRT1050_CLK_SEMC,
- imx_clk_gate2("semc", "semc_podf", base + 0x74, 4));
+ imx_clk_gate2(dev, "semc", "semc_podf", base + 0x74, 4));
clk_dm(IMXRT1050_CLK_LCDIF_APB,
- imx_clk_gate2("lcdif", "lcdif_podf", base + 0x70, 28));
+ imx_clk_gate2(dev, "lcdif", "lcdif_podf", base + 0x70, 28));
clk_dm(IMXRT1050_CLK_LCDIF_PIX,
- imx_clk_gate2("lcdif_pix", "lcdif", base + 0x74, 10));
+ imx_clk_gate2(dev, "lcdif_pix", "lcdif", base + 0x74, 10));
clk_dm(IMXRT1050_CLK_USBOH3,
- imx_clk_gate2("usboh3", "pll3_usb_otg", base + 0x80, 0));
+ imx_clk_gate2(dev, "usboh3", "pll3_usb_otg", base + 0x80, 0));
struct clk *clk, *clk1;
diff --git a/drivers/clk/imx/clk-imxrt1170.c b/drivers/clk/imx/clk-imxrt1170.c
index 88a294f4165..3f55d0d0127 100644
--- a/drivers/clk/imx/clk-imxrt1170.c
+++ b/drivers/clk/imx/clk-imxrt1170.c
@@ -114,20 +114,20 @@ static int imxrt1170_clk_probe(struct udevice *dev)
base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop"));
clk_dm(IMXRT1170_CLK_RCOSC_48M,
- imx_clk_fixed_factor("rcosc48M", "rcosc16M", 3, 1));
+ imx_clk_fixed_factor(dev, "rcosc48M", "rcosc16M", 3, 1));
clk_dm(IMXRT1170_CLK_RCOSC_400M,
- imx_clk_fixed_factor("rcosc400M", "rcosc16M", 25, 1));
+ imx_clk_fixed_factor(dev, "rcosc400M", "rcosc16M", 25, 1));
clk_dm(IMXRT1170_CLK_RCOSC_48M_DIV2,
- imx_clk_fixed_factor("rcosc48M_div2", "rcosc48M", 1, 2));
+ imx_clk_fixed_factor(dev, "rcosc48M_div2", "rcosc48M", 1, 2));
clk_dm(IMXRT1170_CLK_PLL_ARM,
- imx_clk_pllv3(IMX_PLLV3_SYS, "pll_arm", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_SYS, "pll_arm", "osc",
base + 0x200, 0xff));
clk_dm(IMXRT1170_CLK_PLL3,
- imx_clk_pllv3(IMX_PLLV3_GENERICV2, "pll3_sys", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_GENERICV2, "pll3_sys", "osc",
base + 0x210, 1));
clk_dm(IMXRT1170_CLK_PLL2,
- imx_clk_pllv3(IMX_PLLV3_GENERICV2, "pll2_sys", "osc",
+ imx_clk_pllv3(dev, IMX_PLLV3_GENERICV2, "pll2_sys", "osc",
base + 0x240, 1));
clk_dm(IMXRT1170_CLK_PLL3_PFD0,
@@ -149,7 +149,7 @@ static int imxrt1170_clk_probe(struct udevice *dev)
imx_clk_pfd("pll2_pfd3", "pll2_sys", base + 0x270, 3));
clk_dm(IMXRT1170_CLK_PLL3_DIV2,
- imx_clk_fixed_factor("pll3_div2", "pll3_sys", 1, 2));
+ imx_clk_fixed_factor(dev, "pll3_div2", "pll3_sys", 1, 2));
/* CCM clocks */
base = dev_read_addr_ptr(dev);
@@ -157,31 +157,31 @@ static int imxrt1170_clk_probe(struct udevice *dev)
return -EINVAL;
clk_dm(IMXRT1170_CLK_LPUART1_SEL,
- imx_clk_mux("lpuart1_sel", base + (25 * 0x80), 8, 3,
+ imx_clk_mux(dev, "lpuart1_sel", base + (25 * 0x80), 8, 3,
lpuart1_sels, ARRAY_SIZE(lpuart1_sels)));
clk_dm(IMXRT1170_CLK_LPUART1,
- imx_clk_divider("lpuart1", "lpuart1_sel",
+ imx_clk_divider(dev, "lpuart1", "lpuart1_sel",
base + (25 * 0x80), 0, 8));
clk_dm(IMXRT1170_CLK_USDHC1_SEL,
- imx_clk_mux("usdhc1_sel", base + (58 * 0x80), 8, 3,
+ imx_clk_mux(dev, "usdhc1_sel", base + (58 * 0x80), 8, 3,
usdhc1_sels, ARRAY_SIZE(usdhc1_sels)));
clk_dm(IMXRT1170_CLK_USDHC1,
- imx_clk_divider("usdhc1", "usdhc1_sel",
+ imx_clk_divider(dev, "usdhc1", "usdhc1_sel",
base + (58 * 0x80), 0, 8));
clk_dm(IMXRT1170_CLK_GPT1_SEL,
- imx_clk_mux("gpt1_sel", base + (14 * 0x80), 8, 3,
+ imx_clk_mux(dev, "gpt1_sel", base + (14 * 0x80), 8, 3,
gpt1_sels, ARRAY_SIZE(gpt1_sels)));
clk_dm(IMXRT1170_CLK_GPT1,
- imx_clk_divider("gpt1", "gpt1_sel",
+ imx_clk_divider(dev, "gpt1", "gpt1_sel",
base + (14 * 0x80), 0, 8));
clk_dm(IMXRT1170_CLK_SEMC_SEL,
- imx_clk_mux("semc_sel", base + (4 * 0x80), 8, 3,
+ imx_clk_mux(dev, "semc_sel", base + (4 * 0x80), 8, 3,
semc_sels, ARRAY_SIZE(semc_sels)));
clk_dm(IMXRT1170_CLK_SEMC,
- imx_clk_divider("semc", "semc_sel",
+ imx_clk_divider(dev, "semc", "semc_sel",
base + (4 * 0x80), 0, 8));
struct clk *clk, *clk1;
diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
index c6692f2f9f5..85b6a9809e8 100644
--- a/drivers/clk/imx/clk-pllv3.c
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -281,9 +281,9 @@ static const struct clk_ops clk_pllv3_enet_ops = {
.get_rate = clk_pllv3_enet_get_rate,
};
-struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
- const char *parent_name, void __iomem *base,
- u32 div_mask)
+struct clk *imx_clk_pllv3(struct udevice *dev, enum imx_pllv3_type type,
+ const char *name, const char *parent_name,
+ void __iomem *base, u32 div_mask)
{
struct clk_pllv3 *pll;
struct clk *clk;
@@ -339,7 +339,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
pll->div_mask = div_mask;
clk = &pll->clk;
- ret = clk_register(clk, drv_name, name, parent_name);
+ ret = clk_register(clk, drv_name, name,
+ clk_resolve_parent_clk(dev, parent_name));
if (ret) {
kfree(pll);
return ERR_PTR(ret);
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 27a53ae5583..7d14dbc395f 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -78,84 +78,89 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
void __iomem *base,
const struct imx_pll14xx_clk *pll_clk);
-struct clk *clk_register_gate2(struct device *dev, const char *name,
+struct clk *clk_register_gate2(struct udevice *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx, u8 cgr_val,
u8 clk_gate_flags, unsigned int *share_count);
-struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
- const char *parent_name, void __iomem *base,
- u32 div_mask);
+struct clk *imx_clk_pllv3(struct udevice *dev, enum imx_pllv3_type type,
+ const char *name, const char *parent_name,
+ void __iomem *base, u32 div_mask);
-static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
- void __iomem *reg, u8 shift)
+static inline struct clk *imx_clk_gate2(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift)
{
- return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT, reg,
shift, 0x3, 0, NULL);
}
-static inline struct clk *imx_clk_gate2_shared(const char *name,
+static inline struct clk *imx_clk_gate2_shared(struct udevice *dev, const char *name,
const char *parent,
void __iomem *reg, u8 shift,
unsigned int *share_count)
{
- return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT, reg,
shift, 0x3, 0, share_count);
}
-static inline struct clk *imx_clk_gate2_shared2(const char *name,
+static inline struct clk *imx_clk_gate2_shared2(struct udevice *dev, const char *name,
const char *parent,
void __iomem *reg, u8 shift,
unsigned int *share_count)
{
- return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT |
+ return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT |
CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0,
share_count);
}
-static inline struct clk *imx_clk_gate4(const char *name, const char *parent,
+static inline struct clk *imx_clk_gate4(struct udevice *dev, const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
- return clk_register_gate2(NULL, name, parent,
+ return clk_register_gate2(dev, name, parent,
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
reg, shift, 0x3, 0, NULL);
}
-static inline struct clk *imx_clk_gate4_flags(const char *name,
+static inline struct clk *imx_clk_gate4_flags(struct udevice *dev, const char *name,
const char *parent, void __iomem *reg, u8 shift,
unsigned long flags)
{
- return clk_register_gate2(NULL, name, parent,
+ return clk_register_gate2(dev, name, parent,
flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
reg, shift, 0x3, 0, NULL);
}
-static inline struct clk *imx_clk_fixed_factor(const char *name,
- const char *parent, unsigned int mult, unsigned int div)
+static inline struct clk *
+imx_clk_fixed_factor(struct udevice *dev, const char *name, const char *parent,
+ unsigned int mult, unsigned int div)
{
- return clk_register_fixed_factor(NULL, name, parent,
+ return clk_register_fixed_factor(dev, name, parent,
CLK_SET_RATE_PARENT, mult, div);
}
-static inline struct clk *imx_clk_divider(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width)
+static inline struct clk *imx_clk_divider(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, u8 width)
{
- return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+ return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT,
reg, shift, width, 0);
}
static inline struct clk *
-imx_clk_busy_divider(const char *name, const char *parent, void __iomem *reg,
- u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift)
+imx_clk_busy_divider(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg, u8 shift, u8 width,
+ void __iomem *busy_reg, u8 busy_shift)
{
- return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+ return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT,
reg, shift, width, 0);
}
-static inline struct clk *imx_clk_divider2(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width)
+static inline struct clk *imx_clk_divider2(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, u8 width)
{
- return clk_register_divider(NULL, name, parent,
+ return clk_register_divider(dev, name, parent,
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
reg, shift, width, 0);
}
@@ -167,90 +172,93 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents,
int num_parents, void (*fixup)(u32 *val));
-static inline struct clk *imx_clk_mux_flags(const char *name,
+static inline struct clk *imx_clk_mux_flags(struct udevice *dev, const char *name,
void __iomem *reg, u8 shift, u8 width,
const char * const *parents, int num_parents,
unsigned long flags)
{
- return clk_register_mux(NULL, name, parents, num_parents,
+ return clk_register_mux(dev, name, parents, num_parents,
flags | CLK_SET_RATE_NO_REPARENT, reg, shift,
width, 0);
}
-static inline struct clk *imx_clk_mux2_flags(const char *name,
+static inline struct clk *imx_clk_mux2_flags(struct udevice *dev, const char *name,
void __iomem *reg, u8 shift, u8 width,
const char * const *parents,
int num_parents, unsigned long flags)
{
- return clk_register_mux(NULL, name, parents, num_parents,
+ return clk_register_mux(dev, name, parents, num_parents,
flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE,
reg, shift, width, 0);
}
-static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
- u8 shift, u8 width, const char * const *parents,
+static inline struct clk *imx_clk_mux(struct udevice *dev, const char *name,
+ void __iomem *reg, u8 shift, u8 width, const char * const *parents,
int num_parents)
{
- return clk_register_mux(NULL, name, parents, num_parents,
+ return clk_register_mux(dev, name, parents, num_parents,
CLK_SET_RATE_NO_REPARENT, reg, shift,
width, 0);
}
static inline struct clk *
-imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, u8 width,
+imx_clk_busy_mux(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width,
void __iomem *busy_reg, u8 busy_shift,
const char * const *parents, int num_parents)
{
- return clk_register_mux(NULL, name, parents, num_parents,
+ return clk_register_mux(dev, name, parents, num_parents,
CLK_SET_RATE_NO_REPARENT, reg, shift,
width, 0);
}
-static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
+static inline struct clk *imx_clk_mux2(struct udevice *dev, const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents,
int num_parents)
{
- return clk_register_mux(NULL, name, parents, num_parents,
+ return clk_register_mux(dev, name, parents, num_parents,
CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE,
reg, shift, width, 0);
}
-static inline struct clk *imx_clk_gate(const char *name, const char *parent,
- void __iomem *reg, u8 shift)
+static inline struct clk *imx_clk_gate(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift)
{
- return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ return clk_register_gate(dev, name, parent, CLK_SET_RATE_PARENT, reg,
shift, 0, NULL);
}
-static inline struct clk *imx_clk_gate_flags(const char *name, const char *parent,
- void __iomem *reg, u8 shift, unsigned long flags)
+static inline struct clk *imx_clk_gate_flags(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, unsigned long flags)
{
- return clk_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg,
+ return clk_register_gate(dev, name, parent, flags | CLK_SET_RATE_PARENT, reg,
shift, 0, NULL);
}
-static inline struct clk *imx_clk_gate3(const char *name, const char *parent,
- void __iomem *reg, u8 shift)
+static inline struct clk *imx_clk_gate3(struct udevice *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift)
{
- return clk_register_gate(NULL, name, parent,
+ return clk_register_gate(dev, name, parent,
CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
reg, shift, 0, NULL);
}
-struct clk *imx8m_clk_composite_flags(const char *name,
+struct clk *imx8m_clk_composite_flags(struct udevice *dev, const char *name,
const char * const *parent_names,
int num_parents, void __iomem *reg, unsigned long flags);
-#define __imx8m_clk_composite(name, parent_names, reg, flags) \
- imx8m_clk_composite_flags(name, parent_names, \
+#define __imx8m_clk_composite(dev, name, parent_names, reg, flags) \
+ imx8m_clk_composite_flags(dev, name, parent_names, \
ARRAY_SIZE(parent_names), reg, \
flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
-#define imx8m_clk_composite(name, parent_names, reg) \
- __imx8m_clk_composite(name, parent_names, reg, 0)
+#define imx8m_clk_composite(dev, name, parent_names, reg) \
+ __imx8m_clk_composite(dev, name, parent_names, reg, 0)
-#define imx8m_clk_composite_critical(name, parent_names, reg) \
- __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL)
+#define imx8m_clk_composite_critical(dev, name, parent_names, reg) \
+ __imx8m_clk_composite(dev, name, parent_names, reg, CLK_IS_CRITICAL)
struct clk *imx93_clk_composite_flags(const char *name,
const char * const *parent_names,
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index e412c92cafc..12893687b68 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -3,7 +3,6 @@
obj-$(CONFIG_ARCH_MEDIATEK) += clk-mtk.o
# SoC Drivers
-obj-$(CONFIG_MT8512) += clk-mt8512.o
obj-$(CONFIG_TARGET_MT7623) += clk-mt7623.o
obj-$(CONFIG_TARGET_MT7622) += clk-mt7622.o
obj-$(CONFIG_TARGET_MT7629) += clk-mt7629.o
@@ -13,5 +12,6 @@ obj-$(CONFIG_TARGET_MT7988) += clk-mt7988.o
obj-$(CONFIG_TARGET_MT7987) += clk-mt7987.o
obj-$(CONFIG_TARGET_MT8183) += clk-mt8183.o
obj-$(CONFIG_TARGET_MT8365) += clk-mt8365.o
+obj-$(CONFIG_TARGET_MT8512) += clk-mt8512.o
obj-$(CONFIG_TARGET_MT8516) += clk-mt8516.o
obj-$(CONFIG_TARGET_MT8518) += clk-mt8518.o
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index cb867acc48c..3ea01f3c969 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -31,6 +31,14 @@ config CLK_QCOM_IPQ4019
on the Snapdragon IPQ4019 SoC. This driver supports the clocks
and resets exposed by the GCC hardware block.
+config CLK_QCOM_IPQ9574
+ bool "Qualcomm IPQ9574 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon IPQ9574 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
config CLK_QCOM_QCM2290
bool "Qualcomm QCM2290 GCC"
select CLK_QCOM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 1bc0f15005b..e13fc8c1071 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_CLK_QCOM_SDM845) += clock-sdm845.o
obj-$(CONFIG_CLK_QCOM_APQ8016) += clock-apq8016.o
obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o
obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o
+obj-$(CONFIG_CLK_QCOM_IPQ9574) += clock-ipq9574.o
obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o
obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o
obj-$(CONFIG_CLK_QCOM_SA8775P) += clock-sa8775p.o
diff --git a/drivers/clk/qcom/clock-apq8016.c b/drivers/clk/qcom/clock-apq8016.c
index b5def55dbc2..6a53f900a9e 100644
--- a/drivers/clk/qcom/clock-apq8016.c
+++ b/drivers/clk/qcom/clock-apq8016.c
@@ -54,8 +54,9 @@ static struct vote_clk gcc_blsp1_ahb_clk = {
};
static const struct gate_clk apq8016_clks[] = {
- GATE_CLK(GCC_USB_HS_AHB_CLK, 0x41008, 0x00000001),
- GATE_CLK(GCC_USB_HS_SYSTEM_CLK, 0x41004, 0x00000001),
+ GATE_CLK(GCC_PRNG_AHB_CLK, 0x45004, BIT(8)),
+ GATE_CLK(GCC_USB_HS_AHB_CLK, 0x41008, BIT(0)),
+ GATE_CLK(GCC_USB_HS_SYSTEM_CLK, 0x41004, BIT(0)),
};
/* SDHCI */
@@ -139,15 +140,14 @@ static int apq8016_clk_enable(struct clk *clk)
{
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
- if (priv->data->num_clks < clk->id) {
+ if (priv->data->num_clks < clk->id || !apq8016_clks[clk->id].reg) {
log_warning("%s: unknown clk id %lu\n", __func__, clk->id);
return 0;
}
- debug("%s: clk %s\n", __func__, apq8016_clks[clk->id].name);
- qcom_gate_clk_en(priv, clk->id);
+ debug("%s: enabling clock %s\n", __func__, apq8016_clks[clk->id].name);
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static struct msm_clk_data apq8016_clk_data = {
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-ipq9574.c b/drivers/clk/qcom/clock-ipq9574.c
new file mode 100644
index 00000000000..b0af4036059
--- /dev/null
+++ b/drivers/clk/qcom/clock-ipq9574.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock drivers for Qualcomm ipq9574
+ *
+ * (C) Copyright 2025 Linaro Ltd.
+ */
+
+#include <linux/types.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <dt-bindings/clock/qcom,ipq9574-gcc.h>
+#include <dt-bindings/reset/qcom,ipq9574-gcc.h>
+
+#include "clock-qcom.h"
+
+#define GCC_BLSP1_AHB_CBCR 0x1004
+#define GCC_BLSP1_UART3_APPS_CMD_RCGR 0x402C
+#define GCC_BLSP1_UART3_APPS_CBCR 0x4054
+
+#define GCC_SDCC1_APPS_CBCR 0x3302C
+#define GCC_SDCC1_AHB_CBCR 0x33034
+#define GCC_SDCC1_APPS_CMD_RCGR 0x33004
+#define GCC_SDCC1_ICE_CORE_CBCR 0x33030
+
+static ulong ipq9574_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GCC_BLSP1_UART3_APPS_CLK:
+ clk_rcg_set_rate_mnd(priv->base, GCC_BLSP1_UART3_APPS_CMD_RCGR,
+ 0, 144, 15625, CFG_CLK_SRC_GPLL0, 16);
+ return rate;
+ case GCC_SDCC1_APPS_CLK:
+ clk_rcg_set_rate_mnd(priv->base, GCC_SDCC1_APPS_CMD_RCGR,
+ 11, 0, 0, CFG_CLK_SRC_GPLL2, 16);
+ return rate;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct gate_clk ipq9574_clks[] = {
+ GATE_CLK(GCC_BLSP1_UART3_APPS_CLK, 0x4054, 0x00000001),
+ GATE_CLK(GCC_BLSP1_AHB_CLK, 0x1004, 0x00000001),
+ GATE_CLK(GCC_SDCC1_AHB_CLK, 0x33034, 0x00000001),
+ GATE_CLK(GCC_SDCC1_APPS_CLK, 0x3302C, 0x00000001),
+ GATE_CLK(GCC_SDCC1_ICE_CORE_CLK, 0x33030, 0x00000001),
+};
+
+static int ipq9574_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ debug("%s: clk %s\n", __func__, ipq9574_clks[clk->id].name);
+
+ if (!ipq9574_clks[clk->id].reg)
+ return -EINVAL;
+
+ qcom_gate_clk_en(priv, clk->id);
+
+ return 0;
+}
+
+static const struct qcom_reset_map ipq9574_gcc_resets[] = {
+ [GCC_SDCC_BCR] = { 0x33000 },
+};
+
+static struct msm_clk_data ipq9574_gcc_data = {
+ .resets = ipq9574_gcc_resets,
+ .num_resets = ARRAY_SIZE(ipq9574_gcc_resets),
+ .enable = ipq9574_enable,
+ .set_rate = ipq9574_set_rate,
+};
+
+static const struct udevice_id gcc_ipq9574_of_match[] = {
+ {
+ .compatible = "qcom,ipq9574-gcc",
+ .data = (ulong)&ipq9574_gcc_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(gcc_ipq9574) = {
+ .name = "gcc_ipq9574",
+ .id = UCLASS_NOP,
+ .of_match = gcc_ipq9574_of_match,
+ .bind = qcom_cc_bind,
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF,
+};
diff --git a/drivers/clk/qcom/clock-qcm2290.c b/drivers/clk/qcom/clock-qcm2290.c
index c78705cb8cf..1326b770c3e 100644
--- a/drivers/clk/qcom/clock-qcm2290.c
+++ b/drivers/clk/qcom/clock-qcm2290.c
@@ -134,9 +134,7 @@ static int qcm2290_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map qcm2290_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h
index ff336dea39c..f43edea2525 100644
--- a/drivers/clk/qcom/clock-qcom.h
+++ b/drivers/clk/qcom/clock-qcom.h
@@ -7,10 +7,12 @@
#include <asm/io.h>
#include <linux/bitfield.h>
+#include <errno.h>
#define CFG_CLK_SRC_CXO (0 << 8)
#define CFG_CLK_SRC_GPLL0 (1 << 8)
#define CFG_CLK_SRC_GPLL0_AUX2 (2 << 8)
+#define CFG_CLK_SRC_GPLL2 (2 << 8)
#define CFG_CLK_SRC_GPLL9 (2 << 8)
#define CFG_CLK_SRC_GPLL0_ODD (3 << 8)
#define CFG_CLK_SRC_GPLL6 (4 << 8)
@@ -105,14 +107,19 @@ void clk_rcg_set_rate(phys_addr_t base, uint32_t cmd_rcgr, int div,
int source);
void clk_phy_mux_enable(phys_addr_t base, uint32_t cmd_rcgr, bool enabled);
-static inline void qcom_gate_clk_en(const struct msm_clk_priv *priv, unsigned long id)
+static inline int qcom_gate_clk_en(const struct msm_clk_priv *priv, unsigned long id)
{
u32 val;
- if (id >= priv->data->num_clks || priv->data->clks[id].reg == 0)
- return;
+ if (id >= priv->data->num_clks || priv->data->clks[id].reg == 0) {
+ log_err("gcc@%#08llx: unknown clock ID %lu!\n",
+ priv->base, id);
+ return -ENOENT;
+ }
val = readl(priv->base + priv->data->clks[id].reg);
writel(val | priv->data->clks[id].en_val, priv->base + priv->data->clks[id].reg);
+
+ return 0;
}
#endif
diff --git a/drivers/clk/qcom/clock-sa8775p.c b/drivers/clk/qcom/clock-sa8775p.c
index e31f24ed4f0..527cecf5c82 100644
--- a/drivers/clk/qcom/clock-sa8775p.c
+++ b/drivers/clk/qcom/clock-sa8775p.c
@@ -73,9 +73,7 @@ static int sa8775p_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sa8775p_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c
index 5d343f12051..9aff8a847ad 100644
--- a/drivers/clk/qcom/clock-sc7280.c
+++ b/drivers/clk/qcom/clock-sc7280.c
@@ -16,29 +16,64 @@
#include "clock-qcom.h"
-#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038
#define USB30_PRIM_MASTER_CLK_CMD_RCGR 0xf020
+#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038
+#define USB30_SEC_MASTER_CLK_CMD_RCGR 0x9e020
+#define USB30_SEC_MOCK_UTMI_CLK_CMD_RCGR 0x9e038
+#define PCIE_1_AUX_CLK_CMD_RCGR 0x8d058
+#define PCIE1_PHY_RCHNG_CMD_RCGR 0x8d03c
+#define PCIE_1_PIPE_CLK_PHY_MUX 0x8d054
+
+static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = {
+ F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0),
+ F(133333333, CFG_CLK_SRC_GPLL0, 4.5, 0, 0),
+ F(200000000, CFG_CLK_SRC_GPLL0_ODD, 1, 0, 0),
+ F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0),
+ { }
+};
+
+static const struct freq_tbl ftbl_gcc_usb30_sec_master_clk_src[] = {
+ F(60000000, CFG_CLK_SRC_GPLL0_EVEN, 5, 0, 0),
+ F(120000000, CFG_CLK_SRC_GPLL0_EVEN, 2.5, 0, 0),
+ { }
+};
static ulong sc7280_set_rate(struct clk *clk, ulong rate)
{
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+ const struct freq_tbl *freq;
if (clk->id < priv->data->num_clks)
debug("%s: %s, requested rate=%ld\n", __func__, priv->data->clks[clk->id].name, rate);
switch (clk->id) {
- case GCC_USB30_PRIM_MOCK_UTMI_CLK:
- WARN(rate != 19200000, "Unexpected rate for USB30_PRIM_MOCK_UTMI_CLK: %lu\n", rate);
- clk_rcg_set_rate(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO);
- return rate;
case GCC_USB30_PRIM_MASTER_CLK:
- WARN(rate != 200000000, "Unexpected rate for USB30_PRIM_MASTER_CLK: %lu\n", rate);
+ freq = qcom_find_freq(ftbl_gcc_usb30_prim_master_clk_src, rate);
clk_rcg_set_rate_mnd(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR,
- 1, 0, 0, CFG_CLK_SRC_GPLL0_ODD, 8);
- clk_rcg_set_rate(priv->base, 0xf064, 0, 0);
- return rate;
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_USB30_PRIM_MOCK_UTMI_CLK:
+ clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0);
+ return 19200000;
+ case GCC_USB3_PRIM_PHY_AUX_CLK_SRC:
+ clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0);
+ return 19200000;
+ case GCC_USB30_SEC_MASTER_CLK:
+ freq = qcom_find_freq(ftbl_gcc_usb30_sec_master_clk_src, rate);
+ clk_rcg_set_rate_mnd(priv->base, USB30_SEC_MASTER_CLK_CMD_RCGR,
+ freq->pre_div, freq->m, freq->n, freq->src, 8);
+ return freq->freq;
+ case GCC_USB30_SEC_MOCK_UTMI_CLK:
+ clk_rcg_set_rate(priv->base, USB30_SEC_MOCK_UTMI_CLK_CMD_RCGR, 1, 0);
+ return 19200000;
+ case GCC_USB3_SEC_PHY_AUX_CLK_SRC:
+ clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0);
+ return 19200000;
+ case GCC_PCIE1_PHY_RCHNG_CLK:
+ clk_rcg_set_rate(priv->base, PCIE1_PHY_RCHNG_CMD_RCGR, 5, CFG_CLK_SRC_GPLL0_EVEN);
+ return 100000000;
default:
- return 0;
+ return rate;
}
}
@@ -50,13 +85,46 @@ static const struct gate_clk sc7280_clks[] = {
GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1),
GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1),
GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1),
+ GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x9e07c, 1),
+ GATE_CLK(GCC_USB30_SEC_MASTER_CLK, 0x9e010, 1),
+ GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x9e080, 1),
+ GATE_CLK(GCC_USB30_SEC_SLEEP_CLK, 0x9e018, 1),
+ GATE_CLK(GCC_USB30_SEC_MOCK_UTMI_CLK, 0x9e01c, 1),
+ GATE_CLK(GCC_USB3_SEC_PHY_AUX_CLK, 0x9e054, 1),
+ GATE_CLK(GCC_USB3_SEC_PHY_COM_AUX_CLK, 0x9e058, 1),
+ GATE_CLK(GCC_PCIE_CLKREF_EN, 0x8c004, 1),
+ GATE_CLK(GCC_PCIE_1_PIPE_CLK, 0x52000, BIT(30)),
+ GATE_CLK(GCC_PCIE_1_AUX_CLK, 0x52000, BIT(29)),
+ GATE_CLK(GCC_PCIE_1_CFG_AHB_CLK, 0x52000, BIT(28)),
+ GATE_CLK(GCC_PCIE_1_MSTR_AXI_CLK, 0x52000, BIT(27)),
+ GATE_CLK(GCC_PCIE_1_SLV_AXI_CLK, 0x52000, BIT(26)),
+ GATE_CLK(GCC_PCIE_1_SLV_Q2A_AXI_CLK, 0x52000, BIT(25)),
+ GATE_CLK(GCC_PCIE1_PHY_RCHNG_CLK, 0x52000, BIT(23)),
+ GATE_CLK(GCC_DDRSS_PCIE_SF_CLK, 0x52000, BIT(19)),
+ GATE_CLK(GCC_AGGRE_NOC_PCIE_TBU_CLK, 0x52000, BIT(18)),
+ GATE_CLK(GCC_AGGRE_NOC_PCIE_1_AXI_CLK, 0x52000, BIT(11)),
+ GATE_CLK(GCC_AGGRE_NOC_PCIE_CENTER_SF_AXI_CLK, 0x52008, BIT(28)),
+ 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)
{
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
- if (priv->data->num_clks < clk->id) {
+ if (priv->data->num_clks <= clk->id) {
debug("%s: unknown clk id %lu\n", __func__, clk->id);
return 0;
}
@@ -71,11 +139,32 @@ static int sc7280_enable(struct clk *clk)
qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_AUX_CLK);
qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_COM_AUX_CLK);
break;
+ case GCC_AGGRE_USB3_SEC_AXI_CLK:
+ qcom_gate_clk_en(priv, GCC_USB30_SEC_MASTER_CLK);
+ fallthrough;
+ case GCC_USB30_SEC_MASTER_CLK:
+ qcom_gate_clk_en(priv, GCC_USB3_SEC_PHY_AUX_CLK);
+ qcom_gate_clk_en(priv, GCC_USB3_SEC_PHY_COM_AUX_CLK);
+ break;
+ case GCC_PCIE_1_PIPE_CLK:
+ clk_phy_mux_enable(priv->base, PCIE_1_PIPE_CLK_PHY_MUX, true);
+ break;
+ case GCC_PCIE_1_AUX_CLK:
+ clk_rcg_set_rate_mnd(priv->base, PCIE_1_AUX_CLK_CMD_RCGR, 1, 0, 0,
+ CFG_CLK_SRC_CXO, 16);
+ break;
+ case GCC_QUPV3_WRAP0_S0_CLK:
+ clk_rcg_set_rate_mnd(priv->base, 0x17010, 1, 0, 0, CFG_CLK_SRC_CXO, 16);
+ break;
+ case GCC_QUPV3_WRAP0_S1_CLK:
+ clk_rcg_set_rate_mnd(priv->base, 0x17140, 1, 0, 0, CFG_CLK_SRC_CXO, 16);
+ break;
+ case GCC_QUPV3_WRAP0_S3_CLK:
+ clk_rcg_set_rate_mnd(priv->base, 0x173a0, 1, 0, 0, CFG_CLK_SRC_CXO, 16);
+ break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sc7280_gcc_resets[] = {
@@ -100,6 +189,20 @@ static const struct qcom_reset_map sc7280_gcc_resets[] = {
static const struct qcom_power_map sc7280_gdscs[] = {
[GCC_UFS_PHY_GDSC] = { 0x77004 },
[GCC_USB30_PRIM_GDSC] = { 0xf004 },
+ [GCC_USB30_SEC_GDSC] = { 0x9e004 },
+ [GCC_PCIE_1_GDSC] = { 0x8d004 },
+};
+
+static const phys_addr_t sc7280_rcg_addrs[] = {
+ 0x10f020, // USB30_PRIM_MASTER_CLK_CMD_RCGR
+ 0x10f038, // USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR
+ 0x18d058, // PCIE_1_AUX_CLK_CMD_RCGR
+};
+
+static const char *const sc7280_rcg_names[] = {
+ "USB30_PRIM_MASTER_CLK_SRC",
+ "USB30_PRIM_MOCK_UTMI_CLK_SRC",
+ "GCC_PCIE_1_AUX_CLK_SRC",
};
static struct msm_clk_data qcs404_gcc_data = {
@@ -113,6 +216,10 @@ static struct msm_clk_data qcs404_gcc_data = {
.enable = sc7280_enable,
.set_rate = sc7280_set_rate,
+
+ .dbg_rcg_addrs = sc7280_rcg_addrs,
+ .num_rcgs = ARRAY_SIZE(sc7280_rcg_addrs),
+ .dbg_rcg_names = sc7280_rcg_names,
};
static const struct udevice_id gcc_sc7280_of_match[] = {
diff --git a/drivers/clk/qcom/clock-sdm845.c b/drivers/clk/qcom/clock-sdm845.c
index adffb0cb240..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),
@@ -162,9 +165,7 @@ static int sdm845_clk_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sdm845_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-sm6115.c b/drivers/clk/qcom/clock-sm6115.c
index 9057dfe0bb1..17c2e561758 100644
--- a/drivers/clk/qcom/clock-sm6115.c
+++ b/drivers/clk/qcom/clock-sm6115.c
@@ -146,9 +146,7 @@ static int sm6115_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sm6115_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-sm8150.c b/drivers/clk/qcom/clock-sm8150.c
index 88f2e678f43..7dd0d56eb43 100644
--- a/drivers/clk/qcom/clock-sm8150.c
+++ b/drivers/clk/qcom/clock-sm8150.c
@@ -243,9 +243,7 @@ static int sm8150_clk_enable(struct clk *clk)
break;
};
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sm8150_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-sm8250.c b/drivers/clk/qcom/clock-sm8250.c
index e322a923a5c..26396847d85 100644
--- a/drivers/clk/qcom/clock-sm8250.c
+++ b/drivers/clk/qcom/clock-sm8250.c
@@ -195,9 +195,7 @@ static int sm8250_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sm8250_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-sm8550.c b/drivers/clk/qcom/clock-sm8550.c
index 62b5a409e8e..7c06489b9c4 100644
--- a/drivers/clk/qcom/clock-sm8550.c
+++ b/drivers/clk/qcom/clock-sm8550.c
@@ -220,9 +220,7 @@ static int sm8550_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sm8550_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-sm8650.c b/drivers/clk/qcom/clock-sm8650.c
index 9baaecb571f..364454644a6 100644
--- a/drivers/clk/qcom/clock-sm8650.c
+++ b/drivers/clk/qcom/clock-sm8650.c
@@ -217,9 +217,7 @@ static int sm8650_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map sm8650_gcc_resets[] = {
diff --git a/drivers/clk/qcom/clock-x1e80100.c b/drivers/clk/qcom/clock-x1e80100.c
index bd9c6ed1c8a..542d6248d65 100644
--- a/drivers/clk/qcom/clock-x1e80100.c
+++ b/drivers/clk/qcom/clock-x1e80100.c
@@ -174,9 +174,7 @@ static int x1e80100_enable(struct clk *clk)
break;
}
- qcom_gate_clk_en(priv, clk->id);
-
- return 0;
+ return qcom_gate_clk_en(priv, clk->id);
}
static const struct qcom_reset_map x1e80100_gcc_resets[] = {
diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index 3c5340df8ee..7fce1f70d13 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -70,17 +70,12 @@ static int rzg2l_cpg_clk_set(struct clk *clk, bool enable)
dev_dbg(clk->dev, "%s %s clock %u\n", enable ? "enable" : "disable",
is_mod_clk(clk->id) ? "module" : "core", cpg_clk_id);
- if (!is_mod_clk(clk->id)) {
- /*
- * Non-module clocks are always on. Ignore attempts to enable
- * them and reject attempts to disable them.
- */
- if (enable)
- return 0;
-
- dev_err(clk->dev, "ID %lu is not a module clock\n", clk->id);
- return -EINVAL;
- }
+ /*
+ * Non-module clocks are always on. Ignore attempts to enable or disable
+ * them.
+ */
+ if (!is_mod_clk(clk->id))
+ return 0;
for (i = 0; i < data->info->num_mod_clks; i++) {
if (data->info->mod_clks[i].id == cpg_clk_id) {
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 9e379cc2e3b..34b63d4df34 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -15,7 +15,9 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += clk_rk3308.o
obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
+obj-$(CONFIG_ROCKCHIP_RK3528) += clk_rk3528.o
obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
+obj-$(CONFIG_ROCKCHIP_RK3576) += clk_rk3576.o
obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
diff --git a/drivers/clk/rockchip/clk_pll.c b/drivers/clk/rockchip/clk_pll.c
index 44c6f14618d..9dec40b1fe8 100644
--- a/drivers/clk/rockchip/clk_pll.c
+++ b/drivers/clk/rockchip/clk_pll.c
@@ -309,9 +309,11 @@ static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll,
* When power on or changing PLL setting,
* we must force PLL into slow mode to ensure output stable clock.
*/
- rk_clrsetreg(base + pll->mode_offset,
- pll->mode_mask << pll->mode_shift,
- RKCLK_PLL_MODE_SLOW << pll->mode_shift);
+ if (!(pll->pll_flags & ROCKCHIP_PLL_FIXED_MODE)) {
+ rk_clrsetreg(base + pll->mode_offset,
+ pll->mode_mask << pll->mode_shift,
+ RKCLK_PLL_MODE_SLOW << pll->mode_shift);
+ }
/* Power down */
rk_setreg(base + pll->con_offset + 0x4,
@@ -345,8 +347,11 @@ static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll,
while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift)))
udelay(1);
- rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift,
- RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
+ if (!(pll->pll_flags & ROCKCHIP_PLL_FIXED_MODE)) {
+ rk_clrsetreg(base + pll->mode_offset,
+ pll->mode_mask << pll->mode_shift,
+ RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
+ }
debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
pll, readl(base + pll->con_offset),
readl(base + pll->con_offset + 0x4),
@@ -362,12 +367,18 @@ static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll,
u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac;
u32 con = 0, shift, mask;
ulong rate;
+ int mode;
con = readl(base + pll->mode_offset);
shift = pll->mode_shift;
mask = pll->mode_mask << shift;
- switch ((con & mask) >> shift) {
+ if (!(pll->pll_flags & ROCKCHIP_PLL_FIXED_MODE))
+ mode = (con & mask) >> shift;
+ else
+ mode = RKCLK_PLL_MODE_NORMAL;
+
+ switch (mode) {
case RKCLK_PLL_MODE_SLOW:
return OSC_HZ;
case RKCLK_PLL_MODE_NORMAL:
diff --git a/drivers/clk/rockchip/clk_rk3528.c b/drivers/clk/rockchip/clk_rk3528.c
new file mode 100644
index 00000000000..06f20895acc
--- /dev/null
+++ b/drivers/clk/rockchip/clk_rk3528.c
@@ -0,0 +1,1754 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#include <bitfield.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/cru_rk3528.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dt-bindings/clock/rockchip,rk3528-cru.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
+
+/*
+ * PLL attention.
+ *
+ * [FRAC PLL]: GPLL, PPLL, DPLL
+ * - frac mode: refdiv can be 1 or 2 only
+ * - int mode: refdiv has no special limit
+ * - VCO range: [950, 3800] MHZ
+ *
+ * [INT PLL]: CPLL, APLL
+ * - int mode: refdiv can be 1 or 2 only
+ * - VCO range: [475, 1900] MHZ
+ *
+ * [PPLL]: normal mode only.
+ *
+ */
+static struct rockchip_pll_rate_table rk3528_pll_rates[] = {
+ /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+ RK3036_PLL_RATE(1896000000, 1, 79, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1800000000, 1, 75, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1704000000, 1, 71, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1188000000, 1, 99, 2, 1, 1, 0), /* GPLL */
+ RK3036_PLL_RATE(1092000000, 2, 91, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1008000000, 1, 42, 1, 1, 1, 0),
+ RK3036_PLL_RATE(1000000000, 1, 125, 3, 1, 1, 0), /* PPLL */
+ RK3036_PLL_RATE(996000000, 2, 83, 1, 1, 1, 0), /* CPLL */
+ RK3036_PLL_RATE(960000000, 1, 40, 1, 1, 1, 0),
+ RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0),
+ RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0),
+ RK3036_PLL_RATE(600000000, 1, 50, 2, 1, 1, 0),
+ RK3036_PLL_RATE(594000000, 2, 99, 2, 1, 1, 0),
+ RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0),
+ RK3036_PLL_RATE(312000000, 1, 78, 6, 1, 1, 0),
+ RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0),
+ RK3036_PLL_RATE(96000000, 1, 24, 3, 2, 1, 0),
+ { /* sentinel */ },
+};
+
+static struct rockchip_pll_clock rk3528_pll_clks[] = {
+ [APLL] = PLL(pll_rk3328, PLL_APLL, RK3528_PLL_CON(0),
+ RK3528_MODE_CON, 0, 10, 0, rk3528_pll_rates),
+
+ [CPLL] = PLL(pll_rk3328, PLL_CPLL, RK3528_PLL_CON(8),
+ RK3528_MODE_CON, 2, 10, 0, rk3528_pll_rates),
+
+ [GPLL] = PLL(pll_rk3328, PLL_GPLL, RK3528_PLL_CON(24),
+ RK3528_MODE_CON, 4, 10, 0, rk3528_pll_rates),
+
+ [PPLL] = PLL(pll_rk3328, PLL_PPLL, RK3528_PCIE_PLL_CON(32),
+ RK3528_MODE_CON, 6, 10, ROCKCHIP_PLL_FIXED_MODE, rk3528_pll_rates),
+
+ [DPLL] = PLL(pll_rk3328, PLL_DPLL, RK3528_DDRPHY_PLL_CON(16),
+ RK3528_DDRPHY_MODE_CON, 0, 10, 0, rk3528_pll_rates),
+};
+
+#define RK3528_CPUCLK_RATE(_rate, _aclk_m_core, _pclk_dbg) \
+{ \
+ .rate = _rate##U, \
+ .aclk_div = (_aclk_m_core), \
+ .pclk_div = (_pclk_dbg), \
+}
+
+/* sign-off: _aclk_m_core: 550M, _pclk_dbg: 137.5M, */
+static struct rockchip_cpu_rate_table rk3528_cpu_rates[] = {
+ RK3528_CPUCLK_RATE(1896000000, 1, 13),
+ RK3528_CPUCLK_RATE(1800000000, 1, 12),
+ RK3528_CPUCLK_RATE(1704000000, 1, 11),
+ RK3528_CPUCLK_RATE(1608000000, 1, 11),
+ RK3528_CPUCLK_RATE(1512000000, 1, 11),
+ RK3528_CPUCLK_RATE(1416000000, 1, 9),
+ RK3528_CPUCLK_RATE(1296000000, 1, 8),
+ RK3528_CPUCLK_RATE(1200000000, 1, 8),
+ RK3528_CPUCLK_RATE(1188000000, 1, 8),
+ RK3528_CPUCLK_RATE(1092000000, 1, 7),
+ RK3528_CPUCLK_RATE(1008000000, 1, 6),
+ RK3528_CPUCLK_RATE(1000000000, 1, 6),
+ RK3528_CPUCLK_RATE(996000000, 1, 6),
+ RK3528_CPUCLK_RATE(960000000, 1, 6),
+ RK3528_CPUCLK_RATE(912000000, 1, 6),
+ RK3528_CPUCLK_RATE(816000000, 1, 5),
+ RK3528_CPUCLK_RATE(600000000, 1, 3),
+ RK3528_CPUCLK_RATE(594000000, 1, 3),
+ RK3528_CPUCLK_RATE(408000000, 1, 2),
+ RK3528_CPUCLK_RATE(312000000, 1, 2),
+ RK3528_CPUCLK_RATE(216000000, 1, 1),
+ RK3528_CPUCLK_RATE(96000000, 1, 0),
+};
+
+/*
+ *
+ * rational_best_approximation(31415, 10000,
+ * (1 << 8) - 1, (1 << 5) - 1, &n, &d);
+ *
+ * you may look at given_numerator as a fixed point number,
+ * with the fractional part size described in given_denominator.
+ *
+ * for theoretical background, see:
+ * http://en.wikipedia.org/wiki/Continued_fraction
+ */
+static void rational_best_approximation(unsigned long given_numerator,
+ unsigned long given_denominator,
+ unsigned long max_numerator,
+ unsigned long max_denominator,
+ unsigned long *best_numerator,
+ unsigned long *best_denominator)
+{
+ unsigned long n, d, n0, d0, n1, d1;
+
+ n = given_numerator;
+ d = given_denominator;
+ n0 = 0;
+ d1 = 0;
+ n1 = 1;
+ d0 = 1;
+ for (;;) {
+ unsigned long t, a;
+
+ if (n1 > max_numerator || d1 > max_denominator) {
+ n1 = n0;
+ d1 = d0;
+ break;
+ }
+ if (d == 0)
+ break;
+ t = d;
+ a = n / d;
+ d = n % d;
+ n = t;
+ t = n0 + a * n1;
+ n0 = n1;
+ n1 = t;
+ t = d0 + a * d1;
+ d0 = d1;
+ d1 = t;
+ }
+ *best_numerator = n1;
+ *best_denominator = d1;
+}
+
+static int rk3528_armclk_set_clk(struct rk3528_clk_priv *priv, ulong new_rate)
+{
+ const struct rockchip_cpu_rate_table *rate;
+ struct rk3528_cru *cru = priv->cru;
+ ulong old_rate;
+
+ rate = rockchip_get_cpu_settings(rk3528_cpu_rates, new_rate);
+ if (!rate) {
+ printf("%s unsupported rate\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * set up dependent divisors for DBG and ACLK clocks.
+ */
+ old_rate = rockchip_pll_get_rate(&rk3528_pll_clks[APLL], priv->cru, APLL);
+ if (old_rate > new_rate) {
+ if (rockchip_pll_set_rate(&rk3528_pll_clks[APLL],
+ priv->cru, APLL, new_rate))
+ return -EINVAL;
+
+ rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK,
+ rate->pclk_div << RK3528_DIV_PCLK_DBG_SHIFT);
+
+ rk_clrsetreg(&cru->clksel_con[39], RK3528_DIV_ACLK_M_CORE_MASK,
+ rate->aclk_div << RK3528_DIV_ACLK_M_CORE_SHIFT);
+ } else if (old_rate < new_rate) {
+ rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK,
+ rate->pclk_div << RK3528_DIV_PCLK_DBG_SHIFT);
+
+ rk_clrsetreg(&cru->clksel_con[39], RK3528_DIV_ACLK_M_CORE_MASK,
+ rate->aclk_div << RK3528_DIV_ACLK_M_CORE_SHIFT);
+
+ if (rockchip_pll_set_rate(&rk3528_pll_clks[APLL],
+ priv->cru, APLL, new_rate))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ulong rk3528_ppll_matrix_get_rate(struct rk3528_clk_priv *priv,
+ ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, mask, shift;
+ void *reg;
+
+ switch (clk_id) {
+ case CLK_PPLL_50M_MATRIX:
+ case CLK_GMAC1_RMII_VPU:
+ mask = PCIE_CLK_MATRIX_50M_SRC_DIV_MASK;
+ shift = PCIE_CLK_MATRIX_50M_SRC_DIV_SHIFT;
+ reg = &cru->pcieclksel_con[1];
+ break;
+
+ case CLK_PPLL_100M_MATRIX:
+ mask = PCIE_CLK_MATRIX_100M_SRC_DIV_MASK;
+ shift = PCIE_CLK_MATRIX_100M_SRC_DIV_SHIFT;
+ reg = &cru->pcieclksel_con[1];
+ break;
+
+ case CLK_PPLL_125M_MATRIX:
+ case CLK_GMAC1_SRC_VPU:
+ mask = CLK_MATRIX_125M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_125M_SRC_DIV_SHIFT;
+ reg = &cru->clksel_con[60];
+ break;
+
+ case CLK_GMAC1_VPU_25M:
+ mask = CLK_MATRIX_25M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_25M_SRC_DIV_SHIFT;
+ reg = &cru->clksel_con[60];
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ div = (readl(reg) & mask) >> shift;
+
+ return DIV_TO_RATE(priv->ppll_hz, div);
+}
+
+static ulong rk3528_ppll_matrix_set_rate(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, div, mask, shift;
+ u8 is_pciecru = 0;
+
+ switch (clk_id) {
+ case CLK_PPLL_50M_MATRIX:
+ id = 1;
+ mask = PCIE_CLK_MATRIX_50M_SRC_DIV_MASK;
+ shift = PCIE_CLK_MATRIX_50M_SRC_DIV_SHIFT;
+ is_pciecru = 1;
+ break;
+
+ case CLK_PPLL_100M_MATRIX:
+ id = 1;
+ mask = PCIE_CLK_MATRIX_100M_SRC_DIV_MASK;
+ shift = PCIE_CLK_MATRIX_100M_SRC_DIV_SHIFT;
+ is_pciecru = 1;
+ break;
+
+ case CLK_PPLL_125M_MATRIX:
+ id = 60;
+ mask = CLK_MATRIX_125M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_125M_SRC_DIV_SHIFT;
+ break;
+ case CLK_GMAC1_VPU_25M:
+ id = 60;
+ mask = CLK_MATRIX_25M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_25M_SRC_DIV_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ div = DIV_ROUND_UP(priv->ppll_hz, rate);
+ if (is_pciecru)
+ rk_clrsetreg(&cru->pcieclksel_con[id], mask, (div - 1) << shift);
+ else
+ rk_clrsetreg(&cru->clksel_con[id], mask, (div - 1) << shift);
+
+ return rk3528_ppll_matrix_get_rate(priv, clk_id);
+}
+
+static ulong rk3528_cgpll_matrix_get_rate(struct rk3528_clk_priv *priv,
+ ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 sel, div, mask, shift, con;
+ u32 sel_mask = 0, sel_shift;
+ u8 is_gpll_parent = 1;
+ u8 is_halfdiv = 0;
+ ulong prate;
+
+ switch (clk_id) {
+ case CLK_MATRIX_50M_SRC:
+ con = 0;
+ mask = CLK_MATRIX_50M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_50M_SRC_DIV_SHIFT;
+ is_gpll_parent = 0;
+ break;
+
+ case CLK_MATRIX_100M_SRC:
+ con = 0;
+ mask = CLK_MATRIX_100M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_100M_SRC_DIV_SHIFT;
+ is_gpll_parent = 0;
+ break;
+
+ case CLK_MATRIX_150M_SRC:
+ con = 1;
+ mask = CLK_MATRIX_150M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_150M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_200M_SRC:
+ con = 1;
+ mask = CLK_MATRIX_200M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_200M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_250M_SRC:
+ con = 1;
+ mask = CLK_MATRIX_250M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_250M_SRC_DIV_SHIFT;
+ sel_mask = CLK_MATRIX_250M_SRC_SEL_MASK;
+ sel_shift = CLK_MATRIX_250M_SRC_SEL_SHIFT;
+ break;
+
+ case CLK_MATRIX_300M_SRC:
+ con = 2;
+ mask = CLK_MATRIX_300M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_300M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_339M_SRC:
+ con = 2;
+ mask = CLK_MATRIX_339M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_339M_SRC_DIV_SHIFT;
+ is_halfdiv = 1;
+ break;
+
+ case CLK_MATRIX_400M_SRC:
+ con = 2;
+ mask = CLK_MATRIX_400M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_400M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_500M_SRC:
+ con = 3;
+ mask = CLK_MATRIX_500M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_500M_SRC_DIV_SHIFT;
+ sel_mask = CLK_MATRIX_500M_SRC_SEL_MASK;
+ sel_shift = CLK_MATRIX_500M_SRC_SEL_SHIFT;
+ break;
+
+ case CLK_MATRIX_600M_SRC:
+ con = 4;
+ mask = CLK_MATRIX_600M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_600M_SRC_DIV_SHIFT;
+ break;
+
+ case ACLK_BUS_VOPGL_ROOT:
+ case ACLK_BUS_VOPGL_BIU:
+ con = 43;
+ mask = ACLK_BUS_VOPGL_ROOT_DIV_MASK;
+ shift = ACLK_BUS_VOPGL_ROOT_DIV_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if (sel_mask) {
+ sel = (readl(&cru->clksel_con[con]) & sel_mask) >> sel_shift;
+ if (sel == CLK_MATRIX_250M_SRC_SEL_CLK_GPLL_MUX) // TODO
+ prate = priv->gpll_hz;
+ else
+ prate = priv->cpll_hz;
+ } else {
+ if (is_gpll_parent)
+ prate = priv->gpll_hz;
+ else
+ prate = priv->cpll_hz;
+ }
+
+ div = (readl(&cru->clksel_con[con]) & mask) >> shift;
+
+ /* NOTE: '-1' to balance the DIV_TO_RATE() 'div+1' */
+ return is_halfdiv ? DIV_TO_RATE(prate * 2, (3 + 2 * div) - 1) : DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3528_cgpll_matrix_set_rate(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 sel, div, mask, shift, con;
+ u32 sel_mask = 0, sel_shift;
+ u8 is_gpll_parent = 1;
+ u8 is_halfdiv = 0;
+ ulong prate = 0;
+
+ switch (clk_id) {
+ case CLK_MATRIX_50M_SRC:
+ con = 0;
+ mask = CLK_MATRIX_50M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_50M_SRC_DIV_SHIFT;
+ is_gpll_parent = 0;
+ break;
+
+ case CLK_MATRIX_100M_SRC:
+ con = 0;
+ mask = CLK_MATRIX_100M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_100M_SRC_DIV_SHIFT;
+ is_gpll_parent = 0;
+ break;
+
+ case CLK_MATRIX_150M_SRC:
+ con = 1;
+ mask = CLK_MATRIX_150M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_150M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_200M_SRC:
+ con = 1;
+ mask = CLK_MATRIX_200M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_200M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_250M_SRC:
+ con = 1;
+ mask = CLK_MATRIX_250M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_250M_SRC_DIV_SHIFT;
+ sel_mask = CLK_MATRIX_250M_SRC_SEL_MASK;
+ sel_shift = CLK_MATRIX_250M_SRC_SEL_SHIFT;
+ break;
+
+ case CLK_MATRIX_300M_SRC:
+ con = 2;
+ mask = CLK_MATRIX_300M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_300M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_339M_SRC:
+ con = 2;
+ mask = CLK_MATRIX_339M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_339M_SRC_DIV_SHIFT;
+ is_halfdiv = 1;
+ break;
+
+ case CLK_MATRIX_400M_SRC:
+ con = 2;
+ mask = CLK_MATRIX_400M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_400M_SRC_DIV_SHIFT;
+ break;
+
+ case CLK_MATRIX_500M_SRC:
+ con = 3;
+ mask = CLK_MATRIX_500M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_500M_SRC_DIV_SHIFT;
+ sel_mask = CLK_MATRIX_500M_SRC_SEL_MASK;
+ sel_shift = CLK_MATRIX_500M_SRC_SEL_SHIFT;
+ break;
+
+ case CLK_MATRIX_600M_SRC:
+ con = 4;
+ mask = CLK_MATRIX_600M_SRC_DIV_MASK;
+ shift = CLK_MATRIX_600M_SRC_DIV_SHIFT;
+ break;
+
+ case ACLK_BUS_VOPGL_ROOT:
+ case ACLK_BUS_VOPGL_BIU:
+ con = 43;
+ mask = ACLK_BUS_VOPGL_ROOT_DIV_MASK;
+ shift = ACLK_BUS_VOPGL_ROOT_DIV_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if (sel_mask) {
+ if (priv->gpll_hz % rate == 0) {
+ sel = CLK_MATRIX_250M_SRC_SEL_CLK_GPLL_MUX; // TODO
+ prate = priv->gpll_hz;
+ } else {
+ sel = CLK_MATRIX_250M_SRC_SEL_CLK_CPLL_MUX;
+ prate = priv->cpll_hz;
+ }
+ } else {
+ if (is_gpll_parent)
+ prate = priv->gpll_hz;
+ else
+ prate = priv->cpll_hz;
+ }
+
+ if (is_halfdiv)
+ /* NOTE: '+1' to balance the following rk_clrsetreg() 'div-1' */
+ div = DIV_ROUND_UP((prate * 2) - (3 * rate), 2 * rate) + 1;
+ else
+ div = DIV_ROUND_UP(prate, rate);
+
+ rk_clrsetreg(&cru->clksel_con[con], mask, (div - 1) << shift);
+ if (sel_mask)
+ rk_clrsetreg(&cru->clksel_con[con], sel_mask, sel << sel_shift);
+
+ return rk3528_cgpll_matrix_get_rate(priv, clk_id);
+}
+
+static ulong rk3528_i2c_get_clk(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, sel, con, mask, shift;
+ u8 is_pmucru = 0;
+ ulong rate;
+
+ switch (clk_id) {
+ case CLK_I2C0:
+ id = 79;
+ mask = CLK_I2C0_SEL_MASK;
+ shift = CLK_I2C0_SEL_SHIFT;
+ break;
+
+ case CLK_I2C1:
+ id = 79;
+ mask = CLK_I2C1_SEL_MASK;
+ shift = CLK_I2C1_SEL_SHIFT;
+ break;
+
+ case CLK_I2C2:
+ id = 0;
+ mask = CLK_I2C2_SEL_MASK;
+ shift = CLK_I2C2_SEL_SHIFT;
+ is_pmucru = 1;
+ break;
+
+ case CLK_I2C3:
+ id = 63;
+ mask = CLK_I2C3_SEL_MASK;
+ shift = CLK_I2C3_SEL_SHIFT;
+ break;
+
+ case CLK_I2C4:
+ id = 85;
+ mask = CLK_I2C4_SEL_MASK;
+ shift = CLK_I2C4_SEL_SHIFT;
+ break;
+
+ case CLK_I2C5:
+ id = 63;
+ mask = CLK_I2C5_SEL_MASK;
+ shift = CLK_I2C5_SEL_SHIFT;
+ break;
+
+ case CLK_I2C6:
+ id = 64;
+ mask = CLK_I2C6_SEL_MASK;
+ shift = CLK_I2C6_SEL_SHIFT;
+ break;
+
+ case CLK_I2C7:
+ id = 86;
+ mask = CLK_I2C7_SEL_MASK;
+ shift = CLK_I2C7_SEL_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if (is_pmucru)
+ con = readl(&cru->pmuclksel_con[id]);
+ else
+ con = readl(&cru->clksel_con[id]);
+ sel = (con & mask) >> shift;
+ if (sel == CLK_I2C3_SEL_CLK_MATRIX_200M_SRC)
+ rate = 200 * MHz;
+ else if (sel == CLK_I2C3_SEL_CLK_MATRIX_100M_SRC)
+ rate = 100 * MHz;
+ else if (sel == CLK_I2C3_SEL_CLK_MATRIX_50M_SRC)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+
+ return rate;
+}
+
+static ulong rk3528_i2c_set_clk(struct rk3528_clk_priv *priv, ulong clk_id,
+ ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, sel, mask, shift;
+ u8 is_pmucru = 0;
+
+ if (rate >= 198 * MHz)
+ sel = CLK_I2C3_SEL_CLK_MATRIX_200M_SRC;
+ else if (rate >= 99 * MHz)
+ sel = CLK_I2C3_SEL_CLK_MATRIX_100M_SRC;
+ else if (rate >= 50 * MHz)
+ sel = CLK_I2C3_SEL_CLK_MATRIX_50M_SRC;
+ else
+ sel = CLK_I2C3_SEL_XIN_OSC0_FUNC;
+
+ switch (clk_id) {
+ case CLK_I2C0:
+ id = 79;
+ mask = CLK_I2C0_SEL_MASK;
+ shift = CLK_I2C0_SEL_SHIFT;
+ break;
+
+ case CLK_I2C1:
+ id = 79;
+ mask = CLK_I2C1_SEL_MASK;
+ shift = CLK_I2C1_SEL_SHIFT;
+ break;
+
+ case CLK_I2C2:
+ id = 0;
+ mask = CLK_I2C2_SEL_MASK;
+ shift = CLK_I2C2_SEL_SHIFT;
+ is_pmucru = 1;
+ break;
+
+ case CLK_I2C3:
+ id = 63;
+ mask = CLK_I2C3_SEL_MASK;
+ shift = CLK_I2C3_SEL_SHIFT;
+ break;
+
+ case CLK_I2C4:
+ id = 85;
+ mask = CLK_I2C4_SEL_MASK;
+ shift = CLK_I2C4_SEL_SHIFT;
+ break;
+
+ case CLK_I2C5:
+ id = 63;
+ mask = CLK_I2C5_SEL_MASK;
+ shift = CLK_I2C5_SEL_SHIFT;
+ break;
+
+ case CLK_I2C6:
+ id = 64;
+ mask = CLK_I2C6_SEL_MASK;
+ shift = CLK_I2C6_SEL_SHIFT;
+ break;
+
+ case CLK_I2C7:
+ id = 86;
+ mask = CLK_I2C7_SEL_MASK;
+ shift = CLK_I2C7_SEL_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if (is_pmucru)
+ rk_clrsetreg(&cru->pmuclksel_con[id], mask, sel << shift);
+ else
+ rk_clrsetreg(&cru->clksel_con[id], mask, sel << shift);
+
+ return rk3528_i2c_get_clk(priv, clk_id);
+}
+
+static ulong rk3528_spi_get_clk(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, sel, con, mask, shift;
+ ulong rate;
+
+ switch (clk_id) {
+ case CLK_SPI0:
+ id = 79;
+ mask = CLK_SPI0_SEL_MASK;
+ shift = CLK_SPI0_SEL_SHIFT;
+ break;
+
+ case CLK_SPI1:
+ id = 63;
+ mask = CLK_SPI1_SEL_MASK;
+ shift = CLK_SPI1_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ con = readl(&cru->clksel_con[id]);
+ sel = (con & mask) >> shift;
+ if (sel == CLK_SPI1_SEL_CLK_MATRIX_200M_SRC)
+ rate = 200 * MHz;
+ else if (sel == CLK_SPI1_SEL_CLK_MATRIX_100M_SRC)
+ rate = 100 * MHz;
+ else if (sel == CLK_SPI1_SEL_CLK_MATRIX_50M_SRC)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+
+ return rate;
+}
+
+static ulong rk3528_spi_set_clk(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, sel, mask, shift;
+
+ if (rate >= 198 * MHz)
+ sel = CLK_SPI1_SEL_CLK_MATRIX_200M_SRC;
+ else if (rate >= 99 * MHz)
+ sel = CLK_SPI1_SEL_CLK_MATRIX_100M_SRC;
+ else if (rate >= 50 * MHz)
+ sel = CLK_SPI1_SEL_CLK_MATRIX_50M_SRC;
+ else
+ sel = CLK_SPI1_SEL_XIN_OSC0_FUNC;
+
+ switch (clk_id) {
+ case CLK_SPI0:
+ id = 79;
+ mask = CLK_SPI0_SEL_MASK;
+ shift = CLK_SPI0_SEL_SHIFT;
+ break;
+
+ case CLK_SPI1:
+ id = 63;
+ mask = CLK_SPI1_SEL_MASK;
+ shift = CLK_SPI1_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[id], mask, sel << shift);
+
+ return rk3528_spi_get_clk(priv, clk_id);
+}
+
+static ulong rk3528_pwm_get_clk(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, sel, con, mask, shift;
+ ulong rate;
+
+ switch (clk_id) {
+ case CLK_PWM0:
+ id = 44;
+ mask = CLK_PWM0_SEL_MASK;
+ shift = CLK_PWM0_SEL_SHIFT;
+ break;
+
+ case CLK_PWM1:
+ id = 44;
+ mask = CLK_PWM1_SEL_MASK;
+ shift = CLK_PWM1_SEL_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ con = readl(&cru->clksel_con[id]);
+ sel = (con & mask) >> shift;
+ if (sel == CLK_PWM0_SEL_CLK_MATRIX_100M_SRC)
+ rate = 100 * MHz;
+ if (sel == CLK_PWM0_SEL_CLK_MATRIX_50M_SRC)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+
+ return rate;
+}
+
+static ulong rk3528_pwm_set_clk(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 id, sel, mask, shift;
+
+ if (rate >= 99 * MHz)
+ sel = CLK_PWM0_SEL_CLK_MATRIX_100M_SRC;
+ else if (rate >= 50 * MHz)
+ sel = CLK_PWM0_SEL_CLK_MATRIX_50M_SRC;
+ else
+ sel = CLK_PWM0_SEL_XIN_OSC0_FUNC;
+
+ switch (clk_id) {
+ case CLK_PWM0:
+ id = 44;
+ mask = CLK_PWM0_SEL_MASK;
+ shift = CLK_PWM0_SEL_SHIFT;
+ break;
+
+ case CLK_PWM1:
+ id = 44;
+ mask = CLK_PWM1_SEL_MASK;
+ shift = CLK_PWM1_SEL_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[id], mask, sel << shift);
+
+ return rk3528_pwm_get_clk(priv, clk_id);
+}
+
+static ulong rk3528_adc_get_clk(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, con;
+
+ con = readl(&cru->clksel_con[74]);
+ switch (clk_id) {
+ case CLK_SARADC:
+ div = (con & CLK_SARADC_DIV_MASK) >>
+ CLK_SARADC_DIV_SHIFT;
+ break;
+
+ case CLK_TSADC_TSEN:
+ div = (con & CLK_TSADC_TSEN_DIV_MASK) >>
+ CLK_TSADC_TSEN_DIV_SHIFT;
+ break;
+
+ case CLK_TSADC:
+ div = (con & CLK_TSADC_DIV_MASK) >>
+ CLK_TSADC_DIV_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rk3528_adc_set_clk(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, mask, shift;
+
+ switch (clk_id) {
+ case CLK_SARADC:
+ mask = CLK_SARADC_DIV_MASK;
+ shift = CLK_SARADC_DIV_SHIFT;
+ break;
+
+ case CLK_TSADC_TSEN:
+ mask = CLK_TSADC_TSEN_DIV_MASK;
+ shift = CLK_TSADC_TSEN_DIV_SHIFT;
+ break;
+
+ case CLK_TSADC:
+ mask = CLK_TSADC_DIV_MASK;
+ shift = CLK_TSADC_DIV_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ rk_clrsetreg(&cru->clksel_con[74], mask, (div - 1) << shift);
+
+ return rk3528_adc_get_clk(priv, clk_id);
+}
+
+static ulong rk3528_sdmmc_get_clk(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, sel, con;
+ ulong prate;
+
+ con = readl(&cru->clksel_con[85]);
+ div = (con & CCLK_SRC_SDMMC0_DIV_MASK) >>
+ CCLK_SRC_SDMMC0_DIV_SHIFT;
+ sel = (con & CCLK_SRC_SDMMC0_SEL_MASK) >>
+ CCLK_SRC_SDMMC0_SEL_SHIFT;
+
+ if (sel == CCLK_SRC_SDMMC0_SEL_CLK_GPLL_MUX)
+ prate = priv->gpll_hz;
+ else if (sel == CCLK_SRC_SDMMC0_SEL_CLK_CPLL_MUX)
+ prate = priv->cpll_hz;
+ else
+ prate = OSC_HZ;
+
+ return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3528_sdmmc_set_clk(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, sel;
+
+ if (OSC_HZ % rate == 0) {
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ sel = CCLK_SRC_SDMMC0_SEL_XIN_OSC0_FUNC;
+ } else if ((priv->cpll_hz % rate) == 0) {
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ sel = CCLK_SRC_SDMMC0_SEL_CLK_CPLL_MUX;
+ } else {
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ sel = CCLK_SRC_SDMMC0_SEL_CLK_GPLL_MUX;
+ }
+
+ assert(div - 1 <= 63);
+ rk_clrsetreg(&cru->clksel_con[85],
+ CCLK_SRC_SDMMC0_SEL_MASK |
+ CCLK_SRC_SDMMC0_DIV_MASK,
+ sel << CCLK_SRC_SDMMC0_SEL_SHIFT |
+ (div - 1) << CCLK_SRC_SDMMC0_DIV_SHIFT);
+
+ return rk3528_sdmmc_get_clk(priv, clk_id);
+}
+
+static ulong rk3528_sfc_get_clk(struct rk3528_clk_priv *priv)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, sel, con, parent;
+
+ con = readl(&cru->clksel_con[61]);
+ div = (con & SCLK_SFC_DIV_MASK) >>
+ SCLK_SFC_DIV_SHIFT;
+ sel = (con & SCLK_SFC_SEL_MASK) >>
+ SCLK_SFC_SEL_SHIFT;
+ if (sel == SCLK_SFC_SEL_CLK_GPLL_MUX)
+ parent = priv->gpll_hz;
+ else if (sel == SCLK_SFC_SEL_CLK_CPLL_MUX)
+ parent = priv->cpll_hz;
+ else
+ parent = OSC_HZ;
+
+ return DIV_TO_RATE(parent, div);
+}
+
+static ulong rk3528_sfc_set_clk(struct rk3528_clk_priv *priv, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ int div, sel;
+
+ if (OSC_HZ % rate == 0) {
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ sel = SCLK_SFC_SEL_XIN_OSC0_FUNC;
+ } else if ((priv->cpll_hz % rate) == 0) {
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ sel = SCLK_SFC_SEL_CLK_CPLL_MUX;
+ } else {
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ sel = SCLK_SFC_SEL_CLK_GPLL_MUX;
+ }
+
+ assert(div - 1 <= 63);
+ rk_clrsetreg(&cru->clksel_con[61],
+ SCLK_SFC_SEL_MASK |
+ SCLK_SFC_DIV_MASK,
+ sel << SCLK_SFC_SEL_SHIFT |
+ (div - 1) << SCLK_SFC_DIV_SHIFT);
+
+ return rk3528_sfc_get_clk(priv);
+}
+
+static ulong rk3528_emmc_get_clk(struct rk3528_clk_priv *priv)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, sel, con, parent;
+
+ con = readl(&cru->clksel_con[62]);
+ div = (con & CCLK_SRC_EMMC_DIV_MASK) >>
+ CCLK_SRC_EMMC_DIV_SHIFT;
+ sel = (con & CCLK_SRC_EMMC_SEL_MASK) >>
+ CCLK_SRC_EMMC_SEL_SHIFT;
+
+ if (sel == CCLK_SRC_EMMC_SEL_CLK_GPLL_MUX)
+ parent = priv->gpll_hz;
+ else if (sel == CCLK_SRC_EMMC_SEL_CLK_CPLL_MUX)
+ parent = priv->cpll_hz;
+ else
+ parent = OSC_HZ;
+
+ return DIV_TO_RATE(parent, div);
+}
+
+static ulong rk3528_emmc_set_clk(struct rk3528_clk_priv *priv, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div, sel;
+
+ if (OSC_HZ % rate == 0) {
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ sel = CCLK_SRC_EMMC_SEL_XIN_OSC0_FUNC;
+ } else if ((priv->cpll_hz % rate) == 0) {
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ sel = CCLK_SRC_EMMC_SEL_CLK_CPLL_MUX;
+ } else {
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ sel = CCLK_SRC_EMMC_SEL_CLK_GPLL_MUX;
+ }
+
+ assert(div - 1 <= 63);
+ rk_clrsetreg(&cru->clksel_con[62],
+ CCLK_SRC_EMMC_SEL_MASK |
+ CCLK_SRC_EMMC_DIV_MASK,
+ sel << CCLK_SRC_EMMC_SEL_SHIFT |
+ (div - 1) << CCLK_SRC_EMMC_DIV_SHIFT);
+
+ return rk3528_emmc_get_clk(priv);
+}
+
+static ulong rk3528_dclk_vop_get_clk(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div_mask, div_shift;
+ u32 sel_mask, sel_shift;
+ u32 id, con, sel, div;
+ ulong prate;
+
+ switch (clk_id) {
+ case DCLK_VOP0:
+ id = 32;
+ sel_mask = DCLK_VOP_SRC0_SEL_MASK;
+ sel_shift = DCLK_VOP_SRC0_SEL_SHIFT;
+ /* FIXME if need src: clk_hdmiphy_pixel_io */
+ div_mask = DCLK_VOP_SRC0_DIV_MASK;
+ div_shift = DCLK_VOP_SRC0_DIV_SHIFT;
+ break;
+
+ case DCLK_VOP1:
+ id = 33;
+ sel_mask = DCLK_VOP_SRC1_SEL_MASK;
+ sel_shift = DCLK_VOP_SRC1_SEL_SHIFT;
+ div_mask = DCLK_VOP_SRC1_DIV_MASK;
+ div_shift = DCLK_VOP_SRC1_DIV_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ con = readl(&cru->clksel_con[id]);
+ div = (con & div_mask) >> div_shift;
+ sel = (con & sel_mask) >> sel_shift;
+ if (sel == DCLK_VOP_SRC_SEL_CLK_GPLL_MUX)
+ prate = priv->gpll_hz;
+ else
+ prate = priv->cpll_hz;
+
+ return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3528_dclk_vop_set_clk(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 div_mask, div_shift;
+ u32 sel_mask, sel_shift;
+ u32 id, sel, div;
+ ulong prate;
+
+ switch (clk_id) {
+ case DCLK_VOP0:
+ id = 32;
+ sel_mask = DCLK_VOP_SRC0_SEL_MASK;
+ sel_shift = DCLK_VOP_SRC0_SEL_SHIFT;
+ /* FIXME if need src: clk_hdmiphy_pixel_io */
+ div_mask = DCLK_VOP_SRC0_DIV_MASK;
+ div_shift = DCLK_VOP_SRC0_DIV_SHIFT;
+ break;
+
+ case DCLK_VOP1:
+ id = 33;
+ sel_mask = DCLK_VOP_SRC1_SEL_MASK;
+ sel_shift = DCLK_VOP_SRC1_SEL_SHIFT;
+ div_mask = DCLK_VOP_SRC1_DIV_MASK;
+ div_shift = DCLK_VOP_SRC1_DIV_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if ((priv->gpll_hz % rate) == 0) {
+ prate = priv->gpll_hz;
+ sel = (DCLK_VOP_SRC_SEL_CLK_GPLL_MUX << sel_shift) & sel_mask;
+ } else {
+ prate = priv->cpll_hz;
+ sel = (DCLK_VOP_SRC_SEL_CLK_CPLL_MUX << sel_shift) & sel_mask;
+ }
+
+ div = ((DIV_ROUND_UP(prate, rate) - 1) << div_shift) & div_mask;
+ rk_clrsetreg(&cru->clksel_con[id], sel, div);
+
+ return rk3528_dclk_vop_get_clk(priv, clk_id);
+}
+
+static ulong rk3528_uart_get_rate(struct rk3528_clk_priv *priv, ulong clk_id)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 sel_shift, sel_mask, div_shift, div_mask;
+ u32 sel, id, con, frac_div, div;
+ ulong m, n, rate;
+
+ switch (clk_id) {
+ case SCLK_UART0:
+ id = 6;
+ sel_shift = SCLK_UART0_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART0_SRC_SEL_MASK;
+ div_shift = CLK_UART0_SRC_DIV_SHIFT;
+ div_mask = CLK_UART0_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART1:
+ id = 8;
+ sel_shift = SCLK_UART1_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART1_SRC_SEL_MASK;
+ div_shift = CLK_UART1_SRC_DIV_SHIFT;
+ div_mask = CLK_UART1_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART2:
+ id = 10;
+ sel_shift = SCLK_UART2_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART2_SRC_SEL_MASK;
+ div_shift = CLK_UART2_SRC_DIV_SHIFT;
+ div_mask = CLK_UART2_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART3:
+ id = 12;
+ sel_shift = SCLK_UART3_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART3_SRC_SEL_MASK;
+ div_shift = CLK_UART3_SRC_DIV_SHIFT;
+ div_mask = CLK_UART3_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART4:
+ id = 14;
+ sel_shift = SCLK_UART4_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART4_SRC_SEL_MASK;
+ div_shift = CLK_UART4_SRC_DIV_SHIFT;
+ div_mask = CLK_UART4_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART5:
+ id = 16;
+ sel_shift = SCLK_UART5_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART5_SRC_SEL_MASK;
+ div_shift = CLK_UART5_SRC_DIV_SHIFT;
+ div_mask = CLK_UART5_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART6:
+ id = 18;
+ sel_shift = SCLK_UART6_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART6_SRC_SEL_MASK;
+ div_shift = CLK_UART6_SRC_DIV_SHIFT;
+ div_mask = CLK_UART6_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART7:
+ id = 20;
+ sel_shift = SCLK_UART7_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART7_SRC_SEL_MASK;
+ div_shift = CLK_UART7_SRC_DIV_SHIFT;
+ div_mask = CLK_UART7_SRC_DIV_MASK;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ con = readl(&cru->clksel_con[id - 2]);
+ div = (con & div_mask) >> div_shift;
+
+ con = readl(&cru->clksel_con[id]);
+ sel = (con & sel_mask) >> sel_shift;
+
+ if (sel == SCLK_UART0_SRC_SEL_CLK_UART0_SRC) {
+ rate = DIV_TO_RATE(priv->gpll_hz, div);
+ } else if (sel == SCLK_UART0_SRC_SEL_CLK_UART0_FRAC) {
+ frac_div = readl(&cru->clksel_con[id - 1]);
+ n = (frac_div & 0xffff0000) >> 16;
+ m = frac_div & 0x0000ffff;
+ rate = DIV_TO_RATE(priv->gpll_hz, div) * n / m;
+ } else {
+ rate = OSC_HZ;
+ }
+
+ return rate;
+}
+
+static ulong rk3528_uart_set_rate(struct rk3528_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 sel_shift, sel_mask, div_shift, div_mask;
+ u32 sel, id, div;
+ ulong m = 0, n = 0, val;
+
+ if (rate == OSC_HZ) {
+ sel = SCLK_UART0_SRC_SEL_XIN_OSC0_FUNC;
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ } else if (priv->gpll_hz % rate == 0) {
+ sel = SCLK_UART0_SRC_SEL_CLK_UART0_SRC;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ } else {
+ sel = SCLK_UART0_SRC_SEL_CLK_UART0_FRAC;
+ div = 2;
+ rational_best_approximation(rate, priv->gpll_hz / div,
+ GENMASK(16 - 1, 0),
+ GENMASK(16 - 1, 0),
+ &n, &m);
+ }
+
+ switch (clk_id) {
+ case SCLK_UART0:
+ id = 6;
+ sel_shift = SCLK_UART0_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART0_SRC_SEL_MASK;
+ div_shift = CLK_UART0_SRC_DIV_SHIFT;
+ div_mask = CLK_UART0_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART1:
+ id = 8;
+ sel_shift = SCLK_UART1_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART1_SRC_SEL_MASK;
+ div_shift = CLK_UART1_SRC_DIV_SHIFT;
+ div_mask = CLK_UART1_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART2:
+ id = 10;
+ sel_shift = SCLK_UART2_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART2_SRC_SEL_MASK;
+ div_shift = CLK_UART2_SRC_DIV_SHIFT;
+ div_mask = CLK_UART2_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART3:
+ id = 12;
+ sel_shift = SCLK_UART3_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART3_SRC_SEL_MASK;
+ div_shift = CLK_UART3_SRC_DIV_SHIFT;
+ div_mask = CLK_UART3_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART4:
+ id = 14;
+ sel_shift = SCLK_UART4_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART4_SRC_SEL_MASK;
+ div_shift = CLK_UART4_SRC_DIV_SHIFT;
+ div_mask = CLK_UART4_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART5:
+ id = 16;
+ sel_shift = SCLK_UART5_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART5_SRC_SEL_MASK;
+ div_shift = CLK_UART5_SRC_DIV_SHIFT;
+ div_mask = CLK_UART5_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART6:
+ id = 18;
+ sel_shift = SCLK_UART6_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART6_SRC_SEL_MASK;
+ div_shift = CLK_UART6_SRC_DIV_SHIFT;
+ div_mask = CLK_UART6_SRC_DIV_MASK;
+ break;
+
+ case SCLK_UART7:
+ id = 20;
+ sel_shift = SCLK_UART7_SRC_SEL_SHIFT;
+ sel_mask = SCLK_UART7_SRC_SEL_MASK;
+ div_shift = CLK_UART7_SRC_DIV_SHIFT;
+ div_mask = CLK_UART7_SRC_DIV_MASK;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[id - 2], div_mask, (div - 1) << div_shift);
+ rk_clrsetreg(&cru->clksel_con[id], sel_mask, sel << sel_shift);
+ if (m && n) {
+ val = n << 16 | m;
+ writel(val, &cru->clksel_con[id - 1]);
+ }
+
+ return rk3528_uart_get_rate(priv, clk_id);
+}
+
+static ulong rk3528_clk_get_rate(struct clk *clk)
+{
+ struct rk3528_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong rate = 0;
+
+ if (!priv->gpll_hz || !priv->cpll_hz) {
+ printf("%s: gpll=%lu, cpll=%ld\n",
+ __func__, priv->gpll_hz, priv->cpll_hz);
+ return -ENOENT;
+ }
+
+ switch (clk->id) {
+ case PLL_APLL:
+ case ARMCLK:
+ rate = rockchip_pll_get_rate(&rk3528_pll_clks[APLL], priv->cru,
+ APLL);
+ break;
+ case PLL_CPLL:
+ rate = rockchip_pll_get_rate(&rk3528_pll_clks[CPLL], priv->cru,
+ CPLL);
+ break;
+ case PLL_GPLL:
+ rate = rockchip_pll_get_rate(&rk3528_pll_clks[GPLL], priv->cru,
+ GPLL);
+ break;
+
+ case PLL_PPLL:
+ rate = rockchip_pll_get_rate(&rk3528_pll_clks[PPLL], priv->cru,
+ PPLL);
+ break;
+ case PLL_DPLL:
+ rate = rockchip_pll_get_rate(&rk3528_pll_clks[DPLL], priv->cru,
+ DPLL);
+ break;
+
+ case TCLK_EMMC:
+ case TCLK_WDT_NS:
+ rate = OSC_HZ;
+ break;
+ case CLK_I2C0:
+ case CLK_I2C1:
+ case CLK_I2C2:
+ case CLK_I2C3:
+ case CLK_I2C4:
+ case CLK_I2C5:
+ case CLK_I2C6:
+ case CLK_I2C7:
+ rate = rk3528_i2c_get_clk(priv, clk->id);
+ break;
+ case CLK_SPI0:
+ case CLK_SPI1:
+ rate = rk3528_spi_get_clk(priv, clk->id);
+ break;
+ case CLK_PWM0:
+ case CLK_PWM1:
+ rate = rk3528_pwm_get_clk(priv, clk->id);
+ break;
+ case CLK_SARADC:
+ case CLK_TSADC:
+ case CLK_TSADC_TSEN:
+ rate = rk3528_adc_get_clk(priv, clk->id);
+ break;
+ case CCLK_SRC_EMMC:
+ rate = rk3528_emmc_get_clk(priv);
+ break;
+ case HCLK_SDMMC0:
+ case CCLK_SRC_SDMMC0:
+ rate = rk3528_sdmmc_get_clk(priv, clk->id);
+ break;
+ case SCLK_SFC:
+ rate = rk3528_sfc_get_clk(priv);
+ break;
+ case DCLK_VOP0:
+ case DCLK_VOP1:
+ rate = rk3528_dclk_vop_get_clk(priv, clk->id);
+ break;
+ case DCLK_CVBS:
+ rate = rk3528_dclk_vop_get_clk(priv, DCLK_VOP1) / 4;
+ break;
+ case DCLK_4X_CVBS:
+ rate = rk3528_dclk_vop_get_clk(priv, DCLK_VOP1);
+ break;
+ case SCLK_UART0:
+ case SCLK_UART1:
+ case SCLK_UART2:
+ case SCLK_UART3:
+ case SCLK_UART4:
+ case SCLK_UART5:
+ case SCLK_UART6:
+ case SCLK_UART7:
+ rate = rk3528_uart_get_rate(priv, clk->id);
+ break;
+ case CLK_MATRIX_50M_SRC:
+ case CLK_MATRIX_100M_SRC:
+ case CLK_MATRIX_150M_SRC:
+ case CLK_MATRIX_200M_SRC:
+ case CLK_MATRIX_250M_SRC:
+ case CLK_MATRIX_300M_SRC:
+ case CLK_MATRIX_339M_SRC:
+ case CLK_MATRIX_400M_SRC:
+ case CLK_MATRIX_500M_SRC:
+ case CLK_MATRIX_600M_SRC:
+ case ACLK_BUS_VOPGL_BIU:
+ rate = rk3528_cgpll_matrix_get_rate(priv, clk->id);
+ break;
+ case CLK_PPLL_50M_MATRIX:
+ case CLK_PPLL_100M_MATRIX:
+ case CLK_PPLL_125M_MATRIX:
+ case CLK_GMAC1_VPU_25M:
+ case CLK_GMAC1_RMII_VPU:
+ case CLK_GMAC1_SRC_VPU:
+ rate = rk3528_ppll_matrix_get_rate(priv, clk->id);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rate;
+};
+
+static ulong rk3528_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct rk3528_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong ret = 0;
+
+ if (!priv->gpll_hz) {
+ printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
+ return -ENOENT;
+ }
+
+ switch (clk->id) {
+ case PLL_APLL:
+ case ARMCLK:
+ if (priv->armclk_hz)
+ rk3528_armclk_set_clk(priv, rate);
+ priv->armclk_hz = rate;
+ break;
+ case PLL_CPLL:
+ ret = rockchip_pll_set_rate(&rk3528_pll_clks[CPLL], priv->cru,
+ CPLL, rate);
+ priv->cpll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[CPLL],
+ priv->cru, CPLL);
+ break;
+ case PLL_GPLL:
+ ret = rockchip_pll_set_rate(&rk3528_pll_clks[GPLL], priv->cru,
+ GPLL, rate);
+ priv->gpll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[GPLL],
+ priv->cru, GPLL);
+ break;
+ case PLL_PPLL:
+ ret = rockchip_pll_set_rate(&rk3528_pll_clks[PPLL], priv->cru,
+ PPLL, rate);
+ priv->ppll_hz = rockchip_pll_get_rate(&rk3528_pll_clks[PPLL],
+ priv->cru, PPLL);
+ break;
+ case TCLK_EMMC:
+ case TCLK_WDT_NS:
+ return (rate == OSC_HZ) ? 0 : -EINVAL;
+ case CLK_I2C0:
+ case CLK_I2C1:
+ case CLK_I2C2:
+ case CLK_I2C3:
+ case CLK_I2C4:
+ case CLK_I2C5:
+ case CLK_I2C6:
+ case CLK_I2C7:
+ ret = rk3528_i2c_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_SPI0:
+ case CLK_SPI1:
+ ret = rk3528_spi_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_PWM0:
+ case CLK_PWM1:
+ ret = rk3528_pwm_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_SARADC:
+ case CLK_TSADC:
+ case CLK_TSADC_TSEN:
+ ret = rk3528_adc_set_clk(priv, clk->id, rate);
+ break;
+ case HCLK_SDMMC0:
+ case CCLK_SRC_SDMMC0:
+ ret = rk3528_sdmmc_set_clk(priv, clk->id, rate);
+ break;
+ case SCLK_SFC:
+ ret = rk3528_sfc_set_clk(priv, rate);
+ break;
+ case CCLK_SRC_EMMC:
+ ret = rk3528_emmc_set_clk(priv, rate);
+ break;
+ case DCLK_VOP0:
+ case DCLK_VOP1:
+ ret = rk3528_dclk_vop_set_clk(priv, clk->id, rate);
+ break;
+ case SCLK_UART0:
+ case SCLK_UART1:
+ case SCLK_UART2:
+ case SCLK_UART3:
+ case SCLK_UART4:
+ case SCLK_UART5:
+ case SCLK_UART6:
+ case SCLK_UART7:
+ ret = rk3528_uart_set_rate(priv, clk->id, rate);
+ break;
+ case CLK_MATRIX_50M_SRC:
+ case CLK_MATRIX_100M_SRC:
+ case CLK_MATRIX_150M_SRC:
+ case CLK_MATRIX_200M_SRC:
+ case CLK_MATRIX_250M_SRC:
+ case CLK_MATRIX_300M_SRC:
+ case CLK_MATRIX_339M_SRC:
+ case CLK_MATRIX_400M_SRC:
+ case CLK_MATRIX_500M_SRC:
+ case CLK_MATRIX_600M_SRC:
+ case ACLK_BUS_VOPGL_BIU:
+ ret = rk3528_cgpll_matrix_set_rate(priv, clk->id, rate);
+ break;
+ case CLK_PPLL_50M_MATRIX:
+ case CLK_PPLL_100M_MATRIX:
+ case CLK_PPLL_125M_MATRIX:
+ case CLK_GMAC1_VPU_25M:
+ ret = rk3528_ppll_matrix_set_rate(priv, clk->id, rate);
+ break;
+ case CLK_GMAC1_RMII_VPU:
+ case CLK_GMAC1_SRC_VPU:
+ /* dummy set */
+ ret = rk3528_ppll_matrix_get_rate(priv, clk->id);
+ break;
+
+ /* Might occur in cru assigned-clocks, can be ignored here */
+ case ACLK_BUS_VOPGL_ROOT:
+ case BCLK_EMMC:
+ case XIN_OSC0_DIV:
+ ret = 0;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return ret;
+};
+
+static struct clk_ops rk3528_clk_ops = {
+ .get_rate = rk3528_clk_get_rate,
+ .set_rate = rk3528_clk_set_rate,
+};
+
+#ifdef CONFIG_XPL_BUILD
+
+#define COREGRF_BASE 0xff300000
+#define PVTPLL_CON0_L 0x0
+#define PVTPLL_CON0_H 0x4
+
+static int rk3528_cpu_pvtpll_set_rate(struct rk3528_clk_priv *priv, ulong rate)
+{
+ struct rk3528_cru *cru = priv->cru;
+ u32 length;
+
+ if (rate >= 1200000000)
+ length = 8;
+ else if (rate >= 1008000000)
+ length = 11;
+ else
+ length = 17;
+
+ /* set pclk dbg div to 9 */
+ rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK,
+ 9 << RK3528_DIV_PCLK_DBG_SHIFT);
+ /* set aclk_m_core div to 1 */
+ rk_clrsetreg(&cru->clksel_con[39], RK3528_DIV_ACLK_M_CORE_MASK,
+ 1 << RK3528_DIV_ACLK_M_CORE_SHIFT);
+
+ /* set ring sel = 1 */
+ writel(0x07000000 | (1 << 8), COREGRF_BASE + PVTPLL_CON0_L);
+ /* set length */
+ writel(0x007f0000 | length, COREGRF_BASE + PVTPLL_CON0_H);
+ /* enable pvtpll */
+ writel(0x00020002, COREGRF_BASE + PVTPLL_CON0_L);
+ /* start monitor */
+ writel(0x00010001, COREGRF_BASE + PVTPLL_CON0_L);
+
+ /* set core mux pvtpll */
+ writel(0x00010001, &cru->clksel_con[40]);
+ writel(0x00100010, &cru->clksel_con[39]);
+
+ /* set pclk dbg div to 8 */
+ rk_clrsetreg(&cru->clksel_con[40], RK3528_DIV_PCLK_DBG_MASK,
+ 8 << RK3528_DIV_PCLK_DBG_SHIFT);
+
+ return 0;
+}
+#endif
+
+static int rk3528_clk_init(struct rk3528_clk_priv *priv)
+{
+ int ret;
+
+ priv->sync_kernel = false;
+
+#ifdef CONFIG_XPL_BUILD
+ /*
+ * BOOTROM:
+ * CPU 1902/2(postdiv1)=546M
+ * CPLL 996/2(postdiv1)=498M
+ * GPLL 1188/2(postdiv1)=594M
+ * |-- clk_matrix_200m_src_div=1 => rate: 300M
+ * |-- clk_matrix_300m_src_div=2 => rate: 200M
+ *
+ * Avoid overclocking when change GPLL rate:
+ * Change clk_matrix_200m_src_div to 5.
+ * Change clk_matrix_300m_src_div to 3.
+ */
+ writel(0x01200120, &priv->cru->clksel_con[1]);
+ writel(0x00030003, &priv->cru->clksel_con[2]);
+
+ if (!priv->armclk_enter_hz) {
+ priv->armclk_enter_hz =
+ rockchip_pll_get_rate(&rk3528_pll_clks[APLL],
+ priv->cru, APLL);
+ priv->armclk_init_hz = priv->armclk_enter_hz;
+ }
+
+ if (priv->armclk_init_hz != APLL_HZ) {
+ ret = rk3528_armclk_set_clk(priv, APLL_HZ);
+ if (!ret)
+ priv->armclk_init_hz = APLL_HZ;
+ }
+
+ if (!rk3528_cpu_pvtpll_set_rate(priv, CPU_PVTPLL_HZ)) {
+ debug("cpu pvtpll %d KHz\n", CPU_PVTPLL_HZ / 1000);
+ priv->armclk_init_hz = CPU_PVTPLL_HZ;
+ }
+#endif
+
+ if (priv->cpll_hz != CPLL_HZ) {
+ ret = rockchip_pll_set_rate(&rk3528_pll_clks[CPLL], priv->cru,
+ CPLL, CPLL_HZ);
+ if (!ret)
+ priv->cpll_hz = CPLL_HZ;
+ }
+
+ if (priv->gpll_hz != GPLL_HZ) {
+ ret = rockchip_pll_set_rate(&rk3528_pll_clks[GPLL], priv->cru,
+ GPLL, GPLL_HZ);
+ if (!ret)
+ priv->gpll_hz = GPLL_HZ;
+ }
+
+ if (priv->ppll_hz != PPLL_HZ) {
+ ret = rockchip_pll_set_rate(&rk3528_pll_clks[PPLL], priv->cru,
+ PPLL, PPLL_HZ);
+ if (!ret)
+ priv->ppll_hz = PPLL_HZ;
+ }
+
+#ifdef CONFIG_XPL_BUILD
+ /* Init to override bootrom config */
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_50M_SRC, 50000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_100M_SRC, 100000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_150M_SRC, 150000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_200M_SRC, 200000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_250M_SRC, 250000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_300M_SRC, 300000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_339M_SRC, 340000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_400M_SRC, 400000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_500M_SRC, 500000000);
+ rk3528_cgpll_matrix_set_rate(priv, CLK_MATRIX_600M_SRC, 600000000);
+ rk3528_cgpll_matrix_set_rate(priv, ACLK_BUS_VOPGL_BIU, 500000000);
+
+ /* The default rate is 100Mhz, it's not friendly for remote IR module */
+ rk3528_pwm_set_clk(priv, CLK_PWM0, 24000000);
+ rk3528_pwm_set_clk(priv, CLK_PWM1, 24000000);
+#endif
+ return 0;
+}
+
+static int rk3528_clk_probe(struct udevice *dev)
+{
+ struct rk3528_clk_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = rk3528_clk_init(priv);
+ if (ret)
+ return ret;
+
+ /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
+ ret = clk_set_defaults(dev, 1);
+ if (ret)
+ debug("%s clk_set_defaults failed %d\n", __func__, ret);
+ else
+ priv->sync_kernel = true;
+
+ return 0;
+}
+
+static int rk3528_clk_ofdata_to_platdata(struct udevice *dev)
+{
+ struct rk3528_clk_priv *priv = dev_get_priv(dev);
+
+ priv->cru = dev_read_addr_ptr(dev);
+
+ return 0;
+}
+
+static int rk3528_clk_bind(struct udevice *dev)
+{
+ struct udevice *sys_child;
+ struct sysreset_reg *priv;
+ int ret;
+
+ /* The reset driver does not have a device node, so bind it here */
+ ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+ &sys_child);
+ if (ret) {
+ debug("Warning: No sysreset driver: ret=%d\n", ret);
+ } else {
+ priv = malloc(sizeof(struct sysreset_reg));
+ priv->glb_srst_fst_value = offsetof(struct rk3528_cru,
+ glb_srst_fst);
+ priv->glb_srst_snd_value = offsetof(struct rk3528_cru,
+ glb_srst_snd);
+ dev_set_priv(sys_child, priv);
+ }
+
+#if CONFIG_IS_ENABLED(RESET_ROCKCHIP)
+ ret = offsetof(struct rk3528_cru, softrst_con[0]);
+ ret = rk3528_reset_bind_lut(dev, ret, 47);
+ if (ret)
+ debug("Warning: software reset driver bind failed\n");
+#endif
+
+ return 0;
+}
+
+static const struct udevice_id rk3528_clk_ids[] = {
+ { .compatible = "rockchip,rk3528-cru" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_rk3528_cru) = {
+ .name = "rockchip_rk3528_cru",
+ .id = UCLASS_CLK,
+ .of_match = rk3528_clk_ids,
+ .priv_auto = sizeof(struct rk3528_clk_priv),
+ .of_to_plat = rk3528_clk_ofdata_to_platdata,
+ .ops = &rk3528_clk_ops,
+ .bind = rk3528_clk_bind,
+ .probe = rk3528_clk_probe,
+};
diff --git a/drivers/clk/rockchip/clk_rk3576.c b/drivers/clk/rockchip/clk_rk3576.c
new file mode 100644
index 00000000000..e84a0943a94
--- /dev/null
+++ b/drivers/clk/rockchip/clk_rk3576.c
@@ -0,0 +1,2513 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Elaine Zhang <zhangqing@rock-chips.com>
+ */
+
+#include <bitfield.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch-rockchip/cru_rk3576.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dt-bindings/clock/rockchip,rk3576-cru.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
+
+static struct rockchip_pll_rate_table rk3576_24m_pll_rates[] = {
+ /* _mhz, _p, _m, _s, _k */
+ RK3588_PLL_RATE(1500000000, 2, 250, 1, 0),
+ RK3588_PLL_RATE(1200000000, 1, 100, 1, 0),
+ RK3588_PLL_RATE(1188000000, 2, 198, 1, 0),
+ RK3588_PLL_RATE(1150000000, 3, 575, 2, 0),
+ RK3588_PLL_RATE(1100000000, 3, 550, 2, 0),
+ RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
+ RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
+ RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
+ RK3588_PLL_RATE(850000000, 3, 425, 2, 0),
+ RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
+ RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
+ RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
+ RK3588_PLL_RATE(742500000, 4, 495, 2, 0),
+ RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
+ RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
+ RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
+ RK3588_PLL_RATE(200000000, 3, 400, 4, 0),
+ RK3588_PLL_RATE(100000000, 3, 400, 5, 0),
+ { /* sentinel */ },
+};
+
+static struct rockchip_pll_clock rk3576_pll_clks[] = {
+ [BPLL] = PLL(pll_rk3588, PLL_BPLL, RK3576_PLL_CON(0),
+ RK3576_BPLL_MODE_CON0, 0, 15, 0,
+ rk3576_24m_pll_rates),
+ [LPLL] = PLL(pll_rk3588, PLL_LPLL, RK3576_LPLL_CON(16),
+ RK3576_LPLL_MODE_CON0, 0, 15, 0, rk3576_24m_pll_rates),
+ [VPLL] = PLL(pll_rk3588, PLL_VPLL, RK3576_PLL_CON(88),
+ RK3576_LPLL_MODE_CON0, 4, 15, 0, rk3576_24m_pll_rates),
+ [AUPLL] = PLL(pll_rk3588, PLL_AUPLL, RK3576_PLL_CON(96),
+ RK3576_MODE_CON0, 6, 15, 0, rk3576_24m_pll_rates),
+ [CPLL] = PLL(pll_rk3588, PLL_CPLL, RK3576_PLL_CON(104),
+ RK3576_MODE_CON0, 8, 15, 0, rk3576_24m_pll_rates),
+ [GPLL] = PLL(pll_rk3588, PLL_GPLL, RK3576_PLL_CON(112),
+ RK3576_MODE_CON0, 2, 15, 0, rk3576_24m_pll_rates),
+ [PPLL] = PLL(pll_rk3588, PLL_PPLL, RK3576_PMU_PLL_CON(128),
+ RK3576_MODE_CON0, 10, 15, 0, rk3576_24m_pll_rates),
+};
+
+#ifdef CONFIG_SPL_BUILD
+#ifndef BITS_WITH_WMASK
+#define BITS_WITH_WMASK(bits, msk, shift) \
+ ((bits) << (shift)) | ((msk) << ((shift) + 16))
+#endif
+#endif
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ *
+ * rational_best_approximation(31415, 10000,
+ * (1 << 8) - 1, (1 << 5) - 1, &n, &d);
+ *
+ * you may look at given_numerator as a fixed point number,
+ * with the fractional part size described in given_denominator.
+ *
+ * for theoretical background, see:
+ * http://en.wikipedia.org/wiki/Continued_fraction
+ */
+static void rational_best_approximation(unsigned long given_numerator,
+ unsigned long given_denominator,
+ unsigned long max_numerator,
+ unsigned long max_denominator,
+ unsigned long *best_numerator,
+ unsigned long *best_denominator)
+{
+ unsigned long n, d, n0, d0, n1, d1;
+
+ n = given_numerator;
+ d = given_denominator;
+ n0 = 0;
+ d1 = 0;
+ n1 = 1;
+ d0 = 1;
+ for (;;) {
+ unsigned long t, a;
+
+ if (n1 > max_numerator || d1 > max_denominator) {
+ n1 = n0;
+ d1 = d0;
+ break;
+ }
+ if (d == 0)
+ break;
+ t = d;
+ a = n / d;
+ d = n % d;
+ n = t;
+ t = n0 + a * n1;
+ n0 = n1;
+ n1 = t;
+ t = d0 + a * d1;
+ d0 = d1;
+ d1 = t;
+ }
+ *best_numerator = n1;
+ *best_denominator = d1;
+}
+#endif
+
+static ulong rk3576_bus_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 con, sel, div, rate;
+
+ switch (clk_id) {
+ case ACLK_BUS_ROOT:
+ con = readl(&cru->clksel_con[55]);
+ sel = (con & ACLK_BUS_ROOT_SEL_MASK) >>
+ ACLK_BUS_ROOT_SEL_SHIFT;
+ div = (con & ACLK_BUS_ROOT_DIV_MASK) >>
+ ACLK_BUS_ROOT_DIV_SHIFT;
+ if (sel == ACLK_BUS_ROOT_SEL_CPLL)
+ rate = DIV_TO_RATE(priv->cpll_hz, div);
+ else
+ rate = DIV_TO_RATE(priv->gpll_hz, div);
+ break;
+ case HCLK_BUS_ROOT:
+ con = readl(&cru->clksel_con[55]);
+ sel = (con & HCLK_BUS_ROOT_SEL_MASK) >>
+ HCLK_BUS_ROOT_SEL_SHIFT;
+ if (sel == HCLK_BUS_ROOT_SEL_200M)
+ rate = 198 * MHz;
+ else if (sel == HCLK_BUS_ROOT_SEL_100M)
+ rate = 100 * MHz;
+ else if (sel == HCLK_BUS_ROOT_SEL_50M)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+ break;
+ case PCLK_BUS_ROOT:
+ con = readl(&cru->clksel_con[55]);
+ sel = (con & PCLK_BUS_ROOT_SEL_MASK) >>
+ PCLK_BUS_ROOT_SEL_SHIFT;
+ if (sel == PCLK_BUS_ROOT_SEL_100M)
+ rate = 100 * MHz;
+ else if (sel == PCLK_BUS_ROOT_SEL_50M)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rate;
+}
+
+static ulong rk3576_bus_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk, src_clk_div;
+
+ switch (clk_id) {
+ case ACLK_BUS_ROOT:
+ if (!(priv->cpll_hz % rate)) {
+ src_clk = ACLK_BUS_ROOT_SEL_CPLL;
+ src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = ACLK_BUS_ROOT_SEL_GPLL;
+ src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ rk_clrsetreg(&cru->clksel_con[55],
+ ACLK_BUS_ROOT_SEL_MASK,
+ src_clk << ACLK_BUS_ROOT_SEL_SHIFT);
+ assert(src_clk_div - 1 <= 31);
+ rk_clrsetreg(&cru->clksel_con[55],
+ ACLK_BUS_ROOT_DIV_MASK |
+ ACLK_BUS_ROOT_SEL_MASK,
+ (src_clk <<
+ ACLK_BUS_ROOT_SEL_SHIFT) |
+ (src_clk_div - 1) << ACLK_BUS_ROOT_DIV_SHIFT);
+ break;
+ case HCLK_BUS_ROOT:
+ if (rate >= 198 * MHz)
+ src_clk = HCLK_BUS_ROOT_SEL_200M;
+ else if (rate >= 99 * MHz)
+ src_clk = HCLK_BUS_ROOT_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = HCLK_BUS_ROOT_SEL_50M;
+ else
+ src_clk = HCLK_BUS_ROOT_SEL_OSC;
+ rk_clrsetreg(&cru->clksel_con[55],
+ HCLK_BUS_ROOT_SEL_MASK,
+ src_clk << HCLK_BUS_ROOT_SEL_SHIFT);
+ break;
+ case PCLK_BUS_ROOT:
+ if (rate >= 99 * MHz)
+ src_clk = PCLK_BUS_ROOT_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = PCLK_BUS_ROOT_SEL_50M;
+ else
+ src_clk = PCLK_BUS_ROOT_SEL_OSC;
+ rk_clrsetreg(&cru->clksel_con[55],
+ PCLK_BUS_ROOT_SEL_MASK,
+ src_clk << PCLK_BUS_ROOT_SEL_SHIFT);
+ break;
+ default:
+ printf("do not support this center freq\n");
+ return -EINVAL;
+ }
+
+ return rk3576_bus_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_top_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 con, sel, div, rate, prate;
+
+ switch (clk_id) {
+ case ACLK_TOP:
+ con = readl(&cru->clksel_con[9]);
+ div = (con & ACLK_TOP_DIV_MASK) >>
+ ACLK_TOP_DIV_SHIFT;
+ sel = (con & ACLK_TOP_SEL_MASK) >>
+ ACLK_TOP_SEL_SHIFT;
+ if (sel == ACLK_TOP_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else if (sel == ACLK_TOP_SEL_AUPLL)
+ prate = priv->aupll_hz;
+ else
+ prate = priv->gpll_hz;
+ return DIV_TO_RATE(prate, div);
+ case ACLK_TOP_MID:
+ con = readl(&cru->clksel_con[10]);
+ div = (con & ACLK_TOP_MID_DIV_MASK) >>
+ ACLK_TOP_MID_DIV_SHIFT;
+ sel = (con & ACLK_TOP_MID_SEL_MASK) >>
+ ACLK_TOP_MID_SEL_SHIFT;
+ if (sel == ACLK_TOP_MID_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else
+ prate = priv->gpll_hz;
+ return DIV_TO_RATE(prate, div);
+ case PCLK_TOP_ROOT:
+ con = readl(&cru->clksel_con[8]);
+ sel = (con & PCLK_TOP_SEL_MASK) >> PCLK_TOP_SEL_SHIFT;
+ if (sel == PCLK_TOP_SEL_100M)
+ rate = 100 * MHz;
+ else if (sel == PCLK_TOP_SEL_50M)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+ break;
+ case HCLK_TOP:
+ con = readl(&cru->clksel_con[19]);
+ sel = (con & HCLK_TOP_SEL_MASK) >> HCLK_TOP_SEL_SHIFT;
+ if (sel == HCLK_TOP_SEL_200M)
+ rate = 200 * MHz;
+ else if (sel == HCLK_TOP_SEL_100M)
+ rate = 100 * MHz;
+ else if (sel == HCLK_TOP_SEL_50M)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rate;
+}
+
+static ulong rk3576_top_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk, src_clk_div;
+
+ switch (clk_id) {
+ case ACLK_TOP:
+ if (!(priv->cpll_hz % rate)) {
+ src_clk = ACLK_TOP_SEL_CPLL;
+ src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = ACLK_TOP_SEL_GPLL;
+ src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ assert(src_clk_div - 1 <= 31);
+ rk_clrsetreg(&cru->clksel_con[9],
+ ACLK_TOP_DIV_MASK |
+ ACLK_TOP_SEL_MASK,
+ (src_clk <<
+ ACLK_TOP_SEL_SHIFT) |
+ (src_clk_div - 1) << ACLK_TOP_SEL_SHIFT);
+ break;
+ case ACLK_TOP_MID:
+ if (!(priv->cpll_hz % rate)) {
+ src_clk = ACLK_TOP_MID_SEL_CPLL;
+ src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = ACLK_TOP_MID_SEL_GPLL;
+ src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ rk_clrsetreg(&cru->clksel_con[10],
+ ACLK_TOP_MID_DIV_MASK |
+ ACLK_TOP_MID_SEL_MASK,
+ (ACLK_TOP_MID_SEL_GPLL <<
+ ACLK_TOP_MID_SEL_SHIFT) |
+ (src_clk_div - 1) << ACLK_TOP_MID_DIV_SHIFT);
+ break;
+ case PCLK_TOP_ROOT:
+ if (rate >= 99 * MHz)
+ src_clk = PCLK_TOP_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = PCLK_TOP_SEL_50M;
+ else
+ src_clk = PCLK_TOP_SEL_OSC;
+ rk_clrsetreg(&cru->clksel_con[8],
+ PCLK_TOP_SEL_MASK,
+ src_clk << PCLK_TOP_SEL_SHIFT);
+ break;
+ case HCLK_TOP:
+ if (rate >= 198 * MHz)
+ src_clk = HCLK_TOP_SEL_200M;
+ else if (rate >= 99 * MHz)
+ src_clk = HCLK_TOP_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = HCLK_TOP_SEL_50M;
+ else
+ src_clk = HCLK_TOP_SEL_OSC;
+ rk_clrsetreg(&cru->clksel_con[19],
+ HCLK_TOP_SEL_MASK,
+ src_clk << HCLK_TOP_SEL_SHIFT);
+ break;
+ default:
+ printf("do not support this top freq\n");
+ return -EINVAL;
+ }
+
+ return rk3576_top_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_i2c_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 sel, con;
+ ulong rate;
+
+ switch (clk_id) {
+ case CLK_I2C0:
+ con = readl(&cru->pmuclksel_con[6]);
+ sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT;
+ break;
+ case CLK_I2C1:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C1_SEL_MASK) >> CLK_I2C1_SEL_SHIFT;
+ break;
+ case CLK_I2C2:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C2_SEL_MASK) >> CLK_I2C2_SEL_SHIFT;
+ break;
+ case CLK_I2C3:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C3_SEL_MASK) >> CLK_I2C3_SEL_SHIFT;
+ break;
+ case CLK_I2C4:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C4_SEL_MASK) >> CLK_I2C4_SEL_SHIFT;
+ break;
+ case CLK_I2C5:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C5_SEL_MASK) >> CLK_I2C5_SEL_SHIFT;
+ break;
+ case CLK_I2C6:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C6_SEL_MASK) >> CLK_I2C6_SEL_SHIFT;
+ break;
+ case CLK_I2C7:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C7_SEL_MASK) >> CLK_I2C7_SEL_SHIFT;
+ break;
+ case CLK_I2C8:
+ con = readl(&cru->clksel_con[57]);
+ sel = (con & CLK_I2C8_SEL_MASK) >> CLK_I2C8_SEL_SHIFT;
+ break;
+ case CLK_I2C9:
+ con = readl(&cru->clksel_con[58]);
+ sel = (con & CLK_I2C9_SEL_MASK) >> CLK_I2C9_SEL_SHIFT;
+ break;
+
+ default:
+ return -ENOENT;
+ }
+ if (sel == CLK_I2C_SEL_200M)
+ rate = 200 * MHz;
+ else if (sel == CLK_I2C_SEL_100M)
+ rate = 100 * MHz;
+ else if (sel == CLK_I2C_SEL_50M)
+ rate = 50 * MHz;
+ else
+ rate = OSC_HZ;
+
+ return rate;
+}
+
+static ulong rk3576_i2c_set_clk(struct rk3576_clk_priv *priv, ulong clk_id,
+ ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk;
+
+ if (rate >= 198 * MHz)
+ src_clk = CLK_I2C_SEL_200M;
+ else if (rate >= 99 * MHz)
+ src_clk = CLK_I2C_SEL_100M;
+ if (rate >= 50 * MHz)
+ src_clk = CLK_I2C_SEL_50M;
+ else
+ src_clk = CLK_I2C_SEL_OSC;
+
+ switch (clk_id) {
+ case CLK_I2C0:
+ rk_clrsetreg(&cru->pmuclksel_con[6], CLK_I2C0_SEL_MASK,
+ src_clk << CLK_I2C0_SEL_SHIFT);
+ break;
+ case CLK_I2C1:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C1_SEL_MASK,
+ src_clk << CLK_I2C1_SEL_SHIFT);
+ break;
+ case CLK_I2C2:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C2_SEL_MASK,
+ src_clk << CLK_I2C2_SEL_SHIFT);
+ break;
+ case CLK_I2C3:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C3_SEL_MASK,
+ src_clk << CLK_I2C3_SEL_SHIFT);
+ break;
+ case CLK_I2C4:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C4_SEL_MASK,
+ src_clk << CLK_I2C4_SEL_SHIFT);
+ break;
+ case CLK_I2C5:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C5_SEL_MASK,
+ src_clk << CLK_I2C5_SEL_SHIFT);
+ break;
+ case CLK_I2C6:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C6_SEL_MASK,
+ src_clk << CLK_I2C6_SEL_SHIFT);
+ break;
+ case CLK_I2C7:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C7_SEL_MASK,
+ src_clk << CLK_I2C7_SEL_SHIFT);
+ break;
+ case CLK_I2C8:
+ rk_clrsetreg(&cru->clksel_con[57], CLK_I2C8_SEL_MASK,
+ src_clk << CLK_I2C8_SEL_SHIFT);
+ case CLK_I2C9:
+ rk_clrsetreg(&cru->clksel_con[58], CLK_I2C9_SEL_MASK,
+ src_clk << CLK_I2C9_SEL_SHIFT);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rk3576_i2c_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_spi_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 sel, con;
+
+ switch (clk_id) {
+ case CLK_SPI0:
+ con = readl(&cru->clksel_con[70]);
+ sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT;
+ break;
+ case CLK_SPI1:
+ con = readl(&cru->clksel_con[71]);
+ sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT;
+ break;
+ case CLK_SPI2:
+ con = readl(&cru->clksel_con[71]);
+ sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT;
+ break;
+ case CLK_SPI3:
+ con = readl(&cru->clksel_con[71]);
+ sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT;
+ break;
+ case CLK_SPI4:
+ con = readl(&cru->clksel_con[71]);
+ sel = (con & CLK_SPI4_SEL_MASK) >> CLK_SPI4_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ switch (sel) {
+ case CLK_SPI_SEL_200M:
+ return 200 * MHz;
+ case CLK_SPI_SEL_100M:
+ return 100 * MHz;
+ case CLK_SPI_SEL_50M:
+ return 50 * MHz;
+ case CLK_SPI_SEL_OSC:
+ return OSC_HZ;
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_spi_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk;
+
+ if (rate >= 198 * MHz)
+ src_clk = CLK_SPI_SEL_200M;
+ else if (rate >= 99 * MHz)
+ src_clk = CLK_SPI_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = CLK_SPI_SEL_50M;
+ else
+ src_clk = CLK_SPI_SEL_OSC;
+
+ switch (clk_id) {
+ case CLK_SPI0:
+ rk_clrsetreg(&cru->clksel_con[70],
+ CLK_SPI0_SEL_MASK,
+ src_clk << CLK_SPI0_SEL_SHIFT);
+ break;
+ case CLK_SPI1:
+ rk_clrsetreg(&cru->clksel_con[71],
+ CLK_SPI1_SEL_MASK,
+ src_clk << CLK_SPI1_SEL_SHIFT);
+ break;
+ case CLK_SPI2:
+ rk_clrsetreg(&cru->clksel_con[71],
+ CLK_SPI2_SEL_MASK,
+ src_clk << CLK_SPI2_SEL_SHIFT);
+ break;
+ case CLK_SPI3:
+ rk_clrsetreg(&cru->clksel_con[71],
+ CLK_SPI3_SEL_MASK,
+ src_clk << CLK_SPI3_SEL_SHIFT);
+ break;
+ case CLK_SPI4:
+ rk_clrsetreg(&cru->clksel_con[71],
+ CLK_SPI4_SEL_MASK,
+ src_clk << CLK_SPI4_SEL_SHIFT);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rk3576_spi_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_pwm_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 sel, con;
+
+ switch (clk_id) {
+ case CLK_PWM1:
+ con = readl(&cru->clksel_con[71]);
+ sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM1_SEL_SHIFT;
+ break;
+ case CLK_PWM2:
+ con = readl(&cru->clksel_con[74]);
+ sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT;
+ break;
+ case CLK_PMU1PWM:
+ con = readl(&cru->pmuclksel_con[5]);
+ sel = (con & CLK_PMU1PWM_SEL_MASK) >> CLK_PMU1PWM_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ switch (sel) {
+ case CLK_PWM_SEL_100M:
+ return 100 * MHz;
+ case CLK_PWM_SEL_50M:
+ return 50 * MHz;
+ case CLK_PWM_SEL_OSC:
+ return OSC_HZ;
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_pwm_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk;
+
+ if (rate >= 99 * MHz)
+ src_clk = CLK_PWM_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = CLK_PWM_SEL_50M;
+ else
+ src_clk = CLK_PWM_SEL_OSC;
+
+ switch (clk_id) {
+ case CLK_PWM1:
+ rk_clrsetreg(&cru->clksel_con[71],
+ CLK_PWM1_SEL_MASK,
+ src_clk << CLK_PWM1_SEL_SHIFT);
+ break;
+ case CLK_PWM2:
+ rk_clrsetreg(&cru->clksel_con[74],
+ CLK_PWM2_SEL_MASK,
+ src_clk << CLK_PWM2_SEL_SHIFT);
+ break;
+ case CLK_PMU1PWM:
+ rk_clrsetreg(&cru->pmuclksel_con[5],
+ CLK_PMU1PWM_SEL_MASK,
+ src_clk << CLK_PMU1PWM_SEL_SHIFT);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rk3576_pwm_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_adc_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 div, sel, con, prate;
+
+ switch (clk_id) {
+ case CLK_SARADC:
+ con = readl(&cru->clksel_con[58]);
+ div = (con & CLK_SARADC_DIV_MASK) >> CLK_SARADC_DIV_SHIFT;
+ sel = (con & CLK_SARADC_SEL_MASK) >>
+ CLK_SARADC_SEL_SHIFT;
+ if (sel == CLK_SARADC_SEL_OSC)
+ prate = OSC_HZ;
+ else
+ prate = priv->gpll_hz;
+ return DIV_TO_RATE(prate, div);
+ case CLK_TSADC:
+ con = readl(&cru->clksel_con[59]);
+ div = (con & CLK_TSADC_DIV_MASK) >>
+ CLK_TSADC_DIV_SHIFT;
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_adc_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk_div;
+
+ switch (clk_id) {
+ case CLK_SARADC:
+ if (!(OSC_HZ % rate)) {
+ src_clk_div = DIV_ROUND_UP(OSC_HZ, rate);
+ assert(src_clk_div - 1 <= 255);
+ rk_clrsetreg(&cru->clksel_con[58],
+ CLK_SARADC_SEL_MASK |
+ CLK_SARADC_DIV_MASK,
+ (CLK_SARADC_SEL_OSC <<
+ CLK_SARADC_SEL_SHIFT) |
+ (src_clk_div - 1) <<
+ CLK_SARADC_DIV_SHIFT);
+ } else {
+ src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ assert(src_clk_div - 1 <= 255);
+ rk_clrsetreg(&cru->clksel_con[59],
+ CLK_SARADC_SEL_MASK |
+ CLK_SARADC_DIV_MASK,
+ (CLK_SARADC_SEL_GPLL <<
+ CLK_SARADC_SEL_SHIFT) |
+ (src_clk_div - 1) <<
+ CLK_SARADC_DIV_SHIFT);
+ }
+ break;
+ case CLK_TSADC:
+ src_clk_div = DIV_ROUND_UP(OSC_HZ, rate);
+ assert(src_clk_div - 1 <= 255);
+ rk_clrsetreg(&cru->clksel_con[58],
+ CLK_TSADC_DIV_MASK,
+ (src_clk_div - 1) <<
+ CLK_TSADC_DIV_SHIFT);
+ break;
+ default:
+ return -ENOENT;
+ }
+ return rk3576_adc_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_mmc_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 sel, con, prate, div = 0;
+
+ switch (clk_id) {
+ case CCLK_SRC_SDIO:
+ case HCLK_SDIO:
+ con = readl(&cru->clksel_con[104]);
+ div = (con & CCLK_SDIO_SRC_DIV_MASK) >> CCLK_SDIO_SRC_DIV_SHIFT;
+ sel = (con & CCLK_SDIO_SRC_SEL_MASK) >>
+ CCLK_SDIO_SRC_SEL_SHIFT;
+ if (sel == CCLK_SDIO_SRC_SEL_GPLL)
+ prate = priv->gpll_hz;
+ else if (sel == CCLK_SDIO_SRC_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ case CCLK_SRC_SDMMC0:
+ case HCLK_SDMMC0:
+ con = readl(&cru->clksel_con[105]);
+ div = (con & CCLK_SDMMC0_SRC_DIV_MASK) >> CCLK_SDMMC0_SRC_DIV_SHIFT;
+ sel = (con & CCLK_SDMMC0_SRC_SEL_MASK) >>
+ CCLK_SDMMC0_SRC_SEL_SHIFT;
+ if (sel == CCLK_SDMMC0_SRC_SEL_GPLL)
+ prate = priv->gpll_hz;
+ else if (sel == CCLK_SDMMC0_SRC_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ case CCLK_SRC_EMMC:
+ case HCLK_EMMC:
+ con = readl(&cru->clksel_con[89]);
+ div = (con & CCLK_EMMC_DIV_MASK) >> CCLK_EMMC_DIV_SHIFT;
+ sel = (con & CCLK_EMMC_SEL_MASK) >>
+ CCLK_EMMC_SEL_SHIFT;
+ if (sel == CCLK_EMMC_SEL_GPLL)
+ prate = priv->gpll_hz;
+ else if (sel == CCLK_EMMC_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ case BCLK_EMMC:
+ con = readl(&cru->clksel_con[90]);
+ sel = (con & BCLK_EMMC_SEL_MASK) >>
+ BCLK_EMMC_SEL_SHIFT;
+ if (sel == BCLK_EMMC_SEL_200M)
+ prate = 200 * MHz;
+ else if (sel == BCLK_EMMC_SEL_100M)
+ prate = 100 * MHz;
+ else if (sel == BCLK_EMMC_SEL_50M)
+ prate = 50 * MHz;
+ else
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ case SCLK_FSPI_X2:
+ con = readl(&cru->clksel_con[89]);
+ div = (con & SCLK_FSPI_DIV_MASK) >> SCLK_FSPI_DIV_SHIFT;
+ sel = (con & SCLK_FSPI_SEL_MASK) >>
+ SCLK_FSPI_SEL_SHIFT;
+ if (sel == SCLK_FSPI_SEL_GPLL)
+ prate = priv->gpll_hz;
+ else if (sel == SCLK_FSPI_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ case SCLK_FSPI1_X2:
+ con = readl(&cru->clksel_con[106]);
+ div = (con & SCLK_FSPI_DIV_MASK) >> SCLK_FSPI_DIV_SHIFT;
+ sel = (con & SCLK_FSPI_SEL_MASK) >>
+ SCLK_FSPI_SEL_SHIFT;
+ if (sel == SCLK_FSPI_SEL_GPLL)
+ prate = priv->gpll_hz;
+ else if (sel == SCLK_FSPI_SEL_CPLL)
+ prate = priv->cpll_hz;
+ else
+ prate = OSC_HZ;
+ return DIV_TO_RATE(prate, div);
+ case DCLK_DECOM:
+ con = readl(&cru->clksel_con[72]);
+ div = (con & DCLK_DECOM_DIV_MASK) >> DCLK_DECOM_DIV_SHIFT;
+ sel = (con & DCLK_DECOM_SEL_MASK) >> DCLK_DECOM_SEL_SHIFT;
+ if (sel == DCLK_DECOM_SEL_SPLL)
+ prate = priv->spll_hz;
+ else
+ prate = priv->gpll_hz;
+ return DIV_TO_RATE(prate, div);
+
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_mmc_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk, div = 0;
+
+ switch (clk_id) {
+ case CCLK_SRC_SDIO:
+ case CCLK_SRC_SDMMC0:
+ case CCLK_SRC_EMMC:
+ case SCLK_FSPI_X2:
+ case SCLK_FSPI1_X2:
+ case HCLK_SDMMC0:
+ case HCLK_EMMC:
+ case HCLK_SDIO:
+ if (!(OSC_HZ % rate)) {
+ src_clk = SCLK_FSPI_SEL_OSC;
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ } else if (!(priv->cpll_hz % rate)) {
+ src_clk = SCLK_FSPI_SEL_CPLL;
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = SCLK_FSPI_SEL_GPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ break;
+ case BCLK_EMMC:
+ if (rate >= 198 * MHz)
+ src_clk = BCLK_EMMC_SEL_200M;
+ else if (rate >= 99 * MHz)
+ src_clk = BCLK_EMMC_SEL_100M;
+ else if (rate >= 50 * MHz)
+ src_clk = BCLK_EMMC_SEL_50M;
+ else
+ src_clk = BCLK_EMMC_SEL_OSC;
+ break;
+ case DCLK_DECOM:
+ if (!(priv->spll_hz % rate)) {
+ src_clk = DCLK_DECOM_SEL_SPLL;
+ div = DIV_ROUND_UP(priv->spll_hz, rate);
+ } else {
+ src_clk = DCLK_DECOM_SEL_GPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ switch (clk_id) {
+ case CCLK_SRC_SDIO:
+ case HCLK_SDIO:
+ rk_clrsetreg(&cru->clksel_con[104],
+ CCLK_SDIO_SRC_SEL_MASK |
+ CCLK_SDIO_SRC_DIV_MASK,
+ (src_clk << CCLK_SDIO_SRC_SEL_SHIFT) |
+ (div - 1) << CCLK_SDIO_SRC_DIV_SHIFT);
+ break;
+ case CCLK_SRC_SDMMC0:
+ case HCLK_SDMMC0:
+ rk_clrsetreg(&cru->clksel_con[105],
+ CCLK_SDMMC0_SRC_SEL_MASK |
+ CCLK_SDMMC0_SRC_DIV_MASK,
+ (src_clk << CCLK_SDMMC0_SRC_SEL_SHIFT) |
+ (div - 1) << CCLK_SDMMC0_SRC_DIV_SHIFT);
+ break;
+ case CCLK_SRC_EMMC:
+ case HCLK_EMMC:
+ rk_clrsetreg(&cru->clksel_con[89],
+ CCLK_EMMC_DIV_MASK |
+ CCLK_EMMC_SEL_MASK,
+ (src_clk << CCLK_EMMC_SEL_SHIFT) |
+ (div - 1) << CCLK_EMMC_DIV_SHIFT);
+ break;
+ case SCLK_FSPI_X2:
+ rk_clrsetreg(&cru->clksel_con[89],
+ SCLK_FSPI_DIV_MASK |
+ SCLK_FSPI_SEL_MASK,
+ (src_clk << SCLK_FSPI_SEL_SHIFT) |
+ (div - 1) << SCLK_FSPI_DIV_SHIFT);
+ break;
+ case SCLK_FSPI1_X2:
+ rk_clrsetreg(&cru->clksel_con[106],
+ SCLK_FSPI_DIV_MASK |
+ SCLK_FSPI_SEL_MASK,
+ (src_clk << SCLK_FSPI_SEL_SHIFT) |
+ (div - 1) << SCLK_FSPI_DIV_SHIFT);
+ break;
+ case BCLK_EMMC:
+ rk_clrsetreg(&cru->clksel_con[90],
+ BCLK_EMMC_SEL_MASK,
+ src_clk << BCLK_EMMC_SEL_SHIFT);
+ break;
+ case DCLK_DECOM:
+ rk_clrsetreg(&cru->clksel_con[72],
+ DCLK_DECOM_DIV_MASK |
+ DCLK_DECOM_SEL_MASK,
+ (src_clk << DCLK_DECOM_SEL_SHIFT) |
+ (div - 1) << DCLK_DECOM_DIV_SHIFT);
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ return rk3576_mmc_get_clk(priv, clk_id);
+}
+
+#ifndef CONFIG_SPL_BUILD
+
+static ulong rk3576_aclk_vop_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 div, sel, con, parent = 0;
+
+ switch (clk_id) {
+ case ACLK_VOP_ROOT:
+ case ACLK_VOP:
+ con = readl(&cru->clksel_con[144]);
+ div = (con & ACLK_VOP_ROOT_DIV_MASK) >> ACLK_VOP_ROOT_DIV_SHIFT;
+ sel = (con & ACLK_VOP_ROOT_SEL_MASK) >> ACLK_VOP_ROOT_SEL_SHIFT;
+ if (sel == ACLK_VOP_ROOT_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else if (sel == ACLK_VOP_ROOT_SEL_CPLL)
+ parent = priv->cpll_hz;
+ else if (sel == ACLK_VOP_ROOT_SEL_AUPLL)
+ parent = priv->aupll_hz;
+ else if (sel == ACLK_VOP_ROOT_SEL_SPLL)
+ parent = priv->spll_hz;
+ else if (sel == ACLK_VOP_ROOT_SEL_LPLL)
+ parent = priv->lpll_hz / 2;
+ return DIV_TO_RATE(parent, div);
+ case ACLK_VO0_ROOT:
+ con = readl(&cru->clksel_con[149]);
+ div = (con & ACLK_VO0_ROOT_DIV_MASK) >> ACLK_VO0_ROOT_DIV_SHIFT;
+ sel = (con & ACLK_VO0_ROOT_SEL_MASK) >> ACLK_VO0_ROOT_SEL_SHIFT;
+ if (sel == ACLK_VO0_ROOT_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else if (sel == ACLK_VO0_ROOT_SEL_CPLL)
+ parent = priv->cpll_hz;
+ else if (sel == ACLK_VO0_ROOT_SEL_LPLL)
+ parent = priv->lpll_hz / 2;
+ else if (sel == ACLK_VO0_ROOT_SEL_BPLL)
+ parent = priv->bpll_hz / 4;
+ return DIV_TO_RATE(parent, div);
+ case ACLK_VO1_ROOT:
+ con = readl(&cru->clksel_con[158]);
+ div = (con & ACLK_VO0_ROOT_DIV_MASK) >> ACLK_VO0_ROOT_DIV_SHIFT;
+ sel = (con & ACLK_VO0_ROOT_SEL_MASK) >> ACLK_VO0_ROOT_SEL_SHIFT;
+ if (sel == ACLK_VO0_ROOT_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else if (sel == ACLK_VO0_ROOT_SEL_CPLL)
+ parent = priv->cpll_hz;
+ else if (sel == ACLK_VO0_ROOT_SEL_LPLL)
+ parent = priv->lpll_hz / 2;
+ else if (sel == ACLK_VO0_ROOT_SEL_BPLL)
+ parent = priv->bpll_hz / 4;
+ return DIV_TO_RATE(parent, div);
+ case HCLK_VOP_ROOT:
+ con = readl(&cru->clksel_con[144]);
+ sel = (con & HCLK_VOP_ROOT_SEL_MASK) >> HCLK_VOP_ROOT_SEL_SHIFT;
+ if (sel == HCLK_VOP_ROOT_SEL_200M)
+ return 200 * MHz;
+ else if (sel == HCLK_VOP_ROOT_SEL_100M)
+ return 100 * MHz;
+ else if (sel == HCLK_VOP_ROOT_SEL_50M)
+ return 50 * MHz;
+ else
+ return OSC_HZ;
+ case PCLK_VOP_ROOT:
+ con = readl(&cru->clksel_con[144]);
+ sel = (con & PCLK_VOP_ROOT_SEL_MASK) >> PCLK_VOP_ROOT_SEL_SHIFT;
+ if (sel == PCLK_VOP_ROOT_SEL_100M)
+ return 100 * MHz;
+ else if (sel == PCLK_VOP_ROOT_SEL_50M)
+ return 50 * MHz;
+ else
+ return OSC_HZ;
+
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_aclk_vop_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int src_clk, div;
+
+ switch (clk_id) {
+ case ACLK_VOP_ROOT:
+ case ACLK_VOP:
+ if (rate == 700 * MHz) {
+ src_clk = ACLK_VOP_ROOT_SEL_SPLL;
+ div = 1;
+ } else if (!(priv->cpll_hz % rate)) {
+ src_clk = ACLK_VOP_ROOT_SEL_CPLL;
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = ACLK_VOP_ROOT_SEL_GPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ rk_clrsetreg(&cru->clksel_con[144],
+ ACLK_VOP_ROOT_DIV_MASK |
+ ACLK_VOP_ROOT_SEL_MASK,
+ (src_clk << ACLK_VOP_ROOT_SEL_SHIFT) |
+ (div - 1) << ACLK_VOP_ROOT_DIV_SHIFT);
+ break;
+ case ACLK_VO0_ROOT:
+ if (!(priv->cpll_hz % rate)) {
+ src_clk = ACLK_VO0_ROOT_SEL_CPLL;
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = ACLK_VO0_ROOT_SEL_GPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ rk_clrsetreg(&cru->clksel_con[149],
+ ACLK_VO0_ROOT_DIV_MASK |
+ ACLK_VO0_ROOT_SEL_MASK,
+ (src_clk << ACLK_VO0_ROOT_SEL_SHIFT) |
+ (div - 1) << ACLK_VO0_ROOT_DIV_SHIFT);
+ break;
+ case ACLK_VO1_ROOT:
+ if (!(priv->cpll_hz % rate)) {
+ src_clk = ACLK_VO0_ROOT_SEL_CPLL;
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+ } else {
+ src_clk = ACLK_VO0_ROOT_SEL_GPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ }
+ rk_clrsetreg(&cru->clksel_con[158],
+ ACLK_VO0_ROOT_DIV_MASK |
+ ACLK_VO0_ROOT_SEL_MASK,
+ (src_clk << ACLK_VO0_ROOT_SEL_SHIFT) |
+ (div - 1) << ACLK_VO0_ROOT_DIV_SHIFT);
+ break;
+ case HCLK_VOP_ROOT:
+ if (rate == 200 * MHz)
+ src_clk = HCLK_VOP_ROOT_SEL_200M;
+ else if (rate == 100 * MHz)
+ src_clk = HCLK_VOP_ROOT_SEL_100M;
+ else if (rate == 50 * MHz)
+ src_clk = HCLK_VOP_ROOT_SEL_50M;
+ else
+ src_clk = HCLK_VOP_ROOT_SEL_OSC;
+ rk_clrsetreg(&cru->clksel_con[144],
+ HCLK_VOP_ROOT_SEL_MASK,
+ src_clk << HCLK_VOP_ROOT_SEL_SHIFT);
+ break;
+ case PCLK_VOP_ROOT:
+ if (rate == 100 * MHz)
+ src_clk = PCLK_VOP_ROOT_SEL_100M;
+ else if (rate == 50 * MHz)
+ src_clk = PCLK_VOP_ROOT_SEL_50M;
+ else
+ src_clk = PCLK_VOP_ROOT_SEL_OSC;
+ rk_clrsetreg(&cru->clksel_con[144],
+ PCLK_VOP_ROOT_SEL_MASK,
+ src_clk << PCLK_VOP_ROOT_SEL_SHIFT);
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ return rk3576_aclk_vop_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_dclk_vop_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 div, sel, con, parent;
+
+ switch (clk_id) {
+ case DCLK_VP0:
+ case DCLK_VP0_SRC:
+ con = readl(&cru->clksel_con[145]);
+ div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT;
+ sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+ break;
+ case DCLK_VP1:
+ case DCLK_VP1_SRC:
+ con = readl(&cru->clksel_con[146]);
+ div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT;
+ sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+ break;
+ case DCLK_VP2:
+ case DCLK_VP2_SRC:
+ con = readl(&cru->clksel_con[147]);
+ div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT;
+ sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (sel == DCLK_VOP_SRC_SEL_VPLL)
+ parent = priv->vpll_hz;
+ else if (sel == DCLK_VOP_SRC_SEL_BPLL)
+ parent = priv->bpll_hz / 4;
+ else if (sel == DCLK_VOP_SRC_SEL_LPLL)
+ parent = priv->lpll_hz / 2;
+ else if (sel == DCLK_VOP_SRC_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else
+ parent = priv->cpll_hz;
+
+ return DIV_TO_RATE(parent, div);
+}
+
+#define RK3576_VOP_PLL_LIMIT_FREQ 600000000
+
+static ulong rk3576_dclk_vop_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ ulong pll_rate, now, best_rate = 0;
+ u32 i, conid, con, sel, div, best_div = 0, best_sel = 0;
+ u32 mask, div_shift, sel_shift;
+
+ switch (clk_id) {
+ case DCLK_VP0:
+ case DCLK_VP0_SRC:
+ conid = 145;
+ con = readl(&cru->clksel_con[conid]);
+ sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+ mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK;
+ div_shift = DCLK0_VOP_SRC_DIV_SHIFT;
+ sel_shift = DCLK0_VOP_SRC_SEL_SHIFT;
+ break;
+ case DCLK_VP1:
+ case DCLK_VP1_SRC:
+ conid = 146;
+ con = readl(&cru->clksel_con[conid]);
+ sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+ mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK;
+ div_shift = DCLK0_VOP_SRC_DIV_SHIFT;
+ sel_shift = DCLK0_VOP_SRC_SEL_SHIFT;
+ break;
+ case DCLK_VP2:
+ case DCLK_VP2_SRC:
+ conid = 147;
+ con = readl(&cru->clksel_con[conid]);
+ sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+ mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK;
+ div_shift = DCLK0_VOP_SRC_DIV_SHIFT;
+ sel_shift = DCLK0_VOP_SRC_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (sel == DCLK_VOP_SRC_SEL_VPLL) {
+ pll_rate = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL],
+ priv->cru, VPLL);
+ if (pll_rate >= RK3576_VOP_PLL_LIMIT_FREQ && pll_rate % rate == 0) {
+ div = DIV_ROUND_UP(pll_rate, rate);
+ rk_clrsetreg(&cru->clksel_con[conid],
+ mask,
+ DCLK_VOP_SRC_SEL_VPLL << sel_shift |
+ ((div - 1) << div_shift));
+ } else {
+ div = DIV_ROUND_UP(RK3576_VOP_PLL_LIMIT_FREQ, rate);
+ if (div % 2)
+ div = div + 1;
+ rk_clrsetreg(&cru->clksel_con[conid],
+ mask,
+ DCLK_VOP_SRC_SEL_VPLL << sel_shift |
+ ((div - 1) << div_shift));
+ rockchip_pll_set_rate(&rk3576_pll_clks[VPLL],
+ priv->cru, VPLL, div * rate);
+ priv->vpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL],
+ priv->cru, VPLL);
+ }
+ } else {
+ for (i = 0; i <= DCLK_VOP_SRC_SEL_LPLL; i++) {
+ switch (i) {
+ case DCLK_VOP_SRC_SEL_GPLL:
+ pll_rate = priv->gpll_hz;
+ break;
+ case DCLK_VOP_SRC_SEL_CPLL:
+ pll_rate = priv->cpll_hz;
+ break;
+ case DCLK_VOP_SRC_SEL_BPLL:
+ pll_rate = 0;
+ break;
+ case DCLK_VOP_SRC_SEL_LPLL:
+ pll_rate = 0;
+ break;
+ case DCLK_VOP_SRC_SEL_VPLL:
+ pll_rate = 0;
+ break;
+ default:
+ printf("do not support this vop pll sel\n");
+ return -EINVAL;
+ }
+
+ div = DIV_ROUND_UP(pll_rate, rate);
+ if (div > 255)
+ continue;
+ now = pll_rate / div;
+ if (abs(rate - now) < abs(rate - best_rate)) {
+ best_rate = now;
+ best_div = div;
+ best_sel = i;
+ }
+ debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n",
+ pll_rate, best_rate, best_div, best_sel);
+ }
+
+ if (best_rate) {
+ rk_clrsetreg(&cru->clksel_con[conid],
+ mask,
+ best_sel << sel_shift |
+ (best_div - 1) << div_shift);
+ } else {
+ printf("do not support this vop freq %lu\n", rate);
+ return -EINVAL;
+ }
+ }
+
+ return rk3576_dclk_vop_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_clk_csihost_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 div, sel, con, parent;
+
+ switch (clk_id) {
+ case CLK_DSIHOST0:
+ con = readl(&cru->clksel_con[151]);
+ div = (con & CLK_DSIHOST0_DIV_MASK) >> CLK_DSIHOST0_DIV_SHIFT;
+ sel = (con & CLK_DSIHOST0_SEL_MASK) >> CLK_DSIHOST0_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (sel == CLK_DSIHOST0_SEL_VPLL)
+ parent = priv->vpll_hz;
+ else if (sel == CLK_DSIHOST0_SEL_BPLL)
+ parent = priv->bpll_hz / 4;
+ else if (sel == CLK_DSIHOST0_SEL_LPLL)
+ parent = priv->lpll_hz / 2;
+ else if (sel == CLK_DSIHOST0_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else if (sel == CLK_DSIHOST0_SEL_SPLL)
+ parent = priv->spll_hz;
+ else
+ parent = priv->cpll_hz;
+
+ return DIV_TO_RATE(parent, div);
+}
+
+static ulong rk3576_clk_csihost_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ ulong pll_rate, now, best_rate = 0;
+ u32 i, con, div, best_div = 0, best_sel = 0;
+ u32 mask, div_shift, sel_shift;
+
+ switch (clk_id) {
+ case CLK_DSIHOST0:
+ con = 151;
+ mask = CLK_DSIHOST0_SEL_MASK | CLK_DSIHOST0_DIV_MASK;
+ div_shift = CLK_DSIHOST0_DIV_SHIFT;
+ sel_shift = CLK_DSIHOST0_SEL_SHIFT;
+ break;
+ default:
+ return -ENOENT;
+ }
+ for (i = 0; i <= CLK_DSIHOST0_SEL_LPLL; i++) {
+ switch (i) {
+ case CLK_DSIHOST0_SEL_GPLL:
+ pll_rate = priv->gpll_hz;
+ break;
+ case CLK_DSIHOST0_SEL_CPLL:
+ pll_rate = priv->cpll_hz;
+ break;
+ case CLK_DSIHOST0_SEL_BPLL:
+ pll_rate = 0;
+ break;
+ case CLK_DSIHOST0_SEL_LPLL:
+ pll_rate = 0;
+ break;
+ case CLK_DSIHOST0_SEL_VPLL:
+ pll_rate = 0;
+ break;
+ case CLK_DSIHOST0_SEL_SPLL:
+ pll_rate = priv->spll_hz;
+ break;
+ default:
+ printf("do not support this vop pll sel\n");
+ return -EINVAL;
+ }
+
+ div = DIV_ROUND_UP(pll_rate, rate);
+ if (div > 255)
+ continue;
+ now = pll_rate / div;
+ if (abs(rate - now) < abs(rate - best_rate)) {
+ best_rate = now;
+ best_div = div;
+ best_sel = i;
+ }
+ debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n",
+ pll_rate, best_rate, best_div, best_sel);
+ }
+
+ if (best_rate) {
+ rk_clrsetreg(&cru->clksel_con[con],
+ mask,
+ best_sel << sel_shift |
+ (best_div - 1) << div_shift);
+ } else {
+ printf("do not support this vop freq %lu\n", rate);
+ return -EINVAL;
+ }
+
+ return rk3576_clk_csihost_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_dclk_ebc_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 div, sel, con, parent;
+ unsigned long m = 0, n = 0;
+
+ switch (clk_id) {
+ case DCLK_EBC:
+ con = readl(&cru->clksel_con[123]);
+ div = (con & DCLK_EBC_DIV_MASK) >> DCLK_EBC_DIV_SHIFT;
+ sel = (con & DCLK_EBC_SEL_MASK) >> DCLK_EBC_SEL_SHIFT;
+ if (sel == DCLK_EBC_SEL_CPLL)
+ parent = priv->cpll_hz;
+ else if (sel == DCLK_EBC_SEL_VPLL)
+ parent = priv->vpll_hz;
+ else if (sel == DCLK_EBC_SEL_AUPLL)
+ parent = priv->aupll_hz;
+ else if (sel == DCLK_EBC_SEL_LPLL)
+ parent = priv->lpll_hz / 2;
+ else if (sel == DCLK_EBC_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else if (sel == DCLK_EBC_SEL_FRAC_SRC)
+ parent = rk3576_dclk_ebc_get_clk(priv, DCLK_EBC_FRAC_SRC);
+ else
+ parent = OSC_HZ;
+ return DIV_TO_RATE(parent, div);
+ case DCLK_EBC_FRAC_SRC:
+ con = readl(&cru->clksel_con[123]);
+ div = readl(&cru->clksel_con[122]);
+ sel = (con & DCLK_EBC_FRAC_SRC_SEL_MASK) >> DCLK_EBC_FRAC_SRC_SEL_SHIFT;
+ if (sel == DCLK_EBC_FRAC_SRC_SEL_GPLL)
+ parent = priv->gpll_hz;
+ else if (sel == DCLK_EBC_FRAC_SRC_SEL_CPLL)
+ parent = priv->cpll_hz;
+ else if (sel == DCLK_EBC_FRAC_SRC_SEL_VPLL)
+ parent = priv->vpll_hz;
+ else if (sel == DCLK_EBC_FRAC_SRC_SEL_AUPLL)
+ parent = priv->aupll_hz;
+ else
+ parent = OSC_HZ;
+
+ n = div & CLK_UART_FRAC_NUMERATOR_MASK;
+ n >>= CLK_UART_FRAC_NUMERATOR_SHIFT;
+ m = div & CLK_UART_FRAC_DENOMINATOR_MASK;
+ m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT;
+ return parent * n / m;
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_dclk_ebc_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ ulong pll_rate, now, best_rate = 0;
+ u32 i, con, sel, div, best_div = 0, best_sel = 0;
+ unsigned long m = 0, n = 0, val;
+
+ switch (clk_id) {
+ case DCLK_EBC:
+ con = readl(&cru->clksel_con[123]);
+ sel = (con & DCLK_EBC_SEL_MASK) >> DCLK_EBC_SEL_SHIFT;
+ if (sel == DCLK_EBC_SEL_VPLL) {
+ pll_rate = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL],
+ priv->cru, VPLL);
+ if (pll_rate >= RK3576_VOP_PLL_LIMIT_FREQ &&
+ pll_rate % rate == 0) {
+ div = DIV_ROUND_UP(pll_rate, rate);
+ rk_clrsetreg(&cru->clksel_con[123],
+ DCLK_EBC_DIV_MASK,
+ (div - 1) << DCLK_EBC_DIV_SHIFT);
+ } else {
+ div = DIV_ROUND_UP(RK3576_VOP_PLL_LIMIT_FREQ,
+ rate);
+ if (div % 2)
+ div = div + 1;
+ rk_clrsetreg(&cru->clksel_con[123],
+ DCLK_EBC_DIV_MASK,
+ (div - 1) << DCLK_EBC_DIV_SHIFT);
+ rockchip_pll_set_rate(&rk3576_pll_clks[VPLL],
+ priv->cru,
+ VPLL, div * rate);
+ priv->vpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL],
+ priv->cru,
+ VPLL);
+ }
+ } else if (sel == DCLK_EBC_SEL_FRAC_SRC) {
+ rk3576_dclk_ebc_set_clk(priv, DCLK_EBC_FRAC_SRC, rate);
+ div = rk3576_dclk_ebc_get_clk(priv, DCLK_EBC_FRAC_SRC) / rate;
+ rk_clrsetreg(&cru->clksel_con[123],
+ DCLK_EBC_DIV_MASK,
+ (div - 1) << DCLK_EBC_DIV_SHIFT);
+ } else {
+ for (i = 0; i <= DCLK_EBC_SEL_LPLL; i++) {
+ switch (i) {
+ case DCLK_EBC_SEL_GPLL:
+ pll_rate = priv->gpll_hz;
+ break;
+ case DCLK_EBC_SEL_CPLL:
+ pll_rate = priv->cpll_hz;
+ break;
+ case DCLK_EBC_SEL_VPLL:
+ pll_rate = 0;
+ break;
+ case DCLK_EBC_SEL_AUPLL:
+ pll_rate = priv->aupll_hz;
+ break;
+ case DCLK_EBC_SEL_LPLL:
+ pll_rate = 0;
+ break;
+ default:
+ printf("not support ebc pll sel\n");
+ return -EINVAL;
+ }
+
+ div = DIV_ROUND_UP(pll_rate, rate);
+ if (div > 255)
+ continue;
+ now = pll_rate / div;
+ if (abs(rate - now) < abs(rate - best_rate)) {
+ best_rate = now;
+ best_div = div;
+ best_sel = i;
+ }
+ }
+
+ if (best_rate) {
+ rk_clrsetreg(&cru->clksel_con[123],
+ DCLK_EBC_DIV_MASK |
+ DCLK_EBC_SEL_MASK,
+ best_sel <<
+ DCLK_EBC_SEL_SHIFT |
+ (best_div - 1) <<
+ DCLK_EBC_DIV_SHIFT);
+ } else {
+ printf("do not support this vop freq %lu\n",
+ rate);
+ return -EINVAL;
+ }
+ }
+ break;
+ case DCLK_EBC_FRAC_SRC:
+ sel = DCLK_EBC_FRAC_SRC_SEL_GPLL;
+ div = 1;
+ rational_best_approximation(rate, priv->gpll_hz,
+ GENMASK(16 - 1, 0),
+ GENMASK(16 - 1, 0),
+ &m, &n);
+
+ if (m < 4 && m != 0) {
+ if (n % 2 == 0)
+ val = 1;
+ else
+ val = DIV_ROUND_UP(4, m);
+
+ n *= val;
+ m *= val;
+ if (n > 0xffff)
+ n = 0xffff;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[123],
+ DCLK_EBC_FRAC_SRC_SEL_MASK,
+ (sel << DCLK_EBC_FRAC_SRC_SEL_SHIFT));
+ if (m && n) {
+ val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n;
+ writel(val, &cru->clksel_con[122]);
+ }
+ break;
+ default:
+ return -ENOENT;
+ }
+ return rk3576_dclk_ebc_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_gmac_get_clk(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 con, div, src, p_rate;
+
+ switch (clk_id) {
+ case CLK_GMAC0_PTP_REF_SRC:
+ case CLK_GMAC0_PTP_REF:
+ con = readl(&cru->clksel_con[105]);
+ div = (con & CLK_GMAC0_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT;
+ src = (con & CLK_GMAC0_PTP_SEL_MASK) >> CLK_GMAC0_PTP_SEL_SHIFT;
+ if (src == CLK_GMAC0_PTP_SEL_GPLL)
+ p_rate = priv->gpll_hz;
+ else if (src == CLK_GMAC0_PTP_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else
+ p_rate = GMAC0_PTP_REFCLK_IN;
+ return DIV_TO_RATE(p_rate, div);
+ case CLK_GMAC1_PTP_REF_SRC:
+ case CLK_GMAC1_PTP_REF:
+ con = readl(&cru->clksel_con[104]);
+ div = (con & CLK_GMAC1_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT;
+ src = (con & CLK_GMAC1_PTP_SEL_MASK) >> CLK_GMAC1_PTP_SEL_SHIFT;
+ if (src == CLK_GMAC1_PTP_SEL_GPLL)
+ p_rate = priv->gpll_hz;
+ else if (src == CLK_GMAC1_PTP_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else
+ p_rate = GMAC1_PTP_REFCLK_IN;
+ return DIV_TO_RATE(p_rate, div);
+ case CLK_GMAC0_125M_SRC:
+ con = readl(&cru->clksel_con[30]);
+ div = (con & CLK_GMAC0_125M_DIV_MASK) >> CLK_GMAC0_125M_DIV_SHIFT;
+ return DIV_TO_RATE(priv->cpll_hz, div);
+ case CLK_GMAC1_125M_SRC:
+ con = readl(&cru->clksel_con[31]);
+ div = (con & CLK_GMAC1_125M_DIV_MASK) >> CLK_GMAC1_125M_DIV_SHIFT;
+ return DIV_TO_RATE(priv->cpll_hz, div);
+ default:
+ return -ENOENT;
+ }
+}
+
+static ulong rk3576_gmac_set_clk(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ int div, src;
+
+ div = DIV_ROUND_UP(priv->cpll_hz, rate);
+
+ switch (clk_id) {
+ case CLK_GMAC0_PTP_REF_SRC:
+ case CLK_GMAC0_PTP_REF:
+ if (rate == GMAC0_PTP_REFCLK_IN) {
+ src = CLK_GMAC0_PTP_SEL_REFIN;
+ div = 1;
+ } else if (!(priv->gpll_hz % rate)) {
+ src = CLK_GMAC0_PTP_SEL_GPLL;
+ div = priv->gpll_hz / rate;
+ } else {
+ src = CLK_GMAC0_PTP_SEL_CPLL;
+ div = priv->cpll_hz / rate;
+ }
+ rk_clrsetreg(&cru->clksel_con[105],
+ CLK_GMAC0_PTP_DIV_MASK | CLK_GMAC0_PTP_SEL_MASK,
+ src << CLK_GMAC0_PTP_SEL_SHIFT |
+ (div - 1) << CLK_GMAC0_PTP_DIV_SHIFT);
+ break;
+ case CLK_GMAC1_PTP_REF_SRC:
+ case CLK_GMAC1_PTP_REF:
+ if (rate == GMAC1_PTP_REFCLK_IN) {
+ src = CLK_GMAC1_PTP_SEL_REFIN;
+ div = 1;
+ } else if (!(priv->gpll_hz % rate)) {
+ src = CLK_GMAC1_PTP_SEL_GPLL;
+ div = priv->gpll_hz / rate;
+ } else {
+ src = CLK_GMAC1_PTP_SEL_CPLL;
+ div = priv->cpll_hz / rate;
+ }
+ rk_clrsetreg(&cru->clksel_con[104],
+ CLK_GMAC1_PTP_DIV_MASK | CLK_GMAC1_PTP_SEL_MASK,
+ src << CLK_GMAC1_PTP_SEL_SHIFT |
+ (div - 1) << CLK_GMAC1_PTP_DIV_SHIFT);
+ break;
+
+ case CLK_GMAC0_125M_SRC:
+ rk_clrsetreg(&cru->clksel_con[30],
+ CLK_GMAC0_125M_DIV_MASK,
+ (div - 1) << CLK_GMAC0_125M_DIV_SHIFT);
+ break;
+ case CLK_GMAC1_125M_SRC:
+ rk_clrsetreg(&cru->clksel_con[31],
+ CLK_GMAC1_125M_DIV_MASK,
+ (div - 1) << CLK_GMAC1_125M_DIV_SHIFT);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rk3576_gmac_get_clk(priv, clk_id);
+}
+
+static ulong rk3576_uart_frac_get_rate(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 reg, con, fracdiv, p_src, p_rate;
+ unsigned long m, n;
+
+ switch (clk_id) {
+ case CLK_UART_FRAC_0:
+ reg = 21;
+ break;
+ case CLK_UART_FRAC_1:
+ reg = 23;
+ break;
+ case CLK_UART_FRAC_2:
+ reg = 25;
+ break;
+ default:
+ return -ENOENT;
+ }
+ con = readl(&cru->clksel_con[reg + 1]);
+ p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT;
+ if (p_src == CLK_UART_SRC_SEL_GPLL)
+ p_rate = priv->gpll_hz;
+ else if (p_src == CLK_UART_SRC_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else if (p_src == CLK_UART_SRC_SEL_AUPLL)
+ p_rate = priv->aupll_hz;
+ else
+ p_rate = OSC_HZ;
+
+ fracdiv = readl(&cru->clksel_con[reg]);
+ n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK;
+ n >>= CLK_UART_FRAC_NUMERATOR_SHIFT;
+ m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK;
+ m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT;
+ return p_rate * n / m;
+}
+
+static ulong rk3576_uart_frac_set_rate(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 reg, clk_src, p_rate;
+ unsigned long m = 0, n = 0, val;
+
+ if (priv->cpll_hz % rate == 0) {
+ clk_src = CLK_UART_SRC_SEL_CPLL;
+ p_rate = priv->cpll_hz;
+ } else if (rate == OSC_HZ) {
+ clk_src = CLK_UART_SRC_SEL_OSC;
+ p_rate = OSC_HZ;
+ } else {
+ clk_src = CLK_UART_SRC_SEL_GPLL;
+ p_rate = priv->cpll_hz;
+ }
+
+ rational_best_approximation(rate, p_rate, GENMASK(16 - 1, 0),
+ GENMASK(16 - 1, 0), &m, &n);
+
+ if (m < 4 && m != 0) {
+ if (n % 2 == 0)
+ val = 1;
+ else
+ val = DIV_ROUND_UP(4, m);
+
+ n *= val;
+ m *= val;
+ if (n > 0xffff)
+ n = 0xffff;
+ }
+
+ switch (clk_id) {
+ case CLK_UART_FRAC_0:
+ reg = 21;
+ break;
+ case CLK_UART_FRAC_1:
+ reg = 23;
+ break;
+ case CLK_UART_FRAC_2:
+ reg = 25;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[reg + 1],
+ CLK_UART_SRC_SEL_MASK,
+ (clk_src << CLK_UART_SRC_SEL_SHIFT));
+ if (m && n) {
+ val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n;
+ writel(val, &cru->clksel_con[reg]);
+ }
+
+ return rk3576_uart_frac_get_rate(priv, clk_id);
+}
+
+static ulong rk3576_uart_get_rate(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 con, div, src, p_rate;
+
+ switch (clk_id) {
+ case SCLK_UART0:
+ con = readl(&cru->clksel_con[60]);
+ break;
+ case SCLK_UART1:
+ con = readl(&cru->pmuclksel_con[8]);
+ src = (con & CLK_UART1_SEL_MASK) >> CLK_UART1_SEL_SHIFT;
+ if (src == CLK_UART1_SEL_OSC)
+ return OSC_HZ;
+ con = readl(&cru->clksel_con[27]);
+ break;
+ case SCLK_UART2:
+ con = readl(&cru->clksel_con[61]);
+ break;
+ case SCLK_UART3:
+ con = readl(&cru->clksel_con[62]);
+ break;
+ case SCLK_UART4:
+ con = readl(&cru->clksel_con[63]);
+ break;
+ case SCLK_UART5:
+ con = readl(&cru->clksel_con[64]);
+ break;
+ case SCLK_UART6:
+ con = readl(&cru->clksel_con[65]);
+ break;
+ case SCLK_UART7:
+ con = readl(&cru->clksel_con[66]);
+ break;
+ case SCLK_UART8:
+ con = readl(&cru->clksel_con[67]);
+ break;
+ case SCLK_UART9:
+ con = readl(&cru->clksel_con[68]);
+ break;
+ case SCLK_UART10:
+ con = readl(&cru->clksel_con[69]);
+ break;
+ case SCLK_UART11:
+ con = readl(&cru->clksel_con[70]);
+ break;
+ default:
+ return -ENOENT;
+ }
+ if (clk_id == SCLK_UART1) {
+ src = (con & CLK_UART1_SRC_SEL_SHIFT) >> CLK_UART1_SRC_SEL_SHIFT;
+ div = (con & CLK_UART1_SRC_DIV_MASK) >> CLK_UART1_SRC_DIV_SHIFT;
+ } else {
+ src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT;
+ div = (con & CLK_UART_DIV_MASK) >> CLK_UART_DIV_SHIFT;
+ }
+ if (src == CLK_UART_SEL_GPLL)
+ p_rate = priv->gpll_hz;
+ else if (src == CLK_UART_SEL_CPLL)
+ p_rate = priv->cpll_hz;
+ else if (src == CLK_UART_SEL_AUPLL)
+ p_rate = priv->aupll_hz;
+ else if (src == CLK_UART_SEL_FRAC0)
+ p_rate = rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_0);
+ else if (src == CLK_UART_SEL_FRAC1)
+ p_rate = rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_1);
+ else if (src == CLK_UART_SEL_FRAC2)
+ p_rate = rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_2);
+ else
+ p_rate = OSC_HZ;
+
+ return DIV_TO_RATE(p_rate, div);
+}
+
+static ulong rk3576_uart_set_rate(struct rk3576_clk_priv *priv,
+ ulong clk_id, ulong rate)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 reg, clk_src = 0, div = 0;
+
+ if (!(priv->gpll_hz % rate)) {
+ clk_src = CLK_UART_SEL_GPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ } else if (!(priv->cpll_hz % rate)) {
+ clk_src = CLK_UART_SEL_CPLL;
+ div = DIV_ROUND_UP(priv->gpll_hz, rate);
+ } else if (!(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_0) % rate)) {
+ clk_src = CLK_UART_SEL_FRAC0;
+ div = DIV_ROUND_UP(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_0), rate);
+ } else if (!(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_1) % rate)) {
+ clk_src = CLK_UART_SEL_FRAC1;
+ div = DIV_ROUND_UP(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_1), rate);
+ } else if (!(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_2) % rate)) {
+ clk_src = CLK_UART_SEL_FRAC2;
+ div = DIV_ROUND_UP(rk3576_uart_frac_get_rate(priv, CLK_UART_FRAC_2), rate);
+ } else if (!(OSC_HZ % rate)) {
+ clk_src = CLK_UART_SEL_OSC;
+ div = DIV_ROUND_UP(OSC_HZ, rate);
+ }
+
+ switch (clk_id) {
+ case SCLK_UART0:
+ reg = 60;
+ break;
+ case SCLK_UART1:
+ if (rate == OSC_HZ) {
+ rk_clrsetreg(&cru->pmuclksel_con[8],
+ CLK_UART1_SEL_MASK,
+ CLK_UART1_SEL_OSC << CLK_UART1_SEL_SHIFT);
+ return 0;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[27],
+ CLK_UART1_SRC_SEL_MASK | CLK_UART1_SRC_DIV_MASK,
+ (clk_src << CLK_UART1_SRC_SEL_SHIFT) |
+ ((div - 1) << CLK_UART1_SRC_DIV_SHIFT));
+ rk_clrsetreg(&cru->pmuclksel_con[8],
+ CLK_UART1_SEL_MASK,
+ CLK_UART1_SEL_TOP << CLK_UART1_SEL_SHIFT);
+ return 0;
+ case SCLK_UART2:
+ reg = 61;
+ break;
+ case SCLK_UART3:
+ reg = 62;
+ break;
+ case SCLK_UART4:
+ reg = 63;
+ break;
+ case SCLK_UART5:
+ reg = 64;
+ break;
+ case SCLK_UART6:
+ reg = 65;
+ break;
+ case SCLK_UART7:
+ reg = 66;
+ break;
+ case SCLK_UART8:
+ reg = 67;
+ break;
+ case SCLK_UART9:
+ reg = 68;
+ break;
+ case SCLK_UART10:
+ reg = 69;
+ break;
+ case SCLK_UART11:
+ reg = 70;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ rk_clrsetreg(&cru->clksel_con[reg],
+ CLK_UART_SEL_MASK |
+ CLK_UART_DIV_MASK,
+ (clk_src << CLK_UART_SEL_SHIFT) |
+ ((div - 1) << CLK_UART_DIV_SHIFT));
+
+ return rk3576_uart_get_rate(priv, clk_id);
+}
+#endif
+
+static ulong rk3576_ufs_ref_get_rate(struct rk3576_clk_priv *priv, ulong clk_id)
+{
+ struct rk3576_cru *cru = priv->cru;
+ u32 src, div;
+
+ src = readl(&cru->pmuclksel_con[3]) & 0x3;
+ div = readl(&cru->pmuclksel_con[1]) & 0xff;
+ if (src == 0)
+ return OSC_HZ;
+ else if (src == 2)
+ return priv->ppll_hz / (div + 1);
+ else
+ return 26000000;
+}
+
+static ulong rk3576_clk_get_rate(struct clk *clk)
+{
+ struct rk3576_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong rate = 0;
+
+ if (!priv->gpll_hz) {
+ printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
+ return -ENOENT;
+ }
+
+ if (!priv->ppll_hz) {
+ priv->ppll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL],
+ priv->cru, PPLL);
+ }
+
+ switch (clk->id) {
+ case PLL_LPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[LPLL], priv->cru,
+ LPLL);
+ priv->lpll_hz = rate;
+ break;
+ case PLL_BPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[BPLL], priv->cru,
+ BPLL);
+ priv->bpll_hz = rate;
+ break;
+ case PLL_GPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[GPLL], priv->cru,
+ GPLL);
+ break;
+ case PLL_CPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[CPLL], priv->cru,
+ CPLL);
+ break;
+ case PLL_VPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL], priv->cru,
+ VPLL);
+ break;
+ case PLL_AUPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[AUPLL], priv->cru,
+ AUPLL);
+ break;
+ case PLL_PPLL:
+ rate = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL], priv->cru,
+ PPLL) * 2;
+ break;
+ case ACLK_BUS_ROOT:
+ case HCLK_BUS_ROOT:
+ case PCLK_BUS_ROOT:
+ rate = rk3576_bus_get_clk(priv, clk->id);
+ break;
+ case ACLK_TOP:
+ case HCLK_TOP:
+ case PCLK_TOP_ROOT:
+ case ACLK_TOP_MID:
+ rate = rk3576_top_get_clk(priv, clk->id);
+ break;
+ case CLK_I2C0:
+ case CLK_I2C1:
+ case CLK_I2C2:
+ case CLK_I2C3:
+ case CLK_I2C4:
+ case CLK_I2C5:
+ case CLK_I2C6:
+ case CLK_I2C7:
+ case CLK_I2C8:
+ case CLK_I2C9:
+ rate = rk3576_i2c_get_clk(priv, clk->id);
+ break;
+ case CLK_SPI0:
+ case CLK_SPI1:
+ case CLK_SPI2:
+ case CLK_SPI3:
+ case CLK_SPI4:
+ rate = rk3576_spi_get_clk(priv, clk->id);
+ break;
+ case CLK_PWM1:
+ case CLK_PWM2:
+ case CLK_PMU1PWM:
+ rate = rk3576_pwm_get_clk(priv, clk->id);
+ break;
+ case CLK_SARADC:
+ case CLK_TSADC:
+ rate = rk3576_adc_get_clk(priv, clk->id);
+ break;
+ case CCLK_SRC_SDIO:
+ case CCLK_SRC_SDMMC0:
+ case CCLK_SRC_EMMC:
+ case BCLK_EMMC:
+ case SCLK_FSPI_X2:
+ case SCLK_FSPI1_X2:
+ case DCLK_DECOM:
+ case HCLK_SDMMC0:
+ case HCLK_EMMC:
+ case HCLK_SDIO:
+ rate = rk3576_mmc_get_clk(priv, clk->id);
+ break;
+ case TCLK_EMMC:
+ case TCLK_WDT0:
+ rate = OSC_HZ;
+ break;
+#ifndef CONFIG_SPL_BUILD
+ case ACLK_VOP_ROOT:
+ case ACLK_VOP:
+ case ACLK_VO0_ROOT:
+ case ACLK_VO1_ROOT:
+ case HCLK_VOP_ROOT:
+ case PCLK_VOP_ROOT:
+ rate = rk3576_aclk_vop_get_clk(priv, clk->id);
+ break;
+ case DCLK_VP0:
+ case DCLK_VP0_SRC:
+ case DCLK_VP1:
+ case DCLK_VP1_SRC:
+ case DCLK_VP2:
+ case DCLK_VP2_SRC:
+ rate = rk3576_dclk_vop_get_clk(priv, clk->id);
+ break;
+ case CLK_GMAC0_PTP_REF_SRC:
+ case CLK_GMAC1_PTP_REF_SRC:
+ case CLK_GMAC0_PTP_REF:
+ case CLK_GMAC1_PTP_REF:
+ case CLK_GMAC0_125M_SRC:
+ case CLK_GMAC1_125M_SRC:
+ rate = rk3576_gmac_get_clk(priv, clk->id);
+ break;
+ case CLK_UART_FRAC_0:
+ case CLK_UART_FRAC_1:
+ case CLK_UART_FRAC_2:
+ rate = rk3576_uart_frac_get_rate(priv, clk->id);
+ break;
+ case SCLK_UART0:
+ case SCLK_UART1:
+ case SCLK_UART2:
+ case SCLK_UART3:
+ case SCLK_UART4:
+ case SCLK_UART5:
+ case SCLK_UART6:
+ case SCLK_UART7:
+ case SCLK_UART8:
+ case SCLK_UART9:
+ case SCLK_UART10:
+ case SCLK_UART11:
+ rate = rk3576_uart_get_rate(priv, clk->id);
+ break;
+ case CLK_DSIHOST0:
+ rate = rk3576_clk_csihost_get_clk(priv, clk->id);
+ break;
+ case DCLK_EBC:
+ case DCLK_EBC_FRAC_SRC:
+ rate = rk3576_dclk_ebc_get_clk(priv, clk->id);
+ break;
+#endif
+ case CLK_REF_UFS_CLKOUT:
+ case CLK_REF_OSC_MPHY:
+ rate = rk3576_ufs_ref_get_rate(priv, clk->id);
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ return rate;
+};
+
+static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct rk3576_clk_priv *priv = dev_get_priv(clk->dev);
+ ulong ret = 0;
+
+ if (!priv->ppll_hz) {
+ priv->ppll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL],
+ priv->cru, PPLL);
+ }
+ if (!priv->aupll_hz) {
+ priv->aupll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[AUPLL],
+ priv->cru, AUPLL);
+ }
+
+ switch (clk->id) {
+ case PLL_CPLL:
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[CPLL], priv->cru,
+ CPLL, rate);
+ priv->cpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[CPLL],
+ priv->cru, CPLL);
+ break;
+ case PLL_GPLL:
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[GPLL], priv->cru,
+ GPLL, rate);
+ priv->gpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[GPLL],
+ priv->cru, GPLL);
+ break;
+ case PLL_VPLL:
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[VPLL], priv->cru,
+ VPLL, rate);
+ priv->vpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[VPLL],
+ priv->cru, VPLL);
+ break;
+ case PLL_AUPLL:
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[AUPLL], priv->cru,
+ AUPLL, rate);
+ priv->aupll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[AUPLL],
+ priv->cru, AUPLL);
+ break;
+ case PLL_PPLL:
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[PPLL], priv->cru,
+ PPLL, rate);
+ priv->ppll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[PPLL],
+ priv->cru, PPLL) * 2;
+ break;
+ case ACLK_BUS_ROOT:
+ case HCLK_BUS_ROOT:
+ case PCLK_BUS_ROOT:
+ ret = rk3576_bus_set_clk(priv, clk->id, rate);
+ break;
+ case ACLK_TOP:
+ case HCLK_TOP:
+ case PCLK_TOP_ROOT:
+ case ACLK_TOP_MID:
+ ret = rk3576_top_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_I2C0:
+ case CLK_I2C1:
+ case CLK_I2C2:
+ case CLK_I2C3:
+ case CLK_I2C4:
+ case CLK_I2C5:
+ case CLK_I2C6:
+ case CLK_I2C7:
+ case CLK_I2C8:
+ case CLK_I2C9:
+ ret = rk3576_i2c_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_SPI0:
+ case CLK_SPI1:
+ case CLK_SPI2:
+ case CLK_SPI3:
+ case CLK_SPI4:
+ ret = rk3576_spi_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_PWM1:
+ case CLK_PWM2:
+ case CLK_PMU1PWM:
+ ret = rk3576_pwm_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_SARADC:
+ case CLK_TSADC:
+ ret = rk3576_adc_set_clk(priv, clk->id, rate);
+ break;
+ case CCLK_SRC_SDIO:
+ case CCLK_SRC_SDMMC0:
+ case CCLK_SRC_EMMC:
+ case BCLK_EMMC:
+ case SCLK_FSPI_X2:
+ case SCLK_FSPI1_X2:
+ case DCLK_DECOM:
+ case HCLK_SDMMC0:
+ case HCLK_EMMC:
+ case HCLK_SDIO:
+ ret = rk3576_mmc_set_clk(priv, clk->id, rate);
+ break;
+ case TCLK_EMMC:
+ case TCLK_WDT0:
+ ret = OSC_HZ;
+ break;
+
+ /* Might occur in cru assigned-clocks, can be ignored here */
+ case CLK_AUDIO_FRAC_0:
+ case CLK_AUDIO_FRAC_1:
+ case CLK_AUDIO_FRAC_0_SRC:
+ case CLK_AUDIO_FRAC_1_SRC:
+ case CLK_CPLL_DIV2:
+ case CLK_CPLL_DIV4:
+ case CLK_CPLL_DIV10:
+ case FCLK_DDR_CM0_CORE:
+ case ACLK_PHP_ROOT:
+ ret = 0;
+ break;
+#ifndef CONFIG_SPL_BUILD
+ case ACLK_VOP_ROOT:
+ case ACLK_VOP:
+ case ACLK_VO0_ROOT:
+ case ACLK_VO1_ROOT:
+ case HCLK_VOP_ROOT:
+ case PCLK_VOP_ROOT:
+ ret = rk3576_aclk_vop_set_clk(priv, clk->id, rate);
+ break;
+ case DCLK_VP0:
+ case DCLK_VP0_SRC:
+ case DCLK_VP1:
+ case DCLK_VP1_SRC:
+ case DCLK_VP2:
+ case DCLK_VP2_SRC:
+ ret = rk3576_dclk_vop_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_GMAC0_PTP_REF_SRC:
+ case CLK_GMAC1_PTP_REF_SRC:
+ case CLK_GMAC0_PTP_REF:
+ case CLK_GMAC1_PTP_REF:
+ case CLK_GMAC0_125M_SRC:
+ case CLK_GMAC1_125M_SRC:
+ ret = rk3576_gmac_set_clk(priv, clk->id, rate);
+ break;
+ case CLK_UART_FRAC_0:
+ case CLK_UART_FRAC_1:
+ case CLK_UART_FRAC_2:
+ ret = rk3576_uart_frac_set_rate(priv, clk->id, rate);
+ break;
+ case SCLK_UART0:
+ case SCLK_UART1:
+ case SCLK_UART2:
+ case SCLK_UART3:
+ case SCLK_UART4:
+ case SCLK_UART5:
+ case SCLK_UART6:
+ case SCLK_UART7:
+ case SCLK_UART8:
+ case SCLK_UART9:
+ case SCLK_UART10:
+ case SCLK_UART11:
+ ret = rk3576_uart_set_rate(priv, clk->id, rate);
+ break;
+ case CLK_DSIHOST0:
+ ret = rk3576_clk_csihost_set_clk(priv, clk->id, rate);
+ break;
+ case DCLK_EBC:
+ case DCLK_EBC_FRAC_SRC:
+ ret = rk3576_dclk_ebc_set_clk(priv, clk->id, rate);
+ break;
+#endif
+ default:
+ return -ENOENT;
+ }
+
+ return ret;
+};
+
+#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA))
+static int __maybe_unused rk3576_dclk_vop_set_parent(struct clk *clk,
+ struct clk *parent)
+{
+ struct rk3576_clk_priv *priv = dev_get_priv(clk->dev);
+ struct rk3576_cru *cru = priv->cru;
+ u32 sel;
+ const char *clock_dev_name = parent->dev->name;
+
+ if (parent->id == PLL_VPLL)
+ sel = 2;
+ else if (parent->id == PLL_GPLL)
+ sel = 0;
+ else if (parent->id == PLL_CPLL)
+ sel = 1;
+ else if (parent->id == PLL_BPLL)
+ sel = 3;
+ else
+ sel = 4;
+
+ switch (clk->id) {
+ case DCLK_VP0_SRC:
+ rk_clrsetreg(&cru->clksel_con[145], DCLK0_VOP_SRC_SEL_MASK,
+ sel << DCLK0_VOP_SRC_SEL_SHIFT);
+ break;
+ case DCLK_VP1_SRC:
+ rk_clrsetreg(&cru->clksel_con[146], DCLK0_VOP_SRC_SEL_MASK,
+ sel << DCLK0_VOP_SRC_SEL_SHIFT);
+ break;
+ case DCLK_VP2_SRC:
+ rk_clrsetreg(&cru->clksel_con[147], DCLK0_VOP_SRC_SEL_MASK,
+ sel << DCLK0_VOP_SRC_SEL_SHIFT);
+ break;
+ case DCLK_VP0:
+ if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
+ sel = 1;
+ else
+ sel = 0;
+ rk_clrsetreg(&cru->clksel_con[147], DCLK0_VOP_SEL_MASK,
+ sel << DCLK0_VOP_SEL_SHIFT);
+ break;
+ case DCLK_VP1:
+ if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
+ sel = 1;
+ else
+ sel = 0;
+ rk_clrsetreg(&cru->clksel_con[147], DCLK1_VOP_SEL_MASK,
+ sel << DCLK1_VOP_SEL_SHIFT);
+ break;
+ case DCLK_VP2:
+ if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
+ sel = 1;
+ else
+ sel = 0;
+ rk_clrsetreg(&cru->clksel_con[147], DCLK2_VOP_SEL_MASK,
+ sel << DCLK2_VOP_SEL_SHIFT);
+ break;
+ case DCLK_EBC:
+ if (parent->id == PLL_GPLL)
+ sel = 0;
+ else if (parent->id == PLL_CPLL)
+ sel = 1;
+ else if (parent->id == PLL_VPLL)
+ sel = 2;
+ else if (parent->id == PLL_AUPLL)
+ sel = 3;
+ else if (parent->id == PLL_LPLL)
+ sel = 4;
+ else if (parent->id == DCLK_EBC_FRAC_SRC)
+ sel = 5;
+ else
+ sel = 6;
+ rk_clrsetreg(&cru->clksel_con[123], DCLK_EBC_SEL_MASK,
+ sel << DCLK_EBC_SEL_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __maybe_unused rk3576_ufs_ref_set_parent(struct clk *clk,
+ struct clk *parent)
+{
+ struct rk3576_clk_priv *priv = dev_get_priv(clk->dev);
+ struct rk3576_cru *cru = priv->cru;
+ u32 sel;
+ const char *clock_dev_name = parent->dev->name;
+
+ if (parent->id == CLK_REF_MPHY_26M)
+ sel = 2;
+ else if (!strcmp(clock_dev_name, "xin24m"))
+ sel = 0;
+ else
+ sel = 1;
+
+ rk_clrsetreg(&cru->pmuclksel_con[3], 0x3, sel << 0);
+ return 0;
+}
+
+static int rk3576_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ switch (clk->id) {
+ case DCLK_VP0_SRC:
+ case DCLK_VP1_SRC:
+ case DCLK_VP2_SRC:
+ case DCLK_VP0:
+ case DCLK_VP1:
+ case DCLK_VP2:
+ case DCLK_EBC:
+ return rk3576_dclk_vop_set_parent(clk, parent);
+ case CLK_REF_OSC_MPHY:
+ return rk3576_ufs_ref_set_parent(clk, parent);
+ case CLK_AUDIO_FRAC_0_SRC:
+ case CLK_AUDIO_FRAC_1_SRC:
+ /* Might occur in cru assigned-clocks, can be ignored here */
+ return 0;
+ default:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+#endif
+
+static struct clk_ops rk3576_clk_ops = {
+ .get_rate = rk3576_clk_get_rate,
+ .set_rate = rk3576_clk_set_rate,
+#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA))
+ .set_parent = rk3576_clk_set_parent,
+#endif
+};
+
+static void rk3576_clk_init(struct rk3576_clk_priv *priv)
+{
+ int ret;
+
+ priv->spll_hz = 702000000;
+
+ if (priv->cpll_hz != CPLL_HZ) {
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[CPLL], priv->cru,
+ CPLL, CPLL_HZ);
+ if (!ret)
+ priv->cpll_hz = CPLL_HZ;
+ }
+ if (priv->gpll_hz != GPLL_HZ) {
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[GPLL], priv->cru,
+ GPLL, GPLL_HZ);
+ if (!ret)
+ priv->gpll_hz = GPLL_HZ;
+ }
+ rk_clrsetreg(&priv->cru->clksel_con[123],
+ DCLK_EBC_FRAC_SRC_SEL_MASK,
+ (DCLK_EBC_FRAC_SRC_SEL_GPLL <<
+ DCLK_EBC_FRAC_SRC_SEL_SHIFT));
+}
+
+static int rk3576_clk_probe(struct udevice *dev)
+{
+ struct rk3576_clk_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->sync_kernel = false;
+
+#ifdef CONFIG_SPL_BUILD
+ /* relase presetn_bigcore_biu/cru/grf */
+ writel(0x1c001c00, 0x26010010);
+ /* set spll to normal mode */
+ writel(BITS_WITH_WMASK(2, 0x7U, 6),
+ RK3576_SCRU_BASE + RK3576_PLL_CON(137));
+ writel(BITS_WITH_WMASK(1, 0x3U, 0),
+ RK3576_SCRU_BASE + RK3576_MODE_CON0);
+ /* fix ppll\aupll\cpll */
+ writel(BITS_WITH_WMASK(2, 0x7U, 6),
+ RK3576_CRU_BASE + RK3576_PMU_PLL_CON(129));
+ writel(BITS_WITH_WMASK(2, 0x7U, 6),
+ RK3576_CRU_BASE + RK3576_PLL_CON(97));
+ writel(BITS_WITH_WMASK(2, 0x7U, 6),
+ RK3576_CRU_BASE + RK3576_PLL_CON(105));
+ writel(BITS_WITH_WMASK(1, 0x3U, 6),
+ RK3576_CRU_BASE + RK3576_MODE_CON0);
+ writel(BITS_WITH_WMASK(1, 0x3U, 8),
+ RK3576_CRU_BASE + RK3576_MODE_CON0);
+ /* init cci */
+ writel(0xffff0000, RK3576_CRU_BASE + RK3576_CCI_CLKSEL_CON(4));
+ rockchip_pll_set_rate(&rk3576_pll_clks[BPLL], priv->cru,
+ BPLL, LPLL_HZ);
+ if (!priv->armclk_enter_hz) {
+ ret = rockchip_pll_set_rate(&rk3576_pll_clks[LPLL], priv->cru,
+ LPLL, LPLL_HZ);
+ priv->armclk_enter_hz =
+ rockchip_pll_get_rate(&rk3576_pll_clks[LPLL],
+ priv->cru, LPLL);
+ priv->armclk_init_hz = priv->armclk_enter_hz;
+ rk_clrsetreg(&priv->cru->litclksel_con[0], CLK_LITCORE_DIV_MASK,
+ 0 << CLK_LITCORE_DIV_SHIFT);
+ }
+ /* init cci */
+ writel(0xffff20cb, RK3576_CRU_BASE + RK3576_CCI_CLKSEL_CON(4));
+
+ /* Change bigcore rm from 4 to 3 */
+ writel(0x001c000c, RK3576_BIGCORE_GRF_BASE + 0x3c);
+ writel(0x001c000c, RK3576_BIGCORE_GRF_BASE + 0x44);
+ writel(0x00020002, RK3576_BIGCORE_GRF_BASE + 0x38);
+ udelay(1);
+ writel(0x00020000, RK3576_BIGCORE_GRF_BASE + 0x38);
+ /* Change litcore rm from 4 to 3 */
+ writel(0x001c000c, RK3576_LITCORE_GRF_BASE + 0x3c);
+ writel(0x001c000c, RK3576_LITCORE_GRF_BASE + 0x44);
+ writel(0x00020002, RK3576_LITCORE_GRF_BASE + 0x38);
+ udelay(1);
+ writel(0x00020000, RK3576_LITCORE_GRF_BASE + 0x38);
+ /* Change cci rm form 4 to 3 */
+ writel(0x001c000c, RK3576_CCI_GRF_BASE + 0x54);
+#endif
+
+ rk3576_clk_init(priv);
+
+ /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
+ ret = clk_set_defaults(dev, 1);
+ if (ret)
+ debug("%s clk_set_defaults failed %d\n", __func__, ret);
+ else
+ priv->sync_kernel = true;
+
+ return 0;
+}
+
+static int rk3576_clk_ofdata_to_platdata(struct udevice *dev)
+{
+ struct rk3576_clk_priv *priv = dev_get_priv(dev);
+
+ priv->cru = dev_read_addr_ptr(dev);
+
+ return 0;
+}
+
+static int rk3576_clk_bind(struct udevice *dev)
+{
+ int ret;
+ struct udevice *sys_child;
+ struct sysreset_reg *priv;
+
+ /* The reset driver does not have a device node, so bind it here */
+ ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+ &sys_child);
+ if (ret) {
+ debug("Warning: No sysreset driver: ret=%d\n", ret);
+ } else {
+ priv = malloc(sizeof(struct sysreset_reg));
+ priv->glb_srst_fst_value = offsetof(struct rk3576_cru,
+ glb_srst_fst);
+ priv->glb_srst_snd_value = offsetof(struct rk3576_cru,
+ glb_srsr_snd);
+ dev_set_priv(sys_child, priv);
+ }
+
+#if CONFIG_IS_ENABLED(RESET_ROCKCHIP)
+ ret = offsetof(struct rk3576_cru, softrst_con[0]);
+ ret = rk3576_reset_bind_lut(dev, ret, 32776);
+ if (ret)
+ debug("Warning: software reset driver bind failed\n");
+#endif
+
+ return 0;
+}
+
+static const struct udevice_id rk3576_clk_ids[] = {
+ { .compatible = "rockchip,rk3576-cru" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_rk3576_cru) = {
+ .name = "rockchip_rk3576_cru",
+ .id = UCLASS_CLK,
+ .of_match = rk3576_clk_ids,
+ .priv_auto = sizeof(struct rk3576_clk_priv),
+ .of_to_plat = rk3576_clk_ofdata_to_platdata,
+ .ops = &rk3576_clk_ops,
+ .bind = rk3576_clk_bind,
+ .probe = rk3576_clk_probe,
+};
diff --git a/drivers/clk/stm32/clk-stm32mp1.c b/drivers/clk/stm32/clk-stm32mp1.c
index 4044edfb768..9cb69a01f7f 100644
--- a/drivers/clk/stm32/clk-stm32mp1.c
+++ b/drivers/clk/stm32/clk-stm32mp1.c
@@ -551,6 +551,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
STM32MP1_CLK_SET_CLR_F(RCC_MP_APB4ENSETR, 0, LTDC_PX, _PLL4_Q),
STM32MP1_CLK_SET_CLR_F(RCC_MP_APB4ENSETR, 4, DSI_PX, _PLL4_Q),
STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 4, DSI_K, _DSI_SEL),
+ STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 4, DSI, _DSI_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 8, DDRPERFM, _UNKNOWN_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 15, IWDG2, _UNKNOWN_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL),
diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
index 8bdc0944896..f44db76c182 100644
--- a/drivers/clk/sunxi/Kconfig
+++ b/drivers/clk/sunxi/Kconfig
@@ -122,4 +122,11 @@ config CLK_SUN50I_A64
This enables common clock driver support for platforms based
on Allwinner A64 SoC.
+config CLK_SUN50I_A100
+ bool "Clock driver for Allwinner A100/A133"
+ default MACH_SUN50I_A133
+ help
+ This enables common clock driver support for platforms based
+ on Allwinner A100/A133 SoCs.
+
endif # CLK_SUNXI
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 90a277489dc..7ff71c756e0 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o
obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o
obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o
obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o
+obj-$(CONFIG_CLK_SUN50I_A100) += clk_a100.o
diff --git a/drivers/clk/sunxi/clk_a100.c b/drivers/clk/sunxi/clk_a100.c
new file mode 100644
index 00000000000..b641feb8612
--- /dev/null
+++ b/drivers/clk/sunxi/clk_a100.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2023-2024 Arm Ltd.
+ */
+
+#include <clk/sunxi.h>
+#include <dt-bindings/clock/sun50i-a100-ccu.h>
+#include <dt-bindings/reset/sun50i-a100-ccu.h>
+#include <linux/bitops.h>
+
+static struct ccu_clk_gate a100_gates[] = {
+ [CLK_PLL_PERIPH0] = GATE(0x020, BIT(31) | BIT(27)),
+
+ [CLK_APB1] = GATE_DUMMY,
+
+ [CLK_DE] = GATE(0x600, BIT(31)),
+ [CLK_BUS_DE] = GATE(0x60c, BIT(0)),
+
+ [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)),
+ [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)),
+ [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)),
+
+ [CLK_BUS_UART0] = GATE(0x90c, BIT(0)),
+ [CLK_BUS_UART1] = GATE(0x90c, BIT(1)),
+ [CLK_BUS_UART2] = GATE(0x90c, BIT(2)),
+ [CLK_BUS_UART3] = GATE(0x90c, BIT(3)),
+ [CLK_BUS_UART4] = GATE(0x90c, BIT(4)),
+
+ [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)),
+ [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)),
+ [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)),
+ [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)),
+
+ [CLK_SPI0] = GATE(0x940, BIT(31)),
+ [CLK_SPI1] = GATE(0x944, BIT(31)),
+ [CLK_SPI2] = GATE(0x948, BIT(31)),
+
+ [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)),
+ [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)),
+ [CLK_BUS_SPI2] = GATE(0x96c, BIT(2)),
+
+ [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)),
+
+ [CLK_USB_PHY0] = GATE(0xa70, BIT(29)),
+ [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)),
+
+ [CLK_USB_PHY1] = GATE(0xa74, BIT(29)),
+ [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)),
+
+ [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)),
+ [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)),
+ [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)),
+ [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)),
+ [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)),
+
+ [CLK_TCON_LCD] = GATE(0xb60, BIT(31)),
+ [CLK_BUS_TCON_LCD] = GATE(0xb7c, BIT(0)),
+};
+
+static struct ccu_reset a100_resets[] = {
+ [RST_BUS_DE] = RESET(0x60c, BIT(16)),
+
+ [RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
+ [RST_BUS_MMC1] = RESET(0x84c, BIT(17)),
+ [RST_BUS_MMC2] = RESET(0x84c, BIT(18)),
+
+ [RST_BUS_UART0] = RESET(0x90c, BIT(16)),
+ [RST_BUS_UART1] = RESET(0x90c, BIT(17)),
+ [RST_BUS_UART2] = RESET(0x90c, BIT(18)),
+ [RST_BUS_UART3] = RESET(0x90c, BIT(19)),
+ [RST_BUS_UART4] = RESET(0x90c, BIT(20)),
+
+ [RST_BUS_I2C0] = RESET(0x91c, BIT(16)),
+ [RST_BUS_I2C1] = RESET(0x91c, BIT(17)),
+ [RST_BUS_I2C2] = RESET(0x91c, BIT(18)),
+ [RST_BUS_I2C3] = RESET(0x91c, BIT(19)),
+
+ [RST_BUS_SPI0] = RESET(0x96c, BIT(16)),
+ [RST_BUS_SPI1] = RESET(0x96c, BIT(17)),
+ [RST_BUS_SPI2] = RESET(0x96c, BIT(18)),
+
+ [RST_BUS_EMAC] = RESET(0x97c, BIT(16)),
+
+ [RST_USB_PHY0] = RESET(0xa70, BIT(30)),
+
+ [RST_USB_PHY1] = RESET(0xa74, BIT(30)),
+
+ [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)),
+ [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)),
+ [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)),
+ [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)),
+ [RST_BUS_OTG] = RESET(0xa8c, BIT(24)),
+
+ [RST_BUS_TCON_LCD] = RESET(0xb7c, BIT(16)),
+};
+
+const struct ccu_desc a100_ccu_desc = {
+ .gates = a100_gates,
+ .resets = a100_resets,
+ .num_gates = ARRAY_SIZE(a100_gates),
+ .num_resets = ARRAY_SIZE(a100_resets),
+};
diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c
index 2ef4f45dacf..e0765cbc6dc 100644
--- a/drivers/clk/sunxi/clk_sunxi.c
+++ b/drivers/clk/sunxi/clk_sunxi.c
@@ -122,6 +122,7 @@ extern const struct ccu_desc f1c100s_ccu_desc;
extern const struct ccu_desc h3_ccu_desc;
extern const struct ccu_desc h6_ccu_desc;
extern const struct ccu_desc h616_ccu_desc;
+extern const struct ccu_desc a100_ccu_desc;
extern const struct ccu_desc h6_r_ccu_desc;
extern const struct ccu_desc r40_ccu_desc;
extern const struct ccu_desc v3s_ccu_desc;
@@ -215,6 +216,10 @@ static const struct udevice_id sunxi_clk_ids[] = {
{ .compatible = "allwinner,sun50i-h616-r-ccu",
.data = (ulong)&h6_r_ccu_desc },
#endif
+#ifdef CONFIG_CLK_SUN50I_A100
+ { .compatible = "allwinner,sun50i-a100-ccu",
+ .data = (ulong)&a100_ccu_desc },
+#endif
#ifdef CONFIG_CLK_SUNIV_F1C100S
{ .compatible = "allwinner,suniv-f1c100s-ccu",
.data = (ulong)&f1c100s_ccu_desc },
diff --git a/drivers/clk/ti/clk-k3.c b/drivers/clk/ti/clk-k3.c
index bf65f573cb8..b38e559e45a 100644
--- a/drivers/clk/ti/clk-k3.c
+++ b/drivers/clk/ti/clk-k3.c
@@ -82,6 +82,8 @@ static const struct soc_attr ti_k3_soc_clk_data[] = {
.family = "J721E",
.data = &j721e_clk_platdata,
},
+#endif
+#if IS_ENABLED(CONFIG_SOC_K3_J7200)
{
.family = "J7200",
.data = &j7200_clk_platdata,
@@ -104,6 +106,10 @@ static const struct soc_attr ti_k3_soc_clk_data[] = {
.family = "J784S4",
.data = &j784s4_clk_platdata,
},
+ {
+ .family = "J742S2",
+ .data = &j784s4_clk_platdata,
+ },
#endif
{ /* sentinel */ }
};
diff --git a/drivers/core/Makefile b/drivers/core/Makefile
index 9ea57911f89..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_OF_CONTROL) += of_extra.o ofnode.o read_extra.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
new file mode 100644
index 00000000000..175ac768771
--- /dev/null
+++ b/drivers/core/ofnode_graph.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#define LOG_CATEGORY LOGC_DT
+
+#include <dm.h>
+#include <log.h>
+#include <dm/ofnode.h>
+#include <linux/err.h>
+
+/**
+ * ofnode_graph_get_endpoint_count() - get the number of endpoints in a device ofnode
+ * @parent: ofnode to the device containing ports and endpoints
+ *
+ * Return: count of endpoint of this device ofnode
+ */
+unsigned int ofnode_graph_get_endpoint_count(ofnode parent)
+{
+ ofnode ports, port, endpoint;
+ unsigned int num = 0;
+
+ /* Check if ports node exists */
+ ports = ofnode_find_subnode(parent, "ports");
+ if (ofnode_valid(ports))
+ parent = ports;
+
+ ofnode_for_each_subnode(port, parent) {
+ if (!strncmp(ofnode_get_name(port), "port", 4)) {
+ /* Port node can only contain endpoints */
+ ofnode_for_each_subnode(endpoint, port)
+ num++;
+ }
+ };
+
+ log_debug("%s: detected %d endpoints\n", __func__, num);
+
+ return num++;
+}
+
+/**
+ * ofnode_graph_get_port_count() - get the number of port in a device or ports ofnode
+ * @parent: ofnode to the device or ports node
+ *
+ * Return: count of port of this device or ports node
+ */
+unsigned int ofnode_graph_get_port_count(ofnode parent)
+{
+ ofnode ports, port;
+ unsigned int num = 0;
+
+ /* Check if ports node exists */
+ ports = ofnode_find_subnode(parent, "ports");
+ if (ofnode_valid(ports))
+ parent = ports;
+
+ ofnode_for_each_subnode(port, parent)
+ if (!strncmp(ofnode_get_name(port), "port", 4))
+ num++;
+
+ log_debug("%s: detected %d ports\n", __func__, num);
+
+ return num++;
+}
+
+/**
+ * ofnode_graph_get_port_by_id() - get the port matching a given id
+ * @parent: parent ofnode
+ * @id: id of the port
+ *
+ * Return: ofnode in given port.
+ */
+ofnode ofnode_graph_get_port_by_id(ofnode parent, u32 id)
+{
+ ofnode ports, port;
+ u32 port_id;
+
+ ports = ofnode_find_subnode(parent, "ports");
+ if (!ofnode_valid(ports))
+ return ofnode_null();
+
+ /* Check ports for node with desired id */
+ ofnode_for_each_subnode(port, ports) {
+ ofnode_read_u32(port, "reg", &port_id);
+ log_debug("%s: detected port %d\n", __func__, port_id);
+ if (port_id == id)
+ return port;
+ }
+
+ return ofnode_null();
+}
+
+/**
+ * ofnode_graph_get_endpoint_by_regs() - get the endpoint matching a given id
+ * @parent: parent ofnode
+ * @reg_id: id of the port
+ * @id: id for the endpoint
+ *
+ * Return: ofnode in given endpoint or ofnode_null() if not found.
+ * reg_id and id are ignored when they are -1.
+ */
+ofnode ofnode_graph_get_endpoint_by_regs(ofnode parent, int reg_id, int id)
+{
+ ofnode port, endpoint;
+ u32 ep_id;
+
+ /* get the port to work with */
+ if (reg_id < 0)
+ port = ofnode_find_subnode(parent, "port");
+ else
+ port = ofnode_graph_get_port_by_id(parent, reg_id);
+
+ if (!ofnode_valid(port)) {
+ log_debug("%s: port node is not found\n", __func__);
+ return ofnode_null();
+ }
+
+ if (id < 0)
+ return ofnode_find_subnode(port, "endpoint");
+
+ /* Check endpoints for node with desired id */
+ ofnode_for_each_subnode(endpoint, port) {
+ ofnode_read_u32(endpoint, "reg", &ep_id);
+ log_debug("%s: detected endpoint %d\n", __func__, ep_id);
+ if (ep_id == id)
+ return endpoint;
+ }
+
+ return ofnode_null();
+}
+
+/**
+ * ofnode_graph_get_remote_endpoint() - get remote endpoint node
+ * @endpoint: ofnode of a local endpoint
+ *
+ * Return: Remote endpoint ofnode linked with local endpoint.
+ */
+ofnode ofnode_graph_get_remote_endpoint(ofnode endpoint)
+{
+ /* Get remote endpoint node. */
+ return ofnode_parse_phandle(endpoint, "remote-endpoint", 0);
+}
+
+/**
+ * ofnode_graph_get_port_parent() - get port's parent node
+ * @endpoint: ofnode of a local endpoint
+ *
+ * Return: device ofnode associated with endpoint
+ */
+ofnode ofnode_graph_get_port_parent(ofnode endpoint)
+{
+ ofnode port = ofnode_get_parent(endpoint);
+ ofnode parent = ofnode_get_parent(port);
+
+ /* check if we are on top level or in ports node */
+ if (!strcmp(ofnode_get_name(parent), "ports"))
+ parent = ofnode_get_parent(parent);
+
+ return parent;
+}
+
+/**
+ * ofnode_graph_get_remote_port_parent() - get remote port's parent ofnode
+ * @endpoint: ofnode of a local endpoint
+ *
+ * Return: device ofnode associated with endpoint linked to local endpoint.
+ */
+ofnode ofnode_graph_get_remote_port_parent(ofnode endpoint)
+{
+ ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint);
+ if (!ofnode_valid(remote_endpoint)) {
+ log_debug("%s: remote endpoint is not found\n", __func__);
+ return ofnode_null();
+ }
+
+ return ofnode_graph_get_port_parent(remote_endpoint);
+}
+
+/**
+ * ofnode_graph_get_remote_port() - get remote port ofnode
+ * @endpoint: ofnode of a local endpoint
+ *
+ * Return: port ofnode associated with remote endpoint node linked
+ * to local endpoint.
+ */
+ofnode ofnode_graph_get_remote_port(ofnode endpoint)
+{
+ ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint);
+ if (!ofnode_valid(remote_endpoint)) {
+ log_debug("%s: remote endpoint is not found\n", __func__);
+ return ofnode_null();
+ }
+
+ return ofnode_get_parent(remote_endpoint);
+}
+
+/**
+ * ofnode_graph_get_remote_node() - get remote parent ofnode for given port/endpoint
+ * @parent: parent ofnode containing graph port/endpoint
+ * @port: identifier (value of reg property) of the parent port ofnode
+ * @endpoint: identifier (value of reg property) of the endpoint ofnode
+ *
+ * Return: device ofnode associated with endpoint linked to local endpoint.
+ */
+ofnode ofnode_graph_get_remote_node(ofnode parent, int port, int endpoint)
+{
+ ofnode endpoint_ofnode;
+
+ endpoint_ofnode = ofnode_graph_get_endpoint_by_regs(parent, port, endpoint);
+ if (!ofnode_valid(endpoint_ofnode)) {
+ log_debug("%s: endpoint is not found\n", __func__);
+ return ofnode_null();
+ }
+
+ return ofnode_graph_get_remote_port_parent(endpoint_ofnode);
+}
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/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c
index 53d31b3c0bf..4e1eccaa5b0 100644
--- a/drivers/cpu/imx8_cpu.c
+++ b/drivers/cpu/imx8_cpu.c
@@ -35,11 +35,49 @@ static const char *get_imx_type_str(u32 imxtype)
{
switch (imxtype) {
case MXC_CPU_IMX8MM:
- return "8MM";
+ return "8MMQ"; /* Quad-core version of the imx8mm */
+ case MXC_CPU_IMX8MML:
+ return "8MMQL"; /* Quad-core Lite version of the imx8mm */
+ case MXC_CPU_IMX8MMD:
+ return "8MMD"; /* Dual-core version of the imx8mm */
+ case MXC_CPU_IMX8MMDL:
+ return "8MMDL"; /* Dual-core Lite version of the imx8mm */
+ case MXC_CPU_IMX8MMS:
+ return "8MMS"; /* Single-core version of the imx8mm */
+ case MXC_CPU_IMX8MMSL:
+ return "8MMSL"; /* Single-core Lite version of the imx8mm */
case MXC_CPU_IMX8MN:
- return "8MN";
+ return "8MNano Quad"; /* Quad-core version */
+ case MXC_CPU_IMX8MND:
+ return "8MNano Dual"; /* Dual-core version */
+ case MXC_CPU_IMX8MNS:
+ return "8MNano Solo"; /* Single-core version */
+ case MXC_CPU_IMX8MNL:
+ return "8MNano QuadLite"; /* Quad-core Lite version */
+ case MXC_CPU_IMX8MNDL:
+ return "8MNano DualLite"; /* Dual-core Lite version */
+ case MXC_CPU_IMX8MNSL:
+ return "8MNano SoloLite";/* Single-core Lite version of the imx8mn */
+ case MXC_CPU_IMX8MNUQ:
+ return "8MNano UltraLite Quad";/* Quad-core UltraLite version of the imx8mn */
+ case MXC_CPU_IMX8MNUD:
+ return "8MNano UltraLite Dual";/* Dual-core UltraLite version of the imx8mn */
+ case MXC_CPU_IMX8MNUS:
+ return "8MNano UltraLite Solo";/* Single-core UltraLite version of the imx8mn */
case MXC_CPU_IMX8MP:
- return "8MP";
+ return "8MP[8]"; /* Quad-core version of the imx8mp */
+ case MXC_CPU_IMX8MPD:
+ return "8MP Dual[3]"; /* Dual-core version of the imx8mp */
+ case MXC_CPU_IMX8MPL:
+ return "8MP Lite[4]"; /* Quad-core Lite version of the imx8mp */
+ case MXC_CPU_IMX8MP6:
+ return "8MP[6]"; /* Quad-core version of the imx8mp, NPU fused */
+ case MXC_CPU_IMX8MQ:
+ return "8MQ"; /* Quad-core version of the imx8mq */
+ case MXC_CPU_IMX8MQL:
+ return "8MQLite"; /* Quad-core Lite version of the imx8mq */
+ case MXC_CPU_IMX8MD:
+ return "8MD"; /* Dual-core version of the imx8mq */
case MXC_CPU_IMX8QXP:
case MXC_CPU_IMX8QXP_A0:
return "8QXP";
@@ -177,19 +215,19 @@ static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size)
ret = snprintf(buf, size, "NXP i.MX%s Rev%s %s at %u MHz",
plat->type, plat->rev, plat->name, plat->freq_mhz);
- if (IS_ENABLED(CONFIG_IMX9)) {
+ if (IS_ENABLED(CONFIG_IMX_TMU)) {
switch (get_cpu_temp_grade(&minc, &maxc)) {
case TEMP_AUTOMOTIVE:
- grade = "Automotive temperature grade ";
+ grade = "Automotive temperature grade";
break;
case TEMP_INDUSTRIAL:
- grade = "Industrial temperature grade ";
+ grade = "Industrial temperature grade";
break;
case TEMP_EXTCOMMERCIAL:
- grade = "Extended Consumer temperature grade ";
+ grade = "Extended Consumer temperature grade";
break;
default:
- grade = "Consumer temperature grade ";
+ grade = "Consumer temperature grade";
break;
}
diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile
index c1d6a6b6c59..c627f16711e 100644
--- a/drivers/ddr/altera/Makefile
+++ b/drivers/ddr/altera/Makefile
@@ -4,12 +4,13 @@
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2010, Thomas Chou <thomas@wytron.com.tw>
-# Copyright (C) 2014-2021 Altera Corporation <www.altera.com>
+# 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
obj-$(CONFIG_TARGET_SOCFPGA_AGILEX) += sdram_soc64.o sdram_agilex.o
obj-$(CONFIG_TARGET_SOCFPGA_N5X) += sdram_soc64.o sdram_n5x.o
+obj-$(CONFIG_TARGET_SOCFPGA_AGILEX5) += sdram_soc64.o sdram_agilex5.o iossm_mailbox.o
endif
diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c
new file mode 100644
index 00000000000..fc09dde3f9e
--- /dev/null
+++ b/drivers/ddr/altera/iossm_mailbox.c
@@ -0,0 +1,923 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
+ *
+ */
+
+#include <hang.h>
+#include <string.h>
+#include <wait_bit.h>
+#include <asm/arch/base_addr_soc64.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/sizes.h>
+#include "iossm_mailbox.h"
+
+#define TIMEOUT_120000MS 120000
+#define TIMEOUT_60000MS 60000
+#define TIMEOUT TIMEOUT_120000MS
+#define IOSSM_STATUS_CAL_SUCCESS BIT(0)
+#define IOSSM_STATUS_CAL_FAIL BIT(1)
+#define IOSSM_STATUS_CAL_BUSY BIT(2)
+#define IOSSM_STATUS_COMMAND_RESPONSE_READY BIT(0)
+#define IOSSM_CMD_RESPONSE_STATUS_OFFSET 0x45C
+#define IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x458
+#define IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x454
+#define IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x450
+#define IOSSM_CMD_REQ_OFFSET 0x43C
+#define IOSSM_CMD_PARAM_0_OFFSET 0x438
+#define IOSSM_CMD_PARAM_1_OFFSET 0x434
+#define IOSSM_CMD_PARAM_2_OFFSET 0x430
+#define IOSSM_CMD_PARAM_3_OFFSET 0x42C
+#define IOSSM_CMD_PARAM_4_OFFSET 0x428
+#define IOSSM_CMD_PARAM_5_OFFSET 0x424
+#define IOSSM_CMD_PARAM_6_OFFSET 0x420
+#define IOSSM_CMD_RESPONSE_DATA_SHORT_MASK GENMASK(31, 16)
+#define IOSSM_CMD_RESPONSE_DATA_SHORT(n) FIELD_GET(IOSSM_CMD_RESPONSE_DATA_SHORT_MASK, n)
+#define IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK GENMASK(7, 5)
+#define IOSSM_STATUS_CMD_RESPONSE_ERROR(n) FIELD_GET(IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK, n)
+#define IOSSM_STATUS_GENERAL_ERROR_MASK GENMASK(4, 1)
+#define IOSSM_STATUS_GENERAL_ERROR(n) FIELD_GET(IOSSM_STATUS_GENERAL_ERROR_MASK, n)
+
+/* Offset of Mailbox Read-only Registers */
+#define IOSSM_MAILBOX_HEADER_OFFSET 0x0
+#define IOSSM_MEM_INTF_INFO_0_OFFSET 0X200
+#define IOSSM_MEM_INTF_INFO_1_OFFSET 0x280
+#define IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET 0x210
+#define IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET 0x290
+#define IOSSM_MEM_WIDTH_INFO_INTF0_OFFSET 0x230
+#define IOSSM_MEM_WIDTH_INFO_INTF1_OFFSET 0x2B0
+#define IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET 0x234
+#define IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET 0x2B4
+#define IOSSM_ECC_ENABLE_INTF0_OFFSET 0x240
+#define IOSSM_ECC_ENABLE_INTF1_OFFSET 0x2C0
+#define IOSSM_ECC_SCRUB_STATUS_INTF0_OFFSET 0x244
+#define IOSSM_ECC_SCRUB_STATUS_INTF1_OFFSET 0x2C4
+#define IOSSM_LP_MODE_INTF0_OFFSET 0x250
+#define IOSSM_LP_MODE_INTF1_OFFSET 0x2D0
+#define IOSSM_MEM_INIT_STATUS_INTF0_OFFSET 0x260
+#define IOSSM_MEM_INIT_STATUS_INTF1_OFFSET 0x2E0
+#define IOSSM_BIST_STATUS_INTF0_OFFSET 0x264
+#define IOSSM_BIST_STATUS_INTF1_OFFSET 0x2E4
+#define IOSSM_ECC_ERR_STATUS_OFFSET 0x300
+#define IOSSM_ECC_ERR_DATA_START_OFFSET 0x310
+#define IOSSM_STATUS_OFFSET 0x400
+#define IOSSM_STATUS_CAL_INTF0_OFFSET 0x404
+#define IOSSM_STATUS_CAL_INTF1_OFFSET 0x408
+
+#define ECC_INTSTATUS_SERR SOCFPGA_SYSMGR_ADDRESS + 0x9C
+#define ECC_INISTATUS_DERR SOCFPGA_SYSMGR_ADDRESS + 0xA0
+#define DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK BIT(16)
+#define DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK BIT(17)
+
+/* offset info of GET_MEM_INTF_INFO */
+#define INTF_IP_TYPE_MASK GENMASK(31, 29)
+#define INTF_INSTANCE_ID_MASK GENMASK(28, 24)
+
+/* offset info of GET_MEM_CAL_STATUS */
+#define INTF_UNUSED 0x0
+#define INTF_MEM_CAL_STATUS_SUCCESS 0x1
+#define INTF_MEM_CAL_STATUS_FAIL 0x2
+#define INTF_MEM_CAL_STATUS_ONGOING 0x4
+
+/* offset info of MEM_TECHNOLOGY_INTF */
+#define INTF_DDR_TYPE_MASK GENMASK(2, 0)
+
+/* offset info of MEM_TOTAL_CAPACITY_INTF */
+#define INTF_CAPACITY_GBITS_MASK GENMASK(7, 0)
+
+/* offset info of ECC_ENABLE_INTF */
+#define INTF_ECC_ENABLE_TYPE_MASK GENMASK(1, 0)
+#define INTF_ECC_TYPE_MASK BIT(8)
+
+/* cmd opcode BIST_MEM_INIT_START, BIST performed on full memory address range */
+#define BIST_FULL_MEM BIT(6)
+
+/* offset info of ECC_ENABLE_INTF */
+#define INTF_BIST_STATUS_MASK BIT(0)
+
+/* offset info of ECC_ERR_STATUS */
+#define ECC_ERR_COUNTER_MASK GENMASK(15, 0)
+#define ECC_ERR_OVERFLOW_MASK GENMASK(31, 16)
+
+/* offset info of ECC_ERR_DATA */
+#define ECC_ERR_IP_TYPE_MASK GENMASK(24, 22)
+#define ECC_ERR_INSTANCE_ID_MASK GENMASK(21, 17)
+#define ECC_ERR_SOURCE_ID_MASK GENMASK(16, 10)
+#define ECC_ERR_TYPE_MASK GENMASK(9, 6)
+#define ECC_ERR_ADDR_UPPER_MASK GENMASK(5, 0)
+#define ECC_ERR_ADDR_LOWER_MASK GENMASK(31, 0)
+#define ECC_FULL_ADDR_UPPER_MASK GENMASK(63, 32)
+#define ECC_FULL_ADDR_LOWER_MASK GENMASK(31, 0)
+
+#define MAX_ECC_ERR_INFO_COUNT 16
+
+#define BIST_START_ADDR_SPACE_MASK GENMASK(5, 0)
+#define BIST_START_ADDR_LOW_MASK GENMASK(31, 0)
+#define BIST_START_ADDR_HIGH_MASK GENMASK(37, 32)
+
+#define IO96B_MB_REQ_SETUP(v, w, x, y, z) \
+ usr_req.ip_type = v; \
+ usr_req.ip_id = w; \
+ usr_req.usr_cmd_type = x; \
+ usr_req.usr_cmd_opcode = y; \
+ usr_req.cmd_param[0] = z; \
+ for (n = 1; n < NUM_CMD_PARAM; n++) \
+ usr_req.cmd_param[n] = 0
+#define MAX_RETRY_COUNT 3
+#define NUM_CMD_RESPONSE_DATA 3
+
+#define IO96B0_PLL_A_MASK BIT(0)
+#define IO96B0_PLL_B_MASK BIT(1)
+#define IO96B1_PLL_A_MASK BIT(2)
+#define IO96B1_PLL_B_MASK BIT(3)
+
+/* supported DDR type list */
+static const char *ddr_type_list[7] = {
+ "DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN"
+};
+
+/* Define an enumeration for ECC error types */
+enum ecc_error_type {
+ SINGLE_BIT_ERROR = 0, /* 0b0000 */
+ MULTIPLE_SINGLE_BIT_ERRORS = 1, /* 0b0001 */
+ DOUBLE_BIT_ERROR = 2, /* 0b0010 */
+ MULTIPLE_DOUBLE_BIT_ERRORS = 3, /* 0b0011 */
+ SINGLE_BIT_ERROR_SCRUBBING = 8, /* 0b1000 */
+ WRITE_LINK_SINGLE_BIT_ERROR = 9, /* 0b1001 */
+ WRITE_LINK_DOUBLE_BIT_ERROR = 10, /* 0b1010 */
+ READ_LINK_SINGLE_BIT_ERROR = 11, /* 0b1011 */
+ READ_LINK_DOUBLE_BIT_ERROR = 12, /* 0b1100 */
+ READ_MODIFY_WRITE_DOUBLE_BIT_ERROR = 13 /* 0b1101 */
+};
+
+/*
+ * ecc error info
+ *
+ * @ip_type: The IP type of the interface that produced the ECC interrupt.
+ * @instance_id: The instance ID of the interface that produced the ECC interrupt.
+ * @ecc_err_source_id: The source ID associated with the ECC event.
+ * @ecc_err_type: The ECC error type of the ECC event.
+ * @ecc_err_addr_upper: Upper 6 bits of the address of the read data that caused the ECC event.
+ * @ecc_err_addr_lower: Lower 32 bits of the address of the read data that caused the ECC event.
+ */
+struct ecc_err_info {
+ u32 ip_type;
+ u32 instance_id;
+ u32 source_id;
+ enum ecc_error_type err_type;
+ u32 addr_upper;
+ u32 addr_lower;
+};
+
+struct ecc_overflow_error_desc {
+ int bit;
+ const char *msg;
+};
+
+static const struct ecc_overflow_error_desc ecc_overflow_errors[] = {
+ { 0, " - Single-bit error\n" },
+ { 1, " - Multiple single-bit errors\n" },
+ { 2, " - Double-bit error\n" },
+ { 3, " - Multiple double-bit errors\n" },
+ { 8, " - Single-bit error during ECC scrubbing\n" },
+ { 9, " - Write link ECC single-bit error (LPDDR5 only)\n" },
+ { 10, " - Write link ECC double-bit error (LPDDR5 only)\n" },
+ { 11, " - Read link ECC single-bit error (LPDDR5 only)\n" },
+ { 12, " - Read link ECC double-bit error (LPDDR5 only)\n" },
+ { 13, " - RMW read link ECC double-bit error (LPDDR5 only)\n" },
+};
+
+static int is_ddr_csr_clkgen_locked(u8 io96b_pll)
+{
+ int ret = 0;
+ const char *pll_names[MAX_IO96B_SUPPORTED][2] = {
+ {"io96b_0 clkgenA", "io96b_0 clkgenB"},
+ {"io96b_1 clkgenA", "io96b_1 clkgenB"}
+ };
+ u32 masks[MAX_IO96B_SUPPORTED][2] = {
+ {IO96B0_PLL_A_MASK, IO96B0_PLL_B_MASK},
+ {IO96B1_PLL_A_MASK, IO96B1_PLL_B_MASK}
+ };
+ u32 lock_masks[MAX_IO96B_SUPPORTED] = {
+ DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK,
+ DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK
+ };
+
+ for (int i = 0; i < MAX_IO96B_SUPPORTED ; i++) {
+ /* Check for PLL_A */
+ if (io96b_pll & masks[i][0]) {
+ ret = wait_for_bit_le32((const void *)(ECC_INTSTATUS_SERR), lock_masks[i],
+ true, TIMEOUT, false);
+
+ if (ret) {
+ debug("%s: ddr csr %s locked is timeout\n",
+ __func__, pll_names[i][0]);
+ goto err;
+ } else {
+ debug("%s: ddr csr %s is successfully locked\n",
+ __func__, pll_names[i][0]);
+ }
+ }
+
+ /* Check for PLL_B */
+ if (io96b_pll & masks[i][1]) {
+ ret = wait_for_bit_le32((const void *)(ECC_INISTATUS_DERR), lock_masks[i],
+ true, TIMEOUT, false);
+
+ if (ret) {
+ debug("%s: ddr csr %s locked is timeout\n",
+ __func__, pll_names[i][1]);
+ goto err;
+ } else {
+ debug("%s: ddr csr %s is successfully locked\n",
+ __func__, pll_names[i][1]);
+ }
+ }
+ }
+
+err:
+ return ret;
+}
+
+/*
+ * Mailbox request function
+ * This function will send the request to IOSSM mailbox and wait for response return
+ *
+ * @io96b_csr_addr: CSR address for the target IO96B
+ * @req: Structure contain command request for IOSSM mailbox command
+ * @resp_data_len: User desire extra response data fields other than
+ * CMD_RESPONSE_DATA_SHORT field on CMD_RESPONSE_STATUS
+ * @resp: Structure contain responses returned from the requested IOSSM
+ * mailbox command
+ */
+int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req,
+ u32 resp_data_len, struct io96b_mb_resp *resp)
+{
+ int i, ret;
+ u32 cmd_req;
+
+ if (!resp) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Zero initialization for responses */
+ resp->cmd_resp_status = 0;
+
+ /* Ensure CMD_REQ is cleared before write any command request */
+ ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET),
+ GENMASK(31, 0), false, TIMEOUT, false);
+ if (ret) {
+ printf("%s: Timeout of waiting DDR mailbox ready to be functioned!\n",
+ __func__);
+ goto err;
+ }
+
+ /* Write CMD_PARAM_* */
+ for (i = 0; i < NUM_CMD_PARAM ; i++) {
+ switch (i) {
+ case 0:
+ if (req.cmd_param[0])
+ writel(req.cmd_param[0], io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET);
+ break;
+ case 1:
+ if (req.cmd_param[1])
+ writel(req.cmd_param[1], io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET);
+ break;
+ case 2:
+ if (req.cmd_param[2])
+ writel(req.cmd_param[2], io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET);
+ break;
+ case 3:
+ if (req.cmd_param[3])
+ writel(req.cmd_param[3], io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET);
+ break;
+ case 4:
+ if (req.cmd_param[4])
+ writel(req.cmd_param[4], io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET);
+ break;
+ case 5:
+ if (req.cmd_param[5])
+ writel(req.cmd_param[5], io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET);
+ break;
+ case 6:
+ if (req.cmd_param[6])
+ writel(req.cmd_param[6], io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET);
+ break;
+ }
+ }
+
+ /* Write CMD_REQ (IP_TYPE, IP_INSTANCE_ID, CMD_TYPE and CMD_OPCODE) */
+ cmd_req = FIELD_PREP(CMD_TARGET_IP_TYPE_MASK, req.ip_type) |
+ FIELD_PREP(CMD_TARGET_IP_INSTANCE_ID_MASK, req.ip_id) |
+ FIELD_PREP(CMD_TYPE_MASK, req.usr_cmd_type) |
+ FIELD_PREP(CMD_OPCODE_MASK, req.usr_cmd_opcode);
+ writel(cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET);
+
+ debug("%s: Write 0x%x to IOSSM_CMD_REQ_OFFSET 0x%llx\n", __func__, cmd_req,
+ io96b_csr_addr + IOSSM_CMD_REQ_OFFSET);
+
+ /* Read CMD_RESPONSE_READY in CMD_RESPONSE_STATUS */
+ ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET),
+ IOSSM_STATUS_COMMAND_RESPONSE_READY, true, TIMEOUT, false);
+
+ /* read CMD_RESPONSE_STATUS */
+ resp->cmd_resp_status = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET);
+
+ debug("%s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", __func__, io96b_csr_addr +
+ IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status);
+
+ if (ret) {
+ printf("%s: CMD_RESPONSE ERROR:\n", __func__);
+
+ printf("%s: STATUS_GENERAL_ERROR: 0x%lx\n", __func__,
+ IOSSM_STATUS_GENERAL_ERROR(resp->cmd_resp_status));
+ printf("%s: STATUS_CMD_RESPONSE_ERROR: 0x%lx\n", __func__,
+ IOSSM_STATUS_CMD_RESPONSE_ERROR(resp->cmd_resp_status));
+ goto err;
+ }
+
+ /* read CMD_RESPONSE_DATA_* */
+ for (i = 0; i < resp_data_len; i++) {
+ switch (i) {
+ case 0:
+ resp->cmd_resp_data[i] =
+ readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET);
+
+ debug("%s: IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x%llx: 0x%x\n", __func__,
+ io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET,
+ resp->cmd_resp_data[i]);
+ break;
+ case 1:
+ resp->cmd_resp_data[i] =
+ readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET);
+
+ debug("%s: IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x%llx: 0x%x\n", __func__,
+ io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET,
+ resp->cmd_resp_data[i]);
+ break;
+ case 2:
+ resp->cmd_resp_data[i] =
+ readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET);
+
+ debug("%s: IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x%llx: 0x%x\n", __func__,
+ io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET,
+ resp->cmd_resp_data[i]);
+ break;
+ default:
+ resp->cmd_resp_data[i] = 0;
+ printf("%s: Invalid response data\n", __func__);
+ }
+ }
+
+ /* write CMD_RESPONSE_READY = 0 */
+ clrbits_le32((u32 *)(uintptr_t)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET),
+ IOSSM_STATUS_COMMAND_RESPONSE_READY);
+
+ debug("%s: After clear CMD_RESPONSE_READY bit: 0x%llx: 0x%x\n", __func__,
+ io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET,
+ readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET));
+
+err:
+ return ret;
+}
+
+/*
+ * Initial function to be called to set memory interface IP type and instance ID
+ * IP type and instance ID need to be determined before sending mailbox command
+ */
+void io96b_mb_init(struct io96b_info *io96b_ctrl)
+{
+ int i, j;
+ u32 mem_intf_info_0, mem_intf_info_1;
+
+ debug("%s: num_instance %d\n", __func__, io96b_ctrl->num_instance);
+
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ debug("%s: get memory interface IO96B %d\n", __func__, i);
+ io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface = 0;
+
+ mem_intf_info_0 = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ IOSSM_MEM_INTF_INFO_0_OFFSET);
+ mem_intf_info_1 = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ IOSSM_MEM_INTF_INFO_1_OFFSET);
+
+ io96b_ctrl->io96b[i].mb_ctrl.ip_type[0] = FIELD_GET(INTF_IP_TYPE_MASK,
+ mem_intf_info_0);
+ io96b_ctrl->io96b[i].mb_ctrl.ip_id[0] = FIELD_GET(INTF_INSTANCE_ID_MASK,
+ mem_intf_info_0);
+ io96b_ctrl->io96b[i].mb_ctrl.ip_type[1] = FIELD_GET(INTF_IP_TYPE_MASK,
+ mem_intf_info_1);
+ io96b_ctrl->io96b[i].mb_ctrl.ip_id[1] = FIELD_GET(INTF_INSTANCE_ID_MASK,
+ mem_intf_info_1);
+
+ for (j = 0; j < MAX_MEM_INTERFACE_SUPPORTED; j++) {
+ if (io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]) {
+ io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface++;
+
+ debug("%s: IO96B %d mem_interface %d: ip_type_ret: 0x%x\n",
+ __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]);
+ debug("%s: IO96B %d mem_interface %d: instance_id_ret: 0x%x\n",
+ __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_id[j]);
+ }
+ }
+
+ debug("%s: IO96B %d: num_mem_interface: 0x%x\n", __func__, i,
+ io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface);
+ }
+}
+
+int io96b_cal_status(phys_addr_t addr)
+{
+ u32 cal_success, cal_fail;
+ phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET;
+ u32 start = get_timer(0);
+
+ do {
+ if (get_timer(start) > TIMEOUT_60000MS) {
+ printf("%s: SDRAM calibration for IO96B instance 0x%llx timeout!\n",
+ __func__, status_addr);
+ hang();
+ }
+
+ udelay(1);
+ schedule();
+
+ /* Polling until getting any calibration result */
+ cal_success = readl(status_addr) & IOSSM_STATUS_CAL_SUCCESS;
+ cal_fail = readl(status_addr) & IOSSM_STATUS_CAL_FAIL;
+ } while (!cal_success && !cal_fail);
+
+ debug("%s: Calibration for IO96B instance 0x%llx done at %ld msec!\n",
+ __func__, status_addr, get_timer(start));
+
+ if (cal_success && !cal_fail)
+ return 0;
+ else
+ return -EPERM;
+}
+
+void init_mem_cal(struct io96b_info *io96b_ctrl)
+{
+ int count, i, ret;
+
+ /* Initialize overall calibration status */
+ io96b_ctrl->overall_cal_status = false;
+
+ if (io96b_ctrl->ckgen_lock) {
+ ret = is_ddr_csr_clkgen_locked(io96b_ctrl->io96b_pll);
+ if (ret) {
+ printf("%s: iossm IO96B ckgena_lock is not locked\n", __func__);
+ hang();
+ }
+ }
+
+ /* Check initial calibration status for the assigned IO96B */
+ count = 0;
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ ret = io96b_cal_status(io96b_ctrl->io96b[i].io96b_csr_addr);
+ if (ret) {
+ io96b_ctrl->io96b[i].cal_status = false;
+
+ printf("%s: Initial DDR calibration IO96B_%d failed %d\n", __func__,
+ i, ret);
+
+ hang();
+ }
+
+ io96b_ctrl->io96b[i].cal_status = true;
+
+ printf("%s: Initial DDR calibration IO96B_%d succeed\n", __func__, i);
+
+ count++;
+ }
+
+ if (count == io96b_ctrl->num_instance)
+ io96b_ctrl->overall_cal_status = true;
+}
+
+int get_mem_technology(struct io96b_info *io96b_ctrl)
+{
+ int i, j, ret = 0;
+ u32 mem_technology_intf;
+ u8 ddr_type_ret;
+
+ u32 mem_technology_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = {
+ IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET,
+ IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET
+ };
+
+ /* Initialize ddr type */
+ io96b_ctrl->ddr_type = ddr_type_list[6];
+
+ /* Get and ensure all memory interface(s) same DDR type */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) {
+ mem_technology_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ mem_technology_intf_offset[j]);
+
+ ddr_type_ret = FIELD_GET(INTF_DDR_TYPE_MASK, mem_technology_intf);
+
+ if (!strcmp(io96b_ctrl->ddr_type, "UNKNOWN"))
+ io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret];
+
+ if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) {
+ printf("%s: Mismatch DDR type on IO96B_%d\n", __func__, i);
+
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ }
+
+err:
+ return ret;
+}
+
+int get_mem_width_info(struct io96b_info *io96b_ctrl)
+{
+ int i, j, ret = 0;
+ u32 mem_width_info;
+ phys_size_t memory_size, total_memory_size = 0;
+
+ u32 mem_total_capacity_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = {
+ IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET,
+ IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET
+ };
+
+ /* Get all memory interface(s) total memory size on all instance(s) */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ memory_size = 0;
+ for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) {
+ mem_width_info = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ mem_total_capacity_intf_offset[j]);
+
+ io96b_ctrl->io96b[i].mb_ctrl.memory_size[j] =
+ FIELD_GET(INTF_CAPACITY_GBITS_MASK, mem_width_info) * SZ_1G / SZ_8;
+
+ if (io96b_ctrl->io96b[i].mb_ctrl.memory_size[j] != 0)
+ memory_size += io96b_ctrl->io96b[i].mb_ctrl.memory_size[j];
+ }
+
+ if (!memory_size) {
+ printf("%s: Failed to get valid memory size\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ total_memory_size = total_memory_size + memory_size;
+ }
+
+ if (!total_memory_size) {
+ printf("%s: Failed to get valid memory size\n", __func__);
+ ret = -EINVAL;
+ }
+
+ io96b_ctrl->overall_size = total_memory_size;
+
+err:
+ return ret;
+}
+
+int ecc_enable_status(struct io96b_info *io96b_ctrl)
+{
+ int i, j, ret = 0;
+ u32 ecc_enable_intf;
+ bool ecc_status, ecc_status_set = false, inline_ecc = false;
+
+ u32 ecc_enable_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = {
+ IOSSM_ECC_ENABLE_INTF0_OFFSET,
+ IOSSM_ECC_ENABLE_INTF1_OFFSET
+ };
+
+ /* Initialize ECC status */
+ io96b_ctrl->ecc_status = false;
+ io96b_ctrl->inline_ecc = false;
+
+ /* Get and ensure all memory interface(s) same ECC status */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) {
+ ecc_enable_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ ecc_enable_intf_offset[j]);
+
+ ecc_status = (FIELD_GET(INTF_ECC_ENABLE_TYPE_MASK, ecc_enable_intf)
+ == 0) ? false : true;
+ inline_ecc = FIELD_GET(INTF_ECC_TYPE_MASK, ecc_enable_intf);
+
+ if (!ecc_status_set) {
+ io96b_ctrl->ecc_status = ecc_status;
+
+ if (io96b_ctrl->ecc_status)
+ io96b_ctrl->inline_ecc = inline_ecc;
+
+ ecc_status_set = true;
+ }
+
+ if (ecc_status != io96b_ctrl->ecc_status ||
+ (io96b_ctrl->ecc_status && inline_ecc != io96b_ctrl->inline_ecc)) {
+ printf("%s: Mismatch DDR ECC status on IO96B_%d\n", __func__, i);
+
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ }
+
+ debug("%s: ECC enable status: %d\n", __func__, io96b_ctrl->ecc_status);
+
+err:
+ return ret;
+}
+
+bool is_double_bit_error(enum ecc_error_type err_type)
+{
+ switch (err_type) {
+ case DOUBLE_BIT_ERROR:
+ case MULTIPLE_DOUBLE_BIT_ERRORS:
+ case WRITE_LINK_DOUBLE_BIT_ERROR:
+ case READ_LINK_DOUBLE_BIT_ERROR:
+ case READ_MODIFY_WRITE_DOUBLE_BIT_ERROR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool ecc_interrupt_status(struct io96b_info *io96b_ctrl)
+{
+ int i, j;
+ u32 ecc_err_status;
+ u16 ecc_err_counter, ecc_overflow_status;
+ bool ecc_error_flag = false;
+
+ /* Get ECC double-bit error status */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ ecc_err_status = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ IOSSM_ECC_ERR_STATUS_OFFSET);
+
+ ecc_err_counter = FIELD_GET(ECC_ERR_COUNTER_MASK, ecc_err_status);
+ log_err("%s: ECC error number detected on IO96B_%d: %d\n",
+ __func__, i, ecc_err_counter);
+
+ ecc_overflow_status = FIELD_GET(ECC_ERR_OVERFLOW_MASK, ecc_err_status);
+ if (ecc_overflow_status != 0) {
+ log_err("ECC Error Overflow Flags:\n");
+
+ for (int i = 0; i < ARRAY_SIZE(ecc_overflow_errors); i++) {
+ if (ecc_overflow_status & BIT(ecc_overflow_errors[i].bit)) {
+ log_err("%s", ecc_overflow_errors[i].msg);
+ }
+ }
+ }
+
+ if (ecc_err_counter != 0) {
+ phys_addr_t address;
+ u32 ecc_err_data;
+ struct ecc_err_info err_info;
+
+ address = io96b_ctrl->io96b[i].io96b_csr_addr +
+ IOSSM_ECC_ERR_DATA_START_OFFSET;
+
+ for (j = 0; j < ecc_err_counter && j < MAX_ECC_ERR_INFO_COUNT; j++) {
+ ecc_err_data = readl(address);
+ err_info.err_type = FIELD_GET(ECC_ERR_TYPE_MASK,
+ ecc_err_data);
+ err_info.ip_type = FIELD_GET(ECC_ERR_IP_TYPE_MASK,
+ ecc_err_data);
+ err_info.instance_id = FIELD_GET(ECC_ERR_INSTANCE_ID_MASK,
+ ecc_err_data);
+ err_info.source_id = FIELD_GET(ECC_ERR_SOURCE_ID_MASK,
+ ecc_err_data);
+ err_info.addr_upper = FIELD_GET(ECC_ERR_ADDR_UPPER_MASK,
+ ecc_err_data);
+ err_info.addr_lower = readl(address + sizeof(u32));
+
+ log_err(" %s: DDR ECC Error Detected on IO96B_%d number:%d\n",
+ __func__, i, j);
+ log_err(" - error info address :0x%llx\n", address);
+ log_err(" - error ip type: %d\n", err_info.ip_type);
+ log_err(" - error instance id: %d\n", err_info.instance_id);
+ log_err(" - error source id: %d\n", err_info.source_id);
+ log_err(" - error type: %s\n",
+ is_double_bit_error(err_info.err_type) ?
+ "Double-bit error" : "Single-bit error");
+ log_err(" - error address: 0x%016llx\n",
+ (u64)FIELD_PREP(ECC_FULL_ADDR_UPPER_MASK,
+ err_info.addr_upper) |
+ FIELD_PREP(ECC_FULL_ADDR_LOWER_MASK,
+ err_info.addr_lower));
+
+ if (is_double_bit_error(err_info.err_type)) {
+ if (!ecc_error_flag)
+ ecc_error_flag = true;
+ }
+
+ address += sizeof(u32) * 2;
+ }
+ }
+ }
+
+ if (ecc_error_flag)
+ log_err("\n%s: ECC double-bit error detected!\n", __func__);
+
+ return ecc_error_flag;
+}
+
+int out_of_band_bist_mem_init_start(struct io96b_info *io96b_ctrl)
+{
+ struct io96b_mb_req usr_req;
+ struct io96b_mb_resp usr_resp;
+ int i, j, n, ret = 0;
+ bool bist_start, bist_success;
+ u32 mem_init_status_intf, start;
+
+ u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] = {
+ IOSSM_MEM_INIT_STATUS_INTF0_OFFSET,
+ IOSSM_MEM_INIT_STATUS_INTF1_OFFSET
+ };
+
+ /* Full memory initialization BIST performed on all memory interface(s) */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) {
+ bist_start = false;
+ bist_success = false;
+
+ /* Start memory initialization BIST on full memory address */
+ IO96B_MB_REQ_SETUP(io96b_ctrl->io96b[i].mb_ctrl.ip_type[j],
+ io96b_ctrl->io96b[i].mb_ctrl.ip_id[j],
+ CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START,
+ BIST_FULL_MEM);
+
+ ret = io96b_mb_req(io96b_ctrl->io96b[i].io96b_csr_addr,
+ usr_req, 0, &usr_resp);
+ if (ret)
+ goto err;
+
+ bist_start = IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status)
+ & BIT(0);
+
+ if (!bist_start) {
+ printf("%s: Failed to initialize memory on IO96B_%d\n", __func__,
+ i);
+ printf("%s: BIST_MEM_INIT_START Error code 0x%lx\n", __func__,
+ IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status));
+
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Polling for the initiated memory initialization BIST status */
+ start = get_timer(0);
+ while (!bist_success) {
+ udelay(1);
+
+ mem_init_status_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr +
+ mem_init_status_offset[j]);
+
+ bist_success = FIELD_GET(INTF_BIST_STATUS_MASK,
+ mem_init_status_intf);
+
+ if (!bist_success && (get_timer(start) > TIMEOUT)) {
+ printf("%s: Timeout initialize memory on IO96B_%d\n",
+ __func__, i);
+ printf("%s: BIST_MEM_INIT_STATUS Error code 0x%lx\n",
+ __func__,
+ IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status));
+
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+ }
+ }
+
+ debug("%s: Memory initialized successfully on IO96B_%d\n", __func__, i);
+ }
+
+err:
+ return ret;
+}
+
+int bist_mem_init_by_addr(struct io96b_info *io96b_ctrl, int inst_id, int intf_id,
+ phys_addr_t base_addr, phys_size_t size)
+{
+ struct io96b_mb_req usr_req;
+ struct io96b_mb_resp usr_resp;
+ int n, ret = 0;
+ bool bist_start, bist_success;
+ u32 mem_exp, mem_init_status_intf, start;
+ phys_size_t chunk_size;
+
+ u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] = {
+ IOSSM_MEM_INIT_STATUS_INTF0_OFFSET,
+ IOSSM_MEM_INIT_STATUS_INTF1_OFFSET
+ };
+
+ /* Check if size is a power of 2 */
+ if (size == 0 || (size & (size - 1)) != 0) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mem_exp = 0;
+ chunk_size = size;
+
+ while (chunk_size >>= 1)
+ mem_exp++;
+
+ /* Start memory initialization BIST on the specified address range */
+ IO96B_MB_REQ_SETUP(io96b_ctrl->io96b[inst_id].mb_ctrl.ip_type[intf_id],
+ io96b_ctrl->io96b[inst_id].mb_ctrl.ip_id[intf_id],
+ CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, 0);
+
+ /* CMD_PARAM_0 bit[5:0] = mem_exp */
+ /* CMD_PARAM_0 bit[6]: 0 - on the specified address range */
+ usr_req.cmd_param[0] = FIELD_PREP(BIST_START_ADDR_SPACE_MASK, mem_exp);
+ /* Extract address fields START_ADDR[31:0] */
+ usr_req.cmd_param[1] = FIELD_GET(BIST_START_ADDR_LOW_MASK, base_addr);
+ /* Extract address fields START_ADDR[37:32] */
+ usr_req.cmd_param[2] = FIELD_GET(BIST_START_ADDR_HIGH_MASK, base_addr);
+ /* Initialize memory to all zeros */
+ usr_req.cmd_param[3] = 0;
+
+ bist_start = false;
+ bist_success = false;
+
+ /* Send request to DDR controller */
+ debug("%s:Initializing memory: Addr=0x%llx, Size=2^%u\n", __func__,
+ base_addr, mem_exp);
+ ret = io96b_mb_req(io96b_ctrl->io96b[inst_id].io96b_csr_addr,
+ usr_req, 0, &usr_resp);
+ if (ret)
+ goto err;
+
+ bist_start = IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status)
+ & BIT(0);
+
+ if (!bist_start) {
+ printf("%s: Failed to initialize memory on IO96B_%d\n", __func__,
+ inst_id);
+ printf("%s: BIST_MEM_INIT_START Error code 0x%lx\n", __func__,
+ IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status));
+
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Polling for the initiated memory initialization BIST status */
+ start = get_timer(0);
+ while (!bist_success) {
+ udelay(1);
+
+ mem_init_status_intf = readl(io96b_ctrl->io96b[inst_id].io96b_csr_addr +
+ mem_init_status_offset[intf_id]);
+
+ bist_success = FIELD_GET(INTF_BIST_STATUS_MASK, mem_init_status_intf);
+
+ if (!bist_success && (get_timer(start) > TIMEOUT)) {
+ printf("%s: Timeout initialize memory on IO96B_%d\n",
+ __func__, inst_id);
+ printf("%s: BIST_MEM_INIT_STATUS Error code 0x%lx\n",
+ __func__,
+ IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status));
+
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+ }
+
+ debug("%s:DDR memory initializationat 0x%llx completed.\n", __func__, base_addr);
+
+err:
+ return ret;
+}
+
+int inline_ecc_bist_mem_init(struct io96b_info *io96b_ctrl)
+{
+ int i, j, ret = 0;
+
+ /* Memory initialization BIST performed on all memory interfaces */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) {
+ ret = bist_mem_init_by_addr(io96b_ctrl, i, j, 0,
+ io96b_ctrl->io96b[i].mb_ctrl.memory_size[j]);
+ if (ret) {
+ printf("Error: Memory init failed at Instance %d, Interface %d\n",
+ i, j);
+ goto err;
+ }
+ }
+ }
+
+err:
+ return ret;
+}
+
+int bist_mem_init_start(struct io96b_info *io96b_ctrl)
+{
+ if (io96b_ctrl->inline_ecc)
+ return inline_ecc_bist_mem_init(io96b_ctrl);
+ else
+ return out_of_band_bist_mem_init_start(io96b_ctrl);
+}
diff --git a/drivers/ddr/altera/iossm_mailbox.h b/drivers/ddr/altera/iossm_mailbox.h
new file mode 100644
index 00000000000..02d1db28e20
--- /dev/null
+++ b/drivers/ddr/altera/iossm_mailbox.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
+ *
+ */
+
+#define MAX_IO96B_SUPPORTED 2
+#define MAX_MEM_INTERFACE_SUPPORTED 2
+#define NUM_CMD_RESPONSE_DATA 3
+#define NUM_CMD_PARAM 7
+
+/* supported mailbox command type */
+enum iossm_mailbox_cmd_type {
+ CMD_NOP,
+ CMD_GET_SYS_INFO,
+ CMD_GET_MEM_INFO,
+ CMD_GET_MEM_CAL_INFO,
+ CMD_TRIG_CONTROLLER_OP,
+ CMD_TRIG_MEM_CAL_OP
+};
+
+/* supported mailbox command opcode */
+enum iossm_mailbox_cmd_opcode {
+ ECC_ENABLE_SET = 0x0101,
+ ECC_INTERRUPT_MASK = 0x0105,
+ ECC_WRITEBACK_ENABLE = 0x0106,
+ ECC_INJECT_ERROR = 0x0109,
+ ECC_SCRUB_MODE_0_START = 0x0202,
+ ECC_SCRUB_MODE_1_START = 0x0203,
+ BIST_STANDARD_MODE_START = 0x0301,
+ BIST_MEM_INIT_START = 0x0303,
+ BIST_SET_DATA_PATTERN_UPPER = 0x0305,
+ BIST_SET_DATA_PATTERN_LOWER = 0x0306,
+ TRIG_MEM_CAL = 0x000a
+};
+
+/*
+ * IOSSM mailbox required information
+ *
+ * @num_mem_interface: Number of memory interfaces instantiated
+ * @ip_type: IP type implemented on the IO96B
+ * @ip_instance_id: IP identifier for every IP instance implemented on the IO96B
+ * @memory_size[2]: Memory size for every IP instance implemented on the IO96B
+ */
+struct io96b_mb_ctrl {
+ u32 num_mem_interface;
+ u32 ip_type[2];
+ u32 ip_id[2];
+ phys_size_t memory_size[2];
+};
+
+/* CMD_REQ Register Definition */
+#define CMD_TARGET_IP_TYPE_MASK GENMASK(31, 29)
+#define CMD_TARGET_IP_INSTANCE_ID_MASK GENMASK(28, 24)
+#define CMD_TYPE_MASK GENMASK(23, 16)
+#define CMD_OPCODE_MASK GENMASK(15, 0)
+
+/* Computes the Inline ECC data region size */
+#define CALC_INLINE_ECC_HW_SIZE(size) (((size) * 7) / 8)
+
+/*
+ * IOSSM mailbox request
+ * @ip_type: IP type for the specified memory interface
+ * @ip_id: IP instance ID for the specified memory interface
+ * @usr_cmd_type: User desire IOSSM mailbox command type
+ * @usr_cmd_opcode: User desire IOSSM mailbox command opcode
+ * @cmd_param_*: Parameters (if applicable) for the requested IOSSM mailbox command
+ */
+struct io96b_mb_req {
+ u32 ip_type;
+ u32 ip_id;
+ u32 usr_cmd_type;
+ u32 usr_cmd_opcode;
+ u32 cmd_param[NUM_CMD_PARAM];
+};
+
+/*
+ * IOSSM mailbox response outputs
+ *
+ * @cmd_resp_status: Command Interface status
+ * @cmd_resp_data_*: More spaces for command response
+ */
+struct io96b_mb_resp {
+ u32 cmd_resp_status;
+ u32 cmd_resp_data[NUM_CMD_RESPONSE_DATA];
+};
+
+/*
+ * IO96B instance specific information
+ *
+ * @io96b_csr_addr: IO96B instance CSR address
+ * @cal_status: IO96B instance calibration status
+ * @mb_ctrl: IOSSM mailbox required information
+ */
+struct io96b_instance {
+ phys_addr_t io96b_csr_addr;
+ bool cal_status;
+ struct io96b_mb_ctrl mb_ctrl;
+};
+
+/*
+ * Overall IO96B instance(s) information
+ *
+ * @num_instance: Number of instance(s) assigned to HPS
+ * @overall_cal_status: Overall calibration status for all IO96B instance(s)
+ * @ddr_type: DDR memory type
+ * @ecc_status: ECC enable status (false = disabled, true = enabled)
+ * @inline_ecc: Inline ECC or Out of Band ECC (false = Out of Band ECC, true = Inline ECC)
+ * @overall_size: Total DDR memory size
+ * @io96b[]: IO96B instance specific information
+ * @ckgen_lock: IO96B GEN PLL lock (false = not locked, true = locked)
+ * @num_port: Number of IO96B port.
+ * @io96b_pll: Selected IO96B PLL. Example bit 0: EMIF0 PLL A selected,
+ * bit 1: EMIF0 PLL B selected, bit 2 - EMIF1 PLL A selected,
+ * bit 3: EMIF1 PLL B selected
+ */
+struct io96b_info {
+ u8 num_instance;
+ bool overall_cal_status;
+ const char *ddr_type;
+ bool ecc_status;
+ bool inline_ecc;
+ phys_size_t overall_size;
+ struct io96b_instance io96b[MAX_IO96B_SUPPORTED];
+ bool ckgen_lock;
+ u8 num_port;
+ u8 io96b_pll;
+};
+
+int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req,
+ u32 resp_data_len, struct io96b_mb_resp *resp);
+
+/* Supported IOSSM mailbox function */
+void io96b_mb_init(struct io96b_info *io96b_ctrl);
+int io96b_cal_status(phys_addr_t addr);
+void init_mem_cal(struct io96b_info *io96b_ctrl);
+int get_mem_technology(struct io96b_info *io96b_ctrl);
+int get_mem_width_info(struct io96b_info *io96b_ctrl);
+int ecc_enable_status(struct io96b_info *io96b_ctrl);
+int bist_mem_init_start(struct io96b_info *io96b_ctrl);
+bool ecc_interrupt_status(struct io96b_info *io96b_ctrl);
diff --git a/drivers/ddr/altera/sdram_agilex5.c b/drivers/ddr/altera/sdram_agilex5.c
new file mode 100644
index 00000000000..ee66c72157a
--- /dev/null
+++ b/drivers/ddr/altera/sdram_agilex5.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <div64.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <hang.h>
+#include <log.h>
+#include <ram.h>
+#include <reset.h>
+#include <wait_bit.h>
+#include <wdt.h>
+#include <linux/bitfield.h>
+#include <linux/sizes.h>
+#include <asm/arch/firewall.h>
+#include <asm/arch/reset_manager.h>
+#include <asm/arch/system_manager.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include "iossm_mailbox.h"
+#include "sdram_soc64.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* MPFE NOC registers */
+#define F2SDRAM_SIDEBAND_FLAGOUTSET0 0x50
+#define F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 0x58
+#define SIDEBANDMGR_FLAGOUTSET0_REG SOCFPGA_F2SDRAM_MGR_ADDRESS +\
+ F2SDRAM_SIDEBAND_FLAGOUTSET0
+#define SIDEBANDMGR_FLAGOUTSTATUS0_REG SOCFPGA_F2SDRAM_MGR_ADDRESS +\
+ F2SDRAM_SIDEBAND_FLAGOUTSTATUS0
+#define BOOT_SCRATCH_COLD3_REG (socfpga_get_sysmgr_addr() +\
+ SYSMGR_SOC64_BOOT_SCRATCH_COLD3)
+#define PORT_EMIF_CONFIG_OFFSET 4
+#define EMIF_PLL_MASK GENMASK(19, 16)
+
+#define IO96B0_DUAL_PORT_MASK BIT(0)
+#define IO96B0_DUAL_EMIF_MASK BIT(1)
+
+#define FIREWALL_MPFE_SCR_IO96B0_REG 0x18000d00
+#define FIREWALL_MPFE_SCR_IO96B1_REG 0x18000d04
+#define FIREWALL_MPFE_NOC_CSR_REG 0x18000d08
+
+#define MEMORY_BANK_MAX_COUNT 3
+
+/* Reset type */
+enum reset_type {
+ POR_RESET,
+ WARM_RESET,
+ COLD_RESET,
+ NCONFIG,
+ JTAG_CONFIG,
+ RSU_RECONFIG
+};
+
+phys_addr_t io96b_csr_reg_addr[] = {
+ 0x18400000, /* IO96B_0 CSR registers address */
+ 0x18800000 /* IO96B_1 CSR registers address */
+};
+
+struct dram_bank_info_s {
+ phys_addr_t start;
+ phys_size_t max_size;
+};
+
+struct dram_bank_info_s dram_bank_info[MEMORY_BANK_MAX_COUNT] = {
+ {0x80000000, 0x80000000}, /* Memory Bank 0 */
+ {0x880000000, 0x780000000}, /* Memory Bank 1 */
+ {0x8800000000, 0x7800000000} /* Memory Bank 2 */
+};
+
+static enum reset_type get_reset_type(u32 reg)
+{
+ return FIELD_GET(ALT_SYSMGR_SCRATCH_REG_3_DDR_RESET_TYPE_MASK, reg);
+}
+
+static void update_io96b_assigned_to_hps(bool dual_port_flag, bool dual_emif_flag)
+{
+ clrsetbits_le32(BOOT_SCRATCH_COLD3_REG,
+ ALT_SYSMGR_SCRATCH_REG_3_DDR_PORT_EMIF_INFO_MASK,
+ FIELD_PREP(ALT_SYSMGR_SCRATCH_REG_3_DDR_PORT_INFO_MASK, dual_port_flag) |
+ FIELD_PREP(ALT_SYSMGR_SCRATCH_REG_3_DDR_EMIF_INFO_MASK, dual_emif_flag));
+
+ debug("%s: update dual port dual emif info: 0x%x\n", __func__,
+ readl(BOOT_SCRATCH_COLD3_REG));
+}
+
+static void set_mpfe_config(void)
+{
+ /* Set mpfe_lite_intfcsel */
+ setbits_le32(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG, BIT(2));
+
+ /* Set mpfe_lite_active */
+ setbits_le32(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG, BIT(8));
+
+ debug("%s: mpfe_config: 0x%x\n", __func__,
+ readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG));
+}
+
+static bool is_ddr_init_hang(void)
+{
+ u32 reg = readl(socfpga_get_sysmgr_addr() +
+ SYSMGR_SOC64_BOOT_SCRATCH_POR0);
+
+ debug("%s: 0x%x\n", __func__, reg);
+
+ if (reg & ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK)
+ return true;
+
+ return false;
+}
+
+static void ddr_init_inprogress(bool start)
+{
+ if (start)
+ setbits_le32(socfpga_get_sysmgr_addr() +
+ SYSMGR_SOC64_BOOT_SCRATCH_POR0,
+ ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK);
+ else
+ clrbits_le32(socfpga_get_sysmgr_addr() +
+ SYSMGR_SOC64_BOOT_SCRATCH_POR0,
+ ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK);
+}
+
+static void populate_ddr_handoff(struct udevice *dev, struct io96b_info *io96b_ctrl)
+{
+ struct altera_sdram_plat *plat = dev_get_plat(dev);
+ int i;
+ u32 len = SOC64_HANDOFF_SDRAM_LEN;
+ u32 handoff_table[len];
+
+ /* Read handoff for DDR configuration */
+ socfpga_handoff_read((void *)SOC64_HANDOFF_SDRAM, handoff_table, len);
+
+ /* Read handoff - dual port */
+ plat->dualport = FIELD_GET(IO96B0_DUAL_PORT_MASK, handoff_table[PORT_EMIF_CONFIG_OFFSET]);
+ debug("%s: dualport from handoff: 0x%x\n", __func__, plat->dualport);
+
+ if (plat->dualport)
+ io96b_ctrl->num_port = 2;
+ else
+ io96b_ctrl->num_port = 1;
+
+ /* Read handoff - dual EMIF */
+ plat->dualemif = FIELD_GET(IO96B0_DUAL_EMIF_MASK, handoff_table[PORT_EMIF_CONFIG_OFFSET]);
+ debug("%s: dualemif from handoff: 0x%x\n", __func__, plat->dualemif);
+
+ if (plat->dualemif)
+ io96b_ctrl->num_instance = 2;
+ else
+ io96b_ctrl->num_instance = 1;
+
+ io96b_ctrl->io96b_pll = FIELD_GET(EMIF_PLL_MASK,
+ handoff_table[PORT_EMIF_CONFIG_OFFSET]);
+ debug("%s: io96b enabled pll from handoff: 0x%x\n", __func__, io96b_ctrl->io96b_pll);
+
+ update_io96b_assigned_to_hps(plat->dualport, plat->dualemif);
+
+ /* Assign IO96B CSR base address if it is valid */
+ for (i = 0; i < io96b_ctrl->num_instance; i++) {
+ io96b_ctrl->io96b[i].io96b_csr_addr = io96b_csr_reg_addr[i];
+ debug("%s: IO96B 0x%llx CSR enabled\n", __func__,
+ io96b_ctrl->io96b[i].io96b_csr_addr);
+ }
+}
+
+static void config_mpfe_sideband_mgr(struct udevice *dev)
+{
+ struct altera_sdram_plat *plat = dev_get_plat(dev);
+
+ /* Dual port setting */
+ if (plat->dualport)
+ setbits_le32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(4));
+
+ /* Dual EMIF setting */
+ if (plat->dualemif) {
+ set_mpfe_config();
+ setbits_le32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(5));
+ }
+
+ debug("%s: SIDEBANDMGR_FLAGOUTSTATUS0: 0x%x\n", __func__,
+ readl(SIDEBANDMGR_FLAGOUTSTATUS0_REG));
+}
+
+static void config_ccu_mgr(struct udevice *dev)
+{
+ int ret = 0;
+ struct altera_sdram_plat *plat = dev_get_plat(dev);
+
+ if (plat->dualport || plat->dualemif) {
+ debug("%s: config interleaving on ccu reg\n", __func__);
+ ret = uclass_get_device_by_name(UCLASS_NOP,
+ "socfpga-ccu-ddr-interleaving-on", &dev);
+ } else {
+ debug("%s: config interleaving off ccu reg\n", __func__);
+ ret = uclass_get_device_by_name(UCLASS_NOP,
+ "socfpga-ccu-ddr-interleaving-off", &dev);
+ }
+
+ if (ret) {
+ printf("interleaving on/off ccu settings init failed: %d\n", ret);
+ hang();
+ }
+}
+
+static void config_firewall_mpfe_csr(struct udevice *dev)
+{
+ int ret = 0;
+
+ debug("%s: config Firewall setting for MPFE CSR\n", __func__);
+ ret = uclass_get_device_by_name(UCLASS_NOP,
+ "socfpga-noc-fw-mpfe-csr", &dev);
+
+ if (ret) {
+ printf("Firewall setting for MPFE CSR init failed: %d\n", ret);
+ hang();
+ }
+}
+
+static bool hps_ocram_dbe_status(void)
+{
+ u32 reg = readl(BOOT_SCRATCH_COLD3_REG);
+
+ if (reg & ALT_SYSMGR_SCRATCH_REG_3_OCRAM_DBE_MASK)
+ return true;
+
+ return false;
+}
+
+int sdram_mmr_init_full(struct udevice *dev)
+{
+ int i, ret = 0;
+ phys_size_t hw_size;
+ struct altera_sdram_plat *plat = dev_get_plat(dev);
+ struct altera_sdram_priv *priv = dev_get_priv(dev);
+ struct io96b_info *io96b_ctrl = malloc(sizeof(*io96b_ctrl));
+
+ u32 reg = readl(BOOT_SCRATCH_COLD3_REG);
+ enum reset_type reset_t = get_reset_type(reg);
+ bool full_mem_init = false;
+
+ /* DDR initialization progress status tracking */
+ bool is_ddr_hang_be4_rst = is_ddr_init_hang();
+
+ debug("DDR: SDRAM init in progress ...\n");
+ ddr_init_inprogress(true);
+
+ gd->bd = (struct bd_info *)malloc(sizeof(struct bd_info));
+ memset(gd->bd, '\0', sizeof(struct bd_info));
+
+ debug("DDR: Address MPFE 0x%llx\n", plat->mpfe_base_addr);
+
+ /* Populating DDR handoff data */
+ debug("DDR: Checking SDRAM configuration in progress ...\n");
+ populate_ddr_handoff(dev, io96b_ctrl);
+
+ /* Configuring MPFE sideband manager registers - dual port & dual emif */
+ config_mpfe_sideband_mgr(dev);
+
+ /* Configuring Interleave/Non-interleave ccu registers */
+ config_ccu_mgr(dev);
+
+ /* Configure if polling is needed for IO96B GEN PLL locked */
+ io96b_ctrl->ckgen_lock = true;
+
+ /* Ensure calibration status passing */
+ init_mem_cal(io96b_ctrl);
+
+ printf("DDR: Calibration success\n");
+
+ /* Initiate IOSSM mailbox */
+ io96b_mb_init(io96b_ctrl);
+
+ /* DDR type, DDR size and ECC status) */
+ ret = get_mem_technology(io96b_ctrl);
+ if (ret) {
+ printf("DDR: Failed to get DDR type\n");
+
+ goto err;
+ }
+
+ ret = get_mem_width_info(io96b_ctrl);
+ if (ret) {
+ printf("DDR: Failed to get DDR size\n");
+
+ goto err;
+ }
+
+ ret = ecc_enable_status(io96b_ctrl);
+ if (ret) {
+ printf("DDR: Failed to get ECC enabled status\n");
+
+ goto err;
+ }
+
+ hw_size = io96b_ctrl->overall_size;
+
+ /* Get bank configuration from devicetree */
+ ret = fdtdec_decode_ram_size(gd->fdt_blob, NULL, 0, NULL,
+ (phys_size_t *)&gd->ram_size, gd->bd);
+ if (ret) {
+ puts("DDR: Failed to decode memory node\n");
+ ret = -ENXIO;
+
+ goto err;
+ }
+
+ if (io96b_ctrl->inline_ecc)
+ hw_size = CALC_INLINE_ECC_HW_SIZE(hw_size);
+
+ if (gd->ram_size > hw_size) {
+ printf("DDR: Warning: DRAM size from device tree (%lld MiB) exceeds\n",
+ gd->ram_size >> 20);
+ printf(" the actual hardware capacity(%lld MiB). Memory configuration will be\n",
+ hw_size >> 20);
+ printf(" adjusted to match the detected hardware size.\n");
+ gd->ram_size = 0;
+ }
+
+ if (gd->ram_size > 0 && gd->ram_size != hw_size) {
+ printf("DDR: Warning: DRAM size from device tree (%lld MiB)\n",
+ gd->ram_size >> 20);
+ printf(" mismatch with hardware capacity(%lld MiB).\n",
+ hw_size >> 20);
+ }
+
+ if (gd->ram_size == 0 && hw_size > 0) {
+ phys_size_t remaining_size, size_counter = 0;
+ u8 config_dram_banks;
+
+ if (CONFIG_NR_DRAM_BANKS > MEMORY_BANK_MAX_COUNT) {
+ printf("DDR: Warning: CONFIG_NR_DRAM_BANKS(%d) is bigger than Max Memory Bank count(%d).\n",
+ CONFIG_NR_DRAM_BANKS, MEMORY_BANK_MAX_COUNT);
+ printf(" Max Memory Bank count is in use instead of CONFIG_NR_DRAM_BANKS.\n");
+ config_dram_banks = MEMORY_BANK_MAX_COUNT;
+ } else {
+ config_dram_banks = CONFIG_NR_DRAM_BANKS;
+ }
+
+ for (i = 0; i < config_dram_banks; i++) {
+ remaining_size = hw_size - size_counter;
+ if (remaining_size <= dram_bank_info[i].max_size) {
+ gd->bd->bi_dram[i].start = dram_bank_info[i].start;
+ gd->bd->bi_dram[i].size = remaining_size;
+ debug("Memory bank[%d] Starting address: 0x%llx size: 0x%llx\n",
+ i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size);
+ break;
+ }
+
+ gd->bd->bi_dram[i].start = dram_bank_info[i].start;
+ gd->bd->bi_dram[i].size = dram_bank_info[i].max_size;
+
+ debug("Memory bank[%d] Starting address: 0x%llx size: 0x%llx\n",
+ i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size);
+ size_counter += gd->bd->bi_dram[i].size;
+ }
+
+ gd->ram_size = hw_size;
+ }
+
+ printf("%s: %lld MiB\n", io96b_ctrl->ddr_type, gd->ram_size >> 20);
+
+ /* Is HPS cold or warm reset? If yes, Skip full memory initialization if ECC
+ * enabled to preserve memory content
+ */
+ if (io96b_ctrl->ecc_status) {
+ if (ecc_interrupt_status(io96b_ctrl)) {
+ if (CONFIG_IS_ENABLED(WDT)) {
+ struct udevice *wdt;
+
+ printf("DDR: ECC error recover start now\n");
+ ret = uclass_first_device_err(UCLASS_WDT, &wdt);
+ if (ret) {
+ printf("DDR: Failed to trigger watchdog reset\n");
+ hang();
+ }
+
+ wdt_expire_now(wdt, 0);
+ }
+ hang();
+ }
+
+ full_mem_init = hps_ocram_dbe_status() | is_ddr_hang_be4_rst;
+ if (full_mem_init || !(reset_t == WARM_RESET || reset_t == COLD_RESET)) {
+ ret = bist_mem_init_start(io96b_ctrl);
+ if (ret) {
+ printf("DDR: Failed to fully initialize DDR memory\n");
+
+ goto err;
+ }
+ }
+
+ printf("SDRAM-ECC: Initialized success\n");
+ }
+
+ sdram_size_check(gd->bd);
+ printf("DDR: size check success\n");
+
+ sdram_set_firewall(gd->bd);
+
+ /* Firewall setting for MPFE CSR */
+ config_firewall_mpfe_csr(dev);
+
+ printf("DDR: firewall init success\n");
+
+ priv->info.base = gd->bd->bi_dram[0].start;
+ priv->info.size = gd->ram_size;
+
+ /* Ending DDR driver initialization success tracking */
+ ddr_init_inprogress(false);
+
+ printf("DDR: init success\n");
+
+err:
+ free(io96b_ctrl);
+
+ return ret;
+}
diff --git a/drivers/ddr/altera/sdram_soc64.c b/drivers/ddr/altera/sdram_soc64.c
index 10a8e64af3d..27fbe80ed41 100644
--- a/drivers/ddr/altera/sdram_soc64.c
+++ b/drivers/ddr/altera/sdram_soc64.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016-2022 Intel Corporation <www.intel.com>
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
*
*/
@@ -28,6 +29,7 @@
#define PGTABLE_OFF 0x4000
+#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)
u32 hmc_readl(struct altera_sdram_plat *plat, u32 reg)
{
return readl(plat->iomhc + reg);
@@ -99,8 +101,9 @@ int emif_reset(struct altera_sdram_plat *plat)
debug("DDR: %s triggered successly\n", __func__);
return 0;
}
+#endif
-#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X)
+#if !(IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) || IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5))
int poll_hmc_clock_status(void)
{
return wait_for_bit_le32((const void *)(socfpga_get_sysmgr_addr() +
@@ -182,35 +185,51 @@ void sdram_init_ecc_bits(struct bd_info *bd)
void sdram_size_check(struct bd_info *bd)
{
phys_size_t total_ram_check = 0;
- phys_size_t ram_check = 0;
- phys_addr_t start = 0;
- phys_size_t size, remaining_size;
int bank;
/* Sanity check ensure correct SDRAM size specified */
debug("DDR: Running SDRAM size sanity check\n");
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
+ phys_size_t ram_check = 0;
+ phys_addr_t start = 0;
+ phys_size_t remaining_size;
+
start = bd->bi_dram[bank].start;
remaining_size = bd->bi_dram[bank].size;
+ debug("Checking bank %d: start=0x%llx, size=0x%llx\n",
+ bank, start, remaining_size);
+
while (ram_check < bd->bi_dram[bank].size) {
- size = min((phys_addr_t)SZ_1G,
- (phys_addr_t)remaining_size);
-
- /*
- * Ensure the size is power of two, this is requirement
- * to run get_ram_size() / memory test
- */
- if (size != 0 && ((size & (size - 1)) == 0)) {
- ram_check += get_ram_size((void *)
- (start + ram_check), size);
- remaining_size = bd->bi_dram[bank].size -
- ram_check;
- } else {
- puts("DDR: Memory test requires SDRAM size ");
- puts("in power of two!\n");
+ phys_size_t size, test_size, detected_size;
+
+ size = min((phys_addr_t)SZ_1G, (phys_addr_t)remaining_size);
+
+ if (size < SZ_8) {
+ puts("Invalid size: Memory size required to be multiple\n");
+ puts("of 64-Bit word!\n");
+ hang();
+ }
+
+ /* Adjust size to the nearest power of two to support get_ram_size() */
+ test_size = SZ_8;
+
+ while (test_size * 2 <= size)
+ test_size *= 2;
+
+ debug("Testing memory at 0x%llx with size 0x%llx\n",
+ start + ram_check, test_size);
+ detected_size = get_ram_size((void *)(start + ram_check), test_size);
+
+ if (detected_size != test_size) {
+ debug("Detected size 0x%llx doesn’t match the test size 0x%llx!\n",
+ detected_size, test_size);
+ puts("Memory testing failed!\n");
hang();
}
+
+ ram_check += detected_size;
+ remaining_size = bd->bi_dram[bank].size - ram_check;
}
total_ram_check += ram_check;
@@ -246,13 +265,13 @@ phys_size_t sdram_calculate_size(struct altera_sdram_plat *plat)
DRAMADDRW_CFG_ROW_ADDR_WIDTH(dramaddrw) +
DRAMADDRW_CFG_COL_ADDR_WIDTH(dramaddrw));
- size *= (2 << (hmc_ecc_readl(plat, DDRIOCTRL) &
+ size *= ((phys_size_t)2 << (hmc_ecc_readl(plat, DDRIOCTRL) &
DDR_HMC_DDRIOCTRL_IOSIZE_MSK));
return size;
}
-void sdram_set_firewall(struct bd_info *bd)
+static void sdram_set_firewall_non_f2sdram(struct bd_info *bd)
{
u32 i;
phys_size_t value;
@@ -288,7 +307,7 @@ void sdram_set_firewall(struct bd_info *bd)
FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT +
(i * 4 * sizeof(u32)));
- /* Setting non-secure MPU limit and limit extexded */
+ /* Setting non-secure MPU limit and limit extended */
value = bd->bi_dram[i].start + bd->bi_dram[i].size - 1;
lower = lower_32_bits(value);
@@ -301,7 +320,7 @@ void sdram_set_firewall(struct bd_info *bd)
FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT +
(i * 4 * sizeof(u32)));
- /* Setting non-secure Non-MPU limit and limit extexded */
+ /* Setting non-secure Non-MPU limit and limit extended */
FW_MPU_DDR_SCR_WRITEL(lower,
FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT +
(i * 4 * sizeof(u32)));
@@ -314,15 +333,77 @@ void sdram_set_firewall(struct bd_info *bd)
}
}
+#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)
+static void sdram_set_firewall_f2sdram(struct bd_info *bd)
+{
+ u32 i, lower, upper;
+ phys_size_t value;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ if (!bd->bi_dram[i].size)
+ continue;
+
+ value = bd->bi_dram[i].start;
+
+ /* Keep first 1MB of SDRAM memory region as secure region when
+ * using ATF flow, where the ATF code is located.
+ */
+ if (IS_ENABLED(CONFIG_SPL_ATF) && i == 0)
+ value += SZ_1M;
+
+ /* Setting base and base extended */
+ lower = lower_32_bits(value);
+ upper = upper_32_bits(value);
+ FW_F2SDRAM_DDR_SCR_WRITEL(lower,
+ FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE +
+ (i * 4 * sizeof(u32)));
+ FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff,
+ FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT +
+ (i * 4 * sizeof(u32)));
+
+ /* Setting limit and limit extended */
+ value = bd->bi_dram[i].start + bd->bi_dram[i].size - 1;
+
+ lower = lower_32_bits(value);
+ upper = upper_32_bits(value);
+
+ FW_F2SDRAM_DDR_SCR_WRITEL(lower,
+ FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT +
+ (i * 4 * sizeof(u32)));
+ FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff,
+ FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT +
+ (i * 4 * sizeof(u32)));
+
+ FW_F2SDRAM_DDR_SCR_WRITEL(BIT(i), FW_F2SDRAM_DDR_SCR_EN_SET);
+ }
+}
+#endif
+
+void sdram_set_firewall(struct bd_info *bd)
+{
+ sdram_set_firewall_non_f2sdram(bd);
+
+#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)
+ sdram_set_firewall_f2sdram(bd);
+#endif
+}
+
static int altera_sdram_of_to_plat(struct udevice *dev)
{
+#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X)
struct altera_sdram_plat *plat = dev_get_plat(dev);
fdt_addr_t addr;
+#endif
/* These regs info are part of DDR handoff in bitstream */
#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X)
return 0;
-#endif
+#elif IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)
+ addr = dev_read_addr_index(dev, 0);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+ plat->mpfe_base_addr = addr;
+#else
addr = dev_read_addr_index(dev, 0);
if (addr == FDT_ADDR_T_NONE)
@@ -338,7 +419,7 @@ static int altera_sdram_of_to_plat(struct udevice *dev)
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
plat->hmc = (void __iomem *)addr;
-
+#endif
return 0;
}
@@ -385,6 +466,7 @@ static const struct udevice_id altera_sdram_ids[] = {
{ .compatible = "altr,sdr-ctl-s10" },
{ .compatible = "intel,sdr-ctl-agilex" },
{ .compatible = "intel,sdr-ctl-n5x" },
+ { .compatible = "intel,sdr-ctl-agilex5" },
{ /* sentinel */ }
};
diff --git a/drivers/ddr/altera/sdram_soc64.h b/drivers/ddr/altera/sdram_soc64.h
index 87a70a861ba..183b1a33080 100644
--- a/drivers/ddr/altera/sdram_soc64.h
+++ b/drivers/ddr/altera/sdram_soc64.h
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2017-2019 Intel Corporation <www.intel.com>
+ * Copyright (C) 2025 Altera Corporation <www.altera.com>
+ *
*/
#ifndef _SDRAM_SOC64_H_
@@ -13,11 +15,19 @@ struct altera_sdram_priv {
struct reset_ctl_bulk resets;
};
+#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)
+struct altera_sdram_plat {
+ fdt_addr_t mpfe_base_addr;
+ bool dualport;
+ bool dualemif;
+};
+#else
struct altera_sdram_plat {
void __iomem *hmc;
void __iomem *ddr_sch;
void __iomem *iomhc;
};
+#endif
/* ECC HMC registers */
#define DDRIOCTRL 0x8
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/dma/Kconfig b/drivers/dma/Kconfig
index 3c64e894646..4b47be6b016 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -76,6 +76,13 @@ config XILINX_DPDMA
this file is used as placeholder for driver. The main reason is
to record compatible string and calling power domain driver.
+config ADI_DMA
+ bool "ADI DMA driver"
+ depends on DMA && DMA_CHANNELS
+ help
+ Enable DMA support for Analog Devices SOCs, such as the SC5xx.
+ Currently this is a minimalistic driver tested against OSPI use only.
+
if APBH_DMA
config APBH_DMA_BURST
bool "Enable DMA BURST"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 48811eaaeb3..00d765864cd 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -13,5 +13,6 @@ obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o
obj-$(CONFIG_XILINX_DPDMA) += xilinx_dpdma.o
+obj-$(CONFIG_ADI_DMA) += adi_dma.o
obj-y += ti/
diff --git a/drivers/dma/adi_dma.c b/drivers/dma/adi_dma.c
new file mode 100644
index 00000000000..28afe488db0
--- /dev/null
+++ b/drivers/dma/adi_dma.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Analog Devices DMA controller driver
+ *
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ * Contact: Ian Roberts <ian.roberts@timesys.com>
+ *
+ */
+#include <dm.h>
+#include <dma.h>
+#include <dma-uclass.h>
+#include <dm/device_compat.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#define HAS_MDMA BIT(0)
+
+#define REG_ADDRSTART 0x04
+#define REG_CFG 0x08
+#define REG_XCNT 0x0C
+#define REG_XMOD 0x10
+#define REG_STAT 0x30
+
+#define BITP_DMA_CFG_MSIZE 8
+#define BITP_DMA_CFG_PSIZE 4
+#define BITM_DMA_CFG_WNR 0x00000002
+#define BITM_DMA_CFG_EN 0x00000001
+#define ENUM_DMA_CFG_XCNT_INT 0x00100000
+
+#define BITP_DMA_STAT_PBWID 12
+#define BITP_DMA_STAT_ERRC 4
+#define BITM_DMA_STAT_PBWID 0x00003000
+#define BITM_DMA_STAT_ERRC 0x00000070
+#define BITM_DMA_STAT_PIRQ 0x00000004
+#define BITM_DMA_STAT_IRQERR 0x00000002
+#define BITM_DMA_STAT_IRQDONE 0x00000001
+
+#define DMA_MDMA_SRC_DEFAULT_CONFIG(psize, msize) \
+ (BITM_DMA_CFG_EN | ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE))
+#define DMA_MDMA_DST_DEFAULT_CONFIG(psize, msize) \
+ (BITM_DMA_CFG_EN | BITM_DMA_CFG_WNR | ENUM_DMA_CFG_XCNT_INT | \
+ ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE))
+
+struct adi_dma_channel {
+ int id;
+ struct adi_dma *dma;
+ void __iomem *iosrc;
+ void __iomem *iodest;
+};
+
+struct adi_dma {
+ struct udevice *dev;
+ struct adi_dma_channel channels[1];
+ void __iomem *ioaddr;
+ unsigned long hw_cfg;
+};
+
+static const struct udevice_id dma_dt_ids[] = {
+ { .compatible = "adi,mdma-controller", .data = HAS_MDMA },
+ { }
+};
+
+static u8 adi_dma_get_msize(u32 n_bytecount, u32 n_address)
+{
+ /* Calculate MSIZE, PSIZE, XCNT and XMOD */
+ u8 n_msize = 0;
+ u32 n_value = n_bytecount | n_address;
+ u32 n_mask = 0x1;
+
+ for (n_msize = 0; n_msize < 5; n_msize++, n_mask <<= 1) {
+ if ((n_value & n_mask) == n_mask)
+ break;
+ }
+
+ return n_msize;
+}
+
+static int adi_dma_get_ch_error(void __iomem *ch)
+{
+ u32 cause = (ioread32(ch + REG_STAT) & BITM_DMA_STAT_ERRC) >>
+ BITP_DMA_STAT_ERRC;
+ switch (cause) {
+ case 0:
+ return -EINVAL;
+ case 1:
+ return -EBUSY;
+ case 2:
+ return -EFAULT;
+ case 3:
+ fallthrough;
+ case 5:
+ fallthrough;
+ case 6:
+ fallthrough;
+ default:
+ return -EIO;
+ }
+}
+
+static int adi_mdma_transfer(struct udevice *dev, int direction,
+ dma_addr_t dst, dma_addr_t src, size_t len)
+{
+ struct adi_dma *priv = dev_get_priv(dev);
+ void __iomem *chsrc = priv->channels[0].iosrc;
+ void __iomem *chdst = priv->channels[0].iodest;
+
+ int result = 0;
+ u32 reg;
+ u32 bytecount = len;
+
+ u8 n_srcmsize;
+ u8 n_dstmsize;
+ u8 n_srcpsize;
+ u8 n_dstpsize;
+ u8 n_psize;
+ u32 srcconfig;
+ u32 dstconfig;
+ u8 srcpsizemax = (ioread32(chsrc + REG_STAT) & BITM_DMA_STAT_PBWID) >>
+ BITP_DMA_STAT_PBWID;
+ u8 dstpsizemax = (ioread32(chdst + REG_STAT) & BITM_DMA_STAT_PBWID) >>
+ BITP_DMA_STAT_PBWID;
+
+ const u32 CLRSTAT = (BITM_DMA_STAT_IRQDONE | BITM_DMA_STAT_IRQERR |
+ BITM_DMA_STAT_PIRQ);
+
+ if (len == 0)
+ return -EINVAL;
+
+ /* Clear DMA status */
+ iowrite32(CLRSTAT, chsrc + REG_STAT);
+ iowrite32(CLRSTAT, chdst + REG_STAT);
+
+ /* Calculate MSIZE, PSIZE, XCNT and XMOD */
+ n_srcmsize = adi_dma_get_msize(bytecount, src);
+ n_dstmsize = adi_dma_get_msize(bytecount, dst);
+ n_srcpsize = min(n_srcmsize, srcpsizemax);
+ n_dstpsize = min(n_dstmsize, dstpsizemax);
+ n_psize = min(n_srcpsize, n_dstpsize);
+
+ srcconfig = DMA_MDMA_SRC_DEFAULT_CONFIG(n_psize, n_srcmsize);
+ dstconfig = DMA_MDMA_DST_DEFAULT_CONFIG(n_psize, n_dstmsize);
+
+ /* Load the DMA descriptors */
+ iowrite32(src, chsrc + REG_ADDRSTART);
+ iowrite32(bytecount >> n_srcmsize, chsrc + REG_XCNT);
+ iowrite32(1 << n_srcmsize, chsrc + REG_XMOD);
+ iowrite32(dst, chdst + REG_ADDRSTART);
+ iowrite32(bytecount >> n_dstmsize, chdst + REG_XCNT);
+ iowrite32(1 << n_dstmsize, chdst + REG_XMOD);
+
+ iowrite32(dstconfig, chdst + REG_CFG);
+ iowrite32(srcconfig, chsrc + REG_CFG);
+
+ /* Wait for DMA to complete while checking for a DMA error */
+ do {
+ reg = ioread32(chsrc + REG_STAT);
+ if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) {
+ result = adi_dma_get_ch_error(chsrc);
+ break;
+ }
+ reg = ioread32(chdst + REG_STAT);
+ if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) {
+ result = adi_dma_get_ch_error(chdst);
+ break;
+ }
+ } while ((reg & BITM_DMA_STAT_IRQDONE) == 0);
+
+ clrbits_32(chsrc + REG_CFG, 1);
+ clrbits_32(chdst + REG_CFG, 1);
+
+ return result;
+}
+
+static int adi_dma_init_channel(struct adi_dma *dma,
+ struct adi_dma_channel *channel, ofnode node)
+{
+ u32 offset;
+
+ if (ofnode_read_u32(node, "adi,id", &channel->id)) {
+ dev_err(dma->dev, "Missing adi,id for channel %s\n",
+ ofnode_get_name(node));
+ return -ENOENT;
+ }
+
+ if (ofnode_read_u32(node, "adi,src-offset", &offset)) {
+ dev_err(dma->dev, "Missing adi,src-offset for channel %s\n",
+ ofnode_get_name(node));
+ return -ENOENT;
+ }
+
+ channel->iosrc = dma->ioaddr + offset;
+ channel->dma = dma;
+
+ if (dma->hw_cfg & HAS_MDMA) {
+ if (ofnode_read_u32(node, "adi,dest-offset", &offset)) {
+ dev_err(dma->dev,
+ "Missing adi,dest-offset for channel %s\n",
+ ofnode_get_name(node));
+ return -ENOENT;
+ }
+ channel->iodest = dma->ioaddr + offset;
+ }
+
+ return 0;
+}
+
+static int adi_dma_probe(struct udevice *dev)
+{
+ struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct adi_dma *priv = dev_get_priv(dev);
+ ofnode node, child;
+
+ priv->hw_cfg = dev_get_driver_data(dev);
+ if (priv->hw_cfg & HAS_MDMA)
+ uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM;
+
+ priv->ioaddr = dev_remap_addr(dev);
+ if (!priv->ioaddr)
+ return -EINVAL;
+
+ node = dev_read_first_subnode(dev);
+ if (!ofnode_valid(node)) {
+ dev_err(dev,
+ "Error: device tree DMA channel config missing!\n");
+ return -ENODEV;
+ }
+
+ node = dev_ofnode(dev);
+ ofnode_for_each_subnode(child, node) {
+ adi_dma_init_channel(priv, priv->channels, child);
+ break; //Only 1 channel supported for now
+ }
+
+ return 0;
+}
+
+static const struct dma_ops adi_dma_ops = {
+ .transfer = adi_mdma_transfer,
+};
+
+U_BOOT_DRIVER(adi_dma) = {
+ .name = "adi_dma",
+ .id = UCLASS_DMA,
+ .of_match = dma_dt_ids,
+ .ops = &adi_dma_ops,
+ .probe = adi_dma_probe,
+ .priv_auto = sizeof(struct adi_dma),
+};
diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
index 94ec13ba7ca..90c20a6a3fa 100644
--- a/drivers/dma/ti/Makefile
+++ b/drivers/dma/ti/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_TI_K3_PSIL) += k3-psil-data.o
k3-psil-data-y += k3-psil.o
k3-psil-data-$(CONFIG_SOC_K3_AM654) += k3-psil-am654.o
k3-psil-data-$(CONFIG_SOC_K3_J721E) += k3-psil-j721e.o
+k3-psil-data-$(CONFIG_SOC_K3_J7200) += k3-psil-j721e.o
k3-psil-data-$(CONFIG_SOC_K3_J721S2) += k3-psil-j721s2.o
k3-psil-data-$(CONFIG_SOC_K3_AM642) += k3-psil-am64.o
k3-psil-data-$(CONFIG_SOC_K3_AM625) += k3-psil-am62.o
diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c
index 369e679886f..39798844a8a 100644
--- a/drivers/dma/ti/k3-psil.c
+++ b/drivers/dma/ti/k3-psil.c
@@ -20,6 +20,8 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id)
soc_ep_map = &am654_ep_map;
else if (IS_ENABLED(CONFIG_SOC_K3_J721E))
soc_ep_map = &j721e_ep_map;
+ else if (IS_ENABLED(CONFIG_SOC_K3_J7200))
+ soc_ep_map = &j721e_ep_map;
else if (IS_ENABLED(CONFIG_SOC_K3_J721S2))
soc_ep_map = &j721s2_ep_map;
else if (IS_ENABLED(CONFIG_SOC_K3_AM642))
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 3013c4741d0..723265ab2e5 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -36,6 +36,7 @@
#include "k3-psil-priv.h"
#define K3_UDMA_MAX_RFLOWS 1024
+#define K3_UDMA_MAX_TR 2
struct udma_chan;
@@ -74,7 +75,6 @@ struct udma_tchan {
struct k3_nav_ring *t_ring; /* Transmit ring */
struct k3_nav_ring *tc_ring; /* Transmit Completion ring */
int tflow_id; /* applicable only for PKTDMA */
-
};
#define udma_bchan udma_tchan
@@ -175,6 +175,7 @@ struct udma_dev {
struct udma_rflow *rflows;
struct udma_match_data *match_data;
+ void *bc_desc;
struct udma_chan *channels;
u32 psil_base;
@@ -1349,6 +1350,7 @@ static int udma_setup_resources(struct udma_dev *ud)
struct ti_sci_resource_desc *rm_desc;
struct ti_sci_resource *rm_res;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+ size_t desc_size;
ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
@@ -1366,9 +1368,11 @@ static int udma_setup_resources(struct udma_dev *ud)
ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows),
GFP_KERNEL);
+ desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t));
+ ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL);
if (!ud->tchan_map || !ud->rchan_map || !ud->rflow_map ||
!ud->rflow_map_reserved || !ud->tchans || !ud->rchans ||
- !ud->rflows)
+ !ud->rflows || !ud->bc_desc)
return -ENOMEM;
/*
@@ -1444,6 +1448,7 @@ static int bcdma_setup_resources(struct udma_dev *ud)
struct ti_sci_resource_desc *rm_desc;
struct ti_sci_resource *rm_res;
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+ size_t desc_size;
ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
@@ -1460,9 +1465,12 @@ static int bcdma_setup_resources(struct udma_dev *ud)
ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows),
GFP_KERNEL);
+ desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t));
+ ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL);
+
if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map ||
!ud->bchans || !ud->tchans || !ud->rchans ||
- !ud->rflows)
+ !ud->rflows || !ud->bc_desc)
return -ENOMEM;
/* Get resource ranges from tisci */
@@ -1718,8 +1726,7 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest,
int num_tr;
size_t tr_size = sizeof(struct cppi5_tr_type15_t);
u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
- unsigned long dummy;
- void *tr_desc;
+ void *tr_desc = uc->ud->bc_desc;
size_t desc_size;
if (len < SZ_64K) {
@@ -1748,9 +1755,6 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest,
}
desc_size = cppi5_trdesc_calc_size(num_tr, tr_size);
- tr_desc = dma_alloc_coherent(desc_size, &dummy);
- if (!tr_desc)
- return NULL;
memset(tr_desc, 0, desc_size);
cppi5_trdesc_init(tr_desc, num_tr, tr_size, 0, 0);
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_command.c b/drivers/fastboot/fb_command.c
index e4484d65aca..2cdbac50ac4 100644
--- a/drivers/fastboot/fb_command.c
+++ b/drivers/fastboot/fb_command.c
@@ -186,6 +186,7 @@ void fastboot_multiresponse(int cmd, char *response)
}
break;
}
+ fallthrough;
default:
fastboot_fail("Unknown multiresponse command", response);
break;
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/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index 8c907c3b032..e6e43ae936a 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -427,14 +427,8 @@ static int scmi_bind_protocols(struct udevice *dev)
break;
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id)) {
- node = ofnode_find_subnode(node, "regulators");
- if (!ofnode_valid(node)) {
- dev_err(dev, "no regulators node\n");
- return -ENXIO;
- }
+ scmi_protocol_is_supported(dev, protocol_id))
drv = DM_DRIVER_GET(scmi_voltage_domain);
- }
break;
default:
break;
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/firmware/ti_sci_static_data.h b/drivers/firmware/ti_sci_static_data.h
index 3370f80231d..2e61e0c7de1 100644
--- a/drivers/firmware/ti_sci_static_data.h
+++ b/drivers/firmware/ti_sci_static_data.h
@@ -16,7 +16,7 @@ struct ti_sci_resource_static_data {
#if IS_ENABLED(CONFIG_K3_DM_FW)
-#if IS_ENABLED(CONFIG_SOC_K3_J721E)
+#if IS_ENABLED(CONFIG_SOC_K3_J721E) || IS_ENABLED(CONFIG_SOC_K3_J7200)
static struct ti_sci_resource_static_data rm_static_data[] = {
/* Free rings */
{
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/Kconfig b/drivers/gpio/Kconfig
index 21361f56e69..e11109fb56d 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -97,6 +97,15 @@ config SPL_DM_GPIO_LOOKUP_LABEL
different gpios on different hardware versions
for the same functionality in board code.
+config ADI_GPIO
+ bool "ADI GPIO driver"
+ depends on DM_GPIO && ARCH_SC5XX
+ help
+ This driver supports GPIO banks on SC5xx processors. It
+ supports inputs and outputs but does not support pin
+ interrupt functionality (PINT) or other features in the
+ Linux version of the driver.
+
config ALTERA_PIO
bool "Altera PIO driver"
depends on DM_GPIO
@@ -545,6 +554,14 @@ config DM_PCA953X
Now, max 24 bits chips and PCA953X compatible chips are
supported
+config ADP5588_GPIO
+ bool "ADP5588 GPIO expander driver"
+ depends on DM_GPIO && DM_I2C
+ help
+ Say yes here to support GPIO functionality of ADI ADP5588 chips.
+
+ The ADP5588 is an 18-port I2C GPIO expander and keypad controller.
+
config SPL_DM_PCA953X
bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports in SPL"
depends on SPL_DM_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 3f882c065d8..d64c14db5cf 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -10,8 +10,9 @@ 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
obj-$(CONFIG_ASPEED_G7_GPIO) += gpio-aspeed-g7.o
obj-$(CONFIG_ASPEED_SGPIO) += gpio-aspeed-sgpio.o
@@ -58,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
@@ -72,10 +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/adp5588_gpio.c b/drivers/gpio/adp5588_gpio.c
new file mode 100644
index 00000000000..d081e169897
--- /dev/null
+++ b/drivers/gpio/adp5588_gpio.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * GPIO Chip driver for Analog Devices
+ * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
+ *
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Based on Michael Hennerich's Linux driver:
+ * Michael Hennerich <michael.hennerich@analog.com>
+ *
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <asm-generic/gpio.h>
+
+#define ADP5588_MAXGPIO 18
+#define ADP5588_BANK(offs) ((offs) >> 3)
+#define ADP5588_BIT(offs) (1u << ((offs) & 0x7))
+
+#define DEV_ID 0x00 /* Device ID */
+#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */
+#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */
+#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */
+#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */
+#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */
+#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */
+#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */
+#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */
+#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */
+#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */
+#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */
+#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */
+#define GPIO_DIR1 0x23 /* GPIO Data Direction */
+#define GPIO_DIR2 0x24 /* GPIO Data Direction */
+#define GPIO_DIR3 0x25 /* GPIO Data Direction */
+#define GPIO_PULL1 0x2C /* GPIO Pull Disable */
+#define GPIO_PULL2 0x2D /* GPIO Pull Disable */
+#define GPIO_PULL3 0x2E /* GPIO Pull Disable */
+#define ID_MASK 0x0F
+
+struct adp5588_gpio {
+ u8 dat_out[3];
+ u8 dir[3];
+};
+
+static int adp5588_gpio_read(struct udevice *dev, u8 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = dm_i2c_read(dev, reg, &val, 1);
+
+ if (ret < 0) {
+ pr_err("%s: read error\n", __func__);
+ return ret;
+ }
+
+ return val;
+}
+
+static int adp5588_gpio_write(struct udevice *dev, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = dm_i2c_write(dev, reg, &val, 1);
+ if (ret < 0) {
+ pr_err("%s: write error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adp5588_get_value(struct udevice *dev, u32 offset)
+{
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+ unsigned int bank = ADP5588_BANK(offset);
+ unsigned int bit = ADP5588_BIT(offset);
+ int val;
+
+ if (plat->dir[bank] & bit)
+ val = plat->dat_out[bank];
+ else
+ val = adp5588_gpio_read(dev, GPIO_DAT_STAT1 + bank);
+
+ return !!(val & bit);
+}
+
+static int adp5588_set_value(struct udevice *dev, u32 offset,
+ int32_t value)
+{
+ unsigned int bank, bit;
+ int ret;
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+
+ bank = ADP5588_BANK(offset);
+ bit = ADP5588_BIT(offset);
+
+ if (value)
+ plat->dat_out[bank] |= bit;
+ else
+ plat->dat_out[bank] &= ~bit;
+
+ ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
+ plat->dat_out[bank]);
+
+ return ret;
+}
+
+static int adp5588_direction_input(struct udevice *dev, u32 offset)
+{
+ int ret;
+ unsigned int bank;
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+
+ bank = ADP5588_BANK(offset);
+
+ plat->dir[bank] &= ~ADP5588_BIT(offset);
+ ret = adp5588_gpio_write(dev, GPIO_DIR1 + bank, plat->dir[bank]);
+
+ return ret;
+}
+
+static int adp5588_direction_output(struct udevice *dev,
+ u32 offset, int value)
+{
+ int ret;
+ unsigned int bank, bit;
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+
+ bank = ADP5588_BANK(offset);
+ bit = ADP5588_BIT(offset);
+
+ plat->dir[bank] |= bit;
+
+ if (value)
+ plat->dat_out[bank] |= bit;
+ else
+ plat->dat_out[bank] &= ~bit;
+
+ ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
+ plat->dat_out[bank]);
+ ret |= adp5588_gpio_write(dev, GPIO_DIR1 + bank,
+ plat->dir[bank]);
+
+ return ret;
+}
+
+static int adp5588_ofdata_platdata(struct udevice *dev)
+{
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
+ int node = dev_of_offset(dev);
+ int ret, i, revid;
+
+ priv->gpio_count = ADP5588_MAXGPIO;
+ priv->bank_name = fdt_get_name(gd->fdt_blob, node, NULL);
+
+ ret = adp5588_gpio_read(dev, DEV_ID);
+ if (ret < 0)
+ return ret;
+
+ revid = ret & ID_MASK;
+
+ printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid);
+
+ for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
+ plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i);
+ plat->dir[i] = adp5588_gpio_read(dev, GPIO_DIR1 + i);
+ ret |= adp5588_gpio_write(dev, KP_GPIO1 + i, 0);
+ ret |= adp5588_gpio_write(dev, GPIO_PULL1 + i, 0);
+ ret |= adp5588_gpio_write(dev, GPIO_INT_EN1 + i, 0);
+ if (ret) {
+ pr_err("%s: Initialization error\n", __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct dm_gpio_ops adp5588_ops = {
+ .direction_input = adp5588_direction_input,
+ .direction_output = adp5588_direction_output,
+ .get_value = adp5588_get_value,
+ .set_value = adp5588_set_value,
+};
+
+static const struct udevice_id adp5588_of_match_list[] = {
+ { .compatible = "adi,adp5588"},
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(gpio_adp5588) = {
+ .name = "gpio_adp5588",
+ .id = UCLASS_GPIO,
+ .ops = &adp5588_ops,
+ .of_match = adp5588_of_match_list,
+ .of_to_plat = adp5588_ofdata_platdata,
+ .plat_auto = sizeof(struct adp5588_gpio),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c
index 6e632c8fc73..181c53bfe72 100644
--- a/drivers/gpio/axp_gpio.c
+++ b/drivers/gpio/axp_gpio.c
@@ -15,6 +15,9 @@
#include <errno.h>
#include <sunxi_gpio.h>
+#define AXP_GPIO_PREFIX "AXP0-"
+#define AXP_GPIO_COUNT 4
+
static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val);
static u8 axp_get_gpio_ctrl_reg(unsigned pin)
@@ -46,28 +49,14 @@ static int axp_gpio_direction_input(struct udevice *dev, unsigned pin)
static int axp_gpio_direction_output(struct udevice *dev, unsigned pin,
int val)
{
- __maybe_unused int ret;
u8 reg;
- switch (pin) {
-#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC
- /* Only available on later PMICs */
- case SUNXI_GPIO_AXP0_VBUS_ENABLE:
- ret = pmic_bus_clrbits(AXP_MISC_CTRL,
- AXP_MISC_CTRL_N_VBUSEN_FUNC);
- if (ret)
- return ret;
-
- return axp_gpio_set_value(dev, pin, val);
-#endif
- default:
- reg = axp_get_gpio_ctrl_reg(pin);
- if (reg == 0)
- return -EINVAL;
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
- return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH :
- AXP_GPIO_CTRL_OUTPUT_LOW);
- }
+ return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH :
+ AXP_GPIO_CTRL_OUTPUT_LOW);
}
static int axp_gpio_get_value(struct udevice *dev, unsigned pin)
@@ -75,25 +64,16 @@ static int axp_gpio_get_value(struct udevice *dev, unsigned pin)
u8 reg, val, mask;
int ret;
- switch (pin) {
-#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC
- /* Only available on later PMICs */
- case SUNXI_GPIO_AXP0_VBUS_ENABLE:
- ret = pmic_bus_read(AXP_VBUS_IPSOUT, &val);
- mask = AXP_VBUS_IPSOUT_DRIVEBUS;
- break;
-#endif
- default:
- reg = axp_get_gpio_ctrl_reg(pin);
- if (reg == 0)
- return -EINVAL;
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
- ret = pmic_bus_read(AXP_GPIO_STATE, &val);
- mask = 1 << (pin + AXP_GPIO_STATE_OFFSET);
- }
+ ret = pmic_bus_read(AXP_GPIO_STATE, &val);
if (ret)
return ret;
+ mask = 1 << (pin + AXP_GPIO_STATE_OFFSET);
+
return (val & mask) ? 1 : 0;
}
@@ -101,25 +81,12 @@ static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
u8 reg;
- switch (pin) {
-#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC
- /* Only available on later PMICs */
- case SUNXI_GPIO_AXP0_VBUS_ENABLE:
- if (val)
- return pmic_bus_setbits(AXP_VBUS_IPSOUT,
- AXP_VBUS_IPSOUT_DRIVEBUS);
- else
- return pmic_bus_clrbits(AXP_VBUS_IPSOUT,
- AXP_VBUS_IPSOUT_DRIVEBUS);
-#endif
- default:
- reg = axp_get_gpio_ctrl_reg(pin);
- if (reg == 0)
- return -EINVAL;
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
- return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH :
- AXP_GPIO_CTRL_OUTPUT_LOW);
- }
+ return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH :
+ AXP_GPIO_CTRL_OUTPUT_LOW);
}
static const struct dm_gpio_ops gpio_axp_ops = {
@@ -134,8 +101,8 @@ static int gpio_axp_probe(struct udevice *dev)
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
/* Tell the uclass how many GPIOs we have */
- uc_priv->bank_name = strdup(SUNXI_GPIO_AXP0_PREFIX);
- uc_priv->gpio_count = SUNXI_GPIO_AXP0_GPIO_COUNT;
+ uc_priv->bank_name = AXP_GPIO_PREFIX;
+ uc_priv->gpio_count = AXP_GPIO_COUNT;
return 0;
}
diff --git a/drivers/gpio/gpio-adi-adsp.c b/drivers/gpio/gpio-adi-adsp.c
new file mode 100644
index 00000000000..0ce00572e08
--- /dev/null
+++ b/drivers/gpio/gpio-adi-adsp.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <dm.h>
+#include <asm-generic/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#define ADSP_PORT_MMIO_SIZE 0x80
+#define ADSP_PORT_PIN_SIZE 16
+
+#define ADSP_PORT_REG_FER 0x00
+#define ADSP_PORT_REG_FER_SET 0x04
+#define ADSP_PORT_REG_FER_CLEAR 0x08
+#define ADSP_PORT_REG_DATA 0x0c
+#define ADSP_PORT_REG_DATA_SET 0x10
+#define ADSP_PORT_REG_DATA_CLEAR 0x14
+#define ADSP_PORT_REG_DIR 0x18
+#define ADSP_PORT_REG_DIR_SET 0x1c
+#define ADSP_PORT_REG_DIR_CLEAR 0x20
+#define ADSP_PORT_REG_INEN 0x24
+#define ADSP_PORT_REG_INEN_SET 0x28
+#define ADSP_PORT_REG_INEN_CLEAR 0x2c
+#define ADSP_PORT_REG_PORT_MUX 0x30
+#define ADSP_PORT_REG_DATA_TGL 0x34
+#define ADSP_PORT_REG_POLAR 0x38
+#define ADSP_PORT_REG_POLAR_SET 0x3c
+#define ADSP_PORT_REG_POLAR_CLEAR 0x40
+#define ADSP_PORT_REG_LOCK 0x44
+#define ADSP_PORT_REG_TRIG_TGL 0x48
+
+struct adsp_gpio_priv {
+ void __iomem *base;
+ int ngpio;
+};
+
+static u32 get_port(unsigned int pin)
+{
+ return pin / ADSP_PORT_PIN_SIZE;
+}
+
+static u32 get_offset(unsigned int pin)
+{
+ return pin % ADSP_PORT_PIN_SIZE;
+}
+
+static int adsp_gpio_input(struct udevice *udev, unsigned int pin)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR);
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_CLEAR);
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_SET);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int adsp_gpio_output(struct udevice *udev, unsigned int pin, int value)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR);
+
+ if (value)
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET);
+ else
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR);
+
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_SET);
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_CLEAR);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int adsp_gpio_get_value(struct udevice *udev, unsigned int pin)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ u16 val;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ val = ioread16(portbase + ADSP_PORT_REG_DATA);
+ return !!(val & BIT(offset));
+ }
+
+ return 0;
+}
+
+static int adsp_gpio_set_value(struct udevice *udev, unsigned int pin, int value)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ if (value)
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET);
+ else
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR);
+ }
+
+ return 0;
+}
+
+static const struct dm_gpio_ops adsp_gpio_ops = {
+ .direction_input = adsp_gpio_input,
+ .direction_output = adsp_gpio_output,
+ .get_value = adsp_gpio_get_value,
+ .set_value = adsp_gpio_set_value,
+};
+
+static int adsp_gpio_probe(struct udevice *udev)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+ uc_priv->bank_name = "adsp gpio";
+ uc_priv->gpio_count = dev_read_u32_default(udev, "adi,ngpios", 0);
+
+ if (!uc_priv->gpio_count) {
+ dev_err(udev, "Missing adi,ngpios property!\n");
+ return -ENOENT;
+ }
+
+ priv->base = dev_read_addr_ptr(udev);
+ priv->ngpio = uc_priv->gpio_count;
+
+ return 0;
+}
+
+static const struct udevice_id adsp_gpio_match[] = {
+ { .compatible = "adi,adsp-gpio" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_adsp_gpio) = {
+ .name = "adi_adsp_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &adsp_gpio_ops,
+ .probe = adsp_gpio_probe,
+ .priv_auto = sizeof(struct adsp_gpio_priv),
+ .of_match = adsp_gpio_match,
+ .flags = DM_FLAG_PRE_RELOC,
+};
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/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
index e84038f312e..523ca8473a8 100644
--- a/drivers/gpio/pca953x_gpio.c
+++ b/drivers/gpio/pca953x_gpio.c
@@ -393,6 +393,8 @@ static const struct udevice_id pca953x_ids[] = {
{ .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
{ .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+ { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
{ .compatible = "maxim,max7310", .data = OF_953X(8, 0), },
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index 2ca4960f17a..094c45a6927 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -244,17 +244,7 @@ int sunxi_name_to_gpio(const char *name)
int sunxi_name_to_gpio(const char *name)
{
unsigned int gpio;
- int ret;
-#if !defined CONFIG_XPL_BUILD && defined CONFIG_AXP_GPIO
- char lookup[8];
-
- if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) {
- sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d",
- SUNXI_GPIO_AXP0_VBUS_ENABLE);
- name = lookup;
- }
-#endif
- ret = gpio_lookup_name(name, NULL, NULL, &gpio);
+ int ret = gpio_lookup_name(name, NULL, NULL, &gpio);
return ret ? ret : gpio;
}
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index cdae6825736..146bc621c7e 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -154,6 +154,13 @@ config SPL_DM_I2C_GPIO
bindings are supported.
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
+config SYS_I2C_ADI
+ bool "ADI I2C driver"
+ depends on DM_I2C && ARCH_SC5XX
+ help
+ Add support for the ADI (Analog Devices) I2C driver as used
+ in SC57X, SC58X, SC59X, SC59X_64.
+
config SYS_I2C_AT91
bool "Atmel I2C driver"
depends on DM_I2C && ARCH_AT91
@@ -503,6 +510,15 @@ config SYS_I2C_OMAP24XX
help
Add support for the OMAP2+ I2C driver.
+config SYS_I2C_OMAP24XX_REPEATED_START
+ bool "Enable I2C repeated start"
+ depends on SYS_I2C_OMAP24XX
+ default y if ARCH_K3
+ help
+ Enable support for repeated start. Updates driver defaults to not
+ send a Stop condition and issue Repeated Start (Sr) for subsequent
+ i2c msgs.
+
config SYS_I2C_RCAR_I2C
bool "Renesas R-Car I2C driver"
depends on (RCAR_GEN2 || RCAR_64) && DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index bebd728e7da..42326db85f6 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -2,15 +2,16 @@
#
# (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
obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o
@@ -57,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/i2c/adi_i2c.c b/drivers/i2c/adi_i2c.c
new file mode 100644
index 00000000000..4cddcfa6b7f
--- /dev/null
+++ b/drivers/i2c/adi_i2c.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Converted to driver model by Nathan Barrett-Morrison
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <i2c.h>
+#include <mapmem.h>
+#include <linux/io.h>
+
+#define CLKLOW(x) ((x) & 0xFF) // Periods Clock Is Held Low
+#define CLKHI(y) (((y) & 0xFF) << 0x8) // Periods Clock Is High
+
+#define PRESCALE 0x007F // SCLKs Per Internal Time Reference (10MHz)
+#define TWI_ENA 0x0080 // TWI Enable
+#define SCCB 0x0200 // SCCB Compatibility Enable
+
+#define SEN 0x0001 // Slave Enable
+#define SADD_LEN 0x0002 // Slave Address Length
+#define STDVAL 0x0004 // Slave Transmit Data Valid
+#define TSC_NAK 0x0008 // NAK Generated At Conclusion Of Transfer
+#define GEN 0x0010 // General Call Adrress Matching Enabled
+
+#define SDIR 0x0001 // Slave Transfer Direction
+#define GCALL 0x0002 // General Call Indicator
+
+#define MEN 0x0001 // Master Mode Enable
+#define MADD_LEN 0x0002 // Master Address Length
+#define MDIR 0x0004 // Master Transmit Direction (RX/TX*)
+#define FAST 0x0008 // Use Fast Mode Timing Specs
+#define STOP 0x0010 // Issue Stop Condition
+#define RSTART 0x0020 // Repeat Start or Stop* At End Of Transfer
+#define DCNT 0x3FC0 // Data Bytes To Transfer
+#define SDAOVR 0x4000 // Serial Data Override
+#define SCLOVR 0x8000 // Serial Clock Override
+
+#define MPROG 0x0001 // Master Transfer In Progress
+#define LOSTARB 0x0002 // Lost Arbitration Indicator (Xfer Aborted)
+#define ANAK 0x0004 // Address Not Acknowledged
+#define DNAK 0x0008 // Data Not Acknowledged
+#define BUFRDERR 0x0010 // Buffer Read Error
+#define BUFWRERR 0x0020 // Buffer Write Error
+#define SDASEN 0x0040 // Serial Data Sense
+#define SCLSEN 0x0080 // Serial Clock Sense
+#define BUSBUSY 0x0100 // Bus Busy Indicator
+
+#define SINIT 0x0001 // Slave Transfer Initiated
+#define SCOMP 0x0002 // Slave Transfer Complete
+#define SERR 0x0004 // Slave Transfer Error
+#define SOVF 0x0008 // Slave Overflow
+#define MCOMP 0x0010 // Master Transfer Complete
+#define MERR 0x0020 // Master Transfer Error
+#define XMTSERV 0x0040 // Transmit FIFO Service
+#define RCVSERV 0x0080 // Receive FIFO Service
+
+#define XMTFLUSH 0x0001 // Transmit Buffer Flush
+#define RCVFLUSH 0x0002 // Receive Buffer Flush
+#define XMTINTLEN 0x0004 // Transmit Buffer Interrupt Length
+#define RCVINTLEN 0x0008 // Receive Buffer Interrupt Length
+
+#define XMTSTAT 0x0003 // Transmit FIFO Status
+#define XMT_EMPTY 0x0000 // Transmit FIFO Empty
+#define XMT_HALF 0x0001 // Transmit FIFO Has 1 Byte To Write
+#define XMT_FULL 0x0003 // Transmit FIFO Full (2 Bytes To Write)
+
+#define RCVSTAT 0x000C // Receive FIFO Status
+#define RCV_EMPTY 0x0000 // Receive FIFO Empty
+#define RCV_HALF 0x0004 // Receive FIFO Has 1 Byte To Read
+#define RCV_FULL 0x000C // Receive FIFO Full (2 Bytes To Read)
+
+/* Every register is 32bit aligned, but only 16bits in size */
+#define ureg(name) u16 name; u16 __pad_##name
+
+struct twi_regs {
+ ureg(clkdiv);
+ ureg(control);
+ ureg(slave_ctl);
+ ureg(slave_stat);
+ ureg(slave_addr);
+ ureg(master_ctl);
+ ureg(master_stat);
+ ureg(master_addr);
+ ureg(int_stat);
+ ureg(int_mask);
+ ureg(fifo_ctl);
+ ureg(fifo_stat);
+ u8 __pad[0x50];
+
+ ureg(xmt_data8);
+ ureg(xmt_data16);
+ ureg(rcv_data8);
+ ureg(rcv_data16);
+};
+
+#undef ureg
+
+/*
+ * The way speed is changed into duty often results in integer truncation
+ * with 50% duty, so we'll force rounding up to the next duty by adding 1
+ * to the max. In practice this will get us a speed of something like
+ * 385 KHz. The other limit is easy to handle as it is only 8 bits.
+ */
+#define I2C_SPEED_MAX 400000
+#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed))
+#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1)
+#define I2C_DUTY_MIN 0xff /* 8 bit limited */
+
+#define I2C_M_COMBO 0x4
+#define I2C_M_STOP 0x2
+#define I2C_M_READ 0x1
+
+/*
+ * All transfers are described by this data structure
+ */
+struct adi_i2c_msg {
+ u8 flags;
+ u32 len; /* msg length */
+ u8 *buf; /* pointer to msg data */
+ u32 olen; /* addr length */
+ u8 *obuf; /* addr buffer */
+};
+
+struct adi_i2c_dev {
+ struct twi_regs __iomem *base;
+ u32 i2c_clk;
+ uint speed;
+};
+
+/* Allow msec timeout per ~byte transfer */
+#define I2C_TIMEOUT 10
+
+/**
+ * wait_for_completion - manage the actual i2c transfer
+ * @msg: the i2c msg
+ */
+static int wait_for_completion(struct twi_regs *twi, struct adi_i2c_msg *msg)
+{
+ u16 int_stat;
+ ulong timebase = get_timer(0);
+
+ do {
+ int_stat = ioread16(&twi->int_stat);
+
+ if (int_stat & XMTSERV) {
+ iowrite16(XMTSERV, &twi->int_stat);
+ if (msg->olen) {
+ iowrite16(*(msg->obuf++), &twi->xmt_data8);
+ --msg->olen;
+ } else if (!(msg->flags & I2C_M_COMBO) && msg->len) {
+ iowrite16(*(msg->buf++), &twi->xmt_data8);
+ --msg->len;
+ } else {
+ if (msg->flags & I2C_M_COMBO)
+ setbits_16(&twi->master_ctl, RSTART | MDIR);
+ else
+ setbits_16(&twi->master_ctl, STOP);
+ }
+ }
+ if (int_stat & RCVSERV) {
+ iowrite16(RCVSERV, &twi->int_stat);
+ if (msg->len) {
+ *(msg->buf++) = ioread16(&twi->rcv_data8);
+ --msg->len;
+ } else if (msg->flags & I2C_M_STOP) {
+ setbits_16(&twi->master_ctl, STOP);
+ }
+ }
+ if (int_stat & MERR) {
+ pr_err("%s: master transmit terror: %d\n", __func__,
+ ioread16(&twi->master_stat));
+ iowrite16(MERR, &twi->int_stat);
+ return -EIO;
+ }
+ if (int_stat & MCOMP) {
+ iowrite16(MCOMP, &twi->int_stat);
+ if (msg->flags & I2C_M_COMBO && msg->len) {
+ u16 mlen = min(msg->len, 0xffu) << 6;
+ clrsetbits_16(&twi->master_ctl, RSTART, mlen | MEN | MDIR);
+ } else {
+ break;
+ }
+ }
+
+ /* If we were able to do something, reset timeout */
+ if (int_stat)
+ timebase = get_timer(0);
+
+ } while (get_timer(timebase) < I2C_TIMEOUT);
+
+ return 0;
+}
+
+static int i2c_transfer(struct twi_regs *twi, u8 chip, u8 *offset,
+ int olen, u8 *buffer, int len, u8 flags)
+{
+ int ret;
+ u16 ctl;
+
+ struct adi_i2c_msg msg = {
+ .flags = flags | (len >= 0xff ? I2C_M_STOP : 0),
+ .buf = buffer,
+ .len = len,
+ .obuf = offset,
+ .olen = olen,
+ };
+
+ /* wait for things to settle */
+ while (ioread16(&twi->master_stat) & BUSBUSY)
+ if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc())
+ return -EINTR;
+
+ /* Set Transmit device address */
+ iowrite16(chip, &twi->master_addr);
+
+ /* Clear the FIFO before starting things */
+ iowrite16(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl);
+ iowrite16(0, &twi->fifo_ctl);
+
+ /* Prime the pump */
+ if (msg.olen) {
+ len = (msg.flags & I2C_M_COMBO) ? msg.olen : msg.olen + len;
+ iowrite16(*(msg.obuf++), &twi->xmt_data8);
+ --msg.olen;
+ } else if (!(msg.flags & I2C_M_READ) && msg.len) {
+ iowrite16(*(msg.buf++), &twi->xmt_data8);
+ --msg.len;
+ }
+
+ /* clear int stat */
+ iowrite16(-1, &twi->master_stat);
+ iowrite16(-1, &twi->int_stat);
+ iowrite16(0, &twi->int_mask);
+
+ /* Master enable */
+ ctl = ioread16(&twi->master_ctl);
+ ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN |
+ ((msg.flags & I2C_M_READ) ? MDIR : 0);
+ iowrite16(ctl, &twi->master_ctl);
+
+ /* Process the rest */
+ ret = wait_for_completion(twi, &msg);
+
+ clrbits_16(&twi->master_ctl, MEN);
+ clrbits_16(&twi->control, TWI_ENA);
+ setbits_16(&twi->control, TWI_ENA);
+ return ret;
+}
+
+static int adi_i2c_read(struct twi_regs *twi, u8 chip,
+ u8 *offset, int olen, u8 *buffer, int len)
+{
+ return i2c_transfer(twi, chip, offset, olen, buffer,
+ len, olen ? I2C_M_COMBO : I2C_M_READ);
+}
+
+static int adi_i2c_write(struct twi_regs *twi, u8 chip,
+ u8 *offset, int olen, u8 *buffer, int len)
+{
+ return i2c_transfer(twi, chip, offset, olen, buffer, len, 0);
+}
+
+static int adi_i2c_set_bus_speed(struct udevice *bus, uint speed)
+{
+ struct adi_i2c_dev *dev = dev_get_priv(bus);
+ struct twi_regs *twi = dev->base;
+ u16 clkdiv = I2C_SPEED_TO_DUTY(speed);
+
+ /* Set TWI interface clock */
+ if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN)
+ return -1;
+ clkdiv = (clkdiv << 8) | (clkdiv & 0xff);
+ iowrite16(clkdiv, &twi->clkdiv);
+
+ /* Don't turn it on */
+ iowrite16(speed > 100000 ? FAST : 0, &twi->master_ctl);
+
+ return 0;
+}
+
+static int adi_i2c_of_to_plat(struct udevice *bus)
+{
+ struct adi_i2c_dev *dev = dev_get_priv(bus);
+ struct clk clock;
+ u32 ret;
+
+ dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct twi_regs));
+
+ if (!dev->base)
+ return -ENOMEM;
+
+ dev->speed = dev_read_u32_default(bus, "clock-frequency",
+ I2C_SPEED_FAST_RATE);
+
+ ret = clk_get_by_name(bus, "i2c", &clock);
+ if (ret < 0)
+ printf("%s: Can't get I2C clk: %d\n", __func__, ret);
+ else
+ dev->i2c_clk = clk_get_rate(&clock);
+
+ return 0;
+}
+
+static int adi_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
+ u32 chip_flags)
+{
+ struct adi_i2c_dev *dev = dev_get_priv(bus);
+ u8 byte;
+
+ return adi_i2c_read(dev->base, chip_addr, NULL, 0, &byte, 1);
+}
+
+static int adi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+ struct adi_i2c_dev *dev = dev_get_priv(bus);
+ struct i2c_msg *dmsg, *omsg, dummy;
+
+ memset(&dummy, 0, sizeof(struct i2c_msg));
+
+ /*
+ * We expect either two messages (one with an offset and one with the
+ * actual data) or one message (just data)
+ */
+ if (nmsgs > 2 || nmsgs == 0) {
+ debug("%s: Only one or two messages are supported.", __func__);
+ return -EINVAL;
+ }
+
+ omsg = nmsgs == 1 ? &dummy : msg;
+ dmsg = nmsgs == 1 ? msg : msg + 1;
+
+ if (dmsg->flags & I2C_M_RD)
+ return adi_i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len,
+ dmsg->buf, dmsg->len);
+ else
+ return adi_i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len,
+ dmsg->buf, dmsg->len);
+}
+
+int adi_i2c_probe(struct udevice *bus)
+{
+ struct adi_i2c_dev *dev = dev_get_priv(bus);
+ struct twi_regs *twi = dev->base;
+
+ u16 prescale = ((dev->i2c_clk / 1000 / 1000 + 5) / 10) & 0x7F;
+
+ /* Set TWI internal clock as 10MHz */
+ iowrite16(prescale, &twi->control);
+
+ /* Set TWI interface clock as specified */
+ adi_i2c_set_bus_speed(bus, dev->speed);
+
+ /* Enable it */
+ iowrite16(TWI_ENA | prescale, &twi->control);
+
+ return 0;
+}
+
+static const struct dm_i2c_ops adi_i2c_ops = {
+ .xfer = adi_i2c_xfer,
+ .probe_chip = adi_i2c_probe_chip,
+ .set_bus_speed = adi_i2c_set_bus_speed,
+};
+
+static const struct udevice_id adi_i2c_ids[] = {
+ { .compatible = "adi-i2c", },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(i2c_adi) = {
+ .name = "i2c_adi",
+ .id = UCLASS_I2C,
+ .of_match = adi_i2c_ids,
+ .probe = adi_i2c_probe,
+ .of_to_plat = adi_i2c_of_to_plat,
+ .priv_auto = sizeof(struct adi_i2c_dev),
+ .ops = &adi_i2c_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/i2c/mtk_i2c.c b/drivers/i2c/mtk_i2c.c
index 3450177741a..55381dbeced 100644
--- a/drivers/i2c/mtk_i2c.c
+++ b/drivers/i2c/mtk_i2c.c
@@ -143,7 +143,6 @@ static const uint mt_i2c_regs_v1[] = {
[REG_RSV_DEBUG] = 0x44,
[REG_HS] = 0x48,
[REG_SOFTRESET] = 0x50,
- [REG_SOFTRESET] = 0x50,
[REG_DCM_EN] = 0x54,
[REG_DEBUGSTAT] = 0x64,
[REG_DEBUGCTRL] = 0x68,
@@ -879,7 +878,7 @@ static const struct udevice_id mtk_i2c_ids[] = {
}, {
.compatible = "mediatek,mt8518-i2c",
.data = (ulong)&mt8518_soc_data,
- }
+ }, {}
};
U_BOOT_DRIVER(mtk_i2c) = {
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index ebe472e20cd..a6361d3d17d 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -535,12 +535,16 @@ pr_exit:
return res;
}
+#if !CONFIG_IS_ENABLED(DM_I2C)
+/*
+ * The legacy I2C functions. These need to get removed once
+ * all users of this driver are converted to DM.
+ */
+
/*
* i2c_read: Function now uses a single I2C read transaction with bulk transfer
* of the requested number of bytes (note that the 'i2c md' command
- * limits this to 16 bytes anyway). If CONFIG_I2C_REPEATED_START is
- * defined in the board config header, this transaction shall be with
- * Repeated Start (Sr) between the address and data phases; otherwise
+ * limits this to 16 bytes anyway).
* Stop-Start (P-S) shall be used (some I2C chips do require a P-S).
* The address (reg offset) may be 0, 1 or 2 bytes long.
* Function now reads correctly from chips that return more than one
@@ -608,16 +612,10 @@ static int __omap24_i2c_read(void __iomem *i2c_base, int ip_rev, int waitdelay,
if (alen) {
/* Must write reg offset first */
-#ifdef CONFIG_I2C_REPEATED_START
- /* No stop bit, use Repeated Start (Sr) */
- omap_i2c_write_reg(i2c_base, ip_rev, I2C_CON_EN | I2C_CON_MST |
- I2C_CON_STT | I2C_CON_TRX, OMAP_I2C_CON_REG);
-#else
/* Stop - Start (P-S) */
omap_i2c_write_reg(i2c_base, ip_rev, I2C_CON_EN | I2C_CON_MST |
I2C_CON_STT | I2C_CON_STP | I2C_CON_TRX,
OMAP_I2C_CON_REG);
-#endif
/* Send register offset */
while (1) {
status = wait_for_event(i2c_base, ip_rev, waitdelay);
@@ -836,11 +834,6 @@ wr_exit:
return i2c_error;
}
-#if !CONFIG_IS_ENABLED(DM_I2C)
-/*
- * The legacy I2C functions. These need to get removed once
- * all users of this driver are converted to DM.
- */
static void __iomem *omap24_get_base(struct i2c_adapter *adap)
{
switch (adap->hwadapnr) {
@@ -971,28 +964,140 @@ U_BOOT_I2C_ADAP_COMPLETE(omap24_4, omap24_i2c_init, omap24_i2c_probe,
#else /* CONFIG_DM_I2C */
+static int __omap24_i2c_xfer_msg(void __iomem *i2c_base, int ip_rev, int waitdelay,
+ uchar chip, uchar *buffer, int len, u16 i2c_con_reg)
+{
+ int i;
+ u16 status;
+ int i2c_error = 0;
+ int timeout = I2C_TIMEOUT;
+
+ if (len < 0) {
+ printf("%s: data len < 0\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!buffer) {
+ printf("%s: NULL pointer passed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!(i2c_con_reg & I2C_CON_EN)) {
+ printf("%s: I2C_CON_EN not set\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Set slave address */
+ omap_i2c_write_reg(i2c_base, ip_rev, chip, OMAP_I2C_SA_REG);
+ /* Read/Write len bytes data */
+ omap_i2c_write_reg(i2c_base, ip_rev, len, OMAP_I2C_CNT_REG);
+ /* Configure the I2C_CON register */
+ omap_i2c_write_reg(i2c_base, ip_rev, i2c_con_reg, OMAP_I2C_CON_REG);
+
+ /* read/write data bytewise */
+ for (i = 0; i < len; i++) {
+ status = wait_for_event(i2c_base, ip_rev, waitdelay);
+ /* Ignore I2C_STAT_RRDY in transmitter mode */
+ if (i2c_con_reg & I2C_CON_TRX)
+ status &= ~I2C_STAT_RRDY;
+ else
+ status &= ~I2C_STAT_XRDY;
+
+ /* Try to identify bus that is not padconf'd for I2C */
+ if (status == I2C_STAT_XRDY) {
+ i2c_error = -EREMOTEIO;
+ printf("%s: pads on bus probably not configured (status=0x%x)\n",
+ __func__, status);
+ goto xfer_exit;
+ }
+ if (status == 0 || (status & I2C_STAT_NACK)) {
+ i2c_error = -EREMOTEIO;
+ printf("%s: error waiting for ACK (status=0x%x)\n",
+ __func__, status);
+ goto xfer_exit;
+ }
+ if (status & I2C_STAT_XRDY) {
+ /* Transmit data */
+ omap_i2c_write_reg(i2c_base, ip_rev,
+ buffer[i], OMAP_I2C_DATA_REG);
+ omap_i2c_write_reg(i2c_base, ip_rev,
+ I2C_STAT_XRDY, OMAP_I2C_STAT_REG);
+ }
+ if (status & I2C_STAT_RRDY) {
+ /* Receive data */
+ *buffer++ = omap_i2c_read_reg(i2c_base, ip_rev,
+ OMAP_I2C_DATA_REG);
+ omap_i2c_write_reg(i2c_base, ip_rev,
+ I2C_STAT_RRDY, OMAP_I2C_STAT_REG);
+ }
+ }
+
+ /*
+ * poll ARDY bit for making sure that last byte really has been
+ * transferred on the bus.
+ */
+ do {
+ status = wait_for_event(i2c_base, ip_rev, waitdelay);
+ } while (!(status & I2C_STAT_ARDY) && timeout--);
+ if (timeout <= 0) {
+ printf("%s: timed out on last byte!\n", __func__);
+ i2c_error = -EREMOTEIO;
+ goto xfer_exit;
+ } else {
+ omap_i2c_write_reg(i2c_base, ip_rev, I2C_STAT_ARDY, OMAP_I2C_STAT_REG);
+ }
+
+ /* If Stop bit set, flush FIFO. */
+ if (i2c_con_reg & I2C_CON_STP)
+ goto xfer_exit;
+
+ return 0;
+
+xfer_exit:
+ flush_fifo(i2c_base, ip_rev);
+ omap_i2c_write_reg(i2c_base, ip_rev, 0xFFFF, OMAP_I2C_STAT_REG);
+ return i2c_error;
+}
+
static int omap_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
{
struct omap_i2c *priv = dev_get_priv(bus);
int ret;
+ u16 i2c_con_reg = 0;
- debug("i2c_xfer: %d messages\n", nmsgs);
- for (; nmsgs > 0; nmsgs--, msg++) {
- debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
- if (msg->flags & I2C_M_RD) {
- ret = __omap24_i2c_read(priv->regs, priv->ip_rev,
- priv->waitdelay,
- msg->addr, 0, 0, msg->buf,
- msg->len);
- } else {
- ret = __omap24_i2c_write(priv->regs, priv->ip_rev,
- priv->waitdelay,
- msg->addr, 0, 0, msg->buf,
- msg->len);
- }
+ debug("%s: %d messages\n", __func__, nmsgs);
+ for (int i = 0; i < nmsgs; i++, msg++) {
+ /*
+ * If previous msg sent a Stop or if this is the first msg
+ * Wait until bus not busy
+ */
+ if ((i2c_con_reg & I2C_CON_STP) || (i == 0))
+ if (wait_for_bb(priv->regs, priv->ip_rev, priv->waitdelay))
+ return -EREMOTEIO;
+
+ /* Set Controller mode with Start bit */
+ i2c_con_reg = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT;
+ /* Set Transmitter/Receiver mode if it is a write/read msg */
+ if (msg->flags & I2C_M_RD)
+ i2c_con_reg &= ~I2C_CON_TRX;
+ else
+ i2c_con_reg |= I2C_CON_TRX;
+ /* Send Stop condition (P) by default */
+ if (!IS_ENABLED(CONFIG_SYS_I2C_OMAP24XX_REPEATED_START))
+ i2c_con_reg |= I2C_CON_STP;
+ /* Send Stop if explicitly requested or if this is the last msg */
+ if ((msg->flags & I2C_M_STOP) || (i == nmsgs - 1))
+ i2c_con_reg |= I2C_CON_STP;
+
+ debug("%s: chip=0x%x, len=0x%x, i2c_con_reg=0x%x\n",
+ __func__, msg->addr, msg->len, i2c_con_reg);
+
+ ret = __omap24_i2c_xfer_msg(priv->regs, priv->ip_rev, priv->waitdelay,
+ msg->addr, msg->buf, msg->len,
+ i2c_con_reg);
if (ret) {
- debug("i2c_write: error sending\n");
- return -EREMOTEIO;
+ printf("%s: errored out at msg %d: %d\n", __func__, i, ret);
+ return ret;
}
}
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/Kconfig b/drivers/mailbox/Kconfig
index 67d5ac1a742..4d9f004ebad 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -21,6 +21,13 @@ config APPLE_MBOX
such as the System Management Controller (SMC) and NVMe and this
driver is required to get that functionality up and running.
+config IMX_MU_MBOX
+ bool "Enable i.MX MU MBOX support"
+ depends on DM_MAILBOX
+ help
+ Enable support for i.MX Messaging Unit for communication with other
+ processors on the SoC using mailbox interface
+
config SANDBOX_MBOX
bool "Enable the sandbox mailbox test driver"
depends on DM_MAILBOX && SANDBOX
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 6072fa1956b..e8c745f7d79 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -3,8 +3,9 @@
# 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
obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o
obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
new file mode 100644
index 00000000000..b1e0465e7a8
--- /dev/null
+++ b/drivers/mailbox/imx-mailbox.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <mailbox-uclass.h>
+#include <linux/bitfield.h>
+#include <linux/bug.h>
+#include <linux/iopoll.h>
+#include <linux/compat.h>
+
+/* This driver only exposes the status bits to keep with the
+ * polling methodology of u-boot.
+ */
+DECLARE_GLOBAL_DATA_PTR;
+
+#define IMX_MU_CHANS 24
+
+#define IMX_MU_V2_PAR_OFF 0x4
+#define IMX_MU_V2_TR_MASK GENMASK(7, 0)
+#define IMX_MU_V2_RR_MASK GENMASK(15, 8)
+
+enum imx_mu_chan_type {
+ IMX_MU_TYPE_TX = 0, /* Tx */
+ IMX_MU_TYPE_RX = 1, /* Rx */
+ IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */
+ IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */
+ IMX_MU_TYPE_RST = 4, /* Reset */
+ IMX_MU_TYPE_TXDB_V2 = 5, /* Tx doorbell with S/W ACK */
+};
+
+enum imx_mu_xcr {
+ IMX_MU_CR,
+ IMX_MU_GIER,
+ IMX_MU_GCR,
+ IMX_MU_TCR,
+ IMX_MU_RCR,
+ IMX_MU_xCR_MAX,
+};
+
+enum imx_mu_xsr {
+ IMX_MU_SR,
+ IMX_MU_GSR,
+ IMX_MU_TSR,
+ IMX_MU_RSR,
+ IMX_MU_xSR_MAX,
+};
+
+struct imx_mu_con_priv {
+ unsigned int idx;
+ enum imx_mu_chan_type type;
+ struct mbox_chan *chan;
+};
+
+enum imx_mu_type {
+ IMX_MU_V1,
+ IMX_MU_V2 = BIT(1),
+ IMX_MU_V2_S4 = BIT(15),
+ IMX_MU_V2_IRQ = BIT(16),
+};
+
+struct imx_mu {
+ void __iomem *base;
+ const struct imx_mu_dcfg *dcfg;
+ u32 num_tr;
+ u32 num_rr;
+ /* use pointers to channel as a way to reserve channels */
+ struct mbox_chan *channels[IMX_MU_CHANS];
+ struct imx_mu_con_priv con_priv[IMX_MU_CHANS];
+};
+
+struct imx_mu_dcfg {
+ int (*tx)(struct imx_mu *plat, struct imx_mu_con_priv *cp, const void *data);
+ int (*rx)(struct imx_mu *plat, struct imx_mu_con_priv *cp);
+ int (*rxdb)(struct imx_mu *plat, struct imx_mu_con_priv *cp);
+ int (*init)(struct imx_mu *plat);
+ int (*of_xlate)(struct mbox_chan *chan, struct ofnode_phandle_args *args);
+ enum imx_mu_type type;
+ u32 xTR; /* Transmit Register0 */
+ u32 xRR; /* Receive Register0 */
+ u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */
+ u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */
+};
+
+#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
+#define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
+#define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
+
+/* General Purpose Interrupt Enable */
+#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
+/* Receive Interrupt Enable */
+#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x))))
+/* Transmit Interrupt Enable */
+#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x))))
+/* General Purpose Interrupt Request */
+#define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x))))
+/* MU reset */
+#define IMX_MU_xCR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(5))
+#define IMX_MU_xSR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(7))
+
+static void imx_mu_write(struct imx_mu *plat, u32 val, u32 offs)
+{
+ iowrite32(val, plat->base + offs);
+}
+
+static u32 imx_mu_read(struct imx_mu *plat, u32 offs)
+{
+ return ioread32(plat->base + offs);
+}
+
+static u32 imx_mu_xcr_rmw(struct imx_mu *plat, enum imx_mu_xcr type, u32 set, u32 clr)
+{
+ u32 val;
+
+ val = imx_mu_read(plat, plat->dcfg->xCR[type]);
+ val &= ~clr;
+ val |= set;
+ imx_mu_write(plat, val, plat->dcfg->xCR[type]);
+
+ return val;
+}
+
+/* check that the channel is open or owned by caller */
+static int imx_mu_check_channel(struct mbox_chan *chan)
+{
+ struct imx_mu *plat = dev_get_plat(chan->dev);
+
+ if (plat->channels[chan->id]) {
+ /* if reserved check that caller owns */
+ if (plat->channels[chan->id] == chan)
+ return 1; /* caller owns the channel */
+
+ return -EACCES;
+ }
+
+ return 0; /* channel empty */
+}
+
+static int imx_mu_chan_request(struct mbox_chan *chan)
+{
+ struct imx_mu *plat = dev_get_plat(chan->dev);
+ struct imx_mu_con_priv *cp;
+ enum imx_mu_chan_type type;
+ int idx;
+
+ type = chan->id / 4;
+ idx = chan->id % 4;
+
+ if (imx_mu_check_channel(chan) < 0) /* check if channel already in use */
+ return -EPERM;
+
+ plat->channels[chan->id] = chan;
+ chan->con_priv = kcalloc(1, sizeof(struct imx_mu_con_priv), 0);
+ if (!chan->con_priv)
+ return -ENOMEM;
+ cp = chan->con_priv;
+ cp->idx = idx;
+ cp->type = type;
+ cp->chan = chan;
+
+ switch (type) {
+ case IMX_MU_TYPE_RX:
+ imx_mu_xcr_rmw(plat, IMX_MU_RCR, IMX_MU_xCR_RIEn(plat->dcfg->type, idx), 0);
+ break;
+ case IMX_MU_TYPE_TXDB_V2:
+ case IMX_MU_TYPE_TXDB:
+ case IMX_MU_TYPE_RXDB:
+ imx_mu_xcr_rmw(plat, IMX_MU_GIER, IMX_MU_xCR_GIEn(plat->dcfg->type, idx), 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int imx_mu_chan_free(struct mbox_chan *chan)
+{
+ struct imx_mu *plat = dev_get_plat(chan->dev);
+ struct imx_mu_con_priv *cp = chan->con_priv;
+
+ if (imx_mu_check_channel(chan) <= 0) /* check that the channel is also not empty */
+ return -EINVAL;
+
+ /* if you own channel and channel is NOT empty */
+ plat->channels[chan->id] = NULL;
+ switch (cp->type) {
+ case IMX_MU_TYPE_TX:
+ imx_mu_xcr_rmw(plat, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(plat->dcfg->type, cp->idx));
+ break;
+ case IMX_MU_TYPE_RX:
+ imx_mu_xcr_rmw(plat, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(plat->dcfg->type, cp->idx));
+ break;
+ case IMX_MU_TYPE_TXDB_V2:
+ case IMX_MU_TYPE_TXDB:
+ case IMX_MU_TYPE_RXDB:
+ imx_mu_xcr_rmw(plat, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx));
+ break;
+ default:
+ break;
+ }
+
+ kfree(cp);
+
+ return 0;
+}
+
+static int imx_mu_send(struct mbox_chan *chan, const void *data)
+{
+ struct imx_mu *plat = dev_get_plat(chan->dev);
+ struct imx_mu_con_priv *cp = chan->con_priv;
+
+ if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */
+ return -EPERM;
+
+ return plat->dcfg->tx(plat, cp, data);
+}
+
+static int imx_mu_recv(struct mbox_chan *chan, void *data)
+{
+ struct imx_mu *plat = dev_get_plat(chan->dev);
+ struct imx_mu_con_priv *cp = chan->con_priv;
+ u32 ctrl, val;
+
+ if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */
+ return -EPERM;
+
+ switch (cp->type) {
+ case IMX_MU_TYPE_TXDB_V2:
+ case IMX_MU_TYPE_RXDB:
+ /* check if GSR[GIRn] bit is set */
+ if (readx_poll_timeout(ioread32, plat->base + plat->dcfg->xSR[IMX_MU_GSR],
+ val, val & BIT(cp->idx), 1000000) < 0)
+ return -EBUSY;
+
+ ctrl = imx_mu_read(plat, plat->dcfg->xCR[IMX_MU_GIER]);
+ val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]);
+ val &= IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx) &
+ (ctrl & IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx));
+ break;
+ default:
+ dev_warn(chan->dev, "Unhandled channel type %d\n", cp->type);
+ return -EOPNOTSUPP;
+ };
+
+ if (val == IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx))
+ plat->dcfg->rxdb(plat, cp);
+
+ return 0;
+}
+
+static int imx_mu_of_to_plat(struct udevice *dev)
+{
+ struct imx_mu *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -ENODEV;
+
+ plat->base = (struct mu_type *)addr;
+
+ return 0;
+}
+
+static int imx_mu_init_generic(struct imx_mu *plat)
+{
+ unsigned int i;
+ unsigned int val;
+
+ if (plat->num_rr > 4 || plat->num_tr > 4) {
+ WARN_ONCE(true, "%s not support TR/RR larger than 4\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ /* Set default MU configuration */
+ for (i = 0; i < IMX_MU_xCR_MAX; i++)
+ imx_mu_write(plat, 0, plat->dcfg->xCR[i]);
+
+ /* Clear any pending GIP */
+ val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]);
+ imx_mu_write(plat, val, plat->dcfg->xSR[IMX_MU_GSR]);
+
+ /* Clear any pending RSR */
+ for (i = 0; i < plat->num_rr; i++)
+ imx_mu_read(plat, plat->dcfg->xRR + i * 4);
+
+ return 0;
+}
+
+static int imx_mu_generic_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args)
+{
+ enum imx_mu_chan_type type;
+ int idx, cid;
+
+ if (args->args_count != 2) {
+ dev_err(chan->dev, "Invalid argument count %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ type = args->args[0]; /* channel type */
+ idx = args->args[1]; /* index */
+
+ cid = type * 4 + idx;
+ if (cid >= IMX_MU_CHANS) {
+ dev_err(chan->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n",
+ cid, type, idx);
+ return -EINVAL;
+ }
+
+ chan->id = cid;
+
+ return 0;
+}
+
+static int imx_mu_generic_tx(struct imx_mu *plat, struct imx_mu_con_priv *cp,
+ const void *data)
+{
+ switch (cp->type) {
+ case IMX_MU_TYPE_TXDB_V2:
+ imx_mu_xcr_rmw(plat, IMX_MU_GCR, IMX_MU_xCR_GIRn(plat->dcfg->type, cp->idx), 0);
+ break;
+ default:
+ dev_warn(cp->chan->dev, "Send data on wrong channel type: %d\n", cp->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int imx_mu_generic_rxdb(struct imx_mu *plat, struct imx_mu_con_priv *cp)
+{
+ imx_mu_write(plat, IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx),
+ plat->dcfg->xSR[IMX_MU_GSR]);
+
+ return 0;
+}
+
+static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
+ .tx = imx_mu_generic_tx,
+ .rxdb = imx_mu_generic_rxdb,
+ .init = imx_mu_init_generic,
+ .of_xlate = imx_mu_generic_of_xlate,
+ .type = IMX_MU_V1,
+ .xTR = 0x0,
+ .xRR = 0x10,
+ .xSR = {0x20, 0x20, 0x20, 0x20},
+ .xCR = {0x24, 0x24, 0x24, 0x24, 0x24},
+};
+
+static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
+ .tx = imx_mu_generic_tx,
+ .rxdb = imx_mu_generic_rxdb,
+ .init = imx_mu_init_generic,
+ .of_xlate = imx_mu_generic_of_xlate,
+ .type = IMX_MU_V1,
+ .xTR = 0x20,
+ .xRR = 0x40,
+ .xSR = {0x60, 0x60, 0x60, 0x60},
+ .xCR = {0x64, 0x64, 0x64, 0x64, 0x64},
+};
+
+static const struct imx_mu_dcfg imx_mu_cfg_imx95 = {
+ .tx = imx_mu_generic_tx,
+ .rxdb = imx_mu_generic_rxdb,
+ .init = imx_mu_init_generic,
+ .of_xlate = imx_mu_generic_of_xlate,
+ .type = IMX_MU_V2,
+ .xTR = 0x200,
+ .xRR = 0x280,
+ .xSR = {0xC, 0x118, 0x124, 0x12C},
+ .xCR = {0x8, 0x110, 0x114, 0x120, 0x128},
+};
+
+static const struct udevice_id ids[] = {
+ { .compatible = "fsl,imx6sx-mu", .data = (ulong)&imx_mu_cfg_imx6sx },
+ { .compatible = "fsl,imx7ulp-mu", .data = (ulong)&imx_mu_cfg_imx7ulp },
+ { .compatible = "fsl,imx95-mu", .data = (ulong)&imx_mu_cfg_imx95 },
+ { }
+};
+
+int imx_mu_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args)
+{
+ struct imx_mu *plat = dev_get_plat(chan->dev);
+
+ return plat->dcfg->of_xlate(chan, args);
+}
+
+struct mbox_ops imx_mu_ops = {
+ .of_xlate = imx_mu_of_xlate,
+ .request = imx_mu_chan_request,
+ .rfree = imx_mu_chan_free,
+ .send = imx_mu_send,
+ .recv = imx_mu_recv,
+};
+
+static void imx_mu_get_tr_rr(struct imx_mu *plat)
+{
+ u32 val;
+
+ if (plat->dcfg->type & IMX_MU_V2) {
+ val = imx_mu_read(plat, IMX_MU_V2_PAR_OFF);
+ plat->num_tr = FIELD_GET(IMX_MU_V2_TR_MASK, val);
+ plat->num_rr = FIELD_GET(IMX_MU_V2_RR_MASK, val);
+ } else {
+ plat->num_tr = 4;
+ plat->num_rr = 4;
+ }
+}
+
+static int imx_mu_probe(struct udevice *dev)
+{
+ struct imx_mu *plat = dev_get_plat(dev);
+ int ret;
+
+ debug("%s(dev=%p)\n", __func__, dev);
+
+ plat->dcfg = (void *)dev_get_driver_data(dev);
+
+ imx_mu_get_tr_rr(plat);
+
+ ret = plat->dcfg->init(plat);
+ if (ret) {
+ dev_err(dev, "Failed to init MU\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(imx_mu) = {
+ .name = "imx-mu",
+ .id = UCLASS_MAILBOX,
+ .of_match = ids,
+ .of_to_plat = imx_mu_of_to_plat,
+ .plat_auto = sizeof(struct imx_mu),
+ .probe = imx_mu_probe,
+ .ops = &imx_mu_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c
index 713d93a200c..851aa737c03 100644
--- a/drivers/mailbox/zynqmp-ipi.c
+++ b/drivers/mailbox/zynqmp-ipi.c
@@ -270,5 +270,4 @@ U_BOOT_DRIVER(zynqmp_ipi) = {
.id = UCLASS_NOP,
.of_match = zynqmp_ipi_ids,
.probe = zynqmp_ipi_probe,
- .flags = DM_FLAG_PROBE_AFTER_BIND,
};
diff --git a/drivers/memory/ti-gpmc.c b/drivers/memory/ti-gpmc.c
index e979c431e33..29e02f12ae0 100644
--- a/drivers/memory/ti-gpmc.c
+++ b/drivers/memory/ti-gpmc.c
@@ -1242,4 +1242,5 @@ U_BOOT_DRIVER(ti_gpmc) = {
.of_match = gpmc_dt_ids,
.probe = gpmc_probe,
.flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .priv_auto = sizeof(struct ti_gpmc),
};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index da84b35e804..0911d2fc0cc 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -468,6 +468,13 @@ config STM32MP_FUSE
for STM32MP architecture.
This API is needed for CMD_FUSE.
+config K3_FUSE
+ bool "Enable TI K3 fuse wrapper providing the fuse API"
+ depends on MISC && CMD_FUSE && CMD_FUSE_WRITEBUFF
+ help
+ If you say Y here, you will get support for the fuse API (OTP)
+ for TI K3 architecture.
+
config STM32_RCC
bool "Enable RCC driver for the STM32 SoC's family"
depends on (ARCH_STM32 || ARCH_STM32MP) && MISC
@@ -569,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 dac805e4cdd..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,29 +37,30 @@ 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/
+obj-$(CONFIG_K3_FUSE) += k3_fuse.o
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/misc/k3_fuse.c b/drivers/misc/k3_fuse.c
new file mode 100644
index 00000000000..4a8ff1f2523
--- /dev/null
+++ b/drivers/misc/k3_fuse.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Texas Instruments Incorporated, <www.ti.com>
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <fuse.h>
+#include <linux/arm-smccc.h>
+#include <string.h>
+
+#define K3_SIP_OTP_WRITEBUFF 0xC2000000
+#define K3_SIP_OTP_WRITE 0xC2000001
+#define K3_SIP_OTP_READ 0xC2000002
+
+int fuse_read(u32 bank, u32 word, u32 *val)
+{
+ struct arm_smccc_res res;
+
+ if (bank != 0U) {
+ printf("Invalid bank argument, ONLY bank 0 is supported\n");
+ return -EINVAL;
+ }
+
+ /* Make SiP SMC call and send the word in the parameter register */
+ arm_smccc_smc(K3_SIP_OTP_READ, word,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ *val = res.a1;
+ if (res.a0 != 0)
+ printf("SMC call failed: Error code %lu\n", res.a0);
+
+ return res.a0;
+}
+
+int fuse_sense(u32 bank, u32 word, u32 *val)
+{
+ return -EPERM;
+}
+
+int fuse_prog(u32 bank, u32 word, u32 val)
+{
+ struct arm_smccc_res res;
+ u32 mask = val;
+
+ if (bank != 0U) {
+ printf("Invalid bank argument, ONLY bank 0 is supported\n");
+ return -EINVAL;
+ }
+
+ /* Make SiP SMC call and send the word, val and mask in the parameter register */
+ arm_smccc_smc(K3_SIP_OTP_WRITE, word,
+ val, mask, 0, 0, 0, 0, &res);
+
+ if (res.a0 != 0)
+ printf("SMC call failed: Error code %lu\n", res.a0);
+
+ return res.a0;
+}
+
+int fuse_override(u32 bank, u32 word, u32 val)
+{
+ return -EPERM;
+}
+
+int fuse_writebuff(ulong addr)
+{
+ struct arm_smccc_res res;
+
+ /* Make SiP SMC call and send the addr in the parameter register */
+ arm_smccc_smc(K3_SIP_OTP_WRITEBUFF, (unsigned long)addr,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ if (res.a0 != 0)
+ printf("SMC call failed: Error code %lu\n", res.a0);
+
+ return res.a0;
+}
diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c
index 0d0cf764689..c6c052ac6c3 100644
--- a/drivers/misc/qfw_acpi.c
+++ b/drivers/misc/qfw_acpi.c
@@ -25,17 +25,18 @@ DECLARE_GLOBAL_DATA_PTR;
*
* @entry : BIOS linker command entry which tells where to allocate memory
* (either high memory or low memory)
- * @addr : The address that should be used for low memory allcation. If the
+ * @addr : The address that should be used for low memory allocation. If the
* memory allocation request is 'ZONE_HIGH' then this parameter will
* be ignored.
* @return: 0 on success, or negative value on failure
*/
-static int bios_linker_allocate(struct udevice *dev,
+static int bios_linker_allocate(struct acpi_ctx *ctx, struct udevice *dev,
struct bios_linker_entry *entry, ulong *addr)
{
uint32_t size, align;
struct fw_file *file;
unsigned long aligned_addr;
+ struct acpi_rsdp *rsdp;
align = le32_to_cpu(entry->alloc.align);
/* align must be power of 2 */
@@ -58,7 +59,9 @@ static int bios_linker_allocate(struct udevice *dev,
* If allocation zone is ZONE_FSEG, then we use the 'addr' passed
* in which is low memory
*/
- if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
+ if (IS_ENABLED(CONFIG_BLOBLIST_TABLES)) {
+ aligned_addr = ALIGN(*addr, align);
+ } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
aligned_addr = (unsigned long)memalign(align, size);
if (!aligned_addr) {
printf("error: allocating resource\n");
@@ -83,8 +86,13 @@ static int bios_linker_allocate(struct udevice *dev,
(void *)aligned_addr);
file->addr = aligned_addr;
+ rsdp = (void *)aligned_addr;
+ if (!strncmp(rsdp->signature, RSDP_SIG, sizeof(rsdp->signature)))
+ ctx->rsdp = rsdp;
+
/* adjust address for low memory allocation */
- if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
+ if (IS_ENABLED(CONFIG_BLOBLIST_TABLES) ||
+ entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
*addr = (aligned_addr + size);
return 0;
@@ -209,19 +217,23 @@ ulong write_acpi_tables(ulong addr)
qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader);
for (i = 0; i < (size / sizeof(*entry)); i++) {
+ log_content("entry %d: addr %lx\n", i, addr);
entry = table_loader + i;
switch (le32_to_cpu(entry->command)) {
case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
- ret = bios_linker_allocate(dev, entry, &addr);
+ log_content(" - %s\n", entry->alloc.file);
+ ret = bios_linker_allocate(ctx, dev, entry, &addr);
if (ret)
goto out;
break;
case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
+ log_content(" - %s\n", entry->pointer.src_file);
ret = bios_linker_add_pointer(dev, entry);
if (ret)
goto out;
break;
case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
+ log_content(" - %s\n", entry->cksum.file);
ret = bios_linker_add_checksum(dev, entry);
if (ret)
goto out;
@@ -246,6 +258,16 @@ out:
free(table_loader);
+ if (!ctx->rsdp) {
+ printf("error: no RSDP found\n");
+ return addr;
+ }
+ struct acpi_rsdp *rsdp = ctx->rsdp;
+
+ rsdp->length = sizeof(*rsdp);
+ rsdp->xsdt_address = 0;
+ rsdp->ext_checksum = table_compute_checksum((u8 *)rsdp, sizeof(*rsdp));
+
gd_set_acpi_start(acpi_get_rsdp_addr());
return addr;
@@ -305,7 +327,7 @@ static int evt_write_acpi_tables(void)
/* Generate ACPI tables */
end = write_acpi_tables(addr);
gd->arch.table_start = addr;
- gd->arch.table_end = addr;
+ gd->arch.table_end = end;
return 0;
}
diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c
index 2123c31038f..46820425a84 100644
--- a/drivers/misc/rockchip-otp.c
+++ b/drivers/misc/rockchip-otp.c
@@ -361,6 +361,13 @@ static const struct rockchip_otp_data rk3568_data = {
.block_size = 2,
};
+static const struct rockchip_otp_data rk3576_data = {
+ .read = rockchip_rk3588_otp_read,
+ .offset = 0x700,
+ .size = 0x100,
+ .block_size = 4,
+};
+
static const struct rockchip_otp_data rk3588_data = {
.read = rockchip_rk3588_otp_read,
.offset = 0xC00,
@@ -384,10 +391,18 @@ static const struct udevice_id rockchip_otp_ids[] = {
.data = (ulong)&px30_data,
},
{
+ .compatible = "rockchip,rk3528-otp",
+ .data = (ulong)&rk3568_data,
+ },
+ {
.compatible = "rockchip,rk3568-otp",
.data = (ulong)&rk3568_data,
},
{
+ .compatible = "rockchip,rk3576-otp",
+ .data = (ulong)&rk3576_data,
+ },
+ {
.compatible = "rockchip,rk3588-otp",
.data = (ulong)&rk3588_data,
},
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 4827834b4aa..38867f30a7e 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -292,6 +292,15 @@ config MMC_DW_ROCKCHIP
SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well
as removeable SD and micro-SD cards.
+config MMC_SDHCI_ADI
+ bool "ADI SD/MMC controller support"
+ depends on ARCH_SC5XX
+ depends on DM_MMC && OF_CONTROL
+ depends on MMC_SDHCI && MMC_SDHCI_ADMA
+ help
+ This enables support for the SD/MMC controller included in some Analog
+ Devices SC5XX Socs.
+
config MMC_DW_SOCFPGA
bool "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
depends on ARCH_SOCFPGA
@@ -519,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
@@ -528,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
@@ -860,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.
@@ -919,6 +930,12 @@ config ESDHC_DETECT_QUIRK
bool "QIXIS-based eSDHC quirk detection"
depends on FSL_ESDHC && FSL_QIXIS
+config ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE
+ bool
+ depends on FSL_ESDHC || FSL_ESDHC_IMX
+ def_bool y if ARCH_T1024 || ARCH_T1040 || ARCH_T1042 || ARCH_T2080 \
+ || FSL_ESDHC_IMX
+
config FSL_ESDHC_IMX
bool "Freescale/NXP i.MX eSDHC controller support"
help
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 90e76f90769..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
@@ -70,6 +70,7 @@ obj-$(CONFIG_MMC_SDHCI_MV) += mv_sdhci.o
obj-$(CONFIG_MMC_SDHCI_NPCM) += npcm_sdhci.o
obj-$(CONFIG_MMC_SDHCI_PIC32) += pic32_sdhci.o
obj-$(CONFIG_MMC_SDHCI_ROCKCHIP) += rockchip_sdhci.o
+obj-$(CONFIG_MMC_SDHCI_ADI) += adi_sdhci.o
obj-$(CONFIG_MMC_SDHCI_S5P) += s5p_sdhci.o
obj-$(CONFIG_MMC_SDHCI_SNPS) += snps_sdhci.o
obj-$(CONFIG_MMC_SDHCI_STI) += sti_sdhci.o
diff --git a/drivers/mmc/adi_sdhci.c b/drivers/mmc/adi_sdhci.c
new file mode 100644
index 00000000000..65a22cefb71
--- /dev/null
+++ b/drivers/mmc/adi_sdhci.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Based on Rockchip's sdhci.c file
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <malloc.h>
+#include <sdhci.h>
+#include <asm/cache.h>
+
+/* 400KHz is max freq for card ID etc. Use that as min */
+#define EMMC_MIN_FREQ 400000
+
+/* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */
+#define ADMA_BOUNDARY_ALGN SZ_128M
+#define BOUNDARY_OK(addr, len) \
+ (((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \
+ (ADMA_BOUNDARY_ALGN - 1)))
+
+/* We split a descriptor for every crossing of the ADMA alignment boundary,
+ * so we need an additional descriptor for every expected crossing.
+ * As I understand it, the max expected transaction size is:
+ * CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN
+ *
+ * With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a
+ * clean power of two, we'd only ever need +1 descriptor as the first
+ * descriptor that got split would then bring the remaining DMA
+ * destination addresses into alignment. Unfortunately, it's currently
+ * hardcoded to a non-power-of-two value.
+ *
+ * If that ever becomes parameterized, ADMA max length can be set to
+ * 0x10000, and set this to 1.
+ */
+#define ADMA_POTENTIAL_CROSSINGS \
+ DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \
+ ADMA_BOUNDARY_ALGN)
+/* +1 descriptor for each crossing.
+ */
+#define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN)
+
+struct adi_sdhc_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+};
+
+void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
+ dma_addr_t addr, int len, bool end)
+{
+ int tmplen, offset;
+
+ if (likely(!len || BOUNDARY_OK(addr, len))) {
+ sdhci_adma_write_desc(host, desc, addr, len, end);
+ return;
+ }
+
+ offset = addr & (ADMA_BOUNDARY_ALGN - 1);
+ tmplen = ADMA_BOUNDARY_ALGN - offset;
+ sdhci_adma_write_desc(host, desc, addr, tmplen, false);
+
+ addr += tmplen;
+ len -= tmplen;
+ sdhci_adma_write_desc(host, desc, addr, len, end);
+}
+
+struct sdhci_ops adi_dwcmshc_sdhci_ops = {
+ .adma_write_desc = adi_dwcmshc_adma_write_desc,
+};
+
+static int adi_dwcmshc_sdhci_probe(struct udevice *dev)
+{
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct adi_sdhc_plat *plat = dev_get_plat(dev);
+ struct sdhci_host *host = dev_get_priv(dev);
+ int max_frequency, ret;
+ struct clk clk;
+
+ max_frequency = dev_read_u32_default(dev, "max-frequency", 0);
+ ret = clk_get_by_index(dev, 0, &clk);
+
+ host->quirks = 0;
+ host->max_clk = max_frequency;
+ /*
+ * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg
+ * doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't
+ * check for other bus-width values.
+ */
+ if (host->bus_width == 8)
+ host->host_caps |= MMC_MODE_8BIT;
+
+ host->mmc = &plat->mmc;
+ host->mmc->priv = host;
+ host->mmc->dev = dev;
+ upriv->mmc = host->mmc;
+
+ host->ops = &adi_dwcmshc_sdhci_ops;
+ host->adma_desc_table = memalign(ARCH_DMA_MINALIGN,
+ ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ);
+ host->adma_addr = virt_to_phys(host->adma_desc_table);
+
+ ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ);
+ if (ret)
+ return ret;
+
+ return sdhci_probe(dev);
+}
+
+static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev)
+{
+ struct sdhci_host *host = dev_get_priv(dev);
+
+ host->name = dev->name;
+ host->ioaddr = dev_read_addr_ptr(dev);
+ host->bus_width = dev_read_u32_default(dev, "bus-width", 4);
+
+ return 0;
+}
+
+static int adi_sdhci_bind(struct udevice *dev)
+{
+ struct adi_sdhc_plat *plat = dev_get_plat(dev);
+
+ return sdhci_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static const struct udevice_id adi_dwcmshc_sdhci_ids[] = {
+ { .compatible = "adi,dwc-sdhci" },
+ { }
+};
+
+U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = {
+ .name = "adi_sdhci",
+ .id = UCLASS_MMC,
+ .of_match = adi_dwcmshc_sdhci_ids,
+ .of_to_plat = adi_dwcmshc_sdhci_of_to_plat,
+ .ops = &sdhci_ops,
+ .bind = adi_sdhci_bind,
+ .probe = adi_dwcmshc_sdhci_probe,
+ .priv_auto = sizeof(struct sdhci_host),
+ .plat_auto = sizeof(struct adi_sdhc_plat),
+};
diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index b4c60a48d2e..0df3568f073 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -125,12 +125,15 @@ static const struct timing_data td[] = {
[MMC_LEGACY] = {"ti,otap-del-sel-legacy",
"ti,itap-del-sel-legacy",
0},
- [MMC_HS] = {"ti,otap-del-sel-mmc-hs",
- "ti,itap-del-sel-mms-hs",
+ [MMC_HS] = {"ti,otap-del-sel-mmc-hs26",
+ "ti,itap-del-sel-mmc-hs26",
MMC_CAP(MMC_HS)},
[SD_HS] = {"ti,otap-del-sel-sd-hs",
"ti,itap-del-sel-sd-hs",
MMC_CAP(SD_HS)},
+ [MMC_HS_52] = {"ti,otap-del-sel-mmc-hs",
+ "ti,itap-del-sel-mmc-hs",
+ MMC_CAP(MMC_HS_52)},
[UHS_SDR12] = {"ti,otap-del-sel-sdr12",
"ti,itap-del-sel-sdr12",
MMC_CAP(UHS_SDR12)},
@@ -409,8 +412,7 @@ static void am654_sdhci_write_b(struct sdhci_host *host, u8 val, int reg)
*/
case SD_HS:
case MMC_HS:
- case UHS_SDR12:
- case UHS_SDR25:
+ case MMC_HS_52:
val &= ~SDHCI_CTRL_HISPD;
default:
break;
@@ -521,13 +523,24 @@ static int am654_sdhci_execute_tuning(struct mmc *mmc, u8 opcode)
return 0;
}
#endif
+
+void am654_sdhci_set_control_reg(struct sdhci_host *host)
+{
+ struct mmc *mmc = host->mmc;
+
+ sdhci_set_voltage(host);
+
+ if (mmc->selected_mode > MMC_HS_52)
+ sdhci_set_uhs_timing(host);
+}
+
const struct sdhci_ops am654_sdhci_ops = {
#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING)
.platform_execute_tuning = am654_sdhci_execute_tuning,
#endif
.deferred_probe = am654_sdhci_deferred_probe,
.set_ios_post = &am654_sdhci_set_ios_post,
- .set_control_reg = sdhci_set_control_reg,
+ .set_control_reg = am654_sdhci_set_control_reg,
.write_b = am654_sdhci_write_b,
};
@@ -587,7 +600,7 @@ const struct sdhci_ops j721e_4bit_sdhci_ops = {
#endif
.deferred_probe = am654_sdhci_deferred_probe,
.set_ios_post = &j721e_4bit_sdhci_set_ios_post,
- .set_control_reg = sdhci_set_control_reg,
+ .set_control_reg = am654_sdhci_set_control_reg,
.write_b = am654_sdhci_write_b,
};
diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c
index d7a45ef0ad0..926113f79d3 100644
--- a/drivers/mmc/fsl_esdhc_imx.c
+++ b/drivers/mmc/fsl_esdhc_imx.c
@@ -40,12 +40,6 @@
#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
-#ifndef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE
-#ifdef CONFIG_FSL_USDHC
-#define ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE 1
-#endif
-#endif
-
DECLARE_GLOBAL_DATA_PTR;
#define SDHCI_IRQ_EN_BITS (IRQSTATEN_CC | IRQSTATEN_TC | \
@@ -376,7 +370,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc,
(timeout == 4 || timeout == 8 || timeout == 12))
timeout++;
- if (IS_ENABLED(ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE))
+ if (IS_ENABLED(CONFIG_ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE))
timeout = 0xE;
esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index c8db4f811c2..2f4dc5bd887 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -83,6 +83,19 @@ int mmc_wait_dat0(struct mmc *mmc, int state, int timeout_us)
return dm_mmc_wait_dat0(mmc->dev, state, timeout_us);
}
+void dm_mmc_send_init_stream(struct udevice *dev)
+{
+ struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+ if (ops->send_init_stream)
+ ops->send_init_stream(dev);
+}
+
+void mmc_send_init_stream(struct mmc *mmc)
+{
+ dm_mmc_send_init_stream(mmc->dev);
+}
+
static int dm_mmc_get_wp(struct udevice *dev)
{
struct dm_mmc_ops *ops = mmc_get_ops(dev);
@@ -498,22 +511,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 +536,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 +545,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..cdcf2e0c8fe 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -1663,6 +1663,10 @@ static int mmc_execute_tuning(struct mmc *mmc, uint opcode)
}
#endif
+static void mmc_send_init_stream(struct mmc *mmc)
+{
+}
+
static int mmc_set_ios(struct mmc *mmc)
{
int ret = 0;
@@ -2550,7 +2554,7 @@ static int mmc_startup(struct mmc *mmc)
/*
* For MMC cards, set the Relative Address.
- * For SD cards, get the Relatvie Address.
+ * For SD cards, get the Relative Address.
* This also puts the cards into Standby State
*/
if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
@@ -2929,6 +2933,8 @@ int mmc_get_op_cond(struct mmc *mmc, bool quiet)
retry:
mmc_set_initial_state(mmc);
+ mmc_send_init_stream(mmc);
+
/* Reset the Card */
err = mmc_go_idle(mmc);
@@ -3040,9 +3046,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 +3079,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 +3098,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/msm_sdhci.c b/drivers/mmc/msm_sdhci.c
index 27bb7052fca..ac77fb06bf7 100644
--- a/drivers/mmc/msm_sdhci.c
+++ b/drivers/mmc/msm_sdhci.c
@@ -10,6 +10,7 @@
#include <clk.h>
#include <dm.h>
#include <malloc.h>
+#include <reset.h>
#include <sdhci.h>
#include <wait_bit.h>
#include <asm/global_data.h>
@@ -153,9 +154,18 @@ static int msm_sdc_probe(struct udevice *dev)
const struct msm_sdhc_variant_info *var_info;
struct sdhci_host *host = &prv->host;
u32 core_version, core_minor, core_major;
+ struct reset_ctl bcr_rst;
u32 caps;
int ret;
+ ret = reset_get_by_index(dev, 0, &bcr_rst);
+ if (!ret) {
+ reset_assert(&bcr_rst);
+ udelay(200);
+ reset_deassert(&bcr_rst);
+ udelay(200);
+ }
+
host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B;
host->max_clk = 0;
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index e66ab25d02a..92bc72b267c 100644
--- a/drivers/mmc/omap_hsmmc.c
+++ b/drivers/mmc/omap_hsmmc.c
@@ -780,6 +780,14 @@ tuning_error:
return ret;
}
#endif
+
+static void omap_hsmmc_send_init_stream(struct udevice *dev)
+{
+ struct omap_hsmmc_data *priv = dev_get_priv(dev);
+ struct hsmmc *mmc_base = priv->base_addr;
+
+ mmc_init_stream(mmc_base);
+}
#endif
static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd)
@@ -1515,9 +1523,10 @@ static const struct dm_mmc_ops omap_hsmmc_ops = {
.get_wp = omap_hsmmc_getwp,
#endif
#if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING)
- .execute_tuning = omap_hsmmc_execute_tuning,
+ .execute_tuning = omap_hsmmc_execute_tuning,
#endif
- .wait_dat0 = omap_hsmmc_wait_dat0,
+ .send_init_stream = omap_hsmmc_send_init_stream,
+ .wait_dat0 = omap_hsmmc_wait_dat0,
};
#else
static const struct mmc_ops omap_hsmmc_ops = {
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
index 422b8f7e4c8..7a72abaa38a 100644
--- a/drivers/mmc/rockchip_dw_mmc.c
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -171,6 +171,7 @@ static int rockchip_dwmmc_bind(struct udevice *dev)
static const struct udevice_id rockchip_dwmmc_ids[] = {
{ .compatible = "rockchip,rk2928-dw-mshc" },
{ .compatible = "rockchip,rk3288-dw-mshc" },
+ { .compatible = "rockchip,rk3576-dw-mshc" },
{ }
};
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c
index da630b9d97a..761e3619329 100644
--- a/drivers/mmc/rockchip_sdhci.c
+++ b/drivers/mmc/rockchip_sdhci.c
@@ -50,6 +50,10 @@
#define DWCMSHC_EMMC_EMMC_CTRL 0x52c
#define DWCMSHC_CARD_IS_EMMC BIT(0)
#define DWCMSHC_ENHANCED_STROBE BIT(8)
+#define DWCMSHC_EMMC_AT_CTRL 0x540
+#define EMMC_AT_CTRL_TUNE_CLK_STOP_EN BIT(16)
+#define EMMC_AT_CTRL_PRE_CHANGE_DLY 17
+#define EMMC_AT_CTRL_POST_CHANGE_DLY 19
#define DWCMSHC_EMMC_DLL_CTRL 0x800
#define DWCMSHC_EMMC_DLL_CTRL_RESET BIT(1)
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
@@ -156,6 +160,9 @@ struct sdhci_data {
u32 flags;
u8 hs200_txclk_tapnum;
u8 hs400_txclk_tapnum;
+ u8 hs400_cmdout_tapnum;
+ u8 hs400_strbin_tapnum;
+ u8 ddr50_strbin_delay_num;
};
static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock)
@@ -323,6 +330,11 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab
udelay(1);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
+ extra = 0x3 << EMMC_AT_CTRL_POST_CHANGE_DLY |
+ 0x3 << EMMC_AT_CTRL_PRE_CHANGE_DLY |
+ EMMC_AT_CTRL_TUNE_CLK_STOP_EN;
+ sdhci_writel(host, extra, DWCMSHC_EMMC_AT_CTRL);
+
/* Init DLL settings */
extra = DWCMSHC_EMMC_DLL_START_DEFAULT << DWCMSHC_EMMC_DLL_START_POINT |
DWCMSHC_EMMC_DLL_INC_VALUE << DWCMSHC_EMMC_DLL_INC |
@@ -348,7 +360,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab
extra = DLL_CMDOUT_SRC_CLK_NEG |
DLL_CMDOUT_BOTH_CLK_EDGE |
DWCMSHC_EMMC_DLL_DLYENA |
- DLL_CMDOUT_TAPNUM_90_DEGREES |
+ data->hs400_cmdout_tapnum |
DLL_CMDOUT_TAPNUM_FROM_SW;
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CMDOUT);
}
@@ -360,7 +372,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);
extra = DWCMSHC_EMMC_DLL_DLYENA |
- DLL_STRBIN_TAPNUM_DEFAULT |
+ data->hs400_strbin_tapnum |
DLL_STRBIN_TAPNUM_FROM_SW;
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
} else {
@@ -380,7 +392,7 @@ static int rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab
*/
extra = DWCMSHC_EMMC_DLL_DLYENA |
DLL_STRBIN_DELAY_NUM_SEL |
- DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
+ data->ddr50_strbin_delay_num << DLL_STRBIN_DELAY_NUM_OFFSET;
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
}
@@ -647,6 +659,17 @@ static const struct sdhci_data rk3399_data = {
.set_enhanced_strobe = rk3399_sdhci_set_enhanced_strobe,
};
+static const struct sdhci_data rk3528_data = {
+ .set_ios_post = rk3568_sdhci_set_ios_post,
+ .set_clock = rk3568_sdhci_set_clock,
+ .config_dll = rk3568_sdhci_config_dll,
+ .hs200_txclk_tapnum = 0xc,
+ .hs400_txclk_tapnum = 0x6,
+ .hs400_cmdout_tapnum = 0x6,
+ .hs400_strbin_tapnum = 0x3,
+ .ddr50_strbin_delay_num = 0xa,
+};
+
static const struct sdhci_data rk3568_data = {
.set_ios_post = rk3568_sdhci_set_ios_post,
.set_clock = rk3568_sdhci_set_clock,
@@ -654,6 +677,20 @@ static const struct sdhci_data rk3568_data = {
.flags = FLAG_INVERTER_FLAG_IN_RXCLK,
.hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT,
.hs400_txclk_tapnum = 0x8,
+ .hs400_cmdout_tapnum = DLL_CMDOUT_TAPNUM_90_DEGREES,
+ .hs400_strbin_tapnum = DLL_STRBIN_TAPNUM_DEFAULT,
+ .ddr50_strbin_delay_num = DLL_STRBIN_DELAY_NUM_DEFAULT,
+};
+
+static const struct sdhci_data rk3576_data = {
+ .set_ios_post = rk3568_sdhci_set_ios_post,
+ .set_clock = rk3568_sdhci_set_clock,
+ .config_dll = rk3568_sdhci_config_dll,
+ .hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT,
+ .hs400_txclk_tapnum = 0x7,
+ .hs400_cmdout_tapnum = 0x7,
+ .hs400_strbin_tapnum = 0x5,
+ .ddr50_strbin_delay_num = 0xa,
};
static const struct sdhci_data rk3588_data = {
@@ -662,6 +699,9 @@ static const struct sdhci_data rk3588_data = {
.config_dll = rk3568_sdhci_config_dll,
.hs200_txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT,
.hs400_txclk_tapnum = 0x9,
+ .hs400_cmdout_tapnum = DLL_CMDOUT_TAPNUM_90_DEGREES,
+ .hs400_strbin_tapnum = DLL_STRBIN_TAPNUM_DEFAULT,
+ .ddr50_strbin_delay_num = DLL_STRBIN_DELAY_NUM_DEFAULT,
};
static const struct udevice_id sdhci_ids[] = {
@@ -670,10 +710,18 @@ static const struct udevice_id sdhci_ids[] = {
.data = (ulong)&rk3399_data,
},
{
+ .compatible = "rockchip,rk3528-dwcmshc",
+ .data = (ulong)&rk3528_data,
+ },
+ {
.compatible = "rockchip,rk3568-dwcmshc",
.data = (ulong)&rk3568_data,
},
{
+ .compatible = "rockchip,rk3576-dwcmshc",
+ .data = (ulong)&rk3576_data,
+ },
+ {
.compatible = "rockchip,rk3588-dwcmshc",
.data = (ulong)&rk3588_data,
},
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 4833b5158c7..648dfa4b5ef 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;
@@ -547,7 +549,7 @@ void sdhci_set_uhs_timing(struct sdhci_host *host)
sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
}
-static void sdhci_set_voltage(struct sdhci_host *host)
+void sdhci_set_voltage(struct sdhci_host *host)
{
if (IS_ENABLED(CONFIG_MMC_IO_VOLTAGE)) {
struct mmc *mmc = (struct mmc *)host->mmc;
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 0b56d1405be..06c1e09bf26 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -92,6 +92,13 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
pll = CCM_MMC_CTRL_PLL6;
pll_hz = clock_get_pll6();
#endif
+ /*
+ * On the D1/R528/T113 mux source 1 refers to PLL_PERIPH0(1x),
+ * like for the older SoCs. However we still have the hidden
+ * divider of 2x, so compensate for that here.
+ */
+ if (IS_ENABLED(CONFIG_MACH_SUN8I_R528))
+ pll_hz /= 2;
}
div = pll_hz / hz;
@@ -442,6 +449,26 @@ out:
return error;
}
+static void sunxi_mmc_reset(void *regs)
+{
+ /* Reset controller */
+ writel(SUNXI_MMC_GCTRL_RESET, regs + SUNXI_MMC_GCTRL);
+ udelay(1000);
+
+ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
+ /* Reset card */
+ writel(SUNXI_MMC_HWRST_ASSERT, regs + SUNXI_MMC_HWRST);
+ udelay(10);
+ writel(SUNXI_MMC_HWRST_DEASSERT, regs + SUNXI_MMC_HWRST);
+ udelay(300);
+
+ /* Setup FIFO R/W threshold. Needed on H616. */
+ writel(SUNXI_MMC_THLDC_READ_THLD(512) |
+ SUNXI_MMC_THLDC_WRITE_EN |
+ SUNXI_MMC_THLDC_READ_EN, regs + SUNXI_MMC_THLDC);
+ }
+}
+
/* non-DM code here is used by the (ARM) SPL only */
#if !CONFIG_IS_ENABLED(DM_MMC)
@@ -451,29 +478,29 @@ struct sunxi_mmc_priv mmc_host[4];
static int mmc_resource_init(int sdc_no)
{
struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
- struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ void *ccm = (void *)SUNXI_CCM_BASE;
debug("init mmc %d resource\n", sdc_no);
switch (sdc_no) {
case 0:
priv->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
- priv->mclkreg = &ccm->sd0_clk_cfg;
+ priv->mclkreg = ccm + CCU_MMC0_CLK_CFG;
break;
case 1:
priv->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
- priv->mclkreg = &ccm->sd1_clk_cfg;
+ priv->mclkreg = ccm + CCU_MMC1_CLK_CFG;
break;
#ifdef SUNXI_MMC2_BASE
case 2:
priv->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
- priv->mclkreg = &ccm->sd2_clk_cfg;
+ priv->mclkreg = ccm + CCU_MMC2_CLK_CFG;
break;
#endif
#ifdef SUNXI_MMC3_BASE
case 3:
priv->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
- priv->mclkreg = &ccm->sd3_clk_cfg;
+ priv->mclkreg = ccm + CCU_MMC3_CLK_CFG;
break;
#endif
default:
@@ -489,9 +516,7 @@ static int sunxi_mmc_core_init(struct mmc *mmc)
{
struct sunxi_mmc_priv *priv = mmc->priv;
- /* Reset controller */
- writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl);
- udelay(1000);
+ sunxi_mmc_reset(priv->reg);
return 0;
}
@@ -520,7 +545,7 @@ static const struct mmc_ops sunxi_mmc_ops = {
struct mmc *sunxi_mmc_init(int sdc_no)
{
- struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ void *ccm = (void *)SUNXI_CCM_BASE;
struct sunxi_mmc_priv *priv = &mmc_host[sdc_no];
struct mmc_config *cfg = &priv->cfg;
int ret;
@@ -549,11 +574,11 @@ struct mmc *sunxi_mmc_init(int sdc_no)
/* config ahb clock */
debug("init mmc %d clock and io\n", sdc_no);
#if !defined(CONFIG_SUN50I_GEN_H6) && !defined(CONFIG_SUNXI_GEN_NCAT2)
- setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
+ setbits_le32(ccm + CCU_AHB_GATE0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
#ifdef CONFIG_SUNXI_GEN_SUN6I
/* unassert reset */
- setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
+ setbits_le32(ccm + CCU_AHB_RESET0_CFG, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
#endif
#if defined(CONFIG_MACH_SUN9I)
/* sun9i has a mmc-common module, also set the gate and reset there */
@@ -561,9 +586,9 @@ struct mmc *sunxi_mmc_init(int sdc_no)
SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
#endif
#else /* CONFIG_SUN50I_GEN_H6 */
- setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no);
+ setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << sdc_no);
/* unassert reset */
- setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no));
+ setbits_le32(ccm + CCU_H6_MMC_GATE_RESET, 1 << (RESET_SHIFT + sdc_no));
#endif
ret = mmc_set_mod_clk(priv, 24000000);
if (ret)
@@ -684,9 +709,7 @@ static int sunxi_mmc_probe(struct udevice *dev)
upriv->mmc = &plat->mmc;
- /* Reset controller */
- writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl);
- udelay(1000);
+ sunxi_mmc_reset(priv->reg);
return 0;
}
diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h
index f4ae5a790c8..71865160319 100644
--- a/drivers/mmc/sunxi_mmc.h
+++ b/drivers/mmc/sunxi_mmc.h
@@ -37,7 +37,9 @@ struct sunxi_mmc {
u32 res0; /* 0x54 reserved */
u32 a12a; /* 0x58 Auto command 12 argument */
u32 ntsr; /* 0x5c New timing set register */
- u32 res1[8];
+ u32 res1[6];
+ u32 hwrst; /* 0x78 Hardware Reset */
+ u32 res5;
u32 dmac; /* 0x80 internal DMA control */
u32 dlba; /* 0x84 internal DMA descr list base address */
u32 idst; /* 0x88 internal DMA status */
@@ -46,7 +48,8 @@ struct sunxi_mmc {
u32 cbda; /* 0x94 */
u32 res2[26];
#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2)
- u32 res3[17];
+ u32 thldc; /* 0x100 Threshold control */
+ u32 res3[16];
u32 samp_dl;
u32 res4[46];
#endif
@@ -57,6 +60,7 @@ struct sunxi_mmc {
#define SUNXI_MMC_CLK_ENABLE (0x1 << 16)
#define SUNXI_MMC_CLK_DIVIDER_MASK (0xff)
+#define SUNXI_MMC_GCTRL 0x000
#define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0)
#define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1)
#define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2)
@@ -123,6 +127,10 @@ struct sunxi_mmc {
#define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31)
+#define SUNXI_MMC_HWRST 0x078
+#define SUNXI_MMC_HWRST_ASSERT (0x0 << 0)
+#define SUNXI_MMC_HWRST_DEASSERT (0x1 << 0)
+
#define SUNXI_MMC_IDMAC_RESET (0x1 << 0)
#define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1)
#define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7)
@@ -133,6 +141,12 @@ struct sunxi_mmc {
#define SUNXI_MMC_COMMON_CLK_GATE (1 << 16)
#define SUNXI_MMC_COMMON_RESET (1 << 18)
+#define SUNXI_MMC_THLDC 0x100
+#define SUNXI_MMC_THLDC_READ_EN (0x1 << 0)
+#define SUNXI_MMC_THLDC_BSY_CLR_INT_EN (0x1 << 1)
+#define SUNXI_MMC_THLDC_WRITE_EN (0x1 << 2)
+#define SUNXI_MMC_THLDC_READ_THLD(x) (((x) & 0xfff) << 16)
+
#define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7)
#endif /* _SUNXI_MMC_H */
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 88094b81e7a..3f8edeb5093 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -208,7 +208,7 @@ int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
{
struct mtd_partition partition = {}, *parts;
const char *mtdparts = *_mtdparts;
- uint64_t cur_off = 0, cur_sz = 0;
+ uint64_t cur_off = 0;
int nparts = 0;
int ret, idx;
u64 sz;
@@ -237,8 +237,7 @@ int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
return ret;
if (parts[idx].size == MTD_SIZE_REMAINING)
- parts[idx].size = parent->size - cur_sz;
- cur_sz += parts[idx].size;
+ parts[idx].size = parent->size - parts[idx].offset;
sz = parts[idx].size;
if (sz < parent->writesize || do_div(sz, parent->writesize)) {
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 609bdffbf77..adb271dfb8f 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -190,6 +190,21 @@ config SPL_NAND_LOAD
def_bool y
depends on NAND_DAVINCI && ARCH_DAVINCI && SPL_NAND_SUPPORT
+config NAND_CADENCE
+ bool "Support Cadence NAND controller as a DT device"
+ depends on OF_CONTROL && DM_MTD
+ select SYS_NAND_SELF_INIT
+ select SPL_SYS_NAND_SELF_INIT
+ select SPL_NAND_BASE
+ select SPL_NAND_DRIVERS
+ select SPL_NAND_IDENT
+ select SPL_NAND_INIT
+ select SPL_NAND_ECC
+ imply CMD_NAND
+ help
+ Enable the driver for NAND flash on platforms using a Cadence NAND
+ controller as a DT device.
+
config NAND_DENALI
bool
select SYS_NAND_SELF_INIT
@@ -673,7 +688,7 @@ config SYS_NAND_PAGE_SIZE
SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \
MVEBU_SPL_BOOT_DEVICE_NAND || \
(NAND_ATMEL && SPL_NAND_SUPPORT) || \
- SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX
+ SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX || NAND_CADENCE
depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621
help
Number of data bytes in one page for the NAND chip on the
@@ -775,6 +790,13 @@ config SPL_NAND_AM33XX_BCH
so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling
SPL-NAND driver with software ECC correction support.
+config SPL_NAND_CADENCE
+ bool "Support Cadence NAND controller for SPL"
+ depends on SPL_NAND_SUPPORT
+ help
+ This is a small implementation of the Cadence NAND controller
+ for use on SPL.
+
config SPL_NAND_DENALI
bool "Support Denali NAND controller for SPL"
depends on SPL_NAND_SUPPORT
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index b47a3d787ce..34cba77046a 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -10,6 +10,7 @@ NORMAL_DRIVERS=y
endif
obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o
+obj-$(CONFIG_SPL_NAND_CADENCE) += cadence_spl.o
obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o
obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o
obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o
@@ -51,6 +52,7 @@ obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_DM_NAND_ATMEL) += atmel/
obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/
+obj-$(CONFIG_NAND_CADENCE) += cadence_nand.o
obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_NAND_DENALI) += denali.o
obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index 56fbd64ef68..c90a4eab8df 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -1127,7 +1127,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
const struct nand_data_interface *conf,
struct atmel_smc_cs_conf *smcconf)
{
- u32 ncycles, totalcycles, timeps, mckperiodps;
+ u32 ncycles, totalcycles, timeps, mckperiodps, pulse;
struct atmel_nand_controller *nc;
int ret;
@@ -1253,11 +1253,16 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
/*
- * Read pulse timing directly matches tRP:
+ * Read pulse timing would directly match tRP,
+ * but some NAND flash chips (S34ML01G2 and W29N02KVxxAF)
+ * do not work properly in timing mode 3.
+ * The workaround is to extend the SMC NRD pulse to meet tREA
+ * timing.
*
- * NRD_PULSE = tRP
+ * NRD_PULSE = max(tRP, tREA)
*/
- ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
+ pulse = max(conf->timings.sdr.tRP_min, conf->timings.sdr.tREA_max);
+ ncycles = DIV_ROUND_UP(pulse, mckperiodps);
totalcycles += ncycles;
ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
ncycles);
diff --git a/drivers/mtd/nand/raw/cadence_nand.c b/drivers/mtd/nand/raw/cadence_nand.c
new file mode 100644
index 00000000000..27aa7f97a45
--- /dev/null
+++ b/drivers/mtd/nand/raw/cadence_nand.c
@@ -0,0 +1,2423 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cadence NAND flash controller driver
+ *
+ * Copyright (C) 2019 Cadence
+ *
+ * Author: Piotr Sroka <piotrs@cadence.com>
+ *
+ */
+
+#include <cadence-nand.h>
+#include <clk.h>
+#include <dm.h>
+#include <hang.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <nand.h>
+#include <reset.h>
+#include <wait_bit.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bitfield.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+#include <linux/sizes.h>
+
+static inline struct
+cdns_nand_chip *to_cdns_nand_chip(struct nand_chip *chip)
+{
+ return container_of(chip, struct cdns_nand_chip, chip);
+}
+
+static inline struct
+cadence_nand_info *to_cadence_nand_info(struct nand_hw_control *controller)
+{
+ return container_of(controller, struct cadence_nand_info, controller);
+}
+
+static bool
+cadence_nand_dma_buf_ok(struct cadence_nand_info *cadence, const void *buf,
+ u32 buf_len)
+{
+ u8 data_dma_width = cadence->caps2.data_dma_width;
+
+ return buf &&
+ likely(IS_ALIGNED((uintptr_t)buf, data_dma_width)) &&
+ likely(IS_ALIGNED(buf_len, DMA_DATA_SIZE_ALIGN));
+}
+
+static int cadence_nand_wait_for_value(struct cadence_nand_info *cadence,
+ u32 reg_offset, u32 timeout_us,
+ u32 mask, bool is_clear)
+{
+ u32 val;
+ int ret;
+
+ ret = readl_poll_sleep_timeout(cadence->reg + reg_offset,
+ val, !(val & mask) == is_clear,
+ 10, timeout_us);
+
+ if (ret < 0) {
+ dev_err(cadence->dev,
+ "Timeout while waiting for reg %x with mask %x is clear %d\n",
+ reg_offset, mask, is_clear);
+ }
+
+ return ret;
+}
+
+static int cadence_nand_set_ecc_enable(struct cadence_nand_info *cadence,
+ bool enable)
+{
+ u32 reg;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ reg = readl_relaxed(cadence->reg + ECC_CONFIG_0);
+
+ if (enable)
+ reg |= ECC_CONFIG_0_ECC_EN;
+ else
+ reg &= ~ECC_CONFIG_0_ECC_EN;
+
+ writel_relaxed(reg, cadence->reg + ECC_CONFIG_0);
+
+ return 0;
+}
+
+static void cadence_nand_set_ecc_strength(struct cadence_nand_info *cadence,
+ u8 corr_str_idx)
+{
+ u32 reg;
+
+ if (cadence->curr_corr_str_idx == corr_str_idx)
+ return;
+
+ reg = readl_relaxed(cadence->reg + ECC_CONFIG_0);
+ reg &= ~ECC_CONFIG_0_CORR_STR;
+ reg |= FIELD_PREP(ECC_CONFIG_0_CORR_STR, corr_str_idx);
+ writel_relaxed(reg, cadence->reg + ECC_CONFIG_0);
+
+ cadence->curr_corr_str_idx = corr_str_idx;
+}
+
+static int cadence_nand_get_ecc_strength_idx(struct cadence_nand_info *cadence,
+ u8 strength)
+{
+ int i, corr_str_idx = -1;
+
+ for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) {
+ if (cadence->ecc_strengths[i] == strength) {
+ corr_str_idx = i;
+ break;
+ }
+ }
+
+ return corr_str_idx;
+}
+
+static int cadence_nand_set_skip_marker_val(struct cadence_nand_info *cadence,
+ u16 marker_value)
+{
+ u32 reg;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ reg = readl_relaxed(cadence->reg + SKIP_BYTES_CONF);
+ reg &= ~SKIP_BYTES_MARKER_VALUE;
+ reg |= FIELD_PREP(SKIP_BYTES_MARKER_VALUE,
+ marker_value);
+
+ writel_relaxed(reg, cadence->reg + SKIP_BYTES_CONF);
+
+ return 0;
+}
+
+static int cadence_nand_set_skip_bytes_conf(struct cadence_nand_info *cadence,
+ u8 num_of_bytes,
+ u32 offset_value,
+ int enable)
+{
+ u32 reg, skip_bytes_offset;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ if (!enable) {
+ num_of_bytes = 0;
+ offset_value = 0;
+ }
+
+ reg = readl_relaxed(cadence->reg + SKIP_BYTES_CONF);
+ reg &= ~SKIP_BYTES_NUM_OF_BYTES;
+ reg |= FIELD_PREP(SKIP_BYTES_NUM_OF_BYTES,
+ num_of_bytes);
+ skip_bytes_offset = FIELD_PREP(SKIP_BYTES_OFFSET_VALUE,
+ offset_value);
+
+ writel_relaxed(reg, cadence->reg + SKIP_BYTES_CONF);
+ writel_relaxed(skip_bytes_offset, cadence->reg + SKIP_BYTES_OFFSET);
+
+ return 0;
+}
+
+/* Functions enables/disables hardware detection of erased data */
+static void cadence_nand_set_erase_detection(struct cadence_nand_info *cadence,
+ bool enable,
+ u8 bitflips_threshold)
+{
+ u32 reg;
+
+ reg = readl_relaxed(cadence->reg + ECC_CONFIG_0);
+
+ if (enable)
+ reg |= ECC_CONFIG_0_ERASE_DET_EN;
+ else
+ reg &= ~ECC_CONFIG_0_ERASE_DET_EN;
+
+ writel_relaxed(reg, cadence->reg + ECC_CONFIG_0);
+ writel_relaxed(bitflips_threshold, cadence->reg + ECC_CONFIG_1);
+}
+
+static int cadence_nand_set_access_width16(struct cadence_nand_info *cadence,
+ bool bit_bus16)
+{
+ u32 reg;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ reg = readl_relaxed(cadence->reg + COMMON_SET);
+ if (!bit_bus16)
+ reg &= ~COMMON_SET_DEVICE_16BIT;
+ else
+ reg |= COMMON_SET_DEVICE_16BIT;
+ writel_relaxed(reg, cadence->reg + COMMON_SET);
+
+ return 0;
+}
+
+static void
+cadence_nand_clear_interrupt(struct cadence_nand_info *cadence,
+ struct cadence_nand_irq_status *irq_status)
+{
+ writel_relaxed(irq_status->status, cadence->reg + INTR_STATUS);
+ writel_relaxed(irq_status->trd_status,
+ cadence->reg + TRD_COMP_INT_STATUS);
+ writel_relaxed(irq_status->trd_error,
+ cadence->reg + TRD_ERR_INT_STATUS);
+}
+
+static void
+cadence_nand_read_int_status(struct cadence_nand_info *cadence,
+ struct cadence_nand_irq_status *irq_status)
+{
+ irq_status->status = readl_relaxed(cadence->reg + INTR_STATUS);
+ irq_status->trd_status = readl_relaxed(cadence->reg
+ + TRD_COMP_INT_STATUS);
+ irq_status->trd_error = readl_relaxed(cadence->reg
+ + TRD_ERR_INT_STATUS);
+}
+
+static u32 irq_detected(struct cadence_nand_info *cadence,
+ struct cadence_nand_irq_status *irq_status)
+{
+ cadence_nand_read_int_status(cadence, irq_status);
+
+ return irq_status->status || irq_status->trd_status ||
+ irq_status->trd_error;
+}
+
+static void cadence_nand_reset_irq(struct cadence_nand_info *cadence)
+{
+ memset(&cadence->irq_status, 0, sizeof(cadence->irq_status));
+ memset(&cadence->irq_mask, 0, sizeof(cadence->irq_mask));
+}
+
+/*
+ * This is the interrupt service routine. It handles all interrupts
+ * sent to this device.
+ */
+static irqreturn_t cadence_nand_isr(struct cadence_nand_info *cadence)
+{
+ struct cadence_nand_irq_status irq_status;
+ irqreturn_t result = IRQ_NONE;
+
+ if (irq_detected(cadence, &irq_status)) {
+ /* Handle interrupt. */
+ /* First acknowledge it. */
+ cadence_nand_clear_interrupt(cadence, &irq_status);
+ /* Status in the device context for someone to read. */
+ cadence->irq_status.status |= irq_status.status;
+ cadence->irq_status.trd_status |= irq_status.trd_status;
+ cadence->irq_status.trd_error |= irq_status.trd_error;
+ /* Tell the OS that we've handled this. */
+ result = IRQ_HANDLED;
+ }
+ return result;
+}
+
+static void cadence_nand_set_irq_mask(struct cadence_nand_info *cadence,
+ struct cadence_nand_irq_status *irq_mask)
+{
+ writel_relaxed(INTR_ENABLE_INTR_EN | irq_mask->status,
+ cadence->reg + INTR_ENABLE);
+
+ writel_relaxed(irq_mask->trd_error,
+ cadence->reg + TRD_ERR_INT_STATUS_EN);
+}
+
+static void
+cadence_nand_wait_for_irq(struct cadence_nand_info *cadence,
+ struct cadence_nand_irq_status *irq_mask,
+ struct cadence_nand_irq_status *irq_status)
+{
+ irqreturn_t result = IRQ_NONE;
+ u32 start = get_timer(0);
+
+ while (get_timer(start) < TIMEOUT_US) {
+ result = cadence_nand_isr(cadence);
+
+ if (result == IRQ_HANDLED) {
+ *irq_status = cadence->irq_status;
+ break;
+ }
+ udelay(1);
+ }
+
+ if (!result) {
+ /* Timeout error. */
+ dev_err(cadence->dev, "timeout occurred:\n");
+ dev_err(cadence->dev, "\tstatus = 0x%x, mask = 0x%x\n",
+ irq_status->status, irq_mask->status);
+ dev_err(cadence->dev,
+ "\ttrd_status = 0x%x, trd_status mask = 0x%x\n",
+ irq_status->trd_status, irq_mask->trd_status);
+ dev_err(cadence->dev,
+ "\t trd_error = 0x%x, trd_error mask = 0x%x\n",
+ irq_status->trd_error, irq_mask->trd_error);
+ }
+}
+
+/* Execute generic command on NAND controller. */
+static int cadence_nand_generic_cmd_send(struct cadence_nand_info *cadence,
+ u8 chip_nr,
+ u64 mini_ctrl_cmd)
+{
+ u32 mini_ctrl_cmd_l, mini_ctrl_cmd_h, reg;
+
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_CS, chip_nr);
+ mini_ctrl_cmd_l = mini_ctrl_cmd & 0xFFFFFFFF;
+ mini_ctrl_cmd_h = mini_ctrl_cmd >> 32;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ cadence_nand_reset_irq(cadence);
+
+ writel_relaxed(mini_ctrl_cmd_l, cadence->reg + CMD_REG2);
+ writel_relaxed(mini_ctrl_cmd_h, cadence->reg + CMD_REG3);
+
+ /* Select generic command. */
+ reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_GEN);
+ /* Thread number. */
+ reg |= FIELD_PREP(CMD_REG0_TN, 0);
+
+ /* Issue command. */
+ writel_relaxed(reg, cadence->reg + CMD_REG0);
+ cadence->buf_index = 0;
+
+ return 0;
+}
+
+/* Wait for data on slave DMA interface. */
+static int cadence_nand_wait_on_sdma(struct cadence_nand_info *cadence, u8 *out_sdma_trd,
+ u32 *out_sdma_size)
+{
+ struct cadence_nand_irq_status irq_mask, irq_status;
+
+ irq_mask.trd_status = 0;
+ irq_mask.trd_error = 0;
+ irq_mask.status = INTR_STATUS_SDMA_TRIGG
+ | INTR_STATUS_SDMA_ERR
+ | INTR_STATUS_UNSUPP_CMD;
+
+ cadence_nand_set_irq_mask(cadence, &irq_mask);
+ cadence_nand_wait_for_irq(cadence, &irq_mask, &irq_status);
+ if (irq_status.status == 0) {
+ dev_err(cadence->dev, "Timeout while waiting for SDMA\n");
+ return -ETIMEDOUT;
+ }
+
+ if (irq_status.status & INTR_STATUS_SDMA_TRIGG) {
+ *out_sdma_size = readl_relaxed(cadence->reg + SDMA_SIZE);
+ *out_sdma_trd = readl_relaxed(cadence->reg + SDMA_TRD_NUM);
+ *out_sdma_trd =
+ FIELD_GET(SDMA_TRD_NUM_SDMA_TRD, *out_sdma_trd);
+ } else {
+ dev_err(cadence->dev, "SDMA error - irq_status %x\n",
+ irq_status.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void cadence_nand_get_caps(struct cadence_nand_info *cadence)
+{
+ u32 reg;
+
+ reg = readl_relaxed(cadence->reg + CTRL_FEATURES);
+
+ cadence->caps2.max_banks = 1 << FIELD_GET(CTRL_FEATURES_N_BANKS, reg);
+
+ if (FIELD_GET(CTRL_FEATURES_DMA_DWITH64, reg))
+ cadence->caps2.data_dma_width = 8;
+ else
+ cadence->caps2.data_dma_width = 4;
+
+ if (reg & CTRL_FEATURES_CONTROL_DATA)
+ cadence->caps2.data_control_supp = true;
+
+ if (reg & (CTRL_FEATURES_NVDDR_2_3
+ | CTRL_FEATURES_NVDDR))
+ cadence->caps2.is_phy_type_dll = true;
+}
+
+/* Prepare CDMA descriptor. */
+static void
+cadence_nand_cdma_desc_prepare(struct cadence_nand_info *cadence,
+ char nf_mem, u32 flash_ptr, dma_addr_t mem_ptr,
+ dma_addr_t ctrl_data_ptr, u16 ctype)
+{
+ struct cadence_nand_cdma_desc *cdma_desc = cadence->cdma_desc;
+
+ memset(cdma_desc, 0, sizeof(struct cadence_nand_cdma_desc));
+
+ /* Set fields for one descriptor. */
+ cdma_desc->flash_pointer = flash_ptr;
+ if (cadence->ctrl_rev >= 13)
+ cdma_desc->bank = nf_mem;
+ else
+ cdma_desc->flash_pointer |= (nf_mem << CDMA_CFPTR_MEM_SHIFT);
+
+ cdma_desc->command_flags |= CDMA_CF_DMA_MASTER;
+ cdma_desc->command_flags |= CDMA_CF_INT;
+
+ cdma_desc->memory_pointer = mem_ptr;
+ cdma_desc->status = 0;
+ cdma_desc->sync_flag_pointer = 0;
+ cdma_desc->sync_arguments = 0;
+
+ cdma_desc->command_type = ctype;
+ cdma_desc->ctrl_data_ptr = ctrl_data_ptr;
+
+ flush_cache((dma_addr_t)cadence->cdma_desc,
+ ROUND(sizeof(struct cadence_nand_cdma_desc),
+ ARCH_DMA_MINALIGN));
+}
+
+static u8 cadence_nand_check_desc_error(struct cadence_nand_info *cadence,
+ u32 desc_status)
+{
+ if (desc_status & CDMA_CS_ERP)
+ return STAT_ERASED;
+
+ if (desc_status & CDMA_CS_UNCE)
+ return STAT_ECC_UNCORR;
+
+ if (desc_status & CDMA_CS_ERR) {
+ dev_err(cadence->dev, ":CDMA desc error flag detected.\n");
+ return STAT_FAIL;
+ }
+
+ if (FIELD_GET(CDMA_CS_MAXERR, desc_status))
+ return STAT_ECC_CORR;
+
+ return STAT_FAIL;
+}
+
+static int cadence_nand_cdma_finish(struct cadence_nand_info *cadence)
+{
+ struct cadence_nand_cdma_desc *desc_ptr = cadence->cdma_desc;
+ u8 status = STAT_BUSY;
+
+ invalidate_dcache_range((dma_addr_t)cadence->cdma_desc,
+ (dma_addr_t)cadence->cdma_desc +
+ ROUND(sizeof(struct cadence_nand_cdma_desc),
+ ARCH_DMA_MINALIGN));
+
+ if (desc_ptr->status & CDMA_CS_FAIL) {
+ status = cadence_nand_check_desc_error(cadence,
+ desc_ptr->status);
+ dev_err(cadence->dev, ":CDMA error %x\n", desc_ptr->status);
+ } else if (desc_ptr->status & CDMA_CS_COMP) {
+ /* Descriptor finished with no errors. */
+ if (desc_ptr->command_flags & CDMA_CF_CONT) {
+ dev_info(cadence->dev, "DMA unsupported flag is set");
+ status = STAT_UNKNOWN;
+ } else {
+ /* Last descriptor. */
+ status = STAT_OK;
+ }
+ }
+
+ return status;
+}
+
+static int cadence_nand_cdma_send(struct cadence_nand_info *cadence,
+ u8 thread)
+{
+ u32 reg;
+ int status;
+
+ /* Wait for thread ready. */
+ status = cadence_nand_wait_for_value(cadence, TRD_STATUS,
+ TIMEOUT_US,
+ BIT(thread), true);
+ if (status)
+ return status;
+
+ cadence_nand_reset_irq(cadence);
+
+ writel_relaxed((u32)cadence->dma_cdma_desc,
+ cadence->reg + CMD_REG2);
+ writel_relaxed(0, cadence->reg + CMD_REG3);
+
+ /* Select CDMA mode. */
+ reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_CDMA);
+ /* Thread number. */
+ reg |= FIELD_PREP(CMD_REG0_TN, thread);
+ /* Issue command. */
+ writel_relaxed(reg, cadence->reg + CMD_REG0);
+
+ return 0;
+}
+
+/* Send SDMA command and wait for finish. */
+static u32
+cadence_nand_cdma_send_and_wait(struct cadence_nand_info *cadence,
+ u8 thread)
+{
+ struct cadence_nand_irq_status irq_mask, irq_status = {0};
+ int status;
+ u32 val;
+
+ irq_mask.trd_status = BIT(thread);
+ irq_mask.trd_error = BIT(thread);
+ irq_mask.status = INTR_STATUS_CDMA_TERR;
+
+ cadence_nand_set_irq_mask(cadence, &irq_mask);
+
+ status = cadence_nand_cdma_send(cadence, thread);
+ if (status)
+ return status;
+
+ /* Make sure the descriptor processing is complete */
+ status = readl_poll_timeout(cadence->reg + TRD_COMP_INT_STATUS, val,
+ (val & BIT(thread)), TIMEOUT_US);
+ if (status) {
+ pr_err("cmd thread completion timeout!\n");
+ return status;
+ }
+
+ cadence_nand_wait_for_irq(cadence, &irq_mask, &irq_status);
+
+ if (irq_status.status == 0 && irq_status.trd_status == 0 &&
+ irq_status.trd_error == 0) {
+ dev_err(cadence->dev, "CDMA command timeout\n");
+ return -ETIMEDOUT;
+ }
+ if (irq_status.status & irq_mask.status) {
+ dev_err(cadence->dev, "CDMA command failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ECC size depends on configured ECC strength and on maximum supported
+ * ECC step size.
+ */
+static int cadence_nand_calc_ecc_bytes(int max_step_size, int strength)
+{
+ int nbytes = DIV_ROUND_UP(fls(8 * max_step_size) * strength, 8);
+
+ return ALIGN(nbytes, 2);
+}
+
+#define CADENCE_NAND_CALC_ECC_BYTES(max_step_size) \
+ static int \
+ cadence_nand_calc_ecc_bytes_##max_step_size(int step_size, \
+ int strength)\
+ {\
+ return cadence_nand_calc_ecc_bytes(max_step_size, strength);\
+ }
+
+CADENCE_NAND_CALC_ECC_BYTES(256)
+CADENCE_NAND_CALC_ECC_BYTES(512)
+CADENCE_NAND_CALC_ECC_BYTES(1024)
+CADENCE_NAND_CALC_ECC_BYTES(2048)
+CADENCE_NAND_CALC_ECC_BYTES(4096)
+
+/* Function reads BCH capabilities. */
+static int cadence_nand_read_bch_caps(struct cadence_nand_info *cadence)
+{
+ struct nand_ecc_caps *ecc_caps = &cadence->ecc_caps;
+ int max_step_size = 0, nstrengths, i;
+ u32 reg;
+
+ reg = readl_relaxed(cadence->reg + BCH_CFG_3);
+ cadence->bch_metadata_size = FIELD_GET(BCH_CFG_3_METADATA_SIZE, reg);
+ if (cadence->bch_metadata_size < 4) {
+ dev_err(cadence->dev,
+ "Driver needs at least 4 bytes of BCH meta data\n");
+ return -EIO;
+ }
+
+ reg = readl_relaxed(cadence->reg + BCH_CFG_0);
+ cadence->ecc_strengths[0] = FIELD_GET(BCH_CFG_0_CORR_CAP_0, reg);
+ cadence->ecc_strengths[1] = FIELD_GET(BCH_CFG_0_CORR_CAP_1, reg);
+ cadence->ecc_strengths[2] = FIELD_GET(BCH_CFG_0_CORR_CAP_2, reg);
+ cadence->ecc_strengths[3] = FIELD_GET(BCH_CFG_0_CORR_CAP_3, reg);
+
+ reg = readl_relaxed(cadence->reg + BCH_CFG_1);
+ cadence->ecc_strengths[4] = FIELD_GET(BCH_CFG_1_CORR_CAP_4, reg);
+ cadence->ecc_strengths[5] = FIELD_GET(BCH_CFG_1_CORR_CAP_5, reg);
+ cadence->ecc_strengths[6] = FIELD_GET(BCH_CFG_1_CORR_CAP_6, reg);
+ cadence->ecc_strengths[7] = FIELD_GET(BCH_CFG_1_CORR_CAP_7, reg);
+
+ reg = readl_relaxed(cadence->reg + BCH_CFG_2);
+ cadence->ecc_stepinfos[0].stepsize =
+ FIELD_GET(BCH_CFG_2_SECT_0, reg);
+
+ cadence->ecc_stepinfos[1].stepsize =
+ FIELD_GET(BCH_CFG_2_SECT_1, reg);
+
+ nstrengths = 0;
+ for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) {
+ if (cadence->ecc_strengths[i] != 0)
+ nstrengths++;
+ }
+
+ ecc_caps->nstepinfos = 0;
+ for (i = 0; i < BCH_MAX_NUM_SECTOR_SIZES; i++) {
+ /* ECC strengths are common for all step infos. */
+ cadence->ecc_stepinfos[i].nstrengths = nstrengths;
+ cadence->ecc_stepinfos[i].strengths =
+ cadence->ecc_strengths;
+
+ if (cadence->ecc_stepinfos[i].stepsize != 0)
+ ecc_caps->nstepinfos++;
+
+ if (cadence->ecc_stepinfos[i].stepsize > max_step_size)
+ max_step_size = cadence->ecc_stepinfos[i].stepsize;
+ }
+ ecc_caps->stepinfos = &cadence->ecc_stepinfos[0];
+
+ switch (max_step_size) {
+ case 256:
+ ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_256;
+ break;
+ case 512:
+ ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_512;
+ break;
+ case 1024:
+ ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_1024;
+ break;
+ case 2048:
+ ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_2048;
+ break;
+ case 4096:
+ ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_4096;
+ break;
+ default:
+ dev_err(cadence->dev,
+ "Unsupported sector size(ecc step size) %d\n",
+ max_step_size);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Hardware initialization. */
+static int cadence_nand_hw_init(struct cadence_nand_info *cadence)
+{
+ int status;
+ u32 reg;
+
+ status = cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_INIT_COMP, false);
+ if (status)
+ return status;
+
+ reg = readl_relaxed(cadence->reg + CTRL_VERSION);
+ cadence->ctrl_rev = FIELD_GET(CTRL_VERSION_REV, reg);
+
+ dev_info(cadence->dev,
+ "%s: cadence nand controller version reg %x\n",
+ __func__, reg);
+
+ /* Disable cache and multiplane. */
+ writel_relaxed(0, cadence->reg + MULTIPLANE_CFG);
+ writel_relaxed(0, cadence->reg + CACHE_CFG);
+
+ /* Clear all interrupts. */
+ writel_relaxed(0xFFFFFFFF, cadence->reg + INTR_STATUS);
+
+ cadence_nand_get_caps(cadence);
+ if (cadence_nand_read_bch_caps(cadence))
+ return -EIO;
+
+ /*
+ * Set IO width access to 8.
+ * It is because during SW device discovering width access
+ * is expected to be 8.
+ */
+ status = cadence_nand_set_access_width16(cadence, false);
+
+ return status;
+}
+
+#define TT_MAIN_OOB_AREAS 2
+#define TT_RAW_PAGE 3
+#define TT_BBM 4
+#define TT_MAIN_OOB_AREA_EXT 5
+
+/* Prepare size of data to transfer. */
+static void
+cadence_nand_prepare_data_size(struct mtd_info *mtd,
+ int transfer_type)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ u32 sec_size = 0, offset = 0, sec_cnt = 1;
+ u32 last_sec_size = cdns_chip->sector_size;
+ u32 data_ctrl_size = 0;
+ u32 reg = 0;
+
+ if (cadence->curr_trans_type == transfer_type)
+ return;
+
+ switch (transfer_type) {
+ case TT_MAIN_OOB_AREA_EXT:
+ sec_cnt = cdns_chip->sector_count;
+ sec_size = cdns_chip->sector_size;
+ data_ctrl_size = cdns_chip->avail_oob_size;
+ break;
+ case TT_MAIN_OOB_AREAS:
+ sec_cnt = cdns_chip->sector_count;
+ last_sec_size = cdns_chip->sector_size
+ + cdns_chip->avail_oob_size;
+ sec_size = cdns_chip->sector_size;
+ break;
+ case TT_RAW_PAGE:
+ last_sec_size = mtd->writesize + mtd->oobsize;
+ break;
+ case TT_BBM:
+ offset = mtd->writesize + cdns_chip->bbm_offs;
+ last_sec_size = 8;
+ break;
+ }
+
+ reg = 0;
+ reg |= FIELD_PREP(TRAN_CFG_0_OFFSET, offset);
+ reg |= FIELD_PREP(TRAN_CFG_0_SEC_CNT, sec_cnt);
+ writel_relaxed(reg, cadence->reg + TRAN_CFG_0);
+
+ reg = 0;
+ reg |= FIELD_PREP(TRAN_CFG_1_LAST_SEC_SIZE, last_sec_size);
+ reg |= FIELD_PREP(TRAN_CFG_1_SECTOR_SIZE, sec_size);
+ writel_relaxed(reg, cadence->reg + TRAN_CFG_1);
+
+ if (cadence->caps2.data_control_supp) {
+ reg = readl_relaxed(cadence->reg + CONTROL_DATA_CTRL);
+ reg &= ~CONTROL_DATA_CTRL_SIZE;
+ reg |= FIELD_PREP(CONTROL_DATA_CTRL_SIZE, data_ctrl_size);
+ writel_relaxed(reg, cadence->reg + CONTROL_DATA_CTRL);
+ }
+
+ cadence->curr_trans_type = transfer_type;
+}
+
+static int
+cadence_nand_cdma_transfer(struct cadence_nand_info *cadence, u8 chip_nr,
+ int page, void *buf, void *ctrl_dat, u32 buf_size,
+ u32 ctrl_dat_size, enum dma_data_direction dir,
+ bool with_ecc)
+{
+ dma_addr_t dma_buf, dma_ctrl_dat = 0;
+ u8 thread_nr = chip_nr;
+ int status;
+ u16 ctype;
+
+ if (dir == DMA_FROM_DEVICE)
+ ctype = CDMA_CT_RD;
+ else
+ ctype = CDMA_CT_WR;
+
+ cadence_nand_set_ecc_enable(cadence, with_ecc);
+
+ dma_buf = dma_map_single(buf, buf_size, dir);
+ if (dma_mapping_error(cadence->dev, dma_buf)) {
+ dev_err(cadence->dev, "Failed to map DMA buffer\n");
+ return -EIO;
+ }
+
+ if (ctrl_dat && ctrl_dat_size) {
+ dma_ctrl_dat = dma_map_single(ctrl_dat,
+ ctrl_dat_size, dir);
+ if (dma_mapping_error(cadence->dev, dma_ctrl_dat)) {
+ dma_unmap_single(dma_buf,
+ buf_size, dir);
+ dev_err(cadence->dev, "Failed to map DMA buffer\n");
+ return -EIO;
+ }
+ }
+
+ cadence_nand_cdma_desc_prepare(cadence, chip_nr, page,
+ dma_buf, dma_ctrl_dat, ctype);
+
+ status = cadence_nand_cdma_send_and_wait(cadence, thread_nr);
+
+ dma_unmap_single(dma_buf,
+ buf_size, dir);
+
+ if (ctrl_dat && ctrl_dat_size)
+ dma_unmap_single(dma_ctrl_dat,
+ ctrl_dat_size, dir);
+ if (status)
+ return status;
+
+ return cadence_nand_cdma_finish(cadence);
+}
+
+static void cadence_nand_set_timings(struct cadence_nand_info *cadence,
+ struct cadence_nand_timings *t)
+{
+ writel_relaxed(t->async_toggle_timings,
+ cadence->reg + ASYNC_TOGGLE_TIMINGS);
+ writel_relaxed(t->timings0, cadence->reg + TIMINGS0);
+ writel_relaxed(t->timings1, cadence->reg + TIMINGS1);
+ writel_relaxed(t->timings2, cadence->reg + TIMINGS2);
+
+ if (cadence->caps2.is_phy_type_dll)
+ writel_relaxed(t->dll_phy_ctrl, cadence->reg + DLL_PHY_CTRL);
+
+ writel_relaxed(t->phy_ctrl, cadence->reg + PHY_CTRL);
+
+ if (cadence->caps2.is_phy_type_dll) {
+ writel_relaxed(0, cadence->reg + PHY_TSEL);
+ writel_relaxed(2, cadence->reg + PHY_DQ_TIMING);
+ writel_relaxed(t->phy_dqs_timing,
+ cadence->reg + PHY_DQS_TIMING);
+ writel_relaxed(t->phy_gate_lpbk_ctrl,
+ cadence->reg + PHY_GATE_LPBK_CTRL);
+ writel_relaxed(PHY_DLL_MASTER_CTRL_BYPASS_MODE,
+ cadence->reg + PHY_DLL_MASTER_CTRL);
+ writel_relaxed(0, cadence->reg + PHY_DLL_SLAVE_CTRL);
+ }
+}
+
+static int cadence_nand_select_target(struct nand_chip *chip)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+
+ if (chip == cadence->selected_chip)
+ return 0;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ cadence_nand_set_timings(cadence, &cdns_chip->timings);
+
+ cadence_nand_set_ecc_strength(cadence,
+ cdns_chip->corr_str_idx);
+
+ cadence_nand_set_erase_detection(cadence, true,
+ chip->ecc.strength);
+
+ cadence->curr_trans_type = -1;
+ cadence->selected_chip = chip;
+
+ return 0;
+}
+
+static int cadence_nand_erase(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ int status;
+ u8 thread_nr = cdns_chip->cs[chip->cur_cs];
+
+ cadence_nand_cdma_desc_prepare(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, 0, 0,
+ CDMA_CT_ERASE);
+ status = cadence_nand_cdma_send_and_wait(cadence, thread_nr);
+ if (status) {
+ dev_err(cadence->dev, "erase operation failed\n");
+ return -EIO;
+ }
+
+ status = cadence_nand_cdma_finish(cadence);
+ if (status)
+ return status;
+
+ return 0;
+}
+
+static int cadence_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, int oobavail)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ int ret;
+
+ /*
+ * If .size and .strength are already set (usually by DT),
+ * check if they are supported by this controller.
+ */
+ if (chip->ecc.size && chip->ecc.strength)
+ return nand_check_ecc_caps(chip, &cadence->ecc_caps, oobavail);
+
+ /*
+ * We want .size and .strength closest to the chip's requirement
+ * unless NAND_ECC_MAXIMIZE is requested.
+ */
+ if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+ ret = nand_match_ecc_req(chip, &cadence->ecc_caps, oobavail);
+ if (!ret)
+ return 0;
+ }
+
+ /* Max ECC strength is the last thing we can do */
+ return nand_maximize_ecc(chip, &cadence->ecc_caps, oobavail);
+}
+
+static int cadence_nand_read_bbm(struct mtd_info *mtd, struct nand_chip *chip, int page, u8 *buf)
+{
+ int status;
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+
+ cadence_nand_prepare_data_size(mtd, TT_BBM);
+
+ cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0);
+
+ /*
+ * Read only bad block marker from offset
+ * defined by a memory manufacturer.
+ */
+ status = cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, cadence->buf, NULL,
+ mtd->oobsize,
+ 0, DMA_FROM_DEVICE, false);
+ if (status) {
+ dev_err(cadence->dev, "read BBM failed\n");
+ return -EIO;
+ }
+
+ memcpy(buf + cdns_chip->bbm_offs, cadence->buf, cdns_chip->bbm_len);
+
+ return 0;
+}
+
+static int cadence_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int oob_required, int page)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ int status;
+ u16 marker_val = 0xFFFF;
+
+ status = cadence_nand_select_target(chip);
+ if (status)
+ return status;
+
+ cadence_nand_set_skip_bytes_conf(cadence, cdns_chip->bbm_len,
+ mtd->writesize
+ + cdns_chip->bbm_offs,
+ 1);
+
+ if (oob_required) {
+ marker_val = *(u16 *)(chip->oob_poi
+ + cdns_chip->bbm_offs);
+ } else {
+ /* Set oob data to 0xFF. */
+ memset(cadence->buf + mtd->writesize, 0xFF,
+ cdns_chip->avail_oob_size);
+ }
+
+ cadence_nand_set_skip_marker_val(cadence, marker_val);
+
+ cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREA_EXT);
+
+ if (cadence_nand_dma_buf_ok(cadence, buf, mtd->writesize) &&
+ cadence->caps2.data_control_supp && !(chip->options & NAND_USE_BOUNCE_BUFFER)) {
+ u8 *oob;
+
+ if (oob_required)
+ oob = chip->oob_poi;
+ else
+ oob = cadence->buf + mtd->writesize;
+
+ status = cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, (void *)buf, oob,
+ mtd->writesize,
+ cdns_chip->avail_oob_size,
+ DMA_TO_DEVICE, true);
+ if (status) {
+ dev_err(cadence->dev, "write page failed\n");
+ return -EIO;
+ }
+
+ return 0;
+ }
+
+ if (oob_required) {
+ /* Transfer the data to the oob area. */
+ memcpy(cadence->buf + mtd->writesize, chip->oob_poi,
+ cdns_chip->avail_oob_size);
+ }
+
+ memcpy(cadence->buf, buf, mtd->writesize);
+
+ cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREAS);
+
+ return cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, cadence->buf, NULL,
+ mtd->writesize
+ + cdns_chip->avail_oob_size,
+ 0, DMA_TO_DEVICE, true);
+}
+
+static int cadence_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+
+ memset(cadence->buf, 0xFF, mtd->writesize);
+
+ return cadence_nand_write_page(mtd, chip, cadence->buf, 1, page);
+}
+
+static int cadence_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int oob_required, int page)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *tmp_buf = cadence->buf;
+ int oob_skip = cdns_chip->bbm_len;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+ int status;
+
+ status = cadence_nand_select_target(chip);
+ if (status)
+ return status;
+
+ /*
+ * Fill the buffer with 0xff first except the full page transfer.
+ * This simplifies the logic.
+ */
+ if (!buf || !oob_required)
+ memset(tmp_buf, 0xff, size);
+
+ cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0);
+
+ /* Arrange the buffer for syndrome payload/ecc layout. */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(tmp_buf + pos, buf, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(tmp_buf + writesize + oob_skip, buf,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ const u8 *oob = chip->oob_poi;
+ u32 oob_data_offset = (cdns_chip->sector_count - 1) *
+ (cdns_chip->sector_size + chip->ecc.bytes)
+ + cdns_chip->sector_size + oob_skip;
+
+ /* BBM at the beginning of the OOB area. */
+ memcpy(tmp_buf + writesize, oob, oob_skip);
+
+ /* OOB free. */
+ memcpy(tmp_buf + oob_data_offset, oob,
+ cdns_chip->avail_oob_size);
+ oob += cdns_chip->avail_oob_size;
+
+ /* OOB ECC. */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ if (i == (ecc_steps - 1))
+ pos += cdns_chip->avail_oob_size;
+
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(tmp_buf + pos, oob, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(tmp_buf + writesize + oob_skip, oob,
+ len);
+ oob += len;
+ }
+ }
+ }
+
+ cadence_nand_prepare_data_size(mtd, TT_RAW_PAGE);
+
+ return cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, cadence->buf, NULL,
+ mtd->writesize +
+ mtd->oobsize,
+ 0, DMA_TO_DEVICE, false);
+}
+
+static int cadence_nand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return cadence_nand_write_page_raw(mtd, chip, NULL, true, page);
+}
+
+static int cadence_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ int status;
+ int ecc_err_count = 0;
+
+ status = cadence_nand_select_target(chip);
+ if (status)
+ return status;
+
+ cadence_nand_set_skip_bytes_conf(cadence, cdns_chip->bbm_len,
+ mtd->writesize
+ + cdns_chip->bbm_offs, 1);
+
+ /*
+ * If data buffer can be accessed by DMA and data_control feature
+ * is supported then transfer data and oob directly.
+ */
+ if (cadence_nand_dma_buf_ok(cadence, buf, mtd->writesize) &&
+ cadence->caps2.data_control_supp && !(chip->options & NAND_USE_BOUNCE_BUFFER)) {
+ u8 *oob;
+
+ if (oob_required)
+ oob = chip->oob_poi;
+ else
+ oob = cadence->buf + mtd->writesize;
+
+ cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREA_EXT);
+ status = cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, buf, oob,
+ mtd->writesize,
+ cdns_chip->avail_oob_size,
+ DMA_FROM_DEVICE, true);
+ /* Otherwise use bounce buffer. */
+ } else {
+ cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREAS);
+ status = cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, cadence->buf,
+ NULL, mtd->writesize
+ + cdns_chip->avail_oob_size,
+ 0, DMA_FROM_DEVICE, true);
+
+ memcpy(buf, cadence->buf, mtd->writesize);
+ if (oob_required)
+ memcpy(chip->oob_poi,
+ cadence->buf + mtd->writesize,
+ mtd->oobsize);
+ }
+
+ switch (status) {
+ case STAT_ECC_UNCORR:
+ mtd->ecc_stats.failed++;
+ ecc_err_count++;
+ break;
+ case STAT_ECC_CORR:
+ ecc_err_count = FIELD_GET(CDMA_CS_MAXERR,
+ cadence->cdma_desc->status);
+ mtd->ecc_stats.corrected += ecc_err_count;
+ break;
+ case STAT_ERASED:
+ case STAT_OK:
+ break;
+ default:
+ dev_err(cadence->dev, "read page failed\n");
+ return -EIO;
+ }
+
+ if (oob_required)
+ if (cadence_nand_read_bbm(mtd, chip, page, chip->oob_poi))
+ return -EIO;
+
+ return ecc_err_count;
+}
+
+static int cadence_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+
+ return cadence_nand_read_page(mtd, chip, cadence->buf, 1, page);
+}
+
+static int cadence_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ int oob_skip = cdns_chip->bbm_len;
+ int writesize = mtd->writesize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *tmp_buf = cadence->buf;
+ int i, pos, len;
+ int status;
+
+ status = cadence_nand_select_target(chip);
+ if (status)
+ return status;
+
+ cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0);
+
+ cadence_nand_prepare_data_size(mtd, TT_RAW_PAGE);
+ status = cadence_nand_cdma_transfer(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ page, cadence->buf, NULL,
+ mtd->writesize
+ + mtd->oobsize,
+ 0, DMA_FROM_DEVICE, false);
+
+ switch (status) {
+ case STAT_ERASED:
+ case STAT_OK:
+ break;
+ default:
+ dev_err(cadence->dev, "read raw page failed\n");
+ return -EIO;
+ }
+
+ /* Arrange the buffer for syndrome payload/ecc layout. */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(buf, tmp_buf + pos, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(buf, tmp_buf + writesize + oob_skip,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ u8 *oob = chip->oob_poi;
+ u32 oob_data_offset = (cdns_chip->sector_count - 1) *
+ (cdns_chip->sector_size + chip->ecc.bytes)
+ + cdns_chip->sector_size + oob_skip;
+
+ /* OOB free. */
+ memcpy(oob, tmp_buf + oob_data_offset,
+ cdns_chip->avail_oob_size);
+
+ /* BBM at the beginning of the OOB area. */
+ memcpy(oob, tmp_buf + writesize, oob_skip);
+
+ oob += cdns_chip->avail_oob_size;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (i == (ecc_steps - 1))
+ pos += cdns_chip->avail_oob_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(oob, tmp_buf + pos, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(oob, tmp_buf + writesize + oob_skip,
+ len);
+ oob += len;
+ }
+ }
+ }
+ return 0;
+}
+
+static int cadence_nand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return cadence_nand_read_page_raw(mtd, chip, NULL, true, page);
+}
+
+static void cadence_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ u8 thread_nr = 0;
+ u32 sdma_size;
+ int status;
+ int len_in_words = len >> 2;
+
+ /* Wait until slave DMA interface is ready to data transfer. */
+ status = cadence_nand_wait_on_sdma(cadence, &thread_nr, &sdma_size);
+ if (status) {
+ pr_err("Wait on sdma failed:%x\n", status);
+ hang();
+ }
+
+ if (!cadence->caps1->has_dma) {
+ readsq(cadence->io.virt, buf, len_in_words);
+
+ if (sdma_size > len) {
+ memcpy(cadence->buf, buf + (len_in_words << 2),
+ len - (len_in_words << 2));
+ readsl(cadence->io.virt, cadence->buf,
+ sdma_size / 4 - len_in_words);
+ }
+ }
+}
+
+static void cadence_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ u8 thread_nr = 0;
+ u32 sdma_size;
+ int status;
+ int len_in_words = len >> 2;
+
+ /* Wait until slave DMA interface is ready to data transfer. */
+ status = cadence_nand_wait_on_sdma(cadence, &thread_nr, &sdma_size);
+ if (status) {
+ pr_err("Wait on sdma failed:%x\n", status);
+ hang();
+ }
+
+ if (!cadence->caps1->has_dma) {
+ writesq(cadence->io.virt, buf, len_in_words);
+
+ if (sdma_size > len) {
+ memcpy(cadence->buf, buf + (len_in_words << 2),
+ len - (len_in_words << 2));
+ writesl(cadence->io.virt, cadence->buf,
+ sdma_size / 4 - len_in_words);
+ }
+ }
+}
+
+static int cadence_nand_cmd_opcode(struct nand_chip *chip, unsigned int op_id)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ u64 mini_ctrl_cmd = 0;
+ int ret;
+
+ mini_ctrl_cmd |= GCMD_LAY_TWB;
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, GCMD_LAY_INSTR_CMD);
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_CMD, op_id);
+
+ ret = cadence_nand_generic_cmd_send(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ mini_ctrl_cmd);
+
+ if (ret)
+ dev_err(cadence->dev, "send cmd %x failed\n",
+ op_id);
+
+ return ret;
+}
+
+static int cadence_nand_cmd_address(struct nand_chip *chip,
+ unsigned int naddrs, const u8 *addrs)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ u64 address = 0;
+ u64 mini_ctrl_cmd = 0;
+ int ret;
+ int i;
+
+ mini_ctrl_cmd |= GCMD_LAY_TWB;
+
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR,
+ GCMD_LAY_INSTR_ADDR);
+
+ for (i = 0; i < naddrs; i++)
+ address |= (u64)addrs[i] << (8 * i);
+
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR,
+ address);
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR_SIZE,
+ naddrs - 1);
+
+ ret = cadence_nand_generic_cmd_send(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ mini_ctrl_cmd);
+
+ if (ret)
+ pr_err("send address %llx failed\n", address);
+
+ return ret;
+}
+
+static int cadence_nand_cmd_data(struct nand_chip *chip,
+ unsigned int len, u8 mode)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ u64 mini_ctrl_cmd = 0;
+ int ret;
+
+ mini_ctrl_cmd |= GCMD_LAY_TWB;
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR,
+ GCMD_LAY_INSTR_DATA);
+
+ if (mode)
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_DIR, GCMD_DIR_WRITE);
+
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_SECT_CNT, 1);
+ mini_ctrl_cmd |= FIELD_PREP(GCMD_LAST_SIZE, len);
+
+ ret = cadence_nand_generic_cmd_send(cadence,
+ cdns_chip->cs[chip->cur_cs],
+ mini_ctrl_cmd);
+
+ if (ret) {
+ pr_err("send generic data cmd failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cadence_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ int status;
+
+ status = cadence_nand_wait_for_value(cadence, RBN_SETINGS,
+ TIMEOUT_US,
+ BIT(cdns_chip->cs[chip->cur_cs]),
+ false);
+ return status;
+}
+
+static int cadence_nand_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = cdns_chip->bbm_len;
+ oobregion->length = cdns_chip->avail_oob_size
+ - cdns_chip->bbm_len;
+
+ return 0;
+}
+
+static int cadence_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = cdns_chip->avail_oob_size;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops cadence_nand_ooblayout_ops = {
+ .rfree = cadence_nand_ooblayout_free,
+ .ecc = cadence_nand_ooblayout_ecc,
+};
+
+static int calc_cycl(u32 timing, u32 clock)
+{
+ if (timing == 0 || clock == 0)
+ return 0;
+
+ if ((timing % clock) > 0)
+ return timing / clock;
+ else
+ return timing / clock - 1;
+}
+
+/* Calculate max data valid window. */
+static inline u32 calc_tdvw_max(u32 trp_cnt, u32 clk_period, u32 trhoh_min,
+ u32 board_delay_skew_min, u32 ext_mode)
+{
+ if (ext_mode == 0)
+ clk_period /= 2;
+
+ return (trp_cnt + 1) * clk_period + trhoh_min +
+ board_delay_skew_min;
+}
+
+/* Calculate data valid window. */
+static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min,
+ u32 trea_max, u32 ext_mode)
+{
+ if (ext_mode == 0)
+ clk_period /= 2;
+
+ return (trp_cnt + 1) * clk_period + trhoh_min - trea_max;
+}
+
+static inline int of_get_child_count(const ofnode node)
+{
+ return fdtdec_get_child_count(gd->fdt_blob, ofnode_to_offset(node));
+}
+
+static int cadence_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(mtd_to_nand(mtd));
+ const struct nand_sdr_timings *sdr;
+ struct cadence_nand_timings *t = &cdns_chip->timings;
+ u32 reg;
+ u32 board_delay = cadence->board_delay;
+ u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL,
+ cadence->nf_clk_rate);
+ u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt;
+ u32 tfeat_cnt, trhz_cnt, tvdly_cnt;
+ u32 trhw_cnt, twb_cnt, twh_cnt = 0, twhr_cnt;
+ u32 twp_cnt = 0, trp_cnt = 0, trh_cnt = 0;
+ u32 if_skew = cadence->caps1->if_skew;
+ u32 board_delay_skew_min = board_delay - if_skew;
+ u32 board_delay_skew_max = board_delay + if_skew;
+ u32 dqs_sampl_res, phony_dqs_mod;
+ u32 tdvw, tdvw_min, tdvw_max;
+ u32 ext_rd_mode, ext_wr_mode;
+ u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0;
+ u32 sampling_point;
+
+ sdr = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ memset(t, 0, sizeof(*t));
+ /* Sampling point calculation. */
+ if (cadence->caps2.is_phy_type_dll)
+ phony_dqs_mod = 2;
+ else
+ phony_dqs_mod = 1;
+
+ dqs_sampl_res = clk_period / phony_dqs_mod;
+
+ tdvw_min = sdr->tREA_max + board_delay_skew_max;
+ /*
+ * The idea of those calculation is to get the optimum value
+ * for tRP and tRH timings. If it is NOT possible to sample data
+ * with optimal tRP/tRH settings, the parameters will be extended.
+ * If clk_period is 50ns (the lowest value) this condition is met
+ * for SDR timing modes 1, 2, 3, 4 and 5.
+ * If clk_period is 20ns the condition is met only for SDR timing
+ * mode 5.
+ */
+ if (sdr->tRC_min <= clk_period &&
+ sdr->tRP_min <= (clk_period / 2) &&
+ sdr->tREH_min <= (clk_period / 2)) {
+ /* Performance mode. */
+ ext_rd_mode = 0;
+ tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min,
+ sdr->tREA_max, ext_rd_mode);
+ tdvw_max = calc_tdvw_max(trp_cnt, clk_period, sdr->tRHOH_min,
+ board_delay_skew_min,
+ ext_rd_mode);
+ /*
+ * Check if data valid window and sampling point can be found
+ * and is not on the edge (ie. we have hold margin).
+ * If not extend the tRP timings.
+ */
+ if (tdvw > 0) {
+ if (tdvw_max <= tdvw_min ||
+ (tdvw_max % dqs_sampl_res) == 0) {
+ /*
+ * No valid sampling point so the RE pulse need
+ * to be widen widening by half clock cycle.
+ */
+ ext_rd_mode = 1;
+ }
+ } else {
+ /*
+ * There is no valid window
+ * to be able to sample data the tRP need to be widen.
+ * Very safe calculations are performed here.
+ */
+ trp_cnt = (sdr->tREA_max + board_delay_skew_max
+ + dqs_sampl_res) / clk_period;
+ ext_rd_mode = 1;
+ }
+
+ } else {
+ /* Extended read mode. */
+ u32 trh;
+
+ ext_rd_mode = 1;
+ trp_cnt = calc_cycl(sdr->tRP_min, clk_period);
+ trh = sdr->tRC_min - ((trp_cnt + 1) * clk_period);
+ if (sdr->tREH_min >= trh)
+ trh_cnt = calc_cycl(sdr->tREH_min, clk_period);
+ else
+ trh_cnt = calc_cycl(trh, clk_period);
+
+ tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min,
+ sdr->tREA_max, ext_rd_mode);
+ /*
+ * Check if data valid window and sampling point can be found
+ * or if it is at the edge check if previous is valid
+ * - if not extend the tRP timings.
+ */
+ if (tdvw > 0) {
+ tdvw_max = calc_tdvw_max(trp_cnt, clk_period,
+ sdr->tRHOH_min,
+ board_delay_skew_min,
+ ext_rd_mode);
+
+ if ((((tdvw_max / dqs_sampl_res)
+ * dqs_sampl_res) <= tdvw_min) ||
+ (((tdvw_max % dqs_sampl_res) == 0) &&
+ (((tdvw_max / dqs_sampl_res - 1)
+ * dqs_sampl_res) <= tdvw_min))) {
+ /*
+ * Data valid window width is lower than
+ * sampling resolution and do not hit any
+ * sampling point to be sure the sampling point
+ * will be found the RE low pulse width will be
+ * extended by one clock cycle.
+ */
+ trp_cnt = trp_cnt + 1;
+ }
+ } else {
+ /*
+ * There is no valid window to be able to sample data.
+ * The tRP need to be widen.
+ * Very safe calculations are performed here.
+ */
+ trp_cnt = (sdr->tREA_max + board_delay_skew_max
+ + dqs_sampl_res) / clk_period;
+ }
+ }
+
+ tdvw_max = calc_tdvw_max(trp_cnt, clk_period,
+ sdr->tRHOH_min,
+ board_delay_skew_min, ext_rd_mode);
+
+ if (sdr->tWC_min <= clk_period &&
+ (sdr->tWP_min + if_skew) <= (clk_period / 2) &&
+ (sdr->tWH_min + if_skew) <= (clk_period / 2)) {
+ ext_wr_mode = 0;
+ } else {
+ u32 twh;
+
+ ext_wr_mode = 1;
+ twp_cnt = calc_cycl(sdr->tWP_min + if_skew, clk_period);
+ if ((twp_cnt + 1) * clk_period < (sdr->tALS_min + if_skew))
+ twp_cnt = calc_cycl(sdr->tALS_min + if_skew,
+ clk_period);
+
+ twh = (sdr->tWC_min - (twp_cnt + 1) * clk_period);
+ if (sdr->tWH_min >= twh)
+ twh = sdr->tWH_min;
+
+ twh_cnt = calc_cycl(twh + if_skew, clk_period);
+ }
+
+ reg = FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRH, trh_cnt);
+ reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRP, trp_cnt);
+ reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWH, twh_cnt);
+ reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWP, twp_cnt);
+ t->async_toggle_timings = reg;
+ dev_dbg(cadence->dev, "ASYNC_TOGGLE_TIMINGS_SDR\t%x\n", reg);
+
+ tadl_cnt = calc_cycl((sdr->tADL_min + if_skew), clk_period);
+ tccs_cnt = calc_cycl((sdr->tCCS_min + if_skew), clk_period);
+ twhr_cnt = calc_cycl((sdr->tWHR_min + if_skew), clk_period);
+ trhw_cnt = calc_cycl((sdr->tRHW_min + if_skew), clk_period);
+ reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt);
+
+ /*
+ * If timing exceeds delay field in timing register
+ * then use maximum value.
+ */
+ if (FIELD_FIT(TIMINGS0_TCCS, tccs_cnt))
+ reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt);
+ else
+ reg |= TIMINGS0_TCCS;
+
+ reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt);
+ reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt);
+ t->timings0 = reg;
+ dev_dbg(cadence->dev, "TIMINGS0_SDR\t%x\n", reg);
+
+ /* The following is related to single signal so skew is not needed. */
+ trhz_cnt = calc_cycl(sdr->tRHZ_max, clk_period);
+ trhz_cnt = trhz_cnt + 1;
+ twb_cnt = calc_cycl((sdr->tWB_max + board_delay), clk_period);
+ /*
+ * Because of the two stage syncflop the value must be increased by 3
+ * first value is related with sync, second value is related
+ * with output if delay.
+ */
+ twb_cnt = twb_cnt + 3 + 5;
+ /*
+ * The following is related to the we edge of the random data input
+ * sequence so skew is not needed.
+ */
+ tvdly_cnt = calc_cycl(500000 + if_skew, clk_period);
+ reg = FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt);
+ reg |= FIELD_PREP(TIMINGS1_TWB, twb_cnt);
+ reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt);
+ t->timings1 = reg;
+ dev_dbg(cadence->dev, "TIMINGS1_SDR\t%x\n", reg);
+
+ tfeat_cnt = calc_cycl(sdr->tFEAT_max, clk_period);
+ if (tfeat_cnt < twb_cnt)
+ tfeat_cnt = twb_cnt;
+
+ tceh_cnt = calc_cycl(sdr->tCEH_min, clk_period);
+ tcs_cnt = calc_cycl((sdr->tCS_min + if_skew), clk_period);
+
+ reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt);
+ reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt);
+ reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt);
+ t->timings2 = reg;
+ dev_dbg(cadence->dev, "TIMINGS2_SDR\t%x\n", reg);
+
+ if (cadence->caps2.is_phy_type_dll) {
+ reg = DLL_PHY_CTRL_DLL_RST_N;
+ if (ext_wr_mode)
+ reg |= DLL_PHY_CTRL_EXTENDED_WR_MODE;
+ if (ext_rd_mode)
+ reg |= DLL_PHY_CTRL_EXTENDED_RD_MODE;
+
+ reg |= FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, 7);
+ reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, 7);
+ t->dll_phy_ctrl = reg;
+ dev_dbg(cadence->dev, "DLL_PHY_CTRL_SDR\t%x\n", reg);
+ }
+
+ /* Sampling point calculation. */
+ if ((tdvw_max % dqs_sampl_res) > 0)
+ sampling_point = tdvw_max / dqs_sampl_res;
+ else
+ sampling_point = (tdvw_max / dqs_sampl_res - 1);
+
+ if (sampling_point * dqs_sampl_res > tdvw_min) {
+ dll_phy_dqs_timing =
+ FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, 4);
+ dll_phy_dqs_timing |= PHY_DQS_TIMING_USE_PHONY_DQS;
+ phony_dqs_timing = sampling_point / phony_dqs_mod;
+
+ if ((sampling_point % 2) > 0) {
+ dll_phy_dqs_timing |= PHY_DQS_TIMING_PHONY_DQS_SEL;
+ if ((tdvw_max % dqs_sampl_res) == 0)
+ /*
+ * Calculation for sampling point at the edge
+ * of data and being odd number.
+ */
+ phony_dqs_timing = (tdvw_max / dqs_sampl_res)
+ / phony_dqs_mod - 1;
+
+ if (!cadence->caps2.is_phy_type_dll)
+ phony_dqs_timing--;
+
+ } else {
+ phony_dqs_timing--;
+ }
+ rd_del_sel = phony_dqs_timing + 3;
+ } else {
+ dev_warn(cadence->dev,
+ "ERROR : cannot find valid sampling point\n");
+ }
+
+ reg = FIELD_PREP(PHY_CTRL_PHONY_DQS, phony_dqs_timing);
+ if (cadence->caps2.is_phy_type_dll)
+ reg |= PHY_CTRL_SDR_DQS;
+ t->phy_ctrl = reg;
+ dev_dbg(cadence->dev, "PHY_CTRL_REG_SDR\t%x\n", reg);
+
+ if (cadence->caps2.is_phy_type_dll) {
+ dev_dbg(cadence->dev, "PHY_TSEL_REG_SDR\t%x\n", 0);
+ dev_dbg(cadence->dev, "PHY_DQ_TIMING_REG_SDR\t%x\n", 2);
+ dev_dbg(cadence->dev, "PHY_DQS_TIMING_REG_SDR\t%x\n",
+ dll_phy_dqs_timing);
+ t->phy_dqs_timing = dll_phy_dqs_timing;
+
+ reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel);
+ dev_dbg(cadence->dev, "PHY_GATE_LPBK_CTRL_REG_SDR\t%x\n",
+ reg);
+ t->phy_gate_lpbk_ctrl = reg;
+
+ dev_dbg(cadence->dev, "PHY_DLL_MASTER_CTRL_REG_SDR\t%lx\n",
+ PHY_DLL_MASTER_CTRL_BYPASS_MODE);
+ dev_dbg(cadence->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0);
+ }
+ return 0;
+}
+
+static int cadence_nand_attach_chip(struct nand_chip *chip)
+{
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ static struct nand_ecclayout nand_oob;
+ u32 ecc_size;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ if (chip->options & NAND_BUSWIDTH_16) {
+ ret = cadence_nand_set_access_width16(cadence, true);
+ if (ret)
+ return ret;
+ }
+
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+ chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ cdns_chip->bbm_offs = chip->badblockpos;
+ cdns_chip->bbm_offs &= ~0x01;
+ /* this value should be even number */
+ cdns_chip->bbm_len = 2;
+
+ ret = cadence_ecc_setup(mtd, chip, mtd->oobsize - cdns_chip->bbm_len);
+ if (ret) {
+ dev_err(cadence->dev, "ECC configuration failed\n");
+ return ret;
+ }
+
+ dev_dbg(cadence->dev,
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
+
+ /* Error correction configuration. */
+ cdns_chip->sector_size = chip->ecc.size;
+ cdns_chip->sector_count = mtd->writesize / cdns_chip->sector_size;
+ ecc_size = cdns_chip->sector_count * chip->ecc.bytes;
+
+ cdns_chip->avail_oob_size = mtd->oobsize - ecc_size;
+
+ if (cdns_chip->avail_oob_size > cadence->bch_metadata_size)
+ cdns_chip->avail_oob_size = cadence->bch_metadata_size;
+
+ if ((cdns_chip->avail_oob_size + cdns_chip->bbm_len + ecc_size)
+ > mtd->oobsize)
+ cdns_chip->avail_oob_size -= 4;
+
+ ret = cadence_nand_get_ecc_strength_idx(cadence, chip->ecc.strength);
+ if (ret < 0)
+ return -EINVAL;
+
+ cdns_chip->corr_str_idx = (u8)ret;
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ cadence_nand_set_ecc_strength(cadence,
+ cdns_chip->corr_str_idx);
+
+ cadence_nand_set_erase_detection(cadence, true,
+ chip->ecc.strength);
+
+ dev_dbg(cadence->dev,
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
+
+ /* Override the default read operations. */
+ chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
+ chip->ecc.read_page = cadence_nand_read_page;
+ chip->ecc.read_page_raw = cadence_nand_read_page_raw;
+ chip->ecc.write_page = cadence_nand_write_page;
+ chip->ecc.write_page_raw = cadence_nand_write_page_raw;
+ chip->ecc.read_oob = cadence_nand_read_oob;
+ chip->ecc.write_oob = cadence_nand_write_oob;
+ chip->ecc.read_oob_raw = cadence_nand_read_oob_raw;
+ chip->ecc.write_oob_raw = cadence_nand_write_oob_raw;
+ chip->erase = cadence_nand_erase;
+
+ if ((mtd->writesize + mtd->oobsize) > cadence->buf_size)
+ cadence->buf_size = mtd->writesize + mtd->oobsize;
+
+ mtd_set_ooblayout(mtd, &cadence_nand_ooblayout_ops);
+
+ nand_oob.eccbytes = cdns_chip->chip.ecc.bytes;
+ cdns_chip->chip.ecc.layout = &nand_oob;
+
+ return 0;
+}
+
+/* Dummy implementation: we don't support multiple chips */
+static void cadence_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ switch (chipnr) {
+ case -1:
+ case 0:
+ break;
+
+ default:
+ WARN_ON(chipnr);
+ }
+}
+
+static int cadence_nand_status(struct mtd_info *mtd, unsigned int command)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ ret = cadence_nand_cmd_opcode(chip, command);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_cmd_data(chip, 1, GCMD_DIR_READ);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cadence_nand_readid(struct mtd_info *mtd, int offset_in_page, unsigned int command)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u8 addrs = (u8)offset_in_page;
+ int ret = 0;
+
+ ret = cadence_nand_cmd_opcode(chip, command);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &addrs);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_cmd_data(chip, 8, GCMD_DIR_READ);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cadence_nand_param(struct mtd_info *mtd, u8 offset_in_page, unsigned int command)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ ret = cadence_nand_cmd_opcode(chip, command);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &offset_in_page);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_waitfunc(mtd, chip);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_cmd_data(chip, sizeof(struct nand_jedec_params), GCMD_DIR_READ);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cadence_nand_reset(struct mtd_info *mtd, unsigned int command)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ ret = cadence_nand_cmd_opcode(chip, command);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_waitfunc(mtd, chip);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cadence_nand_features(struct mtd_info *mtd, u8 offset_in_page, u32 command)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ ret = cadence_nand_cmd_opcode(chip, command);
+ if (ret)
+ return ret;
+
+ ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &offset_in_page);
+ if (ret)
+ return ret;
+
+ if (command == NAND_CMD_GET_FEATURES)
+ ret = cadence_nand_cmd_data(chip, ONFI_SUBFEATURE_PARAM_LEN,
+ GCMD_DIR_READ);
+ else
+ ret = cadence_nand_cmd_data(chip, ONFI_SUBFEATURE_PARAM_LEN,
+ GCMD_DIR_WRITE);
+
+ return ret;
+}
+
+static void cadence_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int offset_in_page, int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ int ret = 0;
+
+ cadence->cmd = command;
+ switch (command) {
+ case NAND_CMD_STATUS:
+ ret = cadence_nand_status(mtd, command);
+ break;
+
+ case NAND_CMD_READID:
+ ret = cadence_nand_readid(mtd, offset_in_page, command);
+ break;
+
+ case NAND_CMD_PARAM:
+ ret = cadence_nand_param(mtd, offset_in_page, command);
+ break;
+
+ case NAND_CMD_RESET:
+ ret = cadence_nand_reset(mtd, command);
+ break;
+
+ case NAND_CMD_SET_FEATURES:
+ case NAND_CMD_GET_FEATURES:
+ ret = cadence_nand_features(mtd, offset_in_page, command);
+ break;
+ /*
+ * ecc will override other command for read, write and erase
+ */
+ default:
+ break;
+ }
+
+ if (cadence->cmd == NAND_CMD_RESET) {
+ ret = cadence_nand_select_target(chip);
+ if (ret)
+ dev_err(cadence->dev, "Chip select failure after reset\n");
+ }
+
+ if (ret != 0)
+ printf("ERROR:%s:command:0x%x\n", __func__, cadence->cmd);
+}
+
+static int cadence_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+
+ if (cadence_nand_wait_for_value(cadence, CTRL_STATUS,
+ TIMEOUT_US,
+ CTRL_STATUS_CTRL_BUSY, true))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static u8 cadence_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller);
+ u32 size = 1;
+ u8 val;
+
+ if (cadence->buf_index == 0) {
+ if (cadence->cmd == NAND_CMD_READID)
+ size = 8;
+ else if (cadence->cmd == NAND_CMD_PARAM)
+ size = sizeof(struct nand_jedec_params);
+ else if (cadence->cmd == NAND_CMD_GET_FEATURES)
+ size = ONFI_SUBFEATURE_PARAM_LEN;
+
+ cadence_nand_read_buf(mtd, &cadence->buf[0], size);
+ }
+
+ val = *(&cadence->buf[0] + cadence->buf_index);
+ cadence->buf_index++;
+
+ return val;
+}
+
+static void cadence_nand_write_byte(struct mtd_info *mtd, u8 byte)
+{
+ cadence_nand_write_buf(mtd, &byte, 1);
+}
+
+static int cadence_nand_chip_init(struct cadence_nand_info *cadence, ofnode node)
+{
+ struct cdns_nand_chip *cdns_chip;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ int ret, i;
+ int nsels;
+ u32 cs;
+
+ if (!ofnode_get_property(node, "reg", &nsels))
+ return -ENODEV;
+
+ nsels /= sizeof(u32);
+ if (nsels <= 0) {
+ dev_err(cadence->dev, "invalid reg property size %d\n", nsels);
+ return -EINVAL;
+ }
+
+ cdns_chip = devm_kzalloc(cadence->dev, sizeof(*cdns_chip) +
+ (nsels * sizeof(u8)), GFP_KERNEL);
+ if (!cdns_chip)
+ return -ENODEV;
+
+ cdns_chip->nsels = nsels;
+ for (i = 0; i < nsels; i++) {
+ /* Retrieve CS id. */
+ ret = ofnode_read_u32_index(node, "reg", i, &cs);
+ if (ret) {
+ dev_err(cadence->dev,
+ "could not retrieve reg property: %d\n",
+ ret);
+ goto free_buf;
+ }
+
+ if (cs >= cadence->caps2.max_banks) {
+ dev_err(cadence->dev,
+ "invalid reg value: %u (max CS = %d)\n",
+ cs, cadence->caps2.max_banks);
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ if (test_and_set_bit(cs, &cadence->assigned_cs)) {
+ dev_err(cadence->dev,
+ "CS %d already assigned\n", cs);
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ cdns_chip->cs[i] = cs;
+ }
+
+ chip = &cdns_chip->chip;
+ chip->controller = &cadence->controller;
+ nand_set_flash_node(chip, node);
+ mtd = nand_to_mtd(chip);
+ mtd->dev->parent = cadence->dev;
+
+ chip->options |= NAND_BUSWIDTH_AUTO;
+ chip->select_chip = cadence_nand_select_chip;
+ chip->cmdfunc = cadence_nand_cmdfunc;
+ chip->dev_ready = cadence_nand_dev_ready;
+ chip->read_byte = cadence_nand_read_byte;
+ chip->write_byte = cadence_nand_write_byte;
+ chip->waitfunc = cadence_nand_waitfunc;
+ chip->read_buf = cadence_nand_read_buf;
+ chip->write_buf = cadence_nand_write_buf;
+ chip->setup_data_interface = cadence_setup_data_interface;
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret) {
+ dev_err(cadence->dev, "Chip identification failure\n");
+ goto free_buf;
+ }
+
+ ret = cadence_nand_attach_chip(chip);
+ if (ret) {
+ dev_err(cadence->dev, "Chip not able to attached\n");
+ goto free_buf;
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(cadence->dev, "could not scan the nand chip\n");
+ goto free_buf;
+ }
+
+ ret = nand_register(0, mtd);
+ if (ret) {
+ dev_err(cadence->dev, "Failed to register MTD: %d\n", ret);
+ goto free_buf;
+ }
+
+ return 0;
+
+free_buf:
+ devm_kfree(cadence->dev, cdns_chip);
+ return ret;
+}
+
+static int cadence_nand_chips_init(struct cadence_nand_info *cadence)
+{
+ struct udevice *dev = cadence->dev;
+ ofnode node = dev_ofnode(dev);
+ ofnode nand_node;
+ int max_cs = cadence->caps2.max_banks;
+ int nchips, ret;
+
+ nchips = of_get_child_count(node);
+
+ if (nchips > max_cs) {
+ dev_err(cadence->dev,
+ "too many NAND chips: %d (max = %d CS)\n",
+ nchips, max_cs);
+ return -EINVAL;
+ }
+
+ ofnode_for_each_subnode(nand_node, node) {
+ ret = cadence_nand_chip_init(cadence, nand_node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cadence_nand_init(struct cadence_nand_info *cadence)
+{
+ int ret;
+
+ cadence->cdma_desc = dma_alloc_coherent(sizeof(*cadence->cdma_desc),
+ (unsigned long *)&cadence->dma_cdma_desc);
+ if (!cadence->cdma_desc)
+ return -ENOMEM;
+
+ cadence->buf_size = SZ_16K;
+ cadence->buf = kmalloc(cadence->buf_size, GFP_KERNEL);
+ if (!cadence->buf) {
+ ret = -ENOMEM;
+ goto free_buf_desc;
+ }
+
+ //Hardware initialization
+ ret = cadence_nand_hw_init(cadence);
+ if (ret)
+ goto free_buf;
+
+ cadence->curr_corr_str_idx = 0xFF;
+
+ ret = cadence_nand_chips_init(cadence);
+ if (ret) {
+ dev_err(cadence->dev, "Failed to register MTD: %d\n",
+ ret);
+ goto free_buf;
+ }
+
+ kfree(cadence->buf);
+ cadence->buf = kzalloc(cadence->buf_size, GFP_KERNEL);
+ if (!cadence->buf) {
+ ret = -ENOMEM;
+ goto free_buf_desc;
+ }
+
+ return 0;
+
+free_buf:
+ kfree(cadence->buf);
+
+free_buf_desc:
+ dma_free_coherent(cadence->cdma_desc);
+
+ return ret;
+}
+
+static const struct cadence_nand_dt_devdata cadence_nand_default = {
+ .if_skew = 0,
+ .has_dma = 0,
+};
+
+static const struct udevice_id cadence_nand_dt_ids[] = {
+ {
+ .compatible = "cdns,nand",
+ .data = (unsigned long)&cadence_nand_default
+ }, {}
+};
+
+static int cadence_nand_dt_probe(struct udevice *dev)
+{
+ struct cadence_nand_info *cadence = dev_get_priv(dev);
+ const struct udevice_id *of_id;
+ const struct cadence_nand_dt_devdata *devdata;
+ struct resource res;
+ int ret;
+ u32 val;
+
+ if (!dev) {
+ dev_warn(dev, "Device ptr null\n");
+ return -EINVAL;
+ }
+
+ of_id = &cadence_nand_dt_ids[0];
+ devdata = (struct cadence_nand_dt_devdata *)of_id->data;
+
+ cadence->caps1 = devdata;
+ cadence->dev = dev;
+
+ ret = clk_get_by_index(dev, 0, &cadence->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&cadence->clk);
+ if (ret && ret != -ENOSYS && ret != -ENOMEM) {
+ dev_err(dev, "failed to enable clock\n");
+ return ret;
+ }
+ cadence->nf_clk_rate = clk_get_rate(&cadence->clk);
+
+ ret = reset_get_by_index(dev, 1, &cadence->softphy_reset);
+ if (ret) {
+ if (ret != -ENOMEM)
+ dev_warn(dev, "Can't get softphy_reset: %d\n", ret);
+ } else {
+ reset_deassert(&cadence->softphy_reset);
+ }
+
+ ret = reset_get_by_index(dev, 0, &cadence->nand_reset);
+ if (ret) {
+ if (ret != -ENOMEM)
+ dev_warn(dev, "Can't get nand_reset: %d\n", ret);
+ } else {
+ reset_deassert(&cadence->nand_reset);
+ }
+
+ ret = dev_read_resource_byname(dev, "reg", &res);
+ if (ret)
+ return ret;
+ cadence->reg = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "sdma", &res);
+ if (ret)
+ return ret;
+ cadence->io.dma = res.start;
+ cadence->io.virt = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = ofnode_read_u32(dev_ofnode(dev->parent),
+ "cdns,board-delay-ps", &val);
+ if (ret) {
+ val = 4830;
+ dev_info(cadence->dev,
+ "missing cdns,board-delay-ps property, %d was set\n",
+ val);
+ }
+ cadence->board_delay = val;
+
+ ret = cadence_nand_init(cadence);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(cadence_nand_dt) = {
+ .name = "cadence-nand-dt",
+ .id = UCLASS_MTD,
+ .of_match = cadence_nand_dt_ids,
+ .probe = cadence_nand_dt_probe,
+ .priv_auto = sizeof(struct cadence_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_DRIVER_GET(cadence_nand_dt),
+ &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize Cadence NAND controller. (error %d)\n",
+ ret);
+}
diff --git a/drivers/mtd/nand/raw/cadence_spl.c b/drivers/mtd/nand/raw/cadence_spl.c
new file mode 100644
index 00000000000..17058e49faa
--- /dev/null
+++ b/drivers/mtd/nand/raw/cadence_spl.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Intel Corporation <www.intel.com>
+ */
+
+#include <cadence-nand.h>
+#include <dm.h>
+#include <hang.h>
+#include <nand.h>
+#include <system-constants.h>
+
+/* Unselect after operation */
+void nand_deselect(void)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+
+ mtd = get_nand_dev_by_index(nand_curr_device);
+ if (!mtd)
+ hang();
+ chip = mtd_to_nand(mtd);
+
+ if (chip->select_chip)
+ chip->select_chip(mtd, -1);
+}
+
+static int nand_is_bad_block(int block)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ loff_t ofs = block * CONFIG_SYS_NAND_BLOCK_SIZE;
+
+ mtd = get_nand_dev_by_index(nand_curr_device);
+ if (!mtd)
+ hang();
+ chip = mtd_to_nand(mtd);
+
+ return chip->block_bad(mtd, ofs);
+}
+
+static int nand_read_page(int block, int page, uchar *dst)
+{
+ struct mtd_info *mtd;
+ int page_addr = block * SYS_NAND_BLOCK_PAGES + page;
+ loff_t ofs = page_addr * CONFIG_SYS_NAND_PAGE_SIZE;
+ int ret;
+ size_t len = CONFIG_SYS_NAND_PAGE_SIZE;
+
+ mtd = get_nand_dev_by_index(nand_curr_device);
+ if (!mtd)
+ hang();
+
+ ret = nand_read(mtd, ofs, &len, dst);
+ if (ret)
+ printf("nand_read failed %d\n", ret);
+
+ return ret;
+}
+#include "nand_spl_loaders.c"
diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 28c851f103b..7e683f49c5e 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -618,9 +618,7 @@ static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *ch
memcpy(meson_chip->data_buf, buf, mtd->writesize);
memset(meson_chip->info_buf, 0, chip->ecc.steps * PER_INFO_BYTE);
-
- if (oob_required)
- meson_nfc_set_user_byte(chip, chip->oob_poi);
+ meson_nfc_set_user_byte(chip, chip->oob_poi);
return meson_nfc_write_page_sub(chip, page, false);
}
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 1b65c6f6443..d3d1b93947b 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -306,6 +306,35 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
}
+/*
+ * nand_bbm_get_next_page - Get the next page for bad block markers
+ * @chip: The NAND chip
+ * @page: First page to start checking for bad block marker usage
+ *
+ * Returns an integer that corresponds to the page offset within a block, for
+ * a page that is used to store bad block markers. If no more pages are
+ * available, -EINVAL is returned.
+ */
+int nand_bbm_get_next_page(struct nand_chip *chip, int page)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int last_page = ((mtd->erasesize - mtd->writesize) >>
+ chip->page_shift) & chip->pagemask;
+ unsigned int bbm_flags = NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE
+ | NAND_BBM_LASTPAGE;
+
+ if (page == 0 && !(chip->options & bbm_flags))
+ return 0;
+ if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE)
+ return 0;
+ if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE)
+ return 1;
+ if (page <= last_page && chip->options & NAND_BBM_LASTPAGE)
+ return last_page;
+
+ return -EINVAL;
+}
+
/**
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
* @mtd: MTD device structure
@@ -315,40 +344,32 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
*/
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
{
- int page, res = 0, i = 0;
struct nand_chip *chip = mtd_to_nand(mtd);
- u16 bad;
+ int first_page, page_offset;
+ int res;
+ u8 bad;
- if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
- ofs += mtd->erasesize - mtd->writesize;
+ first_page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ page_offset = nand_bbm_get_next_page(chip, 0);
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ while (page_offset >= 0) {
+ res = chip->ecc.read_oob(mtd, chip, first_page + page_offset);
+ if (res < 0)
+ return res;
- do {
- if (chip->options & NAND_BUSWIDTH_16) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB,
- chip->badblockpos & 0xFE, page);
- bad = cpu_to_le16(chip->read_word(mtd));
- if (chip->badblockpos & 0x1)
- bad >>= 8;
- else
- bad &= 0xFF;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
- page);
- bad = chip->read_byte(mtd);
- }
+ bad = chip->oob_poi[chip->badblockpos];
if (likely(chip->badblockbits == 8))
res = bad != 0xFF;
else
res = hweight8(bad) < chip->badblockbits;
- ofs += mtd->writesize;
- page = (int)(ofs >> chip->page_shift) & chip->pagemask;
- i++;
- } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+ if (res)
+ return res;
- return res;
+ page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
+ }
+
+ return 0;
}
/**
@@ -774,6 +795,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
NAND_NCE | NAND_CTRL_CHANGE);
/* This applies to read commands */
+ fallthrough;
default:
/*
* If we don't have access to the busy pin, we apply the given
@@ -4974,6 +4996,7 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!ecc->read_page)
ecc->read_page = nand_read_page_hwecc_oob_first;
+ fallthrough;
case NAND_ECC_HW:
/* Use standard hwecc read page function? */
if (!ecc->read_page)
@@ -4993,6 +5016,7 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
ecc->write_subpage = nand_write_subpage_hwecc;
+ fallthrough;
case NAND_ECC_HW_SYNDROME:
if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
(!ecc->read_page ||
@@ -5027,6 +5051,7 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc->size, mtd->writesize);
ecc->mode = NAND_ECC_SOFT;
+ fallthrough;
case NAND_ECC_SOFT:
ecc->calculate = nand_calculate_ecc;
ecc->correct = nand_correct_data;
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/mtd/spi/spi-nor-tiny.c b/drivers/mtd/spi/spi-nor-tiny.c
index 5755c5eed29..23de64a1520 100644
--- a/drivers/mtd/spi/spi-nor-tiny.c
+++ b/drivers/mtd/spi/spi-nor-tiny.c
@@ -219,6 +219,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
case SNOR_MFR_MICRON:
/* Some Micron need WREN command; all will accept it */
need_wren = true;
+ fallthrough;
case SNOR_MFR_MACRONIX:
case SNOR_MFR_WINBOND:
if (need_wren)
diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c
index 2ef8fde3d32..306da509ec4 100644
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -934,6 +934,7 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
* be a result of power cut during erasure.
*/
ai->maybe_bad_peb_count += 1;
+ fallthrough;
case UBI_IO_BAD_HDR:
if (ec_err)
/*
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 104537784ab..50e43928af0 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1400,12 +1400,15 @@ static int __init bytes_str_to_int(const char *str)
switch (*endp) {
case 'G':
result *= 1024;
+ fallthrough;
case 'M':
result *= 1024;
+ fallthrough;
case 'K':
result *= 1024;
if (endp[1] == 'i' && endp[2] == 'B')
endp += 2;
+ fallthrough;
case '\0':
break;
default:
diff --git a/drivers/mtd/ubi/part.c b/drivers/mtd/ubi/part.c
index 13d1f165c30..6c017eb7299 100644
--- a/drivers/mtd/ubi/part.c
+++ b/drivers/mtd/ubi/part.c
@@ -47,7 +47,7 @@ static int __maybe_unused part_get_info_ubi(struct blk_desc *dev_desc, int part_
*/
vol = ubi_get_volume_by_index(part_idx - 1);
if (!vol)
- return 0;
+ return -ENOENT;
snprintf(info->name, PART_NAME_LEN, vol->name);
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
index f15ee4f833f..2b95eb02177 100644
--- a/drivers/mux/Kconfig
+++ b/drivers/mux/Kconfig
@@ -10,6 +10,16 @@ config MULTIPLEXER
if MULTIPLEXER
+config SPL_MUX_MMIO
+ bool "MMIO register bitfield-controlled Multiplexer"
+ depends on MULTIPLEXER && SYSCON
+ help
+ MMIO register bitfield-controlled Multiplexer controller.
+
+ The driver builds multiplexer controllers for bitfields in a syscon
+ register. For N bit wide bitfields, there will be 2^N possible
+ multiplexer states.
+
config MUX_MMIO
bool "MMIO register bitfield-controlled Multiplexer"
depends on MULTIPLEXER && SYSCON
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 1563404ca17..4434d364777 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -48,7 +48,6 @@ config DM_DSA
bool "Enable Driver Model for DSA switches"
depends on DM_MDIO
depends on PHY_FIXED
- depends on !NET_LWIP
help
Enable driver model for DSA switches
@@ -122,6 +121,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"
@@ -237,6 +244,13 @@ config DWC_ETH_QOS
Of Service) IP block. The IP supports many options for bus type,
clocking/reset structure, and feature list.
+config DWC_ETH_QOS_ADI
+ bool "Synopsys DWC Ethernet QOS device support for ADI SC59x-64 parts"
+ depends on DWC_ETH_QOS
+ help
+ The Synopsis Designware Ethernet QoS IP block with the specific
+ configuration used in the ADI ADSP-SC59X 64 bit SoCs
+
config DWC_ETH_QOS_IMX
bool "Synopsys DWC Ethernet QOS device support for IMX"
depends on DWC_ETH_QOS
@@ -343,7 +357,7 @@ config ESSEDMA
config ETH_SANDBOX
depends on SANDBOX
- depends on NET
+ depends on NET || NET_LWIP
default y
bool "Sandbox: Mocked Ethernet driver"
help
@@ -352,17 +366,6 @@ config ETH_SANDBOX
This driver is particularly useful in the test/dm/eth.c tests
-config ETH_SANDBOX_LWIP
- depends on SANDBOX
- depends on NET_LWIP
- default y
- bool "Sandbox: Mocked Ethernet driver (for NET_LWIP)"
- help
- This driver is meant as a replacement for ETH_SANDBOX when
- the network stack is NET_LWIP rather than NET. It currently
- does nothing, i.e. it drops the sent packets and never receives
- data.
-
config ETH_SANDBOX_RAW
depends on SANDBOX
depends on NET
@@ -852,6 +855,7 @@ config RENESAS_ETHER_SWITCH
config RENESAS_RAVB
bool "Renesas Ethernet AVB MAC"
depends on RCAR_64
+ select BITBANGMII
select PHYLIB
select PHY_ETHERNET_ID
help
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 80d70212971..67bba3a8536 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
@@ -19,6 +20,7 @@ obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o
obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o
obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o
+obj-$(CONFIG_DWC_ETH_QOS_ADI) += dwc_eth_qos_adi.o
obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o
obj-$(CONFIG_DWC_ETH_QOS_INTEL) += dwc_eth_qos_intel.o
obj-$(CONFIG_DWC_ETH_QOS_ROCKCHIP) += dwc_eth_qos_rockchip.o
@@ -39,7 +41,6 @@ obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o
obj-$(CONFIG_ETH_SANDBOX) += sandbox.o
obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw-bus.o
obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o
-obj-$(CONFIG_ETH_SANDBOX_LWIP) += sandbox-lwip.o
obj-$(CONFIG_FEC_MXC) += fec_mxc.o
obj-$(CONFIG_FMAN_ENET) += fm/
obj-$(CONFIG_FMAN_ENET) += fsl_mdio.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(&eth->xsi_rsts);
+ if (ret)
+ return ret;
+
+ ret = reset_assert_bulk(&eth->rsts);
+ if (ret)
+ return ret;
+
+ mdelay(20);
+
+ ret = reset_deassert_bulk(&eth->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, &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", &eth->rsts.resets[0]);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_name(dev, "pdma", &eth->rsts.resets[1]);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_name(dev, "qdma", &eth->rsts.resets[2]);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_name(dev, "hsi0-mac", &eth->xsi_rsts.resets[0]);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_name(dev, "hsi1-mac", &eth->xsi_rsts.resets[1]);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_name(dev, "hsi-mac", &eth->xsi_rsts.resets[2]);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_name(dev, "xfp-mac", &eth->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 = &eth->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 = &eth->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 = &eth->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 = &eth->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 = &eth->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/designware.c b/drivers/net/designware.c
index 0a1fff38727..0f93c25e3fe 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -33,6 +33,9 @@
#include <linux/printk.h>
#include <power/regulator.h>
#include "designware.h"
+#if IS_ENABLED(CONFIG_ARCH_NPCM8XX)
+#include <asm/arch/gmac.h>
+#endif
static int dw_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
{
@@ -223,6 +226,136 @@ static int dw_dm_mdio_init(const char *name, void *priv)
}
#endif
+#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO)
+static int dw_eth_bb_mdio_active(struct mii_dev *miidev)
+{
+ struct dw_eth_dev *priv = miidev->priv;
+ struct gpio_desc *desc = &priv->mdio_gpio;
+
+ desc->flags = 0;
+ dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+
+ return 0;
+}
+
+static int dw_eth_bb_mdio_tristate(struct mii_dev *miidev)
+{
+ struct dw_eth_dev *priv = miidev->priv;
+ struct gpio_desc *desc = &priv->mdio_gpio;
+
+ desc->flags = 0;
+ dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN);
+
+ return 0;
+}
+
+static int dw_eth_bb_set_mdio(struct mii_dev *miidev, int v)
+{
+ struct dw_eth_dev *priv = miidev->priv;
+
+ if (v)
+ dm_gpio_set_value(&priv->mdio_gpio, 1);
+ else
+ dm_gpio_set_value(&priv->mdio_gpio, 0);
+
+ return 0;
+}
+
+static int dw_eth_bb_get_mdio(struct mii_dev *miidev, int *v)
+{
+ struct dw_eth_dev *priv = miidev->priv;
+
+ *v = dm_gpio_get_value(&priv->mdio_gpio);
+
+ return 0;
+}
+
+static int dw_eth_bb_set_mdc(struct mii_dev *miidev, int v)
+{
+ struct dw_eth_dev *priv = miidev->priv;
+
+ if (v)
+ dm_gpio_set_value(&priv->mdc_gpio, 1);
+ else
+ dm_gpio_set_value(&priv->mdc_gpio, 0);
+
+ return 0;
+}
+
+static int dw_eth_bb_delay(struct mii_dev *miidev)
+{
+ struct dw_eth_dev *priv = miidev->priv;
+
+ udelay(priv->bb_delay);
+ return 0;
+}
+
+static const struct bb_miiphy_bus_ops dw_eth_bb_miiphy_bus_ops = {
+ .mdio_active = dw_eth_bb_mdio_active,
+ .mdio_tristate = dw_eth_bb_mdio_tristate,
+ .set_mdio = dw_eth_bb_set_mdio,
+ .get_mdio = dw_eth_bb_get_mdio,
+ .set_mdc = dw_eth_bb_set_mdc,
+ .delay = dw_eth_bb_delay,
+};
+
+static int dw_bb_miiphy_read(struct mii_dev *miidev, int addr,
+ int devad, int reg)
+{
+ return bb_miiphy_read(miidev, &dw_eth_bb_miiphy_bus_ops,
+ addr, devad, reg);
+}
+
+static int dw_bb_miiphy_write(struct mii_dev *miidev, int addr,
+ int devad, int reg, u16 value)
+{
+ return bb_miiphy_write(miidev, &dw_eth_bb_miiphy_bus_ops,
+ addr, devad, reg, value);
+}
+
+static int dw_bb_mdio_init(const char *name, struct udevice *dev)
+{
+ struct dw_eth_dev *dwpriv = dev_get_priv(dev);
+ struct mii_dev *bus = mdio_alloc();
+ int ret;
+
+ if (!bus) {
+ printf("Failed to allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ debug("\n%s: use bitbang mii..\n", dev->name);
+ ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0,
+ &dwpriv->mdc_gpio,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret) {
+ debug("no mdc-gpio\n");
+ return ret;
+ }
+ ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0,
+ &dwpriv->mdio_gpio,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret) {
+ debug("no mdio-gpio\n");
+ return ret;
+ }
+ dwpriv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1);
+
+ dwpriv->bus = bus;
+ dwpriv->dev = dev;
+
+ snprintf(bus->name, sizeof(bus->name), "%s", name);
+ bus->read = dw_bb_miiphy_read;
+ bus->write = dw_bb_miiphy_write;
+#if CONFIG_IS_ENABLED(DM_GPIO)
+ bus->reset = dw_mdio_reset;
+#endif
+ bus->priv = dwpriv;
+
+ return mdio_register(bus);
+}
+#endif
+
static void tx_descs_init(struct dw_eth_dev *priv)
{
struct eth_dma_regs *dma_p = priv->dma_regs_p;
@@ -352,10 +485,35 @@ static int dw_adjust_link(struct dw_eth_dev *priv, struct eth_mac_regs *mac_p,
(phydev->port == PORT_FIBRE) ? ", fiber mode" : "");
#ifdef CONFIG_ARCH_NPCM8XX
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ unsigned int start;
+
+ /* Indirect access to VR_MII_MMD registers */
+ writew((VR_MII_MMD >> 9), PCS_BA + PCS_IND_AC);
+ /* Set PCS_Mode to SGMII */
+ clrsetbits_le16(PCS_BA + VR_MII_MMD_AN_CTRL, BIT(1), BIT(2));
+ /* Set Auto Speed Mode Change */
+ setbits_le16(PCS_BA + VR_MII_MMD_CTRL1, BIT(9));
+ /* Indirect access to SR_MII_MMD registers */
+ writew((SR_MII_MMD >> 9), PCS_BA + PCS_IND_AC);
+ /* Restart Auto-Negotiation */
+ setbits_le16(PCS_BA + SR_MII_MMD_CTRL, BIT(9) | BIT(12));
+
+ printf("SGMII PHY Wait for link up \n");
+ /* SGMII PHY Wait for link up */
+ start = get_timer(0);
+ while (!(readw(PCS_BA + SR_MII_MMD_STS) & BIT(2))) {
+ if (get_timer(start) >= LINK_UP_TIMEOUT) {
+ printf("PHY link up timeout\n");
+ return -ETIMEDOUT;
+ }
+ mdelay(1);
+ };
+ }
/* Pass all Multicast Frames */
setbits_le32(&mac_p->framefilt, BIT(4));
-
#endif
+
return 0;
}
@@ -784,51 +942,29 @@ int designware_eth_probe(struct udevice *dev)
priv->interface = pdata->phy_interface;
priv->max_speed = pdata->max_speed;
-#if IS_ENABLED(CONFIG_DM_MDIO)
- ret = dw_dm_mdio_init(dev->name, dev);
-#else
- ret = dw_mdio_init(dev->name, dev);
-#endif
- if (ret) {
- err = ret;
- goto mdio_err;
- }
- priv->bus = miiphy_get_dev_by_name(dev->name);
- priv->dev = dev;
-
#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO)
if (dev_read_bool(dev, "snps,bitbang-mii")) {
- int bus_idx;
-
- debug("\n%s: use bitbang mii..\n", dev->name);
- ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0,
- &priv->mdc_gpio, GPIOD_IS_OUT
- | GPIOD_IS_OUT_ACTIVE);
+ ret = dw_bb_mdio_init(dev->name, dev);
if (ret) {
- debug("no mdc-gpio\n");
- return ret;
+ err = ret;
+ goto mdio_err;
}
- ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0,
- &priv->mdio_gpio, GPIOD_IS_OUT
- | GPIOD_IS_OUT_ACTIVE);
+ } else
+#endif
+ {
+#if IS_ENABLED(CONFIG_DM_MDIO)
+ ret = dw_dm_mdio_init(dev->name, dev);
+#else
+ ret = dw_mdio_init(dev->name, dev);
+#endif
if (ret) {
- debug("no mdio-gpio\n");
- return ret;
- }
- priv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1);
-
- for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; bus_idx++) {
- if (!bb_miiphy_buses[bus_idx].priv) {
- bb_miiphy_buses[bus_idx].priv = priv;
- strlcpy(bb_miiphy_buses[bus_idx].name, priv->bus->name,
- MDIO_NAME_LEN);
- priv->bus->read = bb_miiphy_read;
- priv->bus->write = bb_miiphy_write;
- break;
- }
+ err = ret;
+ goto mdio_err;
}
+ priv->bus = miiphy_get_dev_by_name(dev->name);
+ priv->dev = dev;
}
-#endif
+
ret = dw_phy_init(priv, dev);
debug("%s, ret=%d\n", __func__, ret);
if (!ret)
@@ -939,83 +1075,3 @@ static struct pci_device_id supported[] = {
};
U_BOOT_PCI_DEVICE(eth_designware, supported);
-
-#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO)
-static int dw_eth_bb_mdio_active(struct bb_miiphy_bus *bus)
-{
- struct dw_eth_dev *priv = bus->priv;
- struct gpio_desc *desc = &priv->mdio_gpio;
-
- desc->flags = 0;
- dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
-
- return 0;
-}
-
-static int dw_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus)
-{
- struct dw_eth_dev *priv = bus->priv;
- struct gpio_desc *desc = &priv->mdio_gpio;
-
- desc->flags = 0;
- dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN);
-
- return 0;
-}
-
-static int dw_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
-{
- struct dw_eth_dev *priv = bus->priv;
-
- if (v)
- dm_gpio_set_value(&priv->mdio_gpio, 1);
- else
- dm_gpio_set_value(&priv->mdio_gpio, 0);
-
- return 0;
-}
-
-static int dw_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
-{
- struct dw_eth_dev *priv = bus->priv;
-
- *v = dm_gpio_get_value(&priv->mdio_gpio);
-
- return 0;
-}
-
-static int dw_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
-{
- struct dw_eth_dev *priv = bus->priv;
-
- if (v)
- dm_gpio_set_value(&priv->mdc_gpio, 1);
- else
- dm_gpio_set_value(&priv->mdc_gpio, 0);
-
- return 0;
-}
-
-static int dw_eth_bb_delay(struct bb_miiphy_bus *bus)
-{
- struct dw_eth_dev *priv = bus->priv;
-
- udelay(priv->bb_delay);
- return 0;
-}
-
-struct bb_miiphy_bus bb_miiphy_buses[] = {
- {
- .name = BB_MII_DEVNAME,
- .mdio_active = dw_eth_bb_mdio_active,
- .mdio_tristate = dw_eth_bb_mdio_tristate,
- .set_mdio = dw_eth_bb_set_mdio,
- .get_mdio = dw_eth_bb_get_mdio,
- .set_mdc = dw_eth_bb_set_mdc,
- .delay = dw_eth_bb_delay,
- .priv = NULL,
- }
-};
-
-int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
-#endif
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 2279481d935..0cfe09333f7 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -1173,7 +1173,7 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
eqos->config->ops->eqos_inval_buffer(packet, length);
- if ((eqos->rx_desc_idx & idx_mask) == idx_mask) {
+ if (eqos->started && (eqos->rx_desc_idx & idx_mask) == idx_mask) {
for (idx = eqos->rx_desc_idx - idx_mask;
idx <= eqos->rx_desc_idx;
idx++) {
@@ -1612,10 +1612,18 @@ static const struct udevice_id eqos_ids[] = {
#endif
#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ROCKCHIP)
{
+ .compatible = "rockchip,rk3528-gmac",
+ .data = (ulong)&eqos_rockchip_config
+ },
+ {
.compatible = "rockchip,rk3568-gmac",
.data = (ulong)&eqos_rockchip_config
},
{
+ .compatible = "rockchip,rk3576-gmac",
+ .data = (ulong)&eqos_rockchip_config
+ },
+ {
.compatible = "rockchip,rk3588-gmac",
.data = (ulong)&eqos_rockchip_config
},
@@ -1632,6 +1640,12 @@ static const struct udevice_id eqos_ids[] = {
.data = (ulong)&eqos_jh7110_config
},
#endif
+#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ADI)
+ {
+ .compatible = "adi,sc59x-dwmac-eqos",
+ .data = (ulong)&eqos_adi_config
+ },
+#endif
{ }
};
diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h
index 123f98d5d53..403e8203974 100644
--- a/drivers/net/dwc_eth_qos.h
+++ b/drivers/net/dwc_eth_qos.h
@@ -87,6 +87,7 @@ struct eqos_mac_regs {
#define EQOS_MAC_MDIO_ADDRESS_CR_MASK GENMASK(11, 8)
#define EQOS_MAC_MDIO_ADDRESS_CR_100_150 1
#define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2
+#define EQOS_MAC_MDIO_ADDRESS_CR_150_250 4
#define EQOS_MAC_MDIO_ADDRESS_CR_250_300 5
#define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4)
#define EQOS_MAC_MDIO_ADDRESS_GOC_MASK GENMASK(3, 2)
@@ -301,3 +302,4 @@ extern struct eqos_config eqos_qcom_config;
extern struct eqos_config eqos_stm32mp13_config;
extern struct eqos_config eqos_stm32mp15_config;
extern struct eqos_config eqos_jh7110_config;
+extern struct eqos_config eqos_adi_config;
diff --git a/drivers/net/dwc_eth_qos_adi.c b/drivers/net/dwc_eth_qos_adi.c
new file mode 100644
index 00000000000..0e6a901e303
--- /dev/null
+++ b/drivers/net/dwc_eth_qos_adi.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * (C) Copyright 2024 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <net.h>
+#include <phy.h>
+#include <reset.h>
+#include <linux/io.h>
+
+#include <asm/arch-adi/sc5xx/sc5xx.h>
+
+#include "dwc_eth_qos.h"
+
+static int eqos_start_resets_adi(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+
+ /*
+ * Settings need to latch with the DMA reset below. Currently only
+ * rgmii is supported but other phy interfaces may be supported in
+ * the future
+ */
+ sc5xx_enable_rgmii();
+ setbits_32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR);
+
+ return 0;
+}
+
+static int eqos_probe_resources_adi(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ phy_interface_t interface;
+ int ret;
+
+ ret = eqos_get_base_addr_dt(dev);
+ if (ret) {
+ pr_err("eqos_get_base_addr_dt failed: %d\n", ret);
+ return ret;
+ }
+
+ interface = eqos->config->interface(dev);
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ pr_err("Invalid PHY interface\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * rgmii tx clock rate is set to 125 MHz regardless of phy mode, and
+ * by default the internal clock is always connected to 125 MHz. According
+ * to the HRM it is invalid for this clock to have any other speed, so
+ * the hardware won't work anyway if this is wrong.
+ */
+static ulong eqos_get_tick_clk_rate_adi(struct udevice *dev)
+{
+ return 125 * 1000000;
+}
+
+static int eqos_get_enetaddr_adi(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+
+ return eth_env_get_enetaddr("ethaddr", pdata->enetaddr);
+}
+
+static struct eqos_ops eqos_adi_ops = {
+ .eqos_inval_desc = eqos_inval_desc_generic,
+ .eqos_flush_desc = eqos_flush_desc_generic,
+ .eqos_inval_buffer = eqos_inval_buffer_generic,
+ .eqos_flush_buffer = eqos_flush_buffer_generic,
+ .eqos_probe_resources = eqos_probe_resources_adi,
+ .eqos_remove_resources = eqos_null_ops,
+ .eqos_start_resets = eqos_start_resets_adi,
+ .eqos_stop_resets = eqos_null_ops,
+ .eqos_start_clks = eqos_null_ops,
+ .eqos_stop_clks = eqos_null_ops,
+ .eqos_calibrate_pads = eqos_null_ops,
+ .eqos_disable_calibration = eqos_null_ops,
+ .eqos_set_tx_clk_speed = eqos_null_ops,
+ .eqos_get_enetaddr = eqos_get_enetaddr_adi,
+ .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_adi,
+};
+
+struct eqos_config __maybe_unused eqos_adi_config = {
+ .reg_access_always_ok = true,
+ .mdio_wait = 20,
+ .swr_wait = 50,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_150_250,
+ .axi_bus_width = EQOS_AXI_WIDTH_32,
+ .interface = dev_read_phy_mode,
+ .ops = &eqos_adi_ops,
+};
diff --git a/drivers/net/dwc_eth_qos_rockchip.c b/drivers/net/dwc_eth_qos_rockchip.c
index f3a0f63003e..d646d3ebac8 100644
--- a/drivers/net/dwc_eth_qos_rockchip.c
+++ b/drivers/net/dwc_eth_qos_rockchip.c
@@ -50,6 +50,132 @@ struct rockchip_platform_data {
(((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \
((rx) ? soc##_GMAC_RXCLK_DLY_ENABLE : soc##_GMAC_RXCLK_DLY_DISABLE))
+#define RK3528_VO_GRF_GMAC_CON 0x0018
+#define RK3528_VPU_GRF_GMAC_CON5 0x0018
+#define RK3528_VPU_GRF_GMAC_CON6 0x001c
+
+#define RK3528_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
+#define RK3528_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
+#define RK3528_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14)
+#define RK3528_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14)
+
+#define RK3528_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 8)
+#define RK3528_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 0)
+
+#define RK3528_GMAC0_PHY_INTF_SEL_RMII GRF_BIT(1)
+#define RK3528_GMAC1_PHY_INTF_SEL_RGMII GRF_CLR_BIT(8)
+#define RK3528_GMAC1_PHY_INTF_SEL_RMII GRF_BIT(8)
+
+#define RK3528_GMAC1_CLK_SELECT_CRU GRF_CLR_BIT(12)
+#define RK3528_GMAC1_CLK_SELECT_IO GRF_BIT(12)
+
+#define RK3528_GMAC0_CLK_RMII_DIV2 GRF_BIT(3)
+#define RK3528_GMAC0_CLK_RMII_DIV20 GRF_CLR_BIT(3)
+#define RK3528_GMAC1_CLK_RMII_DIV2 GRF_BIT(10)
+#define RK3528_GMAC1_CLK_RMII_DIV20 GRF_CLR_BIT(10)
+
+#define RK3528_GMAC1_CLK_RGMII_DIV1 (GRF_CLR_BIT(11) | GRF_CLR_BIT(10))
+#define RK3528_GMAC1_CLK_RGMII_DIV5 (GRF_BIT(11) | GRF_BIT(10))
+#define RK3528_GMAC1_CLK_RGMII_DIV50 (GRF_BIT(11) | GRF_CLR_BIT(10))
+
+#define RK3528_GMAC0_CLK_RMII_GATE GRF_BIT(2)
+#define RK3528_GMAC0_CLK_RMII_NOGATE GRF_CLR_BIT(2)
+#define RK3528_GMAC1_CLK_RMII_GATE GRF_BIT(9)
+#define RK3528_GMAC1_CLK_RMII_NOGATE GRF_CLR_BIT(9)
+
+static int rk3528_set_to_rgmii(struct udevice *dev,
+ int tx_delay, int rx_delay)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+
+ regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5,
+ RK3528_GMAC1_PHY_INTF_SEL_RGMII);
+
+ regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5,
+ DELAY_ENABLE(RK3528, tx_delay, rx_delay));
+
+ regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON6,
+ RK3528_GMAC_CLK_RX_DL_CFG(rx_delay) |
+ RK3528_GMAC_CLK_TX_DL_CFG(tx_delay));
+
+ return 0;
+}
+
+static int rk3528_set_to_rmii(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+
+ if (data->id == 1)
+ regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5,
+ RK3528_GMAC1_PHY_INTF_SEL_RMII);
+ else
+ regmap_write(data->grf, RK3528_VO_GRF_GMAC_CON,
+ RK3528_GMAC0_PHY_INTF_SEL_RMII |
+ RK3528_GMAC0_CLK_RMII_DIV2);
+
+ return 0;
+}
+
+static int rk3528_set_gmac_speed(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+ u32 val, reg;
+
+ switch (eqos->phy->speed) {
+ case SPEED_10:
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII)
+ val = data->id == 1 ? RK3528_GMAC1_CLK_RMII_DIV20 :
+ RK3528_GMAC0_CLK_RMII_DIV20;
+ else
+ val = RK3528_GMAC1_CLK_RGMII_DIV50;
+ break;
+ case SPEED_100:
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII)
+ val = data->id == 1 ? RK3528_GMAC1_CLK_RMII_DIV2 :
+ RK3528_GMAC0_CLK_RMII_DIV2;
+ else
+ val = RK3528_GMAC1_CLK_RGMII_DIV5;
+ break;
+ case SPEED_1000:
+ if (pdata->phy_interface != PHY_INTERFACE_MODE_RMII)
+ val = RK3528_GMAC1_CLK_RGMII_DIV1;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg = data->id == 1 ? RK3528_VPU_GRF_GMAC_CON5 :
+ RK3528_VO_GRF_GMAC_CON;
+ regmap_write(data->grf, reg, val);
+
+ return 0;
+}
+
+static void rk3528_set_clock_selection(struct udevice *dev, bool enable)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+ u32 val;
+
+ if (data->id == 1) {
+ val = data->clock_input ? RK3528_GMAC1_CLK_SELECT_IO :
+ RK3528_GMAC1_CLK_SELECT_CRU;
+ val |= enable ? RK3528_GMAC1_CLK_RMII_NOGATE :
+ RK3528_GMAC1_CLK_RMII_GATE;
+ regmap_write(data->grf, RK3528_VPU_GRF_GMAC_CON5, val);
+ } else {
+ val = enable ? RK3528_GMAC0_CLK_RMII_NOGATE :
+ RK3528_GMAC0_CLK_RMII_GATE;
+ regmap_write(data->grf, RK3528_VO_GRF_GMAC_CON, val);
+ }
+}
+
#define RK3568_GRF_GMAC0_CON0 0x0380
#define RK3568_GRF_GMAC0_CON1 0x0384
#define RK3568_GRF_GMAC1_CON0 0x0388
@@ -134,6 +260,145 @@ static int rk3568_set_gmac_speed(struct udevice *dev)
return 0;
}
+/* VCCIO0_1_3_IOC */
+#define RK3576_VCCIO0_1_3_IOC_CON2 0x6408
+#define RK3576_VCCIO0_1_3_IOC_CON3 0x640c
+#define RK3576_VCCIO0_1_3_IOC_CON4 0x6410
+#define RK3576_VCCIO0_1_3_IOC_CON5 0x6414
+
+#define RK3576_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
+#define RK3576_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
+#define RK3576_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7)
+#define RK3576_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7)
+
+#define RK3576_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
+#define RK3576_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+
+/* SDGMAC_GRF */
+#define RK3576_GRF_GMAC_CON0 0x0020
+#define RK3576_GRF_GMAC_CON1 0x0024
+
+#define RK3576_GMAC_RMII_MODE GRF_BIT(3)
+#define RK3576_GMAC_RGMII_MODE GRF_CLR_BIT(3)
+
+#define RK3576_GMAC_CLK_SELECT_IO GRF_BIT(7)
+#define RK3576_GMAC_CLK_SELECT_CRU GRF_CLR_BIT(7)
+
+#define RK3576_GMAC_CLK_RMII_DIV2 GRF_BIT(5)
+#define RK3576_GMAC_CLK_RMII_DIV20 GRF_CLR_BIT(5)
+
+#define RK3576_GMAC_CLK_RGMII_DIV1 \
+ (GRF_CLR_BIT(6) | GRF_CLR_BIT(5))
+#define RK3576_GMAC_CLK_RGMII_DIV5 \
+ (GRF_BIT(6) | GRF_BIT(5))
+#define RK3576_GMAC_CLK_RGMII_DIV50 \
+ (GRF_BIT(6) | GRF_CLR_BIT(5))
+
+#define RK3576_GMAC_CLK_RMII_GATE GRF_BIT(4)
+#define RK3576_GMAC_CLK_RMII_NOGATE GRF_CLR_BIT(4)
+
+static int rk3576_set_to_rgmii(struct udevice *dev,
+ int tx_delay, int rx_delay)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+ u32 offset_con;
+
+ offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 :
+ RK3576_GRF_GMAC_CON0;
+
+ regmap_write(data->grf, offset_con, RK3576_GMAC_RGMII_MODE);
+
+ offset_con = data->id == 1 ? RK3576_VCCIO0_1_3_IOC_CON4 :
+ RK3576_VCCIO0_1_3_IOC_CON2;
+
+ /* m0 && m1 delay enabled */
+ regmap_write(data->php_grf, offset_con,
+ DELAY_ENABLE(RK3576, tx_delay, rx_delay));
+ regmap_write(data->php_grf, offset_con + 0x4,
+ DELAY_ENABLE(RK3576, tx_delay, rx_delay));
+
+ /* m0 && m1 delay value */
+ regmap_write(data->php_grf, offset_con,
+ RK3576_GMAC_CLK_TX_DL_CFG(tx_delay) |
+ RK3576_GMAC_CLK_RX_DL_CFG(rx_delay));
+ regmap_write(data->php_grf, offset_con + 0x4,
+ RK3576_GMAC_CLK_TX_DL_CFG(tx_delay) |
+ RK3576_GMAC_CLK_RX_DL_CFG(rx_delay));
+
+ return 0;
+}
+
+static int rk3576_set_to_rmii(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+ u32 offset_con;
+
+ offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 :
+ RK3576_GRF_GMAC_CON0;
+
+ regmap_write(data->grf, offset_con, RK3576_GMAC_RMII_MODE);
+
+ return 0;
+}
+
+static int rk3576_set_gmac_speed(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+ u32 val = 0, offset_con;
+
+ switch (eqos->phy->speed) {
+ case SPEED_10:
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII)
+ val = RK3576_GMAC_CLK_RMII_DIV20;
+ else
+ val = RK3576_GMAC_CLK_RGMII_DIV50;
+ break;
+ case SPEED_100:
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII)
+ val = RK3576_GMAC_CLK_RMII_DIV2;
+ else
+ val = RK3576_GMAC_CLK_RGMII_DIV5;
+ break;
+ case SPEED_1000:
+ if (pdata->phy_interface != PHY_INTERFACE_MODE_RMII)
+ val = RK3576_GMAC_CLK_RGMII_DIV1;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 :
+ RK3576_GRF_GMAC_CON0;
+
+ regmap_write(data->grf, offset_con, val);
+
+ return 0;
+}
+
+static void rk3576_set_clock_selection(struct udevice *dev, bool enable)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct rockchip_platform_data *data = pdata->priv_pdata;
+
+ u32 val = data->clock_input ? RK3576_GMAC_CLK_SELECT_IO :
+ RK3576_GMAC_CLK_SELECT_CRU;
+ u32 offset_con;
+
+ val |= enable ? RK3576_GMAC_CLK_RMII_NOGATE :
+ RK3576_GMAC_CLK_RMII_GATE;
+
+ offset_con = data->id == 1 ? RK3576_GRF_GMAC_CON1 :
+ RK3576_GRF_GMAC_CON0;
+
+ regmap_write(data->grf, offset_con, val);
+}
+
#define RK3588_DELAY_ENABLE(id, tx, rx) \
(((tx) ? RK3588_GMAC_TXCLK_DLY_ENABLE(id) : RK3588_GMAC_TXCLK_DLY_DISABLE(id)) | \
((rx) ? RK3588_GMAC_RXCLK_DLY_ENABLE(id) : RK3588_GMAC_RXCLK_DLY_DISABLE(id)))
@@ -270,6 +535,18 @@ static void rk3588_set_clock_selection(struct udevice *dev, bool enable)
static const struct rk_gmac_ops rk_gmac_ops[] = {
{
+ .compatible = "rockchip,rk3528-gmac",
+ .set_to_rgmii = rk3528_set_to_rgmii,
+ .set_to_rmii = rk3528_set_to_rmii,
+ .set_gmac_speed = rk3528_set_gmac_speed,
+ .set_clock_selection = rk3528_set_clock_selection,
+ .regs = {
+ 0xffbd0000, /* gmac0 */
+ 0xffbe0000, /* gmac1 */
+ 0x0, /* sentinel */
+ },
+ },
+ {
.compatible = "rockchip,rk3568-gmac",
.set_to_rgmii = rk3568_set_to_rgmii,
.set_to_rmii = rk3568_set_to_rmii,
@@ -281,6 +558,18 @@ static const struct rk_gmac_ops rk_gmac_ops[] = {
},
},
{
+ .compatible = "rockchip,rk3576-gmac",
+ .set_to_rgmii = rk3576_set_to_rgmii,
+ .set_to_rmii = rk3576_set_to_rmii,
+ .set_gmac_speed = rk3576_set_gmac_speed,
+ .set_clock_selection = rk3576_set_clock_selection,
+ .regs = {
+ 0x2a220000, /* gmac0 */
+ 0x2a230000, /* gmac1 */
+ 0x0, /* sentinel */
+ },
+ },
+ {
.compatible = "rockchip,rk3588-gmac",
.set_to_rgmii = rk3588_set_to_rgmii,
.set_to_rmii = rk3588_set_to_rmii,
@@ -357,7 +646,8 @@ static int eqos_probe_resources_rk(struct udevice *dev)
goto err_free;
}
- if (device_is_compatible(dev, "rockchip,rk3588-gmac")) {
+ if (device_is_compatible(dev, "rockchip,rk3588-gmac") ||
+ device_is_compatible(dev, "rockchip,rk3576-gmac")) {
data->php_grf =
syscon_regmap_lookup_by_phandle(dev, "rockchip,php-grf");
if (IS_ERR(data->php_grf)) {
diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c
index 8f432b8637b..b77298070f8 100644
--- a/drivers/net/e1000.c
+++ b/drivers/net/e1000.c
@@ -4830,6 +4830,7 @@ static int e1000_set_phy_type (struct e1000_hw *hw)
hw->phy_type = e1000_phy_igp;
break;
}
+ fallthrough;
case IGP03E1000_E_PHY_ID:
hw->phy_type = e1000_phy_igp_3;
break;
@@ -4843,6 +4844,7 @@ static int e1000_set_phy_type (struct e1000_hw *hw)
hw->phy_type = e1000_phy_gg82563;
break;
}
+ fallthrough;
case BME1000_E_PHY_ID:
hw->phy_type = e1000_phy_bm;
break;
diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c
index c48ae6e88f3..ee8eae1efd9 100644
--- a/drivers/net/phy/micrel_ksz90x1.c
+++ b/drivers/net/phy/micrel_ksz90x1.c
@@ -389,10 +389,126 @@ U_BOOT_PHY_DRIVER(ksz9031) = {
#define KSZ9131RN_DLL_ENABLE_DELAY 0
#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12)
+#define KSZ9131RN_COMMON_CTRL 0
+#define KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE BIT(4)
+
+#define KSZ9131RN_LED_ERRATA_REG 0x1e
+#define KSZ9131RN_LED_ERRATA_BIT BIT(9)
+
+#define KSZ9131RN_CONTROL_PAD_SKEW 4
+#define KSZ9131RN_RX_DATA_PAD_SKEW 5
+#define KSZ9131RN_TX_DATA_PAD_SKEW 6
+#define KSZ9131RN_CLK_PAD_SKEW 8
+
+#define KSZ9131RN_SKEW_5BIT_MAX 2400
+#define KSZ9131RN_SKEW_4BIT_MAX 800
+#define KSZ9131RN_OFFSET 700
+#define KSZ9131RN_STEP 100
+
+static int ksz9131_of_load_skew_values(struct phy_device *phydev,
+ ofnode of_node,
+ u16 reg, size_t field_sz,
+ const char *field[], u8 numfields)
+{
+ int val[4] = {-(1 + KSZ9131RN_OFFSET), -(2 + KSZ9131RN_OFFSET),
+ -(3 + KSZ9131RN_OFFSET), -(4 + KSZ9131RN_OFFSET)};
+ int skewval, skewmax = 0;
+ int matches = 0;
+ u16 maxval;
+ u16 newval;
+ u16 mask;
+ int i;
+
+ /* psec properties in dts should mean x pico seconds */
+ if (field_sz == 5)
+ skewmax = KSZ9131RN_SKEW_5BIT_MAX;
+ else
+ skewmax = KSZ9131RN_SKEW_4BIT_MAX;
+
+ for (i = 0; i < numfields; i++)
+ if (!ofnode_read_s32(of_node, field[i], &skewval)) {
+ if (skewval < -KSZ9131RN_OFFSET)
+ skewval = -KSZ9131RN_OFFSET;
+ else if (skewval > skewmax)
+ skewval = skewmax;
+
+ val[i] = skewval + KSZ9131RN_OFFSET;
+ matches++;
+ }
+
+ if (!matches)
+ return 0;
+
+ if (matches < numfields)
+ newval = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg);
+ else
+ newval = 0;
+
+ maxval = (field_sz == 4) ? 0xf : 0x1f;
+ for (i = 0; i < numfields; i++)
+ if (val[i] != -(i + 1 + KSZ9131RN_OFFSET)) {
+ mask = 0xffff;
+ mask ^= maxval << (field_sz * i);
+ newval = (newval & mask) |
+ (((val[i] / KSZ9131RN_STEP) & maxval)
+ << (field_sz * i));
+ }
+
+ return phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg, newval);
+}
+
+static int ksz9131_of_load_all_skew_values(struct phy_device *phydev)
+{
+ const char *control_skews[2] = { "txen-skew-psec", "rxdv-skew-psec" };
+ const char *clk_skews[2] = { "rxc-skew-psec", "txc-skew-psec" };
+ const char *rx_data_skews[4] = {
+ "rxd0-skew-psec", "rxd1-skew-psec",
+ "rxd2-skew-psec", "rxd3-skew-psec"
+ };
+ const char *tx_data_skews[4] = {
+ "txd0-skew-psec", "txd1-skew-psec",
+ "txd2-skew-psec", "txd3-skew-psec"
+ };
+ struct ofnode_phandle_args phandle_args;
+ int ret;
+
+ /*
+ * Silently ignore failure here as the device tree is not required to
+ * contain a phy node.
+ */
+ if (dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL, 0, 0,
+ &phandle_args))
+ return 0;
+
+ if (!ofnode_valid(phandle_args.node))
+ return 0;
+
+ ret = ksz9131_of_load_skew_values(phydev, phandle_args.node,
+ KSZ9131RN_CLK_PAD_SKEW, 5,
+ clk_skews, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_of_load_skew_values(phydev, phandle_args.node,
+ KSZ9131RN_CONTROL_PAD_SKEW, 4,
+ control_skews, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_of_load_skew_values(phydev, phandle_args.node,
+ KSZ9131RN_RX_DATA_PAD_SKEW, 4,
+ rx_data_skews, 4);
+ if (ret < 0)
+ return ret;
+
+ return ksz9131_of_load_skew_values(phydev, phandle_args.node,
+ KSZ9131RN_TX_DATA_PAD_SKEW, 4,
+ tx_data_skews, 4);
+}
+
static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
{
- struct phy_driver *drv = phydev->drv;
- u16 rxcdll_val, txcdll_val, val;
+ u16 rxcdll_val, txcdll_val;
int ret;
switch (phydev->interface) {
@@ -416,24 +532,37 @@ static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
return 0;
}
- val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
- KSZ9131RN_RXC_DLL_CTRL);
- val &= ~KSZ9131RN_DLL_CTRL_BYPASS;
- val |= rxcdll_val;
- ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
- KSZ9131RN_RXC_DLL_CTRL, val);
- if (ret)
+ ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
+ rxcdll_val);
+ if (ret < 0)
return ret;
- val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
- KSZ9131RN_TXC_DLL_CTRL);
+ return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
+ txcdll_val);
+}
+
+/* Silicon Errata DS80000693B
+ *
+ * When LEDs are configured in Individual Mode, LED1 is ON in a no-link
+ * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves
+ * according to the datasheet (off if there is no link).
+ */
+static int ksz9131_led_errata(struct phy_device *phydev)
+{
+ int reg;
- val &= ~KSZ9131RN_DLL_CTRL_BYPASS;
- val |= txcdll_val;
- ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG,
- KSZ9131RN_TXC_DLL_CTRL, val);
+ reg = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
+ KSZ9131RN_COMMON_CTRL);
+ if (reg < 0)
+ return reg;
- return ret;
+ if (!(reg & KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE))
+ return 0;
+
+ return phy_set_bits(phydev, MDIO_DEVAD_NONE, KSZ9131RN_LED_ERRATA_REG,
+ KSZ9131RN_LED_ERRATA_BIT);
}
static int ksz9131_config(struct phy_device *phydev)
@@ -446,6 +575,14 @@ static int ksz9131_config(struct phy_device *phydev)
return ret;
}
+ ret = ksz9131_of_load_all_skew_values(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = ksz9131_led_errata(phydev);
+ if (ret < 0)
+ return ret;
+
/* add an option to disable the gigabit feature of this PHY */
if (env_get("disable_giga")) {
unsigned features;
diff --git a/drivers/net/phy/miiphybb.c b/drivers/net/phy/miiphybb.c
index 9f5f9b12c9f..76463da7299 100644
--- a/drivers/net/phy/miiphybb.c
+++ b/drivers/net/phy/miiphybb.c
@@ -17,37 +17,13 @@
#include <miiphy.h>
#include <asm/global_data.h>
-int bb_miiphy_init(void)
-{
- int i;
-
- for (i = 0; i < bb_miiphy_buses_num; i++)
- if (bb_miiphy_buses[i].init != NULL)
- bb_miiphy_buses[i].init(&bb_miiphy_buses[i]);
-
- return 0;
-}
-
-static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
-{
- int i;
-
- /* Search the correct bus */
- for (i = 0; i < bb_miiphy_buses_num; i++) {
- if (!strcmp(bb_miiphy_buses[i].name, devname)) {
- return &bb_miiphy_buses[i];
- }
- }
- return NULL;
-}
-
/*****************************************************************************
*
* Utility to send the preamble, address, and register (common to read
* and write).
*/
-static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
- unsigned char addr, unsigned char reg)
+static void miiphy_pre(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
+ char read, unsigned char addr, unsigned char reg)
{
int j;
@@ -59,62 +35,62 @@ static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
* but it is safer and will be much more robust.
*/
- bus->mdio_active(bus);
- bus->set_mdio(bus, 1);
+ ops->mdio_active(miidev);
+ ops->set_mdio(miidev, 1);
for (j = 0; j < 32; j++) {
- bus->set_mdc(bus, 0);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
}
/* send the start bit (01) and the read opcode (10) or write (10) */
- bus->set_mdc(bus, 0);
- bus->set_mdio(bus, 0);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 0);
- bus->set_mdio(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 0);
- bus->set_mdio(bus, read);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 0);
- bus->set_mdio(bus, !read);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->set_mdio(miidev, 0);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 0);
+ ops->set_mdio(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 0);
+ ops->set_mdio(miidev, read);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 0);
+ ops->set_mdio(miidev, !read);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
/* send the PHY address */
for (j = 0; j < 5; j++) {
- bus->set_mdc(bus, 0);
+ ops->set_mdc(miidev, 0);
if ((addr & 0x10) == 0) {
- bus->set_mdio(bus, 0);
+ ops->set_mdio(miidev, 0);
} else {
- bus->set_mdio(bus, 1);
+ ops->set_mdio(miidev, 1);
}
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
addr <<= 1;
}
/* send the register address */
for (j = 0; j < 5; j++) {
- bus->set_mdc(bus, 0);
+ ops->set_mdc(miidev, 0);
if ((reg & 0x10) == 0) {
- bus->set_mdio(bus, 0);
+ ops->set_mdio(miidev, 0);
} else {
- bus->set_mdio(bus, 1);
+ ops->set_mdio(miidev, 1);
}
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
reg <<= 1;
}
}
@@ -126,62 +102,57 @@ static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
* Returns:
* 0 on success
*/
-int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
+int bb_miiphy_read(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
+ int addr, int devad, int reg)
{
unsigned short rdreg; /* register working value */
int v;
int j; /* counter */
- struct bb_miiphy_bus *bus;
- bus = bb_miiphy_getbus(miidev->name);
- if (bus == NULL) {
- return -1;
- }
-
- miiphy_pre (bus, 1, addr, reg);
+ miiphy_pre(miidev, ops, 1, addr, reg);
/* tri-state our MDIO I/O pin so we can read */
- bus->set_mdc(bus, 0);
- bus->mdio_tristate(bus);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->mdio_tristate(miidev);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
/* check the turnaround bit: the PHY should be driving it to zero */
- bus->get_mdio(bus, &v);
+ ops->get_mdio(miidev, &v);
if (v != 0) {
/* puts ("PHY didn't drive TA low\n"); */
for (j = 0; j < 32; j++) {
- bus->set_mdc(bus, 0);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
}
/* There is no PHY, return */
return -1;
}
- bus->set_mdc(bus, 0);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->delay(miidev);
/* read 16 bits of register data, MSB first */
rdreg = 0;
for (j = 0; j < 16; j++) {
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
rdreg <<= 1;
- bus->get_mdio(bus, &v);
+ ops->get_mdio(miidev, &v);
rdreg |= (v & 0x1);
- bus->set_mdc(bus, 0);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->delay(miidev);
}
- bus->set_mdc(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 0);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 0);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
@@ -195,54 +166,47 @@ int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
* Returns:
* 0 on success
*/
-int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
- u16 value)
+int bb_miiphy_write(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
+ int addr, int devad, int reg, u16 value)
{
- struct bb_miiphy_bus *bus;
int j; /* counter */
- bus = bb_miiphy_getbus(miidev->name);
- if (bus == NULL) {
- /* Bus not found! */
- return -1;
- }
-
- miiphy_pre (bus, 0, addr, reg);
+ miiphy_pre(miidev, ops, 0, addr, reg);
/* send the turnaround (10) */
- bus->set_mdc(bus, 0);
- bus->set_mdio(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
- bus->set_mdc(bus, 0);
- bus->set_mdio(bus, 0);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->set_mdc(miidev, 0);
+ ops->set_mdio(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 0);
+ ops->set_mdio(miidev, 0);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
/* write 16 bits of register data, MSB first */
for (j = 0; j < 16; j++) {
- bus->set_mdc(bus, 0);
+ ops->set_mdc(miidev, 0);
if ((value & 0x00008000) == 0) {
- bus->set_mdio(bus, 0);
+ ops->set_mdio(miidev, 0);
} else {
- bus->set_mdio(bus, 1);
+ ops->set_mdio(miidev, 1);
}
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
value <<= 1;
}
/*
* Tri-state the MDIO line.
*/
- bus->mdio_tristate(bus);
- bus->set_mdc(bus, 0);
- bus->delay(bus);
- bus->set_mdc(bus, 1);
- bus->delay(bus);
+ ops->mdio_tristate(miidev);
+ ops->set_mdc(miidev, 0);
+ ops->delay(miidev);
+ ops->set_mdc(miidev, 1);
+ ops->delay(miidev);
return 0;
}
diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c
index 7286ad19598..539fd37ee59 100644
--- a/drivers/net/ravb.c
+++ b/drivers/net/ravb.c
@@ -354,8 +354,15 @@ static int ravb_mac_init(struct ravb_priv *eth)
/* Disable MAC Interrupt */
writel(0, eth->iobase + RAVB_REG_ECSIPR);
- /* Recv frame limit set register */
- writel(RFLR_RFL_MIN, eth->iobase + RAVB_REG_RFLR);
+ /*
+ * Set receive frame length
+ *
+ * The length set here describes the frame from the destination address
+ * up to and including the CRC data. However only the frame data,
+ * excluding the CRC, are transferred to memory. To allow for the
+ * largest frames add the CRC length to the maximum Rx descriptor size.
+ */
+ writel(RFLR_RFL_MIN + ETH_FCS_LEN, eth->iobase + RAVB_REG_RFLR);
return 0;
}
@@ -490,6 +497,88 @@ static void ravb_stop(struct udevice *dev)
ravb_reset(dev);
}
+/* Bitbang MDIO access */
+static int ravb_bb_mdio_active(struct mii_dev *miidev)
+{
+ struct ravb_priv *eth = miidev->priv;
+
+ setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD);
+
+ return 0;
+}
+
+static int ravb_bb_mdio_tristate(struct mii_dev *miidev)
+{
+ struct ravb_priv *eth = miidev->priv;
+
+ clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD);
+
+ return 0;
+}
+
+static int ravb_bb_set_mdio(struct mii_dev *miidev, int v)
+{
+ struct ravb_priv *eth = miidev->priv;
+
+ if (v)
+ setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO);
+ else
+ clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO);
+
+ return 0;
+}
+
+static int ravb_bb_get_mdio(struct mii_dev *miidev, int *v)
+{
+ struct ravb_priv *eth = miidev->priv;
+
+ *v = (readl(eth->iobase + RAVB_REG_PIR) & PIR_MDI) >> 3;
+
+ return 0;
+}
+
+static int ravb_bb_set_mdc(struct mii_dev *miidev, int v)
+{
+ struct ravb_priv *eth = miidev->priv;
+
+ if (v)
+ setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC);
+ else
+ clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC);
+
+ return 0;
+}
+
+static int ravb_bb_delay(struct mii_dev *miidev)
+{
+ udelay(10);
+
+ return 0;
+}
+
+static const struct bb_miiphy_bus_ops ravb_bb_miiphy_bus_ops = {
+ .mdio_active = ravb_bb_mdio_active,
+ .mdio_tristate = ravb_bb_mdio_tristate,
+ .set_mdio = ravb_bb_set_mdio,
+ .get_mdio = ravb_bb_get_mdio,
+ .set_mdc = ravb_bb_set_mdc,
+ .delay = ravb_bb_delay,
+};
+
+static int ravb_bb_miiphy_read(struct mii_dev *miidev, int addr,
+ int devad, int reg)
+{
+ return bb_miiphy_read(miidev, &ravb_bb_miiphy_bus_ops,
+ addr, devad, reg);
+}
+
+static int ravb_bb_miiphy_write(struct mii_dev *miidev, int addr,
+ int devad, int reg, u16 value)
+{
+ return bb_miiphy_write(miidev, &ravb_bb_miiphy_bus_ops,
+ addr, devad, reg, value);
+}
+
static int ravb_probe(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
@@ -503,7 +592,7 @@ static int ravb_probe(struct udevice *dev)
ret = clk_get_bulk(dev, &eth->clks);
if (ret < 0)
- goto err_mdio_alloc;
+ goto err_clk_get;
mdiodev = mdio_alloc();
if (!mdiodev) {
@@ -511,37 +600,39 @@ static int ravb_probe(struct udevice *dev)
goto err_mdio_alloc;
}
- mdiodev->read = bb_miiphy_read;
- mdiodev->write = bb_miiphy_write;
- bb_miiphy_buses[0].priv = eth;
+ mdiodev->read = ravb_bb_miiphy_read;
+ mdiodev->write = ravb_bb_miiphy_write;
+ mdiodev->priv = eth;
snprintf(mdiodev->name, sizeof(mdiodev->name), dev->name);
ret = mdio_register(mdiodev);
if (ret < 0)
goto err_mdio_register;
- eth->bus = miiphy_get_dev_by_name(dev->name);
+ eth->bus = mdiodev;
/* Bring up PHY */
ret = clk_enable_bulk(&eth->clks);
if (ret)
- goto err_mdio_register;
+ goto err_clk_enable;
ret = ravb_reset(dev);
if (ret)
- goto err_mdio_reset;
+ goto err_clk_enable;
ret = ravb_phy_config(dev);
if (ret)
- goto err_mdio_reset;
+ goto err_clk_enable;
return 0;
-err_mdio_reset:
- clk_release_bulk(&eth->clks);
+err_clk_enable:
+ mdio_unregister(mdiodev);
err_mdio_register:
mdio_free(mdiodev);
err_mdio_alloc:
+ clk_release_bulk(&eth->clks);
+err_clk_get:
unmap_physmem(eth->iobase, MAP_NOCACHE);
return ret;
}
@@ -560,83 +651,6 @@ static int ravb_remove(struct udevice *dev)
return 0;
}
-static int ravb_bb_init(struct bb_miiphy_bus *bus)
-{
- return 0;
-}
-
-static int ravb_bb_mdio_active(struct bb_miiphy_bus *bus)
-{
- struct ravb_priv *eth = bus->priv;
-
- setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD);
-
- return 0;
-}
-
-static int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus)
-{
- struct ravb_priv *eth = bus->priv;
-
- clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD);
-
- return 0;
-}
-
-static int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
-{
- struct ravb_priv *eth = bus->priv;
-
- if (v)
- setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO);
- else
- clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO);
-
- return 0;
-}
-
-static int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
-{
- struct ravb_priv *eth = bus->priv;
-
- *v = (readl(eth->iobase + RAVB_REG_PIR) & PIR_MDI) >> 3;
-
- return 0;
-}
-
-static int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
-{
- struct ravb_priv *eth = bus->priv;
-
- if (v)
- setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC);
- else
- clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC);
-
- return 0;
-}
-
-static int ravb_bb_delay(struct bb_miiphy_bus *bus)
-{
- udelay(10);
-
- return 0;
-}
-
-struct bb_miiphy_bus bb_miiphy_buses[] = {
- {
- .name = "ravb",
- .init = ravb_bb_init,
- .mdio_active = ravb_bb_mdio_active,
- .mdio_tristate = ravb_bb_mdio_tristate,
- .set_mdio = ravb_bb_set_mdio,
- .get_mdio = ravb_bb_get_mdio,
- .set_mdc = ravb_bb_set_mdc,
- .delay = ravb_bb_delay,
- },
-};
-int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
-
static const struct eth_ops ravb_ops = {
.start = ravb_start,
.send = ravb_send,
@@ -658,8 +672,6 @@ int ravb_of_to_plat(struct udevice *dev)
pdata->max_speed = dev_read_u32_default(dev, "max-speed", 1000);
- sprintf(bb_miiphy_buses[0].name, dev->name);
-
return 0;
}
diff --git a/drivers/net/sandbox-lwip.c b/drivers/net/sandbox-lwip.c
deleted file mode 100644
index 3721033c310..00000000000
--- a/drivers/net/sandbox-lwip.c
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2015 National Instruments
- *
- * (C) Copyright 2015
- * Joe Hershberger <joe.hershberger@ni.com>
- */
-
-#include <dm.h>
-#include <log.h>
-#include <malloc.h>
-#include <net.h>
-#include <asm/eth.h>
-#include <asm/global_data.h>
-#include <asm/test.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static int sb_lwip_eth_start(struct udevice *dev)
-{
- debug("eth_sandbox_lwip: Start\n");
-
- return 0;
-}
-
-static int sb_lwip_eth_send(struct udevice *dev, void *packet, int length)
-{
- debug("eth_sandbox_lwip: Send packet %d\n", length);
-
- return -ENOTSUPP;
-}
-
-static int sb_lwip_eth_recv(struct udevice *dev, int flags, uchar **packetp)
-{
- return -EAGAIN;
-}
-
-static int sb_lwip_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
-{
- return 0;
-}
-
-static void sb_lwip_eth_stop(struct udevice *dev)
-{
-}
-
-static int sb_lwip_eth_write_hwaddr(struct udevice *dev)
-{
- return 0;
-}
-
-static const struct eth_ops sb_eth_ops = {
- .start = sb_lwip_eth_start,
- .send = sb_lwip_eth_send,
- .recv = sb_lwip_eth_recv,
- .free_pkt = sb_lwip_eth_free_pkt,
- .stop = sb_lwip_eth_stop,
- .write_hwaddr = sb_lwip_eth_write_hwaddr,
-};
-
-static int sb_lwip_eth_remove(struct udevice *dev)
-{
- return 0;
-}
-
-static int sb_lwip_eth_of_to_plat(struct udevice *dev)
-{
- return 0;
-}
-
-static const struct udevice_id sb_eth_ids[] = {
- { .compatible = "sandbox,eth" },
- { }
-};
-
-U_BOOT_DRIVER(eth_sandbox) = {
- .name = "eth_lwip_sandbox",
- .id = UCLASS_ETH,
- .of_match = sb_eth_ids,
- .of_to_plat = sb_lwip_eth_of_to_plat,
- .remove = sb_lwip_eth_remove,
- .ops = &sb_eth_ops,
- .priv_auto = 0,
- .plat_auto = sizeof(struct eth_pdata),
-};
diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c
index fe3627db6e3..2011fd31f41 100644
--- a/drivers/net/sandbox.c
+++ b/drivers/net/sandbox.c
@@ -9,13 +9,84 @@
#include <dm.h>
#include <log.h>
#include <malloc.h>
-#include <net.h>
#include <asm/eth.h>
#include <asm/global_data.h>
#include <asm/test.h>
+#include <asm/types.h>
+
+/*
+ * Structure definitions for network protocols. Since this file is used for
+ * both NET and NET_LWIP, and given that the two network stacks do have
+ * conflicting types (for instance struct icmp_hdr), it is on purpose that the
+ * structures are defined locally with minimal dependencies -- <asm/types.h> is
+ * included for the bit types and that's it.
+ */
+
+#define ETHADDR_LEN 6
+#define IP4_LEN 4
+
+struct ethhdr {
+ u8 dst[ETHADDR_LEN];
+ u8 src[ETHADDR_LEN];
+ u16 protlen;
+} __attribute__((packed));
+
+#define ETHHDR_SIZE (sizeof(struct ethhdr))
+
+struct arphdr {
+ u16 htype;
+ u16 ptype;
+ u8 hlen;
+ u8 plen;
+ u16 op;
+} __attribute__((packed));
+
+#define ARPHDR_SIZE (sizeof(struct arphdr))
+
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+struct arpdata {
+ u8 sha[ETHADDR_LEN];
+ u32 spa;
+ u8 tha[ETHADDR_LEN];
+ u32 tpa;
+} __attribute__((packed));
+
+#define ARPDATA_SIZE (sizeof(struct arpdata))
+
+struct iphdr {
+ u8 hl_v;
+ u8 tos;
+ u16 len;
+ u16 id;
+ u16 off;
+ u8 ttl;
+ u8 prot;
+ u16 sum;
+ u32 src;
+ u32 dst;
+} __attribute__((packed));
+
+#define IPHDR_SIZE (sizeof(struct iphdr))
+
+struct icmphdr {
+ u8 type;
+ u8 code;
+ u16 checksum;
+ u16 id;
+ u16 sequence;
+} __attribute__((packed));
+
+#define ICMPHDR_SIZE (sizeof(struct icmphdr))
+
+#define ICMP_ECHO_REQUEST 8
+#define ICMP_ECHO_REPLY 0
+#define IPPROTO_ICMP 1
DECLARE_GLOBAL_DATA_PTR;
+static const u8 null_ethaddr[6];
static bool skip_timeout;
/*
@@ -59,17 +130,19 @@ int sandbox_eth_arp_req_to_reply(struct udevice *dev, void *packet,
unsigned int len)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
- struct ethernet_hdr *eth = packet;
- struct arp_hdr *arp;
- struct ethernet_hdr *eth_recv;
- struct arp_hdr *arp_recv;
-
- if (ntohs(eth->et_protlen) != PROT_ARP)
+ struct ethhdr *eth = packet;
+ struct arphdr *arp;
+ struct arpdata *arpd;
+ struct ethhdr *eth_recv;
+ struct arphdr *arp_recv;
+ struct arpdata *arp_recvd;
+
+ if (ntohs(eth->protlen) != PROT_ARP)
return -EAGAIN;
- arp = packet + ETHER_HDR_SIZE;
+ arp = packet + ETHHDR_SIZE;
- if (ntohs(arp->ar_op) != ARPOP_REQUEST)
+ if (ntohs(arp->op) != ARP_REQUEST)
return -EAGAIN;
/* Don't allow the buffer to overrun */
@@ -77,27 +150,29 @@ int sandbox_eth_arp_req_to_reply(struct udevice *dev, void *packet,
return 0;
/* store this as the assumed IP of the fake host */
- priv->fake_host_ipaddr = net_read_ip(&arp->ar_tpa);
+ arpd = (struct arpdata *)(arp + 1);
+ priv->fake_host_ipaddr.s_addr = arpd->tpa;
/* Formulate a fake response */
eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
- memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
- memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
- eth_recv->et_protlen = htons(PROT_ARP);
-
- arp_recv = (void *)eth_recv + ETHER_HDR_SIZE;
- arp_recv->ar_hrd = htons(ARP_ETHER);
- arp_recv->ar_pro = htons(PROT_IP);
- arp_recv->ar_hln = ARP_HLEN;
- arp_recv->ar_pln = ARP_PLEN;
- arp_recv->ar_op = htons(ARPOP_REPLY);
- memcpy(&arp_recv->ar_sha, priv->fake_host_hwaddr, ARP_HLEN);
- net_write_ip(&arp_recv->ar_spa, priv->fake_host_ipaddr);
- memcpy(&arp_recv->ar_tha, &arp->ar_sha, ARP_HLEN);
- net_copy_ip(&arp_recv->ar_tpa, &arp->ar_spa);
-
- priv->recv_packet_length[priv->recv_packets] =
- ETHER_HDR_SIZE + ARP_HDR_SIZE;
+ memcpy(eth_recv->dst, eth->src, ETHADDR_LEN);
+ memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN);
+ eth_recv->protlen = htons(PROT_ARP);
+
+ arp_recv = (void *)eth_recv + ETHHDR_SIZE;
+ arp_recv->htype = htons(ARP_ETHER);
+ arp_recv->ptype = htons(PROT_IP);
+ arp_recv->hlen = ETHADDR_LEN;
+ arp_recv->plen = IP4_LEN;
+ arp_recv->op = htons(ARP_REPLY);
+ arp_recvd = (struct arpdata *)(arp_recv + 1);
+ memcpy(&arp_recvd->sha, priv->fake_host_hwaddr, ETHADDR_LEN);
+ arp_recvd->spa = priv->fake_host_ipaddr.s_addr;
+ memcpy(&arp_recvd->tha, &arpd->sha, ETHADDR_LEN);
+ arp_recvd->tpa = arpd->spa;
+
+ priv->recv_packet_length[priv->recv_packets] = ETHHDR_SIZE +
+ ARPHDR_SIZE + ARPDATA_SIZE;
++priv->recv_packets;
return 0;
@@ -114,22 +189,22 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
unsigned int len)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
- struct ethernet_hdr *eth = packet;
- struct ip_udp_hdr *ip;
- struct icmp_hdr *icmp;
- struct ethernet_hdr *eth_recv;
- struct ip_udp_hdr *ipr;
- struct icmp_hdr *icmpr;
-
- if (ntohs(eth->et_protlen) != PROT_IP)
+ struct ethhdr *eth = packet;
+ struct iphdr *ip;
+ struct icmphdr *icmp;
+ struct ethhdr *eth_recv;
+ struct iphdr *ipr;
+ struct icmphdr *icmpr;
+
+ if (ntohs(eth->protlen) != PROT_IP)
return -EAGAIN;
- ip = packet + ETHER_HDR_SIZE;
+ ip = packet + ETHHDR_SIZE;
- if (ip->ip_p != IPPROTO_ICMP)
+ if (ip->prot != IPPROTO_ICMP)
return -EAGAIN;
- icmp = (struct icmp_hdr *)&ip->udp_src;
+ icmp = (struct icmphdr *)(ip + 1);
if (icmp->type != ICMP_ECHO_REQUEST)
return -EAGAIN;
@@ -141,19 +216,19 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
/* reply to the ping */
eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
memcpy(eth_recv, packet, len);
- ipr = (void *)eth_recv + ETHER_HDR_SIZE;
- icmpr = (struct icmp_hdr *)&ipr->udp_src;
- memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
- memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
- ipr->ip_sum = 0;
- ipr->ip_off = 0;
- net_copy_ip((void *)&ipr->ip_dst, &ip->ip_src);
- net_write_ip((void *)&ipr->ip_src, priv->fake_host_ipaddr);
- ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
+ ipr = (void *)eth_recv + ETHHDR_SIZE;
+ icmpr = (struct icmphdr *)(ipr + 1);
+ memcpy(eth_recv->dst, eth->src, ETHADDR_LEN);
+ memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN);
+ ipr->sum = 0;
+ ipr->off = 0;
+ ipr->dst = ip->src;
+ ipr->src = priv->fake_host_ipaddr.s_addr;
+ ipr->sum = compute_ip_checksum(ipr, IPHDR_SIZE);
icmpr->type = ICMP_ECHO_REPLY;
icmpr->checksum = 0;
- icmpr->checksum = compute_ip_checksum(icmpr, ICMP_HDR_SIZE);
+ icmpr->checksum = compute_ip_checksum(icmpr, ICMPHDR_SIZE);
priv->recv_packet_length[priv->recv_packets] = len;
++priv->recv_packets;
@@ -171,8 +246,9 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
int sandbox_eth_recv_arp_req(struct udevice *dev)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
- struct ethernet_hdr *eth_recv;
- struct arp_hdr *arp_recv;
+ struct ethhdr *eth_recv;
+ struct arphdr *arp_recv;
+ struct arpdata *arp_recvd;
/* Don't allow the buffer to overrun */
if (priv->recv_packets >= PKTBUFSRX)
@@ -180,23 +256,24 @@ int sandbox_eth_recv_arp_req(struct udevice *dev)
/* Formulate a fake request */
eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
- memcpy(eth_recv->et_dest, net_bcast_ethaddr, ARP_HLEN);
- memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
- eth_recv->et_protlen = htons(PROT_ARP);
-
- arp_recv = (void *)eth_recv + ETHER_HDR_SIZE;
- arp_recv->ar_hrd = htons(ARP_ETHER);
- arp_recv->ar_pro = htons(PROT_IP);
- arp_recv->ar_hln = ARP_HLEN;
- arp_recv->ar_pln = ARP_PLEN;
- arp_recv->ar_op = htons(ARPOP_REQUEST);
- memcpy(&arp_recv->ar_sha, priv->fake_host_hwaddr, ARP_HLEN);
- net_write_ip(&arp_recv->ar_spa, priv->fake_host_ipaddr);
- memcpy(&arp_recv->ar_tha, net_null_ethaddr, ARP_HLEN);
- net_write_ip(&arp_recv->ar_tpa, net_ip);
+ memcpy(eth_recv->dst, net_bcast_ethaddr, ETHADDR_LEN);
+ memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN);
+ eth_recv->protlen = htons(PROT_ARP);
+
+ arp_recv = (void *)eth_recv + ETHHDR_SIZE;
+ arp_recv->htype = htons(ARP_ETHER);
+ arp_recv->ptype = htons(PROT_IP);
+ arp_recv->hlen = ETHADDR_LEN;
+ arp_recv->plen = IP4_LEN;
+ arp_recv->op = htons(ARP_REQUEST);
+ arp_recvd = (struct arpdata *)(arp_recv + 1);
+ memcpy(&arp_recvd->sha, priv->fake_host_hwaddr, ETHADDR_LEN);
+ arp_recvd->spa = priv->fake_host_ipaddr.s_addr;
+ memcpy(&arp_recvd->tha, null_ethaddr, ETHADDR_LEN);
+ arp_recvd->tpa = net_ip.s_addr;
priv->recv_packet_length[priv->recv_packets] =
- ETHER_HDR_SIZE + ARP_HDR_SIZE;
+ ETHHDR_SIZE + ARPHDR_SIZE + ARPDATA_SIZE;
++priv->recv_packets;
return 0;
@@ -212,9 +289,10 @@ int sandbox_eth_recv_arp_req(struct udevice *dev)
int sandbox_eth_recv_ping_req(struct udevice *dev)
{
struct eth_sandbox_priv *priv = dev_get_priv(dev);
- struct ethernet_hdr *eth_recv;
- struct ip_udp_hdr *ipr;
- struct icmp_hdr *icmpr;
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct ethhdr *eth_recv;
+ struct iphdr *ipr;
+ struct icmphdr *icmpr;
/* Don't allow the buffer to overrun */
if (priv->recv_packets >= PKTBUFSRX)
@@ -223,31 +301,31 @@ int sandbox_eth_recv_ping_req(struct udevice *dev)
/* Formulate a fake ping */
eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
- memcpy(eth_recv->et_dest, net_ethaddr, ARP_HLEN);
- memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
- eth_recv->et_protlen = htons(PROT_IP);
+ memcpy(eth_recv->dst, pdata->enetaddr, ETHADDR_LEN);
+ memcpy(eth_recv->src, priv->fake_host_hwaddr, ETHADDR_LEN);
+ eth_recv->protlen = htons(PROT_IP);
- ipr = (void *)eth_recv + ETHER_HDR_SIZE;
- ipr->ip_hl_v = 0x45;
- ipr->ip_len = htons(IP_ICMP_HDR_SIZE);
- ipr->ip_off = htons(IP_FLAGS_DFRAG);
- ipr->ip_p = IPPROTO_ICMP;
- ipr->ip_sum = 0;
- net_write_ip(&ipr->ip_src, priv->fake_host_ipaddr);
- net_write_ip(&ipr->ip_dst, net_ip);
- ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
+ ipr = (void *)eth_recv + ETHHDR_SIZE;
+ ipr->hl_v = 0x45;
+ ipr->len = htons(IPHDR_SIZE + ICMPHDR_SIZE);
+ ipr->off = htons(IP_FLAGS_DFRAG);
+ ipr->prot = IPPROTO_ICMP;
+ ipr->sum = 0;
+ ipr->src = priv->fake_host_ipaddr.s_addr;
+ ipr->dst = net_ip.s_addr;
+ ipr->sum = compute_ip_checksum(ipr, IPHDR_SIZE);
- icmpr = (struct icmp_hdr *)&ipr->udp_src;
+ icmpr = (struct icmphdr *)(ipr + 1);
icmpr->type = ICMP_ECHO_REQUEST;
icmpr->code = 0;
icmpr->checksum = 0;
- icmpr->un.echo.id = 0;
- icmpr->un.echo.sequence = htons(1);
- icmpr->checksum = compute_ip_checksum(icmpr, ICMP_HDR_SIZE);
+ icmpr->id = 0;
+ icmpr->sequence = htons(1);
+ icmpr->checksum = compute_ip_checksum(icmpr, ICMPHDR_SIZE);
priv->recv_packet_length[priv->recv_packets] =
- ETHER_HDR_SIZE + IP_ICMP_HDR_SIZE;
+ ETHHDR_SIZE + IPHDR_SIZE + ICMPHDR_SIZE;
++priv->recv_packets;
return 0;
@@ -398,7 +476,7 @@ static int sb_eth_write_hwaddr(struct udevice *dev)
debug("eth_sandbox %s: Write HW ADDR - %pM\n", dev->name,
pdata->enetaddr);
- memcpy(priv->fake_host_hwaddr, pdata->enetaddr, ARP_HLEN);
+ memcpy(priv->fake_host_hwaddr, pdata->enetaddr, ETHADDR_LEN);
return 0;
}
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index f1ce994cfd5..f695a3a41d2 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -643,6 +643,97 @@ static void sh_ether_stop(struct udevice *dev)
sh_eth_stop(&priv->shdev);
}
+/******* for bb_miiphy *******/
+static int sh_eth_bb_mdio_active(struct mii_dev *miidev)
+{
+ struct sh_eth_dev *eth = miidev->priv;
+ struct sh_eth_info *port_info = &eth->port_info[eth->port];
+
+ sh_eth_write(port_info, sh_eth_read(port_info, PIR) | PIR_MMD, PIR);
+
+ return 0;
+}
+
+static int sh_eth_bb_mdio_tristate(struct mii_dev *miidev)
+{
+ struct sh_eth_dev *eth = miidev->priv;
+ struct sh_eth_info *port_info = &eth->port_info[eth->port];
+
+ sh_eth_write(port_info, sh_eth_read(port_info, PIR) & ~PIR_MMD, PIR);
+
+ return 0;
+}
+
+static int sh_eth_bb_set_mdio(struct mii_dev *miidev, int v)
+{
+ struct sh_eth_dev *eth = miidev->priv;
+ struct sh_eth_info *port_info = &eth->port_info[eth->port];
+
+ if (v)
+ sh_eth_write(port_info,
+ sh_eth_read(port_info, PIR) | PIR_MDO, PIR);
+ else
+ sh_eth_write(port_info,
+ sh_eth_read(port_info, PIR) & ~PIR_MDO, PIR);
+
+ return 0;
+}
+
+static int sh_eth_bb_get_mdio(struct mii_dev *miidev, int *v)
+{
+ struct sh_eth_dev *eth = miidev->priv;
+ struct sh_eth_info *port_info = &eth->port_info[eth->port];
+
+ *v = (sh_eth_read(port_info, PIR) & PIR_MDI) >> 3;
+
+ return 0;
+}
+
+static int sh_eth_bb_set_mdc(struct mii_dev *miidev, int v)
+{
+ struct sh_eth_dev *eth = miidev->priv;
+ struct sh_eth_info *port_info = &eth->port_info[eth->port];
+
+ if (v)
+ sh_eth_write(port_info,
+ sh_eth_read(port_info, PIR) | PIR_MDC, PIR);
+ else
+ sh_eth_write(port_info,
+ sh_eth_read(port_info, PIR) & ~PIR_MDC, PIR);
+
+ return 0;
+}
+
+static int sh_eth_bb_delay(struct mii_dev *miidev)
+{
+ udelay(10);
+
+ return 0;
+}
+
+static const struct bb_miiphy_bus_ops sh_ether_bb_miiphy_bus_ops = {
+ .mdio_active = sh_eth_bb_mdio_active,
+ .mdio_tristate = sh_eth_bb_mdio_tristate,
+ .set_mdio = sh_eth_bb_set_mdio,
+ .get_mdio = sh_eth_bb_get_mdio,
+ .set_mdc = sh_eth_bb_set_mdc,
+ .delay = sh_eth_bb_delay,
+};
+
+static int sh_eth_bb_miiphy_read(struct mii_dev *miidev, int addr,
+ int devad, int reg)
+{
+ return bb_miiphy_read(miidev, &sh_ether_bb_miiphy_bus_ops,
+ addr, devad, reg);
+}
+
+static int sh_eth_bb_miiphy_write(struct mii_dev *miidev, int addr,
+ int devad, int reg, u16 value)
+{
+ return bb_miiphy_write(miidev, &sh_ether_bb_miiphy_bus_ops,
+ addr, devad, reg, value);
+}
+
static int sh_ether_probe(struct udevice *udev)
{
struct eth_pdata *pdata = dev_get_plat(udev);
@@ -664,16 +755,16 @@ static int sh_ether_probe(struct udevice *udev)
return ret;
}
- mdiodev->read = bb_miiphy_read;
- mdiodev->write = bb_miiphy_write;
- bb_miiphy_buses[0].priv = eth;
+ mdiodev->read = sh_eth_bb_miiphy_read;
+ mdiodev->write = sh_eth_bb_miiphy_write;
+ mdiodev->priv = eth;
snprintf(mdiodev->name, sizeof(mdiodev->name), udev->name);
ret = mdio_register(mdiodev);
if (ret < 0)
goto err_mdio_register;
- priv->bus = miiphy_get_dev_by_name(udev->name);
+ priv->bus = mdiodev;
eth->port = CFG_SH_ETHER_USE_PORT;
eth->port_info[eth->port].phy_addr = CFG_SH_ETHER_PHY_ADDR;
@@ -748,8 +839,6 @@ int sh_ether_of_to_plat(struct udevice *dev)
if (cell)
pdata->max_speed = fdt32_to_cpu(*cell);
- sprintf(bb_miiphy_buses[0].name, dev->name);
-
return 0;
}
@@ -775,91 +864,3 @@ U_BOOT_DRIVER(eth_sh_ether) = {
.plat_auto = sizeof(struct eth_pdata),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
-
-/******* for bb_miiphy *******/
-static int sh_eth_bb_init(struct bb_miiphy_bus *bus)
-{
- return 0;
-}
-
-static int sh_eth_bb_mdio_active(struct bb_miiphy_bus *bus)
-{
- struct sh_eth_dev *eth = bus->priv;
- struct sh_eth_info *port_info = &eth->port_info[eth->port];
-
- sh_eth_write(port_info, sh_eth_read(port_info, PIR) | PIR_MMD, PIR);
-
- return 0;
-}
-
-static int sh_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus)
-{
- struct sh_eth_dev *eth = bus->priv;
- struct sh_eth_info *port_info = &eth->port_info[eth->port];
-
- sh_eth_write(port_info, sh_eth_read(port_info, PIR) & ~PIR_MMD, PIR);
-
- return 0;
-}
-
-static int sh_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v)
-{
- struct sh_eth_dev *eth = bus->priv;
- struct sh_eth_info *port_info = &eth->port_info[eth->port];
-
- if (v)
- sh_eth_write(port_info,
- sh_eth_read(port_info, PIR) | PIR_MDO, PIR);
- else
- sh_eth_write(port_info,
- sh_eth_read(port_info, PIR) & ~PIR_MDO, PIR);
-
- return 0;
-}
-
-static int sh_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v)
-{
- struct sh_eth_dev *eth = bus->priv;
- struct sh_eth_info *port_info = &eth->port_info[eth->port];
-
- *v = (sh_eth_read(port_info, PIR) & PIR_MDI) >> 3;
-
- return 0;
-}
-
-static int sh_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v)
-{
- struct sh_eth_dev *eth = bus->priv;
- struct sh_eth_info *port_info = &eth->port_info[eth->port];
-
- if (v)
- sh_eth_write(port_info,
- sh_eth_read(port_info, PIR) | PIR_MDC, PIR);
- else
- sh_eth_write(port_info,
- sh_eth_read(port_info, PIR) & ~PIR_MDC, PIR);
-
- return 0;
-}
-
-static int sh_eth_bb_delay(struct bb_miiphy_bus *bus)
-{
- udelay(10);
-
- return 0;
-}
-
-struct bb_miiphy_bus bb_miiphy_buses[] = {
- {
- .name = "sh_eth",
- .init = sh_eth_bb_init,
- .mdio_active = sh_eth_bb_mdio_active,
- .mdio_tristate = sh_eth_bb_mdio_tristate,
- .set_mdio = sh_eth_bb_set_mdio,
- .get_mdio = sh_eth_bb_get_mdio,
- .set_mdc = sh_eth_bb_set_mdc,
- .delay = sh_eth_bb_delay,
- }
-};
-
-int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses);
diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index 0da182d9f4c..8433e7db265 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -335,6 +335,7 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
reg |= SC_RMII_EN | SC_ETCS_EXT_GMII;
break;
}
+ fallthrough;
default:
debug("%s: Invalid PHY interface\n", __func__);
return -EINVAL;
diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c
index c70b42f6bcc..3c62fc0b428 100644
--- a/drivers/net/ti/am65-cpsw-nuss.c
+++ b/drivers/net/ti/am65-cpsw-nuss.c
@@ -661,7 +661,7 @@ static int am65_cpsw_ofdata_parse_phy(struct udevice *dev)
dev_read_u32(dev, "max-speed", (u32 *)&pdata->max_speed);
if (pdata->max_speed)
- dev_err(dev, "Port %u speed froced to %uMbit\n",
+ dev_err(dev, "Port %u speed forced to %uMbit\n",
priv->port_id, pdata->max_speed);
return 0;
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/pci/Kconfig b/drivers/pci/Kconfig
index 4f876d39875..409049137cc 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -67,6 +67,7 @@ config PCI_CONFIG_HOST_BRIDGE
config PCI_MAP_SYSTEM_MEMORY
bool "Map local system memory from a virtual base address"
depends on MIPS
+ default y if !ARCH_MAP_SYSMEM
help
Say Y if base address of system memory is being used as a virtual address
instead of a physical address (e.g. on MIPS). The PCI core will then remap
@@ -75,6 +76,15 @@ config PCI_MAP_SYSTEM_MEMORY
This should only be required on MIPS where CFG_SYS_SDRAM_BASE is still
being used as virtual address.
+config PCI_BRIDGE_MEM_ALIGNMENT
+ hex "Alignment boundary of PCI memory resource allocation"
+ default 0x10000 if TARGET_BOSTON
+ default 0x100000
+ help
+ Specify a boundary for alignment of PCI memory resource allocation,
+ this is normally 0x100000 (1MB) but can be reduced to accommodate
+ hardware with tight bridge range if hardware allows.
+
config PCI_SRIOV
bool "Enable Single Root I/O Virtualization support for PCI"
help
diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c
index 90f81886445..4a1c782be36 100644
--- a/drivers/pci/pci_auto.c
+++ b/drivers/pci/pci_auto.c
@@ -107,7 +107,8 @@ static void dm_pciauto_setup_device(struct udevice *dev,
}
if (prefetch &&
- (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH))
+ (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
+ (found_mem64 || prefetch->bus_lower < 0x100000000ULL))
bar_res = prefetch;
else
bar_res = mem;
@@ -372,8 +373,8 @@ void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus)
dm_pci_write_config8(dev, PCI_SUBORDINATE_BUS, 0xff);
if (pci_mem) {
- /* Round memory allocator to 1MB boundary */
- pciauto_region_align(pci_mem, 0x100000);
+ /* Round memory allocator */
+ pciauto_region_align(pci_mem, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT);
/*
* Set up memory and I/O filter limits, assume 32-bit
@@ -387,8 +388,8 @@ void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus)
}
if (pci_prefetch) {
- /* Round memory allocator to 1MB boundary */
- pciauto_region_align(pci_prefetch, 0x100000);
+ /* Round memory allocator */
+ pciauto_region_align(pci_prefetch, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT);
/*
* Set up memory and I/O filter limits, assume 32-bit
@@ -465,8 +466,8 @@ void dm_pciauto_postscan_setup_bridge(struct udevice *dev, int sub_bus)
dm_pci_write_config8(dev, PCI_SUBORDINATE_BUS, sub_bus - dev_seq(ctlr));
if (pci_mem) {
- /* Round memory allocator to 1MB boundary */
- pciauto_region_align(pci_mem, 0x100000);
+ /* Round memory allocator */
+ pciauto_region_align(pci_mem, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT);
dm_pci_write_config16(dev, PCI_MEMORY_LIMIT,
((pci_mem->bus_lower - 1) >> 16) &
@@ -480,8 +481,8 @@ void dm_pciauto_postscan_setup_bridge(struct udevice *dev, int sub_bus)
&prefechable_64);
prefechable_64 &= PCI_PREF_RANGE_TYPE_MASK;
- /* Round memory allocator to 1MB boundary */
- pciauto_region_align(pci_prefetch, 0x100000);
+ /* Round memory allocator */
+ pciauto_region_align(pci_prefetch, CONFIG_PCI_BRIDGE_MEM_ALIGNMENT);
dm_pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT,
(((pci_prefetch->bus_lower - 1) >> 16) &
diff --git a/drivers/pci/pcie_cdns_ti.c b/drivers/pci/pcie_cdns_ti.c
index 41469a186a3..9d1d123a18c 100644
--- a/drivers/pci/pcie_cdns_ti.c
+++ b/drivers/pci/pcie_cdns_ti.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/log2.h>
+#include <linux/sizes.h>
#include <power-domain.h>
#include <regmap.h>
#include <syscon.h>
@@ -834,11 +835,21 @@ static const struct pcie_cdns_ti_data j7200_pcie_rc_data = {
.max_lanes = 2,
};
+static const struct pcie_cdns_ti_data am64_pcie_rc_data = {
+ .mode = PCIE_MODE_RC,
+ .quirk_detect_quiet_flag = true,
+ .max_lanes = 1,
+};
+
static const struct udevice_id pcie_cdns_ti_ids[] = {
{
.compatible = "ti,j7200-pcie-host",
.data = (ulong)&j7200_pcie_rc_data,
},
+ {
+ .compatible = "ti,am64-pcie-host",
+ .data = (ulong)&am64_pcie_rc_data,
+ },
{},
};
diff --git a/drivers/pci/pcie_xilinx.c b/drivers/pci/pcie_xilinx.c
index a674ab04bee..63058e8e7c5 100644
--- a/drivers/pci/pcie_xilinx.c
+++ b/drivers/pci/pcie_xilinx.c
@@ -18,14 +18,19 @@
*/
struct xilinx_pcie {
void *cfg_base;
+ pci_size_t size;
+ int first_busno;
};
/* Register definitions */
-#define XILINX_PCIE_REG_PSCR 0x144
-#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11)
-#define XILINX_PCIE_REG_RPSC 0x148
-#define XILINX_PCIE_REG_RPSC_BEN BIT(0)
-
+#define XILINX_PCIE_REG_BRIDGE_INFO 0x130
+#define XILINX_PCIE_REG_BRIDGE_INFO_ECAMSZ_SHIFT 16
+#define XILINX_PCIE_REG_BRIDGE_INFO_ECAMSZ_MASK (0x7 << 16)
+#define XILINX_PCIE_REG_INT_MASK 0x13c
+#define XILINX_PCIE_REG_PSCR 0x144
+#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11)
+#define XILINX_PCIE_REG_RPSC 0x148
+#define XILINX_PCIE_REG_RPSC_BEN BIT(0)
/**
* pcie_xilinx_link_up() - Check whether the PCIe link is up
* @pcie: Pointer to the PCI controller state
@@ -61,14 +66,18 @@ static int pcie_xilinx_config_address(const struct udevice *udev, pci_dev_t bdf,
uint offset, void **paddress)
{
struct xilinx_pcie *pcie = dev_get_priv(udev);
- unsigned int bus = PCI_BUS(bdf);
+ unsigned int bus = PCI_BUS(bdf) - pcie->first_busno;
unsigned int dev = PCI_DEV(bdf);
unsigned int func = PCI_FUNC(bdf);
+ int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16);
void *addr;
if ((bus > 0) && !pcie_xilinx_link_up(pcie))
return -ENODEV;
+ if (bus > num_buses)
+ return -ENODEV;
+
/*
* Busses 0 (host-PCIe bridge) & 1 (its immediate child) are
* limited to a single device each.
@@ -142,20 +151,37 @@ static int pcie_xilinx_of_to_plat(struct udevice *dev)
struct xilinx_pcie *pcie = dev_get_priv(dev);
fdt_addr_t addr;
fdt_size_t size;
- u32 rpsc;
addr = dev_read_addr_size(dev, &size);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
- pcie->cfg_base = devm_ioremap(dev, addr, size);
- if (IS_ERR(pcie->cfg_base))
- return PTR_ERR(pcie->cfg_base);
+ pcie->cfg_base = map_physmem(addr, size, MAP_NOCACHE);
+ if (!pcie->cfg_base)
+ return -ENOMEM;
+ pcie->size = size;
+ return 0;
+}
- /* Enable the Bridge enable bit */
- rpsc = __raw_readl(pcie->cfg_base + XILINX_PCIE_REG_RPSC);
+static int pci_xilinx_probe(struct udevice *dev)
+{
+ struct xilinx_pcie *pcie = dev_get_priv(dev);
+ u32 rpsc;
+ int num_buses = DIV_ROUND_UP(pcie->size, 1 << 16);
+
+ pcie->first_busno = dev_seq(dev);
+
+ /* Disable all interrupts */
+ writel(0, pcie->cfg_base + XILINX_PCIE_REG_INT_MASK);
+
+ /* Enable the bridge */
+ rpsc = readl(pcie->cfg_base + XILINX_PCIE_REG_RPSC);
rpsc |= XILINX_PCIE_REG_RPSC_BEN;
- __raw_writel(rpsc, pcie->cfg_base + XILINX_PCIE_REG_RPSC);
+ writel(rpsc, pcie->cfg_base + XILINX_PCIE_REG_RPSC);
+
+ /* Enable access to all possible subordinate buses */
+ writel((0 << 0) | (1 << 8) | (num_buses << 16),
+ pcie->cfg_base + PCI_PRIMARY_BUS);
return 0;
}
@@ -176,5 +202,6 @@ U_BOOT_DRIVER(pcie_xilinx) = {
.of_match = pcie_xilinx_ids,
.ops = &pcie_xilinx_ops,
.of_to_plat = pcie_xilinx_of_to_plat,
+ .probe = pci_xilinx_probe,
.priv_auto = sizeof(struct xilinx_pcie),
};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e12347e8a03..d3fe90d939e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -163,8 +163,8 @@ config PHY_RCAR_GEN2
config PHY_RCAR_GEN3
tristate "Renesas R-Car Gen3 USB PHY"
- depends on PHY && RCAR_GEN3 && CLK && DM_REGULATOR
- default y if RCAR_GEN3
+ depends on PHY && CLK && DM_REGULATOR && (RCAR_GEN3 || RZG2L)
+ default y if (RCAR_GEN3 || RZG2L)
help
Support for the Renesas R-Car Gen3 USB PHY. This driver operates the
PHY connected to EHCI USB module and controls USB OTG operation.
@@ -309,5 +309,6 @@ source "drivers/phy/cadence/Kconfig"
source "drivers/phy/ti/Kconfig"
source "drivers/phy/qcom/Kconfig"
source "drivers/phy/renesas/Kconfig"
+source "drivers/phy/starfive/Kconfig"
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c35f9294dd9..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
@@ -44,3 +44,4 @@ obj-y += cadence/
obj-y += ti/
obj-y += qcom/
obj-y += renesas/
+obj-y += starfive/
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index b9306c9a827..3acffb40b0b 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -85,41 +85,14 @@ struct sun4i_usb_phy_cfg {
int missing_phys;
};
-struct sun4i_usb_phy_info {
- const char *gpio_vbus;
- const char *gpio_vbus_det;
- const char *gpio_id_det;
-} phy_info[] = {
- {
- .gpio_vbus = CONFIG_USB0_VBUS_PIN,
- .gpio_vbus_det = CONFIG_USB0_VBUS_DET,
- .gpio_id_det = CONFIG_USB0_ID_DET,
- },
- {
- .gpio_vbus = CONFIG_USB1_VBUS_PIN,
- .gpio_vbus_det = NULL,
- .gpio_id_det = NULL,
- },
- {
- .gpio_vbus = CONFIG_USB2_VBUS_PIN,
- .gpio_vbus_det = NULL,
- .gpio_id_det = NULL,
- },
- {
- .gpio_vbus = CONFIG_USB3_VBUS_PIN,
- .gpio_vbus_det = NULL,
- .gpio_id_det = NULL,
- },
-};
-
struct sun4i_usb_phy_plat {
void __iomem *pmu;
- struct gpio_desc gpio_vbus;
struct gpio_desc gpio_vbus_det;
struct gpio_desc gpio_id_det;
struct clk clocks;
struct clk clk2;
struct reset_ctl resets;
+ struct udevice *vbus;
int id;
};
@@ -208,6 +181,7 @@ static int sun4i_usb_phy_power_on(struct phy *phy)
{
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
+ int ret;
if (initial_usb_scan_delay) {
mdelay(initial_usb_scan_delay);
@@ -220,8 +194,9 @@ static int sun4i_usb_phy_power_on(struct phy *phy)
return 0;
}
- if (dm_gpio_is_valid(&usb_phy->gpio_vbus))
- dm_gpio_set_value(&usb_phy->gpio_vbus, 1);
+ ret = regulator_set_enable_if_allowed(usb_phy->vbus, true);
+ if (ret)
+ return ret;
return 0;
}
@@ -230,9 +205,11 @@ static int sun4i_usb_phy_power_off(struct phy *phy)
{
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
+ int ret;
- if (dm_gpio_is_valid(&usb_phy->gpio_vbus))
- dm_gpio_set_value(&usb_phy->gpio_vbus, 0);
+ ret = regulator_set_enable_if_allowed(usb_phy->vbus, false);
+ if (ret)
+ return ret;
return 0;
}
@@ -481,48 +458,39 @@ static int sun4i_usb_phy_probe(struct udevice *dev)
data->usb_phy = plat;
for (i = 0; i < data->cfg->num_phys; i++) {
struct sun4i_usb_phy_plat *phy = &plat[i];
- struct sun4i_usb_phy_info *info = &phy_info[i];
- char name[16];
+ char name[32];
if (data->cfg->missing_phys & BIT(i))
continue;
- ret = dm_gpio_lookup_name(info->gpio_vbus, &phy->gpio_vbus);
- if (ret == 0) {
- ret = dm_gpio_request(&phy->gpio_vbus, "usb_vbus");
- if (ret)
- return ret;
- ret = dm_gpio_set_dir_flags(&phy->gpio_vbus,
- GPIOD_IS_OUT);
- if (ret)
- return ret;
- ret = dm_gpio_set_value(&phy->gpio_vbus, 0);
- if (ret)
- return ret;
- }
-
- ret = dm_gpio_lookup_name(info->gpio_vbus_det,
- &phy->gpio_vbus_det);
- if (ret == 0) {
- ret = dm_gpio_request(&phy->gpio_vbus_det,
- "usb_vbus_det");
- if (ret)
- return ret;
- ret = dm_gpio_set_dir_flags(&phy->gpio_vbus_det,
- GPIOD_IS_IN);
+ snprintf(name, sizeof(name), "usb%d_vbus-supply", i);
+ ret = device_get_supply_regulator(dev, name, &phy->vbus);
+ if (phy->vbus) {
+ ret = regulator_set_enable_if_allowed(phy->vbus, false);
if (ret)
return ret;
}
- ret = dm_gpio_lookup_name(info->gpio_id_det, &phy->gpio_id_det);
- if (ret == 0) {
- ret = dm_gpio_request(&phy->gpio_id_det, "usb_id_det");
- if (ret)
+ if (i == 0) {
+ ret = gpio_request_by_name(dev, "usb0_vbus_det-gpios",
+ 0, &phy->gpio_vbus_det,
+ GPIOD_IS_IN);
+ if (ret && ret != -ENOENT) {
+ dev_err(dev,
+ "failed to get VBUS detect GPIO: %d\n",
+ ret);
return ret;
- ret = dm_gpio_set_dir_flags(&phy->gpio_id_det,
- GPIOD_IS_IN | GPIOD_PULL_UP);
- if (ret)
+ }
+
+ ret = gpio_request_by_name(dev, "usb0_id_det-gpios", 0,
+ &phy->gpio_id_det,
+ GPIOD_IS_IN | GPIOD_PULL_UP);
+ if (ret && ret != -ENOENT) {
+ dev_err(dev,
+ "failed to get ID detect GPIO: %d\n",
+ ret);
return ret;
+ }
}
if (data->cfg->dedicated_clocks)
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/phy-rcar-gen3.c b/drivers/phy/phy-rcar-gen3.c
index 8c004eaf4c6..d06861c4f79 100644
--- a/drivers/phy/phy-rcar-gen3.c
+++ b/drivers/phy/phy-rcar-gen3.c
@@ -55,6 +55,7 @@
/* VBCTRL */
#define USB2_VBCTRL_DRVVBUSSEL BIT(8)
+#define USB2_VBCTRL_VBOUT BIT(0)
/* LINECTRL1 */
#define USB2_LINECTRL1_DPRPD_EN BIT(19)
@@ -68,6 +69,13 @@
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4)
+/* RZ/G2L specific */
+#define USB2_OBINT_IDCHG_EN BIT(0)
+#define USB2_LINECTRL1_USB2_IDMON BIT(0)
+
+/* Device flags */
+#define RCAR_GEN3_PHY_NO_ADPCTRL BIT(0)
+
struct rcar_gen3_phy {
fdt_addr_t regs;
struct clk clk;
@@ -122,15 +130,50 @@ static int rcar_gen3_phy_phy_power_off(struct phy *phy)
return regulator_set_enable(priv->vbus_supply, false);
}
+static bool rcar_gen3_phy_check_id(struct phy *phy)
+{
+ const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD;
+ struct rcar_gen3_phy *priv = dev_get_priv(phy->dev);
+ ulong flags = dev_get_driver_data(phy->dev);
+ u32 val;
+
+ if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) {
+ val = readl(priv->regs + USB2_LINECTRL1);
+ return !!(val & USB2_LINECTRL1_USB2_IDMON);
+ }
+
+ val = readl(priv->regs + USB2_ADPCTRL);
+ return (val & adpdevmask) == adpdevmask;
+}
+
+static void rcar_gen3_phy_set_vbus(struct phy *phy, bool enable)
+{
+ struct rcar_gen3_phy *priv = dev_get_priv(phy->dev);
+ ulong flags = dev_get_driver_data(phy->dev);
+ u32 bits = USB2_ADPCTRL_DRVVBUS;
+ u64 reg = USB2_ADPCTRL;
+
+ if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) {
+ bits = USB2_VBCTRL_VBOUT;
+ reg = USB2_VBCTRL;
+ }
+
+ if (enable)
+ setbits_le32(priv->regs + reg, bits);
+ else
+ clrbits_le32(priv->regs + reg, bits);
+}
+
static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
- const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD;
struct rcar_gen3_phy *priv = dev_get_priv(phy->dev);
- u32 adpctrl;
+ ulong flags = dev_get_driver_data(phy->dev);
if (mode == PHY_MODE_USB_OTG) {
if (submode) {
+ u32 obint_enable_bits;
+
/* OTG submode is used as initialization indicator */
writel(USB2_INT_ENABLE_UCOM_INTEN |
USB2_INT_ENABLE_USBH_INTB_EN |
@@ -138,13 +181,16 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode,
priv->regs + USB2_INT_ENABLE);
setbits_le32(priv->regs + USB2_VBCTRL,
USB2_VBCTRL_DRVVBUSSEL);
- writel(USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG,
- priv->regs + USB2_OBINTSTA);
- setbits_le32(priv->regs + USB2_OBINTEN,
- USB2_OBINT_SESSVLDCHG |
- USB2_OBINT_IDDIGCHG);
- setbits_le32(priv->regs + USB2_ADPCTRL,
- USB2_ADPCTRL_IDPULLUP);
+ if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) {
+ obint_enable_bits = USB2_OBINT_IDCHG_EN;
+ } else {
+ obint_enable_bits = USB2_OBINT_SESSVLDCHG |
+ USB2_OBINT_IDDIGCHG;
+ setbits_le32(priv->regs + USB2_ADPCTRL,
+ USB2_ADPCTRL_IDPULLUP);
+ }
+ writel(obint_enable_bits, priv->regs + USB2_OBINTSTA);
+ setbits_le32(priv->regs + USB2_OBINTEN, obint_enable_bits);
clrsetbits_le32(priv->regs + USB2_LINECTRL1,
USB2_LINECTRL1_DP_RPD |
USB2_LINECTRL1_DM_RPD |
@@ -154,8 +200,7 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode,
USB2_LINECTRL1_DMRPD_EN);
}
- adpctrl = readl(priv->regs + USB2_ADPCTRL);
- if ((adpctrl & adpdevmask) == adpdevmask)
+ if (rcar_gen3_phy_check_id(phy))
mode = PHY_MODE_USB_DEVICE;
else
mode = PHY_MODE_USB_HOST;
@@ -165,13 +210,13 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode,
clrbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI);
setbits_le32(priv->regs + USB2_LINECTRL1,
USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
- setbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS);
+ rcar_gen3_phy_set_vbus(phy, true);
} else if (mode == PHY_MODE_USB_DEVICE) {
setbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI);
clrsetbits_le32(priv->regs + USB2_LINECTRL1,
USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD,
USB2_LINECTRL1_DM_RPD);
- clrbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS);
+ rcar_gen3_phy_set_vbus(phy, false);
} else {
dev_err(phy->dev, "Unknown mode %d\n", mode);
return -EINVAL;
@@ -226,7 +271,13 @@ static int rcar_gen3_phy_remove(struct udevice *dev)
}
static const struct udevice_id rcar_gen3_phy_of_match[] = {
- { .compatible = "renesas,rcar-gen3-usb2-phy", },
+ {
+ .compatible = "renesas,rcar-gen3-usb2-phy",
+ },
+ {
+ .compatible = "renesas,rzg2l-usb2-phy",
+ .data = RCAR_GEN3_PHY_NO_ADPCTRL,
+ },
{ },
};
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/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 43f6e020a6a..88b33de1b2a 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -40,11 +40,13 @@ struct rockchip_usb2phy_port_cfg {
struct rockchip_usb2phy_cfg {
unsigned int reg;
struct usb2phy_reg clkout_ctl;
+ struct usb2phy_reg clkout_ctl_phy;
const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
};
struct rockchip_usb2phy {
struct regmap *reg_base;
+ struct regmap *phy_base;
struct clk phyclk;
const struct rockchip_usb2phy_cfg *phy_cfg;
};
@@ -165,6 +167,22 @@ static struct phy_ops rockchip_usb2phy_ops = {
.of_xlate = rockchip_usb2phy_of_xlate,
};
+static void rockchip_usb2phy_clkout_ctl(struct clk *clk, struct regmap **base,
+ const struct usb2phy_reg **clkout_ctl)
+{
+ struct udevice *parent = dev_get_parent(clk->dev);
+ struct rockchip_usb2phy *priv = dev_get_priv(parent);
+ const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg;
+
+ if (priv->phy_cfg->clkout_ctl_phy.enable) {
+ *base = priv->phy_base;
+ *clkout_ctl = &phy_cfg->clkout_ctl_phy;
+ } else {
+ *base = priv->reg_base;
+ *clkout_ctl = &phy_cfg->clkout_ctl;
+ }
+}
+
/**
* round_rate() - Adjust a rate to the exact rate a clock can provide.
* @clk: The clock to manipulate.
@@ -185,13 +203,14 @@ ulong rockchip_usb2phy_clk_round_rate(struct clk *clk, ulong rate)
*/
int rockchip_usb2phy_clk_enable(struct clk *clk)
{
- struct udevice *parent = dev_get_parent(clk->dev);
- struct rockchip_usb2phy *priv = dev_get_priv(parent);
- const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg;
+ const struct usb2phy_reg *clkout_ctl;
+ struct regmap *base;
+
+ rockchip_usb2phy_clkout_ctl(clk, &base, &clkout_ctl);
/* turn on 480m clk output if it is off */
- if (!property_enabled(priv->reg_base, &phy_cfg->clkout_ctl)) {
- property_enable(priv->reg_base, &phy_cfg->clkout_ctl, true);
+ if (!property_enabled(base, clkout_ctl)) {
+ property_enable(base, clkout_ctl, true);
/* waiting for the clk become stable */
usleep_range(1200, 1300);
@@ -208,12 +227,13 @@ int rockchip_usb2phy_clk_enable(struct clk *clk)
*/
int rockchip_usb2phy_clk_disable(struct clk *clk)
{
- struct udevice *parent = dev_get_parent(clk->dev);
- struct rockchip_usb2phy *priv = dev_get_priv(parent);
- const struct rockchip_usb2phy_cfg *phy_cfg = priv->phy_cfg;
+ const struct usb2phy_reg *clkout_ctl;
+ struct regmap *base;
+
+ rockchip_usb2phy_clkout_ctl(clk, &base, &clkout_ctl);
/* turn off 480m clk output */
- property_enable(priv->reg_base, &phy_cfg->clkout_ctl, false);
+ property_enable(base, clkout_ctl, false);
return 0;
}
@@ -281,7 +301,10 @@ static int rockchip_usb2phy_probe(struct udevice *dev)
return ret;
}
- return 0;
+ if (priv->phy_cfg->clkout_ctl_phy.enable)
+ ret = regmap_init_mem_index(dev_ofnode(dev), &priv->phy_base, 0);
+
+ return ret;
}
static int rockchip_usb2phy_bind(struct udevice *dev)
@@ -389,6 +412,22 @@ static const struct rockchip_usb2phy_cfg rk3399_usb2phy_cfgs[] = {
{ /* sentinel */ }
};
+static const struct rockchip_usb2phy_cfg rk3528_phy_cfgs[] = {
+ {
+ .reg = 0xffdf0000,
+ .clkout_ctl_phy = { 0x041c, 7, 2, 0, 0x27 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x004c, 1, 0, 2, 1 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x005c, 1, 0, 2, 1 },
+ }
+ },
+ },
+ { /* sentinel */ }
+};
+
static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
{
.reg = 0xfe8a0000,
@@ -471,6 +510,10 @@ static const struct udevice_id rockchip_usb2phy_ids[] = {
.data = (ulong)&rk3399_usb2phy_cfgs,
},
{
+ .compatible = "rockchip,rk3528-usb2phy",
+ .data = (ulong)&rk3528_phy_cfgs,
+ },
+ {
.compatible = "rockchip,rk3568-usb2phy",
.data = (ulong)&rk3568_phy_cfgs,
},
diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig
new file mode 100644
index 00000000000..d11338ed484
--- /dev/null
+++ b/drivers/phy/starfive/Kconfig
@@ -0,0 +1,21 @@
+#
+# PHY drivers for Starfive platforms
+#
+
+menu "Starfive PHY driver"
+
+config PHY_STARFIVE_JH7110_PCIE
+ bool "Starfive JH7110 PCIe 2.0 PHY driver"
+ depends on PHY
+ help
+ Enable this to support the Starfive JH7110 PCIE 2.0/USB 3.0 PHY.
+ Generic PHY driver JH7110 USB 3.0/ PCIe 2.0.
+
+config PHY_STARFIVE_JH7110_USB2
+ bool "Starfive JH7110 USB 2.0 PHY driver"
+ depends on PHY
+ help
+ Enable this to support the Starfive JH7110 USB 2.0 PHY.
+ Generic PHY driver JH7110 USB 2.0.
+
+endmenu
diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile
new file mode 100644
index 00000000000..82f25aa21b7
--- /dev/null
+++ b/drivers/phy/starfive/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2023 Starfive
+#
+
+obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o
+obj-$(CONFIG_PHY_STARFIVE_JH7110_USB2) += phy-jh7110-usb2.o
diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c
new file mode 100644
index 00000000000..a30582821d9
--- /dev/null
+++ b/drivers/phy/starfive/phy-jh7110-pcie.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * StarFive JH7110 PCIe 2.0 PHY driver
+ *
+ * Copyright (C) 2024 StarFive Technology Co., Ltd.
+ * Author: Minda Chen <minda.chen@starfivetech.com>
+ */
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <errno.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <soc.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#include "phy-jh7110-usb-syscon.h"
+
+#define PCIE_KVCO_LEVEL_OFF 0x28
+#define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c
+#define PCIE_USB3_PHY_SS_MODE BIT(4)
+#define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80
+#define PHY_KVCO_FINE_TUNE_LEVEL 0x91
+#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc
+
+#define PCIE_USB3_PHY_MODE 0x1
+#define PCIE_BUS_WIDTH 0x2
+#define PCIE_USB3_PHY_ENABLE 0x1
+#define PCIE_USB3_PHY_SPLIT 0x1
+
+struct jh7110_pcie_phy {
+ struct phy *phy;
+ struct regmap *stg_syscon;
+ struct regmap *sys_syscon;
+ void __iomem *regs;
+ struct regmap_field *phy_mode;
+ struct regmap_field *bus_width;
+ struct regmap_field *usb3_phy_en;
+ struct regmap_field *usb_split;
+ enum phy_mode mode;
+};
+
+static int phy_pcie_mode_set(struct jh7110_pcie_phy *data, bool usb_mode)
+{
+ unsigned int phy_mode, width, usb3_phy, ss_mode, split;
+
+ /* default is PCIe mode */
+ if (!data->stg_syscon || !data->sys_syscon) {
+ if (usb_mode) {
+ dev_err(data->phy->dev, "doesn't support USB3 mode\n");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (usb_mode) {
+ phy_mode = PCIE_USB3_PHY_MODE;
+ width = 0;
+ usb3_phy = PCIE_USB3_PHY_ENABLE;
+ ss_mode = PCIE_USB3_PHY_SS_MODE;
+ split = 0;
+ } else {
+ phy_mode = 0;
+ width = PCIE_BUS_WIDTH;
+ usb3_phy = 0;
+ ss_mode = 0;
+ split = PCIE_USB3_PHY_SPLIT;
+ }
+
+ regmap_field_write(data->phy_mode, phy_mode);
+ regmap_field_write(data->bus_width, width);
+ regmap_field_write(data->usb3_phy_en, usb3_phy);
+ clrsetbits_le32(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF,
+ PCIE_USB3_PHY_SS_MODE, ss_mode);
+ regmap_field_write(data->usb_split, split);
+
+ return 0;
+}
+
+static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy)
+{
+ /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */
+ writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF);
+ writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF);
+}
+
+static int jh7110_pcie_phy_set_mode(struct phy *phy,
+ enum phy_mode mode, int submode)
+{
+ struct udevice *dev = phy->dev;
+ struct jh7110_pcie_phy *pcie_phy = dev_get_priv(dev);
+ int ret;
+
+ if (mode == pcie_phy->mode)
+ return 0;
+
+ switch (mode) {
+ case PHY_MODE_USB_HOST:
+ case PHY_MODE_USB_DEVICE:
+ case PHY_MODE_USB_OTG:
+ ret = phy_pcie_mode_set(pcie_phy, 1);
+ if (ret)
+ return ret;
+ break;
+ case PHY_MODE_PCIE:
+ phy_pcie_mode_set(pcie_phy, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(phy->dev, "Changing PHY mode to %d\n", mode);
+ pcie_phy->mode = mode;
+
+ return 0;
+}
+
+static const struct phy_ops jh7110_pcie_phy_ops = {
+ .set_mode = jh7110_pcie_phy_set_mode,
+};
+
+static int phy_stg_regfield_init(struct udevice *dev, int mode, int usb3)
+{
+ struct jh7110_pcie_phy *phy = dev_get_priv(dev);
+ struct reg_field phy_mode = REG_FIELD(mode, 20, 21);
+ struct reg_field bus_width = REG_FIELD(usb3, 2, 3);
+ struct reg_field usb3_phy_en = REG_FIELD(usb3, 4, 4);
+
+ phy->phy_mode = devm_regmap_field_alloc(dev, phy->stg_syscon, phy_mode);
+ if (IS_ERR(phy->phy_mode)) {
+ dev_err(dev, "PHY mode reg field init failed\n");
+ return PTR_ERR(phy->phy_mode);
+ }
+
+ phy->bus_width = devm_regmap_field_alloc(dev, phy->stg_syscon, bus_width);
+ if (IS_ERR(phy->bus_width)) {
+ dev_err(dev, "PHY bus width reg field init failed\n");
+ return PTR_ERR(phy->bus_width);
+ }
+
+ phy->usb3_phy_en = devm_regmap_field_alloc(dev, phy->stg_syscon, usb3_phy_en);
+ if (IS_ERR(phy->usb3_phy_en)) {
+ dev_err(dev, "USB3 PHY enable field init failed\n");
+ return PTR_ERR(phy->bus_width);
+ }
+
+ return 0;
+}
+
+static int phy_sys_regfield_init(struct udevice *dev, int split)
+{
+ struct jh7110_pcie_phy *phy = dev_get_priv(dev);
+ struct reg_field usb_split = REG_FIELD(split, USB_PDRSTN_SPLIT_BIT, USB_PDRSTN_SPLIT_BIT);
+
+ phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split);
+ if (IS_ERR(phy->usb_split)) {
+ dev_err(dev, "USB split field init failed\n");
+ return PTR_ERR(phy->usb_split);
+ }
+
+ return 0;
+}
+
+static int starfive_pcie_phy_get_syscon(struct udevice *dev)
+{
+ struct jh7110_pcie_phy *phy = dev_get_priv(dev);
+ struct ofnode_phandle_args sys_phandle, stg_phandle;
+ int ret;
+
+ /* get corresponding syscon phandle */
+ ret = dev_read_phandle_with_args(dev, "starfive,sys-syscon", NULL, 1, 0,
+ &sys_phandle);
+
+ if (ret < 0) {
+ dev_err(dev, "Can't get sys cfg phandle: %d\n", ret);
+ return ret;
+ }
+
+ ret = dev_read_phandle_with_args(dev, "starfive,stg-syscon", NULL, 2, 0,
+ &stg_phandle);
+
+ if (ret < 0) {
+ dev_err(dev, "Can't get stg cfg phandle: %d\n", ret);
+ return ret;
+ }
+
+ phy->sys_syscon = syscon_node_to_regmap(sys_phandle.node);
+ /* get syscon register offset */
+ if (!IS_ERR(phy->sys_syscon)) {
+ ret = phy_sys_regfield_init(dev, SYSCON_USB_PDRSTN_REG_OFFSET);
+ if (ret)
+ return ret;
+ } else {
+ phy->sys_syscon = NULL;
+ }
+
+ phy->stg_syscon = syscon_node_to_regmap(stg_phandle.node);
+ if (!IS_ERR(phy->stg_syscon))
+ return phy_stg_regfield_init(dev, stg_phandle.args[0],
+ stg_phandle.args[1]);
+ else
+ phy->stg_syscon = NULL;
+
+ return 0;
+}
+
+int jh7110_pcie_phy_probe(struct udevice *dev)
+{
+ struct jh7110_pcie_phy *phy = dev_get_priv(dev);
+ int rc;
+
+ phy->regs = dev_read_addr_ptr(dev);
+ if (!phy->regs)
+ return -EINVAL;
+
+ rc = starfive_pcie_phy_get_syscon(dev);
+ if (rc)
+ return rc;
+
+ phy_kvco_gain_set(phy);
+
+ return 0;
+}
+
+static const struct udevice_id jh7110_pcie_phy[] = {
+ { .compatible = "starfive,jh7110-pcie-phy"},
+ {},
+};
+
+U_BOOT_DRIVER(jh7110_pcie_phy) = {
+ .name = "jh7110_pcie_phy",
+ .id = UCLASS_PHY,
+ .of_match = jh7110_pcie_phy,
+ .probe = jh7110_pcie_phy_probe,
+ .ops = &jh7110_pcie_phy_ops,
+ .priv_auto = sizeof(struct jh7110_pcie_phy),
+};
diff --git a/drivers/phy/starfive/phy-jh7110-usb-syscon.h b/drivers/phy/starfive/phy-jh7110-usb-syscon.h
new file mode 100644
index 00000000000..0eb66f0d859
--- /dev/null
+++ b/drivers/phy/starfive/phy-jh7110-usb-syscon.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef PHY_JH7110_USB_SYSCON_H_
+#define PHY_JH7110_USB_SYSCON_H_
+
+#define SYSCON_USB_PDRSTN_REG_OFFSET 0x18
+#define USB_PDRSTN_SPLIT_BIT 17
+
+#endif
diff --git a/drivers/phy/starfive/phy-jh7110-usb2.c b/drivers/phy/starfive/phy-jh7110-usb2.c
new file mode 100644
index 00000000000..1a28381e0df
--- /dev/null
+++ b/drivers/phy/starfive/phy-jh7110-usb2.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * StarFive JH7110 USB 2.0 PHY driver
+ *
+ * Copyright (C) 2024 StarFive Technology Co., Ltd.
+ * Author: Minda Chen <minda.chen@starfivetech.com>
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <errno.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <soc.h>
+#include <syscon.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#include "phy-jh7110-usb-syscon.h"
+
+#define USB_LS_KEEPALIVE_OFF 0x4
+#define USB_LS_KEEPALIVE_ENABLE BIT(4)
+#define USB_PHY_CLK_RATE 125000000
+
+struct jh7110_usb2_phy {
+ struct phy *phy;
+ struct regmap *sys_syscon;
+ void __iomem *regs;
+ struct clk *usb_125m_clk;
+ struct clk *app_125m;
+ struct regmap_field *usb_split;
+ enum phy_mode mode;
+};
+
+static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set)
+{
+ /* Host mode enable the LS speed keep-alive signal */
+ clrsetbits_le32(phy->regs + USB_LS_KEEPALIVE_OFF,
+ USB_LS_KEEPALIVE_ENABLE,
+ set ? USB_LS_KEEPALIVE_ENABLE : 0);
+}
+
+static int usb2_phy_set_mode(struct phy *phy,
+ enum phy_mode mode, int submode)
+{
+ struct udevice *dev = phy->dev;
+ struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev);
+
+ if (mode == usb2_phy->mode)
+ return 0;
+
+ switch (mode) {
+ case PHY_MODE_USB_HOST:
+ case PHY_MODE_USB_DEVICE:
+ case PHY_MODE_USB_OTG:
+ dev_dbg(dev, "Changing PHY to %d\n", mode);
+ usb2_phy->mode = mode;
+ usb2_set_ls_keepalive(usb2_phy, (mode != PHY_MODE_USB_DEVICE));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set default split usb 2.0 only mode */
+ regmap_field_write(usb2_phy->usb_split, true);
+
+ return 0;
+}
+
+static int jh7110_usb2_phy_init(struct phy *phy)
+{
+ struct udevice *dev = phy->dev;
+ struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev);
+ int ret;
+
+ ret = clk_set_rate(usb2_phy->usb_125m_clk, USB_PHY_CLK_RATE);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set 125m clock\n");
+ return ret;
+ }
+
+ return clk_prepare_enable(usb2_phy->app_125m);
+}
+
+static int jh7110_usb2_phy_exit(struct phy *phy)
+{
+ struct udevice *dev = phy->dev;
+ struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev);
+
+ clk_disable_unprepare(usb2_phy->app_125m);
+
+ return 0;
+}
+
+struct phy_ops jh7110_usb2_phy_ops = {
+ .init = jh7110_usb2_phy_init,
+ .exit = jh7110_usb2_phy_exit,
+ .set_mode = usb2_phy_set_mode,
+};
+
+int jh7110_usb2_phy_probe(struct udevice *dev)
+{
+ struct jh7110_usb2_phy *phy = dev_get_priv(dev);
+ ofnode node;
+ struct reg_field usb_split;
+ int ret;
+
+ phy->regs = dev_read_addr_ptr(dev);
+ if (!phy->regs)
+ return -EINVAL;
+
+ node = ofnode_by_compatible(ofnode_null(), "starfive,jh7110-sys-syscon");
+ if (!ofnode_valid(node)) {
+ dev_err(dev, "Can't get syscon dev node\n");
+ return -ENODEV;
+ }
+
+ phy->sys_syscon = syscon_node_to_regmap(node);
+ if (IS_ERR(phy->sys_syscon)) {
+ dev_err(dev, "Can't get syscon regmap: %d\n", ret);
+ return PTR_ERR(phy->sys_syscon);
+ }
+
+ usb_split.reg = SYSCON_USB_PDRSTN_REG_OFFSET;
+ usb_split.lsb = USB_PDRSTN_SPLIT_BIT;
+ usb_split.msb = USB_PDRSTN_SPLIT_BIT;
+ phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split);
+ if (IS_ERR(phy->usb_split)) {
+ dev_err(dev, "USB split field init failed\n");
+ return PTR_ERR(phy->usb_split);
+ }
+
+ phy->usb_125m_clk = devm_clk_get(dev, "125m");
+ if (IS_ERR(phy->usb_125m_clk)) {
+ dev_err(dev, "Failed to get 125m clock\n");
+ return PTR_ERR(phy->usb_125m_clk);
+ }
+
+ phy->app_125m = devm_clk_get(dev, "app_125m");
+ if (IS_ERR(phy->app_125m)) {
+ dev_err(dev, "Failed to get app 125m clock\n");
+ return PTR_ERR(phy->app_125m);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id jh7110_usb2_phy[] = {
+ { .compatible = "starfive,jh7110-usb-phy"},
+ {},
+};
+
+U_BOOT_DRIVER(jh7110_usb2_phy) = {
+ .name = "jh7110_usb2_phy",
+ .id = UCLASS_PHY,
+ .of_match = jh7110_usb2_phy,
+ .probe = jh7110_usb2_phy_probe,
+ .ops = &jh7110_usb2_phy_ops,
+ .priv_auto = sizeof(struct jh7110_usb2_phy),
+};
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/Kconfig b/drivers/pinctrl/Kconfig
index 6ee7dc1cce8..687fb339ea0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -178,6 +178,14 @@ config PINCTRL_APPLE
both the GPIO definitions and pin control functions for each
available multiplex function.
+config PINCTRL_ADI
+ bool "ADI pinctrl driver"
+ depends on DM && ARCH_SC5XX
+ help
+ This driver enables pinctrl support on SC5xx processors. This
+ driver covers only the pin configuration functionality, and
+ GPIO functionality is contained in the separate GPIO driver.
+
config PINCTRL_AR933X
bool "QCA/Athores ar933x pin control driver"
depends on DM && SOC_AR933X
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 634047a91f4..a8eba656843 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -1,13 +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/
@@ -15,10 +16,9 @@ obj-$(CONFIG_ARCH_MTMIPS) += mtmips/
obj-$(CONFIG_ARCH_NPCM) += nuvoton/
obj-$(CONFIG_PINCTRL_QCOM) += qcom/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
-obj-$(CONFIG_ARCH_RZN1) += 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/intel/Kconfig b/drivers/pinctrl/intel/Kconfig
index 316a8fe27fd..0d8bfc53c11 100644
--- a/drivers/pinctrl/intel/Kconfig
+++ b/drivers/pinctrl/intel/Kconfig
@@ -8,9 +8,6 @@ config INTEL_PINCTRL_DUAL_ROUTE_SUPPORT
bool
default y
-config INTEL_PINCTRL_PADCFG_PADTOL
- bool n
-
config INTEL_PINCTRL_IOSTANDBY
bool
default y
diff --git a/drivers/pinctrl/pinctrl-adi-adsp.c b/drivers/pinctrl/pinctrl-adi-adsp.c
new file mode 100644
index 00000000000..debf434212d
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-adi-adsp.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ *
+ * dm pinctrl implementation for ADI ADSP SoCs
+ *
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#define ADSP_PORT_MMIO_SIZE 0x80
+#define ADSP_PORT_PIN_SIZE 16
+
+#define ADSP_PORT_PORT_MUX_BITS 2
+#define ADSP_PORT_PORT_MUX_MASK 0x03
+#define ADSP_PINCTRL_FUNCTION_COUNT 4
+
+#define ADSP_PORT_REG_FER 0x00
+#define ADSP_PORT_REG_FER_SET 0x04
+#define ADSP_PORT_REG_FER_CLEAR 0x08
+#define ADSP_PORT_REG_DATA 0x0c
+#define ADSP_PORT_REG_DATA_SET 0x10
+#define ADSP_PORT_REG_DATA_CLEAR 0x14
+#define ADSP_PORT_REG_DIR 0x18
+#define ADSP_PORT_REG_DIR_SET 0x1c
+#define ADSP_PORT_REG_DIR_CLEAR 0x20
+#define ADSP_PORT_REG_INEN 0x24
+#define ADSP_PORT_REG_INEN_SET 0x28
+#define ADSP_PORT_REG_INEN_CLEAR 0x2c
+#define ADSP_PORT_REG_PORT_MUX 0x30
+#define ADSP_PORT_REG_DATA_TGL 0x34
+#define ADSP_PORT_REG_POLAR 0x38
+#define ADSP_PORT_REG_POLAR_SET 0x3c
+#define ADSP_PORT_REG_POLAR_CLEAR 0x40
+#define ADSP_PORT_REG_LOCK 0x44
+#define ADSP_PORT_REG_TRIG_TGL 0x48
+
+struct adsp_pinctrl_priv {
+ void __iomem *base;
+ int npins;
+ char pinbuf[16];
+};
+
+static u32 get_port(unsigned int pin)
+{
+ return pin / ADSP_PORT_PIN_SIZE;
+}
+
+static u32 get_offset(unsigned int pin)
+{
+ return pin % ADSP_PORT_PIN_SIZE;
+}
+
+static int adsp_pinctrl_pinmux_set(struct udevice *udev, unsigned int pin, unsigned int func)
+{
+ struct adsp_pinctrl_priv *priv = dev_get_priv(udev);
+ void __iomem *portbase;
+ u32 port, offset;
+ u32 val;
+
+ if (pin >= priv->npins)
+ return -ENODEV;
+
+ if (func >= ADSP_PINCTRL_FUNCTION_COUNT)
+ return -EINVAL;
+
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ val = ioread32(portbase + ADSP_PORT_REG_PORT_MUX);
+ val &= ~(ADSP_PORT_PORT_MUX_MASK << (ADSP_PORT_PORT_MUX_BITS * offset));
+ val |= func << (ADSP_PORT_PORT_MUX_BITS * offset);
+ iowrite32(val, portbase + ADSP_PORT_REG_PORT_MUX);
+
+ iowrite32(BIT(offset), portbase + ADSP_PORT_REG_FER_SET);
+ return 0;
+}
+
+static int adsp_pinctrl_set_state(struct udevice *udev, struct udevice *config)
+{
+ const struct fdt_property *pinlist;
+ int length = 0;
+ int ret, i;
+ u32 pin, function;
+
+ pinlist = dev_read_prop(config, "adi,pins", &length);
+ if (!pinlist) {
+ dev_err(udev, "missing adi,pins property in pinctrl config node\n");
+ return -EINVAL;
+ }
+
+ if (length % (sizeof(uint32_t) * 2)) {
+ dev_err(udev, "adi,pins property must be a multiple of two uint32_ts\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < length / sizeof(uint32_t); i += 2) {
+ ret = dev_read_u32_index(config, "adi,pins", i, &pin);
+ if (ret)
+ return ret;
+
+ ret = dev_read_u32_index(config, "adi,pins", i + 1, &function);
+ if (ret)
+ return ret;
+
+ ret = adsp_pinctrl_pinmux_set(udev, pin, function);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct pinctrl_ops adsp_pinctrl_ops = {
+ .set_state = adsp_pinctrl_set_state,
+};
+
+static int adsp_pinctrl_probe(struct udevice *udev)
+{
+ struct adsp_pinctrl_priv *priv = dev_get_priv(udev);
+
+ priv->base = dev_read_addr_ptr(udev);
+ priv->npins = dev_read_u32_default(udev, "adi,npins", 0);
+
+ if (!priv->base) {
+ dev_err(udev, "Missing or invalid pinctrl base address\n");
+ return -ENOENT;
+ }
+
+ if (!priv->npins) {
+ dev_err(udev, "Missing adi,npins property!\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id adsp_pinctrl_match[] = {
+ { .compatible = "adi,adsp-pinctrl" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_adsp_pinctrl) = {
+ .name = "adi_adsp_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = adsp_pinctrl_match,
+ .probe = adsp_pinctrl_probe,
+ .priv_auto = sizeof(struct adsp_pinctrl_priv),
+ .ops = &adsp_pinctrl_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 5038cb535e3..2938635ed95 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -9,6 +9,8 @@
#include <dm.h>
#include <log.h>
#include <asm/global_data.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
#include <dm/pinctrl.h>
#include <asm/hardware.h>
#include <linux/bitops.h>
@@ -492,21 +494,58 @@ const struct pinctrl_ops at91_pinctrl_ops = {
.set_state = at91_pinctrl_set_state,
};
+/**
+ * at91_pinctrl_bind() - Iterates through all subnodes of the pinctrl device
+ * in the DT and binds them to U-Boot's device model. Each subnode
+ * typically represents a GPIO controller or pin configuration data.
+ *
+ * @dev: Pointer to the pinctrl device
+ *
+ * Returns 0 on success or negative error on failure
+ */
+static int at91_pinctrl_bind(struct udevice *dev)
+{
+ ofnode gpio_node;
+ struct udevice *gpio;
+ int ret;
+
+ ofnode_for_each_subnode(gpio_node, dev_ofnode(dev)) {
+ ret = lists_bind_fdt(dev, gpio_node, &gpio, NULL, false);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int at91_pinctrl_probe(struct udevice *dev)
{
struct at91_pinctrl_priv *priv = dev_get_priv(dev);
fdt_addr_t addr_base;
+ struct udevice *gpio_node;
int index;
- for (index = 0; index < MAX_GPIO_BANKS; index++) {
- addr_base = devfdt_get_addr_index(dev, index);
- if (addr_base == FDT_ADDR_T_NONE)
- break;
+ if (list_empty(&dev->child_head)) {
+ for (index = 0; index < MAX_GPIO_BANKS; index++) {
+ addr_base = devfdt_get_addr_index(dev, index);
+ if (addr_base == FDT_ADDR_T_NONE)
+ break;
- priv->reg_base[index] = (struct at91_port *)addr_base;
+ priv->reg_base[index] = (struct at91_port *)addr_base;
+ }
+ } else {
+ index = 0;
+ list_for_each_entry(gpio_node, &dev->child_head, sibling_node) {
+ addr_base = dev_read_addr(gpio_node);
+ if (addr_base == FDT_ADDR_T_NONE)
+ break;
+
+ priv->reg_base[index] = (struct at91_port *)addr_base;
+ index++;
+ }
}
- priv->nbanks = index;
+ priv->nbanks = index < MAX_GPIO_BANKS ? index : MAX_GPIO_BANKS;
return 0;
}
@@ -524,6 +563,7 @@ U_BOOT_DRIVER(atmel_sama5d3_pinctrl) = {
.id = UCLASS_PINCTRL,
.of_match = at91_pinctrl_match,
.probe = at91_pinctrl_probe,
+ .bind = at91_pinctrl_bind,
.priv_auto = sizeof(struct at91_pinctrl_priv),
.ops = &at91_pinctrl_ops,
};
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index d3eb6998551..e567cb113a3 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -7,84 +7,111 @@ config PINCTRL_QCOM
menu "Qualcomm pinctrl drivers"
config PINCTRL_QCOM_APQ8016
- bool "Qualcomm APQ8016 GCC"
+ bool "Qualcomm APQ8016 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the MSM8916 / APQ8016
Snapdragon 410 SoC, as well as the associated GPIO driver.
config PINCTRL_QCOM_APQ8096
- bool "Qualcomm APQ8096 GCC"
+ bool "Qualcomm APQ8096 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the MSM8996 / APQ8096
Snapdragon 820 SoC, as well as the associated GPIO driver.
config PINCTRL_QCOM_IPQ4019
- bool "Qualcomm IPQ4019 GCC"
+ bool "Qualcomm IPQ4019 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the IPQ4019 SoC,
as well as the associated GPIO driver.
+config PINCTRL_QCOM_IPQ9574
+ bool "Qualcomm IPQ9574 Pinctrl"
+ select PINCTRL_QCOM
+ help
+ Say Y here to enable support for pinctrl on the IPQ9574 SoC,
+ as well as the associated GPIO driver.
+
config PINCTRL_QCOM_QCM2290
- bool "Qualcomm QCM2290 GCC"
+ bool "Qualcomm QCM2290 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon QCM2290 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_QCS404
- bool "Qualcomm QCS404 GCC"
+ bool "Qualcomm QCS404 Pinctrl"
select PINCTRL_QCOM
help
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,
+
+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 GCC"
+ bool "Qualcomm SDM845 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon 845 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_SM6115
- bool "Qualcomm SM6115 GCC"
+ bool "Qualcomm SM6115 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon SM6115 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_SM8150
- bool "Qualcomm SM8150 GCC"
+ bool "Qualcomm SM8150 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon SM8150 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_SM8250
- bool "Qualcomm SM8250 GCC"
+ bool "Qualcomm SM8250 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon SM8250 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_SM8550
- bool "Qualcomm SM8550 GCC"
+ bool "Qualcomm SM8550 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon SM8550 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_SM8650
- bool "Qualcomm SM8650 GCC"
+ bool "Qualcomm SM8650 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon SM8650 SoC,
as well as the associated GPIO driver.
config PINCTRL_QCOM_X1E80100
- bool "Qualcomm X1E80100 GCC"
+ bool "Qualcomm X1E80100 Pinctrl"
select PINCTRL_QCOM
help
Say Y here to enable support for pinctrl on the Snapdragon X1E80100 SoC,
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 06d3c95f93a..6ffc6d48c15 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -5,9 +5,13 @@
obj-$(CONFIG_PINCTRL_QCOM) += pinctrl-qcom.o
obj-$(CONFIG_PINCTRL_QCOM_APQ8016) += pinctrl-apq8016.o
obj-$(CONFIG_PINCTRL_QCOM_IPQ4019) += pinctrl-ipq4019.o
+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-apq8016.c b/drivers/pinctrl/qcom/pinctrl-apq8016.c
index 0c7437822ff..9ae07d43d93 100644
--- a/drivers/pinctrl/qcom/pinctrl-apq8016.c
+++ b/drivers/pinctrl/qcom/pinctrl-apq8016.c
@@ -50,8 +50,8 @@ static const char *apq8016_get_pin_name(struct udevice *dev,
}
}
-static unsigned int apq8016_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int apq8016_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-apq8096.c b/drivers/pinctrl/qcom/pinctrl-apq8096.c
index 132ece868bf..eaa927c6e22 100644
--- a/drivers/pinctrl/qcom/pinctrl-apq8096.c
+++ b/drivers/pinctrl/qcom/pinctrl-apq8096.c
@@ -43,8 +43,8 @@ static const char *apq8096_get_pin_name(struct udevice *dev,
}
}
-static unsigned int apq8096_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int apq8096_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c
index 3215c677b26..dafcd494df4 100644
--- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c
+++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c
@@ -311,8 +311,7 @@ static const char *ipq4019_get_pin_name(struct udevice *dev,
return pin_name;
}
-static unsigned int ipq4019_get_function_mux(unsigned int pin,
- unsigned int selector)
+static int ipq4019_get_function_mux(unsigned int pin, unsigned int selector)
{
unsigned int i;
const msm_pin_function *func = ipq4019_pin_functions + pin;
diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9574.c b/drivers/pinctrl/qcom/pinctrl-ipq9574.c
new file mode 100644
index 00000000000..ce09072ae3c
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-ipq9574.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pinctrl driver for Qualcomm ipq9574
+ *
+ * (C) Copyright 2025 Linaro Ltd.
+ */
+
+#include <dm.h>
+
+#include "pinctrl-qcom.h"
+
+#define MAX_PIN_NAME_LEN 32
+static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
+
+enum ipq9574_functions {
+ msm_mux_blsp0_spi,
+ msm_mux_blsp0_uart,
+ msm_mux_blsp1_i2c,
+ msm_mux_blsp1_spi,
+ msm_mux_blsp1_uart,
+ msm_mux_blsp2_i2c,
+ msm_mux_blsp2_spi,
+ msm_mux_blsp2_uart,
+ msm_mux_blsp3_i2c,
+ msm_mux_blsp3_spi,
+ msm_mux_blsp3_uart,
+ msm_mux_blsp4_i2c,
+ msm_mux_blsp4_spi,
+ msm_mux_blsp4_uart,
+ msm_mux_blsp5_i2c,
+ msm_mux_blsp5_uart,
+ msm_mux_gpio,
+ msm_mux_mdc,
+ msm_mux_mdio,
+ msm_mux_pcie0_clk,
+ msm_mux_pcie0_wake,
+ msm_mux_pcie1_clk,
+ msm_mux_pcie1_wake,
+ msm_mux_pcie2_clk,
+ msm_mux_pcie2_wake,
+ msm_mux_pcie3_clk,
+ msm_mux_pcie3_wake,
+ msm_mux_qspi_data,
+ msm_mux_qspi_clk,
+ msm_mux_qspi_cs,
+ msm_mux_sdc_data,
+ msm_mux_sdc_clk,
+ msm_mux_sdc_cmd,
+ msm_mux_sdc_rclk,
+ msm_mux_NA,
+};
+
+#define MSM_PIN_FUNCTION(fname) \
+ [msm_mux_##fname] = {#fname, msm_mux_##fname}
+
+static const struct pinctrl_function msm_pinctrl_functions[] = {
+ MSM_PIN_FUNCTION(blsp0_spi),
+ MSM_PIN_FUNCTION(blsp0_uart),
+ MSM_PIN_FUNCTION(blsp1_i2c),
+ MSM_PIN_FUNCTION(blsp1_spi),
+ MSM_PIN_FUNCTION(blsp1_uart),
+ MSM_PIN_FUNCTION(blsp2_i2c),
+ MSM_PIN_FUNCTION(blsp2_spi),
+ MSM_PIN_FUNCTION(blsp2_uart),
+ MSM_PIN_FUNCTION(blsp3_i2c),
+ MSM_PIN_FUNCTION(blsp3_spi),
+ MSM_PIN_FUNCTION(blsp3_uart),
+ MSM_PIN_FUNCTION(blsp4_i2c),
+ MSM_PIN_FUNCTION(blsp4_spi),
+ MSM_PIN_FUNCTION(blsp4_uart),
+ MSM_PIN_FUNCTION(blsp5_i2c),
+ MSM_PIN_FUNCTION(blsp5_uart),
+ MSM_PIN_FUNCTION(gpio),
+ MSM_PIN_FUNCTION(mdc),
+ MSM_PIN_FUNCTION(mdio),
+ MSM_PIN_FUNCTION(pcie0_clk),
+ MSM_PIN_FUNCTION(pcie0_wake),
+ MSM_PIN_FUNCTION(pcie1_clk),
+ MSM_PIN_FUNCTION(pcie1_wake),
+ MSM_PIN_FUNCTION(pcie2_clk),
+ MSM_PIN_FUNCTION(pcie2_wake),
+ MSM_PIN_FUNCTION(pcie3_clk),
+ MSM_PIN_FUNCTION(pcie3_wake),
+ MSM_PIN_FUNCTION(qspi_data),
+ MSM_PIN_FUNCTION(qspi_clk),
+ MSM_PIN_FUNCTION(qspi_cs),
+ MSM_PIN_FUNCTION(sdc_data),
+ MSM_PIN_FUNCTION(sdc_clk),
+ MSM_PIN_FUNCTION(sdc_cmd),
+ MSM_PIN_FUNCTION(sdc_rclk),
+};
+
+typedef unsigned int msm_pin_function[10];
+
+#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
+ [id] = { 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, \
+ }
+
+static const msm_pin_function ipq9574_pin_functions[] = {
+ PINGROUP(0, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(1, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(2, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(3, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(4, sdc_cmd, qspi_cs, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(5, sdc_clk, qspi_clk, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(6, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(7, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(8, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(9, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(10, sdc_rclk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(11, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(12, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(13, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(14, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(15, blsp3_spi, blsp3_i2c, blsp3_uart, NA, NA, NA, NA, NA, NA),
+ PINGROUP(16, blsp3_spi, blsp3_i2c, blsp3_uart, NA, NA, NA, NA, NA, NA),
+ PINGROUP(17, blsp3_spi, blsp3_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(18, blsp3_spi, blsp3_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(19, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(20, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(21, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(22, pcie0_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(23, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(24, pcie0_wake, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(25, pcie1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(27, pcie1_wake, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(28, pcie2_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(29, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(30, pcie2_wake, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(31, pcie3_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(32, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(33, pcie3_wake, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(34, blsp2_uart, blsp2_i2c, blsp2_spi, blsp1_uart, NA, NA, NA, NA, NA),
+ PINGROUP(35, blsp2_uart, blsp2_i2c, blsp2_spi, blsp1_uart, NA, NA, NA, NA, NA),
+ PINGROUP(36, blsp1_uart, blsp1_i2c, blsp2_spi, NA, NA, NA, NA, NA, NA),
+ PINGROUP(37, blsp1_uart, blsp1_i2c, blsp2_spi, NA, NA, NA, NA, NA, NA),
+ PINGROUP(38, mdc, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(39, mdio, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(40, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(41, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(42, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(44, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(45, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(46, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(47, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(48, blsp5_i2c, blsp5_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(49, blsp5_i2c, blsp5_uart, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(50, blsp4_uart, blsp4_i2c, blsp4_spi, NA, NA, NA, NA, NA, NA),
+ PINGROUP(51, blsp4_uart, blsp4_i2c, blsp4_spi, NA, NA, NA, NA, NA, NA),
+ PINGROUP(52, blsp4_uart, blsp4_spi, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(53, blsp4_uart, blsp4_spi, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(54, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(55, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(56, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(57, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(61, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(62, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(63, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(64, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA),
+};
+
+static const char *ipq9574_get_function_name(struct udevice *dev,
+ unsigned int selector)
+{
+ return msm_pinctrl_functions[selector].name;
+}
+
+static const char *ipq9574_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector);
+ return pin_name;
+}
+
+static int ipq9574_get_function_mux(unsigned int pin, unsigned int selector)
+{
+ unsigned int i;
+ const msm_pin_function *func = ipq9574_pin_functions + pin;
+
+ for (i = 0; i < 10; i++)
+ if ((*func)[i] == selector)
+ return i;
+
+ debug("Can't find requested function for pin:selector %u:%u\n",
+ pin, selector);
+
+ return -EINVAL;
+}
+
+static const struct msm_pinctrl_data ipq9574_data = {
+ .pin_data = {
+ .pin_count = 65,
+ },
+ .functions_count = ARRAY_SIZE(msm_pinctrl_functions),
+ .get_function_name = ipq9574_get_function_name,
+ .get_function_mux = ipq9574_get_function_mux,
+ .get_pin_name = ipq9574_get_pin_name,
+};
+
+static const struct udevice_id msm_pinctrl_ids[] = {
+ { .compatible = "qcom,ipq9574-tlmm", .data = (ulong)&ipq9574_data },
+ { /* Sentinal */ }
+};
+
+U_BOOT_DRIVER(pinctrl_ipq9574) = {
+ .name = "pinctrl_ipq9574",
+ .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-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c
index af969e177d7..0c2222ce663 100644
--- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c
+++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c
@@ -38,7 +38,7 @@ static const char *qcm2290_get_pin_name(struct udevice *dev, unsigned int select
return pin_name;
}
-static unsigned int qcm2290_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
+static int qcm2290_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c
index 26a3fba194a..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;
}
@@ -92,7 +143,13 @@ static int msm_pinmux_set(struct udevice *dev, unsigned int pin_selector,
unsigned int func_selector)
{
struct msm_pinctrl_priv *priv = dev_get_priv(dev);
- u32 func = priv->data->get_function_mux(pin_selector, func_selector);
+ int func = priv->data->get_function_mux(pin_selector, func_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))
@@ -142,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);
@@ -238,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-qcom.h b/drivers/pinctrl/qcom/pinctrl-qcom.h
index 49b7bfbc001..cd167e63868 100644
--- a/drivers/pinctrl/qcom/pinctrl-qcom.h
+++ b/drivers/pinctrl/qcom/pinctrl-qcom.h
@@ -18,8 +18,7 @@ struct msm_pinctrl_data {
int functions_count;
const char *(*get_function_name)(struct udevice *dev,
unsigned int selector);
- unsigned int (*get_function_mux)(unsigned int pin,
- unsigned int selector);
+ int (*get_function_mux)(unsigned int pin, unsigned int selector);
const char *(*get_pin_name)(struct udevice *dev,
unsigned int selector);
};
diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c
index fb6defaeddf..c272595aa2a 100644
--- a/drivers/pinctrl/qcom/pinctrl-qcs404.c
+++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c
@@ -93,8 +93,8 @@ static const char *qcs404_get_pin_name(struct udevice *dev,
}
}
-static unsigned int qcs404_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int qcs404_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
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-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c
new file mode 100644
index 00000000000..fe87947fbbf
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm sc7280 pinctrl
+ *
+ * (C) Copyright 2024 Linaro Ltd.
+ *
+ */
+
+#include <dm.h>
+
+#include "pinctrl-qcom.h"
+
+#define WEST 0x00000000
+#define SOUTH 0x00400000
+#define NORTH 0x00800000
+
+#define MAX_PIN_NAME_LEN 32
+static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
+
+static const struct pinctrl_function msm_pinctrl_functions[] = {
+ { "qup05", 1 },
+ { "gpio", 0 },
+ { "pcie1_clkreqn", 3},
+};
+#define SDC_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, offset) \
+ { \
+ .name = pg_name, \
+ .ctl_reg = offset, \
+ .io_reg = offset + 0x4, \
+ .pull_bit = 3, \
+ .drv_bit = 0, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = 0, \
+ }
+
+static const struct msm_special_pin_data sc7280_special_pins_data[] = {
+ [0] = UFS_RESET("ufs_reset", SOUTH + 0xbe000),
+ [1] = SDC_PINGROUP("sdc1_rclk", 0xb3004, 0, 6),
+ [2] = SDC_PINGROUP("sdc1_clk", 0xb3000, 13, 6),
+ [3] = SDC_PINGROUP("sdc1_cmd", 0xb3000, 11, 3),
+ [4] = SDC_PINGROUP("sdc1_data", 0xb3000, 9, 0),
+ [5] = SDC_PINGROUP("sdc2_clk", 0xb4000, 14, 6),
+ [6] = SDC_PINGROUP("sdc2_cmd", 0xb4000, 11, 3),
+ [7] = SDC_PINGROUP("sdc2_data", 0xb4000, 9, 0),
+};
+
+static const char *sc7280_get_function_name(struct udevice *dev, unsigned int selector)
+{
+ return msm_pinctrl_functions[selector].name;
+}
+
+static const char *sc7280_get_pin_name(struct udevice *dev, unsigned int selector)
+{
+ if (selector >= 175 && selector <= 182)
+ snprintf(pin_name, MAX_PIN_NAME_LEN,
+ sc7280_special_pins_data[selector - 175].name);
+ else
+ snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector);
+
+ return pin_name;
+}
+
+static int sc7280_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
+{
+ return msm_pinctrl_functions[selector].val;
+}
+
+static struct msm_pinctrl_data sc7280_data = {
+ .pin_data = {
+ .pin_count = 183,
+ .special_pins_start = 175,
+ .special_pins_data = sc7280_special_pins_data,
+ },
+ .functions_count = ARRAY_SIZE(msm_pinctrl_functions),
+ .get_function_name = sc7280_get_function_name,
+ .get_function_mux = sc7280_get_function_mux,
+ .get_pin_name = sc7280_get_pin_name,
+};
+
+static const struct udevice_id msm_pinctrl_ids[] = {
+ {
+ .compatible = "qcom,sc7280-pinctrl",
+ .data = (ulong)&sc7280_data
+ },
+ { /* Sentinel */ } };
+
+U_BOOT_DRIVER(pinctrl_sc7280) = {
+ .name = "pinctrl_sc7280",
+ .id = UCLASS_NOP,
+ .of_match = msm_pinctrl_ids,
+ .ops = &msm_pinctrl_ops,
+ .bind = msm_pinctrl_bind,
+};
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/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
index f1a23f51099..3f55fc81c8e 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -80,8 +80,8 @@ static const char *sdm845_get_pin_name(struct udevice *dev,
return pin_name;
}
-static unsigned int sdm845_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int sdm845_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c
index f07f39f4ac3..7e80ea39a12 100644
--- a/drivers/pinctrl/qcom/pinctrl-sm6115.c
+++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c
@@ -167,7 +167,7 @@ static const char *sm6115_get_pin_name(struct udevice *dev, unsigned int selecto
return pin_name;
}
-static unsigned int sm6115_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
+static int sm6115_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c
index 1fb2ffb9597..fe789e8639b 100644
--- a/drivers/pinctrl/qcom/pinctrl-sm8150.c
+++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c
@@ -123,8 +123,8 @@ static const char *sm8150_get_pin_name(struct udevice *dev,
return pin_name;
}
-static unsigned int sm8150_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int sm8150_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c
index b21cdc4d24b..d5447651695 100644
--- a/drivers/pinctrl/qcom/pinctrl-sm8250.c
+++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c
@@ -99,7 +99,7 @@ static const char *sm8250_get_pin_name(struct udevice *dev, unsigned int selecto
return pin_name;
}
-static unsigned int sm8250_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
+static int sm8250_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550.c b/drivers/pinctrl/qcom/pinctrl-sm8550.c
index 25b972a6d82..f7fcad0e4aa 100644
--- a/drivers/pinctrl/qcom/pinctrl-sm8550.c
+++ b/drivers/pinctrl/qcom/pinctrl-sm8550.c
@@ -68,8 +68,8 @@ static const char *sm8550_get_pin_name(struct udevice *dev,
return pin_name;
}
-static unsigned int sm8550_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int sm8550_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c
index 9146d6abd9a..6b9d56b1058 100644
--- a/drivers/pinctrl/qcom/pinctrl-sm8650.c
+++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c
@@ -69,8 +69,8 @@ static const char *sm8650_get_pin_name(struct udevice *dev,
return pin_name;
}
-static unsigned int sm8650_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int sm8650_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c
index f39dc426d68..319667bfc16 100644
--- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c
+++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c
@@ -72,8 +72,8 @@ static const char *x1e80100_get_pin_name(struct udevice *dev,
return pin_name;
}
-static unsigned int x1e80100_get_function_mux(__maybe_unused unsigned int pin,
- unsigned int selector)
+static int x1e80100_get_function_mux(__maybe_unused unsigned int pin,
+ unsigned int selector)
{
return msm_pinctrl_functions[selector].val;
}
diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile
index c91f650b043..e17415e1ca6 100644
--- a/drivers/pinctrl/rockchip/Makefile
+++ b/drivers/pinctrl/rockchip/Makefile
@@ -14,7 +14,9 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += pinctrl-rk3308.o
obj-$(CONFIG_ROCKCHIP_RK3328) += pinctrl-rk3328.o
obj-$(CONFIG_ROCKCHIP_RK3368) += pinctrl-rk3368.o
obj-$(CONFIG_ROCKCHIP_RK3399) += pinctrl-rk3399.o
+obj-$(CONFIG_ROCKCHIP_RK3528) += pinctrl-rk3528.o
obj-$(CONFIG_ROCKCHIP_RK3568) += pinctrl-rk3568.o
+obj-$(CONFIG_ROCKCHIP_RK3576) += pinctrl-rk3576.o
obj-$(CONFIG_ROCKCHIP_RK3588) += pinctrl-rk3588.o
obj-$(CONFIG_ROCKCHIP_RV1108) += pinctrl-rv1108.o
obj-$(CONFIG_ROCKCHIP_RV1126) += pinctrl-rv1126.o
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3528.c b/drivers/pinctrl/rockchip/pinctrl-rk3528.c
new file mode 100644
index 00000000000..a3e1f0b2c9d
--- /dev/null
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3528.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pinctrl-rockchip.h"
+#include <dt-bindings/pinctrl/rockchip.h>
+
+static int rk3528_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+ int iomux_num = (pin / 8);
+ struct regmap *regmap;
+ int reg, mask;
+ u8 bit;
+ u32 data, rmask;
+
+ regmap = priv->regmap_base;
+ reg = bank->iomux[iomux_num].offset;
+ if ((pin % 8) >= 4)
+ reg += 0x4;
+ bit = (pin % 4) * 4;
+ mask = 0xf;
+
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= (mux & mask) << bit;
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3528_DRV_BITS_PER_PIN 8
+#define RK3528_DRV_PINS_PER_REG 2
+#define RK3528_DRV_GPIO0_OFFSET 0x100
+#define RK3528_DRV_GPIO1_OFFSET 0x20120
+#define RK3528_DRV_GPIO2_OFFSET 0x30160
+#define RK3528_DRV_GPIO3_OFFSET 0x20190
+#define RK3528_DRV_GPIO4_OFFSET 0x101C0
+
+static void rk3528_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+
+ *regmap = priv->regmap_base;
+
+ if (bank->bank_num == 0) {
+ *reg = RK3528_DRV_GPIO0_OFFSET;
+ } else if (bank->bank_num == 1) {
+ *reg = RK3528_DRV_GPIO1_OFFSET;
+ } else if (bank->bank_num == 2) {
+ *reg = RK3528_DRV_GPIO2_OFFSET;
+ } else if (bank->bank_num == 3) {
+ *reg = RK3528_DRV_GPIO3_OFFSET;
+ } else if (bank->bank_num == 4) {
+ *reg = RK3528_DRV_GPIO4_OFFSET;
+ } else {
+ *reg = 0;
+ debug("unsupported bank_num %d\n", bank->bank_num);
+ }
+
+ *reg += ((pin_num / RK3528_DRV_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3528_DRV_PINS_PER_REG;
+ *bit *= RK3528_DRV_BITS_PER_PIN;
+}
+
+static int rk3528_set_drive(struct rockchip_pin_bank *bank,
+ int pin_num, int strength)
+{
+ struct regmap *regmap;
+ int reg;
+ u32 data, rmask;
+ u8 bit;
+ int drv = (1 << (strength + 1)) - 1;
+
+ rk3528_calc_drv_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3528_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (drv << bit);
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3528_PULL_BITS_PER_PIN 2
+#define RK3528_PULL_PINS_PER_REG 8
+#define RK3528_PULL_GPIO0_OFFSET 0x200
+#define RK3528_PULL_GPIO1_OFFSET 0x20210
+#define RK3528_PULL_GPIO2_OFFSET 0x30220
+#define RK3528_PULL_GPIO3_OFFSET 0x20230
+#define RK3528_PULL_GPIO4_OFFSET 0x10240
+
+static void rk3528_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+
+ *regmap = priv->regmap_base;
+
+ if (bank->bank_num == 0) {
+ *reg = RK3528_PULL_GPIO0_OFFSET;
+ } else if (bank->bank_num == 1) {
+ *reg = RK3528_PULL_GPIO1_OFFSET;
+ } else if (bank->bank_num == 2) {
+ *reg = RK3528_PULL_GPIO2_OFFSET;
+ } else if (bank->bank_num == 3) {
+ *reg = RK3528_PULL_GPIO3_OFFSET;
+ } else if (bank->bank_num == 4) {
+ *reg = RK3528_PULL_GPIO4_OFFSET;
+ } else {
+ *reg = 0;
+ debug("unsupported bank_num %d\n", bank->bank_num);
+ }
+
+ *reg += ((pin_num / RK3528_PULL_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3528_PULL_PINS_PER_REG;
+ *bit *= RK3528_PULL_BITS_PER_PIN;
+}
+
+static int rk3528_set_pull(struct rockchip_pin_bank *bank,
+ int pin_num, int pull)
+{
+ struct regmap *regmap;
+ int reg, ret;
+ u8 bit, type;
+ u32 data, rmask;
+
+ if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT)
+ return -EOPNOTSUPP;
+
+ rk3528_calc_pull_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+ type = bank->pull_type[pin_num / 8];
+ ret = rockchip_translate_pull_value(type, pull);
+ if (ret < 0) {
+ debug("unsupported pull setting %d\n", pull);
+ return ret;
+ }
+
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3528_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (ret << bit);
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3528_SMT_BITS_PER_PIN 1
+#define RK3528_SMT_PINS_PER_REG 8
+#define RK3528_SMT_GPIO0_OFFSET 0x400
+#define RK3528_SMT_GPIO1_OFFSET 0x20410
+#define RK3528_SMT_GPIO2_OFFSET 0x30420
+#define RK3528_SMT_GPIO3_OFFSET 0x20430
+#define RK3528_SMT_GPIO4_OFFSET 0x10440
+
+static int rk3528_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+
+ *regmap = priv->regmap_base;
+
+ if (bank->bank_num == 0) {
+ *reg = RK3528_SMT_GPIO0_OFFSET;
+ } else if (bank->bank_num == 1) {
+ *reg = RK3528_SMT_GPIO1_OFFSET;
+ } else if (bank->bank_num == 2) {
+ *reg = RK3528_SMT_GPIO2_OFFSET;
+ } else if (bank->bank_num == 3) {
+ *reg = RK3528_SMT_GPIO3_OFFSET;
+ } else if (bank->bank_num == 4) {
+ *reg = RK3528_SMT_GPIO4_OFFSET;
+ } else {
+ *reg = 0;
+ debug("unsupported bank_num %d\n", bank->bank_num);
+ }
+
+ *reg += ((pin_num / RK3528_SMT_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3528_SMT_PINS_PER_REG;
+ *bit *= RK3528_SMT_BITS_PER_PIN;
+
+ return 0;
+}
+
+static int rk3528_set_schmitt(struct rockchip_pin_bank *bank,
+ int pin_num, int enable)
+{
+ struct regmap *regmap;
+ int reg;
+ u32 data, rmask;
+ u8 bit;
+
+ rk3528_calc_schmitt_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3528_SMT_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (enable << bit);
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+static struct rockchip_pin_bank rk3528_pin_banks[] = {
+ PIN_BANK_IOMUX_FLAGS_OFFSET(0, 32, "gpio0",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0, 0, 0, 0),
+ PIN_BANK_IOMUX_FLAGS_OFFSET(1, 32, "gpio1",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0x20020, 0x20028, 0x20030, 0x20038),
+ PIN_BANK_IOMUX_FLAGS_OFFSET(2, 32, "gpio2",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0x30040, 0, 0, 0),
+ PIN_BANK_IOMUX_FLAGS_OFFSET(3, 32, "gpio3",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0x20060, 0x20068, 0x20070, 0),
+ PIN_BANK_IOMUX_FLAGS_OFFSET(4, 32, "gpio4",
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ IOMUX_WIDTH_4BIT,
+ 0x10080, 0x10088, 0x10090, 0x10098),
+};
+
+static const struct rockchip_pin_ctrl rk3528_pin_ctrl = {
+ .pin_banks = rk3528_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3528_pin_banks),
+ .grf_mux_offset = 0x0,
+ .set_mux = rk3528_set_mux,
+ .set_pull = rk3528_set_pull,
+ .set_drive = rk3528_set_drive,
+ .set_schmitt = rk3528_set_schmitt,
+};
+
+static const struct udevice_id rk3528_pinctrl_ids[] = {
+ {
+ .compatible = "rockchip,rk3528-pinctrl",
+ .data = (ulong)&rk3528_pin_ctrl
+ },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_rk3528_pinctrl) = {
+ .name = "rockchip_rk3528_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = rk3528_pinctrl_ids,
+ .priv_auto = sizeof(struct rockchip_pinctrl_priv),
+ .ops = &rockchip_pinctrl_ops,
+#if CONFIG_IS_ENABLED(OF_REAL)
+ .bind = dm_scan_fdt_dev,
+#endif
+ .probe = rockchip_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3576.c b/drivers/pinctrl/rockchip/pinctrl-rk3576.c
new file mode 100644
index 00000000000..66e1142ac1f
--- /dev/null
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3576.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 Rockchip Electronics Co., Ltd
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pinctrl-rockchip.h"
+#include <dt-bindings/pinctrl/rockchip.h>
+
+static int rk3576_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+ int iomux_num = (pin / 8);
+ struct regmap *regmap;
+ int reg, mask;
+ u8 bit;
+ u32 data, rmask;
+
+ regmap = priv->regmap_base;
+ reg = bank->iomux[iomux_num].offset;
+ if ((pin % 8) >= 4)
+ reg += 0x4;
+ bit = (pin % 4) * 4;
+ mask = 0xf;
+
+ data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
+ data |= (mux & mask) << bit;
+
+ if (bank->bank_num == 0 && pin >= RK_PB4 && pin <= RK_PB7)
+ reg += 0x1FF4; /* GPIO0_IOC_GPIO0B_IOMUX_SEL_H */
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3576_DRV_BITS_PER_PIN 4
+#define RK3576_DRV_PINS_PER_REG 4
+#define RK3576_DRV_GPIO0_AL_OFFSET 0x10
+#define RK3576_DRV_GPIO0_BH_OFFSET 0x2014
+#define RK3576_DRV_GPIO1_OFFSET 0x6020
+#define RK3576_DRV_GPIO2_OFFSET 0x6040
+#define RK3576_DRV_GPIO3_OFFSET 0x6060
+#define RK3576_DRV_GPIO4_AL_OFFSET 0x6080
+#define RK3576_DRV_GPIO4_CL_OFFSET 0xA090
+#define RK3576_DRV_GPIO4_DL_OFFSET 0xB098
+
+static void rk3576_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+
+ *regmap = priv->regmap_base;
+ if (bank->bank_num == 0 && pin_num < 12) {
+ *reg = RK3576_DRV_GPIO0_AL_OFFSET;
+ } else if (bank->bank_num == 0) {
+ *reg = RK3576_DRV_GPIO0_BH_OFFSET - 0xc;
+ } else if (bank->bank_num == 1) {
+ *reg = RK3576_DRV_GPIO1_OFFSET;
+ } else if (bank->bank_num == 2) {
+ *reg = RK3576_DRV_GPIO2_OFFSET;
+ } else if (bank->bank_num == 3) {
+ *reg = RK3576_DRV_GPIO3_OFFSET;
+ } else if (bank->bank_num == 4 && pin_num < 16) {
+ *reg = RK3576_DRV_GPIO4_AL_OFFSET;
+ } else if (bank->bank_num == 4 && pin_num < 24) {
+ *reg = RK3576_DRV_GPIO4_CL_OFFSET - 0x10;
+ } else if (bank->bank_num == 4) {
+ *reg = RK3576_DRV_GPIO4_DL_OFFSET - 0x18;
+ } else {
+ *reg = 0;
+ debug("unsupported bank_num %d\n", bank->bank_num);
+ }
+
+ *reg += ((pin_num / RK3576_DRV_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3576_DRV_PINS_PER_REG;
+ *bit *= RK3576_DRV_BITS_PER_PIN;
+}
+
+static int rk3576_set_drive(struct rockchip_pin_bank *bank,
+ int pin_num, int strength)
+{
+ struct regmap *regmap;
+ int reg;
+ u32 data, rmask;
+ u8 bit;
+ int drv = ((strength & BIT(2)) >> 2) | ((strength & BIT(0)) << 2) | (strength & BIT(1));
+
+ rk3576_calc_drv_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3576_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (drv << bit);
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3576_PULL_BITS_PER_PIN 2
+#define RK3576_PULL_PINS_PER_REG 8
+#define RK3576_PULL_GPIO0_AL_OFFSET 0x20
+#define RK3576_PULL_GPIO0_BH_OFFSET 0x2028
+#define RK3576_PULL_GPIO1_OFFSET 0x6110
+#define RK3576_PULL_GPIO2_OFFSET 0x6120
+#define RK3576_PULL_GPIO3_OFFSET 0x6130
+#define RK3576_PULL_GPIO4_AL_OFFSET 0x6140
+#define RK3576_PULL_GPIO4_CL_OFFSET 0xA148
+#define RK3576_PULL_GPIO4_DL_OFFSET 0xB14C
+
+static void rk3576_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+
+ *regmap = priv->regmap_base;
+ if (bank->bank_num == 0 && pin_num < 12) {
+ *reg = RK3576_PULL_GPIO0_AL_OFFSET;
+ } else if (bank->bank_num == 0) {
+ *reg = RK3576_PULL_GPIO0_BH_OFFSET - 0x4;
+ } else if (bank->bank_num == 1) {
+ *reg = RK3576_PULL_GPIO1_OFFSET;
+ } else if (bank->bank_num == 2) {
+ *reg = RK3576_PULL_GPIO2_OFFSET;
+ } else if (bank->bank_num == 3) {
+ *reg = RK3576_PULL_GPIO3_OFFSET;
+ } else if (bank->bank_num == 4 && pin_num < 16) {
+ *reg = RK3576_PULL_GPIO4_AL_OFFSET;
+ } else if (bank->bank_num == 4 && pin_num < 24) {
+ *reg = RK3576_PULL_GPIO4_CL_OFFSET - 0x8;
+ } else if (bank->bank_num == 4) {
+ *reg = RK3576_PULL_GPIO4_DL_OFFSET - 0xc;
+ } else {
+ *reg = 0;
+ debug("unsupported bank_num %d\n", bank->bank_num);
+ }
+
+ *reg += ((pin_num / RK3576_PULL_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3576_PULL_PINS_PER_REG;
+ *bit *= RK3576_PULL_BITS_PER_PIN;
+}
+
+static int rk3576_set_pull(struct rockchip_pin_bank *bank,
+ int pin_num, int pull)
+{
+ struct regmap *regmap;
+ int reg, ret;
+ u8 bit, type;
+ u32 data, rmask;
+
+ if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT)
+ return -ENOTSUPP;
+
+ rk3576_calc_pull_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+ type = 1; /* FIXME: was always set to 1 in vendor kernel */
+ ret = rockchip_translate_pull_value(type, pull);
+ if (ret < 0) {
+ debug("unsupported pull setting %d\n", pull);
+ return ret;
+ }
+
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3576_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (ret << bit);
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3576_SMT_BITS_PER_PIN 1
+#define RK3576_SMT_PINS_PER_REG 8
+#define RK3576_SMT_GPIO0_AL_OFFSET 0x30
+#define RK3576_SMT_GPIO0_BH_OFFSET 0x2040
+#define RK3576_SMT_GPIO1_OFFSET 0x6210
+#define RK3576_SMT_GPIO2_OFFSET 0x6220
+#define RK3576_SMT_GPIO3_OFFSET 0x6230
+#define RK3576_SMT_GPIO4_AL_OFFSET 0x6240
+#define RK3576_SMT_GPIO4_CL_OFFSET 0xA248
+#define RK3576_SMT_GPIO4_DL_OFFSET 0xB24C
+
+static void rk3576_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num,
+ struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl_priv *priv = bank->priv;
+
+ *regmap = priv->regmap_base;
+ if (bank->bank_num == 0 && pin_num < 12) {
+ *reg = RK3576_SMT_GPIO0_AL_OFFSET;
+ } else if (bank->bank_num == 0) {
+ *reg = RK3576_SMT_GPIO0_BH_OFFSET - 0x4;
+ } else if (bank->bank_num == 1) {
+ *reg = RK3576_SMT_GPIO1_OFFSET;
+ } else if (bank->bank_num == 2) {
+ *reg = RK3576_SMT_GPIO2_OFFSET;
+ } else if (bank->bank_num == 3) {
+ *reg = RK3576_SMT_GPIO3_OFFSET;
+ } else if (bank->bank_num == 4 && pin_num < 16) {
+ *reg = RK3576_SMT_GPIO4_AL_OFFSET;
+ } else if (bank->bank_num == 4 && pin_num < 24) {
+ *reg = RK3576_SMT_GPIO4_CL_OFFSET - 0x8;
+ } else if (bank->bank_num == 4) {
+ *reg = RK3576_SMT_GPIO4_DL_OFFSET - 0xc;
+ } else {
+ *reg = 0;
+ debug("unsupported bank_num %d\n", bank->bank_num);
+ }
+
+ *reg += ((pin_num / RK3576_SMT_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3576_SMT_PINS_PER_REG;
+ *bit *= RK3576_SMT_BITS_PER_PIN;
+}
+
+static int rk3576_set_schmitt(struct rockchip_pin_bank *bank,
+ int pin_num, int enable)
+{
+ struct regmap *regmap;
+ int reg;
+ u32 data, rmask;
+ u8 bit;
+
+ rk3576_calc_schmitt_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+
+ /* enable the write to the equivalent lower bits */
+ data = ((1 << RK3576_SMT_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
+ data |= (enable << bit);
+
+ return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+static struct rockchip_pin_bank rk3576_pin_banks[] = {
+ RK3576_PIN_BANK_FLAGS(0, 32, "gpio0", IOMUX_WIDTH_4BIT,
+ 0, 0x8, 0x2004, 0x200C),
+ RK3576_PIN_BANK_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT,
+ 0x4020, 0x4028, 0x4030, 0x4038),
+ RK3576_PIN_BANK_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT,
+ 0x4040, 0x4048, 0x4050, 0x4058),
+ RK3576_PIN_BANK_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT,
+ 0x4060, 0x4068, 0x4070, 0x4078),
+ RK3576_PIN_BANK_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT,
+ 0x4080, 0x4088, 0xA390, 0xB398),
+};
+
+static const struct rockchip_pin_ctrl rk3576_pin_ctrl = {
+ .pin_banks = rk3576_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3576_pin_banks),
+ .grf_mux_offset = 0x0,
+ .set_mux = rk3576_set_mux,
+ .set_pull = rk3576_set_pull,
+ .set_drive = rk3576_set_drive,
+ .set_schmitt = rk3576_set_schmitt,
+};
+
+static const struct udevice_id rk3576_pinctrl_ids[] = {
+ {
+ .compatible = "rockchip,rk3576-pinctrl",
+ .data = (ulong)&rk3576_pin_ctrl
+ },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_rk3576) = {
+ .name = "rockchip_rk3576_pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = rk3576_pinctrl_ids,
+ .priv_auto = sizeof(struct rockchip_pinctrl_priv),
+ .ops = &rockchip_pinctrl_ops,
+#if CONFIG_IS_ENABLED(OF_REAL)
+ .bind = dm_scan_fdt_dev,
+#endif
+ .probe = rockchip_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip.h b/drivers/pinctrl/rockchip/pinctrl-rockchip.h
index df7bc684d29..5e3c9c90760 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rockchip.h
+++ b/drivers/pinctrl/rockchip/pinctrl-rockchip.h
@@ -458,6 +458,9 @@ struct rockchip_pin_bank {
#define MR_PMUGRF(ID, PIN, FUNC, REG, VAL) \
PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROUTE_TYPE_PMUGRF)
+#define RK3576_PIN_BANK_FLAGS(ID, PIN, LABEL, M, O1, O2, O3, O4) \
+ PIN_BANK_IOMUX_FLAGS_OFFSET(ID, PIN, LABEL, M, M, M, M, O1, O2, O3, O4)
+
#define RK3588_PIN_BANK_FLAGS(ID, PIN, LABEL, M, P) \
PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(ID, PIN, LABEL, M, M, M, M, P, P, P, P)
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
index cbd61795986..65e8192a99a 100644
--- a/drivers/pinctrl/sunxi/Kconfig
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -124,6 +124,16 @@ config PINCTRL_SUN50I_H616_R
default MACH_SUN50I_H616
select PINCTRL_SUNXI
+config PINCTRL_SUN50I_A100
+ bool "Support for the Allwinner A100/A133 PIO"
+ default MACH_SUN50I_A133
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_A100_R
+ bool "Support for the Allwinner A100/A133 R-PIO"
+ default MACH_SUN50I_A133
+ select PINCTRL_SUNXI
+
config PINCTRL_SUN20I_D1
bool "Support for the Allwinner D1/R528 PIO"
default MACH_SUN8I_R528
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index 37ea93715d1..c38edf7d4f5 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -774,6 +774,41 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc
.num_banks = 1,
};
+static const struct sunxi_pinctrl_function sun50i_a100_pinctrl_functions[] = {
+ { "emac0", 5 }, /* PH0-PH16 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC0-PC16 */
+ { "spi0", 4 }, /* PC2-PC4, PC7, PC12, PC15-PC16 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PB9-PB10 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_pinctrl_desc = {
+ .functions = sun50i_a100_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_a100_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun50i_a100_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c0", 2 },
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_r_pinctrl_desc = {
+ .functions = sun50i_a100_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_a100_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 1,
+};
+
static const struct udevice_id sunxi_pinctrl_ids[] = {
#ifdef CONFIG_PINCTRL_SUNIV_F1C100S
{
@@ -937,6 +972,18 @@ static const struct udevice_id sunxi_pinctrl_ids[] = {
.data = (ulong)&sun50i_h616_r_pinctrl_desc,
},
#endif
+#ifdef CONFIG_PINCTRL_SUN50I_A100
+ {
+ .compatible = "allwinner,sun50i-a100-pinctrl",
+ .data = (ulong)&sun50i_a100_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_A100_R
+ {
+ .compatible = "allwinner,sun50i-a100-r-pinctrl",
+ .data = (ulong)&sun50i_a100_r_pinctrl_desc,
+ },
+#endif
{}
};
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 ad7112a05e6..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);
@@ -23,18 +23,18 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt)
return;
}
- drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", 0);
- drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", 0);
- drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", 0);
- drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", 0);
+ drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", PMUX_SLWF_NONE);
+ drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", PMUX_SLWR_NONE);
+ drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", PMUX_DRVUP_NONE);
+ drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", PMUX_DRVDN_NONE);
#ifdef TEGRA_PMX_GRPS_HAVE_LPMD
- drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", 0);
+ drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", PMUX_LPMD_NONE);
#endif
#ifdef TEGRA_PMX_GRPS_HAVE_SCHMT
- drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0);
+ drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE);
#endif
#ifdef TEGRA_PMX_GRPS_HAVE_HSM
- drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0);
+ drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE);
#endif
for (i = 1; i < drvcnt; i++)
@@ -46,31 +46,110 @@ 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:
kfree(drive_group);
}
+#ifdef TEGRA_PMX_SOC_HAS_MIPI_PAD_CTRL_GRPS
+static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt)
+{
+ struct pmux_mipipadctrlgrp_config *mipipad_group;
+ int i, ret, pad_id, count = 0;
+ const char *function;
+ const char **pads;
+
+ mipipad_group = kmalloc_array(padcnt, sizeof(*mipipad_group), GFP_KERNEL);
+ if (!mipipad_group) {
+ log_debug("%s: cannot allocate mipi pad group array\n", __func__);
+ return;
+ }
+
+ /* decode function id and fill the first copy of pmux_mipipadctrlgrp_config */
+ function = dev_read_string(config, "nvidia,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;
+
+ 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++)
+ memcpy(&mipipad_group[i], &mipipad_group[0], sizeof(mipipad_group[0]));
+
+ ret = dev_read_string_list(config, "nvidia,pins", &pads);
+ if (ret < 0) {
+ log_debug("%s: could not parse property nvidia,pins\n", __func__);
+ 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[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, count);
+
+ free(pads);
+exit:
+ kfree(mipipad_group);
+}
+#else
+static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) { }
+#endif
+
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;
@@ -88,33 +167,38 @@ 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", 0);
- pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", 0);
+ pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", PMUX_PULL_NORMAL);
+ pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", PMUX_TRI_TRISTATE);
#ifdef TEGRA_PMX_PINS_HAVE_E_INPUT
- pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", 0);
+ pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", PMUX_PIN_NONE);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_LOCK
- pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", 0);
+ pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", PMUX_PIN_LOCK_DEFAULT);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_OD
- pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", 0);
+ pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", PMUX_PIN_OD_DEFAULT);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_IO_RESET
- pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", 0);
+ pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", PMUX_PIN_IO_RESET_DEFAULT);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_RCV_SEL
- pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", 0);
+ pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", PMUX_PIN_RCV_SEL_DEFAULT);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_E_IO_HV
- pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", 0);
+ pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", PMUX_PIN_E_IO_HV_DEFAULT);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_SCHMT
- pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0);
+ pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE);
#endif
#ifdef TEGRA_PMX_PINS_HAVE_HSM
- pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0);
+ pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE);
#endif
for (i = 1; i < pincnt; i++)
@@ -126,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:
@@ -170,6 +265,9 @@ static int tegra_pinctrl_set_state(struct udevice *dev, struct udevice *config)
if (!strncmp(name, "drive_", 6))
/* Drive node is detected */
tegra_pinctrl_set_drive(child, ret);
+ else if (!strncmp(name, "mipi_pad_ctrl_", 14))
+ /* Handle T124 specific pinconfig */
+ tegra_pinctrl_set_mipipad(child, ret);
else
/* Pin node is detected */
tegra_pinctrl_set_pin(child, ret);
@@ -236,6 +334,7 @@ static int tegra_pinctrl_bind(struct udevice *dev)
static const struct udevice_id tegra_pinctrl_ids[] = {
{ .compatible = "nvidia,tegra30-pinmux" },
{ .compatible = "nvidia,tegra114-pinmux" },
+ { .compatible = "nvidia,tegra124-pinmux" },
{ },
};
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/Kconfig b/drivers/power/Kconfig
index 4b81aeb7497..eed65058e66 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -116,6 +116,12 @@ config AXP717_POWER
---help---
Select this to enable support for the AXP717 PMIC found on some boards.
+config AXP803_POWER
+ bool "AXP803 PMIC support"
+ select AXP_PMIC_BUS
+ ---help---
+ Select this to enable support for the AXP803 PMIC found on some boards.
+
config AXP809_POWER
bool "axp809 pmic support"
depends on MACH_SUN9I
@@ -142,10 +148,20 @@ config SY8106A_POWER
endchoice
+config AXP_I2C_ADDRESS
+ hex "AXP PMIC I2C address"
+ depends on ARCH_SUNXI && !SUNXI_NO_PMIC
+ default 0x36 if AXP305_POWER
+ default 0x36 if AXP313_POWER
+ default 0x30 if AXP152_POWER
+ default 0x34
+ ---help---
+ I2C address of the AXP PMIC, used for the SPL only.
+
config AXP_DCDC1_VOLT
int "axp pmic dcdc1 voltage"
- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
- default 3300 if AXP818_POWER || MACH_SUN8I_R40
+ depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER
+ default 3300 if AXP818_POWER || MACH_SUN8I_R40 || AXP803_POWER
default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I
---help---
Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to
@@ -158,11 +174,12 @@ config AXP_DCDC1_VOLT
config AXP_DCDC2_VOLT
int "axp pmic dcdc2 voltage"
- depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER
+ depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER || AXP803_POWER
default 900 if AXP818_POWER
default 1400 if AXP152_POWER || AXP209_POWER
default 1000 if AXP313_POWER
default 1000 if AXP717_POWER
+ default 1000 if AXP803_POWER
default 1200 if MACH_SUN6I
default 1100 if MACH_SUN8I
default 0 if MACH_SUN9I
@@ -219,7 +236,7 @@ config AXP_DCDC4_VOLT
config AXP_DCDC5_VOLT
int "axp pmic dcdc5 voltage"
- depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
+ depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER
default 1500 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I
---help---
Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3f4d56f5139..3363191fdc8 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_AXP313_POWER) += axp_spl.o
obj-$(CONFIG_AXP717_POWER) += axp_spl.o
obj-$(CONFIG_AXP809_POWER) += axp809.o
obj-$(CONFIG_AXP818_POWER) += axp818.o
+obj-$(CONFIG_AXP803_POWER) += axp_spl.o
endif
obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o
obj-$(CONFIG_SY8106A_POWER) += sy8106a.o
diff --git a/drivers/power/axp_spl.c b/drivers/power/axp_spl.c
index 3c86eb20ab4..7c51a9b3dfb 100644
--- a/drivers/power/axp_spl.c
+++ b/drivers/power/axp_spl.c
@@ -36,6 +36,23 @@ static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
#define AXP_SHUTDOWN_REG 0x27
#define AXP_SHUTDOWN_MASK BIT(0)
+#elif defined(CONFIG_AXP803_POWER) /* AXP803 */
+
+static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
+ { 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
+ { 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 },
+ { 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 },
+ { 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 },
+ { 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 },
+ { 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 },
+};
+
+#define AXP_CHIP_VERSION 0x3
+#define AXP_CHIP_VERSION_MASK 0xcf
+#define AXP_CHIP_ID 0x41
+#define AXP_SHUTDOWN_REG 0x32
+#define AXP_SHUTDOWN_MASK BIT(7)
+
#elif defined(CONFIG_AXP313_POWER) /* AXP313 */
static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = {
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/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c
index 5e7a4c5648d..c3519307340 100644
--- a/drivers/power/domain/ti-power-domain.c
+++ b/drivers/power/domain/ti-power-domain.c
@@ -94,6 +94,8 @@ static const struct soc_attr ti_k3_soc_pd_data[] = {
.family = "J721E",
.data = &j721e_pd_platdata,
},
+#endif
+#if IS_ENABLED(CONFIG_SOC_K3_J7200)
{
.family = "J7200",
.data = &j7200_pd_platdata,
@@ -116,6 +118,10 @@ static const struct soc_attr ti_k3_soc_pd_data[] = {
.family = "J784S4",
.data = &j784s4_pd_platdata,
},
+ {
+ .family = "J742S2",
+ .data = &j784s4_pd_platdata,
+ },
#endif
{ /* sentinel */ }
};
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/axp.c b/drivers/power/pmic/axp.c
index 521a39dd566..c300fd2bbc2 100644
--- a/drivers/power/pmic/axp.c
+++ b/drivers/power/pmic/axp.c
@@ -51,6 +51,7 @@ static const struct pmic_child_info axp_pmic_child_info[] = {
{ "cldo", "axp_regulator" },
{ "dc", "axp_regulator" },
{ "dldo", "axp_regulator" },
+ { "drivevbus", "axp_drivevbus" },
{ "eldo", "axp_regulator" },
{ "fldo", "axp_regulator" },
{ "ldo", "axp_regulator" },
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 958f337c7e7..95912ef5633 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -57,6 +57,13 @@ config SPL_REGULATOR_AXP
Enable support in SPL for the regulators (DCDCs, LDOs) in the
X-Powers AXP152, AXP2xx, and AXP8xx PMICs.
+config REGULATOR_AXP_DRIVEVBUS
+ bool "Enable driver for X-Powers AXP PMIC drivevbus"
+ depends on DM_REGULATOR && PMIC_AXP
+ help
+ Enable support for sensing or driving the USB VBUS on
+ X-Powers AXP2xx and AXP8xx PMICs.
+
config REGULATOR_AXP_USB_POWER
bool "Enable driver for X-Powers AXP PMIC USB power supply"
depends on DM_REGULATOR && PMIC_AXP
@@ -224,6 +231,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
@@ -478,3 +492,20 @@ config DM_REGULATOR_TPS65219
features for REGULATOR TPS65219 and the family of TPS65219 PMICs.
TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs.
The driver implements get/set api for value and enable.
+
+config REGULATOR_RZG2L_USBPHY
+ bool "Enable driver for RZ/G2L USB PHY VBUS supply"
+ depends on DM_REGULATOR
+ help
+ 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 ca6c89d13b5..0ee5d908a2a 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -4,41 +4,45 @@
# 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_DRIVEVBUS) += axp_drivevbus.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/axp_drivevbus.c b/drivers/power/regulator/axp_drivevbus.c
new file mode 100644
index 00000000000..c463de8786b
--- /dev/null
+++ b/drivers/power/regulator/axp_drivevbus.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+#define AXP_VBUS_IPSOUT 0x30
+#define AXP_VBUS_IPSOUT_DRIVEBUS BIT(2)
+#define AXP_MISC_CTRL 0x8f
+#define AXP_MISC_CTRL_N_VBUSEN_FUNC BIT(4)
+
+static int axp_drivevbus_get_enable(struct udevice *dev)
+{
+ int ret;
+
+ ret = pmic_reg_read(dev->parent, AXP_VBUS_IPSOUT);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & AXP_VBUS_IPSOUT_DRIVEBUS);
+}
+
+static int axp_drivevbus_set_enable(struct udevice *dev, bool enable)
+{
+ return pmic_clrsetbits(dev->parent, AXP_VBUS_IPSOUT,
+ AXP_VBUS_IPSOUT_DRIVEBUS,
+ enable ? AXP_VBUS_IPSOUT_DRIVEBUS : 0);
+}
+
+static const struct dm_regulator_ops axp_drivevbus_ops = {
+ .get_enable = axp_drivevbus_get_enable,
+ .set_enable = axp_drivevbus_set_enable,
+};
+
+static int axp_drivevbus_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_plat = dev_get_uclass_plat(dev);
+ int ret;
+
+ uc_plat->type = REGULATOR_TYPE_FIXED;
+
+ if (dev_read_bool(dev->parent, "x-powers,drive-vbus-en")) {
+ ret = pmic_clrsetbits(dev->parent, AXP_MISC_CTRL,
+ AXP_MISC_CTRL_N_VBUSEN_FUNC, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(axp_drivevbus) = {
+ .name = "axp_drivevbus",
+ .id = UCLASS_REGULATOR,
+ .probe = axp_drivevbus_probe,
+ .ops = &axp_drivevbus_ops,
+};
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/power/regulator/rzg2l-usbphy-regulator.c b/drivers/power/regulator/rzg2l-usbphy-regulator.c
new file mode 100644
index 00000000000..451f04c140e
--- /dev/null
+++ b/drivers/power/regulator/rzg2l-usbphy-regulator.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Renesas Electronics Corporation
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <power/regulator.h>
+#include <renesas/rzg2l-usbphy.h>
+
+#define VBENCTL 0x03c
+#define VBENCTL_VBUS_SEL BIT(0)
+
+static int rzg2l_usbphy_regulator_set_enable(struct udevice *dev, bool enable)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent);
+
+ if (enable)
+ clrbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL);
+ else
+ setbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL);
+
+ return 0;
+}
+
+static int rzg2l_usbphy_regulator_get_enable(struct udevice *dev)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent);
+
+ return !!readl(priv->regs + VBENCTL) & VBENCTL_VBUS_SEL;
+}
+
+static const struct dm_regulator_ops rzg2l_usbphy_regulator_ops = {
+ .get_enable = rzg2l_usbphy_regulator_get_enable,
+ .set_enable = rzg2l_usbphy_regulator_set_enable,
+};
+
+U_BOOT_DRIVER(rzg2l_usbphy_regulator) = {
+ .name = "rzg2l_usbphy_regulator",
+ .id = UCLASS_REGULATOR,
+ .ops = &rzg2l_usbphy_regulator_ops,
+};
diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c
index 99f6506f162..79db1a6a8aa 100644
--- a/drivers/power/regulator/scmi_regulator.c
+++ b/drivers/power/regulator/scmi_regulator.c
@@ -175,12 +175,19 @@ U_BOOT_DRIVER(scmi_regulator) = {
static int scmi_regulator_bind(struct udevice *dev)
{
struct driver *drv;
+ ofnode regul_node;
ofnode node;
int ret;
+ regul_node = ofnode_find_subnode(dev_ofnode(dev), "regulators");
+ if (!ofnode_valid(regul_node)) {
+ dev_err(dev, "no regulators node\n");
+ return -ENXIO;
+ }
+
drv = DM_DRIVER_GET(scmi_regulator);
- ofnode_for_each_subnode(node, dev_ofnode(dev)) {
+ ofnode_for_each_subnode(node, regul_node) {
ret = device_bind(dev, drv, ofnode_get_name(node),
NULL, node, NULL);
if (ret)
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6e79868d0ef..de312656746 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -105,6 +105,14 @@ config PWM_TEGRA
32KHz clock is supported by the driver but the duty cycle is
configurable.
+config PWM_STM32
+ bool "Enable support for STM32 PWM"
+ depends on DM_PWM && MFD_STM32_TIMERS
+ help
+ This enables PWM driver for STMicroelectronics STM32 pulse width
+ modulation. It uses STM32 timer devices that can have up to 4 output
+ channels, with complementary outputs and configurable polarity.
+
config PWM_SUNXI
bool "Enable support for the Allwinner Sunxi PWM"
depends on DM_PWM
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index e4d10c8dc3e..76305b93bc9 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -22,5 +22,6 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o
obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
+obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o
obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o
diff --git a/drivers/pwm/pwm-mtk.c b/drivers/pwm/pwm-mtk.c
index 5cf2eba2ba0..898e353b370 100644
--- a/drivers/pwm/pwm-mtk.c
+++ b/drivers/pwm/pwm-mtk.c
@@ -30,6 +30,7 @@
enum mtk_pwm_reg_ver {
PWM_REG_V1,
PWM_REG_V2,
+ PWM_REG_V3,
};
static const unsigned int mtk_pwm_reg_offset_v1[] = {
@@ -40,6 +41,10 @@ static const unsigned int mtk_pwm_reg_offset_v2[] = {
0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x01c0, 0x0200, 0x0240
};
+static const unsigned int mtk_pwm_reg_offset_v3[] = {
+ 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x600, 0x700, 0x0800
+};
+
struct mtk_pwm_soc {
unsigned int num_pwms;
bool pwm45_fixup;
@@ -60,6 +65,10 @@ static void mtk_pwm_w32(struct udevice *dev, uint channel, uint reg, uint val)
u32 offset;
switch (priv->soc->reg_ver) {
+ case PWM_REG_V3:
+ offset = mtk_pwm_reg_offset_v3[channel];
+ break;
+
case PWM_REG_V2:
offset = mtk_pwm_reg_offset_v2[channel];
break;
@@ -203,6 +212,12 @@ static const struct mtk_pwm_soc mt7986_data = {
.reg_ver = PWM_REG_V1,
};
+static const struct mtk_pwm_soc mt7987_data = {
+ .num_pwms = 3,
+ .pwm45_fixup = false,
+ .reg_ver = PWM_REG_V3,
+};
+
static const struct mtk_pwm_soc mt7988_data = {
.num_pwms = 8,
.pwm45_fixup = false,
@@ -215,6 +230,7 @@ static const struct udevice_id mtk_pwm_ids[] = {
{ .compatible = "mediatek,mt7629-pwm", .data = (ulong)&mt7629_data },
{ .compatible = "mediatek,mt7981-pwm", .data = (ulong)&mt7981_data },
{ .compatible = "mediatek,mt7986-pwm", .data = (ulong)&mt7986_data },
+ { .compatible = "mediatek,mt7987-pwm", .data = (ulong)&mt7987_data },
{ .compatible = "mediatek,mt7988-pwm", .data = (ulong)&mt7988_data },
{ }
};
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
new file mode 100644
index 00000000000..5fa649b5903
--- /dev/null
+++ b/drivers/pwm/pwm-stm32.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025, STMicroelectronics - All Rights Reserved
+ * Author: Cheick Traore <cheick.traore@foss.st.com>
+ *
+ * Originally based on the Linux kernel v6.10 drivers/pwm/pwm-stm32.c.
+ */
+
+#include <div64.h>
+#include <dm.h>
+#include <pwm.h>
+#include <asm/io.h>
+#include <asm/arch/timers.h>
+#include <dm/device_compat.h>
+#include <linux/time.h>
+
+#define CCMR_CHANNEL_SHIFT 8
+#define CCMR_CHANNEL_MASK 0xFF
+
+struct stm32_pwm_priv {
+ bool have_complementary_output;
+ bool invert_polarity;
+};
+
+static u32 active_channels(struct stm32_timers_plat *plat)
+{
+ return readl(plat->base + TIM_CCER) & TIM_CCER_CCXE;
+}
+
+static int stm32_pwm_set_config(struct udevice *dev, uint channel,
+ uint period_ns, uint duty_ns)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_timers_priv *priv = dev_get_priv(dev_get_parent(dev));
+ unsigned long long prd, div, dty;
+ unsigned int prescaler = 0;
+ u32 ccmr, mask, shift;
+
+ if (duty_ns > period_ns)
+ return -EINVAL;
+
+ /*
+ * Period and prescaler values depends on clock rate
+ * First we need to find the minimal value for prescaler such that
+ *
+ * period_ns * clkrate
+ * ------------------------------ < max_arr + 1
+ * NSEC_PER_SEC * (prescaler + 1)
+ *
+ * This equation is equivalent to
+ *
+ * period_ns * clkrate
+ * ---------------------------- < prescaler + 1
+ * NSEC_PER_SEC * (max_arr + 1)
+ *
+ * Using integer division and knowing that the right hand side is
+ * integer, this is further equivalent to
+ *
+ * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler
+ */
+
+ div = (unsigned long long)priv->rate * period_ns;
+ do_div(div, NSEC_PER_SEC);
+ prd = div;
+
+ do_div(div, priv->max_arr + 1);
+ prescaler = div;
+ if (prescaler > MAX_TIM_PSC)
+ return -EINVAL;
+
+ do_div(prd, prescaler + 1);
+ if (!prd)
+ return -EINVAL;
+
+ /*
+ * All channels share the same prescaler and counter so when two
+ * channels are active at the same time we can't change them
+ */
+ if (active_channels(plat) & ~(1 << channel * 4)) {
+ u32 psc, arr;
+
+ psc = readl(plat->base + TIM_PSC);
+ arr = readl(plat->base + TIM_ARR);
+ if (psc != prescaler || arr != prd - 1)
+ return -EBUSY;
+ }
+
+ writel(prescaler, plat->base + TIM_PSC);
+ writel(prd - 1, plat->base + TIM_ARR);
+ setbits_le32(plat->base + TIM_CR1, TIM_CR1_ARPE);
+
+ /* Calculate the duty cycles */
+ dty = prd * duty_ns;
+ do_div(dty, period_ns);
+
+ writel(dty, plat->base + TIM_CCRx(channel + 1));
+
+ /* Configure output mode */
+ shift = (channel & 0x1) * CCMR_CHANNEL_SHIFT;
+ ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift;
+ mask = CCMR_CHANNEL_MASK << shift;
+ if (channel < 2)
+ clrsetbits_le32(plat->base + TIM_CCMR1, mask, ccmr);
+ else
+ clrsetbits_le32(plat->base + TIM_CCMR2, mask, ccmr);
+
+ setbits_le32(plat->base + TIM_BDTR, TIM_BDTR_MOE);
+
+ return 0;
+}
+
+static int stm32_pwm_set_enable(struct udevice *dev, uint channel,
+ bool enable)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_pwm_priv *priv = dev_get_priv(dev);
+ u32 mask;
+
+ /* Enable channel */
+ mask = TIM_CCER_CC1E << (channel * 4);
+ if (priv->have_complementary_output)
+ mask |= TIM_CCER_CC1NE << (channel * 4);
+
+ if (enable) {
+ setbits_le32(plat->base + TIM_CCER, mask);
+ /* Make sure that registers are updated */
+ setbits_le32(plat->base + TIM_EGR, TIM_EGR_UG);
+ /* Enable controller */
+ setbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN);
+ } else {
+ clrbits_le32(plat->base + TIM_CCER, mask);
+ /* When all channels are disabled, we can disable the controller */
+ if (!active_channels(plat))
+ clrbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN);
+ }
+
+ return 0;
+}
+
+static int stm32_pwm_set_invert(struct udevice *dev, uint channel,
+ bool polarity)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_pwm_priv *priv = dev_get_priv(dev);
+ u32 mask;
+
+ mask = TIM_CCER_CC1P << (channel * 4);
+ if (priv->have_complementary_output)
+ mask |= TIM_CCER_CC1NP << (channel * 4);
+
+ clrsetbits_le32(plat->base + TIM_CCER, mask, polarity ? mask : 0);
+
+ return 0;
+}
+
+static void stm32_pwm_detect_complementary(struct udevice *dev)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_pwm_priv *priv = dev_get_priv(dev);
+ u32 ccer;
+
+ /*
+ * If complementary bit doesn't exist writing 1 will have no
+ * effect so we can detect it.
+ */
+ setbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE);
+ ccer = readl(plat->base + TIM_CCER);
+ clrbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE);
+
+ priv->have_complementary_output = (ccer != 0);
+}
+
+static int stm32_pwm_probe(struct udevice *dev)
+{
+ struct stm32_timers_priv *timer = dev_get_priv(dev_get_parent(dev));
+
+ if (timer->rate > 1000000000) {
+ dev_err(dev, "Clock freq too high (%lu)\n", timer->rate);
+ return -EINVAL;
+ }
+
+ stm32_pwm_detect_complementary(dev);
+
+ return 0;
+}
+
+static const struct pwm_ops stm32_pwm_ops = {
+ .set_config = stm32_pwm_set_config,
+ .set_enable = stm32_pwm_set_enable,
+ .set_invert = stm32_pwm_set_invert,
+};
+
+static const struct udevice_id stm32_pwm_ids[] = {
+ { .compatible = "st,stm32-pwm" },
+ { }
+};
+
+U_BOOT_DRIVER(stm32_pwm) = {
+ .name = "stm32_pwm",
+ .id = UCLASS_PWM,
+ .of_match = stm32_pwm_ids,
+ .ops = &stm32_pwm_ops,
+ .probe = stm32_pwm_probe,
+ .priv_auto = sizeof(struct stm32_pwm_priv),
+};
diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index 899d7585489..2a40b0c9f81 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -71,7 +71,7 @@ choice
depends on K3_DDRSS
prompt "K3 DDRSS Arch Support"
- default K3_J721E_DDRSS if SOC_K3_J721E || SOC_K3_J721S2 || SOC_K3_J784S4
+ default K3_J721E_DDRSS if SOC_K3_J721E || SOC_K3_J7200 || SOC_K3_J721S2 || SOC_K3_J784S4
default K3_AM64_DDRSS if SOC_K3_AM642
default K3_AM64_DDRSS if SOC_K3_AM625
default K3_AM62A_DDRSS if SOC_K3_AM62A7 || SOC_K3_AM62P5 || SOC_K3_J722S
diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c
index 6f78afb0ab5..ca8a7fb4783 100644
--- a/drivers/ram/renesas/dbsc5/dram.c
+++ b/drivers/ram/renesas/dbsc5/dram.c
@@ -1325,6 +1325,11 @@ static const u32 PI_DARRAY3_1_CSx_Fx[CS_CNT][3] = {
#define DBSC_DBACEN 0x200
#define DBSC_DBRFEN 0x204
#define DBSC_DBCMD 0x208
+#define DBSC_DBCMD_CMD_OPCODE_PD 0x8
+#define DBSC_DBCMD_CMD_OPCODE_MRW 0xe
+#define DBSC_DBCMD_CMD_OPCODE_MRR 0xf
+#define DBSC_DBCMD_CMD_CHANNEL_ALL 0x8
+#define DBSC_DBCMD_CMD_RANK_ALL 0x4
#define DBSC_DBWAIT 0x210
#define DBSC_DBBL 0x400
#define DBSC_DBBLA 0x400
@@ -1736,24 +1741,20 @@ static void dbsc5_reg_write(void __iomem *addr, u32 data)
}
/**
- * dbsc5_reg_write() - DRAM Command Write Access
+ * dbsc5_wait_dbwait() - DRAM Command Wait Access Completion
* @dev: DBSC5 device
- * @cmd DRAM command.
*
- * First, execute the dummy read to DBSC_DBCMD.
- * Confirm that no DBSC command operation is in progress 0.
- * Write the contents of the command to be sent to DRAM.
+ * Wait for DRAM access completion. This is used before sending a command
+ * to the DRAM to assure no other command is in flight already, or while
+ * waiting for MRR command to complete.
*/
-static void dbsc5_send_dbcmd2(struct udevice *dev, u32 cmd)
+static void dbsc5_wait_dbwait(struct udevice *dev)
{
struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev);
void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET;
u32 val;
int ret;
- /* dummy read */
- readl(regs_dbsc_d + DBSC_DBCMD);
-
ret = readl_poll_timeout(regs_dbsc_d + DBSC_DBWAIT, val, ((val & BIT(0)) == 0), 1000000);
if (ret < 0) {
printf("%s DBWAIT bit 0 timeout\n", __func__);
@@ -1765,6 +1766,32 @@ static void dbsc5_send_dbcmd2(struct udevice *dev, u32 cmd)
printf("%s DBWAIT + 0x4000 bit 0 timeout\n", __func__);
hang();
}
+}
+
+/**
+ * dbsc5_send_dbcmd2() - DRAM Command Write Access
+ * @dev: DBSC5 device
+ * @opcode DRAM controller opcode
+ * @channel DRAM controller channel (0..3)
+ * @rank DRAM controller rank (0..1)
+ * @arg Command and argument bits (command specific encoding)
+ *
+ * First, execute the dummy read to DBSC_DBCMD.
+ * Confirm that no DBSC command operation is in progress 0.
+ * Write the contents of the command to be sent to DRAM.
+ */
+static void dbsc5_send_dbcmd2(struct udevice *dev, const u8 opcode,
+ const u8 channel, const u8 rank,
+ const u16 arg)
+{
+ const u32 cmd = (opcode << 24) | (channel << 20) | (rank << 16) | arg;
+ struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev);
+ void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET;
+
+ /* dummy read */
+ readl(regs_dbsc_d + DBSC_DBCMD);
+
+ dbsc5_wait_dbwait(dev);
dbsc5_reg_write(regs_dbsc_d + DBSC_DBCMD, cmd);
}
@@ -2950,10 +2977,14 @@ static u32 dbsc5_pi_training(struct udevice *dev)
writel(0x21, regs_dbsc_d + DBSC_DBDFICNT(ch));
/* Dummy PDE */
- dbsc5_send_dbcmd2(dev, 0x8840000);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_PD,
+ DBSC_DBCMD_CMD_CHANNEL_ALL,
+ DBSC_DBCMD_CMD_RANK_ALL, 0);
/* PDX */
- dbsc5_send_dbcmd2(dev, 0x8840001);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_PD,
+ DBSC_DBCMD_CMD_CHANNEL_ALL,
+ DBSC_DBCMD_CMD_RANK_ALL, 1);
/* Wait init_complete */
for (retry = 0; retry < retry_max; retry++) {
@@ -4085,25 +4116,27 @@ static u32 dbsc5_read_training(struct udevice *dev)
}
/**
- * dbsc5_ddr_register_set() - DDR mode register setting
+ * dbsc5_ddr_register_mr28_set() - DDR mode register MR28 set
* @dev: DBSC5 device
*
* Set the mode register 28 of the SDRAM.
* ZQ Mode: Command-Based ZQ Calibration
* ZQ interval: Background Cal Interval < 64ms
*/
-static void dbsc5_ddr_register_set(struct udevice *dev)
+static void dbsc5_ddr_register_mr28_set(struct udevice *dev)
{
- dbsc5_send_dbcmd2(dev, 0xE841C24);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRW,
+ DBSC_DBCMD_CMD_CHANNEL_ALL,
+ DBSC_DBCMD_CMD_RANK_ALL, (28 << 8) | 0x24);
}
/**
- * dbsc5_ddr_register_read() - DDR mode register read
+ * dbsc5_ddr_register_mr27_mr57_read() - DDR mode register MR27/MR57 read
* @dev: DBSC5 device
*
* Set the mode register 27 and 57 of the SDRAM.
*/
-static void dbsc5_ddr_register_read(struct udevice *dev)
+static void dbsc5_ddr_register_mr27_mr57_read(struct udevice *dev)
{
struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev);
@@ -4111,17 +4144,21 @@ static void dbsc5_ddr_register_read(struct udevice *dev)
return;
/* MR27 rank0 */
- dbsc5_send_dbcmd2(dev, 0xF801B00);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR,
+ DBSC_DBCMD_CMD_CHANNEL_ALL, 0, 27 << 8);
/* MR57 rank0 */
- dbsc5_send_dbcmd2(dev, 0xF803900);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR,
+ DBSC_DBCMD_CMD_CHANNEL_ALL, 0, 57 << 8);
if (!priv->ch_have_this_cs[1])
return;
/* MR27 rank1 */
- dbsc5_send_dbcmd2(dev, 0xF811B00);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR,
+ DBSC_DBCMD_CMD_CHANNEL_ALL, 1, 27 << 8);
/* MR57 rank1 */
- dbsc5_send_dbcmd2(dev, 0xF813900);
+ dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR,
+ DBSC_DBCMD_CMD_CHANNEL_ALL, 1, 57 << 8);
}
/**
@@ -4200,8 +4237,8 @@ static u32 dbsc5_init_ddr(struct udevice *dev)
dbsc5_dbsc_regset(dev);
/* Frequency selection change (F1->F2) */
- dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_INDEX, 0x1);
- dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_MULTICAST_EN, 0x0);
+ dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_INDEX, 0x1);
+ dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_MULTICAST_EN, 0x0);
/* dfi_init_start (start ddrphy) & execute pi_training */
phytrainingok = dbsc5_pi_training(dev);
@@ -4278,10 +4315,10 @@ static u32 dbsc5_init_ddr(struct udevice *dev)
/* setup DDR mode registers */
/* MRS */
- dbsc5_ddr_register_set(dev);
+ dbsc5_ddr_register_mr28_set(dev);
/* MRR */
- dbsc5_ddr_register_read(dev);
+ dbsc5_ddr_register_mr27_mr57_read(dev);
/* training complete, setup DBSC */
dbsc5_dbsc_regset_post(dev);
@@ -4296,10 +4333,13 @@ static u32 dbsc5_init_ddr(struct udevice *dev)
/**
* dbsc5_get_board_data() - Obtain board specific DRAM configuration
+ * @dev: DBSC5 device
+ * @modemr0: MODEMR0 register content
*
* Return board specific DRAM configuration structure pointer.
*/
-__weak const struct renesas_dbsc5_board_config *dbsc5_get_board_data(void)
+__weak const struct renesas_dbsc5_board_config *
+dbsc5_get_board_data(struct udevice *dev, const u32 modemr0)
{
return &renesas_v4h_dbsc5_board_config;
}
@@ -4335,7 +4375,7 @@ static int renesas_dbsc5_dram_probe(struct udevice *dev)
u32 ch, cs;
/* Get board data */
- priv->dbsc5_board_config = dbsc5_get_board_data();
+ priv->dbsc5_board_config = dbsc5_get_board_data(dev, modemr0);
priv->ddr_phyvalid = (u32)(priv->dbsc5_board_config->bdcfg_phyvalid);
priv->max_density = 0;
priv->cpg_regs = (void __iomem *)ofnode_get_addr(cnode);
diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile
index 36dc0500dab..fd94aad0cd4 100644
--- a/drivers/ram/rockchip/Makefile
+++ b/drivers/ram/rockchip/Makefile
@@ -13,7 +13,9 @@ obj-$(CONFIG_ROCKCHIP_RK3288) = sdram_rk3288.o
obj-$(CONFIG_ROCKCHIP_RK3308) = sdram_rk3308.o
obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o sdram_pctl_px30.o sdram_phy_px30.o
obj-$(CONFIG_ROCKCHIP_RK3399) += sdram_rk3399.o
+obj-$(CONFIG_ROCKCHIP_RK3528) += sdram_rk3528.o
obj-$(CONFIG_ROCKCHIP_RK3568) += sdram_rk3568.o
+obj-$(CONFIG_ROCKCHIP_RK3576) += sdram_rk3576.o
obj-$(CONFIG_ROCKCHIP_RK3588) += sdram_rk3588.o
obj-$(CONFIG_ROCKCHIP_RV1126) += sdram_rv1126.o sdram_pctl_px30.o
obj-$(CONFIG_ROCKCHIP_SDRAM_COMMON) += sdram_common.o
diff --git a/drivers/ram/rockchip/sdram_rk3528.c b/drivers/ram/rockchip/sdram_rk3528.c
new file mode 100644
index 00000000000..89d325bea66
--- /dev/null
+++ b/drivers/ram/rockchip/sdram_rk3528.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright Contributors to the U-Boot project.
+
+#include <dm.h>
+#include <ram.h>
+#include <asm/arch-rockchip/sdram.h>
+
+#define PMUGRF_BASE 0xff370000
+#define OS_REG18_REG 0x248
+
+static int rk3528_dmc_get_info(struct udevice *dev, struct ram_info *info)
+{
+ info->base = CFG_SYS_SDRAM_BASE;
+ info->size = rockchip_sdram_size(PMUGRF_BASE + OS_REG18_REG);
+
+ return 0;
+}
+
+static struct ram_ops rk3528_dmc_ops = {
+ .get_info = rk3528_dmc_get_info,
+};
+
+static const struct udevice_id rk3528_dmc_ids[] = {
+ { .compatible = "rockchip,rk3528-dmc" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_rk3528_dmc) = {
+ .name = "rockchip_rk3528_dmc",
+ .id = UCLASS_RAM,
+ .of_match = rk3528_dmc_ids,
+ .ops = &rk3528_dmc_ops,
+};
diff --git a/drivers/ram/rockchip/sdram_rk3576.c b/drivers/ram/rockchip/sdram_rk3576.c
new file mode 100644
index 00000000000..5a66032ef8f
--- /dev/null
+++ b/drivers/ram/rockchip/sdram_rk3576.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 Rockchip Electronics Co., Ltd.
+ */
+
+#include <dm.h>
+#include <ram.h>
+#include <asm/arch-rockchip/sdram.h>
+
+#define PMU1GRF_BASE 0x26026000
+#define OS_REG2_REG 0x208
+
+static int rk3576_dmc_get_info(struct udevice *dev, struct ram_info *info)
+{
+ info->base = CFG_SYS_SDRAM_BASE;
+ info->size = rockchip_sdram_size(PMU1GRF_BASE + OS_REG2_REG);
+
+ return 0;
+}
+
+static struct ram_ops rk3576_dmc_ops = {
+ .get_info = rk3576_dmc_get_info,
+};
+
+static const struct udevice_id rk3576_dmc_ids[] = {
+ { .compatible = "rockchip,rk3576-dmc" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_rk3576_dmc) = {
+ .name = "rockchip_rk3576_dmc",
+ .id = UCLASS_RAM,
+ .of_match = rk3576_dmc_ids,
+ .ops = &rk3576_dmc_ops,
+};
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 2790b168b19..a3ecea05e20 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -13,6 +13,7 @@ config REMOTEPROC
depends on DM
# Please keep the configuration alphabetically sorted.
+
config K3_SYSTEM_CONTROLLER
bool "Support for TI' K3 System Controller"
select REMOTEPROC
@@ -22,6 +23,16 @@ config K3_SYSTEM_CONTROLLER
help
Say 'y' here to add support for TI' K3 System Controller.
+config REMOTEPROC_ADI_SC5XX
+ bool "Support for ADI SC5xx SHARC cores"
+ select REMOTEPROC
+ depends on DM
+ depends on ARCH_SC5XX
+ depends on SYSCON
+ help
+ Say 'y' here to add support for loading code onto SHARC cores in
+ an ADSP-SC5xx SoC from Analog Devices
+
config REMOTEPROC_RENESAS_APMU
bool "Support for Renesas R-Car Gen4 APMU start of CR52 processor"
select REMOTEPROC
@@ -55,6 +66,7 @@ config REMOTEPROC_TI_K3_ARM64
depends on DM
depends on ARCH_K3
depends on OF_CONTROL
+ default y if SYS_K3_SPL_ATF
help
Say y here to support TI's ARM64 processor subsystems
on various TI K3 family of SoCs through the remote processor
@@ -70,6 +82,16 @@ config REMOTEPROC_TI_K3_DSP
on various TI K3 family of SoCs through the remote processor
framework.
+config REMOTEPROC_TI_K3_M4F
+ bool "TI K3 M4F remoteproc support"
+ select REMOTEPROC
+ depends on ARCH_K3
+ depends on TI_SCI_PROTOCOL
+ help
+ Say y here to support TI's M4F remote processor subsystems
+ on various TI K3 family of SoCs through the remote processor
+ framework.
+
config REMOTEPROC_TI_K3_R5F
bool "TI K3 R5F remoteproc support"
select REMOTEPROC
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 3a092b7660e..47bd57c7890 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -4,15 +4,17 @@
# 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
+obj-$(CONFIG_REMOTEPROC_ADI_SC5XX) += adi_sc5xx_rproc.o
obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o
obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o
obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o
+obj-$(CONFIG_REMOTEPROC_TI_K3_M4F) += ti_k3_m4_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o
obj-$(CONFIG_REMOTEPROC_TI_PRU) += pru_rproc.o
diff --git a/drivers/remoteproc/adi_sc5xx_rproc.c b/drivers/remoteproc/adi_sc5xx_rproc.c
new file mode 100644
index 00000000000..86acd1b98c7
--- /dev/null
+++ b/drivers/remoteproc/adi_sc5xx_rproc.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Analog Devices SC5xx remoteproc driver for loading code onto SHARC cores
+ */
+
+#include <dm.h>
+#include <regmap.h>
+#include <remoteproc.h>
+#include <syscon.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+/* Register offsets */
+#ifdef CONFIG_SC58X
+#define ADI_RCU_REG_CTL 0x00
+#define ADI_RCU_REG_STAT 0x04
+#define ADI_RCU_REG_CRCTL 0x08
+#define ADI_RCU_REG_CRSTAT 0x0c
+#define ADI_RCU_REG_SIDIS 0x10
+#define ADI_RCU_REG_SISTAT 0x14
+#define ADI_RCU_REG_BCODE 0x1c
+#define ADI_RCU_REG_SVECT0 0x20
+#define ADI_RCU_REG_SVECT1 0x24
+#define ADI_RCU_REG_SVECT2 0x28
+#define ADI_RCU_REG_MSG 0x60
+#define ADI_RCU_REG_MSG_SET 0x64
+#define ADI_RCU_REG_MSG_CLR 0x68
+#else
+#define ADI_RCU_REG_CTL 0x00
+#define ADI_RCU_REG_STAT 0x04
+#define ADI_RCU_REG_CRCTL 0x08
+#define ADI_RCU_REG_CRSTAT 0x0c
+#define ADI_RCU_REG_SRRQSTAT 0x18
+#define ADI_RCU_REG_SIDIS 0x1c
+#define ADI_RCU_REG_SISTAT 0x20
+#define ADI_RCU_REG_SVECT_LCK 0x24
+#define ADI_RCU_REG_BCODE 0x28
+#define ADI_RCU_REG_SVECT0 0x2c
+#define ADI_RCU_REG_SVECT1 0x30
+#define ADI_RCU_REG_SVECT2 0x34
+#define ADI_RCU_REG_MSG 0x6c
+#define ADI_RCU_REG_MSG_SET 0x70
+#define ADI_RCU_REG_MSG_CLR 0x74
+#endif /* CONFIG_SC58X */
+
+/* Register bit definitions */
+#define ADI_RCU_CTL_SYSRST BIT(0)
+
+/* Bit values for the RCU0_MSG register */
+#define RCU0_MSG_C0IDLE 0x00000100 /* Core 0 Idle */
+#define RCU0_MSG_C1IDLE 0x00000200 /* Core 1 Idle */
+#define RCU0_MSG_C2IDLE 0x00000400 /* Core 2 Idle */
+#define RCU0_MSG_CRR0 0x00001000 /* Core 0 reset request */
+#define RCU0_MSG_CRR1 0x00002000 /* Core 1 reset request */
+#define RCU0_MSG_CRR2 0x00004000 /* Core 2 reset request */
+#define RCU0_MSG_C1ACTIVATE 0x00080000 /* Core 1 Activated */
+#define RCU0_MSG_C2ACTIVATE 0x00100000 /* Core 2 Activated */
+
+struct sc5xx_rproc_data {
+ /* Address to load to svect when rebooting core */
+ u32 load_addr;
+
+ /* RCU parameters */
+ struct regmap *rcu;
+ u32 svect_offset;
+ u32 coreid;
+};
+
+struct block_code_flag {
+ u32 bcode:4, /* 0-3 */
+ bflag_save:1, /* 4 */
+ bflag_aux:1, /* 5 */
+ breserved:1, /* 6 */
+ bflag_forward:1, /* 7 */
+ bflag_fill:1, /* 8 */
+ bflag_quickboot:1, /* 9 */
+ bflag_callback:1, /* 10 */
+ bflag_init:1, /* 11 */
+ bflag_ignore:1, /* 12 */
+ bflag_indirect:1, /* 13 */
+ bflag_first:1, /* 14 */
+ bflag_final:1, /* 15 */
+ bhdrchk:8, /* 16-23 */
+ bhdrsign:8; /* 0xAD, 0xAC or 0xAB */
+};
+
+struct ldr_hdr {
+ struct block_code_flag bcode_flag;
+ u32 target_addr;
+ u32 byte_count;
+ u32 argument;
+};
+
+static int is_final(struct ldr_hdr *hdr)
+{
+ return hdr->bcode_flag.bflag_final;
+}
+
+static int is_empty(struct ldr_hdr *hdr)
+{
+ return hdr->bcode_flag.bflag_ignore || (hdr->byte_count == 0);
+}
+
+static int adi_valid_firmware(struct ldr_hdr *adi_ldr_hdr)
+{
+ if (!adi_ldr_hdr->byte_count &&
+ (adi_ldr_hdr->bcode_flag.bhdrsign == 0xAD ||
+ adi_ldr_hdr->bcode_flag.bhdrsign == 0xAC ||
+ adi_ldr_hdr->bcode_flag.bhdrsign == 0xAB))
+ return 1;
+
+ return 0;
+}
+
+static int sharc_load(struct udevice *dev, ulong addr, ulong size)
+{
+ struct sc5xx_rproc_data *priv = dev_get_priv(dev);
+ size_t offset;
+ u8 *buf = (u8 *)addr;
+ struct ldr_hdr *ldr = (struct ldr_hdr *)addr;
+ struct ldr_hdr *block_hdr;
+ struct ldr_hdr *next_hdr;
+
+ if (!adi_valid_firmware(ldr)) {
+ dev_err(dev, "Firmware at 0x%lx does not appear to be an LDR image\n", addr);
+ dev_err(dev, "Note: Signed firmware is not currently supported\n");
+ return -EINVAL;
+ }
+
+ do {
+ block_hdr = (struct ldr_hdr *)buf;
+ offset = sizeof(struct ldr_hdr) + (block_hdr->bcode_flag.bflag_fill ?
+ 0 : block_hdr->byte_count);
+ next_hdr = (struct ldr_hdr *)(buf + offset);
+
+ if (block_hdr->bcode_flag.bflag_first)
+ priv->load_addr = (unsigned long)block_hdr->target_addr;
+
+ if (!is_empty(block_hdr)) {
+ if (block_hdr->bcode_flag.bflag_fill) {
+ memset_io((void *)(phys_addr_t)block_hdr->target_addr,
+ block_hdr->argument,
+ block_hdr->byte_count);
+ } else {
+ memcpy_toio((void *)(phys_addr_t)block_hdr->target_addr,
+ buf + sizeof(struct ldr_hdr),
+ block_hdr->byte_count);
+ }
+ }
+
+ if (is_final(block_hdr))
+ break;
+
+ buf += offset;
+ } while (1);
+
+ return 0;
+}
+
+static void sharc_reset(struct sc5xx_rproc_data *priv)
+{
+ u32 coreid = priv->coreid;
+ u32 val;
+
+ /* First put core in reset.
+ * Clear CRSTAT bit for given coreid.
+ */
+ regmap_write(priv->rcu, ADI_RCU_REG_CRSTAT, 1 << coreid);
+
+ /* Set SIDIS to disable the system interface */
+ regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val);
+ regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val | (1 << (coreid - 1)));
+
+ /*
+ * Wait for access to coreX have been disabled and all the pending
+ * transactions have completed
+ */
+ udelay(50);
+
+ /* Set CRCTL bit to put core in reset */
+ regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val);
+ regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val | (1 << coreid));
+
+ /* Poll until Core is in reset */
+ while (!(regmap_read(priv->rcu, ADI_RCU_REG_CRSTAT, &val), val & (1 << coreid)))
+ ;
+
+ /* Clear SIDIS to reenable the system interface */
+ regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val);
+ regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val & ~(1 << (coreid - 1)));
+
+ udelay(50);
+
+ /* Take Core out of reset */
+ regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val);
+ regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val & ~(1 << coreid));
+
+ /* Wait for done */
+ udelay(50);
+}
+
+static int sharc_start(struct udevice *dev)
+{
+ struct sc5xx_rproc_data *priv = dev_get_priv(dev);
+
+ /* Write load address to appropriate SVECT for core */
+ regmap_write(priv->rcu, priv->svect_offset, priv->load_addr);
+
+ sharc_reset(priv);
+
+ /* Clear the IDLE bit when start the SHARC core */
+ regmap_write(priv->rcu, ADI_RCU_REG_MSG_CLR, RCU0_MSG_C0IDLE << priv->coreid);
+
+ /* Notify CCES */
+ regmap_write(priv->rcu, ADI_RCU_REG_MSG_SET, RCU0_MSG_C1ACTIVATE << (priv->coreid - 1));
+ return 0;
+}
+
+static const struct dm_rproc_ops sc5xx_ops = {
+ .load = sharc_load,
+ .start = sharc_start,
+};
+
+static int sc5xx_probe(struct udevice *dev)
+{
+ struct sc5xx_rproc_data *priv = dev_get_priv(dev);
+ u32 coreid;
+
+ if (dev_read_u32(dev, "coreid", &coreid)) {
+ dev_err(dev, "Missing property coreid\n");
+ return -ENOENT;
+ }
+
+ priv->coreid = coreid;
+ switch (coreid) {
+ case 1:
+ priv->svect_offset = ADI_RCU_REG_SVECT1;
+ break;
+ case 2:
+ priv->svect_offset = ADI_RCU_REG_SVECT2;
+ break;
+ default:
+ dev_err(dev, "Invalid value %d for coreid, must be 1 or 2\n", coreid);
+ return -EINVAL;
+ }
+
+ priv->rcu = syscon_regmap_lookup_by_phandle(dev, "adi,rcu");
+ if (IS_ERR(priv->rcu))
+ return PTR_ERR(priv->rcu);
+
+ dev_err(dev, "sc5xx remoteproc core %d available\n", priv->coreid);
+
+ return 0;
+}
+
+static const struct udevice_id sc5xx_ids[] = {
+ { .compatible = "adi,sc5xx-rproc" },
+ { }
+};
+
+U_BOOT_DRIVER(adi_sc5xx_rproc) = {
+ .name = "adi_sc5xx_rproc",
+ .of_match = sc5xx_ids,
+ .id = UCLASS_REMOTEPROC,
+ .ops = &sc5xx_ops,
+ .probe = sc5xx_probe,
+ .priv_auto = sizeof(struct sc5xx_rproc_data),
+ .flags = 0,
+};
diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c
index e90f75a188c..5a7d6377283 100644
--- a/drivers/remoteproc/ti_k3_dsp_rproc.c
+++ b/drivers/remoteproc/ti_k3_dsp_rproc.c
@@ -15,6 +15,7 @@
#include <clk.h>
#include <reset.h>
#include <asm/io.h>
+#include <asm/system.h>
#include <power-domain.h>
#include <dm/device_compat.h>
#include <linux/err.h>
@@ -56,7 +57,9 @@ struct k3_dsp_boot_data {
* @data: Pointer to DSP specific boot data structure
* @mem: Array of available memories
* @num_mem: Number of available memories
- * @in_use: flag to tell if the core is already in use.
+ * @cached_addr: Cached memory address
+ * @cached_size: Cached memory size
+ * @in_use: flag to tell if the core is already in use.
*/
struct k3_dsp_privdata {
struct reset_ctl dsp_rst;
@@ -64,6 +67,8 @@ struct k3_dsp_privdata {
struct k3_dsp_boot_data *data;
struct k3_dsp_mem *mem;
int num_mems;
+ void __iomem *cached_addr;
+ size_t cached_size;
bool in_use;
};
@@ -158,6 +163,13 @@ static int k3_dsp_load(struct udevice *dev, ulong addr, ulong size)
goto unprepare;
}
+ if (dsp->cached_addr && IS_ENABLED(CONFIG_SYS_DISABLE_DCACHE_OPS)) {
+ dev_dbg(dev, "final flush 0x%lx to 0x%lx\n",
+ (ulong)dsp->cached_addr, dsp->cached_size);
+ __asm_invalidate_dcache_range((u64)dsp->cached_addr,
+ (u64)dsp->cached_addr + (u64)dsp->cached_size);
+ }
+
boot_vector = rproc_elf_get_boot_addr(dev, addr);
if (boot_vector & (data->boot_align_addr - 1)) {
ret = -EINVAL;
@@ -253,7 +265,6 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len)
{
struct k3_dsp_privdata *dsp = dev_get_priv(dev);
phys_addr_t bus_addr, dev_addr;
- void __iomem *va = NULL;
size_t size;
u32 offset;
int i;
@@ -263,6 +274,16 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len)
if (len <= 0)
return NULL;
+ if (dsp->cached_addr && IS_ENABLED(CONFIG_SYS_DISABLE_DCACHE_OPS)) {
+ dev_dbg(dev, "flush 0x%lx to 0x%lx\n", (ulong)dsp->cached_addr,
+ dsp->cached_size);
+ __asm_invalidate_dcache_range((u64)dsp->cached_addr,
+ (u64)dsp->cached_addr + (u64)dsp->cached_size);
+ }
+
+ dsp->cached_size = len;
+ dsp->cached_addr = NULL;
+
for (i = 0; i < dsp->num_mems; i++) {
bus_addr = dsp->mem[i].bus_addr;
dev_addr = dsp->mem[i].dev_addr;
@@ -270,19 +291,20 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len)
if (da >= dev_addr && ((da + len) <= (dev_addr + size))) {
offset = da - dev_addr;
- va = dsp->mem[i].cpu_addr + offset;
- return (__force void *)va;
+ dsp->cached_addr = dsp->mem[i].cpu_addr + offset;
}
if (da >= bus_addr && (da + len) <= (bus_addr + size)) {
offset = da - bus_addr;
- va = dsp->mem[i].cpu_addr + offset;
- return (__force void *)va;
+ dsp->cached_addr = dsp->mem[i].cpu_addr + offset;
}
}
/* Assume it is DDR region and return da */
- return map_physmem(da, len, MAP_NOCACHE);
+ if (!dsp->cached_addr)
+ dsp->cached_addr = map_physmem(da, len, MAP_NOCACHE);
+
+ return dsp->cached_addr;
}
static const struct dm_rproc_ops k3_dsp_ops = {
diff --git a/drivers/remoteproc/ti_k3_m4_rproc.c b/drivers/remoteproc/ti_k3_m4_rproc.c
new file mode 100644
index 00000000000..31b9de71579
--- /dev/null
+++ b/drivers/remoteproc/ti_k3_m4_rproc.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments' K3 M4 Remoteproc driver
+ *
+ * Copyright (C) 2024 Texas Instruments Incorporated - http://www.ti.com/
+ * Hari Nagalla <hnagalla@ti.com>
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <remoteproc.h>
+#include <errno.h>
+#include <clk.h>
+#include <reset.h>
+#include <asm/io.h>
+#include <power-domain.h>
+#include <dm/device_compat.h>
+#include <linux/err.h>
+#include <linux/sizes.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+#include "ti_sci_proc.h"
+#include <mach/security.h>
+
+/**
+ * struct k3_m4_mem - internal memory structure
+ * @cpu_addr: MPU virtual address of the memory region
+ * @bus_addr: Bus address used to access the memory region
+ * @dev_addr: Device address from remoteproc view
+ * @size: Size of the memory region
+ */
+struct k3_m4_mem {
+ void __iomem *cpu_addr;
+ phys_addr_t bus_addr;
+ phys_addr_t dev_addr;
+ size_t size;
+};
+
+/**
+ * struct k3_m4_mem_data - memory definitions for m4 remote core
+ * @name: name for this memory entry
+ * @dev_addr: device address for the memory entry
+ */
+struct k3_m4_mem_data {
+ const char *name;
+ const u32 dev_addr;
+};
+
+/**
+ * struct k3_m4_boot_data - internal data structure used for boot
+ * @boot_align_addr: Boot vector address alignment granularity
+ */
+struct k3_m4_boot_data {
+ u32 boot_align_addr;
+};
+
+/**
+ * struct k3_m4_privdata - Structure representing Remote processor data.
+ * @m4_rst: m4 rproc reset control data
+ * @tsp: Pointer to TISCI proc contrl handle
+ * @data: Pointer to DSP specific boot data structure
+ * @mem: Array of available memories
+ * @num_mem: Number of available memories
+ */
+struct k3_m4_privdata {
+ struct reset_ctl m4_rst;
+ struct ti_sci_proc tsp;
+ struct k3_m4_boot_data *data;
+ struct k3_m4_mem *mem;
+ int num_mems;
+};
+
+/*
+ * The M4 cores have a local reset that affects only the CPU, and a
+ * generic module reset that powers on the device and allows the M4 internal
+ * memories to be accessed while the local reset is asserted. This function is
+ * used to release the global reset on M4F to allow loading into the M4F
+ * internal RAMs. This helper function is invoked in k3_m4_load() before any
+ * actual firmware loading happens and is undone only in k3_m4_stop(). The local
+ * reset cannot be released on M4 cores until after the firmware images are loaded.
+ */
+static int k3_m4_prepare(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+ int ret;
+
+ ret = ti_sci_proc_power_domain_on(&m4->tsp);
+ if (ret)
+ dev_err(dev, "cannot enable internal RAM loading, ret = %d\n",
+ ret);
+
+ return ret;
+}
+
+/*
+ * This function is the counterpart to k3_m4_prepare() and is used to assert
+ * the global reset on M4 cores. This completes the second step of powering
+ * down the M4 cores. The cores themselves are halted through the local reset
+ * in first step. This function is invoked in k3_m4_stop() after the local
+ * reset is asserted.
+ */
+static int k3_m4_unprepare(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+
+ return ti_sci_proc_power_domain_off(&m4->tsp);
+}
+
+/**
+ * k3_m4_load() - Load up the Remote processor image
+ * @dev: rproc device pointer
+ * @addr: Address at which image is available
+ * @size: size of the image
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int k3_m4_load(struct udevice *dev, ulong addr, ulong size)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+ void *image_addr = (void *)addr;
+ int ret;
+
+ ret = ti_sci_proc_request(&m4->tsp);
+ if (ret)
+ return ret;
+
+ ret = k3_m4_prepare(dev);
+ if (ret) {
+ dev_err(dev, "Prepare failed for core %d\n",
+ m4->tsp.proc_id);
+ goto proc_release;
+ }
+
+ ti_secure_image_post_process(&image_addr, &size);
+
+ ret = rproc_elf_load_image(dev, addr, size);
+ if (ret < 0) {
+ dev_err(dev, "Loading elf failed %d\n", ret);
+ goto unprepare;
+ }
+
+unprepare:
+ if (ret)
+ k3_m4_unprepare(dev);
+proc_release:
+ ti_sci_proc_release(&m4->tsp);
+ return ret;
+}
+
+/**
+ * k3_m4_start() - Start the remote processor
+ * @dev: rproc device pointer
+ *
+ * Return: 0 if all went ok, else return appropriate error
+ */
+static int k3_m4_start(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+ int ret;
+
+ ret = ti_sci_proc_request(&m4->tsp);
+ if (ret)
+ return ret;
+
+ ret = reset_deassert(&m4->m4_rst);
+
+ ti_sci_proc_release(&m4->tsp);
+
+ return ret;
+}
+
+static int k3_m4_stop(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+
+ ti_sci_proc_request(&m4->tsp);
+ reset_assert(&m4->m4_rst);
+ k3_m4_unprepare(dev);
+ ti_sci_proc_release(&m4->tsp);
+
+ return 0;
+}
+
+static void *k3_m4_da_to_va(struct udevice *dev, ulong da, ulong len)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+ phys_addr_t bus_addr, dev_addr;
+ void __iomem *va = NULL;
+ size_t size;
+ u32 offset;
+ int i;
+
+ if (len <= 0)
+ return NULL;
+
+ for (i = 0; i < m4->num_mems; i++) {
+ bus_addr = m4->mem[i].bus_addr;
+ dev_addr = m4->mem[i].dev_addr;
+ size = m4->mem[i].size;
+
+ if (da >= dev_addr && ((da + len) <= (dev_addr + size))) {
+ offset = da - dev_addr;
+ va = m4->mem[i].cpu_addr + offset;
+ return (__force void *)va;
+ }
+
+ if (da >= bus_addr && (da + len) <= (bus_addr + size)) {
+ offset = da - bus_addr;
+ va = m4->mem[i].cpu_addr + offset;
+ return (__force void *)va;
+ }
+ }
+
+ /* Assume it is DDR region and return da */
+ return map_physmem(da, len, MAP_NOCACHE);
+}
+
+static const struct dm_rproc_ops k3_m4_ops = {
+ .load = k3_m4_load,
+ .start = k3_m4_start,
+ .stop = k3_m4_stop,
+ .device_to_virt = k3_m4_da_to_va,
+};
+
+static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp)
+{
+ u32 ids[2];
+ int ret;
+
+ tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci");
+ if (IS_ERR(tsp->sci)) {
+ dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci));
+ return PTR_ERR(tsp->sci);
+ }
+
+ ret = dev_read_u32_array(dev, "ti,sci-proc-ids", ids, 2);
+ if (ret) {
+ dev_err(dev, "Proc IDs not populated %d\n", ret);
+ return ret;
+ }
+
+ tsp->ops = &tsp->sci->ops.proc_ops;
+ tsp->proc_id = ids[0];
+ tsp->host_id = ids[1];
+ tsp->dev_id = dev_read_u32_default(dev, "ti,sci-dev-id",
+ TI_SCI_RESOURCE_NULL);
+ if (tsp->dev_id == TI_SCI_RESOURCE_NULL) {
+ dev_err(dev, "Device ID not populated %d\n", ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct k3_m4_mem_data am6_m4_mems[] = {
+ { .name = "iram", .dev_addr = 0x0 },
+ { .name = "dram", .dev_addr = 0x30000 },
+};
+
+static int k3_m4_of_get_memories(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+ int i;
+
+ m4->num_mems = ARRAY_SIZE(am6_m4_mems);
+ m4->mem = calloc(m4->num_mems, sizeof(*m4->mem));
+ if (!m4->mem)
+ return -ENOMEM;
+
+ for (i = 0; i < m4->num_mems; i++) {
+ m4->mem[i].bus_addr = dev_read_addr_size_name(dev,
+ am6_m4_mems[i].name,
+ (fdt_addr_t *)&m4->mem[i].size);
+ if (m4->mem[i].bus_addr == FDT_ADDR_T_NONE) {
+ dev_err(dev, "%s bus address not found\n",
+ am6_m4_mems[i].name);
+ return -EINVAL;
+ }
+ m4->mem[i].cpu_addr = map_physmem(m4->mem[i].bus_addr,
+ m4->mem[i].size,
+ MAP_NOCACHE);
+ m4->mem[i].dev_addr = am6_m4_mems[i].dev_addr;
+ }
+
+ return 0;
+}
+
+/**
+ * k3_of_to_priv() - generate private data from device tree
+ * @dev: corresponding k3 m4 processor device
+ * @m4: pointer to driver specific private data
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int k3_m4_of_to_priv(struct udevice *dev, struct k3_m4_privdata *m4)
+{
+ int ret;
+
+ ret = reset_get_by_index(dev, 0, &m4->m4_rst);
+ if (ret) {
+ dev_err(dev, "reset_get() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = ti_sci_proc_of_to_priv(dev, &m4->tsp);
+ if (ret)
+ return ret;
+
+ ret = k3_m4_of_get_memories(dev);
+ if (ret)
+ return ret;
+
+ m4->data = (struct k3_m4_boot_data *)dev_get_driver_data(dev);
+
+ return 0;
+}
+
+/**
+ * k3_m4_probe() - Basic probe
+ * @dev: corresponding k3 remote processor device
+ *
+ * Return: 0 if all goes good, else appropriate error message.
+ */
+static int k3_m4_probe(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4;
+ int ret;
+
+ m4 = dev_get_priv(dev);
+ ret = k3_m4_of_to_priv(dev, m4);
+ if (ret)
+ return ret;
+
+ /*
+ * The M4 local resets are deasserted by default on Power-On-Reset.
+ * Assert the local resets to ensure the M4s don't execute bogus code
+ * in .load() callback when the module reset is released to support
+ * internal memory loading. This is needed for M4 cores.
+ */
+ reset_assert(&m4->m4_rst);
+
+ return 0;
+}
+
+static int k3_m4_remove(struct udevice *dev)
+{
+ struct k3_m4_privdata *m4 = dev_get_priv(dev);
+
+ free(m4->mem);
+
+ return 0;
+}
+
+static const struct k3_m4_boot_data m4_data = {
+ .boot_align_addr = SZ_1K,
+};
+
+static const struct udevice_id k3_m4_ids[] = {
+ { .compatible = "ti,am64-m4fss", .data = (ulong)&m4_data, },
+ {}
+};
+
+U_BOOT_DRIVER(k3_m4) = {
+ .name = "k3_m4",
+ .of_match = k3_m4_ids,
+ .id = UCLASS_REMOTEPROC,
+ .ops = &k3_m4_ops,
+ .probe = k3_m4_probe,
+ .remove = k3_m4_remove,
+ .priv_auto = sizeof(struct k3_m4_privdata),
+};
diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c
index d78b3fa1bbd..57268e7f8ff 100644
--- a/drivers/remoteproc/ti_k3_r5f_rproc.c
+++ b/drivers/remoteproc/ti_k3_r5f_rproc.c
@@ -886,6 +886,7 @@ static const struct udevice_id k3_r5f_rproc_ids[] = {
{ .compatible = "ti,j7200-r5f", .data = (ulong)&j7200_j721s2_data, },
{ .compatible = "ti,j721s2-r5f", .data = (ulong)&j7200_j721s2_data, },
{ .compatible = "ti,am62-r5f", .data = (ulong)&am62_data, },
+ { .compatible = "ti,am64-r5f", .data = (ulong)&j7200_j721s2_data, },
{}
};
@@ -930,6 +931,7 @@ static const struct udevice_id k3_r5fss_ids[] = {
{ .compatible = "ti,j7200-r5fss"},
{ .compatible = "ti,j721s2-r5fss"},
{ .compatible = "ti,am62-r5fss"},
+ { .compatible = "ti,am64-r5fss"},
{}
};
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index fe5c1214f57..a0d079c4555 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -49,6 +49,13 @@ config TEGRA186_RESET
Enable support for manipulating Tegra's on-SoC reset signals via IPC
requests to the BPMP (Boot and Power Management Processor).
+config RESET_AIROHA
+ bool "Reset controller driver for Airoha SoCs"
+ depends on DM_RESET && ARCH_AIROHA
+ default y
+ help
+ Support for reset controller on Airoha SoCs.
+
config RESET_TI_SCI
bool "TI System Control Interface (TI SCI) reset driver"
depends on DM_RESET && TI_SCI_PROTOCOL
@@ -235,4 +242,20 @@ config RESET_AT91
This enables the Reset Controller driver support for Microchip/Atmel
SoCs. Mainly used to expose assert/deassert methods to other drivers
that require it.
+
+config RESET_RZG2L_USBPHY_CTRL
+ bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control"
+ depends on DM_RESET
+ select REGULATOR_RZG2L_USBPHY
+ help
+ Enable support for controlling USB 2.0 PHY resets on the Renesas
+ RZ/G2L SoC. This is required for USB 2.0 functionality to work on this
+ SoC.
+
+config RESET_SPACEMIT_K1
+ bool "Support for SPACEMIT's K1 Reset driver"
+ depends on DM_RESET
+ help
+ Support for SPACEMIT's K1 Reset system. Basic Assert/Deassert
+ is supported.
endmenu
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index d99a78c9828..1dd3cd99a14 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -10,13 +10,14 @@ obj-$(CONFIG_STI_RESET) += sti-reset.o
obj-$(CONFIG_STM32_RESET) += stm32-reset.o
obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o
obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o
+obj-$(CONFIG_RESET_AIROHA) += reset-airoha.o
obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_AST2500) += reset-ast2500.o
obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o
-obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3588.o
+obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3528.o rst-rk3576.o rst-rk3588.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o
@@ -33,3 +34,5 @@ obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o
obj-$(CONFIG_RESET_DRA7) += reset-dra7.o
obj-$(CONFIG_RESET_AT91) += reset-at91.o
obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o
+obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
+obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o
diff --git a/drivers/reset/reset-airoha.c b/drivers/reset/reset-airoha.c
new file mode 100644
index 00000000000..e878af6167c
--- /dev/null
+++ b/drivers/reset/reset-airoha.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based on Linux drivers/clk/clk-en7523.c reworked
+ * and detached to a dedicated driver
+ *
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver)
+ * Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <dm.h>
+#include <linux/io.h>
+#include <reset-uclass.h>
+
+#include <dt-bindings/reset/airoha,en7581-reset.h>
+
+#define RST_NR_PER_BANK 32
+
+#define REG_RESET_CONTROL2 0x830
+#define REG_RESET_CONTROL1 0x834
+
+struct airoha_reset_priv {
+ const u16 *bank_ofs;
+ const u16 *idx_map;
+ void __iomem *base;
+};
+
+static const u16 en7581_rst_ofs[] = {
+ REG_RESET_CONTROL2,
+ REG_RESET_CONTROL1,
+};
+
+static const u16 en7581_rst_map[] = {
+ /* RST_CTRL2 */
+ [EN7581_XPON_PHY_RST] = 0,
+ [EN7581_CPU_TIMER2_RST] = 2,
+ [EN7581_HSUART_RST] = 3,
+ [EN7581_UART4_RST] = 4,
+ [EN7581_UART5_RST] = 5,
+ [EN7581_I2C2_RST] = 6,
+ [EN7581_XSI_MAC_RST] = 7,
+ [EN7581_XSI_PHY_RST] = 8,
+ [EN7581_NPU_RST] = 9,
+ [EN7581_I2S_RST] = 10,
+ [EN7581_TRNG_RST] = 11,
+ [EN7581_TRNG_MSTART_RST] = 12,
+ [EN7581_DUAL_HSI0_RST] = 13,
+ [EN7581_DUAL_HSI1_RST] = 14,
+ [EN7581_HSI_RST] = 15,
+ [EN7581_DUAL_HSI0_MAC_RST] = 16,
+ [EN7581_DUAL_HSI1_MAC_RST] = 17,
+ [EN7581_HSI_MAC_RST] = 18,
+ [EN7581_WDMA_RST] = 19,
+ [EN7581_WOE0_RST] = 20,
+ [EN7581_WOE1_RST] = 21,
+ [EN7581_HSDMA_RST] = 22,
+ [EN7581_TDMA_RST] = 24,
+ [EN7581_EMMC_RST] = 25,
+ [EN7581_SOE_RST] = 26,
+ [EN7581_PCIE2_RST] = 27,
+ [EN7581_XFP_MAC_RST] = 28,
+ [EN7581_USB_HOST_P1_RST] = 29,
+ [EN7581_USB_HOST_P1_U3_PHY_RST] = 30,
+ /* RST_CTRL1 */
+ [EN7581_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0,
+ [EN7581_FE_PDMA_RST] = RST_NR_PER_BANK + 1,
+ [EN7581_FE_QDMA_RST] = RST_NR_PER_BANK + 2,
+ [EN7581_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4,
+ [EN7581_CRYPTO_RST] = RST_NR_PER_BANK + 6,
+ [EN7581_TIMER_RST] = RST_NR_PER_BANK + 8,
+ [EN7581_PCM1_RST] = RST_NR_PER_BANK + 11,
+ [EN7581_UART_RST] = RST_NR_PER_BANK + 12,
+ [EN7581_GPIO_RST] = RST_NR_PER_BANK + 13,
+ [EN7581_GDMA_RST] = RST_NR_PER_BANK + 14,
+ [EN7581_I2C_MASTER_RST] = RST_NR_PER_BANK + 16,
+ [EN7581_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17,
+ [EN7581_SFC_RST] = RST_NR_PER_BANK + 18,
+ [EN7581_UART2_RST] = RST_NR_PER_BANK + 19,
+ [EN7581_GDMP_RST] = RST_NR_PER_BANK + 20,
+ [EN7581_FE_RST] = RST_NR_PER_BANK + 21,
+ [EN7581_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22,
+ [EN7581_GSW_RST] = RST_NR_PER_BANK + 23,
+ [EN7581_SFC2_PCM_RST] = RST_NR_PER_BANK + 25,
+ [EN7581_PCIE0_RST] = RST_NR_PER_BANK + 26,
+ [EN7581_PCIE1_RST] = RST_NR_PER_BANK + 27,
+ [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28,
+ [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
+ [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
+};
+
+static int airoha_reset_update(struct airoha_reset_priv *priv,
+ unsigned long id, bool assert)
+{
+ void __iomem *addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK];
+ u32 val;
+
+ val = readl(addr);
+ if (assert)
+ val |= BIT(id % RST_NR_PER_BANK);
+ else
+ val &= ~BIT(id % RST_NR_PER_BANK);
+ writel(val, addr);
+
+ return 0;
+}
+
+static int airoha_reset_assert(struct reset_ctl *reset_ctl)
+{
+ struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+ int id = reset_ctl->id;
+
+ return airoha_reset_update(priv, id, true);
+}
+
+static int airoha_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+ int id = reset_ctl->id;
+
+ return airoha_reset_update(priv, id, false);
+}
+
+static int airoha_reset_status(struct reset_ctl *reset_ctl)
+{
+ struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+ int id = reset_ctl->id;
+ void __iomem *addr;
+
+ addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK];
+
+ return !!(readl(addr) & BIT(id % RST_NR_PER_BANK));
+}
+
+static int airoha_reset_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+
+ if (args->args[0] >= ARRAY_SIZE(en7581_rst_map))
+ return -EINVAL;
+
+ reset_ctl->id = priv->idx_map[args->args[0]];
+
+ return 0;
+}
+
+static struct reset_ops airoha_reset_ops = {
+ .of_xlate = airoha_reset_xlate,
+ .rst_assert = airoha_reset_assert,
+ .rst_deassert = airoha_reset_deassert,
+ .rst_status = airoha_reset_status,
+};
+
+static int airoha_reset_probe(struct udevice *dev)
+{
+ struct airoha_reset_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->bank_ofs = en7581_rst_ofs;
+ priv->idx_map = en7581_rst_map;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(airoha_reset) = {
+ .name = "airoha-reset",
+ .id = UCLASS_RESET,
+ .probe = airoha_reset_probe,
+ .ops = &airoha_reset_ops,
+ .priv_auto = sizeof(struct airoha_reset_priv),
+};
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
new file mode 100644
index 00000000000..622d7b9cf4f
--- /dev/null
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Renesas Electronics Corporation
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <renesas/rzg2l-usbphy.h>
+#include <reset-uclass.h>
+#include <reset.h>
+
+#define RESET 0x000
+
+#define RESET_SEL_PLLRESET BIT(12)
+#define RESET_PLLRESET BIT(8)
+
+#define RESET_SEL_P2RESET BIT(5)
+#define RESET_SEL_P1RESET BIT(4)
+#define RESET_PHYRST_2 BIT(1)
+#define RESET_PHYRST_1 BIT(0)
+
+#define PHY_RESET_MASK (RESET_PHYRST_1 | RESET_PHYRST_2)
+
+#define NUM_PORTS 2
+
+static int rzg2l_usbphy_ctrl_assert(struct reset_ctl *reset_ctl)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev);
+ u32 val;
+
+ val = readl(priv->regs + RESET);
+ val |= reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1;
+
+ /* If both ports are in reset, we can also place the PLL into reset. */
+ if ((val & PHY_RESET_MASK) == PHY_RESET_MASK)
+ val |= RESET_PLLRESET;
+
+ writel(val, priv->regs + RESET);
+ return 0;
+}
+
+static int rzg2l_usbphy_ctrl_deassert(struct reset_ctl *reset_ctl)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev);
+ u32 val = reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1;
+
+ /* If either port is out of reset, the PLL must also be out of reset. */
+ val |= RESET_PLLRESET;
+
+ clrbits_le32(priv->regs + RESET, val);
+ return 0;
+}
+
+static int rzg2l_usbphy_ctrl_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ if (args->args[0] >= NUM_PORTS)
+ return -EINVAL;
+
+ reset_ctl->id = args->args[0];
+ return 0;
+}
+
+struct reset_ops rzg2l_usbphy_ctrl_ops = {
+ .rst_assert = rzg2l_usbphy_ctrl_assert,
+ .rst_deassert = rzg2l_usbphy_ctrl_deassert,
+ .of_xlate = rzg2l_usbphy_ctrl_of_xlate,
+};
+
+static int rzg2l_usbphy_ctrl_probe(struct udevice *dev)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev);
+ struct reset_ctl rst;
+ int ret;
+
+ priv->regs = dev_read_addr(dev);
+
+ ret = reset_get_by_index(dev, 0, &rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to get reset line: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_deassert(&rst);
+ if (ret < 0) {
+ dev_err(dev, "failed to de-assert reset line: %d\n", ret);
+ return ret;
+ }
+
+ /* put pll and phy into reset state */
+ setbits_le32(priv->regs + RESET,
+ RESET_SEL_PLLRESET | RESET_PLLRESET |
+ RESET_SEL_P1RESET | RESET_PHYRST_1 |
+ RESET_SEL_P2RESET | RESET_PHYRST_2);
+
+ return 0;
+}
+
+static const struct udevice_id rzg2l_usbphy_ctrl_ids[] = {
+ { .compatible = "renesas,rzg2l-usbphy-ctrl", },
+ { /* sentinel */ }
+};
+
+static int rzg2l_usbphy_ctrl_bind(struct udevice *dev)
+{
+ struct driver *drv;
+ ofnode node;
+ int ret;
+
+ node = ofnode_find_subnode(dev_ofnode(dev), "regulator-vbus");
+ if (!ofnode_valid(node)) {
+ dev_err(dev, "Failed to find vbus regulator devicetree node\n");
+ return -ENOENT;
+ }
+
+ drv = lists_driver_lookup_name("rzg2l_usbphy_regulator");
+ if (!drv) {
+ dev_err(dev, "Failed to find vbus regulator driver\n");
+ return -ENOENT;
+ }
+
+ ret = device_bind(dev, drv, dev->name, NULL, node, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to bind vbus regulator: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(rzg2l_usbphy_ctrl) = {
+ .name = "rzg2l_usbphy_ctrl",
+ .id = UCLASS_RESET,
+ .of_match = rzg2l_usbphy_ctrl_ids,
+ .bind = rzg2l_usbphy_ctrl_bind,
+ .probe = rzg2l_usbphy_ctrl_probe,
+ .ops = &rzg2l_usbphy_ctrl_ops,
+ .priv_auto = sizeof(struct rzg2l_usbphy_ctrl_priv),
+};
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
index 76d108080d9..e57729f0ef9 100644
--- a/drivers/reset/reset-socfpga.c
+++ b/drivers/reset/reset-socfpga.c
@@ -23,6 +23,7 @@
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/sizes.h>
+#include <linux/kconfig.h>
#define BANK_INCREMENT 4
#define NR_BANKS 8
@@ -114,6 +115,8 @@ static int socfpga_reset_remove(struct udevice *dev)
if (socfpga_reset_keep_enabled()) {
puts("Deasserting all peripheral resets\n");
writel(0, data->modrst_base + 4);
+ if (IS_ENABLED(CONFIG_TARGET_SOCFPGA_ARRIA10))
+ writel(0, data->modrst_base + 8);
}
return 0;
diff --git a/drivers/reset/reset-spacemit-k1.c b/drivers/reset/reset-spacemit-k1.c
new file mode 100644
index 00000000000..613e002fc4f
--- /dev/null
+++ b/drivers/reset/reset-spacemit-k1.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Spacemit Inc.
+ * Copyright (C) 2025 Huan Zhou <pericycle.cc@gmail.com>
+ */
+
+#include <asm/io.h>
+#include <config.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dt-bindings/reset/spacemit-k1-reset.h>
+#include <linux/bitops.h>
+#include <reset-uclass.h>
+
+/* APBC register offset */
+#define APBC_UART1_CLK_RST 0x0
+#define APBC_UART2_CLK_RST 0x4
+#define APBC_GPIO_CLK_RST 0x8
+#define APBC_PWM0_CLK_RST 0xc
+#define APBC_PWM1_CLK_RST 0x10
+#define APBC_PWM2_CLK_RST 0x14
+#define APBC_PWM3_CLK_RST 0x18
+#define APBC_TWSI8_CLK_RST 0x20
+#define APBC_UART3_CLK_RST 0x24
+#define APBC_RTC_CLK_RST 0x28
+#define APBC_TWSI0_CLK_RST 0x2c
+#define APBC_TWSI1_CLK_RST 0x30
+#define APBC_TIMERS1_CLK_RST 0x34
+#define APBC_TWSI2_CLK_RST 0x38
+#define APBC_AIB_CLK_RST 0x3c
+#define APBC_TWSI4_CLK_RST 0x40
+#define APBC_TIMERS2_CLK_RST 0x44
+#define APBC_ONEWIRE_CLK_RST 0x48
+#define APBC_TWSI5_CLK_RST 0x4c
+#define APBC_DRO_CLK_RST 0x58
+#define APBC_IR_CLK_RST 0x5c
+#define APBC_TWSI6_CLK_RST 0x60
+#define APBC_TWSI7_CLK_RST 0x68
+#define APBC_TSEN_CLK_RST 0x6c
+
+#define APBC_UART4_CLK_RST 0x70
+#define APBC_UART5_CLK_RST 0x74
+#define APBC_UART6_CLK_RST 0x78
+#define APBC_SSP3_CLK_RST 0x7c
+
+#define APBC_SSPA0_CLK_RST 0x80
+#define APBC_SSPA1_CLK_RST 0x84
+
+#define APBC_IPC_AP2AUD_CLK_RST 0x90
+#define APBC_UART7_CLK_RST 0x94
+#define APBC_UART8_CLK_RST 0x98
+#define APBC_UART9_CLK_RST 0x9c
+
+#define APBC_CAN0_CLK_RST 0xa0
+#define APBC_PWM4_CLK_RST 0xa8
+#define APBC_PWM5_CLK_RST 0xac
+#define APBC_PWM6_CLK_RST 0xb0
+#define APBC_PWM7_CLK_RST 0xb4
+#define APBC_PWM8_CLK_RST 0xb8
+#define APBC_PWM9_CLK_RST 0xbc
+#define APBC_PWM10_CLK_RST 0xc0
+#define APBC_PWM11_CLK_RST 0xc4
+#define APBC_PWM12_CLK_RST 0xc8
+#define APBC_PWM13_CLK_RST 0xcc
+#define APBC_PWM14_CLK_RST 0xd0
+#define APBC_PWM15_CLK_RST 0xd4
+#define APBC_PWM16_CLK_RST 0xd8
+#define APBC_PWM17_CLK_RST 0xdc
+#define APBC_PWM18_CLK_RST 0xe0
+#define APBC_PWM19_CLK_RST 0xe4
+/* end of APBC register offset */
+
+/* MPMU register offset */
+#define MPMU_WDTPCR 0x200
+/* end of MPMU register offset */
+
+/* APMU register offset */
+#define APMU_JPG_CLK_RES_CTRL 0x20
+#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24
+#define APMU_ISP_CLK_RES_CTRL 0x38
+#define APMU_LCD_CLK_RES_CTRL1 0x44
+#define APMU_LCD_SPI_CLK_RES_CTRL 0x48
+#define APMU_LCD_CLK_RES_CTRL2 0x4c
+#define APMU_CCIC_CLK_RES_CTRL 0x50
+#define APMU_SDH0_CLK_RES_CTRL 0x54
+#define APMU_SDH1_CLK_RES_CTRL 0x58
+#define APMU_USB_CLK_RES_CTRL 0x5c
+#define APMU_QSPI_CLK_RES_CTRL 0x60
+#define APMU_USB_CLK_RES_CTRL 0x5c
+#define APMU_DMA_CLK_RES_CTRL 0x64
+#define APMU_AES_CLK_RES_CTRL 0x68
+#define APMU_VPU_CLK_RES_CTRL 0xa4
+#define APMU_GPU_CLK_RES_CTRL 0xcc
+#define APMU_SDH2_CLK_RES_CTRL 0xe0
+#define APMU_PMUA_MC_CTRL 0xe8
+#define APMU_PMU_CC2_AP 0x100
+#define APMU_PMUA_EM_CLK_RES_CTRL 0x104
+
+#define APMU_AUDIO_CLK_RES_CTRL 0x14c
+#define APMU_HDMI_CLK_RES_CTRL 0x1B8
+
+#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc
+#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4
+#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc
+
+#define APMU_EMAC0_CLK_RES_CTRL 0x3e4
+#define APMU_EMAC1_CLK_RES_CTRL 0x3ec
+/* end of APMU register offset */
+
+/* APBC2 register offset */
+#define APBC2_UART1_CLK_RST 0x00
+#define APBC2_SSP2_CLK_RST 0x04
+#define APBC2_TWSI3_CLK_RST 0x08
+#define APBC2_RTC_CLK_RST 0x0c
+#define APBC2_TIMERS0_CLK_RST 0x10
+#define APBC2_KPC_CLK_RST 0x14
+#define APBC2_GPIO_CLK_RST 0x1c
+/* end of APBC2 register offset */
+
+enum spacemit_reset_base_type {
+ RST_BASE_TYPE_MPMU = 0,
+ RST_BASE_TYPE_APMU = 1,
+ RST_BASE_TYPE_APBC = 2,
+ RST_BASE_TYPE_APBS = 3,
+ RST_BASE_TYPE_CIU = 4,
+ RST_BASE_TYPE_DCIU = 5,
+ RST_BASE_TYPE_DDRC = 6,
+ RST_BASE_TYPE_AUDC = 7,
+ RST_BASE_TYPE_APBC2 = 8,
+};
+
+struct spacemit_reset_signal {
+ u32 offset;
+ u32 mask;
+ u32 deassert_val;
+ u32 assert_val;
+ enum spacemit_reset_base_type type;
+};
+
+struct spacemit_reset_base {
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbc_base;
+ void __iomem *apbs_base;
+ void __iomem *ciu_base;
+ void __iomem *dciu_base;
+ void __iomem *ddrc_base;
+ void __iomem *audio_ctrl_base;
+ void __iomem *apbc2_base;
+};
+
+struct spacemit_reset {
+ struct spacemit_reset_base io_base;
+ const struct spacemit_reset_signal *signals;
+};
+
+enum {
+ RESET_TWSI6_SPL = 0,
+ RESET_TWSI8_SPL,
+ RESET_SDH_AXI_SPL,
+ RESET_SDH0_SPL,
+ RESET_USB_AXI_SPL,
+ RESET_USBP1_AXI_SPL,
+ RESET_USB3_0_SPL,
+ RESET_QSPI_SPL,
+ RESET_QSPI_BUS_SPL,
+ RESET_AES_SPL,
+ RESET_SDH2_SPL,
+ RESET_NUMBER_SPL
+};
+
+static const struct spacemit_reset_signal
+ k1_reset_signals[RESET_NUMBER] = {
+ [RESET_UART1] = { APBC_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART2] = { APBC_UART2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_GPIO] = { APBC_GPIO_CLK_RST, BIT(2), 0,
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM0] = { APBC_PWM0_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM1] = { APBC_PWM1_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM2] = { APBC_PWM2_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM3] = { APBC_PWM3_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM4] = { APBC_PWM4_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM5] = { APBC_PWM5_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM6] = { APBC_PWM6_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM7] = { APBC_PWM7_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM8] = { APBC_PWM8_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM9] = { APBC_PWM9_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM10] = { APBC_PWM10_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM11] = { APBC_PWM11_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM12] = { APBC_PWM12_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM13] = { APBC_PWM13_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM14] = { APBC_PWM14_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM15] = { APBC_PWM15_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM16] = { APBC_PWM16_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM17] = { APBC_PWM17_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM18] = { APBC_PWM18_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_PWM19] = { APBC_PWM19_CLK_RST,
+ BIT(2) | BIT(0),
+ BIT(0),
+ BIT(2),
+ RST_BASE_TYPE_APBC },
+ [RESET_SSP3] = { APBC_SSP3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART3] = { APBC_UART3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_RTC] = { APBC_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI0] = { APBC_TWSI0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TIMERS1] = { APBC_TIMERS1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_AIB] = { APBC_AIB_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TIMERS2] = { APBC_TIMERS2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_ONEWIRE] = { APBC_ONEWIRE_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_SSPA0] = { APBC_SSPA0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_SSPA1] = { APBC_SSPA1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_DRO] = { APBC_DRO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_IR] = { APBC_IR_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI1] = { APBC_TWSI1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TSEN] = { APBC_TSEN_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI2] = { APBC_TWSI2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI4] = { APBC_TWSI4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI5] = { APBC_TWSI5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI6] = { APBC_TWSI6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI7] = { APBC_TWSI7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_TWSI8] = { APBC_TWSI8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_IPC_AP2AUD] = { APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART4] = { APBC_UART4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART5] = { APBC_UART5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART6] = { APBC_UART6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART7] = { APBC_UART7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART8] = { APBC_UART8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_UART9] = { APBC_UART9_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ [RESET_CAN0] = { APBC_CAN0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC },
+ /* MPMU */
+ [RESET_WDT] = { MPMU_WDTPCR, BIT(2), 0, BIT(2), RST_BASE_TYPE_MPMU },
+ /* APMU */
+ [RESET_JPG] = { APMU_JPG_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_CSI] = { APMU_CSI_CCIC2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_CCIC2_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL,
+ BIT(2),
+ BIT(2),
+ 0,
+ RST_BASE_TYPE_APMU },
+ [RESET_CCIC3_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL,
+ BIT(29),
+ BIT(29),
+ 0,
+ RST_BASE_TYPE_APMU },
+ [RESET_ISP] = { APMU_ISP_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_ISP_AHB] = { APMU_ISP_CLK_RES_CTRL, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU },
+ [RESET_ISP_CI] = { APMU_ISP_CLK_RES_CTRL, BIT(16), BIT(16), 0, RST_BASE_TYPE_APMU },
+ [RESET_ISP_CPP] = { APMU_ISP_CLK_RES_CTRL, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU },
+ [RESET_LCD] = { APMU_LCD_CLK_RES_CTRL1, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
+ [RESET_DSI_ESC] = { APMU_LCD_CLK_RES_CTRL1, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU },
+ [RESET_V2D] = { APMU_LCD_CLK_RES_CTRL1, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU },
+ [RESET_MIPI] = { APMU_LCD_CLK_RES_CTRL1, BIT(15), BIT(15), 0, RST_BASE_TYPE_APMU },
+ [RESET_LCD_SPI] = { APMU_LCD_SPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_LCD_SPI_BUS] = { APMU_LCD_SPI_CLK_RES_CTRL,
+ BIT(4),
+ BIT(4),
+ 0,
+ RST_BASE_TYPE_APMU },
+ [RESET_LCD_SPI_HBUS] = { APMU_LCD_SPI_CLK_RES_CTRL,
+ BIT(2),
+ BIT(2),
+ 0,
+ RST_BASE_TYPE_APMU },
+ [RESET_LCD_MCLK] = { APMU_LCD_CLK_RES_CTRL2, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU },
+ [RESET_CCIC_4X] = { APMU_CCIC_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_CCIC1_PHY] = { APMU_CCIC_CLK_RES_CTRL, BIT(2), BIT(2), 0, RST_BASE_TYPE_APMU },
+ [RESET_SDH_AXI] = { APMU_SDH0_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_SDH0] = { APMU_SDH0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_SDH1] = { APMU_SDH1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_USB_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_USBP1_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
+ [RESET_USB3_0] = { APMU_USB_CLK_RES_CTRL,
+ BIT(9) | BIT(10) | BIT(11),
+ BIT(9) | BIT(10) | BIT(11),
+ 0,
+ RST_BASE_TYPE_APMU },
+ [RESET_QSPI] = { APMU_QSPI_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_QSPI_BUS] = { APMU_QSPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_DMA] = { APMU_DMA_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_AES] = { APMU_AES_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU },
+ [RESET_VPU] = { APMU_VPU_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_GPU] = { APMU_GPU_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_SDH2] = { APMU_SDH2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_MC] = { APMU_PMUA_MC_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_EM_AXI] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU },
+ [RESET_EM] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_AUDIO_SYS] = { APMU_AUDIO_CLK_RES_CTRL,
+ BIT(0) | BIT(2) | BIT(3),
+ BIT(0) | BIT(2) | BIT(3),
+ 0,
+ RST_BASE_TYPE_APMU },
+ [RESET_HDMI] = { APMU_HDMI_CLK_RES_CTRL, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU },
+ [RESET_PCIE0] = { APMU_PCIE_CLK_RES_CTRL_0,
+ BIT(3) | BIT(4) | BIT(5) | BIT(8),
+ BIT(3) | BIT(4) | BIT(5),
+ BIT(8),
+ RST_BASE_TYPE_APMU },
+ [RESET_PCIE1] = { APMU_PCIE_CLK_RES_CTRL_1,
+ BIT(3) | BIT(4) | BIT(5) | BIT(8),
+ BIT(3) | BIT(4) | BIT(5),
+ BIT(8),
+ RST_BASE_TYPE_APMU },
+ [RESET_PCIE2] = { APMU_PCIE_CLK_RES_CTRL_2, 0x138, 0x38, 0x100, RST_BASE_TYPE_APMU },
+ [RESET_EMAC0] = { APMU_EMAC0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_EMAC1] = { APMU_EMAC1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU },
+ [RESET_SEC_UART1] = { APBC2_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ [RESET_SEC_SSP2] = { APBC2_SSP2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ [RESET_SEC_TWSI3] = { APBC2_TWSI3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ [RESET_SEC_RTC] = { APBC2_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ [RESET_SEC_TIMERS0] = { APBC2_TIMERS0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ [RESET_SEC_KPC] = { APBC2_KPC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ [RESET_SEC_GPIO] = { APBC2_GPIO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 },
+ };
+
+static u32 spacemit_reset_read(struct spacemit_reset *reset, u32 id)
+{
+ void __iomem *base;
+
+ switch (reset->signals[id].type) {
+ case RST_BASE_TYPE_APMU:
+ base = reset->io_base.apmu_base;
+ break;
+ case RST_BASE_TYPE_APBC:
+ base = reset->io_base.apbc_base;
+ break;
+ default:
+ base = reset->io_base.apbc_base;
+ break;
+ }
+
+ return readl(base + reset->signals[id].offset);
+}
+
+static void spacemit_reset_write(struct spacemit_reset *reset, u32 value, u32 id)
+{
+ void __iomem *base;
+
+ switch (reset->signals[id].type) {
+ case RST_BASE_TYPE_APMU:
+ base = reset->io_base.apmu_base;
+ break;
+ case RST_BASE_TYPE_APBC:
+ base = reset->io_base.apbc_base;
+ break;
+ default:
+ base = reset->io_base.apbc_base;
+ break;
+ }
+
+ writel(value, base + reset->signals[id].offset);
+}
+
+static void spacemit_reset_set(struct reset_ctl *rst, u32 id, bool assert)
+{
+ u32 value;
+ struct spacemit_reset *reset = dev_get_priv(rst->dev);
+
+ value = spacemit_reset_read(reset, id);
+
+ if (assert) {
+ value &= ~reset->signals[id].mask;
+ value |= reset->signals[id].assert_val;
+ } else {
+ value &= ~reset->signals[id].mask;
+ value |= reset->signals[id].deassert_val;
+ }
+
+ spacemit_reset_write(reset, value, id);
+}
+
+static int spacemit_reset_update(struct reset_ctl *rst, bool assert)
+{
+ if (rst->id < RESET_UART1 || rst->id >= RESET_NUMBER)
+ return 0;
+
+ /* can not write to twsi8 */
+ if (rst->id == RESET_TWSI8)
+ return 0;
+
+ spacemit_reset_set(rst, rst->id, assert);
+ return 0;
+}
+
+static int spacemit_reset_assert(struct reset_ctl *rst)
+{
+ return spacemit_reset_update(rst, true);
+}
+
+static int spacemit_reset_deassert(struct reset_ctl *rst)
+{
+ return spacemit_reset_update(rst, false);
+}
+
+static int spacemit_k1_reset_probe(struct udevice *dev)
+{
+ struct spacemit_reset *reset = dev_get_priv(dev);
+
+ reset->io_base.mpmu_base = (void *)dev_remap_addr_index(dev, 0);
+ if (!reset->io_base.mpmu_base) {
+ pr_err("failed to map mpmu registers\n");
+ goto out;
+ }
+
+ reset->io_base.apmu_base = (void *)dev_remap_addr_index(dev, 1);
+ if (!reset->io_base.apmu_base) {
+ pr_err("failed to map apmu registers\n");
+ goto out;
+ }
+
+ reset->io_base.apbc_base = (void *)dev_remap_addr_index(dev, 2);
+ if (!reset->io_base.apbc_base) {
+ pr_err("failed to map apbc registers\n");
+ goto out;
+ }
+
+ reset->io_base.apbs_base = (void *)dev_remap_addr_index(dev, 3);
+ if (!reset->io_base.apbs_base) {
+ pr_err("failed to map apbs registers\n");
+ goto out;
+ }
+
+ reset->io_base.ciu_base = (void *)dev_remap_addr_index(dev, 4);
+ if (!reset->io_base.ciu_base) {
+ pr_err("failed to map ciu registers\n");
+ goto out;
+ }
+
+ reset->io_base.dciu_base = (void *)dev_remap_addr_index(dev, 5);
+ if (!reset->io_base.dciu_base) {
+ pr_err("failed to map dragon ciu registers\n");
+ goto out;
+ }
+
+ reset->io_base.ddrc_base = (void *)dev_remap_addr_index(dev, 6);
+ if (!reset->io_base.ddrc_base) {
+ pr_err("failed to map ddrc registers\n");
+ goto out;
+ }
+
+ reset->io_base.apbc2_base = (void *)dev_remap_addr_index(dev, 7);
+ if (!reset->io_base.apbc2_base) {
+ pr_err("failed to map apbc2 registers\n");
+ goto out;
+ }
+
+ reset->signals = k1_reset_signals;
+
+out:
+ return 0;
+}
+
+const struct reset_ops k1_reset_ops = {
+ .rst_assert = spacemit_reset_assert,
+ .rst_deassert = spacemit_reset_deassert,
+};
+
+static const struct udevice_id k1_reset_ids[] = {
+ { .compatible = "spacemit,k1-reset", },
+ {},
+};
+
+U_BOOT_DRIVER(k1_reset) = {
+ .name = "spacemit,k1-reset",
+ .id = UCLASS_RESET,
+ .ops = &k1_reset_ops,
+ .of_match = k1_reset_ids,
+ .probe = spacemit_k1_reset_probe,
+ .priv_auto = sizeof(struct spacemit_reset),
+};
diff --git a/drivers/reset/rst-rk3528.c b/drivers/reset/rst-rk3528.c
new file mode 100644
index 00000000000..f6e760d468d
--- /dev/null
+++ b/drivers/reset/rst-rk3528.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ * Based on Sebastian Reichel's implementation for RK3588
+ */
+
+#include <dm.h>
+#include <asm/arch-rockchip/clock.h>
+#include <dt-bindings/reset/rockchip,rk3528-cru.h>
+
+/* 0xFF4A0000 + 0x0A00 */
+#define RK3528_CRU_RESET_OFFSET(id, reg, bit) [id] = (0 + reg * 16 + bit)
+
+/* mapping table for reset ID to register offset */
+static const int rk3528_register_offset[] = {
+ /* CRU_SOFTRST_CON03 */
+ RK3528_CRU_RESET_OFFSET(SRST_CORE0_PO, 3, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE1_PO, 3, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE2_PO, 3, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE3_PO, 3, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE0, 3, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE1, 3, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE2, 3, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE3, 3, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_NL2, 3, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE_BIU, 3, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE_CRYPTO, 3, 10),
+
+ /* CRU_SOFTRST_CON05 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_DBG, 5, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_POT_DBG, 5, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_NT_DBG, 5, 15),
+
+ /* CRU_SOFTRST_CON06 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_CORE_GRF, 6, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DAPLITE_BIU, 6, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CPU_BIU, 6, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_REF_PVTPLL_CORE, 6, 7),
+
+ /* CRU_SOFTRST_CON08 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_BUS_VOPGL_BIU, 8, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_A_BUS_H_BIU, 8, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_A_SYSMEM_BIU, 8, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_A_BUS_BIU, 8, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_H_BUS_BIU, 8, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_P_BUS_BIU, 8, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DFT2APB, 8, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_P_BUS_GRF, 8, 15),
+
+ /* CRU_SOFTRST_CON09 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_BUS_M_BIU, 9, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_A_GIC, 9, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_A_SPINLOCK, 9, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_A_DMAC, 9, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_P_TIMER, 9, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER0, 9, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER1, 9, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER2, 9, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER3, 9, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER4, 9, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER5, 9, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_P_JDBCK_DAP, 9, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_JDBCK_DAP, 9, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_P_WDT_NS, 9, 15),
+
+ /* CRU_SOFTRST_CON10 */
+ RK3528_CRU_RESET_OFFSET(SRST_T_WDT_NS, 10, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_H_TRNG_NS, 10, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART0, 10, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART0, 10, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_PKA_CRYPTO, 10, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_A_CRYPTO, 10, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_H_CRYPTO, 10, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DMA2DDR, 10, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_A_DMA2DDR, 10, 14),
+
+ /* CRU_SOFTRST_CON11 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_PWM0, 11, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_PWM0, 11, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_P_PWM1, 11, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_PWM1, 11, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_P_SCR, 11, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_A_DCF, 11, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_P_INTMUX, 11, 12),
+
+ /* CRU_SOFTRST_CON25 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_VPU_BIU, 25, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_H_VPU_BIU, 25, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VPU_BIU, 25, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_A_VPU, 25, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_H_VPU, 25, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CRU_PCIE, 25, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VPU_GRF, 25, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SFC, 25, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_S_SFC, 25, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_C_EMMC, 25, 15),
+
+ /* CRU_SOFTRST_CON26 */
+ RK3528_CRU_RESET_OFFSET(SRST_H_EMMC, 26, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_A_EMMC, 26, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_B_EMMC, 26, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_T_EMMC, 26, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_GPIO1, 26, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO1, 26, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_A_VPU_L_BIU, 26, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VPU_IOC, 26, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S0, 26, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S0, 26, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S2, 26, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S2, 26, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_ACODEC, 26, 13),
+
+ /* CRU_SOFTRST_CON27 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_GPIO3, 27, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO3, 27, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_P_SPI1, 27, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_SPI1, 27, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART2, 27, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART2, 27, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART5, 27, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART5, 27, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART6, 27, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART6, 27, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART7, 27, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART7, 27, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C3, 27, 15),
+
+ /* CRU_SOFTRST_CON28 */
+ RK3528_CRU_RESET_OFFSET(SRST_I2C3, 28, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C5, 28, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_I2C5, 28, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C6, 28, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_I2C6, 28, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_A_MAC, 28, 5),
+
+ /* CRU_SOFTRST_CON30 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_PCIE, 30, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_PCIE_PIPE_PHY, 30, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_PCIE_POWER_UP, 30, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_PCIE_PHY, 30, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_PIPE_GRF, 30, 7),
+
+ /* CRU_SOFTRST_CON32 */
+ RK3528_CRU_RESET_OFFSET(SRST_H_SDIO0, 32, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SDIO1, 32, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_TS_0, 32, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_TS_1, 32, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CAN2, 32, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_CAN2, 32, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CAN3, 32, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_CAN3, 32, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_P_SARADC, 32, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_SARADC, 32, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_SARADC_PHY, 32, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_P_TSADC, 32, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_TSADC, 32, 15),
+
+ /* CRU_SOFTRST_CON33 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_USB3OTG, 33, 1),
+
+ /* CRU_SOFTRST_CON34 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_GPU_BIU, 34, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_GPU_BIU, 34, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_A_GPU, 34, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_REF_PVTPLL_GPU, 34, 9),
+
+ /* CRU_SOFTRST_CON36 */
+ RK3528_CRU_RESET_OFFSET(SRST_H_RKVENC_BIU, 36, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_A_RKVENC_BIU, 36, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_P_RKVENC_BIU, 36, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_H_RKVENC, 36, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_A_RKVENC, 36, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE_RKVENC, 36, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S1, 36, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S1, 36, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C1, 36, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_I2C1, 36, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C0, 36, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_I2C0, 36, 14),
+
+ /* CRU_SOFTRST_CON37 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_SPI0, 37, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_SPI0, 37, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_GPIO4, 37, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO4, 37, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_P_RKVENC_IOC, 37, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SPDIF, 37, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_M_SPDIF, 37, 15),
+
+ /* CRU_SOFTRST_CON38 */
+ RK3528_CRU_RESET_OFFSET(SRST_H_PDM, 38, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_M_PDM, 38, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART1, 38, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART1, 38, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART3, 38, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART3, 38, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_P_RKVENC_GRF, 38, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CAN0, 38, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_CAN0, 38, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CAN1, 38, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_CAN1, 38, 10),
+
+ /* CRU_SOFTRST_CON39 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_VO_BIU, 39, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_H_VO_BIU, 39, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VO_BIU, 39, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_H_RGA2E, 39, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_A_RGA2E, 39, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE_RGA2E, 39, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_H_VDPP, 39, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_A_VDPP, 39, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_CORE_VDPP, 39, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VO_GRF, 39, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_P_CRU, 39, 15),
+
+ /* CRU_SOFTRST_CON40 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_VOP_BIU, 40, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_H_VOP, 40, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_D_VOP0, 40, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_D_VOP1, 40, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_A_VOP, 40, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_P_HDMI, 40, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_HDMI, 40, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_P_HDMIPHY, 40, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_H_HDCP_KEY, 40, 15),
+
+ /* CRU_SOFTRST_CON41 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_HDCP, 41, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_H_HDCP, 41, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_P_HDCP, 41, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_H_CVBS, 41, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_D_CVBS_VOP, 41, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_D_4X_CVBS_VOP, 41, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_A_JPEG_DECODER, 41, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_H_JPEG_DECODER, 41, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_A_VO_L_BIU, 41, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_A_MAC_VO, 41, 10),
+
+ /* CRU_SOFTRST_CON42 */
+ RK3528_CRU_RESET_OFFSET(SRST_A_JPEG_BIU, 42, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SAI_I2S3, 42, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_M_SAI_I2S3, 42, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_MACPHY, 42, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VCDCPHY, 42, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_P_GPIO2, 42, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_DB_GPIO2, 42, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_VO_IOC, 42, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_H_SDMMC0, 42, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_P_OTPC_NS, 42, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_SBPI_OTPC_NS, 42, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_USER_OTPC_NS, 42, 13),
+
+ /* CRU_SOFTRST_CON43 */
+ RK3528_CRU_RESET_OFFSET(SRST_HDMIHDP0, 43, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_H_USBHOST, 43, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_H_USBHOST_ARB, 43, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_HOST_UTMI, 43, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_UART4, 43, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_S_UART4, 43, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C4, 43, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_I2C4, 43, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_P_I2C7, 43, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_I2C7, 43, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_P_USBPHY, 43, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_USBPHY_POR, 43, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_USBPHY_OTG, 43, 15),
+
+ /* CRU_SOFTRST_CON44 */
+ RK3528_CRU_RESET_OFFSET(SRST_USBPHY_HOST, 44, 0),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDRPHY_CRU, 44, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_H_RKVDEC_BIU, 44, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_A_RKVDEC_BIU, 44, 7),
+ RK3528_CRU_RESET_OFFSET(SRST_A_RKVDEC, 44, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_H_RKVDEC, 44, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_HEVC_CA_RKVDEC, 44, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_REF_PVTPLL_RKVDEC, 44, 12),
+
+ /* CRU_SOFTRST_CON45 */
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDR_BIU, 45, 1),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDRC, 45, 2),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDRMON, 45, 3),
+ RK3528_CRU_RESET_OFFSET(SRST_TIMER_DDRMON, 45, 4),
+ RK3528_CRU_RESET_OFFSET(SRST_P_MSCH_BIU, 45, 5),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDR_GRF, 45, 6),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDR_HWLP, 45, 8),
+ RK3528_CRU_RESET_OFFSET(SRST_P_DDRPHY, 45, 9),
+ RK3528_CRU_RESET_OFFSET(SRST_MSCH_BIU, 45, 10),
+ RK3528_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL, 45, 11),
+ RK3528_CRU_RESET_OFFSET(SRST_DDR_UPCTL, 45, 12),
+ RK3528_CRU_RESET_OFFSET(SRST_DDRMON, 45, 13),
+ RK3528_CRU_RESET_OFFSET(SRST_A_DDR_SCRAMBLE, 45, 14),
+ RK3528_CRU_RESET_OFFSET(SRST_A_SPLIT, 45, 15),
+
+ /* CRU_SOFTRST_CON46 */
+ RK3528_CRU_RESET_OFFSET(SRST_DDR_PHY, 46, 0),
+};
+
+int rk3528_reset_bind_lut(struct udevice *pdev, u32 reg_offset, u32 reg_number)
+{
+ return rockchip_reset_bind_lut(pdev, rk3528_register_offset,
+ reg_offset, reg_number);
+}
diff --git a/drivers/reset/rst-rk3576.c b/drivers/reset/rst-rk3576.c
new file mode 100644
index 00000000000..a6b83a2fd74
--- /dev/null
+++ b/drivers/reset/rst-rk3576.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2024 Collabora Ltd.
+ * Author: Detlev Casanova <detlev.casanova@collabora.com>
+ * Based on Sebastian Reichel's implementation for RK3588
+ */
+
+#include <dm.h>
+#include <asm/arch-rockchip/clock.h>
+#include <dt-bindings/reset/rockchip,rk3576-cru.h>
+
+/* 0x27200000 + 0x0A00 */
+#define RK3576_CRU_RESET_OFFSET(id, reg, bit) [id] = (0 + (reg) * 16 + (bit))
+/* 0x27208000 + 0x0A00 */
+#define RK3576_PHPCRU_RESET_OFFSET(id, reg, bit) [id] = (0x8000 * 4 + (reg) * 16 + (bit))
+/* 0x27210000 + 0x0A00 */
+#define RK3576_SECURENSCRU_RESET_OFFSET(id, reg, bit) [id] = (0x10000 * 4 + (reg) * 16 + (bit))
+/* 0x27220000 + 0x0A00 */
+#define RK3576_PMU1CRU_RESET_OFFSET(id, reg, bit) [id] = (0x20000 * 4 + (reg) * 16 + (bit))
+
+/* mapping table for reset ID to register offset */
+static const int rk3576_register_offset[] = {
+ /* SOFTRST_CON01 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_TOP_BIU, 1, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_TOP_BIU, 1, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_TOP_MID_BIU, 1, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SECURE_HIGH_BIU, 1, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_TOP_BIU, 1, 14),
+
+ /* SOFTRST_CON02 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_VO0VOP_CHANNEL_BIU, 2, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VO0VOP_CHANNEL_BIU, 2, 1),
+
+ /* SOFTRST_CON06 */
+ RK3576_CRU_RESET_OFFSET(SRST_BISRINTF, 6, 2),
+
+ /* SOFTRST_CON07 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_AUDIO_BIU, 7, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_2CH_0, 7, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_2CH_1, 7, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_4CH_0, 7, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_ASRC_4CH_1, 7, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_ASRC_2CH_0, 7, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_ASRC_2CH_1, 7, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_ASRC_4CH_0, 7, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_ASRC_4CH_1, 7, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI0_8CH, 7, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI0_8CH, 7, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_RX0, 7, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_RX0, 7, 15),
+
+ /* SOFTRST_CON08 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_RX1, 8, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_RX1, 8, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI1_8CH, 8, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI1_8CH, 8, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI2_2CH, 8, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI2_2CH, 8, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI3_2CH, 8, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI3_2CH, 8, 14),
+
+ /* SOFTRST_CON09 */
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI4_2CH, 9, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI4_2CH, 9, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_H_ACDCDIG_DSM, 9, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_M_ACDCDIG_DSM, 9, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_PDM1, 9, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_PDM1, 9, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_M_PDM1, 9, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX0, 9, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX0, 9, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX1, 9, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX1, 9, 12),
+
+ /* SOFTRST_CON11 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_BUS_BIU, 11, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_BUS_BIU, 11, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CRU, 11, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_CAN0, 11, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_CAN0, 11, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_CAN1, 11, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_CAN1, 11, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_INTMUX2BUS, 11, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VCCIO_IOC, 11, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_H_BUS_BIU, 11, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_KEY_SHIFT, 11, 15),
+
+ /* SOFTRST_CON12 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C1, 12, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C2, 12, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C3, 12, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C4, 12, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C5, 12, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C6, 12, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C7, 12, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C8, 12, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_I2C9, 12, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_P_WDT_BUSMCU, 12, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_T_WDT_BUSMCU, 12, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_A_GIC, 12, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C1, 12, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C2, 12, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C3, 12, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C4, 12, 15),
+
+ /* SOFTRST_CON13 */
+ RK3576_CRU_RESET_OFFSET(SRST_I2C5, 13, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C6, 13, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C7, 13, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C8, 13, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_I2C9, 13, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SARADC, 13, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_SARADC, 13, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_TSADC, 13, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_TSADC, 13, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART0, 13, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART2, 13, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART3, 13, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART4, 13, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART5, 13, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART6, 13, 15),
+
+ /* SOFTRST_CON14 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART7, 14, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART8, 14, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART9, 14, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART10, 14, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UART11, 14, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART0, 14, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART2, 14, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART3, 14, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART4, 14, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART5, 14, 15),
+
+ /* SOFTRST_CON15 */
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART6, 15, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART7, 15, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART8, 15, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART9, 15, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART10, 15, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_S_UART11, 15, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SPI0, 15, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SPI1, 15, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SPI2, 15, 15),
+
+ /* SOFTRST_CON16 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_SPI3, 16, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SPI4, 16, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_SPI0, 16, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_SPI1, 16, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_SPI2, 16, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_SPI3, 16, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_SPI4, 16, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_P_WDT0, 16, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_T_WDT0, 16, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SYS_GRF, 16, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PWM1, 16, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_PWM1, 16, 11),
+
+ /* SOFTRST_CON17 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_BUSTIMER0, 17, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_BUSTIMER1, 17, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER0, 17, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER1, 17, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER2, 17, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER3, 17, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER4, 17, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER5, 17, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_P_BUSIOC, 17, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_P_MAILBOX0, 17, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GPIO1, 17, 15),
+
+ /* SOFTRST_CON18 */
+ RK3576_CRU_RESET_OFFSET(SRST_GPIO1, 18, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GPIO2, 18, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_GPIO2, 18, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GPIO3, 18, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_GPIO3, 18, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GPIO4, 18, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_GPIO4, 18, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DECOM, 18, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DECOM, 18, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_D_DECOM, 18, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER6, 18, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER7, 18, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER8, 18, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER9, 18, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER10, 18, 15),
+
+ /* SOFTRST_CON19 */
+ RK3576_CRU_RESET_OFFSET(SRST_TIMER11, 19, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DMAC0, 19, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DMAC1, 19, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DMAC2, 19, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SPINLOCK, 19, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_REF_PVTPLL_BUS, 19, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_I3C0, 19, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_I3C1, 19, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_H_BUS_CM0_BIU, 19, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_F_BUS_CM0_CORE, 19, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_T_BUS_CM0_JTAG, 19, 13),
+
+ /* SOFTRST_CON20 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_INTMUX2PMU, 20, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_INTMUX2DDR, 20, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_BUS, 20, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PWM2, 20, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_PWM2, 20, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_FREQ_PWM1, 20, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_COUNTER_PWM1, 20, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_I3C0, 20, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_I3C1, 20, 13),
+
+ /* SOFTRST_CON21 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH0, 21, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_BIU, 21, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH0, 21, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH0, 21, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_BIU, 21, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_DFI_CH0, 21, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_MON_CH0, 21, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_HWLP_CH0, 21, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_MON_CH1, 21, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_HWLP_CH1, 21, 15),
+
+ /* SOFTRST_CON22 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_UPCTL_CH1, 22, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_TM_DDR_MON_CH1, 22, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_DFI_CH1, 22, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR01_MSCH0, 22, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR01_MSCH1, 22, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_MON_CH1, 22, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_SCRAMBLE_CH0, 22, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_SCRAMBLE_CH1, 22, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_P_AHB2APB, 22, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_AHB2APB, 22, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_H_DDR_BIU, 22, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_F_DDR_CM0_CORE, 22, 15),
+
+ /* SOFTRST_CON23 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR01_MSCH0, 23, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR01_MSCH1, 23, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_TIMER0, 23, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_TIMER1, 23, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_T_WDT_DDR, 23, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_P_WDT, 23, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_TIMER, 23, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_T_DDR_CM0_JTAG, 23, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DDR_GRF, 23, 11),
+
+ /* SOFTRST_CON25 */
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH0, 25, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_0_CH0, 25, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_1_CH0, 25, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_2_CH0, 25, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_3_CH0, 25, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_4_CH0, 25, 6),
+
+ /* SOFTRST_CON26 */
+ RK3576_CRU_RESET_OFFSET(SRST_DDR_UPCTL_CH1, 26, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_0_CH1, 26, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_1_CH1, 26, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_2_CH1, 26, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_3_CH1, 26, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_UPCTL_4_CH1, 26, 6),
+
+ /* SOFTRST_CON27 */
+ RK3576_CRU_RESET_OFFSET(SRST_REF_PVTPLL_DDR, 27, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_DDR, 27, 1),
+
+ /* SOFTRST_CON28 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKNN0, 28, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKNN0_BIU, 28, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_L_RKNN0_BIU, 28, 12),
+
+ /* SOFTRST_CON29 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKNN1, 29, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKNN1_BIU, 29, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_L_RKNN1_BIU, 29, 3),
+
+ /* SOFTRST_CON31 */
+ RK3576_CRU_RESET_OFFSET(SRST_NPU_DAP, 31, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_L_NPUSUBSYS_BIU, 31, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_NPUTOP_BIU, 31, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_NPU_TIMER, 31, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_NPUTIMER0, 31, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_NPUTIMER1, 31, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_P_NPU_WDT, 31, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_T_NPU_WDT, 31, 15),
+
+ /* SOFTRST_CON32 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKNN_CBUF, 32, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RVCORE0, 32, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_NPU_GRF, 32, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_NPU, 32, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_NPU_PVTPLL, 32, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_H_NPU_CM0_BIU, 32, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_F_NPU_CM0_CORE, 32, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_T_NPU_CM0_JTAG, 32, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKNNTOP_BIU, 32, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_H_RKNN_CBUF, 32, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_RKNNTOP_BIU, 32, 13),
+
+ /* SOFTRST_CON33 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_NVM_BIU, 33, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_NVM_BIU, 33, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_S_FSPI, 33, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_H_FSPI, 33, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_C_EMMC, 33, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_H_EMMC, 33, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_A_EMMC, 33, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_B_EMMC, 33, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_T_EMMC, 33, 12),
+
+ /* SOFTRST_CON34 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_GRF, 34, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PHP_BIU, 34, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_PHP_BIU, 34, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PCIE0, 34, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_PCIE0_POWER_UP, 34, 15),
+
+ /* SOFTRST_CON35 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_USB3OTG1, 35, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_MMU0, 35, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SLV_MMU0, 35, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_A_MMU1, 35, 14),
+
+ /* SOFTRST_CON36 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_SLV_MMU1, 36, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PCIE1, 36, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_PCIE1_POWER_UP, 36, 9),
+
+ /* SOFTRST_CON37 */
+ RK3576_CRU_RESET_OFFSET(SRST_RXOOB0, 37, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_RXOOB1, 37, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_PMALIVE0, 37, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_PMALIVE1, 37, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SATA0, 37, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SATA1, 37, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_ASIC1, 37, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_ASIC0, 37, 7),
+
+ /* SOFTRST_CON40 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_CSIDPHY1, 40, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_SCAN_CSIDPHY1, 40, 3),
+
+ /* SOFTRST_CON42 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_SDGMAC_GRF, 42, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SDGMAC_BIU, 42, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SDGMAC_BIU, 42, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SDGMAC_BIU, 42, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_GMAC0, 42, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_A_GMAC1, 42, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GMAC0, 42, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GMAC1, 42, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SDIO, 42, 12),
+
+ /* SOFTRST_CON43 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_SDMMC0, 43, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_S_FSPI1, 43, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_H_FSPI1, 43, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DSMC_BIU, 43, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DSMC, 43, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DSMC, 43, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_H_HSGPIO, 43, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_HSGPIO, 43, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_A_HSGPIO, 43, 13),
+
+ /* SOFTRST_CON45 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_RKVDEC, 45, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_H_RKVDEC_BIU, 45, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RKVDEC_BIU, 45, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_RKVDEC_HEVC_CA, 45, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_RKVDEC_CORE, 45, 9),
+
+ /* SOFTRST_CON47 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_USB_BIU, 47, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_P_USBUFS_BIU, 47, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_USB3OTG0, 47, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_UFS_BIU, 47, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_A_MMU2, 47, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_A_SLV_MMU2, 47, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_A_UFS_SYS, 47, 15),
+
+ /* SOFTRST_CON48 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_UFS, 48, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_USBUFS_GRF, 48, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_UFS_GRF, 48, 2),
+
+ /* SOFTRST_CON49 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_VPU_BIU, 49, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_JPEG_BIU, 49, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RGA_BIU, 49, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VDPP_BIU, 49, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_A_EBC_BIU, 49, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_RGA2E_0, 49, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RGA2E_0, 49, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_CORE_RGA2E_0, 49, 15),
+
+ /* SOFTRST_CON50 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_JPEG, 50, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_H_JPEG, 50, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VDPP, 50, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VDPP, 50, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_CORE_VDPP, 50, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_H_RGA2E_1, 50, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_RGA2E_1, 50, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_CORE_RGA2E_1, 50, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_EBC, 50, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_A_EBC, 50, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_D_EBC, 50, 12),
+
+ /* SOFTRST_CON51 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_VEPU0_BIU, 51, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VEPU0_BIU, 51, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VEPU0, 51, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VEPU0, 51, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_VEPU0_CORE, 51, 6),
+
+ /* SOFTRST_CON53 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_VI_BIU, 53, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VI_BIU, 53, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VI_BIU, 53, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_D_VICAP, 53, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VICAP, 53, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VICAP, 53, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_ISP0, 53, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_ISP0_VICAP, 53, 11),
+
+ /* SOFTRST_CON54 */
+ RK3576_CRU_RESET_OFFSET(SRST_CORE_VPSS, 54, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_0, 54, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_1, 54, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_2, 54, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_3, 54, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CSI_HOST_4, 54, 8),
+
+ /* SOFTRST_CON59 */
+ RK3576_CRU_RESET_OFFSET(SRST_CIFIN, 59, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_VICAP_I0CLK, 59, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_VICAP_I1CLK, 59, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_VICAP_I2CLK, 59, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_VICAP_I3CLK, 59, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_VICAP_I4CLK, 59, 5),
+
+ /* SOFTRST_CON61 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_VOP_BIU, 61, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VOP2_BIU, 61, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VOP_BIU, 61, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VOP_BIU, 61, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VOP, 61, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VOP, 61, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_D_VP0, 61, 13),
+
+ /* SOFTRST_CON62 */
+ RK3576_CRU_RESET_OFFSET(SRST_D_VP1, 62, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_D_VP2, 62, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VOP2_BIU, 62, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VOPGRF, 62, 3),
+
+ /* SOFTRST_CON63 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_VO0_BIU, 63, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VO0_BIU, 63, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_A_HDCP0_BIU, 63, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VO0_GRF, 63, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_A_HDCP0, 63, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_HDCP0, 63, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_HDCP0, 63, 14),
+
+ /* SOFTRST_CON64 */
+ RK3576_CRU_RESET_OFFSET(SRST_P_DSIHOST0, 64, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_DSIHOST0, 64, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_P_HDMITX0, 64, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_HDMITX0_REF, 64, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_EDP0, 64, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_EDP0_24M, 64, 14),
+
+ /* SOFTRST_CON65 */
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI5_8CH, 65, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI5_8CH, 65, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI6_8CH, 65, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI6_8CH, 65, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX2, 65, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX2, 65, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_RX2, 65, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_RX2, 65, 15),
+
+ /* SOFTRST_CON66 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI8_8CH, 66, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI8_8CH, 66, 2),
+
+ /* SOFTRST_CON67 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_VO1_BIU, 67, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VO1_BIU, 67, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI7_8CH, 67, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI7_8CH, 67, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX3, 67, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX4, 67, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SPDIF_TX5, 67, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX3, 67, 14),
+
+ /* SOFTRST_CON68 */
+ RK3576_CRU_RESET_OFFSET(SRST_DP0, 68, 0),
+ RK3576_CRU_RESET_OFFSET(SRST_P_VO1_GRF, 68, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_A_HDCP1_BIU, 68, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_HDCP1, 68, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_H_HDCP1, 68, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_HDCP1, 68, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_H_SAI9_8CH, 68, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SAI9_8CH, 68, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX4, 68, 12),
+ RK3576_CRU_RESET_OFFSET(SRST_M_SPDIF_TX5, 68, 13),
+
+ /* SOFTRST_CON69 */
+ RK3576_CRU_RESET_OFFSET(SRST_GPU, 69, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_S_GPU_BIU, 69, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_M0_GPU_BIU, 69, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GPU_BIU, 69, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_GPU_GRF, 69, 13),
+ RK3576_CRU_RESET_OFFSET(SRST_GPU_PVTPLL, 69, 14),
+ RK3576_CRU_RESET_OFFSET(SRST_P_PVTPLL_GPU, 69, 15),
+
+ /* SOFTRST_CON72 */
+ RK3576_CRU_RESET_OFFSET(SRST_A_CENTER_BIU, 72, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DMA2DDR, 72, 5),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_SHAREMEM, 72, 6),
+ RK3576_CRU_RESET_OFFSET(SRST_A_DDR_SHAREMEM_BIU, 72, 7),
+ RK3576_CRU_RESET_OFFSET(SRST_H_CENTER_BIU, 72, 8),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CENTER_GRF, 72, 9),
+ RK3576_CRU_RESET_OFFSET(SRST_P_DMA2DDR, 72, 10),
+ RK3576_CRU_RESET_OFFSET(SRST_P_SHAREMEM, 72, 11),
+ RK3576_CRU_RESET_OFFSET(SRST_P_CENTER_BIU, 72, 12),
+
+ /* SOFTRST_CON75 */
+ RK3576_CRU_RESET_OFFSET(SRST_LINKSYM_HDMITXPHY0, 75, 1),
+
+ /* SOFTRST_CON78 */
+ RK3576_CRU_RESET_OFFSET(SRST_DP0_PIXELCLK, 78, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_PHY_DP0_TX, 78, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_DP1_PIXELCLK, 78, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_DP2_PIXELCLK, 78, 4),
+
+ /* SOFTRST_CON79 */
+ RK3576_CRU_RESET_OFFSET(SRST_H_VEPU1_BIU, 79, 1),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VEPU1_BIU, 79, 2),
+ RK3576_CRU_RESET_OFFSET(SRST_H_VEPU1, 79, 3),
+ RK3576_CRU_RESET_OFFSET(SRST_A_VEPU1, 79, 4),
+ RK3576_CRU_RESET_OFFSET(SRST_VEPU1_CORE, 79, 5),
+
+ /* PPLL_SOFTRST_CON00 */
+ RK3576_PHPCRU_RESET_OFFSET(SRST_P_PHPPHY_CRU, 0, 1),
+ RK3576_PHPCRU_RESET_OFFSET(SRST_P_APB2ASB_SLV_CHIP_TOP, 0, 3),
+ RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY0, 0, 5),
+ RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY0_GRF, 0, 6),
+ RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY1, 0, 7),
+ RK3576_PHPCRU_RESET_OFFSET(SRST_P_PCIE2_COMBOPHY1_GRF, 0, 8),
+
+ /* PPLL_SOFTRST_CON01 */
+ RK3576_PHPCRU_RESET_OFFSET(SRST_PCIE0_PIPE_PHY, 1, 5),
+ RK3576_PHPCRU_RESET_OFFSET(SRST_PCIE1_PIPE_PHY, 1, 8),
+
+ /* SECURENS_SOFTRST_CON00 */
+ RK3576_SECURENSCRU_RESET_OFFSET(SRST_H_CRYPTO_NS, 0, 3),
+ RK3576_SECURENSCRU_RESET_OFFSET(SRST_H_TRNG_NS, 0, 4),
+ RK3576_SECURENSCRU_RESET_OFFSET(SRST_P_OTPC_NS, 0, 8),
+ RK3576_SECURENSCRU_RESET_OFFSET(SRST_OTPC_NS, 0, 9),
+
+ /* PMU1_SOFTRST_CON00 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_HDPTX_GRF, 0, 0),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_HDPTX_APB, 0, 1),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_MIPI_DCPHY, 0, 2),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_DCPHY_GRF, 0, 3),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_BOT0_APB2ASB, 0, 4),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_BOT1_APB2ASB, 0, 5),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_USB2DEBUG, 0, 6),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_CSIPHY_GRF, 0, 7),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_CSIPHY, 0, 8),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBPHY_GRF_0, 0, 9),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBPHY_GRF_1, 0, 10),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBDP_GRF, 0, 11),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_USBDPPHY, 0, 12),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_INIT, 0, 15),
+
+ /* PMU1_SOFTRST_CON01 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_CMN, 1, 0),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_LANE, 1, 1),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_USBDP_COMBO_PHY_PCS, 1, 2),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_M_MIPI_DCPHY, 1, 3),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_S_MIPI_DCPHY, 1, 4),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_SCAN_CSIPHY, 1, 5),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_VCCIO6_IOC, 1, 6),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_0, 1, 7),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_OTGPHY_1, 1, 8),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_HDPTX_INIT, 1, 9),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_HDPTX_CMN, 1, 10),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_HDPTX_LANE, 1, 11),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_HDMITXHDP, 1, 13),
+
+ /* PMU1_SOFTRST_CON02 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_MPHY_INIT, 2, 0),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_MPHY_GRF, 2, 1),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_VCCIO7_IOC, 2, 3),
+
+ /* PMU1_SOFTRST_CON03 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_H_PMU1_BIU, 3, 9),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_NIU, 3, 10),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_H_PMU_CM0_BIU, 3, 11),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_PMU_CM0_CORE, 3, 12),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_PMU_CM0_JTAG, 3, 13),
+
+ /* PMU1_SOFTRST_CON04 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_CRU_PMU1, 4, 1),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_GRF, 4, 3),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1_IOC, 4, 4),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1WDT, 4, 5),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_T_PMU1WDT, 4, 6),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMUTIMER, 4, 7),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_PMUTIMER0, 4, 9),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_PMUTIMER1, 4, 10),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU1PWM, 4, 11),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_PMU1PWM, 4, 12),
+
+ /* PMU1_SOFTRST_CON05 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_I2C0, 5, 1),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_I2C0, 5, 2),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_S_UART1, 5, 5),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_UART1, 5, 6),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_PDM0, 5, 13),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_H_PDM0, 5, 15),
+
+ /* PMU1_SOFTRST_CON06 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_M_PDM0, 6, 0),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_H_VAD, 6, 1),
+
+ /* PMU1_SOFTRST_CON07 */
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU0GRF, 7, 4),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_PMU0IOC, 7, 5),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_P_GPIO0, 7, 6),
+ RK3576_PMU1CRU_RESET_OFFSET(SRST_DB_GPIO0, 7, 7),
+};
+
+int rk3576_reset_bind_lut(struct udevice *pdev, u32 reg_offset, u32 reg_number)
+{
+ return rockchip_reset_bind_lut(pdev, rk3576_register_offset,
+ reg_offset, reg_number);
+}
diff --git a/drivers/rng/msm_rng.c b/drivers/rng/msm_rng.c
index f790d3b60f9..aab602c5ed0 100644
--- a/drivers/rng/msm_rng.c
+++ b/drivers/rng/msm_rng.c
@@ -44,6 +44,11 @@ static int msm_rng_read(struct udevice *dev, void *data, size_t len)
u32 *retdata = data;
size_t maxsize;
u32 val;
+ int ret;
+
+ ret = clk_enable(&priv->clk);
+ if (ret < 0)
+ return ret;
/* calculate max size bytes to transfer back to caller */
maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len);
@@ -66,6 +71,8 @@ static int msm_rng_read(struct udevice *dev, void *data, size_t len)
break;
} while (currsize < maxsize);
+ clk_disable(&priv->clk);
+
return 0;
}
@@ -76,7 +83,7 @@ static int msm_rng_enable(struct msm_rng_priv *priv, int enable)
if (enable) {
/* Enable PRNG only if it is not already enabled */
val = readl_relaxed(priv->base + PRNG_CONFIG);
- if (val & PRNG_CONFIG_HW_ENABLE) {
+ if (!(val & PRNG_CONFIG_HW_ENABLE)) {
val = readl_relaxed(priv->base + PRNG_LFSR_CFG);
val &= ~PRNG_LFSR_CFG_MASK;
val |= PRNG_LFSR_CFG_CLOCKS;
@@ -118,7 +125,9 @@ static int msm_rng_probe(struct udevice *dev)
if (ret < 0)
return ret;
- return msm_rng_enable(priv, 1);
+ ret = msm_rng_enable(priv, 1);
+ clk_disable(&priv->clk);
+ return ret;
}
static int msm_rng_remove(struct udevice *dev)
diff --git a/drivers/rng/rockchip_rng.c b/drivers/rng/rockchip_rng.c
index 2426648fbd5..d854ea90044 100644
--- a/drivers/rng/rockchip_rng.c
+++ b/drivers/rng/rockchip_rng.c
@@ -70,6 +70,27 @@
#define TRNG_v1_VERSION_CODE 0x46BC
/* end of TRNG V1 register define */
+/* start of RKRNG register define */
+#define RKRNG_CTRL 0x0010
+#define RKRNG_CTRL_INST_REQ BIT(0)
+#define RKRNG_CTRL_RESEED_REQ BIT(1)
+#define RKRNG_CTRL_TEST_REQ BIT(2)
+#define RKRNG_CTRL_SW_DRNG_REQ BIT(3)
+#define RKRNG_CTRL_SW_TRNG_REQ BIT(4)
+
+#define RKRNG_STATE 0x0014
+#define RKRNG_STATE_INST_ACK BIT(0)
+#define RKRNG_STATE_RESEED_ACK BIT(1)
+#define RKRNG_STATE_TEST_ACK BIT(2)
+#define RKRNG_STATE_SW_DRNG_ACK BIT(3)
+#define RKRNG_STATE_SW_TRNG_ACK BIT(4)
+
+/* DRNG_DATA_0 ~ DNG_DATA_7 */
+#define RKRNG_DRNG_DATA_0 0x0070
+#define RKRNG_DRNG_DATA_7 0x008C
+
+/* end of RKRNG register define */
+
#define RK_RNG_TIME_OUT 50000 /* max 50ms */
#define trng_write(pdata, pos, val) writel(val, (pdata)->base + (pos))
@@ -228,6 +249,49 @@ exit:
return retval;
}
+static int rkrng_init(struct udevice *dev)
+{
+ struct rk_rng_plat *pdata = dev_get_priv(dev);
+ u32 reg = 0;
+
+ rk_clrreg(pdata->base + RKRNG_CTRL, 0xffff);
+
+ reg = trng_read(pdata, RKRNG_STATE);
+ trng_write(pdata, RKRNG_STATE, reg);
+
+ return 0;
+}
+
+static int rkrng_rng_read(struct udevice *dev, void *data, size_t len)
+{
+ struct rk_rng_plat *pdata = dev_get_priv(dev);
+ u32 reg = 0;
+ int retval;
+
+ if (len > RK_HW_RNG_MAX)
+ return -EINVAL;
+
+ reg = RKRNG_CTRL_SW_DRNG_REQ;
+
+ rk_clrsetreg(pdata->base + RKRNG_CTRL, 0xffff, reg);
+
+ retval = readl_poll_timeout(pdata->base + RKRNG_STATE, reg,
+ (reg & RKRNG_STATE_SW_DRNG_ACK),
+ RK_RNG_TIME_OUT);
+ if (retval)
+ goto exit;
+
+ trng_write(pdata, RKRNG_STATE, reg);
+
+ rk_rng_read_regs(pdata->base + RKRNG_DRNG_DATA_0, data, len);
+
+exit:
+ /* close TRNG */
+ rk_clrreg(pdata->base + RKRNG_CTRL, 0xffff);
+
+ return retval;
+}
+
static int rockchip_rng_read(struct udevice *dev, void *data, size_t len)
{
unsigned char *buf = data;
@@ -295,6 +359,11 @@ static const struct rk_rng_soc_data rk_trngv1_soc_data = {
.rk_rng_read = rk_trngv1_rng_read,
};
+static const struct rk_rng_soc_data rkrng_soc_data = {
+ .rk_rng_init = rkrng_init,
+ .rk_rng_read = rkrng_rng_read,
+};
+
static const struct dm_rng_ops rockchip_rng_ops = {
.read = rockchip_rng_read,
};
@@ -313,13 +382,21 @@ static const struct udevice_id rockchip_rng_match[] = {
.data = (ulong)&rk_cryptov1_soc_data,
},
{
+ .compatible = "rockchip,rk3568-rng",
+ .data = (ulong)&rk_cryptov2_soc_data,
+ },
+ {
.compatible = "rockchip,cryptov2-rng",
.data = (ulong)&rk_cryptov2_soc_data,
},
{
- .compatible = "rockchip,trngv1",
+ .compatible = "rockchip,rk3588-rng",
.data = (ulong)&rk_trngv1_soc_data,
},
+ {
+ .compatible = "rockchip,rkrng",
+ .data = (ulong)&rkrng_soc_data,
+ },
{},
};
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 c4f4a8d78df..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
@@ -789,6 +792,12 @@ config SPL_SYS_NS16550_SERIAL
default y if SYS_NS16550_SERIAL || ARCH_SUNXI || ARCH_OMAP2PLUS
select SYS_NS16550
+config TPL_SYS_NS16550_SERIAL
+ bool "NS16550 UART or compatible legacy driver in TPL"
+ depends on TPL && !TPL_DM_SERIAL
+ default y if SPL_SYS_NS16550_SERIAL
+ select SYS_NS16550
+
config SYS_NS16550
bool "NS16550 UART or compatible"
help
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index ebe692a9963..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
@@ -63,7 +63,4 @@ obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
obj-$(CONFIG_XTENSA_SEMIHOSTING_SERIAL) += serial_xtensa_semihosting.o
obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
-ifndef CONFIG_XPL_BUILD
-obj-$(CONFIG_USB_TTY) += usbtty.o
-endif
obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c
index 0e267d097c5..4f7de3ea215 100644
--- a/drivers/serial/ns16550.c
+++ b/drivers/serial/ns16550.c
@@ -112,9 +112,9 @@ static void serial_out_dynamic(struct ns16550_plat *plat, u8 *addr,
} else if (plat->reg_width == 4) {
if (plat->flags & NS16550_FLAG_ENDIAN) {
if (plat->flags & NS16550_FLAG_BE)
- out_be32(addr, value);
+ out_be32((u32 *)addr, value);
else
- out_le32(addr, value);
+ out_le32((u32 *)addr, value);
} else {
writel(value, addr);
}
@@ -132,9 +132,9 @@ static int serial_in_dynamic(struct ns16550_plat *plat, u8 *addr)
} else if (plat->reg_width == 4) {
if (plat->flags & NS16550_FLAG_ENDIAN) {
if (plat->flags & NS16550_FLAG_BE)
- return in_be32(addr);
+ return in_be32((u32 *)addr);
else
- return in_le32(addr);
+ return in_le32((u32 *)addr);
} else {
return readl(addr);
}
@@ -294,13 +294,9 @@ void ns16550_putc(struct ns16550 *com_port, char c)
#if !CONFIG_IS_ENABLED(NS16550_MIN_FUNCTIONS)
char ns16550_getc(struct ns16550 *com_port)
{
- while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) {
-#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_TTY)
- extern void usbtty_poll(void);
- usbtty_poll();
-#endif
+ while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0)
schedule();
- }
+
return serial_in(&com_port->rbr);
}
diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c
index 77a1558db68..cc0491bc3c8 100644
--- a/drivers/serial/sandbox.c
+++ b/drivers/serial/sandbox.c
@@ -63,7 +63,7 @@ static int sandbox_serial_probe(struct udevice *dev)
if (state->term_raw != STATE_TERM_RAW)
disable_ctrlc(1);
- membuff_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf));
+ membuf_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf));
return 0;
}
@@ -138,15 +138,15 @@ static int sandbox_serial_pending(struct udevice *dev, bool input)
return 0;
os_usleep(100);
- avail = membuff_putraw(&priv->buf, 100, false, &data);
+ avail = membuf_putraw(&priv->buf, 100, false, &data);
if (!avail)
return 1; /* buffer full */
count = os_read(0, data, avail);
if (count > 0)
- membuff_putraw(&priv->buf, count, true, &data);
+ membuf_putraw(&priv->buf, count, true, &data);
- return membuff_avail(&priv->buf);
+ return membuf_avail(&priv->buf);
}
static int sandbox_serial_getc(struct udevice *dev)
@@ -156,7 +156,7 @@ static int sandbox_serial_getc(struct udevice *dev)
if (!sandbox_serial_pending(dev, true))
return -EAGAIN; /* buffer empty */
- return membuff_getbyte(&priv->buf);
+ return membuf_getbyte(&priv->buf);
}
#ifdef CONFIG_DEBUG_UART_SANDBOX
diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c
index c5fd740be4d..28f4435d01d 100644
--- a/drivers/serial/serial_mxc.c
+++ b/drivers/serial/serial_mxc.c
@@ -3,6 +3,7 @@
* (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>
*/
+#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <watchdog.h>
@@ -312,7 +313,17 @@ int mxc_serial_setbrg(struct udevice *dev, int baudrate)
static int mxc_serial_probe(struct udevice *dev)
{
struct mxc_serial_plat *plat = dev_get_plat(dev);
+#if CONFIG_IS_ENABLED(CLK_CCF)
+ int ret;
+ ret = clk_get_bulk(dev, &plat->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_enable_bulk(&plat->clks);
+ if (ret)
+ return ret;
+#endif
_mxc_serial_init(plat->reg, plat->use_dte);
return 0;
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index 1ee58142b3f..1675a9cb9d1 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -299,13 +299,19 @@ static inline struct stm32_uart_info *_debug_uart_info(void)
static inline void _debug_uart_init(void)
{
- void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
- struct stm32_uart_info *uart_info = _debug_uart_info();
+ void __maybe_unused __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
+ struct stm32_uart_info *uart_info __maybe_unused = _debug_uart_info();
- _stm32_serial_init(base, uart_info);
- _stm32_serial_setbrg(base, uart_info,
- CONFIG_DEBUG_UART_CLOCK,
- CONFIG_BAUDRATE);
+ /*
+ * debug_uart_init() is only usable when SPL_BUILD is enabled
+ * (STM32MP1 case only)
+ */
+ if (IS_ENABLED(CONFIG_DEBUG_UART) && IS_ENABLED(CONFIG_SPL_BUILD)) {
+ _stm32_serial_init(base, uart_info);
+ _stm32_serial_setbrg(base, uart_info,
+ CONFIG_DEBUG_UART_CLOCK,
+ CONFIG_BAUDRATE);
+ }
}
static inline void _debug_uart_putc(int c)
diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c
deleted file mode 100644
index b7d77fbb6a9..00000000000
--- a/drivers/serial/usbtty.c
+++ /dev/null
@@ -1,983 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * (C) Copyright 2003
- * Gerry Hamel, geh@ti.com, Texas Instruments
- *
- * (C) Copyright 2006
- * Bryan O'Donoghue, bodonoghue@codehermit.ie
- */
-
-#include <config.h>
-#include <circbuf.h>
-#include <env.h>
-#include <serial.h>
-#include <stdio_dev.h>
-#include <asm/unaligned.h>
-#include "usbtty.h"
-#include "usb_cdc_acm.h"
-#include "usbdescriptors.h"
-
-#ifdef DEBUG
-#define TTYDBG(fmt,args...)\
- serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args)
-#else
-#define TTYDBG(fmt,args...) do{}while(0)
-#endif
-
-#if 1
-#define TTYERR(fmt,args...)\
- serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\
- __LINE__,##args)
-#else
-#define TTYERR(fmt,args...) do{}while(0)
-#endif
-
-/*
- * Defines
- */
-#define NUM_CONFIGS 1
-#define MAX_INTERFACES 2
-#define NUM_ENDPOINTS 3
-#define ACM_TX_ENDPOINT 3
-#define ACM_RX_ENDPOINT 2
-#define GSERIAL_TX_ENDPOINT 2
-#define GSERIAL_RX_ENDPOINT 1
-#define NUM_ACM_INTERFACES 2
-#define NUM_GSERIAL_INTERFACES 1
-#define CFG_USBD_DATA_INTERFACE_STR "Bulk Data Interface"
-#define CFG_USBD_CTRL_INTERFACE_STR "Control Interface"
-
-/*
- * Buffers to hold input and output data
- */
-#define USBTTY_BUFFER_SIZE 2048
-static circbuf_t usbtty_input;
-static circbuf_t usbtty_output;
-
-/*
- * Instance variables
- */
-static struct stdio_dev usbttydev;
-static struct usb_device_instance device_instance[1];
-static struct usb_bus_instance bus_instance[1];
-static struct usb_configuration_instance config_instance[NUM_CONFIGS];
-static struct usb_interface_instance interface_instance[MAX_INTERFACES];
-static struct usb_alternate_instance alternate_instance[MAX_INTERFACES];
-/* one extra for control endpoint */
-static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1];
-
-/*
- * Global flag
- */
-int usbtty_configured_flag = 0;
-
-/*
- * Serial number
- */
-static char serial_number[16];
-
-/*
- * Descriptors, Strings, Local variables.
- */
-
-/* defined and used by gadget/ep0.c */
-extern struct usb_string_descriptor **usb_strings;
-
-/* Indicies, References */
-static unsigned short rx_endpoint = 0;
-static unsigned short tx_endpoint = 0;
-static unsigned short interface_count = 0;
-static struct usb_string_descriptor *usbtty_string_table[STR_COUNT];
-
-/* USB Descriptor Strings */
-static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
-static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)];
-static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)];
-static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)];
-static u8 wstrConfiguration[2 + 2*(sizeof(CFG_USBD_CONFIGURATION_STR)-1)];
-static u8 wstrDataInterface[2 + 2*(sizeof(CFG_USBD_DATA_INTERFACE_STR)-1)];
-static u8 wstrCtrlInterface[2 + 2*(sizeof(CFG_USBD_DATA_INTERFACE_STR)-1)];
-
-/* Standard USB Data Structures */
-static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES];
-static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];
-static struct usb_configuration_descriptor *configuration_descriptor = 0;
-static struct usb_device_descriptor device_descriptor = {
- .bLength = sizeof(struct usb_device_descriptor),
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(USB_BCD_VERSION),
- .bDeviceSubClass = 0x00,
- .bDeviceProtocol = 0x00,
- .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
- .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID),
- .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE),
- .iManufacturer = STR_MANUFACTURER,
- .iProduct = STR_PRODUCT,
- .iSerialNumber = STR_SERIAL,
- .bNumConfigurations = NUM_CONFIGS
-};
-
-/*
- * Static CDC ACM specific descriptors
- */
-
-struct acm_config_desc {
- struct usb_configuration_descriptor configuration_desc;
-
- /* Master Interface */
- struct usb_interface_descriptor interface_desc;
-
- struct usb_class_header_function_descriptor usb_class_header;
- struct usb_class_call_management_descriptor usb_class_call_mgt;
- struct usb_class_abstract_control_descriptor usb_class_acm;
- struct usb_class_union_function_descriptor usb_class_union;
- struct usb_endpoint_descriptor notification_endpoint;
-
- /* Slave Interface */
- struct usb_interface_descriptor data_class_interface;
- struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1];
-} __attribute__((packed));
-
-static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = {
- {
- .configuration_desc ={
- .bLength =
- sizeof(struct usb_configuration_descriptor),
- .bDescriptorType = USB_DT_CONFIG,
- .wTotalLength =
- cpu_to_le16(sizeof(struct acm_config_desc)),
- .bNumInterfaces = NUM_ACM_INTERFACES,
- .bConfigurationValue = 1,
- .iConfiguration = STR_CONFIG,
- .bmAttributes =
- BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
- .bMaxPower = USBTTY_MAXPOWER
- },
- /* Interface 1 */
- .interface_desc = {
- .bLength = sizeof(struct usb_interface_descriptor),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bAlternateSetting = 0,
- .bNumEndpoints = 0x01,
- .bInterfaceClass =
- COMMUNICATIONS_INTERFACE_CLASS_CONTROL,
- .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS,
- .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL,
- .iInterface = STR_CTRL_INTERFACE,
- },
- .usb_class_header = {
- .bFunctionLength =
- sizeof(struct usb_class_header_function_descriptor),
- .bDescriptorType = CS_INTERFACE,
- .bDescriptorSubtype = USB_ST_HEADER,
- .bcdCDC = cpu_to_le16(110),
- },
- .usb_class_call_mgt = {
- .bFunctionLength =
- sizeof(struct usb_class_call_management_descriptor),
- .bDescriptorType = CS_INTERFACE,
- .bDescriptorSubtype = USB_ST_CMF,
- .bmCapabilities = 0x00,
- .bDataInterface = 0x01,
- },
- .usb_class_acm = {
- .bFunctionLength =
- sizeof(struct usb_class_abstract_control_descriptor),
- .bDescriptorType = CS_INTERFACE,
- .bDescriptorSubtype = USB_ST_ACMF,
- .bmCapabilities = 0x00,
- },
- .usb_class_union = {
- .bFunctionLength =
- sizeof(struct usb_class_union_function_descriptor),
- .bDescriptorType = CS_INTERFACE,
- .bDescriptorSubtype = USB_ST_UF,
- .bMasterInterface = 0x00,
- .bSlaveInterface0 = 0x01,
- },
- .notification_endpoint = {
- .bLength =
- sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize
- = cpu_to_le16(CFG_USBD_SERIAL_INT_PKTSIZE),
- .bInterval = 0xFF,
- },
-
- /* Interface 2 */
- .data_class_interface = {
- .bLength =
- sizeof(struct usb_interface_descriptor),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0x01,
- .bAlternateSetting = 0x00,
- .bNumEndpoints = 0x02,
- .bInterfaceClass =
- COMMUNICATIONS_INTERFACE_CLASS_DATA,
- .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE,
- .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE,
- .iInterface = STR_DATA_INTERFACE,
- },
- .data_endpoints = {
- {
- .bLength =
- sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT,
- .bmAttributes =
- USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize =
- cpu_to_le16(CFG_USBD_SERIAL_BULK_PKTSIZE),
- .bInterval = 0xFF,
- },
- {
- .bLength =
- sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN,
- .bmAttributes =
- USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize =
- cpu_to_le16(CFG_USBD_SERIAL_BULK_PKTSIZE),
- .bInterval = 0xFF,
- },
- },
- },
-};
-
-static struct rs232_emu rs232_desc={
- .dter = 115200,
- .stop_bits = 0x00,
- .parity = 0x00,
- .data_bits = 0x08
-};
-
-/*
- * Static Generic Serial specific data
- */
-
-struct gserial_config_desc {
-
- struct usb_configuration_descriptor configuration_desc;
- struct usb_interface_descriptor interface_desc[NUM_GSERIAL_INTERFACES];
- struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS];
-
-} __attribute__((packed));
-
-static struct gserial_config_desc
-gserial_configuration_descriptors[NUM_CONFIGS] ={
- {
- .configuration_desc ={
- .bLength = sizeof(struct usb_configuration_descriptor),
- .bDescriptorType = USB_DT_CONFIG,
- .wTotalLength =
- cpu_to_le16(sizeof(struct gserial_config_desc)),
- .bNumInterfaces = NUM_GSERIAL_INTERFACES,
- .bConfigurationValue = 1,
- .iConfiguration = STR_CONFIG,
- .bmAttributes =
- BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
- .bMaxPower = USBTTY_MAXPOWER
- },
- .interface_desc = {
- {
- .bLength =
- sizeof(struct usb_interface_descriptor),
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bAlternateSetting = 0,
- .bNumEndpoints = NUM_ENDPOINTS,
- .bInterfaceClass =
- COMMUNICATIONS_INTERFACE_CLASS_VENDOR,
- .bInterfaceSubClass =
- COMMUNICATIONS_NO_SUBCLASS,
- .bInterfaceProtocol =
- COMMUNICATIONS_NO_PROTOCOL,
- .iInterface = STR_DATA_INTERFACE
- },
- },
- .data_endpoints = {
- {
- .bLength =
- sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize =
- cpu_to_le16(CFG_USBD_SERIAL_OUT_PKTSIZE),
- .bInterval= 0xFF,
- },
- {
- .bLength =
- sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize =
- cpu_to_le16(CFG_USBD_SERIAL_IN_PKTSIZE),
- .bInterval = 0xFF,
- },
- {
- .bLength =
- sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize =
- cpu_to_le16(CFG_USBD_SERIAL_INT_PKTSIZE),
- .bInterval = 0xFF,
- },
- },
- },
-};
-
-/*
- * Static Function Prototypes
- */
-
-static void usbtty_init_strings (void);
-static void usbtty_init_instances (void);
-static void usbtty_init_endpoints (void);
-static void usbtty_init_terminal_type(short type);
-static void usbtty_event_handler (struct usb_device_instance *device,
- usb_device_event_t event, int data);
-static int usbtty_cdc_setup(struct usb_device_request *request,
- struct urb *urb);
-static int usbtty_configured (void);
-static int write_buffer (circbuf_t * buf);
-static int fill_buffer (circbuf_t * buf);
-
-void usbtty_poll (void);
-
-/* utility function for converting char* to wide string used by USB */
-static void str2wide (char *str, u16 * wide)
-{
- int i;
- for (i = 0; i < strlen (str) && str[i]; i++){
- #if defined(__LITTLE_ENDIAN)
- wide[i] = (u16) str[i];
- #elif defined(__BIG_ENDIAN)
- wide[i] = ((u16)(str[i])<<8);
- #else
- #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined"
- #endif
- }
-}
-
-/*
- * Test whether a character is in the RX buffer
- */
-
-int usbtty_tstc(struct stdio_dev *dev)
-{
- struct usb_endpoint_instance *endpoint =
- &endpoint_instance[rx_endpoint];
-
- /* If no input data exists, allow more RX to be accepted */
- if(usbtty_input.size <= 0){
- udc_unset_nak(endpoint->endpoint_address&0x03);
- }
-
- usbtty_poll ();
- return (usbtty_input.size > 0);
-}
-
-/*
- * Read a single byte from the usb client port. Returns 1 on success, 0
- * otherwise. When the function is succesfull, the character read is
- * written into its argument c.
- */
-
-int usbtty_getc(struct stdio_dev *dev)
-{
- char c;
- struct usb_endpoint_instance *endpoint =
- &endpoint_instance[rx_endpoint];
-
- while (usbtty_input.size <= 0) {
- udc_unset_nak(endpoint->endpoint_address&0x03);
- usbtty_poll ();
- }
-
- buf_pop (&usbtty_input, &c, 1);
- udc_set_nak(endpoint->endpoint_address&0x03);
-
- return c;
-}
-
-/*
- * Output a single byte to the usb client port.
- */
-void usbtty_putc(struct stdio_dev *dev, const char c)
-{
- if (!usbtty_configured ())
- return;
-
- /* If \n, also do \r */
- if (c == '\n')
- buf_push (&usbtty_output, "\r", 1);
-
- buf_push(&usbtty_output, &c, 1);
-
- /* Poll at end to handle new data... */
- if ((usbtty_output.size + 2) >= usbtty_output.totalsize) {
- usbtty_poll ();
- }
-}
-
-/* usbtty_puts() helper function for finding the next '\n' in a string */
-static int next_nl_pos (const char *s)
-{
- int i;
-
- for (i = 0; s[i] != '\0'; i++) {
- if (s[i] == '\n')
- return i;
- }
- return i;
-}
-
-/*
- * Output a string to the usb client port - implementing flow control
- */
-
-static void __usbtty_puts (const char *str, int len)
-{
- int maxlen = usbtty_output.totalsize;
- int space, n;
-
- /* break str into chunks < buffer size, if needed */
- while (len > 0) {
- usbtty_poll ();
-
- space = maxlen - usbtty_output.size;
- /* Empty buffer here, if needed, to ensure space... */
- if (space) {
- write_buffer (&usbtty_output);
-
- n = min(space, min(len, maxlen));
- buf_push (&usbtty_output, str, n);
-
- str += n;
- len -= n;
- }
- }
-}
-
-void usbtty_puts(struct stdio_dev *dev, const char *str)
-{
- int n;
- int len;
-
- if (!usbtty_configured ())
- return;
-
- len = strlen (str);
- /* add '\r' for each '\n' */
- while (len > 0) {
- n = next_nl_pos (str);
-
- if (str[n] == '\n') {
- __usbtty_puts(str, n);
- __usbtty_puts("\r\n", 2);
- str += (n + 1);
- len -= (n + 1);
- } else {
- /* No \n found. All done. */
- __usbtty_puts (str, n);
- break;
- }
- }
-
- /* Poll at end to handle new data... */
- usbtty_poll ();
-}
-
-/*
- * Initialize the usb client port.
- *
- */
-int drv_usbtty_init (void)
-{
- int rc;
- char * sn;
- char * tt;
- int snlen;
-
- /* Get serial number */
- sn = env_get("serial#");
- if (!sn)
- sn = "000000000000";
- snlen = strlen(sn);
- if (snlen > sizeof(serial_number) - 1) {
- printf ("Warning: serial number %s is too long (%d > %lu)\n",
- sn, snlen, (ulong)(sizeof(serial_number) - 1));
- snlen = sizeof(serial_number) - 1;
- }
- memcpy (serial_number, sn, snlen);
- serial_number[snlen] = '\0';
-
- /* Decide on which type of UDC device to be.
- */
- tt = env_get("usbtty");
- if (!tt)
- tt = "generic";
- usbtty_init_terminal_type(strcmp(tt,"cdc_acm"));
-
- /* prepare buffers... */
- buf_init (&usbtty_input, USBTTY_BUFFER_SIZE);
- buf_init (&usbtty_output, USBTTY_BUFFER_SIZE);
-
- /* Now, set up USB controller and infrastructure */
- udc_init (); /* Basic USB initialization */
-
- usbtty_init_strings ();
- usbtty_init_instances ();
-
- usbtty_init_endpoints ();
-
- udc_startup_events (device_instance);/* Enable dev, init udc pointers */
- udc_connect (); /* Enable pullup for host detection */
-
- /* Device initialization */
- memset (&usbttydev, 0, sizeof (usbttydev));
-
- strcpy (usbttydev.name, "usbtty");
- usbttydev.ext = 0; /* No extensions */
- usbttydev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_OUTPUT;
- usbttydev.tstc = usbtty_tstc; /* 'tstc' function */
- usbttydev.getc = usbtty_getc; /* 'getc' function */
- usbttydev.putc = usbtty_putc; /* 'putc' function */
- usbttydev.puts = usbtty_puts; /* 'puts' function */
-
- rc = stdio_register (&usbttydev);
-
- return (rc == 0) ? 1 : rc;
-}
-
-static void usbtty_init_strings (void)
-{
- struct usb_string_descriptor *string;
-
- usbtty_string_table[STR_LANG] =
- (struct usb_string_descriptor*)wstrLang;
-
- string = (struct usb_string_descriptor *) wstrManufacturer;
- string->bLength = sizeof(wstrManufacturer);
- string->bDescriptorType = USB_DT_STRING;
- str2wide (CONFIG_USBD_MANUFACTURER, string->wData);
- usbtty_string_table[STR_MANUFACTURER]=string;
-
- string = (struct usb_string_descriptor *) wstrProduct;
- string->bLength = sizeof(wstrProduct);
- string->bDescriptorType = USB_DT_STRING;
- str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData);
- usbtty_string_table[STR_PRODUCT]=string;
-
- string = (struct usb_string_descriptor *) wstrSerial;
- string->bLength = sizeof(serial_number);
- string->bDescriptorType = USB_DT_STRING;
- str2wide (serial_number, string->wData);
- usbtty_string_table[STR_SERIAL]=string;
-
- string = (struct usb_string_descriptor *) wstrConfiguration;
- string->bLength = sizeof(wstrConfiguration);
- string->bDescriptorType = USB_DT_STRING;
- str2wide (CFG_USBD_CONFIGURATION_STR, string->wData);
- usbtty_string_table[STR_CONFIG]=string;
-
- string = (struct usb_string_descriptor *) wstrDataInterface;
- string->bLength = sizeof(wstrDataInterface);
- string->bDescriptorType = USB_DT_STRING;
- str2wide (CFG_USBD_DATA_INTERFACE_STR, string->wData);
- usbtty_string_table[STR_DATA_INTERFACE]=string;
-
- string = (struct usb_string_descriptor *) wstrCtrlInterface;
- string->bLength = sizeof(wstrCtrlInterface);
- string->bDescriptorType = USB_DT_STRING;
- str2wide (CFG_USBD_CTRL_INTERFACE_STR, string->wData);
- usbtty_string_table[STR_CTRL_INTERFACE]=string;
-
- /* Now, initialize the string table for ep0 handling */
- usb_strings = usbtty_string_table;
-}
-
-#define init_wMaxPacketSize(x) le16_to_cpu(get_unaligned(\
- &ep_descriptor_ptrs[(x) - 1]->wMaxPacketSize));
-
-static void usbtty_init_instances (void)
-{
- int i;
-
- /* initialize device instance */
- memset (device_instance, 0, sizeof (struct usb_device_instance));
- device_instance->device_state = STATE_INIT;
- device_instance->device_descriptor = &device_descriptor;
- device_instance->event = usbtty_event_handler;
- device_instance->cdc_recv_setup = usbtty_cdc_setup;
- device_instance->bus = bus_instance;
- device_instance->configurations = NUM_CONFIGS;
- device_instance->configuration_instance_array = config_instance;
-
- /* initialize bus instance */
- memset (bus_instance, 0, sizeof (struct usb_bus_instance));
- bus_instance->device = device_instance;
- bus_instance->endpoint_array = endpoint_instance;
- bus_instance->max_endpoints = 1;
- bus_instance->maxpacketsize = 64;
- bus_instance->serial_number_str = serial_number;
-
- /* configuration instance */
- memset (config_instance, 0,
- sizeof (struct usb_configuration_instance));
- config_instance->interfaces = interface_count;
- config_instance->configuration_descriptor = configuration_descriptor;
- config_instance->interface_instance_array = interface_instance;
-
- /* interface instance */
- memset (interface_instance, 0,
- sizeof (struct usb_interface_instance));
- interface_instance->alternates = 1;
- interface_instance->alternates_instance_array = alternate_instance;
-
- /* alternates instance */
- memset (alternate_instance, 0,
- sizeof (struct usb_alternate_instance));
- alternate_instance->interface_descriptor = interface_descriptors;
- alternate_instance->endpoints = NUM_ENDPOINTS;
- alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs;
-
- /* endpoint instances */
- memset (&endpoint_instance[0], 0,
- sizeof (struct usb_endpoint_instance));
- endpoint_instance[0].endpoint_address = 0;
- endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE;
- endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL;
- endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE;
- endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL;
- udc_setup_ep (device_instance, 0, &endpoint_instance[0]);
-
- for (i = 1; i <= NUM_ENDPOINTS; i++) {
- memset (&endpoint_instance[i], 0,
- sizeof (struct usb_endpoint_instance));
-
- endpoint_instance[i].endpoint_address =
- ep_descriptor_ptrs[i - 1]->bEndpointAddress;
-
- endpoint_instance[i].rcv_attributes =
- ep_descriptor_ptrs[i - 1]->bmAttributes;
-
- endpoint_instance[i].rcv_packetSize = init_wMaxPacketSize(i);
-
- endpoint_instance[i].tx_attributes =
- ep_descriptor_ptrs[i - 1]->bmAttributes;
-
- endpoint_instance[i].tx_packetSize = init_wMaxPacketSize(i);
-
- endpoint_instance[i].tx_attributes =
- ep_descriptor_ptrs[i - 1]->bmAttributes;
-
- urb_link_init (&endpoint_instance[i].rcv);
- urb_link_init (&endpoint_instance[i].rdy);
- urb_link_init (&endpoint_instance[i].tx);
- urb_link_init (&endpoint_instance[i].done);
-
- if (endpoint_instance[i].endpoint_address & USB_DIR_IN)
- endpoint_instance[i].tx_urb =
- usbd_alloc_urb (device_instance,
- &endpoint_instance[i]);
- else
- endpoint_instance[i].rcv_urb =
- usbd_alloc_urb (device_instance,
- &endpoint_instance[i]);
- }
-}
-
-static void usbtty_init_endpoints (void)
-{
- int i;
-
- bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
- for (i = 1; i <= NUM_ENDPOINTS; i++) {
- udc_setup_ep (device_instance, i, &endpoint_instance[i]);
- }
-}
-
-/* usbtty_init_terminal_type
- *
- * Do some late binding for our device type.
- */
-static void usbtty_init_terminal_type(short type)
-{
- switch(type){
- /* CDC ACM */
- case 0:
- /* Assign endpoint descriptors */
- ep_descriptor_ptrs[0] =
- &acm_configuration_descriptors[0].notification_endpoint;
- ep_descriptor_ptrs[1] =
- &acm_configuration_descriptors[0].data_endpoints[0];
- ep_descriptor_ptrs[2] =
- &acm_configuration_descriptors[0].data_endpoints[1];
-
- /* Enumerate Device Descriptor */
- device_descriptor.bDeviceClass =
- COMMUNICATIONS_DEVICE_CLASS;
- device_descriptor.idProduct =
- cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM);
-
- /* Assign endpoint indices */
- tx_endpoint = ACM_TX_ENDPOINT;
- rx_endpoint = ACM_RX_ENDPOINT;
-
- /* Configuration Descriptor */
- configuration_descriptor =
- (struct usb_configuration_descriptor*)
- &acm_configuration_descriptors;
-
- /* Interface count */
- interface_count = NUM_ACM_INTERFACES;
- break;
-
- /* BULK IN/OUT & Default */
- case 1:
- default:
- /* Assign endpoint descriptors */
- ep_descriptor_ptrs[0] =
- &gserial_configuration_descriptors[0].data_endpoints[0];
- ep_descriptor_ptrs[1] =
- &gserial_configuration_descriptors[0].data_endpoints[1];
- ep_descriptor_ptrs[2] =
- &gserial_configuration_descriptors[0].data_endpoints[2];
-
- /* Enumerate Device Descriptor */
- device_descriptor.bDeviceClass = 0xFF;
- device_descriptor.idProduct =
- cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL);
- /* Assign endpoint indices */
- tx_endpoint = GSERIAL_TX_ENDPOINT;
- rx_endpoint = GSERIAL_RX_ENDPOINT;
-
- /* Configuration Descriptor */
- configuration_descriptor =
- (struct usb_configuration_descriptor*)
- &gserial_configuration_descriptors;
-
- /* Interface count */
- interface_count = NUM_GSERIAL_INTERFACES;
- break;
- }
-}
-
-/******************************************************************************/
-
-static struct urb *next_urb (struct usb_device_instance *device,
- struct usb_endpoint_instance *endpoint)
-{
- struct urb *current_urb = NULL;
- int space;
-
- /* If there's a queue, then we should add to the last urb */
- if (!endpoint->tx_queue) {
- current_urb = endpoint->tx_urb;
- } else {
- /* Last urb from tx chain */
- current_urb =
- p2surround (struct urb, link, endpoint->tx.prev);
- }
-
- /* Make sure this one has enough room */
- space = current_urb->buffer_length - current_urb->actual_length;
- if (space > 0) {
- return current_urb;
- } else { /* No space here */
- /* First look at done list */
- current_urb = first_urb_detached (&endpoint->done);
- if (!current_urb) {
- current_urb = usbd_alloc_urb (device, endpoint);
- }
-
- urb_append (&endpoint->tx, current_urb);
- endpoint->tx_queue++;
- }
- return current_urb;
-}
-
-static int write_buffer (circbuf_t * buf)
-{
- if (!usbtty_configured ()) {
- return 0;
- }
-
- struct usb_endpoint_instance *endpoint =
- &endpoint_instance[tx_endpoint];
- struct urb *current_urb = NULL;
-
- /* TX data still exists - send it now
- */
- if(endpoint->sent < endpoint->tx_urb->actual_length){
- if(udc_endpoint_write (endpoint)){
- /* Write pre-empted by RX */
- return -1;
- }
- }
-
- if (buf->size) {
- char *dest;
-
- int space_avail;
- int popnum, popped;
- int total = 0;
-
- /* Break buffer into urb sized pieces,
- * and link each to the endpoint
- */
- while (buf->size > 0) {
-
- current_urb = next_urb (device_instance, endpoint);
-
- dest = (char*)current_urb->buffer +
- current_urb->actual_length;
-
- space_avail =
- current_urb->buffer_length -
- current_urb->actual_length;
- popnum = min(space_avail, (int)buf->size);
- if (popnum == 0)
- break;
-
- popped = buf_pop (buf, dest, popnum);
- if (popped == 0)
- break;
- current_urb->actual_length += popped;
- total += popped;
-
- /* If endpoint->last == 0, then transfers have
- * not started on this endpoint
- */
- if (endpoint->last == 0) {
- if(udc_endpoint_write (endpoint)){
- /* Write pre-empted by RX */
- return -1;
- }
- }
-
- }/* end while */
- return total;
- }
-
- return 0;
-}
-
-static int fill_buffer (circbuf_t * buf)
-{
- struct usb_endpoint_instance *endpoint =
- &endpoint_instance[rx_endpoint];
-
- if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) {
- unsigned int nb = 0;
- char *src = (char *) endpoint->rcv_urb->buffer;
- unsigned int rx_avail = buf->totalsize - buf->size;
-
- if(rx_avail >= endpoint->rcv_urb->actual_length){
-
- nb = endpoint->rcv_urb->actual_length;
- buf_push (buf, src, nb);
- endpoint->rcv_urb->actual_length = 0;
-
- }
- return nb;
- }
- return 0;
-}
-
-static int usbtty_configured (void)
-{
- return usbtty_configured_flag;
-}
-
-/******************************************************************************/
-
-static void usbtty_event_handler (struct usb_device_instance *device,
- usb_device_event_t event, int data)
-{
- switch (event) {
- case DEVICE_RESET:
- case DEVICE_BUS_INACTIVE:
- usbtty_configured_flag = 0;
- break;
- case DEVICE_CONFIGURED:
- usbtty_configured_flag = 1;
- break;
-
- case DEVICE_ADDRESS_ASSIGNED:
- usbtty_init_endpoints ();
-
- default:
- break;
- }
-}
-
-/******************************************************************************/
-
-int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb)
-{
- switch (request->bRequest){
-
- case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */
- break;
- case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */
- break;
- case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits
- * per character */
- break;
- case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */
- break;
- case ACM_GET_LINE_ENCODING : /* request DTE rate,
- * stop/parity bits */
- memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc));
- urb->actual_length = sizeof(rs232_desc);
-
- break;
- default:
- return 1;
- }
- return 0;
-}
-
-/******************************************************************************/
-
-/*
- * Since interrupt handling has not yet been implemented, we use this function
- * to handle polling. This is called by the tstc,getc,putc,puts routines to
- * update the USB state.
- */
-void usbtty_poll (void)
-{
- /* New interrupts? */
- udc_irq();
-
- /* Write any output data to host buffer
- * (do this before checking interrupts to avoid missing one)
- */
- if (usbtty_configured ()) {
- write_buffer (&usbtty_output);
- }
-
- /* New interrupts? */
- udc_irq();
-
- /* Check for new data from host..
- * (do this after checking interrupts to get latest data)
- */
- if (usbtty_configured ()) {
- fill_buffer (&usbtty_input);
- }
-
- /* New interrupts? */
- udc_irq();
-
-}
diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h
deleted file mode 100644
index b176a7961b8..00000000000
--- a/drivers/serial/usbtty.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * (C) Copyright 2003
- * Gerry Hamel, geh@ti.com, Texas Instruments
- *
- * (C) Copyright 2006
- * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit
- */
-
-#ifndef __USB_TTY_H__
-#define __USB_TTY_H__
-
-#include <usbdevice.h>
-#if defined(CONFIG_PPC)
-#include <usb/mpc8xx_udc.h>
-#elif defined(CONFIG_CI_UDC)
-#include <usb/ci_udc.h>
-#endif
-
-#include <usb/udc.h>
-#include <version.h>
-
-#ifndef CFG_USBD_CONFIGURATION_STR
-#define CFG_USBD_CONFIGURATION_STR "TTY via USB"
-#endif
-
-#define CFG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT
-#define CFG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE
-#define CFG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT
-#define CFG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE
-#define CFG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT
-#define CFG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE
-#define CFG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE
-
-#define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS
-
-#define USBTTY_BCD_DEVICE 0x00
-#define USBTTY_MAXPOWER 0x00
-
-#define STR_LANG 0x00
-#define STR_MANUFACTURER 0x01
-#define STR_PRODUCT 0x02
-#define STR_SERIAL 0x03
-#define STR_CONFIG 0x04
-#define STR_DATA_INTERFACE 0x05
-#define STR_CTRL_INTERFACE 0x06
-#define STR_COUNT 0x07
-
-#endif
diff --git a/drivers/soc/soc_ti_k3.c b/drivers/soc/soc_ti_k3.c
index a3acca4d394..b34cbd08e07 100644
--- a/drivers/soc/soc_ti_k3.c
+++ b/drivers/soc/soc_ti_k3.c
@@ -18,8 +18,12 @@ struct soc_ti_k3_plat {
static const char *get_family_string(u32 idreg)
{
const char *family;
+ u32 jtag_dev_id;
+ u32 pkg;
u32 soc;
+ jtag_dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID);
+
soc = (idreg & JTAG_ID_PARTNO_MASK) >> JTAG_ID_PARTNO_SHIFT;
switch (soc) {
@@ -51,8 +55,16 @@ static const char *get_family_string(u32 idreg)
family = "J722S";
break;
case JTAG_ID_PARTNO_J784S4:
- family = "J784S4";
- break;
+ {
+ /* Keep default family as J784S4 */
+ family = "J784S4";
+
+ pkg = (jtag_dev_id & JTAG_DEV_J742S2_PKG_MASK) >> JTAG_DEV_J742S2_PKG_SHIFT;
+ if (pkg == JTAG_ID_PKG_J742S2)
+ family = "J742S2";
+
+ break;
+ }
default:
family = "Unknown Silicon";
};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 96ea033082b..a3513f0a3ef 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -52,6 +52,22 @@ config SPI_DIRMAP
if DM_SPI
+config ADI_SPI3
+ bool "Enable ADI SPI Driver"
+ depends on ARCH_SC5XX
+ help
+ 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
@@ -85,7 +101,7 @@ config ATH79_SPI
config ATMEL_QSPI
bool "Atmel Quad SPI Controller"
- depends on ARCH_AT91
+ depends on ARCH_AT91 && SPI_MEM
help
Enable the Atmel Quad SPI controller in master mode. This driver
does not support generic SPI. The implementation supports only the
@@ -135,7 +151,7 @@ config BCMSTB_SPI
config CORTINA_SFLASH
bool "Cortina-Access Serial Flash controller driver"
- depends on DM_SPI && SPI_MEM
+ depends on SPI_MEM
help
Enable the Cortina-Access Serial Flash controller driver. This driver
can be used to access the SPI NOR/NAND flash on platforms embedding this
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7051e2a00c6..da91b18b6ed 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -19,6 +19,8 @@ obj-y += spi.o
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/adi_spi3.c b/drivers/spi/adi_spi3.c
new file mode 100644
index 00000000000..2125d25561a
--- /dev/null
+++ b/drivers/spi/adi_spi3.c
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Converted to driver model by Nathan Barrett-Morrison
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ * Contact: Ian Roberts <ian.roberts@timesys.com>
+ * Contact: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <spi.h>
+#include <spi-mem.h>
+#include <dm/device_compat.h>
+#include <linux/io.h>
+
+#define SPI_IDLE_VAL 0xff
+
+#define MAX_CTRL_CS 7
+
+/* SPI_CONTROL */
+#define SPI_CTL_EN 0x00000001 /* Enable */
+#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */
+#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */
+#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */
+#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */
+#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */
+#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */
+#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in transfers */
+#define SPI_CTL_EMISO 0x00000100 /*Enable MISO */
+#define SPI_CTL_SIZE 0x00000600 /*Word Transfer Size */
+#define SPI_CTL_SIZE08 0x00000000 /*SIZE: 8 bits */
+#define SPI_CTL_SIZE16 0x00000200 /*SIZE: 16 bits */
+#define SPI_CTL_SIZE32 0x00000400 /*SIZE: 32 bits */
+#define SPI_CTL_LSBF 0x00001000 /*LSB First */
+#define SPI_CTL_FCEN 0x00002000 /*Flow-Control Enable */
+#define SPI_CTL_FCCH 0x00004000 /*Flow-Control Channel Selection */
+#define SPI_CTL_FCPL 0x00008000 /*Flow-Control Polarity */
+#define SPI_CTL_FCWM 0x00030000 /*Flow-Control Water-Mark */
+#define SPI_CTL_FIFO0 0x00000000 /*FCWM: Tx empty or Rx Full */
+#define SPI_CTL_FIFO1 0x00010000 /*FCWM: Tx empty or Rx full (>=75%) */
+#define SPI_CTL_FIFO2 0x00020000 /*FCWM: Tx empty or Rx full (>=50%) */
+#define SPI_CTL_FMODE 0x00040000 /*Fast-mode Enable */
+#define SPI_CTL_MIOM 0x00300000 /*Multiple I/O Mode */
+#define SPI_CTL_MIO_DIS 0x00000000 /*MIOM: Disable */
+#define SPI_CTL_MIO_DUAL 0x00100000 /*MIOM: Enable DIOM (Dual I/O Mode) */
+#define SPI_CTL_MIO_QUAD 0x00200000 /*MIOM: Enable QUAD (Quad SPI Mode) */
+#define SPI_CTL_SOSI 0x00400000 /*Start on MOSI */
+#define SPI_CTL_MMWEM 0x40000000 /*Start on MMWEM */
+#define SPI_CTL_MMSE 0x80000000 /*Start on MMSE */
+/* SPI_RX_CONTROL */
+#define SPI_RXCTL_REN 0x00000001 /*Receive Channel Enable */
+#define SPI_RXCTL_RTI 0x00000004 /*Receive Transfer Initiate */
+#define SPI_RXCTL_RWCEN 0x00000008 /*Receive Word Counter Enable */
+#define SPI_RXCTL_RDR 0x00000070 /*Receive Data Request */
+#define SPI_RXCTL_RDR_DIS 0x00000000 /*RDR: Disabled */
+#define SPI_RXCTL_RDR_NE 0x00000010 /*RDR: RFIFO not empty */
+#define SPI_RXCTL_RDR_25 0x00000020 /*RDR: RFIFO 25% full */
+#define SPI_RXCTL_RDR_50 0x00000030 /*RDR: RFIFO 50% full */
+#define SPI_RXCTL_RDR_75 0x00000040 /*RDR: RFIFO 75% full */
+#define SPI_RXCTL_RDR_FULL 0x00000050 /*RDR: RFIFO full */
+#define SPI_RXCTL_RDO 0x00000100 /*Receive Data Over-Run */
+#define SPI_RXCTL_RRWM 0x00003000 /*FIFO Regular Water-Mark */
+#define SPI_RXCTL_RWM_0 0x00000000 /*RRWM: RFIFO Empty */
+#define SPI_RXCTL_RWM_25 0x00001000 /*RRWM: RFIFO 25% full */
+#define SPI_RXCTL_RWM_50 0x00002000 /*RRWM: RFIFO 50% full */
+#define SPI_RXCTL_RWM_75 0x00003000 /*RRWM: RFIFO 75% full */
+#define SPI_RXCTL_RUWM 0x00070000 /*FIFO Urgent Water-Mark */
+#define SPI_RXCTL_UWM_DIS 0x00000000 /*RUWM: Disabled */
+#define SPI_RXCTL_UWM_25 0x00010000 /*RUWM: RFIFO 25% full */
+#define SPI_RXCTL_UWM_50 0x00020000 /*RUWM: RFIFO 50% full */
+#define SPI_RXCTL_UWM_75 0x00030000 /*RUWM: RFIFO 75% full */
+#define SPI_RXCTL_UWM_FULL 0x00040000 /*RUWM: RFIFO full */
+/* SPI_TX_CONTROL */
+#define SPI_TXCTL_TEN 0x00000001 /*Transmit Channel Enable */
+#define SPI_TXCTL_TTI 0x00000004 /*Transmit Transfer Initiate */
+#define SPI_TXCTL_TWCEN 0x00000008 /*Transmit Word Counter Enable */
+#define SPI_TXCTL_TDR 0x00000070 /*Transmit Data Request */
+#define SPI_TXCTL_TDR_DIS 0x00000000 /*TDR: Disabled */
+#define SPI_TXCTL_TDR_NF 0x00000010 /*TDR: TFIFO not full */
+#define SPI_TXCTL_TDR_25 0x00000020 /*TDR: TFIFO 25% empty */
+#define SPI_TXCTL_TDR_50 0x00000030 /*TDR: TFIFO 50% empty */
+#define SPI_TXCTL_TDR_75 0x00000040 /*TDR: TFIFO 75% empty */
+#define SPI_TXCTL_TDR_EMPTY 0x00000050 /*TDR: TFIFO empty */
+#define SPI_TXCTL_TDU 0x00000100 /*Transmit Data Under-Run */
+#define SPI_TXCTL_TRWM 0x00003000 /*FIFO Regular Water-Mark */
+#define SPI_TXCTL_RWM_FULL 0x00000000 /*TRWM: TFIFO full */
+#define SPI_TXCTL_RWM_25 0x00001000 /*TRWM: TFIFO 25% empty */
+#define SPI_TXCTL_RWM_50 0x00002000 /*TRWM: TFIFO 50% empty */
+#define SPI_TXCTL_RWM_75 0x00003000 /*TRWM: TFIFO 75% empty */
+#define SPI_TXCTL_TUWM 0x00070000 /*FIFO Urgent Water-Mark */
+#define SPI_TXCTL_UWM_DIS 0x00000000 /*TUWM: Disabled */
+#define SPI_TXCTL_UWM_25 0x00010000 /*TUWM: TFIFO 25% empty */
+#define SPI_TXCTL_UWM_50 0x00020000 /*TUWM: TFIFO 50% empty */
+#define SPI_TXCTL_UWM_75 0x00030000 /*TUWM: TFIFO 75% empty */
+#define SPI_TXCTL_UWM_EMPTY 0x00040000 /*TUWM: TFIFO empty */
+/* SPI_CLOCK */
+#define SPI_CLK_BAUD 0x0000FFFF /*Baud Rate */
+/* SPI_DELAY */
+#define SPI_DLY_STOP 0x000000FF /*Transfer delay time */
+#define SPI_DLY_LEADX 0x00000100 /*Extended (1 SCK) LEAD Control */
+#define SPI_DLY_LAGX 0x00000200 /*Extended (1 SCK) LAG control */
+/* SPI_SSEL */
+#define SPI_SLVSEL_SSE1 0x00000002 /*SPISSEL1 Enable */
+#define SPI_SLVSEL_SSE2 0x00000004 /*SPISSEL2 Enable */
+#define SPI_SLVSEL_SSE3 0x00000008 /*SPISSEL3 Enable */
+#define SPI_SLVSEL_SSE4 0x00000010 /*SPISSEL4 Enable */
+#define SPI_SLVSEL_SSE5 0x00000020 /*SPISSEL5 Enable */
+#define SPI_SLVSEL_SSE6 0x00000040 /*SPISSEL6 Enable */
+#define SPI_SLVSEL_SSE7 0x00000080 /*SPISSEL7 Enable */
+#define SPI_SLVSEL_SSEL1 0x00000200 /*SPISSEL1 Value */
+#define SPI_SLVSEL_SSEL2 0x00000400 /*SPISSEL2 Value */
+#define SPI_SLVSEL_SSEL3 0x00000800 /*SPISSEL3 Value */
+#define SPI_SLVSEL_SSEL4 0x00001000 /*SPISSEL4 Value */
+#define SPI_SLVSEL_SSEL5 0x00002000 /*SPISSEL5 Value */
+#define SPI_SLVSEL_SSEL6 0x00004000 /*SPISSEL6 Value */
+#define SPI_SLVSEL_SSEL7 0x00008000 /*SPISSEL7 Value */
+/* SPI_RWC */
+#define SPI_RWC_VALUE 0x0000FFFF /*Received Word-Count */
+/* SPI_RWCR */
+#define SPI_RWCR_VALUE 0x0000FFFF /*Received Word-Count Reload */
+/* SPI_TWC */
+#define SPI_TWC_VALUE 0x0000FFFF /*Transmitted Word-Count */
+/* SPI_TWCR */
+#define SPI_TWCR_VALUE 0x0000FFFF /*Transmitted Word-Count Reload */
+/* SPI_IMASK */
+#define SPI_IMSK_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */
+#define SPI_IMSK_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */
+#define SPI_IMSK_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */
+#define SPI_IMSK_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */
+#define SPI_IMSK_TCM 0x00000040 /*Transmit Collision Interrupt Mask */
+#define SPI_IMSK_MFM 0x00000080 /*Mode Fault Interrupt Mask */
+#define SPI_IMSK_RSM 0x00000100 /*Receive Start Interrupt Mask */
+#define SPI_IMSK_TSM 0x00000200 /*Transmit Start Interrupt Mask */
+#define SPI_IMSK_RFM 0x00000400 /*Receive Finish Interrupt Mask */
+#define SPI_IMSK_TFM 0x00000800 /*Transmit Finish Interrupt Mask */
+/* SPI_IMASKCL */
+#define SPI_IMSK_CLR_RUW 0x00000002 /*Receive Water-Mark Interrupt Mask */
+#define SPI_IMSK_CLR_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */
+#define SPI_IMSK_CLR_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */
+#define SPI_IMSK_CLR_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */
+#define SPI_IMSK_CLR_TCM 0x00000040 /*Transmit Collision Interrupt Mask */
+#define SPI_IMSK_CLR_MFM 0x00000080 /*Mode Fault Interrupt Mask */
+#define SPI_IMSK_CLR_RSM 0x00000100 /*Receive Start Interrupt Mask */
+#define SPI_IMSK_CLR_TSM 0x00000200 /*Transmit Start Interrupt Mask */
+#define SPI_IMSK_CLR_RFM 0x00000400 /*Receive Finish Interrupt Mask */
+#define SPI_IMSK_CLR_TFM 0x00000800 /*Transmit Finish Interrupt Mask */
+/* SPI_IMASKST */
+#define SPI_IMSK_SET_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */
+#define SPI_IMSK_SET_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */
+#define SPI_IMSK_SET_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */
+#define SPI_IMSK_SET_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */
+#define SPI_IMSK_SET_TCM 0x00000040 /*Transmit Collision Interrupt Mask */
+#define SPI_IMSK_SET_MFM 0x00000080 /*Mode Fault Interrupt Mask */
+#define SPI_IMSK_SET_RSM 0x00000100 /*Receive Start Interrupt Mask */
+#define SPI_IMSK_SET_TSM 0x00000200 /*Transmit Start Interrupt Mask */
+#define SPI_IMSK_SET_RFM 0x00000400 /*Receive Finish Interrupt Mask */
+#define SPI_IMSK_SET_TFM 0x00000800 /*Transmit Finish Interrupt Mask */
+/* SPI_STATUS */
+#define SPI_STAT_SPIF 0x00000001 /*SPI Finished */
+#define SPI_STAT_RUWM 0x00000002 /*Receive Water-Mark Breached */
+#define SPI_STAT_TUWM 0x00000004 /*Transmit Water-Mark Breached */
+#define SPI_STAT_ROE 0x00000010 /*Receive Over-Run Indication */
+#define SPI_STAT_TUE 0x00000020 /*Transmit Under-Run Indication */
+#define SPI_STAT_TCE 0x00000040 /*Transmit Collision Indication */
+#define SPI_STAT_MODF 0x00000080 /*Mode Fault Indication */
+#define SPI_STAT_RS 0x00000100 /*Receive Start Indication */
+#define SPI_STAT_TS 0x00000200 /*Transmit Start Indication */
+#define SPI_STAT_RF 0x00000400 /*Receive Finish Indication */
+#define SPI_STAT_TF 0x00000800 /*Transmit Finish Indication */
+#define SPI_STAT_RFS 0x00007000 /*SPI_RFIFO status */
+#define SPI_STAT_RFIFO_EMPTY 0x00000000 /*RFS: RFIFO Empty */
+#define SPI_STAT_RFIFO_25 0x00001000 /*RFS: RFIFO 25% Full */
+#define SPI_STAT_RFIFO_50 0x00002000 /*RFS: RFIFO 50% Full */
+#define SPI_STAT_RFIFO_75 0x00003000 /*RFS: RFIFO 75% Full */
+#define SPI_STAT_RFIFO_FULL 0x00004000 /*RFS: RFIFO Full */
+#define SPI_STAT_TFS 0x00070000 /*SPI_TFIFO status */
+#define SPI_STAT_TFIFO_FULL 0x00000000 /*TFS: TFIFO full */
+#define SPI_STAT_TFIFO_25 0x00010000 /*TFS: TFIFO 25% empty */
+#define SPI_STAT_TFIFO_50 0x00020000 /*TFS: TFIFO 50% empty */
+#define SPI_STAT_TFIFO_75 0x00030000 /*TFS: TFIFO 75% empty */
+#define SPI_STAT_TFIFO_EMPTY 0x00040000 /*TFS: TFIFO empty */
+#define SPI_STAT_FCS 0x00100000 /*Flow-Control Stall Indication */
+#define SPI_STAT_RFE 0x00400000 /*SPI_RFIFO Empty */
+#define SPI_STAT_TFF 0x00800000 /*SPI_TFIFO Full */
+/* SPI_ILAT */
+#define SPI_ILAT_RUWMI 0x00000002 /*Receive Water Mark Interrupt */
+#define SPI_ILAT_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */
+#define SPI_ILAT_ROI 0x00000010 /*Receive Over-Run Indication */
+#define SPI_ILAT_TUI 0x00000020 /*Transmit Under-Run Indication */
+#define SPI_ILAT_TCI 0x00000040 /*Transmit Collision Indication */
+#define SPI_ILAT_MFI 0x00000080 /*Mode Fault Indication */
+#define SPI_ILAT_RSI 0x00000100 /*Receive Start Indication */
+#define SPI_ILAT_TSI 0x00000200 /*Transmit Start Indication */
+#define SPI_ILAT_RFI 0x00000400 /*Receive Finish Indication */
+#define SPI_ILAT_TFI 0x00000800 /*Transmit Finish Indication */
+/* SPI_ILATCL */
+#define SPI_ILAT_CLR_RUWMI 0x00000002 /*Receive Water Mark Interrupt */
+#define SPI_ILAT_CLR_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */
+#define SPI_ILAT_CLR_ROI 0x00000010 /*Receive Over-Run Indication */
+#define SPI_ILAT_CLR_TUI 0x00000020 /*Transmit Under-Run Indication */
+#define SPI_ILAT_CLR_TCI 0x00000040 /*Transmit Collision Indication */
+#define SPI_ILAT_CLR_MFI 0x00000080 /*Mode Fault Indication */
+#define SPI_ILAT_CLR_RSI 0x00000100 /*Receive Start Indication */
+#define SPI_ILAT_CLR_TSI 0x00000200 /*Transmit Start Indication */
+#define SPI_ILAT_CLR_RFI 0x00000400 /*Receive Finish Indication */
+#define SPI_ILAT_CLR_TFI 0x00000800 /*Transmit Finish Indication */
+/* SPI_MMRDH */
+#define SPI_MMRDH_MERGE 0x04000000 /*Merge Enable */
+#define SPI_MMRDH_DMY_SZ 0x00007000 /*Bytes of Dummy */
+#define SPI_MMRDH_ADDR_PINS 0x00000800 /*Pins used for Address */
+#define SPI_MMRDH_ADDR_SZ 0x00000700 /*Bytes of Read Address */
+#define SPI_MMRDH_OPCODE 0x000000FF /*Read Opcode */
+
+#define SPI_MMRDH_TRIDMY_OFF 24 /*Bytes of Dummy offset */
+#define SPI_MMRDH_DMY_SZ_OFF 12 /*Bytes of Dummy offset */
+#define SPI_MMRDH_ADDR_SZ_OFF 8 /*Bytes of Read Address offset */
+
+#define BIT_SSEL_VAL(x) ((1 << 8) << (x)) /* Slave Select input value bit */
+#define BIT_SSEL_EN(x) (1 << (x)) /* Slave Select enable bit*/
+
+struct adi_spi_regs {
+ u32 revid;
+ u32 control;
+ u32 rx_control;
+ u32 tx_control;
+ u32 clock;
+ u32 delay;
+ u32 ssel;
+ u32 rwc;
+ u32 rwcr;
+ u32 twc;
+ u32 twcr;
+ u32 reserved0;
+ u32 emask;
+ u32 emaskcl;
+ u32 emaskst;
+ u32 reserved1;
+ u32 status;
+ u32 elat;
+ u32 elatcl;
+ u32 reserved2;
+ u32 rfifo;
+ u32 reserved3;
+ u32 tfifo;
+ u32 reserved4;
+ u32 mmrdh;
+ u32 mmtop;
+};
+
+struct adi_spi_platdata {
+ u32 max_hz;
+ u32 bus_num;
+ struct adi_spi_regs __iomem *regs;
+};
+
+struct adi_spi_priv {
+ u32 control;
+ u32 clock;
+ u32 bus_num;
+ u32 max_cs;
+ struct adi_spi_regs __iomem *regs;
+};
+
+/**
+ * By convention, this driver uses the same CS numbering that is used with the SSEL bit
+ * definitions (both here and in the TRM on which this is based), which are 1-indexed not
+ * 0-indexed. The valid CS range is therefore [1,max_cs], in contrast with other drivers
+ * where it is [0,max_cs-1].
+ */
+static int adi_spi_cs_info(struct udevice *bus, uint cs,
+ struct spi_cs_info *info)
+{
+ struct adi_spi_priv *priv = dev_get_priv(bus);
+
+ if (cs == 0 || cs > priv->max_cs) {
+ dev_err(bus, "invalid chipselect %u\n", cs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adi_spi_of_to_plat(struct udevice *bus)
+{
+ struct adi_spi_platdata *plat = dev_get_plat(bus);
+ fdt_addr_t addr;
+
+ plat->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 500000);
+ plat->bus_num = dev_read_u32_default(bus, "bus-num", 0);
+ addr = dev_read_addr(bus);
+
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->regs = map_sysmem(addr, sizeof(*plat->regs));
+
+ return 0;
+}
+
+static int adi_spi_probe(struct udevice *bus)
+{
+ struct adi_spi_platdata *plat = dev_get_plat(bus);
+ struct adi_spi_priv *priv = dev_get_priv(bus);
+
+ priv->bus_num = plat->bus_num;
+ priv->regs = plat->regs;
+ priv->max_cs = dev_read_u32_default(bus, "num-cs", MAX_CTRL_CS);
+
+ iowrite32(0x0, &plat->regs->control);
+ iowrite32(0x0, &plat->regs->rx_control);
+ iowrite32(0x0, &plat->regs->tx_control);
+
+ return 0;
+}
+
+static int adi_spi_remove(struct udevice *dev)
+{
+ return -ENODEV;
+}
+
+static int adi_spi_claim_bus(struct udevice *dev)
+{
+ struct adi_spi_priv *priv;
+ struct udevice *bus = dev->parent;
+
+ priv = dev_get_priv(bus);
+
+ debug("%s: control:%i clock:%i\n",
+ __func__, priv->control, priv->clock);
+
+ iowrite32(priv->control, &priv->regs->control);
+ iowrite32(priv->clock, &priv->regs->clock);
+ iowrite32(0x0, &priv->regs->delay);
+
+ return 0;
+}
+
+static int adi_spi_release_bus(struct udevice *dev)
+{
+ struct adi_spi_priv *priv;
+ struct udevice *bus = dev->parent;
+
+ priv = dev_get_priv(bus);
+
+ debug("%s: control:%i clock:%i\n",
+ __func__, priv->control, priv->clock);
+
+ iowrite32(0x0, &priv->regs->rx_control);
+ iowrite32(0x0, &priv->regs->tx_control);
+ iowrite32(0x0, &priv->regs->control);
+
+ return 0;
+}
+
+void adi_spi_enable_ssel(struct adi_spi_priv *priv, int cs)
+{
+ setbits_32(&priv->regs->ssel, BIT_SSEL_EN(cs));
+}
+
+void adi_spi_set_ssel(struct adi_spi_priv *priv, int cs, int high)
+{
+ if (high)
+ setbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs));
+ else
+ clrbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs));
+}
+
+void adi_spi_cs_activate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat)
+{
+ bool high = slave_plat->mode & SPI_CS_HIGH;
+
+ adi_spi_set_ssel(priv, slave_plat->cs[0], high);
+ adi_spi_enable_ssel(priv, slave_plat->cs[0]);
+}
+
+void adi_spi_cs_deactivate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat)
+{
+ bool high = slave_plat->mode & SPI_CS_HIGH;
+
+ /* invert CS for matching SSEL to deactivate */
+ adi_spi_set_ssel(priv, slave_plat->cs[0], !high);
+}
+
+static void discard_rx_fifo_contents(struct adi_spi_regs *regs)
+{
+ while (!(ioread32(&regs->status) & SPI_STAT_RFE))
+ ioread32(&regs->rfifo);
+}
+
+static int adi_spi_fifo_mio_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx,
+ uint bytes, uint32_t mio_mode)
+{
+ u8 value;
+
+ /* switch current SPI transfer to mio SPI mode */
+ clrsetbits_32(&priv->regs->control, SPI_CTL_SOSI, mio_mode);
+ /*
+ * Data can only be transferred in one direction in multi-io SPI
+ * modes, trigger the transfer in respective direction.
+ */
+ if (rx) {
+ iowrite32(0x0, &priv->regs->tx_control);
+ iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI, &priv->regs->rx_control);
+
+ while (bytes--) {
+ while (ioread32(&priv->regs->status) &
+ SPI_STAT_RFE)
+ if (ctrlc())
+ return -1;
+ value = ioread32(&priv->regs->rfifo);
+ *rx++ = value;
+ }
+ } else if (tx) {
+ iowrite32(0x0, &priv->regs->rx_control);
+ iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control);
+
+ while (bytes--) {
+ value = *tx++;
+ iowrite32(value, &priv->regs->tfifo);
+ while (ioread32(&priv->regs->status) &
+ SPI_STAT_TFF)
+ if (ctrlc())
+ return -1;
+ }
+
+ /* Wait till the tfifo is empty */
+ while ((ioread32(&priv->regs->status) & SPI_STAT_TFS) != SPI_STAT_TFIFO_EMPTY)
+ if (ctrlc())
+ return -1;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+static int adi_spi_fifo_1x_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx,
+ uint bytes)
+{
+ u8 value;
+
+ /*
+ * Set current SPI transfer in normal mode and trigger
+ * the bi-direction transfer by tx write operation.
+ */
+ iowrite32(priv->control, &priv->regs->control);
+ iowrite32(SPI_RXCTL_REN, &priv->regs->rx_control);
+ iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control);
+
+ while (bytes--) {
+ value = (tx ? *tx++ : SPI_IDLE_VAL);
+ debug("%s: tx:%x ", __func__, value);
+ iowrite32(value, &priv->regs->tfifo);
+ while (ioread32(&priv->regs->status) & SPI_STAT_RFE)
+ if (ctrlc())
+ return -1;
+ value = ioread32(&priv->regs->rfifo);
+ if (rx)
+ *rx++ = value;
+ debug("rx:%x\n", value);
+ }
+ return 0;
+}
+
+static int adi_spi_fifo_xfer(struct adi_spi_priv *priv, int buswidth,
+ const u8 *tx, u8 *rx, uint bytes)
+{
+ switch (buswidth) {
+ case 1:
+ return adi_spi_fifo_1x_xfer(priv, tx, rx, bytes);
+ case 2:
+ return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_DUAL);
+ case 4:
+ return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_QUAD);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int adi_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev->parent;
+ struct adi_spi_priv *priv = dev_get_priv(bus);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+
+ const u8 *tx = dout;
+ u8 *rx = din;
+ uint bytes = bitlen / 8;
+ int ret = 0;
+
+ debug("%s: bus_num:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
+ priv->bus_num, slave_plat->cs[0], bitlen, bytes, flags);
+
+ if (flags & SPI_XFER_BEGIN)
+ adi_spi_cs_activate(priv, slave_plat);
+
+ if (bitlen == 0)
+ goto done;
+
+ /* we can only do 8 bit transfers */
+ if (bitlen % 8) {
+ flags |= SPI_XFER_END;
+ goto done;
+ }
+
+ /* Discard invalid rx data and empty rfifo */
+ discard_rx_fifo_contents(priv->regs);
+
+ ret = adi_spi_fifo_1x_xfer(priv, tx, rx, bytes);
+
+ done:
+ if (flags & SPI_XFER_END)
+ adi_spi_cs_deactivate(priv, slave_plat);
+
+ return ret;
+}
+
+static int adi_spi_set_speed(struct udevice *bus, uint speed)
+{
+ struct adi_spi_platdata *plat = dev_get_plat(bus);
+ struct adi_spi_priv *priv = dev_get_priv(bus);
+ int ret;
+ u32 clock, spi_base_clk;
+ struct clk spi_clk;
+
+ ret = clk_get_by_name(bus, "spi", &spi_clk);
+ if (ret < 0) {
+ dev_err(bus, "Can't get SPI clk: %d\n", ret);
+ return ret;
+ }
+ spi_base_clk = clk_get_rate(&spi_clk);
+
+ if (speed > plat->max_hz)
+ speed = plat->max_hz;
+
+ if (speed > spi_base_clk)
+ return -ENODEV;
+
+ clock = spi_base_clk / speed;
+ if (clock)
+ clock--;
+
+ priv->clock = clock;
+
+ debug("%s: priv->clock: %x, speed: %x, get_spi_clk(): %x\n",
+ __func__, clock, speed, spi_base_clk);
+
+ return 0;
+}
+
+static int adi_spi_set_mode(struct udevice *bus, uint mode)
+{
+ struct adi_spi_priv *priv = dev_get_priv(bus);
+ u32 reg;
+
+ reg = SPI_CTL_EN | SPI_CTL_MSTR;
+ if (mode & SPI_CPHA)
+ reg |= SPI_CTL_CPHA;
+ if (mode & SPI_CPOL)
+ reg |= SPI_CTL_CPOL;
+ if (mode & SPI_LSB_FIRST)
+ reg |= SPI_CTL_LSBF;
+ reg &= ~SPI_CTL_ASSEL;
+
+ priv->control = reg;
+
+ debug("%s: control=%d, cs_pol=%d\n", __func__, reg, mode & SPI_CS_HIGH ? 1 : 0);
+
+ return 0;
+}
+
+/**
+ * U-boot's version of spi-mem does not support mixed bus-width
+ * commands nor anything more than 1x mode.
+ * Using a custom exec_op implementation, we can support it.
+ */
+static int adi_spi_mem_exec_op(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ int rv = 0;
+ struct udevice *bus = slave->dev->parent;
+ struct adi_spi_priv *priv = dev_get_priv(bus);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
+ u8 tmpbuf[64];
+ int i;
+
+ if ((op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes) >
+ sizeof(tmpbuf))
+ return -ENOMEM;
+
+ for (i = 0; i < op->cmd.nbytes; i++)
+ tmpbuf[i] = op->cmd.opcode >>
+ (8 * (op->cmd.nbytes - i - 1));
+ for (i = 0; i < op->addr.nbytes; i++)
+ tmpbuf[i + op->cmd.nbytes] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+ memset(tmpbuf + op->addr.nbytes + op->cmd.nbytes, 0xff,
+ op->dummy.nbytes);
+
+ adi_spi_cs_activate(priv, slave_plat);
+ discard_rx_fifo_contents(priv->regs);
+
+ if (op->cmd.nbytes) {
+ rv = adi_spi_fifo_xfer(priv, op->cmd.buswidth,
+ tmpbuf, NULL, op->cmd.nbytes);
+ if (rv != 0)
+ goto cleanup;
+ }
+
+ if (op->addr.nbytes) {
+ rv = adi_spi_fifo_xfer(priv, op->addr.buswidth,
+ tmpbuf + op->cmd.nbytes, NULL,
+ op->addr.nbytes);
+ if (rv != 0)
+ goto cleanup;
+ }
+
+ if (op->dummy.nbytes) {
+ rv = adi_spi_fifo_xfer(priv, op->dummy.buswidth,
+ tmpbuf + op->cmd.nbytes +
+ op->addr.nbytes,
+ NULL, op->dummy.nbytes);
+ if (rv != 0)
+ goto cleanup;
+ }
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ rv = adi_spi_fifo_xfer(priv, op->data.buswidth,
+ NULL, op->data.buf.in,
+ op->data.nbytes);
+ else if (op->data.dir == SPI_MEM_DATA_OUT)
+ rv = adi_spi_fifo_xfer(priv, op->data.buswidth,
+ op->data.buf.out, NULL,
+ op->data.nbytes);
+
+cleanup:
+ adi_spi_cs_deactivate(priv, slave_plat);
+ return rv;
+}
+
+static const struct spi_controller_mem_ops adi_spi_mem_ops = {
+ .exec_op = adi_spi_mem_exec_op,
+};
+
+static const struct dm_spi_ops adi_spi_ops = {
+ .claim_bus = adi_spi_claim_bus,
+ .release_bus = adi_spi_release_bus,
+ .xfer = adi_spi_xfer,
+ .set_speed = adi_spi_set_speed,
+ .set_mode = adi_spi_set_mode,
+ .cs_info = adi_spi_cs_info,
+ .mem_ops = &adi_spi_mem_ops,
+};
+
+static const struct udevice_id adi_spi_ids[] = {
+ { .compatible = "adi,spi3" },
+ { }
+};
+
+U_BOOT_DRIVER(adi_spi3) = {
+ .name = "adi_spi3",
+ .id = UCLASS_SPI,
+ .of_match = adi_spi_ids,
+ .ops = &adi_spi_ops,
+ .of_to_plat = adi_spi_of_to_plat,
+ .probe = adi_spi_probe,
+ .remove = adi_spi_remove,
+ .plat_auto = sizeof(struct adi_spi_platdata),
+ .priv_auto = sizeof(struct adi_spi_priv),
+ .per_child_auto = sizeof(struct spi_slave),
+};
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/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 3efb661803b..8aa7a83aef4 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -10,11 +10,13 @@
*/
#include <malloc.h>
+#include <asm/gpio.h>
#include <asm/io.h>
#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
+#include <log.h>
#include <dm/device_compat.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
@@ -258,6 +260,7 @@ struct atmel_qspi_caps {
struct atmel_qspi_priv_ops;
+#define MAX_CS_COUNT 2
struct atmel_qspi {
void __iomem *regs;
void __iomem *mem;
@@ -267,6 +270,7 @@ struct atmel_qspi {
struct udevice *dev;
ulong bus_clk_rate;
u32 mr;
+ struct gpio_desc cs_gpios[MAX_CS_COUNT];
};
struct atmel_qspi_priv_ops {
@@ -395,6 +399,26 @@ static void atmel_qspi_write(u32 value, struct atmel_qspi *aq, u32 offset)
writel(value, aq->regs + offset);
}
+static int atmel_qspi_reg_sync(struct atmel_qspi *aq)
+{
+ u32 val;
+
+ return readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ !(val & QSPI_SR2_SYNCBSY),
+ ATMEL_QSPI_SYNC_TIMEOUT);
+}
+
+static int atmel_qspi_update_config(struct atmel_qspi *aq)
+{
+ int ret;
+
+ ret = atmel_qspi_reg_sync(aq);
+ if (ret)
+ return ret;
+ atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR);
+ return atmel_qspi_reg_sync(aq);
+}
+
static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op,
const struct atmel_qspi_mode *mode)
{
@@ -458,6 +482,29 @@ static bool atmel_qspi_supports_op(struct spi_slave *slave,
return true;
}
+/*
+ * Switch QSPI controller between regular SPI mode or Serial Memory Mode (SMM).
+ */
+static int atmel_qspi_set_serial_memory_mode(struct atmel_qspi *aq,
+ bool enable)
+{
+ int ret = 0;
+
+ /* only write if designated state differs from current state */
+ if (!!(aq->mr & QSPI_MR_SMM) != enable) {
+ if (enable)
+ aq->mr |= QSPI_MR_SMM;
+ else
+ aq->mr &= ~QSPI_MR_SMM;
+ atmel_qspi_write(aq->mr, aq, QSPI_MR);
+
+ if (aq->caps->has_gclk)
+ ret = atmel_qspi_update_config(aq);
+ }
+
+ return ret;
+}
+
static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
const struct spi_mem_op *op, u32 *offset)
{
@@ -474,7 +521,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
return mode;
ifr |= atmel_qspi_modes[mode].config;
- if (op->dummy.buswidth && op->dummy.nbytes)
+ if (op->dummy.nbytes)
dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
/*
@@ -529,43 +576,39 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
if (dummy_cycles)
ifr |= QSPI_IFR_NBDUM(dummy_cycles);
- /* Set data enable */
- if (op->data.nbytes)
+ /* Set data enable and data transfer type. */
+ if (op->data.nbytes) {
ifr |= QSPI_IFR_DATAEN;
- /*
- * If the QSPI controller is set in regular SPI mode, set it in
- * Serial Memory Mode (SMM).
- */
- if (aq->mr != QSPI_MR_SMM) {
- atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR);
- aq->mr = QSPI_MR_SMM;
+ if (op->addr.nbytes)
+ ifr |= QSPI_IFR_TFRTYP_MEM;
}
+ mode = atmel_qspi_set_serial_memory_mode(aq, true);
+ if (mode < 0)
+ return mode;
+
/* Clear pending interrupts */
(void)atmel_qspi_read(aq, QSPI_SR);
- if (aq->caps->has_ricr) {
- if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN)
- ifr |= QSPI_IFR_APBTFRTYP_READ;
-
- /* Set QSPI Instruction Frame registers */
+ /* Set QSPI Instruction Frame registers. */
+ if (op->addr.nbytes && !op->data.nbytes)
atmel_qspi_write(iar, aq, QSPI_IAR);
+
+ if (aq->caps->has_ricr) {
if (op->data.dir == SPI_MEM_DATA_IN)
atmel_qspi_write(icr, aq, QSPI_RICR);
else
atmel_qspi_write(icr, aq, QSPI_WICR);
- atmel_qspi_write(ifr, aq, QSPI_IFR);
} else {
- if (op->data.dir == SPI_MEM_DATA_OUT)
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR;
- /* Set QSPI Instruction Frame registers */
- atmel_qspi_write(iar, aq, QSPI_IAR);
atmel_qspi_write(icr, aq, QSPI_ICR);
- atmel_qspi_write(ifr, aq, QSPI_IFR);
}
+ atmel_qspi_write(ifr, aq, QSPI_IFR);
+
return 0;
}
@@ -597,26 +640,6 @@ static int atmel_qspi_transfer(struct atmel_qspi *aq,
ATMEL_QSPI_TIMEOUT);
}
-static int atmel_qspi_reg_sync(struct atmel_qspi *aq)
-{
- u32 val;
-
- return readl_poll_timeout(aq->regs + QSPI_SR2, val,
- !(val & QSPI_SR2_SYNCBSY),
- ATMEL_QSPI_SYNC_TIMEOUT);
-}
-
-static int atmel_qspi_update_config(struct atmel_qspi *aq)
-{
- int ret;
-
- ret = atmel_qspi_reg_sync(aq);
- if (ret)
- return ret;
- atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR);
- return atmel_qspi_reg_sync(aq);
-}
-
static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq,
const struct spi_mem_op *op, u32 *offset)
{
@@ -668,17 +691,9 @@ static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq,
ifr |= QSPI_IFR_TFRTYP_MEM;
}
- /*
- * If the QSPI controller is set in regular SPI mode, set it in
- * Serial Memory Mode (SMM).
- */
- if (aq->mr != QSPI_MR_SMM) {
- atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR);
- ret = atmel_qspi_update_config(aq);
- if (ret)
- return ret;
- aq->mr = QSPI_MR_SMM;
- }
+ ret = atmel_qspi_set_serial_memory_mode(aq, true);
+ if (ret < 0)
+ return ret;
/* Clear pending interrupts */
(void)atmel_qspi_read(aq, QSPI_SR);
@@ -902,11 +917,10 @@ static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz)
}
/* Set the QSPI controller by default in Serial Memory Mode */
- atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR);
- ret = atmel_qspi_update_config(aq);
- if (ret)
+ aq->mr |= QSPI_MR_DQSDLYEN;
+ ret = atmel_qspi_set_serial_memory_mode(aq, true);
+ if (ret < 0)
return ret;
- aq->mr = QSPI_MR_SMM;
/* Enable the QSPI controller. */
ret = atmel_qspi_reg_sync(aq);
@@ -930,6 +944,135 @@ static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz)
return ret;
}
+static int atmel_qspi_claim_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct atmel_qspi *aq = dev_get_priv(bus);
+ int ret;
+
+ aq->mr &= ~QSPI_MR_CSMODE_MASK;
+ aq->mr |= QSPI_MR_CSMODE_LASTXFER | QSPI_MR_WDRBT;
+ atmel_qspi_write(aq->mr, aq, QSPI_MR);
+
+ ret = atmel_qspi_set_serial_memory_mode(aq, false);
+ if (ret)
+ return log_ret(ret);
+
+ /* de-assert all chip selects */
+ if (IS_ENABLED(CONFIG_DM_GPIO)) {
+ for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) {
+ if (dm_gpio_is_valid(&aq->cs_gpios[i]))
+ dm_gpio_set_value(&aq->cs_gpios[i], 0);
+ }
+ }
+
+ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
+
+ return 0;
+}
+
+static int atmel_qspi_release_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct atmel_qspi *aq = dev_get_priv(bus);
+
+ /* de-assert all chip selects */
+ if (IS_ENABLED(CONFIG_DM_GPIO)) {
+ for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) {
+ if (dm_gpio_is_valid(&aq->cs_gpios[i]))
+ dm_gpio_set_value(&aq->cs_gpios[i], 0);
+ }
+ }
+
+ atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
+
+ return 0;
+}
+
+static int atmel_qspi_set_cs(struct udevice *dev, int value)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct atmel_qspi *aq = dev_get_priv(bus);
+ int cs = spi_chip_select(dev);
+
+ if (IS_ENABLED(CONFIG_DM_GPIO)) {
+ if (!dm_gpio_is_valid(&aq->cs_gpios[cs]))
+ return log_ret(-ENOENT);
+
+ return dm_gpio_set_value(&aq->cs_gpios[cs], value);
+ } else {
+ return -ENOENT;
+ }
+}
+
+static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct atmel_qspi *aq = dev_get_priv(bus);
+ unsigned int len, len_rx, len_tx;
+ const u8 *txp = dout;
+ u8 *rxp = din;
+ u32 reg;
+ int ret;
+
+ if (bitlen == 0)
+ goto out;
+
+ if (bitlen % 8) {
+ flags |= SPI_XFER_END;
+ goto out;
+ }
+
+ len = bitlen / 8;
+
+ if (flags & SPI_XFER_BEGIN) {
+ ret = atmel_qspi_set_cs(dev, 1);
+ if (ret)
+ return log_ret(ret);
+ reg = atmel_qspi_read(aq, QSPI_RD);
+ }
+
+ for (len_tx = 0, len_rx = 0; len_rx < len; ) {
+ u32 status = atmel_qspi_read(aq, QSPI_SR);
+ u8 value;
+
+ if (status & QSPI_SR_OVRES)
+ return log_ret(-1);
+
+ if (len_tx < len && (status & QSPI_SR_TDRE)) {
+ if (txp)
+ value = *txp++;
+ else
+ value = 0;
+ atmel_qspi_write(value, aq, QSPI_TD);
+ len_tx++;
+ }
+
+ if (status & QSPI_SR_RDRF) {
+ value = atmel_qspi_read(aq, QSPI_RD);
+ if (rxp)
+ *rxp++ = value;
+ len_rx++;
+ }
+ }
+
+out:
+ if (flags & SPI_XFER_END) {
+ readl_poll_timeout(aq->regs + QSPI_SR, reg,
+ reg & QSPI_SR_TXEMPTY,
+ ATMEL_QSPI_TIMEOUT);
+
+ atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
+
+ ret = atmel_qspi_set_cs(dev, 0);
+ if (ret)
+ return log_ret(ret);
+ }
+
+ return 0;
+}
+
static int atmel_qspi_set_speed(struct udevice *bus, uint hz)
{
struct atmel_qspi *aq = dev_get_priv(bus);
@@ -939,6 +1082,7 @@ static int atmel_qspi_set_speed(struct udevice *bus, uint hz)
return atmel_qspi_sama7g5_set_speed(bus, hz);
/* Compute the QSPI baudrate */
+ dev_dbg(bus, "bus_clk_rate: %lu, hz: %u\n", aq->bus_clk_rate, hz);
scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz);
if (scbr > 0)
scbr--;
@@ -1046,10 +1190,6 @@ static int atmel_qspi_init(struct atmel_qspi *aq)
/* Reset the QSPI controller */
atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);
- /* Set the QSPI controller by default in Serial Memory Mode */
- atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR);
- aq->mr = QSPI_MR_SMM;
-
/* Enable the QSPI controller */
atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
@@ -1075,7 +1215,7 @@ static int atmel_qspi_probe(struct udevice *dev)
aq->caps = (struct atmel_qspi_caps *)dev_get_driver_data(dev);
if (!aq->caps) {
dev_err(dev, "Could not retrieve QSPI caps\n");
- return -EINVAL;
+ return log_ret(-EINVAL);
};
if (aq->caps->has_gclk)
@@ -1083,36 +1223,53 @@ static int atmel_qspi_probe(struct udevice *dev)
else
aq->ops = &atmel_qspi_priv_ops;
+ if (IS_ENABLED(CONFIG_DM_GPIO)) {
+ ret = gpio_request_list_by_name(dev, "cs-gpios", aq->cs_gpios,
+ ARRAY_SIZE(aq->cs_gpios), 0);
+ if (ret < 0) {
+ pr_err("Can't get %s gpios! Error: %d", dev->name, ret);
+ return log_ret(ret);
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) {
+ if (!dm_gpio_is_valid(&aq->cs_gpios[i]))
+ continue;
+
+ dm_gpio_set_dir_flags(&aq->cs_gpios[i],
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ }
+ }
+
/* Map the registers */
ret = dev_read_resource_byname(dev, "qspi_base", &res);
if (ret) {
dev_err(dev, "missing registers\n");
- return ret;
+ return log_ret(ret);
}
aq->regs = devm_ioremap(dev, res.start, resource_size(&res));
if (IS_ERR(aq->regs))
- return PTR_ERR(aq->regs);
+ return log_ret(PTR_ERR(aq->regs));
/* Map the AHB memory */
ret = dev_read_resource_byname(dev, "qspi_mmap", &res);
if (ret) {
dev_err(dev, "missing AHB memory\n");
- return ret;
+ return log_ret(ret);
}
aq->mem = devm_ioremap(dev, res.start, resource_size(&res));
if (IS_ERR(aq->mem))
- return PTR_ERR(aq->mem);
+ return log_ret(PTR_ERR(aq->mem));
aq->mmap_size = resource_size(&res);
ret = atmel_qspi_enable_clk(dev);
if (ret)
- return ret;
+ return log_ret(ret);
aq->dev = dev;
- return atmel_qspi_init(aq);
+ return log_ret(atmel_qspi_init(aq));
}
static const struct spi_controller_mem_ops atmel_qspi_mem_ops = {
@@ -1121,9 +1278,12 @@ static const struct spi_controller_mem_ops atmel_qspi_mem_ops = {
};
static const struct dm_spi_ops atmel_qspi_ops = {
+ .claim_bus = atmel_qspi_claim_bus,
+ .release_bus = atmel_qspi_release_bus,
+ .xfer = atmel_qspi_xfer,
+ .mem_ops = &atmel_qspi_mem_ops,
.set_speed = atmel_qspi_set_speed,
.set_mode = atmel_qspi_set_mode,
- .mem_ops = &atmel_qspi_mem_ops,
};
static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {};
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..9edbfaa821b 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -27,12 +27,20 @@
#define CQSPI_READ 2
#define CQSPI_WRITE 3
+/* Quirks */
+#define CQSPI_DISABLE_STIG_MODE BIT(0)
+
__weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
return 0;
}
+__weak int cadence_device_reset(struct udevice *dev)
+{
+ return 0;
+}
+
__weak int cadence_qspi_flash_reset(struct udevice *dev)
{
return 0;
@@ -217,6 +225,7 @@ static int cadence_spi_probe(struct udevice *bus)
priv->tsd2d_ns = plat->tsd2d_ns;
priv->tchsh_ns = plat->tchsh_ns;
priv->tslch_ns = plat->tslch_ns;
+ priv->quirks = plat->quirks;
if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE))
xilinx_pm_request(PM_REQUEST_NODE, PM_DEV_OSPI,
@@ -251,6 +260,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);
@@ -307,12 +319,16 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
* which is unsupported on some flash devices during register
* reads, prefer STIG mode for such small reads.
*/
- if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
+ if (!op->addr.nbytes ||
+ (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX &&
+ !(priv->quirks & CQSPI_DISABLE_STIG_MODE)))
mode = CQSPI_STIG_READ;
else
mode = CQSPI_READ;
} else {
- if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX)
+ if (!op->addr.nbytes || !op->data.buf.out ||
+ (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX &&
+ !(priv->quirks & CQSPI_DISABLE_STIG_MODE)))
mode = CQSPI_STIG_WRITE;
else
mode = CQSPI_WRITE;
@@ -427,6 +443,10 @@ static int cadence_spi_of_to_plat(struct udevice *bus)
plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay",
-1);
+ const struct cqspi_driver_platdata *drvdata =
+ (struct cqspi_driver_platdata *)dev_get_driver_data(bus);
+ plat->quirks = drvdata->quirks;
+
debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
__func__, plat->regbase, plat->ahbbase, plat->max_hz,
plat->page_size);
@@ -449,9 +469,21 @@ static const struct dm_spi_ops cadence_spi_ops = {
*/
};
+static const struct cqspi_driver_platdata cdns_qspi = {
+ .quirks = CQSPI_DISABLE_STIG_MODE,
+};
+
static const struct udevice_id cadence_spi_ids[] = {
- { .compatible = "cdns,qspi-nor" },
- { .compatible = "ti,am654-ospi" },
+ {
+ .compatible = "cdns,qspi-nor",
+ .data = (ulong)&cdns_qspi,
+ },
+ {
+ .compatible = "ti,am654-ospi"
+ },
+ {
+ .compatible = "amd,versal2-ospi"
+ },
{ }
};
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 1f9125cd239..80510f2542b 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)
@@ -220,6 +222,7 @@ struct cadence_spi_plat {
u32 tsd2d_ns;
u32 tchsh_ns;
u32 tslch_ns;
+ u32 quirks;
bool is_dma;
};
@@ -251,6 +254,7 @@ struct cadence_spi_priv {
u32 tsd2d_ns;
u32 tchsh_ns;
u32 tslch_ns;
+ u32 quirks;
u8 edge_mode;
u8 dll_mode;
bool extra_dummy;
@@ -266,6 +270,11 @@ struct cadence_spi_priv {
bool dtr;
};
+struct cqspi_driver_platdata {
+ u32 hwcaps_mask;
+ u32 quirks;
+};
+
/* Functions call declaration */
void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv);
void cadence_qspi_apb_controller_enable(void *reg_base_addr);
@@ -310,5 +319,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/soft_spi.c b/drivers/spi/soft_spi.c
index a8ec2f4f7b4..50bd7be5640 100644
--- a/drivers/spi/soft_spi.c
+++ b/drivers/spi/soft_spi.c
@@ -124,8 +124,19 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
u8 *rxd = din;
int cpha = !!(priv->mode & SPI_CPHA);
int cidle = !!(priv->mode & SPI_CPOL);
+ int txrx = plat->flags;
unsigned int j;
+ if (priv->mode & SPI_3WIRE) {
+ if (txd && rxd)
+ return -EINVAL;
+
+ txrx = txd ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX;
+ dm_gpio_set_dir_flags(&plat->mosi,
+ txd ? GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE :
+ GPIOD_IS_IN | GPIOD_PULL_UP);
+ }
+
debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd,
bitlen);
@@ -160,7 +171,7 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
*/
if (cpha)
soft_spi_scl(dev, !cidle);
- if ((plat->flags & SPI_MASTER_NO_TX) == 0)
+ if ((txrx & SPI_MASTER_NO_TX) == 0)
soft_spi_sda(dev, !!(tmpdout & 0x80));
udelay(plat->spi_delay_us);
@@ -174,8 +185,10 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
else
soft_spi_scl(dev, cidle);
tmpdin <<= 1;
- if ((plat->flags & SPI_MASTER_NO_RX) == 0)
- tmpdin |= dm_gpio_get_value(&plat->miso);
+ if ((txrx & SPI_MASTER_NO_RX) == 0)
+ tmpdin |= dm_gpio_get_value((priv->mode & SPI_3WIRE) ?
+ &plat->mosi :
+ &plat->miso);
tmpdout <<= 1;
udelay(plat->spi_delay_us);
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, &regs->status);
debug("%s: STATUS = %08x\n", __func__, readl(&regs->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(&regs->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/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
index 2030e4babc9..df83df69ffb 100644
--- a/drivers/sysinfo/Kconfig
+++ b/drivers/sysinfo/Kconfig
@@ -31,6 +31,13 @@ config SYSINFO_RCAR3
help
Support querying SoC version information for Renesas R-Car Gen3.
+config SYSINFO_IOT2050
+ bool "Enable sysinfo driver for the Siemens IOT2050"
+ depends on TARGET_IOT2050_A53
+ default y if TARGET_IOT2050_A53
+ help
+ Support querying device information for Siemens IOT2050.
+
config SYSINFO_SANDBOX
bool "Enable sysinfo driver for the Sandbox board"
help
diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
index 680dde77fe8..26ca3150999 100644
--- a/drivers/sysinfo/Makefile
+++ b/drivers/sysinfo/Makefile
@@ -5,6 +5,7 @@
obj-y += sysinfo-uclass.o
obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
+obj-$(CONFIG_SYSINFO_IOT2050) += iot2050.o
obj-$(CONFIG_SYSINFO_RCAR3) += rcar3.o
obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
diff --git a/drivers/sysinfo/iot2050.c b/drivers/sysinfo/iot2050.c
new file mode 100644
index 00000000000..579a9f4711d
--- /dev/null
+++ b/drivers/sysinfo/iot2050.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Siemens AG, 2025
+ */
+
+#include <dm.h>
+#include <sysinfo.h>
+#include <net.h>
+#include <u-boot/uuid.h>
+#include <asm/arch/hardware.h>
+
+#include "iot2050.h"
+
+#define IOT2050_INFO_MAGIC 0x20502050
+
+#define IOT2050_UUID_STR_LEN (32)
+
+struct iot2050_info {
+ u32 magic;
+ u16 size;
+ char name[20 + 1];
+ char serial[16 + 1];
+ char mlfb[18 + 1];
+ char uuid[IOT2050_UUID_STR_LEN + 1];
+ char a5e[18 + 1];
+ u8 mac_addr_cnt;
+ u8 mac_addr[8][ARP_HLEN];
+ char seboot_version[40 + 1];
+ u8 padding[3];
+ u32 ddr_size_mb;
+} __packed;
+
+/**
+ * struct sysinfo_iot2050_priv - sysinfo private data
+ * @info: iot2050 board info
+ */
+struct sysinfo_iot2050_priv {
+ struct iot2050_info *info;
+ u8 uuid_smbios[16];
+};
+
+static int sysinfo_iot2050_detect(struct udevice *dev)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+
+ if (!priv->info || priv->info->magic != IOT2050_INFO_MAGIC)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int sysinfo_iot2050_get_str(struct udevice *dev, int id, size_t size,
+ char *val)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+
+ switch (id) {
+ case BOARD_NAME:
+ case SYSID_SM_BASEBOARD_VERSION:
+ strlcpy(val, priv->info->name, size);
+ break;
+ case SYSID_SM_SYSTEM_SERIAL:
+ strlcpy(val, priv->info->serial, size);
+ break;
+ case BOARD_MLFB:
+ case SYSID_SM_SYSTEM_VERSION:
+ strlcpy(val, priv->info->mlfb, size);
+ break;
+ case BOARD_UUID:
+ strlcpy(val, priv->info->uuid, size);
+ break;
+ case BOARD_A5E:
+ case SYSID_SM_BASEBOARD_PRODUCT:
+ strlcpy(val, priv->info->a5e, size);
+ break;
+ case BOARD_SEBOOT_VER:
+ case SYSID_PRIOR_STAGE_VERSION:
+ strlcpy(val, priv->info->seboot_version, size);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ val[size - 1] = '\0';
+ return 0;
+}
+
+static int sysinfo_iot2050_get_int(struct udevice *dev, int id, int *val)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+
+ switch (id) {
+ case SYSID_BOARD_RAM_SIZE_MB:
+ *val = priv->info->ddr_size_mb;
+ return 0;
+ default:
+ return -EINVAL;
+ };
+}
+
+static int sysinfo_iot2050_get_data(struct udevice *dev, int id, void **data,
+ size_t *size)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+
+ switch (id) {
+ case SYSID_SM_SYSTEM_UUID:
+ *data = priv->uuid_smbios;
+ *size = 16;
+ return 0;
+ default:
+ return -EINVAL;
+ };
+}
+
+static int sysinfo_iot2050_get_item_count(struct udevice *dev, int id)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+
+ switch (id) {
+ case SYSID_BOARD_MAC_ADDR:
+ return priv->info->mac_addr_cnt;
+ default:
+ return -EINVAL;
+ };
+}
+
+static int sysinfo_iot2050_get_data_by_index(struct udevice *dev, int id,
+ int index, void **data,
+ size_t *size)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+
+ switch (id) {
+ case SYSID_BOARD_MAC_ADDR:
+ if (index >= priv->info->mac_addr_cnt)
+ return -EINVAL;
+ *data = priv->info->mac_addr[index];
+ *size = ARP_HLEN;
+ return 0;
+ default:
+ return -EINVAL;
+ };
+}
+
+static const struct sysinfo_ops sysinfo_iot2050_ops = {
+ .detect = sysinfo_iot2050_detect,
+ .get_str = sysinfo_iot2050_get_str,
+ .get_int = sysinfo_iot2050_get_int,
+ .get_data = sysinfo_iot2050_get_data,
+ .get_item_count = sysinfo_iot2050_get_item_count,
+ .get_data_by_index = sysinfo_iot2050_get_data_by_index,
+};
+
+/**
+ * @brief Convert the IOT2050 UUID string to the SMBIOS format
+ *
+ * @param uuid_raw The IOT2050 UUID string parsed from the eeprom
+ * @param uuid_smbios The buffer to hold the SMBIOS formatted UUID
+ */
+static void sysinfo_iot2050_convert_uuid(const char *uuid_iot2050,
+ u8 *uuid_smbios)
+{
+ char uuid_rfc4122_str[IOT2050_UUID_STR_LEN + 4 + 1] = {0};
+ char *tmp = uuid_rfc4122_str;
+
+ for (int i = 0; i < 16; i++) {
+ memcpy(tmp, uuid_iot2050 + i * 2, 2);
+ tmp += 2;
+ if (i == 3 || i == 5 || i == 7 || i == 9)
+ *tmp++ = '-';
+ }
+ uuid_str_to_bin(uuid_rfc4122_str, uuid_smbios, UUID_STR_FORMAT_GUID);
+}
+
+static int sysinfo_iot2050_probe(struct udevice *dev)
+{
+ struct sysinfo_iot2050_priv *priv = dev_get_priv(dev);
+ unsigned long offset;
+
+ offset = dev_read_u32_default(dev, "offset",
+ TI_SRAM_SCRATCH_BOARD_EEPROM_START);
+ priv->info = (struct iot2050_info *)offset;
+
+ sysinfo_iot2050_convert_uuid(priv->info->uuid, priv->uuid_smbios);
+
+ return 0;
+}
+
+static const struct udevice_id sysinfo_iot2050_ids[] = {
+ { .compatible = "siemens,sysinfo-iot2050" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(sysinfo_iot2050) = {
+ .name = "sysinfo_iot2050",
+ .id = UCLASS_SYSINFO,
+ .of_match = sysinfo_iot2050_ids,
+ .ops = &sysinfo_iot2050_ops,
+ .priv_auto = sizeof(struct sysinfo_iot2050_priv),
+ .probe = sysinfo_iot2050_probe,
+};
diff --git a/drivers/sysinfo/iot2050.h b/drivers/sysinfo/iot2050.h
new file mode 100644
index 00000000000..657221db096
--- /dev/null
+++ b/drivers/sysinfo/iot2050.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Siemens AG, 2025
+ */
+
+#include <sysinfo.h>
+
+enum sysinfo_id_iot2050 {
+ BOARD_MLFB = SYSID_USER,
+ BOARD_A5E,
+ BOARD_NAME,
+ BOARD_UUID,
+ BOARD_SEBOOT_VER,
+};
diff --git a/drivers/sysinfo/sysinfo-uclass.c b/drivers/sysinfo/sysinfo-uclass.c
index 3c0cd51273e..f04998ef8bb 100644
--- a/drivers/sysinfo/sysinfo-uclass.c
+++ b/drivers/sysinfo/sysinfo-uclass.c
@@ -119,6 +119,35 @@ int sysinfo_get_data(struct udevice *dev, int id, void **data, size_t *size)
return ops->get_data(dev, id, data, size);
}
+int sysinfo_get_item_count(struct udevice *dev, int id)
+{
+ struct sysinfo_priv *priv = dev_get_uclass_priv(dev);
+ struct sysinfo_ops *ops = sysinfo_get_ops(dev);
+
+ if (!priv->detected)
+ return -EPERM;
+
+ if (!ops->get_item_count)
+ return -ENOSYS;
+
+ return ops->get_item_count(dev, id);
+}
+
+int sysinfo_get_data_by_index(struct udevice *dev, int id, int index,
+ void **data, size_t *size)
+{
+ struct sysinfo_priv *priv = dev_get_uclass_priv(dev);
+ struct sysinfo_ops *ops = sysinfo_get_ops(dev);
+
+ if (!priv->detected)
+ return -EPERM;
+
+ if (!ops->get_data_by_index)
+ return -ENOSYS;
+
+ return ops->get_data_by_index(dev, id, index, data, size);
+}
+
UCLASS_DRIVER(sysinfo) = {
.id = UCLASS_SYSINFO,
.name = "sysinfo",
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 121194e4418..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
@@ -242,7 +287,6 @@ config SYSRESET_RAA215300
config SYSRESET_QCOM_PSHOLD
bool "Support sysreset for Qualcomm SoCs via PSHOLD"
- depends on ARCH_IPQ40XX
help
Add support for the system reboot on Qualcomm SoCs via PSHOLD.
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/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c
index e8b54a02965..c1baf3c69ec 100644
--- a/drivers/timer/sandbox_timer.c
+++ b/drivers/timer/sandbox_timer.c
@@ -18,6 +18,11 @@ void timer_test_add_offset(unsigned long offset)
sandbox_timer_offset += offset;
}
+ulong timer_test_get_offset(void)
+{
+ return sandbox_timer_offset;
+};
+
u64 notrace timer_early_get_count(void)
{
return os_get_nsec() / 1000 + sandbox_timer_offset * 1000;
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, &reg);
+ ret = zynqmp_pm_ufs_get_txrx_cfgrdy(&reg);
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, &reg);
+ ret = zynqmp_pm_ufs_sram_csr_read(&reg);
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/ufs/ufs.c b/drivers/ufs/ufs.c
index f7d8c40c448..91f6ad3bfef 100644
--- a/drivers/ufs/ufs.c
+++ b/drivers/ufs/ufs.c
@@ -19,6 +19,7 @@
#include <malloc.h>
#include <hexdump.h>
#include <scsi.h>
+#include <ufs.h>
#include <asm/io.h>
#include <asm/dma-mapping.h>
#include <linux/bitops.h>
@@ -313,16 +314,12 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer)
ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
&tx_lanes);
for (i = 0; i < tx_lanes; i++) {
+ unsigned int val = UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
+ UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i));
if (!peer)
- err = ufshcd_dme_set(hba,
- UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
- UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
- 0);
+ err = ufshcd_dme_set(hba, val, 0);
else
- err = ufshcd_dme_peer_set(hba,
- UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
- UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
- 0);
+ err = ufshcd_dme_peer_set(hba, val, 0);
if (err) {
dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d\n",
__func__, peer, i, err);
@@ -1034,8 +1031,8 @@ static inline void ufshcd_init_query(struct ufs_hba *hba,
/**
* ufshcd_query_flag() - API function for sending flag query requests
*/
-int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
- enum flag_idn idn, bool *flag_res)
+static int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
+ enum flag_idn idn, bool *flag_res)
{
struct ufs_query_req *request = NULL;
struct ufs_query_res *response = NULL;
@@ -1170,9 +1167,9 @@ out:
/**
* ufshcd_query_descriptor_retry - API function for sending descriptor requests
*/
-int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode,
- enum desc_idn idn, u8 index, u8 selector,
- u8 *desc_buf, int *buf_len)
+static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode,
+ enum desc_idn idn, u8 index, u8 selector,
+ u8 *desc_buf, int *buf_len)
{
int err;
int retries;
@@ -1264,8 +1261,8 @@ static void ufshcd_init_desc_sizes(struct ufs_hba *hba)
* ufshcd_map_desc_id_to_length - map descriptor IDN to its length
*
*/
-int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
- int *desc_len)
+static int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
+ int *desc_len)
{
switch (desc_id) {
case QUERY_DESC_IDN_DEVICE:
@@ -1302,15 +1299,14 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
}
return 0;
}
-EXPORT_SYMBOL(ufshcd_map_desc_id_to_length);
/**
* ufshcd_read_desc_param - read the specified descriptor parameter
*
*/
-int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id,
- int desc_index, u8 param_offset, u8 *param_read_buf,
- u8 param_size)
+static int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id,
+ int desc_index, u8 param_offset,
+ u8 *param_read_buf, u8 param_size)
{
int ret;
u8 *desc_buf;
@@ -1569,8 +1565,8 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
* ufshcd_read_string_desc - read string descriptor
*
*/
-int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
- u8 *buf, u32 size, bool ascii)
+static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index,
+ u8 *buf, u32 size, bool ascii)
{
int err = 0;
@@ -1881,7 +1877,7 @@ static void ufshcd_def_desc_sizes(struct ufs_hba *hba)
hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE;
}
-int ufs_start(struct ufs_hba *hba)
+static int ufs_start(struct ufs_hba *hba)
{
struct ufs_dev_desc card = {0};
int ret;
@@ -1962,7 +1958,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops)
ufshcd_ops_init(hba);
- /* Read capabilties registers */
+ /* Read capabilities registers */
hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES);
if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS)
hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT;
@@ -2001,7 +1997,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops)
REG_INTERRUPT_STATUS);
ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
- mb();
+ mb(); /* flush previous writes */
/* Reset the attached device */
ufshcd_device_reset(hba);
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index 00ecca350c3..53137fae3a8 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <asm/io.h>
+#include "ufshci.h"
#include "unipro.h"
struct udevice;
@@ -14,11 +15,6 @@ struct udevice;
#define RESPONSE_UPIU_SENSE_DATA_LENGTH 18
#define UFS_MAX_LUNS 0x7F
-enum {
- TASK_REQ_UPIU_SIZE_DWORDS = 8,
- TASK_RSP_UPIU_SIZE_DWORDS = 8,
- ALIGNED_UPIU_SIZE = 512,
-};
/* UFS device power modes */
enum ufs_dev_pwr_mode {
@@ -84,44 +80,6 @@ enum {
/* Offset of the response code in the UPIU header */
#define UPIU_RSP_CODE_OFFSET 8
-/* To accommodate UFS2.0 required Command type */
-enum {
- UTP_CMD_TYPE_UFS_STORAGE = 0x1,
-};
-
-enum {
- UTP_SCSI_COMMAND = 0x00000000,
- UTP_NATIVE_UFS_COMMAND = 0x10000000,
- UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
- UTP_REQ_DESC_INT_CMD = 0x01000000,
-};
-
-/* UTP Transfer Request Data Direction (DD) */
-enum {
- UTP_NO_DATA_TRANSFER = 0x00000000,
- UTP_HOST_TO_DEVICE = 0x02000000,
- UTP_DEVICE_TO_HOST = 0x04000000,
-};
-
-/* Overall command status values */
-enum {
- OCS_SUCCESS = 0x0,
- OCS_INVALID_CMD_TABLE_ATTR = 0x1,
- OCS_INVALID_PRDT_ATTR = 0x2,
- OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
- OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
- OCS_PEER_COMM_FAILURE = 0x5,
- OCS_ABORTED = 0x6,
- OCS_FATAL_ERROR = 0x7,
- OCS_INVALID_COMMAND_STATUS = 0x0F,
- MASK_OCS = 0x0F,
-};
-
-/* The maximum length of the data byte count field in the PRDT is 256KB */
-#define PRDT_DATA_BYTE_COUNT_MAX (256 * 1024)
-/* The granularity of the data byte count field in the PRDT is 32-bit */
-#define PRDT_DATA_BYTE_COUNT_PAD 4
-
#define GENERAL_UPIU_REQUEST_SIZE (sizeof(struct utp_upiu_req))
#define QUERY_DESC_MAX_SIZE 255
#define QUERY_DESC_MIN_SIZE 2
@@ -130,8 +88,8 @@ enum {
(sizeof(struct utp_upiu_header)))
#define RESPONSE_UPIU_SENSE_DATA_LENGTH 18
#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
- cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
- (byte1 << 8) | (byte0))
+ cpu_to_be32(((byte3) << 24) | ((byte2) << 16) |\
+ ((byte1) << 8) | (byte0))
/*
* UFS Protocol Information Unit related definitions
*/
@@ -297,79 +255,6 @@ enum desc_header_offset {
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
};
-struct ufshcd_sg_entry {
- __le32 base_addr;
- __le32 upper_addr;
- __le32 reserved;
- __le32 size;
-};
-
-#define MAX_BUFF 128
-/**
- * struct utp_transfer_cmd_desc - UFS Command Descriptor structure
- * @command_upiu: Command UPIU Frame address
- * @response_upiu: Response UPIU Frame address
- * @prd_table: Physical Region Descriptor
- */
-struct utp_transfer_cmd_desc {
- u8 command_upiu[ALIGNED_UPIU_SIZE];
- u8 response_upiu[ALIGNED_UPIU_SIZE];
- struct ufshcd_sg_entry prd_table[MAX_BUFF];
-};
-
-/**
- * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
- * @dword0: Descriptor Header DW0
- * @dword1: Descriptor Header DW1
- * @dword2: Descriptor Header DW2
- * @dword3: Descriptor Header DW3
- */
-struct request_desc_header {
- __le32 dword_0;
- __le32 dword_1;
- __le32 dword_2;
- __le32 dword_3;
-};
-
-/**
- * struct utp_transfer_req_desc - UTRD structure
- * @header: UTRD header DW-0 to DW-3
- * @command_desc_base_addr_lo: UCD base address low DW-4
- * @command_desc_base_addr_hi: UCD base address high DW-5
- * @response_upiu_length: response UPIU length DW-6
- * @response_upiu_offset: response UPIU offset DW-6
- * @prd_table_length: Physical region descriptor length DW-7
- * @prd_table_offset: Physical region descriptor offset DW-7
- */
-struct utp_transfer_req_desc {
- /* DW 0-3 */
- struct request_desc_header header;
-
- /* DW 4-5*/
- __le32 command_desc_base_addr_lo;
- __le32 command_desc_base_addr_hi;
-
- /* DW 6 */
- __le16 response_upiu_length;
- __le16 response_upiu_offset;
-
- /* DW 7 */
- __le16 prd_table_length;
- __le16 prd_table_offset;
-};
-
-/**
- * struct utp_upiu_header - UPIU header structure
- * @dword_0: UPIU header DW-0
- * @dword_1: UPIU header DW-1
- * @dword_2: UPIU header DW-2
- */
-struct utp_upiu_header {
- __be32 dword_0;
- __be32 dword_1;
- __be32 dword_2;
-};
-
/**
* struct utp_upiu_query - upiu request buffer structure for
* query request.
@@ -403,27 +288,6 @@ struct utp_upiu_cmd {
u8 cdb[UFS_CDB_SIZE];
};
-/*
- * UTMRD structure.
- */
-struct utp_task_req_desc {
- /* DW 0-3 */
- struct request_desc_header header;
-
- /* DW 4-11 - Task request UPIU structure */
- struct utp_upiu_header req_header;
- __be32 input_param1;
- __be32 input_param2;
- __be32 input_param3;
- __be32 __reserved1[2];
-
- /* DW 12-19 - Task Management Response UPIU structure */
- struct utp_upiu_header rsp_header;
- __be32 output_param1;
- __be32 output_param2;
- __be32 __reserved2[3];
-};
-
/**
* struct utp_upiu_req - general upiu request structure
* @header:UPIU header structure DW-0 to DW-2
@@ -551,63 +415,6 @@ struct uic_command {
int result;
};
-/* GenSelectorIndex calculation macros for M-PHY attributes */
-#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane)
-#define UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane) (PA_MAXDATALANES + (lane))
-
-#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\
- ((sel) & 0xFFFF))
-#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)
-#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16)
-#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF)
-
-/* Link Status*/
-enum link_status {
- UFSHCD_LINK_IS_DOWN = 1,
- UFSHCD_LINK_IS_UP = 2,
-};
-
-#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\
- ((sel) & 0xFFFF))
-#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)
-#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16)
-#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF)
-
-/* UIC Commands */
-enum uic_cmd_dme {
- UIC_CMD_DME_GET = 0x01,
- UIC_CMD_DME_SET = 0x02,
- UIC_CMD_DME_PEER_GET = 0x03,
- UIC_CMD_DME_PEER_SET = 0x04,
- UIC_CMD_DME_POWERON = 0x10,
- UIC_CMD_DME_POWEROFF = 0x11,
- UIC_CMD_DME_ENABLE = 0x12,
- UIC_CMD_DME_RESET = 0x14,
- UIC_CMD_DME_END_PT_RST = 0x15,
- UIC_CMD_DME_LINK_STARTUP = 0x16,
- UIC_CMD_DME_HIBER_ENTER = 0x17,
- UIC_CMD_DME_HIBER_EXIT = 0x18,
- UIC_CMD_DME_TEST_MODE = 0x1A,
-};
-
-/* UIC Config result code / Generic error code */
-enum {
- UIC_CMD_RESULT_SUCCESS = 0x00,
- UIC_CMD_RESULT_INVALID_ATTR = 0x01,
- UIC_CMD_RESULT_FAILURE = 0x01,
- UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
- UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
- UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
- UIC_CMD_RESULT_BAD_INDEX = 0x05,
- UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
- UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
- UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
- UIC_CMD_RESULT_BUSY = 0x09,
- UIC_CMD_RESULT_DME_FAILURE = 0x0A,
-};
-
-#define MASK_UIC_COMMAND_RESULT 0xFF
-
/* Host <-> Device UniPro Link state */
enum uic_link_state {
UIC_LINK_OFF_STATE = 0, /* Link powered down or disabled */
@@ -915,7 +722,7 @@ static inline int ufshcd_ops_get_max_pwr_mode(struct ufs_hba *hba,
}
static inline int ufshcd_ops_hce_enable_notify(struct ufs_hba *hba,
- bool status)
+ bool status)
{
if (hba->ops && hba->ops->hce_enable_notify)
return hba->ops->hce_enable_notify(hba, status);
@@ -940,17 +747,6 @@ static inline int ufshcd_vops_device_reset(struct ufs_hba *hba)
return 0;
}
-/* Controller UFSHCI version */
-enum {
- UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */
- UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */
- UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
- UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */
- UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */
- UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */
- UFSHCI_VERSION_40 = 0x00000400, /* 4.0 */
-};
-
/* Interrupt disable masks */
enum {
/* Interrupt disable mask for UFSHCI v1.0 */
@@ -964,123 +760,6 @@ enum {
INTERRUPT_MASK_ALL_VER_21 = 0x71FFF,
};
-/* UFSHCI Registers */
-enum {
- REG_CONTROLLER_CAPABILITIES = 0x00,
- REG_UFS_VERSION = 0x08,
- REG_CONTROLLER_DEV_ID = 0x10,
- REG_CONTROLLER_PROD_ID = 0x14,
- REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18,
- REG_INTERRUPT_STATUS = 0x20,
- REG_INTERRUPT_ENABLE = 0x24,
- REG_CONTROLLER_STATUS = 0x30,
- REG_CONTROLLER_ENABLE = 0x34,
- REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38,
- REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C,
- REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40,
- REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44,
- REG_UIC_ERROR_CODE_DME = 0x48,
- REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C,
- REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50,
- REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54,
- REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58,
- REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C,
- REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60,
- REG_UTP_TASK_REQ_LIST_BASE_L = 0x70,
- REG_UTP_TASK_REQ_LIST_BASE_H = 0x74,
- REG_UTP_TASK_REQ_DOOR_BELL = 0x78,
- REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C,
- REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80,
- REG_UIC_COMMAND = 0x90,
- REG_UIC_COMMAND_ARG_1 = 0x94,
- REG_UIC_COMMAND_ARG_2 = 0x98,
- REG_UIC_COMMAND_ARG_3 = 0x9C,
-
- UFSHCI_REG_SPACE_SIZE = 0xA0,
-
- REG_UFS_CCAP = 0x100,
- REG_UFS_CRYPTOCAP = 0x104,
-
- UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400,
-};
-
-/* Controller capability masks */
-enum {
- MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
- MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
- MASK_AUTO_HIBERN8_SUPPORT = 0x00800000,
- MASK_64_ADDRESSING_SUPPORT = 0x01000000,
- MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
- MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
-};
-
-/* Interrupt Status 20h */
-#define UTP_TRANSFER_REQ_COMPL 0x1
-#define UIC_DME_END_PT_RESET 0x2
-#define UIC_ERROR 0x4
-#define UIC_TEST_MODE 0x8
-#define UIC_POWER_MODE 0x10
-#define UIC_HIBERNATE_EXIT 0x20
-#define UIC_HIBERNATE_ENTER 0x40
-#define UIC_LINK_LOST 0x80
-#define UIC_LINK_STARTUP 0x100
-#define UTP_TASK_REQ_COMPL 0x200
-#define UIC_COMMAND_COMPL 0x400
-#define DEVICE_FATAL_ERROR 0x800
-#define CONTROLLER_FATAL_ERROR 0x10000
-#define SYSTEM_BUS_FATAL_ERROR 0x20000
-
-#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\
- UIC_HIBERNATE_EXIT |\
- UIC_POWER_MODE)
-
-#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UIC_POWER_MODE)
-
-#define UFSHCD_ERROR_MASK (UIC_ERROR |\
- DEVICE_FATAL_ERROR |\
- CONTROLLER_FATAL_ERROR |\
- SYSTEM_BUS_FATAL_ERROR)
-
-#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
- CONTROLLER_FATAL_ERROR |\
- SYSTEM_BUS_FATAL_ERROR)
-
-/* Host Controller Enable 0x34h */
-#define CONTROLLER_ENABLE 0x1
-#define CONTROLLER_DISABLE 0x0
-/* HCS - Host Controller Status 30h */
-#define DEVICE_PRESENT 0x1
-#define UTP_TRANSFER_REQ_LIST_READY 0x2
-#define UTP_TASK_REQ_LIST_READY 0x4
-#define UIC_COMMAND_READY 0x8
-#define HOST_ERROR_INDICATOR 0x10
-#define DEVICE_ERROR_INDICATOR 0x20
-#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8)
-
-#define UFSHCD_STATUS_READY (UTP_TRANSFER_REQ_LIST_READY |\
- UTP_TASK_REQ_LIST_READY |\
- UIC_COMMAND_READY)
-
-enum {
- PWR_OK = 0x0,
- PWR_LOCAL = 0x01,
- PWR_REMOTE = 0x02,
- PWR_BUSY = 0x03,
- PWR_ERROR_CAP = 0x04,
- PWR_FATAL_ERROR = 0x05,
-};
-
-/* UICCMD - UIC Command */
-#define COMMAND_OPCODE_MASK 0xFF
-#define GEN_SELECTOR_INDEX_MASK 0xFFFF
-
-#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16)
-#define RESET_LEVEL 0xFF
-
-#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16)
-#define CFG_RESULT_CODE_MASK 0xFF
-#define GENERIC_ERROR_CODE_MASK 0xFF
-
#define ufshcd_writel(hba, val, reg) \
writel((val), (hba)->mmio_base + (reg))
#define ufshcd_readl(hba, reg) \
@@ -1103,12 +782,6 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
ufshcd_writel(hba, tmp, reg);
}
-/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
-#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT 0x1
-
-/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
-#define UTP_TASK_REQ_LIST_RUN_STOP_BIT 0x1
-
int ufshcd_probe(struct udevice *dev, struct ufs_hba_ops *hba_ops);
#endif
diff --git a/drivers/ufs/ufshci.h b/drivers/ufs/ufshci.h
new file mode 100644
index 00000000000..90cbf87a3a4
--- /dev/null
+++ b/drivers/ufs/ufshci.h
@@ -0,0 +1,469 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __UFSHCI_H
+#define __UFSHCI_H
+
+enum {
+ TASK_REQ_UPIU_SIZE_DWORDS = 8,
+ TASK_RSP_UPIU_SIZE_DWORDS = 8,
+ ALIGNED_UPIU_SIZE = 512,
+};
+
+/* UFSHCI Registers */
+enum {
+ REG_CONTROLLER_CAPABILITIES = 0x00,
+ REG_MCQCAP = 0x04,
+ REG_UFS_VERSION = 0x08,
+ REG_EXT_CONTROLLER_CAPABILITIES = 0x0C,
+ REG_CONTROLLER_PID = 0x10,
+ REG_CONTROLLER_MID = 0x14,
+ REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18,
+ REG_INTERRUPT_STATUS = 0x20,
+ REG_INTERRUPT_ENABLE = 0x24,
+ REG_CONTROLLER_STATUS = 0x30,
+ REG_CONTROLLER_ENABLE = 0x34,
+ REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38,
+ REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C,
+ REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40,
+ REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44,
+ REG_UIC_ERROR_CODE_DME = 0x48,
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C,
+ REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50,
+ REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54,
+ REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58,
+ REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C,
+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60,
+ REG_UTP_TASK_REQ_LIST_BASE_L = 0x70,
+ REG_UTP_TASK_REQ_LIST_BASE_H = 0x74,
+ REG_UTP_TASK_REQ_DOOR_BELL = 0x78,
+ REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C,
+ REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80,
+ REG_UIC_COMMAND = 0x90,
+ REG_UIC_COMMAND_ARG_1 = 0x94,
+ REG_UIC_COMMAND_ARG_2 = 0x98,
+ REG_UIC_COMMAND_ARG_3 = 0x9C,
+
+ UFSHCI_REG_SPACE_SIZE = 0xA0,
+
+ REG_UFS_CCAP = 0x100,
+ REG_UFS_CRYPTOCAP = 0x104,
+
+ REG_UFS_MEM_CFG = 0x300,
+ REG_UFS_MCQ_CFG = 0x380,
+ REG_UFS_ESILBA = 0x384,
+ REG_UFS_ESIUBA = 0x388,
+ UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400,
+};
+
+/* Controller capability masks */
+enum {
+ MASK_TRANSFER_REQUESTS_SLOTS_SDB = 0x0000001F,
+ MASK_TRANSFER_REQUESTS_SLOTS_MCQ = 0x000000FF,
+ MASK_NUMBER_OUTSTANDING_RTT = 0x0000FF00,
+ MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
+ MASK_EHSLUTRD_SUPPORTED = 0x00400000,
+ MASK_AUTO_HIBERN8_SUPPORT = 0x00800000,
+ MASK_64_ADDRESSING_SUPPORT = 0x01000000,
+ MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
+ MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
+ MASK_CRYPTO_SUPPORT = 0x10000000,
+ MASK_LSDB_SUPPORT = 0x20000000,
+ MASK_MCQ_SUPPORT = 0x40000000,
+};
+
+/* MCQ capability mask */
+enum {
+ MASK_EXT_IID_SUPPORT = 0x00000400,
+};
+
+enum {
+ REG_SQATTR = 0x0,
+ REG_SQLBA = 0x4,
+ REG_SQUBA = 0x8,
+ REG_SQDAO = 0xC,
+ REG_SQISAO = 0x10,
+
+ REG_CQATTR = 0x20,
+ REG_CQLBA = 0x24,
+ REG_CQUBA = 0x28,
+ REG_CQDAO = 0x2C,
+ REG_CQISAO = 0x30,
+};
+
+enum {
+ REG_SQHP = 0x0,
+ REG_SQTP = 0x4,
+ REG_SQRTC = 0x8,
+ REG_SQCTI = 0xC,
+ REG_SQRTS = 0x10,
+};
+
+enum {
+ REG_CQHP = 0x0,
+ REG_CQTP = 0x4,
+};
+
+enum {
+ REG_CQIS = 0x0,
+ REG_CQIE = 0x4,
+};
+
+enum {
+ SQ_START = 0x0,
+ SQ_STOP = 0x1,
+ SQ_ICU = 0x2,
+};
+
+enum {
+ SQ_STS = 0x1,
+ SQ_CUS = 0x2,
+};
+
+#define SQ_ICU_ERR_CODE_MASK GENMASK(7, 4)
+#define UFS_MASK(mask, offset) ((mask) << (offset))
+
+/* UFS Version 08h */
+#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0)
+#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16)
+
+/* Controller UFSHCI version */
+enum {
+ UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */
+ UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */
+ UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
+ UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */
+ UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */
+ UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */
+ UFSHCI_VERSION_40 = 0x00000400, /* 4.0 */
+};
+
+/*
+ * IS - Interrupt Status - 20h
+ */
+#define UTP_TRANSFER_REQ_COMPL 0x1
+#define UIC_DME_END_PT_RESET 0x2
+#define UIC_ERROR 0x4
+#define UIC_TEST_MODE 0x8
+#define UIC_POWER_MODE 0x10
+#define UIC_HIBERNATE_EXIT 0x20
+#define UIC_HIBERNATE_ENTER 0x40
+#define UIC_LINK_LOST 0x80
+#define UIC_LINK_STARTUP 0x100
+#define UTP_TASK_REQ_COMPL 0x200
+#define UIC_COMMAND_COMPL 0x400
+#define DEVICE_FATAL_ERROR 0x800
+#define CONTROLLER_FATAL_ERROR 0x10000
+#define SYSTEM_BUS_FATAL_ERROR 0x20000
+#define CRYPTO_ENGINE_FATAL_ERROR 0x40000
+#define MCQ_CQ_EVENT_STATUS 0x100000
+
+#define UFSHCD_UIC_HIBERN8_MASK (UIC_HIBERNATE_ENTER |\
+ UIC_HIBERNATE_EXIT)
+
+#define UFSHCD_UIC_PWR_MASK (UFSHCD_UIC_HIBERN8_MASK |\
+ UIC_POWER_MODE)
+
+#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)
+
+#define UFSHCD_ERROR_MASK (UIC_ERROR | INT_FATAL_ERRORS)
+
+#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
+ CONTROLLER_FATAL_ERROR |\
+ SYSTEM_BUS_FATAL_ERROR |\
+ CRYPTO_ENGINE_FATAL_ERROR |\
+ UIC_LINK_LOST)
+
+/* HCS - Host Controller Status 30h */
+#define DEVICE_PRESENT 0x1
+#define UTP_TRANSFER_REQ_LIST_READY 0x2
+#define UTP_TASK_REQ_LIST_READY 0x4
+#define UIC_COMMAND_READY 0x8
+#define HOST_ERROR_INDICATOR 0x10
+#define DEVICE_ERROR_INDICATOR 0x20
+#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8)
+
+#define UFSHCD_STATUS_READY (UTP_TRANSFER_REQ_LIST_READY |\
+ UTP_TASK_REQ_LIST_READY |\
+ UIC_COMMAND_READY)
+
+enum {
+ PWR_OK = 0x0,
+ PWR_LOCAL = 0x01,
+ PWR_REMOTE = 0x02,
+ PWR_BUSY = 0x03,
+ PWR_ERROR_CAP = 0x04,
+ PWR_FATAL_ERROR = 0x05,
+};
+
+/* HCE - Host Controller Enable 34h */
+#define CONTROLLER_ENABLE 0x1
+#define CONTROLLER_DISABLE 0x0
+#define CRYPTO_GENERAL_ENABLE 0x2
+
+/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
+#define UIC_PHY_ADAPTER_LAYER_ERROR 0x80000000
+#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F
+#define UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK 0xF
+#define UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR 0x10
+
+/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
+#define UIC_DATA_LINK_LAYER_ERROR 0x80000000
+#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK 0xFFFF
+#define UIC_DATA_LINK_LAYER_ERROR_TCX_REP_TIMER_EXP 0x2
+#define UIC_DATA_LINK_LAYER_ERROR_AFCX_REQ_TIMER_EXP 0x4
+#define UIC_DATA_LINK_LAYER_ERROR_FCX_PRO_TIMER_EXP 0x8
+#define UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF 0x20
+#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT 0x2000
+#define UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED 0x0001
+#define UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT 0x0002
+
+/* UECN - Host UIC Error Code Network Layer 40h */
+#define UIC_NETWORK_LAYER_ERROR 0x80000000
+#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7
+#define UIC_NETWORK_UNSUPPORTED_HEADER_TYPE 0x1
+#define UIC_NETWORK_BAD_DEVICEID_ENC 0x2
+#define UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING 0x4
+
+/* UECT - Host UIC Error Code Transport Layer 44h */
+#define UIC_TRANSPORT_LAYER_ERROR 0x80000000
+#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F
+#define UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE 0x1
+#define UIC_TRANSPORT_UNKNOWN_CPORTID 0x2
+#define UIC_TRANSPORT_NO_CONNECTION_RX 0x4
+#define UIC_TRANSPORT_CONTROLLED_SEGMENT_DROPPING 0x8
+#define UIC_TRANSPORT_BAD_TC 0x10
+#define UIC_TRANSPORT_E2E_CREDIT_OVERFOW 0x20
+#define UIC_TRANSPORT_SAFETY_VALUE_DROPPING 0x40
+
+/* UECDME - Host UIC Error Code DME 48h */
+#define UIC_DME_ERROR 0x80000000
+#define UIC_DME_ERROR_CODE_MASK 0x1
+
+/* UTRIACR - Interrupt Aggregation control register - 0x4Ch */
+#define INT_AGGR_TIMEOUT_VAL_MASK 0xFF
+#define INT_AGGR_COUNTER_THRESHOLD_MASK UFS_MASK(0x1F, 8)
+#define INT_AGGR_COUNTER_AND_TIMER_RESET 0x10000
+#define INT_AGGR_STATUS_BIT 0x100000
+#define INT_AGGR_PARAM_WRITE 0x1000000
+#define INT_AGGR_ENABLE 0x80000000
+
+/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
+#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT 0x1
+
+/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
+#define UTP_TASK_REQ_LIST_RUN_STOP_BIT 0x1
+
+/* REG_UFS_MEM_CFG - Global Config Registers 300h */
+#define MCQ_MODE_SELECT BIT(0)
+
+/* CQISy - CQ y Interrupt Status Register */
+#define UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS 0x1
+
+/* UICCMD - UIC Command */
+#define COMMAND_OPCODE_MASK 0xFF
+#define GEN_SELECTOR_INDEX_MASK 0xFFFF
+
+#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16)
+#define RESET_LEVEL 0xFF
+
+#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16)
+#define CFG_RESULT_CODE_MASK 0xFF
+#define GENERIC_ERROR_CODE_MASK 0xFF
+
+/* GenSelectorIndex calculation macros for M-PHY attributes */
+#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane)
+#define UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane) (PA_MAXDATALANES + (lane))
+
+#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\
+ ((sel) & 0xFFFF))
+#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)
+#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16)
+#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF)
+
+/* Link Status*/
+enum link_status {
+ UFSHCD_LINK_IS_DOWN = 1,
+ UFSHCD_LINK_IS_UP = 2,
+};
+
+/* UIC Commands */
+enum uic_cmd_dme {
+ UIC_CMD_DME_GET = 0x01,
+ UIC_CMD_DME_SET = 0x02,
+ UIC_CMD_DME_PEER_GET = 0x03,
+ UIC_CMD_DME_PEER_SET = 0x04,
+ UIC_CMD_DME_POWERON = 0x10,
+ UIC_CMD_DME_POWEROFF = 0x11,
+ UIC_CMD_DME_ENABLE = 0x12,
+ UIC_CMD_DME_RESET = 0x14,
+ UIC_CMD_DME_END_PT_RST = 0x15,
+ UIC_CMD_DME_LINK_STARTUP = 0x16,
+ UIC_CMD_DME_HIBER_ENTER = 0x17,
+ UIC_CMD_DME_HIBER_EXIT = 0x18,
+ UIC_CMD_DME_TEST_MODE = 0x1A,
+};
+
+/* UIC Config result code / Generic error code */
+enum {
+ UIC_CMD_RESULT_SUCCESS = 0x00,
+ UIC_CMD_RESULT_INVALID_ATTR = 0x01,
+ UIC_CMD_RESULT_FAILURE = 0x01,
+ UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
+ UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
+ UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
+ UIC_CMD_RESULT_BAD_INDEX = 0x05,
+ UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
+ UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
+ UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
+ UIC_CMD_RESULT_BUSY = 0x09,
+ UIC_CMD_RESULT_DME_FAILURE = 0x0A,
+};
+
+#define MASK_UIC_COMMAND_RESULT 0xFF
+
+#define INT_AGGR_COUNTER_THLD_VAL(c) (((c) & 0x1F) << 8)
+#define INT_AGGR_TIMEOUT_VAL(t) (((t) & 0xFF) << 0)
+
+/*
+ * Request Descriptor Definitions
+ */
+
+/* To accommodate UFS2.0 required Command type */
+enum {
+ UTP_CMD_TYPE_UFS_STORAGE = 0x1,
+};
+
+enum {
+ UTP_SCSI_COMMAND = 0x00000000,
+ UTP_REQ_DESC_INT_CMD = 0x01000000,
+ UTP_NATIVE_UFS_COMMAND = 0x10000000,
+ UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
+};
+
+/* UTP Transfer Request Data Direction (DD) */
+enum utp_data_direction {
+ UTP_NO_DATA_TRANSFER = 0,
+ UTP_HOST_TO_DEVICE = 1,
+ UTP_DEVICE_TO_HOST = 2,
+};
+
+/* Overall command status values */
+enum utp_ocs {
+ OCS_SUCCESS = 0x0,
+ OCS_INVALID_CMD_TABLE_ATTR = 0x1,
+ OCS_INVALID_PRDT_ATTR = 0x2,
+ OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
+ OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
+ OCS_PEER_COMM_FAILURE = 0x5,
+ OCS_ABORTED = 0x6,
+ OCS_FATAL_ERROR = 0x7,
+ OCS_DEVICE_FATAL_ERROR = 0x8,
+ OCS_INVALID_CRYPTO_CONFIG = 0x9,
+ OCS_GENERAL_CRYPTO_ERROR = 0xA,
+ OCS_INVALID_COMMAND_STATUS = 0x0F,
+};
+
+enum {
+ MASK_OCS = 0x0F,
+};
+
+/* The maximum length of the data byte count field in the PRDT is 256KB */
+#define PRDT_DATA_BYTE_COUNT_MAX SZ_256K
+/* The granularity of the data byte count field in the PRDT is 32-bit */
+#define PRDT_DATA_BYTE_COUNT_PAD 4
+
+
+struct ufshcd_sg_entry {
+ __le32 base_addr;
+ __le32 upper_addr;
+ __le32 reserved;
+ __le32 size;
+};
+
+#define MAX_BUFF 128
+/**
+ * struct utp_transfer_cmd_desc - UFS Command Descriptor structure
+ * @command_upiu: Command UPIU Frame address
+ * @response_upiu: Response UPIU Frame address
+ * @prd_table: Physical Region Descriptor
+ */
+struct utp_transfer_cmd_desc {
+ u8 command_upiu[ALIGNED_UPIU_SIZE];
+ u8 response_upiu[ALIGNED_UPIU_SIZE];
+ struct ufshcd_sg_entry prd_table[MAX_BUFF];
+};
+
+/**
+ * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
+ * @dword0: Descriptor Header DW0
+ * @dword1: Descriptor Header DW1
+ * @dword2: Descriptor Header DW2
+ * @dword3: Descriptor Header DW3
+ */
+struct request_desc_header {
+ __le32 dword_0;
+ __le32 dword_1;
+ __le32 dword_2;
+ __le32 dword_3;
+};
+
+/**
+ * struct utp_transfer_req_desc - UTRD structure
+ * @header: UTRD header DW-0 to DW-3
+ * @command_desc_base_addr_lo: UCD base address low DW-4
+ * @command_desc_base_addr_hi: UCD base address high DW-5
+ * @response_upiu_length: response UPIU length DW-6
+ * @response_upiu_offset: response UPIU offset DW-6
+ * @prd_table_length: Physical region descriptor length DW-7
+ * @prd_table_offset: Physical region descriptor offset DW-7
+ */
+struct utp_transfer_req_desc {
+ /* DW 0-3 */
+ struct request_desc_header header;
+
+ /* DW 4-5*/
+ __le32 command_desc_base_addr_lo;
+ __le32 command_desc_base_addr_hi;
+
+ /* DW 6 */
+ __le16 response_upiu_length;
+ __le16 response_upiu_offset;
+
+ /* DW 7 */
+ __le16 prd_table_length;
+ __le16 prd_table_offset;
+};
+
+/**
+ * struct utp_upiu_header - UPIU header structure
+ * @dword_0: UPIU header DW-0
+ * @dword_1: UPIU header DW-1
+ * @dword_2: UPIU header DW-2
+ */
+struct utp_upiu_header {
+ __be32 dword_0;
+ __be32 dword_1;
+ __be32 dword_2;
+};
+
+/*
+ * UTMRD structure.
+ */
+struct utp_task_req_desc {
+ /* DW 0-3 */
+ struct request_desc_header header;
+
+ /* DW 4-11 - Task request UPIU structure */
+ struct utp_upiu_header req_header;
+ __be32 input_param1;
+ __be32 input_param2;
+ __be32 input_param3;
+ __be32 __reserved1[2];
+
+ /* DW 12-19 - Task Management Response UPIU structure */
+ struct utp_upiu_header rsp_header;
+ __be32 output_param1;
+ __be32 output_param2;
+ __be32 __reserved2[3];
+};
+
+#endif
diff --git a/drivers/ufs/unipro.h b/drivers/ufs/unipro.h
index 6df953e6e60..56833602b77 100644
--- a/drivers/ufs/unipro.h
+++ b/drivers/ufs/unipro.h
@@ -34,6 +34,18 @@
/*
* M-RX Configuration Attributes
*/
+#define RX_HS_G1_SYNC_LENGTH_CAP 0x008B
+#define RX_HS_G1_PREP_LENGTH_CAP 0x008C
+#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F
+#define RX_HIBERN8TIME_CAPABILITY 0x0092
+#define RX_HS_G2_SYNC_LENGTH_CAP 0x0094
+#define RX_HS_G3_SYNC_LENGTH_CAP 0x0095
+#define RX_HS_G2_PREP_LENGTH_CAP 0x0096
+#define RX_HS_G3_PREP_LENGTH_CAP 0x0097
+#define RX_ADV_GRANULARITY_CAP 0x0098
+#define RX_HIBERN8TIME_CAP 0x0092
+#define RX_ADV_HIBERN8TIME_CAP 0x0099
+#define RX_ADV_MIN_ACTIVATETIME_CAP 0x009A
#define RX_MODE 0x00A1
#define RX_HSRATE_SERIES 0x00A2
#define RX_HSGEAR 0x00A3
@@ -42,24 +54,27 @@
#define RX_HS_UNTERMINATED_ENABLE 0x00A6
#define RX_ENTER_HIBERN8 0x00A7
#define RX_BYPASS_8B10B_ENABLE 0x00A8
-#define RX_TERMINATION_FORCE_ENABLE 0x0089
-#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F
-#define RX_HIBERN8TIME_CAPABILITY 0x0092
+#define RX_TERMINATION_FORCE_ENABLE 0x00A9
+#define RXCALCTRL 0x00B4
+#define RXSQCTRL 0x00B5
+#define CFGRXCDR8 0x00BA
+#define CFGRXOVR8 0x00BD
+#define CFGRXOVR6 0x00BF
+#define RXDIRECTCTRL2 0x00C7
+#define CFGRXOVR4 0x00E9
#define RX_REFCLKFREQ 0x00EB
#define RX_CFGCLKFREQVAL 0x00EC
#define CFGWIDEINLN 0x00F0
-#define CFGRXCDR8 0x00BA
#define ENARXDIRECTCFG4 0x00F2
-#define CFGRXOVR8 0x00BD
-#define RXDIRECTCTRL2 0x00C7
#define ENARXDIRECTCFG3 0x00F3
-#define RXCALCTRL 0x00B4
#define ENARXDIRECTCFG2 0x00F4
-#define CFGRXOVR4 0x00E9
-#define RXSQCTRL 0x00B5
-#define CFGRXOVR6 0x00BF
-#define is_mphy_tx_attr(attr) (attr < RX_MODE)
+#define is_mphy_tx_attr(attr) ((attr) < RX_MODE)
+#define RX_ADV_FINE_GRAN_STEP(x) ((((x) & 0x3) << 1) | 0x1)
+#define SYNC_LEN_FINE(x) ((x) & 0x3F)
+#define SYNC_LEN_COARSE(x) ((1 << 6) | ((x) & 0x3F))
+#define PREP_LEN(x) ((x) & 0xF)
+
#define RX_MIN_ACTIVATETIME_UNIT_US 100
#define HIBERN8TIME_UNIT_US 100
@@ -77,51 +92,54 @@
#define CBPRGPLL2 UNIPRO_CB_OFFSET(0x00F8)
#define CBPRGTUNING UNIPRO_CB_OFFSET(0x00FB)
-#define UNIPRO_CB_OFFSET(x) (0x8000 | x)
+#define UNIPRO_CB_OFFSET(x) (0x8000 | (x))
/*
- * PHY Adpater attributes
+ * PHY Adapter attributes
*/
-#define PA_ACTIVETXDATALANES 0x1560
-#define PA_ACTIVERXDATALANES 0x1580
-#define PA_TXTRAILINGCLOCKS 0x1564
#define PA_PHY_TYPE 0x1500
#define PA_AVAILTXDATALANES 0x1520
-#define PA_AVAILRXDATALANES 0x1540
-#define PA_MINRXTRAILINGCLOCKS 0x1543
-#define PA_TXPWRSTATUS 0x1567
-#define PA_RXPWRSTATUS 0x1582
-#define PA_TXFORCECLOCK 0x1562
-#define PA_TXPWRMODE 0x1563
-#define PA_LEGACYDPHYESCDL 0x1570
#define PA_MAXTXSPEEDFAST 0x1521
#define PA_MAXTXSPEEDSLOW 0x1522
#define PA_MAXRXSPEEDFAST 0x1541
#define PA_MAXRXSPEEDSLOW 0x1542
#define PA_TXLINKSTARTUPHS 0x1544
+#define PA_AVAILRXDATALANES 0x1540
+#define PA_MINRXTRAILINGCLOCKS 0x1543
#define PA_LOCAL_TX_LCC_ENABLE 0x155E
+#define PA_ACTIVETXDATALANES 0x1560
+#define PA_CONNECTEDTXDATALANES 0x1561
+#define PA_TXFORCECLOCK 0x1562
+#define PA_TXPWRMODE 0x1563
+#define PA_TXTRAILINGCLOCKS 0x1564
#define PA_TXSPEEDFAST 0x1565
#define PA_TXSPEEDSLOW 0x1566
-#define PA_REMOTEVERINFO 0x15A0
+#define PA_TXPWRSTATUS 0x1567
#define PA_TXGEAR 0x1568
#define PA_TXTERMINATION 0x1569
#define PA_HSSERIES 0x156A
+#define PA_LEGACYDPHYESCDL 0x1570
#define PA_PWRMODE 0x1571
+#define PA_ACTIVERXDATALANES 0x1580
+#define PA_CONNECTEDRXDATALANES 0x1581
+#define PA_RXPWRSTATUS 0x1582
#define PA_RXGEAR 0x1583
#define PA_RXTERMINATION 0x1584
#define PA_MAXRXPWMGEAR 0x1586
#define PA_MAXRXHSGEAR 0x1587
-#define PA_RXHSUNTERMCAP 0x15A5
-#define PA_RXLSTERMCAP 0x15A6
-#define PA_GRANULARITY 0x15AA
#define PA_PACPREQTIMEOUT 0x1590
#define PA_PACPREQEOBTIMEOUT 0x1591
+#define PA_REMOTEVERINFO 0x15A0
+#define PA_LOGICALLANEMAP 0x15A1
+#define PA_SLEEPNOCONFIGTIME 0x15A2
+#define PA_STALLNOCONFIGTIME 0x15A3
+#define PA_SAVECONFIGTIME 0x15A4
+#define PA_RXHSUNTERMCAP 0x15A5
+#define PA_RXLSTERMCAP 0x15A6
#define PA_HIBERN8TIME 0x15A7
#define PA_LOCALVERINFO 0x15A9
+#define PA_GRANULARITY 0x15AA
#define PA_TACTIVATE 0x15A8
-#define PA_PACPFRAMECOUNT 0x15C0
-#define PA_PACPERRORCOUNT 0x15C1
-#define PA_PHYTESTCONTROL 0x15C2
#define PA_PWRMODEUSERDATA0 0x15B0
#define PA_PWRMODEUSERDATA1 0x15B1
#define PA_PWRMODEUSERDATA2 0x15B2
@@ -134,12 +152,9 @@
#define PA_PWRMODEUSERDATA9 0x15B9
#define PA_PWRMODEUSERDATA10 0x15BA
#define PA_PWRMODEUSERDATA11 0x15BB
-#define PA_CONNECTEDTXDATALANES 0x1561
-#define PA_CONNECTEDRXDATALANES 0x1581
-#define PA_LOGICALLANEMAP 0x15A1
-#define PA_SLEEPNOCONFIGTIME 0x15A2
-#define PA_STALLNOCONFIGTIME 0x15A3
-#define PA_SAVECONFIGTIME 0x15A4
+#define PA_PACPFRAMECOUNT 0x15C0
+#define PA_PACPERRORCOUNT 0x15C1
+#define PA_PHYTESTCONTROL 0x15C2
#define PA_TXHSADAPTTYPE 0x15D4
/* Adapt type for PA_TXHSADAPTTYPE attribute */
@@ -151,9 +166,9 @@
#define PA_HIBERN8_TIME_UNIT_US 100
/*Other attributes*/
+#define VS_POWERSTATE 0xD083
#define VS_MPHYCFGUPDT 0xD085
#define VS_DEBUGOMC 0xD09E
-#define VS_POWERSTATE 0xD083
#define VS_MPHYDISABLE 0xD0C1
#define PA_GRANULARITY_MIN_VAL 1
@@ -163,7 +178,7 @@
#define PA_MAXDATALANES 4
/* PA power modes */
-enum {
+enum ufs_pa_pwr_mode {
FAST_MODE = 1,
SLOW_MODE = 2,
FASTAUTO_MODE = 4,
@@ -171,8 +186,11 @@ enum {
UNCHANGED = 7,
};
+#define PWRMODE_MASK 0xF
+#define PWRMODE_RX_OFFSET 4
+
/* PA TX/RX Frequency Series */
-enum {
+enum ufs_hs_gear_rate {
PA_HS_MODE_A = 1,
PA_HS_MODE_B = 2,
};
@@ -193,14 +211,24 @@ enum ufs_hs_gear_tag {
UFS_HS_G1, /* HS Gear 1 (default for reset) */
UFS_HS_G2, /* HS Gear 2 */
UFS_HS_G3, /* HS Gear 3 */
+ UFS_HS_G4, /* HS Gear 4 */
+ UFS_HS_G5 /* HS Gear 5 */
+};
+
+enum ufs_lanes {
+ UFS_LANE_DONT_CHANGE, /* Don't change Lane */
+ UFS_LANE_1, /* Lane 1 (default for reset) */
+ UFS_LANE_2, /* Lane 2 */
};
enum ufs_unipro_ver {
UFS_UNIPRO_VER_RESERVED = 0,
UFS_UNIPRO_VER_1_40 = 1, /* UniPro version 1.40 */
UFS_UNIPRO_VER_1_41 = 2, /* UniPro version 1.41 */
- UFS_UNIPRO_VER_1_6 = 3, /* UniPro version 1.6 */
- UFS_UNIPRO_VER_MAX = 4, /* UniPro unsupported version */
+ UFS_UNIPRO_VER_1_6 = 3, /* UniPro version 1.6 */
+ UFS_UNIPRO_VER_1_61 = 4, /* UniPro version 1.61 */
+ UFS_UNIPRO_VER_1_8 = 5, /* UniPro version 1.8 */
+ UFS_UNIPRO_VER_MAX = 6, /* UniPro unsupported version */
/* UniPro version field mask in PA_LOCALVERINFO */
UFS_UNIPRO_VER_MASK = 0xF,
};
@@ -208,27 +236,27 @@ enum ufs_unipro_ver {
/*
* Data Link Layer Attributes
*/
+#define DL_TXPREEMPTIONCAP 0x2000
+#define DL_TC0TXMAXSDUSIZE 0x2001
+#define DL_TC0RXINITCREDITVAL 0x2002
+#define DL_TC1TXMAXSDUSIZE 0x2003
+#define DL_TC1RXINITCREDITVAL 0x2004
+#define DL_TC0TXBUFFERSIZE 0x2005
+#define DL_TC1TXBUFFERSIZE 0x2006
#define DL_TC0TXFCTHRESHOLD 0x2040
#define DL_FC0PROTTIMEOUTVAL 0x2041
#define DL_TC0REPLAYTIMEOUTVAL 0x2042
#define DL_AFC0REQTIMEOUTVAL 0x2043
#define DL_AFC0CREDITTHRESHOLD 0x2044
#define DL_TC0OUTACKTHRESHOLD 0x2045
+#define DL_PEERTC0PRESENT 0x2046
+#define DL_PEERTC0RXINITCREVAL 0x2047
#define DL_TC1TXFCTHRESHOLD 0x2060
#define DL_FC1PROTTIMEOUTVAL 0x2061
#define DL_TC1REPLAYTIMEOUTVAL 0x2062
#define DL_AFC1REQTIMEOUTVAL 0x2063
#define DL_AFC1CREDITTHRESHOLD 0x2064
#define DL_TC1OUTACKTHRESHOLD 0x2065
-#define DL_TXPREEMPTIONCAP 0x2000
-#define DL_TC0TXMAXSDUSIZE 0x2001
-#define DL_TC0RXINITCREDITVAL 0x2002
-#define DL_TC0TXBUFFERSIZE 0x2005
-#define DL_PEERTC0PRESENT 0x2046
-#define DL_PEERTC0RXINITCREVAL 0x2047
-#define DL_TC1TXMAXSDUSIZE 0x2003
-#define DL_TC1RXINITCREDITVAL 0x2004
-#define DL_TC1TXBUFFERSIZE 0x2006
#define DL_PEERTC1PRESENT 0x2066
#define DL_PEERTC1RXINITCREVAL 0x2067
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index 35b61497d9c..1d5e4afac6c 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -49,6 +49,13 @@ config SPL_USB_CDNS3_HOST
Host controller is compliant with XHCI so it will use
standard XHCI driver.
+config USB_CDNS3_STARFIVE
+ tristate "Cadence USB3 support on Starfive platforms"
+ default y if STARFIVE_JH7110
+ help
+ Say 'Y' here if you are building for Starfive platforms
+ that contain Cadence USB3 controller core. E.g.: JH7110.
+
config USB_CDNS3_TI
tristate "Cadence USB3 support on TI platforms"
default USB_CDNS3
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index d6047856091..dea5316599b 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -4,8 +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/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c
new file mode 100644
index 00000000000..78ceb831b19
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-starfive.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller
+ *
+ * Copyright (C) 2024 StarFive Technology Co., Ltd.
+ *
+ * Author: Minda Chen <minda.chen@starfivetech.com>
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/usb/otg.h>
+#include <malloc.h>
+#include <reset.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "core.h"
+
+#define USB_STRAP_HOST BIT(17)
+#define USB_STRAP_DEVICE BIT(18)
+#define USB_STRAP_MASK GENMASK(18, 16)
+
+#define USB_SUSPENDM_HOST BIT(19)
+#define USB_SUSPENDM_MASK BIT(19)
+
+#define USB_MISC_CFG_MASK GENMASK(23, 20)
+#define USB_SUSPENDM_BYPS BIT(20)
+#define USB_PLL_EN BIT(22)
+#define USB_REFCLK_MODE BIT(23)
+
+struct cdns_starfive {
+ struct udevice *dev;
+ struct regmap *stg_syscon;
+ struct reset_ctl_bulk resets;
+ struct clk_bulk clks;
+ u32 stg_usb_mode;
+ enum usb_dr_mode mode;
+};
+
+static void cdns_mode_init(struct cdns_starfive *data, enum usb_dr_mode mode)
+{
+ unsigned int strap, suspendm;
+
+ regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
+ USB_MISC_CFG_MASK,
+ USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE);
+
+ switch (mode) {
+ case USB_DR_MODE_HOST:
+ strap = USB_STRAP_HOST;
+ suspendm = USB_SUSPENDM_HOST;
+ break;
+ case USB_DR_MODE_PERIPHERAL:
+ strap = USB_STRAP_DEVICE;
+ suspendm = 0;
+ break;
+ default:
+ return;
+ }
+
+ regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
+ USB_SUSPENDM_MASK | USB_STRAP_MASK,
+ strap | suspendm);
+}
+
+static void cdns_clk_rst_deinit(struct cdns_starfive *data)
+{
+ reset_assert_bulk(&data->resets);
+ clk_disable_bulk(&data->clks);
+}
+
+static int cdns_clk_rst_init(struct cdns_starfive *data)
+{
+ int ret;
+
+ ret = clk_get_bulk(data->dev, &data->clks);
+ if (ret)
+ return ret;
+
+ ret = reset_get_bulk(data->dev, &data->resets);
+ if (ret)
+ goto err_clk;
+
+ ret = clk_enable_bulk(&data->clks);
+ if (ret) {
+ dev_err(data->dev, "clk enable failed: %d\n", ret);
+ goto err_en_clk;
+ }
+
+ ret = reset_deassert_bulk(&data->resets);
+ if (ret) {
+ dev_err(data->dev, "reset deassert failed: %d\n", ret);
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ clk_disable_bulk(&data->clks);
+err_en_clk:
+ reset_release_bulk(&data->resets);
+err_clk:
+ clk_release_bulk(&data->clks);
+
+ return ret;
+}
+
+static int cdns_starfive_get_syscon(struct cdns_starfive *data)
+{
+ struct ofnode_phandle_args phandle;
+ int ret;
+
+ ret = dev_read_phandle_with_args(data->dev, "starfive,stg-syscon", NULL, 1, 0,
+ &phandle);
+ if (ret < 0) {
+ dev_err(data->dev, "Can't get stg cfg phandle: %d\n", ret);
+ return ret;
+ }
+
+ data->stg_syscon = syscon_node_to_regmap(phandle.node);
+ if (IS_ERR(data->stg_syscon)) {
+ dev_err(data->dev, "fail to get regmap: %d\n", (int)PTR_ERR(data->stg_syscon));
+ return PTR_ERR(data->stg_syscon);
+ }
+
+ data->stg_usb_mode = phandle.args[0];
+
+ return 0;
+}
+
+static int cdns_starfive_probe(struct udevice *dev)
+{
+ struct cdns_starfive *data = dev_get_plat(dev);
+ enum usb_dr_mode dr_mode;
+ int ret;
+
+ data->dev = dev;
+
+ ret = cdns_starfive_get_syscon(data);
+ if (ret)
+ return ret;
+
+ dr_mode = usb_get_dr_mode(dev_ofnode(dev));
+
+ data->mode = dr_mode;
+ ret = cdns_clk_rst_init(data);
+ if (ret) {
+ dev_err(data->dev, "clk reset failed: %d\n", ret);
+ return ret;
+ }
+ cdns_mode_init(data, dr_mode);
+
+ return 0;
+}
+
+static int cdns_starfive_remove(struct udevice *dev)
+{
+ struct cdns_starfive *data = dev_get_plat(dev);
+
+ cdns_clk_rst_deinit(data);
+ return 0;
+}
+
+static const struct udevice_id cdns_starfive_of_match[] = {
+ { .compatible = "starfive,jh7110-usb", },
+ {},
+};
+
+U_BOOT_DRIVER(cdns_starfive) = {
+ .name = "cdns-starfive",
+ .id = UCLASS_NOP,
+ .of_match = cdns_starfive_of_match,
+ .bind = cdns3_bind,
+ .probe = cdns_starfive_probe,
+ .remove = cdns_starfive_remove,
+ .plat_auto = sizeof(struct cdns_starfive),
+ .flags = DM_FLAG_OS_PREPARE,
+};
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index 4cfd38ec245..4434dc15bec 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -410,6 +410,9 @@ int cdns3_bind(struct udevice *parent)
name = ofnode_get_name(node);
dr_mode = usb_get_dr_mode(node);
+ if (dr_mode == USB_DR_MODE_UNKNOWN)
+ dr_mode = usb_get_dr_mode(dev_ofnode(parent));
+
switch (dr_mode) {
#if defined(CONFIG_SPL_USB_HOST) || \
(!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST))
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index 47874fec29e..cbb13342343 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -217,15 +217,19 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns)
int cdns3_drd_update_mode(struct cdns3 *cdns)
{
int ret = 0;
+ int mode;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
+ mode = PHY_MODE_USB_DEVICE;
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
break;
case USB_DR_MODE_HOST:
+ mode = PHY_MODE_USB_HOST;
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
break;
case USB_DR_MODE_OTG:
+ mode = PHY_MODE_USB_OTG;
ret = cdns3_init_otg_mode(cdns);
break;
default:
@@ -234,6 +238,16 @@ int cdns3_drd_update_mode(struct cdns3 *cdns)
return -EINVAL;
}
+ ret = generic_phy_set_mode(&cdns->usb2_phy, mode, 0);
+ if (ret) {
+ dev_err(cdns->dev, "Set usb 2.0 PHY mode failed %d\n", ret);
+ return ret;
+ }
+
+ ret = generic_phy_set_mode(&cdns->usb3_phy, mode, 0);
+ if (ret)
+ dev_err(cdns->dev, "Set usb 3.0 PHY mode failed %d\n", ret);
+
return ret;
}
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/emul/sandbox_keyb.c b/drivers/usb/emul/sandbox_keyb.c
index db769883ba3..5ed8c2c799a 100644
--- a/drivers/usb/emul/sandbox_keyb.c
+++ b/drivers/usb/emul/sandbox_keyb.c
@@ -38,7 +38,7 @@ enum {
*
*/
struct sandbox_keyb_priv {
- struct membuff in;
+ struct membuf in;
};
struct sandbox_keyb_plat {
@@ -167,7 +167,7 @@ int sandbox_usb_keyb_add_string(struct udevice *dev,
struct sandbox_keyb_priv *priv = dev_get_priv(dev);
int ret;
- ret = membuff_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE);
+ ret = membuf_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE);
if (ret != USB_KBD_BOOT_REPORT_SIZE)
return -ENOSPC;
@@ -194,7 +194,7 @@ static int sandbox_keyb_interrupt(struct udevice *dev, struct usb_device *udev,
if (length < USB_KBD_BOOT_REPORT_SIZE)
return 0;
- membuff_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE);
+ membuf_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE);
return 0;
}
@@ -220,7 +220,7 @@ static int sandbox_keyb_probe(struct udevice *dev)
struct sandbox_keyb_priv *priv = dev_get_priv(dev);
/* Provide an 80 character keyboard buffer */
- return membuff_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE);
+ return membuf_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE);
}
static const struct dm_usb_ops sandbox_usb_keyb_ops = {
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c815764c2bc..46a83141481 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -85,6 +85,7 @@ config USB_GADGET_PRODUCT_NUM
default 0x330e if ROCKCHIP_RK3308
default 0x350a if ROCKCHIP_RK3568
default 0x350b if ROCKCHIP_RK3588
+ default 0x350c if ROCKCHIP_RK3528
default 0x0
help
Product ID of the USB device emulated, reported to the host device.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 4bda224ff1a..db5f8895a33 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -35,7 +35,3 @@ endif
endif
obj-$(CONFIG_CI_UDC) += ci_udc.o
-
-# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
-# This is really only N900 and USBTTY now.
-obj-$(CONFIG_USB_DEVICE) += core.o ep0.o
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/core.c b/drivers/usb/gadget/core.c
deleted file mode 100644
index bcb1ad3082c..00000000000
--- a/drivers/usb/gadget/core.c
+++ /dev/null
@@ -1,621 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * (C) Copyright 2003
- * Gerry Hamel, geh@ti.com, Texas Instruments
- *
- * Based on
- * linux/drivers/usbd/usbd.c.c - USB Device Core Layer
- *
- * Copyright (c) 2000, 2001, 2002 Lineo
- * Copyright (c) 2001 Hewlett Packard
- *
- * By:
- * Stuart Lynne <sl@lineo.com>,
- * Tom Rushworth <tbr@lineo.com>,
- * Bruce Balden <balden@lineo.com>
- */
-
-#include <log.h>
-#include <malloc.h>
-#include <serial.h>
-#include <usbdevice.h>
-
-#define MAX_INTERFACES 2
-
-int maxstrings = 20;
-
-/* Global variables ************************************************************************** */
-
-struct usb_string_descriptor **usb_strings;
-
-int usb_devices;
-
-extern struct usb_function_driver ep0_driver;
-
-int registered_functions;
-int registered_devices;
-
-__maybe_unused static char *usbd_device_events[] = {
- "DEVICE_UNKNOWN",
- "DEVICE_INIT",
- "DEVICE_CREATE",
- "DEVICE_HUB_CONFIGURED",
- "DEVICE_RESET",
- "DEVICE_ADDRESS_ASSIGNED",
- "DEVICE_CONFIGURED",
- "DEVICE_SET_INTERFACE",
- "DEVICE_SET_FEATURE",
- "DEVICE_CLEAR_FEATURE",
- "DEVICE_DE_CONFIGURED",
- "DEVICE_BUS_INACTIVE",
- "DEVICE_BUS_ACTIVITY",
- "DEVICE_POWER_INTERRUPTION",
- "DEVICE_HUB_RESET",
- "DEVICE_DESTROY",
- "DEVICE_FUNCTION_PRIVATE",
-};
-
-__maybe_unused static char *usbd_device_status[] = {
- "USBD_OPENING",
- "USBD_OK",
- "USBD_SUSPENDED",
- "USBD_CLOSING",
-};
-
-#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
-
-/* Descriptor support functions ************************************************************** */
-
-/**
- * usbd_get_string - find and return a string descriptor
- * @index: string index to return
- *
- * Find an indexed string and return a pointer to a it.
- */
-struct usb_string_descriptor *usbd_get_string (__u8 index)
-{
- if (index >= maxstrings) {
- return NULL;
- }
- return usb_strings[index];
-}
-
-/* Access to device descriptor functions ***************************************************** */
-
-/* *
- * usbd_device_configuration_instance - find a configuration instance for this device
- * @device:
- * @configuration: index to configuration, 0 - N-1
- *
- * Get specifed device configuration. Index should be bConfigurationValue-1.
- */
-static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device,
- unsigned int port, unsigned int configuration)
-{
- if (configuration >= device->configurations)
- return NULL;
-
- return device->configuration_instance_array + configuration;
-}
-
-/* *
- * usbd_device_interface_instance
- * @device:
- * @configuration: index to configuration, 0 - N-1
- * @interface: index to interface
- *
- * Return the specified interface descriptor for the specified device.
- */
-struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface)
-{
- struct usb_configuration_instance *configuration_instance;
-
- if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) {
- return NULL;
- }
- if (interface >= configuration_instance->interfaces) {
- return NULL;
- }
- return configuration_instance->interface_instance_array + interface;
-}
-
-/* *
- * usbd_device_alternate_descriptor_list
- * @device:
- * @configuration: index to configuration, 0 - N-1
- * @interface: index to interface
- * @alternate: alternate setting
- *
- * Return the specified alternate descriptor for the specified device.
- */
-struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate)
-{
- struct usb_interface_instance *interface_instance;
-
- if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) {
- return NULL;
- }
-
- if (alternate >= interface_instance->alternates) {
- return NULL;
- }
-
- return interface_instance->alternates_instance_array + alternate;
-}
-
-/* *
- * usbd_device_device_descriptor
- * @device: which device
- * @configuration: index to configuration, 0 - N-1
- * @port: which port
- *
- * Return the specified configuration descriptor for the specified device.
- */
-struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
-{
- return (device->device_descriptor);
-}
-
-/**
- * usbd_device_configuration_descriptor
- * @device: which device
- * @port: which port
- * @configuration: index to configuration, 0 - N-1
- *
- * Return the specified configuration descriptor for the specified device.
- */
-struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct
- usb_device_instance
- *device, int port, int configuration)
-{
- struct usb_configuration_instance *configuration_instance;
- if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
- return NULL;
- }
- return (configuration_instance->configuration_descriptor);
-}
-
-/**
- * usbd_device_interface_descriptor
- * @device: which device
- * @port: which port
- * @configuration: index to configuration, 0 - N-1
- * @interface: index to interface
- * @alternate: alternate setting
- *
- * Return the specified interface descriptor for the specified device.
- */
-struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance
- *device, int port, int configuration, int interface, int alternate)
-{
- struct usb_interface_instance *interface_instance;
- if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
- return NULL;
- }
- if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
- return NULL;
- }
- return (interface_instance->alternates_instance_array[alternate].interface_descriptor);
-}
-
-/**
- * usbd_device_endpoint_descriptor_index
- * @device: which device
- * @port: which port
- * @configuration: index to configuration, 0 - N-1
- * @interface: index to interface
- * @alternate: index setting
- * @index: which index
- *
- * Return the specified endpoint descriptor for the specified device.
- */
-struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance
- *device, int port, int configuration, int interface, int alternate, int index)
-{
- struct usb_alternate_instance *alternate_instance;
-
- if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
- return NULL;
- }
- if (index >= alternate_instance->endpoints) {
- return NULL;
- }
- return *(alternate_instance->endpoints_descriptor_array + index);
-}
-
-/**
- * usbd_device_endpoint_transfersize
- * @device: which device
- * @port: which port
- * @configuration: index to configuration, 0 - N-1
- * @interface: index to interface
- * @index: which index
- *
- * Return the specified endpoint transfer size;
- */
-int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index)
-{
- struct usb_alternate_instance *alternate_instance;
-
- if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
- return 0;
- }
- if (index >= alternate_instance->endpoints) {
- return 0;
- }
- return *(alternate_instance->endpoint_transfersize_array + index);
-}
-
-/**
- * usbd_device_endpoint_descriptor
- * @device: which device
- * @port: which port
- * @configuration: index to configuration, 0 - N-1
- * @interface: index to interface
- * @alternate: alternate setting
- * @endpoint: which endpoint
- *
- * Return the specified endpoint descriptor for the specified device.
- */
-struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint)
-{
- struct usb_endpoint_descriptor *endpoint_descriptor;
- int i;
-
- for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) {
- if (endpoint_descriptor->bEndpointAddress == endpoint) {
- return endpoint_descriptor;
- }
- }
- return NULL;
-}
-
-/**
- * usbd_endpoint_halted
- * @device: point to struct usb_device_instance
- * @endpoint: endpoint to check
- *
- * Return non-zero if endpoint is halted.
- */
-int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint)
-{
- return (device->status == USB_STATUS_HALT);
-}
-
-/**
- * usbd_rcv_complete - complete a receive
- * @endpoint:
- * @len:
- * @urb_bad:
- *
- * Called from rcv interrupt to complete.
- */
-void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad)
-{
- if (endpoint) {
- struct urb *rcv_urb;
-
- /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */
-
- /* if we had an urb then update actual_length, dispatch if neccessary */
- if ((rcv_urb = endpoint->rcv_urb)) {
-
- /*usbdbg("actual: %d buffer: %d\n", */
- /*rcv_urb->actual_length, rcv_urb->buffer_length); */
-
- /* check the urb is ok, are we adding data less than the packetsize */
- if (!urb_bad && (len <= endpoint->rcv_packetSize)) {
- /*usbdbg("updating actual_length by %d\n",len); */
-
- /* increment the received data size */
- rcv_urb->actual_length += len;
-
- } else {
- usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n",
- rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad);
-
- rcv_urb->actual_length = 0;
- rcv_urb->status = RECV_ERROR;
- }
- } else {
- usberr("no rcv_urb!");
- }
- } else {
- usberr("no endpoint!");
- }
-
-}
-
-/**
- * usbd_tx_complete - complete a transmit
- * @endpoint:
- * @resetart:
- *
- * Called from tx interrupt to complete.
- */
-void usbd_tx_complete (struct usb_endpoint_instance *endpoint)
-{
- if (endpoint) {
- struct urb *tx_urb;
-
- /* if we have a tx_urb advance or reset, finish if complete */
- if ((tx_urb = endpoint->tx_urb)) {
- int sent = endpoint->last;
- endpoint->sent += sent;
- endpoint->last -= sent;
-
- if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) {
- tx_urb->actual_length = 0;
- endpoint->sent = 0;
- endpoint->last = 0;
-
- /* Remove from active, save for re-use */
- urb_detach(tx_urb);
- urb_append(&endpoint->done, tx_urb);
- /*usbdbg("done->next %p, tx_urb %p, done %p", */
- /* endpoint->done.next, tx_urb, &endpoint->done); */
-
- endpoint->tx_urb = first_urb_detached(&endpoint->tx);
- if( endpoint->tx_urb ) {
- endpoint->tx_queue--;
- usbdbg("got urb from tx list");
- }
- if( !endpoint->tx_urb ) {
- /*usbdbg("taking urb from done list"); */
- endpoint->tx_urb = first_urb_detached(&endpoint->done);
- }
- if( !endpoint->tx_urb ) {
- usbdbg("allocating new urb for tx_urb");
- endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint);
- }
- }
- }
- }
-}
-
-/* URB linked list functions ***************************************************** */
-
-/*
- * Initialize an urb_link to be a single element list.
- * If the urb_link is being used as a distinguished list head
- * the list is empty when the head is the only link in the list.
- */
-void urb_link_init (urb_link * ul)
-{
- if (ul) {
- ul->prev = ul->next = ul;
- }
-}
-
-/*
- * Detach an urb_link from a list, and set it
- * up as a single element list, so no dangling
- * pointers can be followed, and so it can be
- * joined to another list if so desired.
- */
-void urb_detach (struct urb *urb)
-{
- if (urb) {
- urb_link *ul = &urb->link;
- ul->next->prev = ul->prev;
- ul->prev->next = ul->next;
- urb_link_init (ul);
- }
-}
-
-/*
- * Return the first urb_link in a list with a distinguished
- * head "hd", or NULL if the list is empty. This will also
- * work as a predicate, returning NULL if empty, and non-NULL
- * otherwise.
- */
-urb_link *first_urb_link (urb_link * hd)
-{
- urb_link *nx;
- if (NULL != hd && NULL != (nx = hd->next) && nx != hd) {
- /* There is at least one element in the list */
- /* (besides the distinguished head). */
- return (nx);
- }
- /* The list is empty */
- return (NULL);
-}
-
-/*
- * Return the first urb in a list with a distinguished
- * head "hd", or NULL if the list is empty.
- */
-struct urb *first_urb (urb_link * hd)
-{
- urb_link *nx;
- if (NULL == (nx = first_urb_link (hd))) {
- /* The list is empty */
- return (NULL);
- }
- return (p2surround (struct urb, link, nx));
-}
-
-/*
- * Detach and return the first urb in a list with a distinguished
- * head "hd", or NULL if the list is empty.
- *
- */
-struct urb *first_urb_detached (urb_link * hd)
-{
- struct urb *urb;
- if ((urb = first_urb (hd))) {
- urb_detach (urb);
- }
- return urb;
-}
-
-/*
- * Append an urb_link (or a whole list of
- * urb_links) to the tail of another list
- * of urb_links.
- */
-void urb_append (urb_link * hd, struct urb *urb)
-{
- if (hd && urb) {
- urb_link *new = &urb->link;
-
- /* This allows the new urb to be a list of urbs, */
- /* with new pointing at the first, but the link */
- /* must be initialized. */
- /* Order is important here... */
- urb_link *pul = hd->prev;
- new->prev->next = hd;
- hd->prev = new->prev;
- new->prev = pul;
- pul->next = new;
- }
-}
-
-/* URB create/destroy functions ***************************************************** */
-
-/**
- * usbd_alloc_urb - allocate an URB appropriate for specified endpoint
- * @device: device instance
- * @endpoint: endpoint
- *
- * Allocate an urb structure. The usb device urb structure is used to
- * contain all data associated with a transfer, including a setup packet for
- * control transfers.
- *
- * NOTE: endpoint_address MUST contain a direction flag.
- */
-struct urb *usbd_alloc_urb (struct usb_device_instance *device,
- struct usb_endpoint_instance *endpoint)
-{
- struct urb *urb;
-
- if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) {
- usberr (" F A T A L: malloc(%zu) FAILED!!!!",
- sizeof (struct urb));
- return NULL;
- }
-
- /* Fill in known fields */
- memset (urb, 0, sizeof (struct urb));
- urb->endpoint = endpoint;
- urb->device = device;
- urb->buffer = (u8 *) urb->buffer_data;
- urb->buffer_length = sizeof (urb->buffer_data);
-
- urb_link_init (&urb->link);
-
- return urb;
-}
-
-/**
- * usbd_dealloc_urb - deallocate an URB and associated buffer
- * @urb: pointer to an urb structure
- *
- * Deallocate an urb structure and associated data.
- */
-void usbd_dealloc_urb (struct urb *urb)
-{
- if (urb) {
- free (urb);
- }
-}
-
-/* Event signaling functions ***************************************************** */
-
-/**
- * usbd_device_event - called to respond to various usb events
- * @device: pointer to struct device
- * @event: event to respond to
- *
- * Used by a Bus driver to indicate an event.
- */
-void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data)
-{
- usb_device_state_t state;
-
- if (!device || !device->bus) {
- usberr("(%p,%d) NULL device or device->bus", device, event);
- return;
- }
-
- state = device->device_state;
-
- usbinfo("%s", usbd_device_events[event]);
-
- switch (event) {
- case DEVICE_UNKNOWN:
- break;
- case DEVICE_INIT:
- device->device_state = STATE_INIT;
- break;
-
- case DEVICE_CREATE:
- device->device_state = STATE_ATTACHED;
- break;
-
- case DEVICE_HUB_CONFIGURED:
- device->device_state = STATE_POWERED;
- break;
-
- case DEVICE_RESET:
- device->device_state = STATE_DEFAULT;
- device->address = 0;
- break;
-
- case DEVICE_ADDRESS_ASSIGNED:
- device->device_state = STATE_ADDRESSED;
- break;
-
- case DEVICE_CONFIGURED:
- device->device_state = STATE_CONFIGURED;
- break;
-
- case DEVICE_DE_CONFIGURED:
- device->device_state = STATE_ADDRESSED;
- break;
-
- case DEVICE_BUS_INACTIVE:
- if (device->status != USBD_CLOSING) {
- device->status = USBD_SUSPENDED;
- }
- break;
- case DEVICE_BUS_ACTIVITY:
- if (device->status != USBD_CLOSING) {
- device->status = USBD_OK;
- }
- break;
-
- case DEVICE_SET_INTERFACE:
- break;
- case DEVICE_SET_FEATURE:
- break;
- case DEVICE_CLEAR_FEATURE:
- break;
-
- case DEVICE_POWER_INTERRUPTION:
- device->device_state = STATE_POWERED;
- break;
- case DEVICE_HUB_RESET:
- device->device_state = STATE_ATTACHED;
- break;
- case DEVICE_DESTROY:
- device->device_state = STATE_UNKNOWN;
- break;
-
- case DEVICE_FUNCTION_PRIVATE:
- break;
-
- default:
- usbdbg("event %d - not handled",event);
- break;
- }
- debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d",
- device->name, event, state,
- device->device_state, device->status, device->address);
-
- /* tell the bus interface driver */
- if( device->event ) {
- /* usbdbg("calling device->event"); */
- device->event(device, event, data);
- }
-}
diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c
deleted file mode 100644
index 8c7fc17c2ea..00000000000
--- a/drivers/usb/gadget/ep0.c
+++ /dev/null
@@ -1,619 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * (C) Copyright 2003
- * Gerry Hamel, geh@ti.com, Texas Instruments
- *
- * (C) Copyright 2006
- * Bryan O'Donoghue, deckard@CodeHermit.ie
- *
- * Based on
- * linux/drivers/usbd/ep0.c
- *
- * Copyright (c) 2000, 2001, 2002 Lineo
- * Copyright (c) 2001 Hewlett Packard
- *
- * By:
- * Stuart Lynne <sl@lineo.com>,
- * Tom Rushworth <tbr@lineo.com>,
- * Bruce Balden <balden@lineo.com>
- */
-
-/*
- * This is the builtin ep0 control function. It implements all required functionality
- * for responding to control requests (SETUP packets).
- *
- * XXX
- *
- * Currently we do not pass any SETUP packets (or other) to the configured
- * function driver. This may need to change.
- *
- * XXX
- *
- * As alluded to above, a simple callback cdc_recv_setup has been implemented
- * in the usb_device data structure to facilicate passing
- * Common Device Class packets to a function driver.
- *
- * XXX
- */
-
-#include <serial.h>
-#include <usbdevice.h>
-
-#if 0
-#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
-#else
-#define dbg_ep0(lvl,fmt,args...)
-#endif
-
-__maybe_unused static char *usbd_device_descriptors[] = {
- "UNKNOWN", /* 0 */
- "DEVICE", /* 1 */
- "CONFIG", /* 2 */
- "STRING", /* 3 */
- "INTERFACE", /* 4 */
- "ENDPOINT", /* 5 */
- "DEVICE QUALIFIER", /* 6 */
- "OTHER SPEED", /* 7 */
- "INTERFACE POWER", /* 8 */
-};
-
-#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \
- usbd_device_descriptors[x] : "UNKNOWN")
-
-__maybe_unused static char *usbd_device_states[] = {
- "STATE_INIT",
- "STATE_CREATED",
- "STATE_ATTACHED",
- "STATE_POWERED",
- "STATE_DEFAULT",
- "STATE_ADDRESSED",
- "STATE_CONFIGURED",
- "STATE_UNKNOWN",
-};
-
-#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN")
-
-__maybe_unused static char *usbd_device_requests[] = {
- "GET STATUS", /* 0 */
- "CLEAR FEATURE", /* 1 */
- "RESERVED", /* 2 */
- "SET FEATURE", /* 3 */
- "RESERVED", /* 4 */
- "SET ADDRESS", /* 5 */
- "GET DESCRIPTOR", /* 6 */
- "SET DESCRIPTOR", /* 7 */
- "GET CONFIGURATION", /* 8 */
- "SET CONFIGURATION", /* 9 */
- "GET INTERFACE", /* 10 */
- "SET INTERFACE", /* 11 */
- "SYNC FRAME", /* 12 */
-};
-
-#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN")
-
-/* EP0 Configuration Set ********************************************************************* */
-
-/**
- * ep0_get_status - fill in URB data with appropriate status
- * @device:
- * @urb:
- * @index:
- * @requesttype:
- *
- */
-static int ep0_get_status (struct usb_device_instance *device,
- struct urb *urb, int index, int requesttype)
-{
- char *cp;
-
- urb->actual_length = 2;
- cp = (char*)urb->buffer;
- cp[0] = cp[1] = 0;
-
- switch (requesttype) {
- case USB_REQ_RECIPIENT_DEVICE:
- cp[0] = USB_STATUS_SELFPOWERED;
- break;
- case USB_REQ_RECIPIENT_INTERFACE:
- break;
- case USB_REQ_RECIPIENT_ENDPOINT:
- cp[0] = usbd_endpoint_halted (device, index);
- break;
- case USB_REQ_RECIPIENT_OTHER:
- urb->actual_length = 0;
- default:
- break;
- }
- dbg_ep0 (2, "%02x %02x", cp[0], cp[1]);
- return 0;
-}
-
-/**
- * ep0_get_one
- * @device:
- * @urb:
- * @result:
- *
- * Set a single byte value in the urb send buffer. Return non-zero to signal
- * a request error.
- */
-static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
- __u8 result)
-{
- urb->actual_length = 1; /* XXX 2? */
- ((char *) urb->buffer)[0] = result;
- return 0;
-}
-
-/**
- * copy_config
- * @urb: pointer to urb
- * @data: pointer to configuration data
- * @length: length of data
- *
- * Copy configuration data to urb transfer buffer if there is room for it.
- */
-void copy_config (struct urb *urb, void *data, int max_length,
- int max_buf)
-{
- int available;
- int length;
-
- /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */
- /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */
-
- if (!data) {
- dbg_ep0 (1, "data is NULL");
- return;
- }
- length = max_length;
-
- if (length > max_length) {
- dbg_ep0 (1, "length: %d >= max_length: %d", length,
- max_length);
- return;
- }
- /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */
- /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */
-
- if ((available =
- /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) {
- return;
- }
- /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
- /* urb->actual_length, urb->buffer_length, max_buf, length, available); */
-
- if (length > available) {
- length = available;
- }
- /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
- /* urb->actual_length, urb->buffer_length, max_buf, length, available); */
-
- memcpy (urb->buffer + urb->actual_length, data, length);
- urb->actual_length += length;
-
- dbg_ep0 (3,
- "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d",
- urb->actual_length, urb->buffer_length, max_buf, max_length,
- available);
-}
-
-/**
- * ep0_get_descriptor
- * @device:
- * @urb:
- * @max:
- * @descriptor_type:
- * @index:
- *
- * Called by ep0_rx_process for a get descriptor device command. Determine what
- * descriptor is being requested, copy to send buffer. Return zero if ok to send,
- * return non-zero to signal a request error.
- */
-static int ep0_get_descriptor (struct usb_device_instance *device,
- struct urb *urb, int max, int descriptor_type,
- int index)
-{
- int port = 0; /* XXX compound device */
-
- /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */
-
- if (!urb || !urb->buffer || !urb->buffer_length
- || (urb->buffer_length < 255)) {
- dbg_ep0 (2, "invalid urb %p", urb);
- return -1L;
- }
-
- /* setup tx urb */
- urb->actual_length = 0;
-
- dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
-
- switch (descriptor_type) {
- case USB_DESCRIPTOR_TYPE_DEVICE:
- {
- struct usb_device_descriptor *device_descriptor;
- if (!
- (device_descriptor =
- usbd_device_device_descriptor (device, port))) {
- return -1;
- }
- /* copy descriptor for this device */
- copy_config (urb, device_descriptor,
- sizeof (struct usb_device_descriptor),
- max);
-
- /* correct the correct control endpoint 0 max packet size into the descriptor */
- device_descriptor =
- (struct usb_device_descriptor *) urb->buffer;
-
- }
- dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
- break;
-
- case USB_DESCRIPTOR_TYPE_CONFIGURATION:
- {
- struct usb_configuration_descriptor
- *configuration_descriptor;
- struct usb_device_descriptor *device_descriptor;
- if (!
- (device_descriptor =
- usbd_device_device_descriptor (device, port))) {
- return -1;
- }
- /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */
- if (index >= device_descriptor->bNumConfigurations) {
- dbg_ep0 (0, "index too large: %d >= %d", index,
- device_descriptor->
- bNumConfigurations);
- return -1;
- }
-
- if (!
- (configuration_descriptor =
- usbd_device_configuration_descriptor (device,
- port,
- index))) {
- dbg_ep0 (0,
- "usbd_device_configuration_descriptor failed: %d",
- index);
- return -1;
- }
- dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
- copy_config (urb, configuration_descriptor,
-
- cpu_to_le16(configuration_descriptor->wTotalLength),
- max);
- }
-
- break;
-
- case USB_DESCRIPTOR_TYPE_STRING:
- {
- struct usb_string_descriptor *string_descriptor;
- if (!(string_descriptor = usbd_get_string (index))) {
- dbg_ep0(0, "Invalid string index %d\n", index);
- return -1;
- }
- dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
- copy_config (urb, string_descriptor, string_descriptor->bLength, max);
- }
- break;
- case USB_DESCRIPTOR_TYPE_INTERFACE:
- dbg_ep0(2, "USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
- return -1;
- case USB_DESCRIPTOR_TYPE_ENDPOINT:
- dbg_ep0(2, "USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
- return -1;
- case USB_DESCRIPTOR_TYPE_HID:
- {
- dbg_ep0(2, "USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
- return -1; /* unsupported at this time */
-#if 0
- int bNumInterface =
- le16_to_cpu (urb->device_request.wIndex);
- int bAlternateSetting = 0;
- int class = 0;
- struct usb_class_descriptor *class_descriptor;
-
- if (!(class_descriptor =
- usbd_device_class_descriptor_index (device,
- port, 0,
- bNumInterface,
- bAlternateSetting,
- class))
- || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) {
- dbg_ep0 (3, "[%d] interface is not HID",
- bNumInterface);
- return -1;
- }
- /* copy descriptor for this class */
- copy_config (urb, class_descriptor,
- class_descriptor->descriptor.hid.bLength,
- max);
-#endif
- }
- break;
- case USB_DESCRIPTOR_TYPE_REPORT:
- {
- dbg_ep0(2, "USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
- return -1; /* unsupported at this time */
-#if 0
- int bNumInterface =
- le16_to_cpu (urb->device_request.wIndex);
- int bAlternateSetting = 0;
- int class = 0;
- struct usb_class_report_descriptor *report_descriptor;
-
- if (!(report_descriptor =
- usbd_device_class_report_descriptor_index
- (device, port, 0, bNumInterface,
- bAlternateSetting, class))
- || report_descriptor->bDescriptorType !=
- USB_DT_REPORT) {
- dbg_ep0 (3, "[%d] descriptor is not REPORT",
- bNumInterface);
- return -1;
- }
- /* copy report descriptor for this class */
- /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */
- if (max - urb->actual_length > 0) {
- int length =
- min(report_descriptor->wLength,
- max - urb->actual_length);
- memcpy (urb->buffer + urb->actual_length,
- &report_descriptor->bData[0], length);
- urb->actual_length += length;
- }
-#endif
- }
- break;
- case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
- return -1;
-
- default:
- return -1;
- }
-
- dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
- urb->buffer, urb->buffer_length, urb->actual_length,
- device->bus->endpoint_array[0].tx_packetSize);
-/*
- if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) {
- dbg_ep0(0, "adding null byte");
- urb->buffer[urb->actual_length++] = 0;
- dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d",
- urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize);
- }
-*/
- return 0;
-
-}
-
-/**
- * ep0_recv_setup - called to indicate URB has been received
- * @urb: pointer to struct urb
- *
- * Check if this is a setup packet, process the device request, put results
- * back into the urb and return zero or non-zero to indicate success (DATA)
- * or failure (STALL).
- *
- */
-int ep0_recv_setup (struct urb *urb)
-{
- /*struct usb_device_request *request = urb->buffer; */
- /*struct usb_device_instance *device = urb->device; */
-
- struct usb_device_request *request;
- struct usb_device_instance *device;
- int address;
-
- dbg_ep0 (0, "entering ep0_recv_setup()");
- if (!urb || !urb->device) {
- dbg_ep0 (3, "invalid URB %p", urb);
- return -1;
- }
-
- request = &urb->device_request;
- device = urb->device;
-
- dbg_ep0 (3, "urb: %p device: %p", urb, urb->device);
-
- /*dbg_ep0(2, "- - - - - - - - - -"); */
-
- dbg_ep0 (2,
- "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s",
- request->bmRequestType, request->bRequest,
- le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex),
- le16_to_cpu (request->wLength),
- USBD_DEVICE_REQUESTS (request->bRequest));
-
- /* handle USB Standard Request (c.f. USB Spec table 9-2) */
- if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
- if(device->device_state <= STATE_CONFIGURED){
- /* Attempt to handle a CDC specific request if we are
- * in the configured state.
- */
- return device->cdc_recv_setup(request,urb);
- }
- dbg_ep0 (1, "non standard request: %x",
- request->bmRequestType & USB_REQ_TYPE_MASK);
- return -1; /* Stall here */
- }
-
- switch (device->device_state) {
- case STATE_CREATED:
- case STATE_ATTACHED:
- case STATE_POWERED:
- /* It actually is important to allow requests in these states,
- * Windows will request descriptors before assigning an
- * address to the client.
- */
-
- /*dbg_ep0 (1, "request %s not allowed in this state: %s", */
- /* USBD_DEVICE_REQUESTS(request->bRequest), */
- /* usbd_device_states[device->device_state]); */
- /*return -1; */
- break;
-
- case STATE_INIT:
- case STATE_DEFAULT:
- switch (request->bRequest) {
- case USB_REQ_GET_STATUS:
- case USB_REQ_GET_INTERFACE:
- case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- case USB_REQ_SET_DESCRIPTOR:
- /* case USB_REQ_SET_CONFIGURATION: */
- case USB_REQ_SET_INTERFACE:
- dbg_ep0 (1,
- "request %s not allowed in DEFAULT state: %s",
- USBD_DEVICE_REQUESTS (request->bRequest),
- usbd_device_states[device->device_state]);
- return -1;
-
- case USB_REQ_SET_CONFIGURATION:
- case USB_REQ_SET_ADDRESS:
- case USB_REQ_GET_DESCRIPTOR:
- case USB_REQ_GET_CONFIGURATION:
- break;
- }
- case STATE_ADDRESSED:
- case STATE_CONFIGURED:
- break;
- case STATE_UNKNOWN:
- dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s",
- USBD_DEVICE_REQUESTS (request->bRequest),
- usbd_device_states[device->device_state]);
- return -1;
- }
-
- /* handle all requests that return data (direction bit set on bm RequestType) */
- if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
-
- dbg_ep0 (3, "Device-to-Host");
-
- switch (request->bRequest) {
-
- case USB_REQ_GET_STATUS:
- return ep0_get_status (device, urb, request->wIndex,
- request->bmRequestType &
- USB_REQ_RECIPIENT_MASK);
-
- case USB_REQ_GET_DESCRIPTOR:
- return ep0_get_descriptor (device, urb,
- le16_to_cpu (request->wLength),
- le16_to_cpu (request->wValue) >> 8,
- le16_to_cpu (request->wValue) & 0xff);
-
- case USB_REQ_GET_CONFIGURATION:
- dbg_ep0(2, "get config %d\n", device->configuration);
- return ep0_get_one (device, urb,
- device->configuration);
-
- case USB_REQ_GET_INTERFACE:
- return ep0_get_one (device, urb, device->alternate);
-
- case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
- return -1;
-
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- case USB_REQ_SET_ADDRESS:
- case USB_REQ_SET_DESCRIPTOR:
- case USB_REQ_SET_CONFIGURATION:
- case USB_REQ_SET_INTERFACE:
- return -1;
- }
- }
- /* handle the requests that do not return data */
- else {
-
- /*dbg_ep0(3, "Host-to-Device"); */
- switch (request->bRequest) {
-
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- dbg_ep0 (0, "Host-to-Device");
- switch (request->
- bmRequestType & USB_REQ_RECIPIENT_MASK) {
- case USB_REQ_RECIPIENT_DEVICE:
- /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */
- /* XXX fall through for now as we do not support either */
- case USB_REQ_RECIPIENT_INTERFACE:
- case USB_REQ_RECIPIENT_OTHER:
- dbg_ep0 (0, "request %s not",
- USBD_DEVICE_REQUESTS (request->bRequest));
- default:
- return -1;
-
- case USB_REQ_RECIPIENT_ENDPOINT:
- dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue));
- if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) {
- /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */
- /* request->bRequest == USB_REQ_SET_FEATURE); */
- /* NEED TO IMPLEMENT THIS!!! */
- return -1;
- } else {
- dbg_ep0 (1, "request %s bad wValue: %04x",
- USBD_DEVICE_REQUESTS
- (request->bRequest),
- le16_to_cpu (request->wValue));
- return -1;
- }
- }
-
- case USB_REQ_SET_ADDRESS:
- /* check if this is a re-address, reset first if it is (this shouldn't be possible) */
- if (device->device_state != STATE_DEFAULT) {
- dbg_ep0 (1, "set_address: %02x state: %s",
- le16_to_cpu (request->wValue),
- usbd_device_states[device->device_state]);
- return -1;
- }
- address = le16_to_cpu (request->wValue);
- if ((address & 0x7f) != address) {
- dbg_ep0 (1, "invalid address %04x %04x",
- address, address & 0x7f);
- return -1;
- }
- device->address = address;
-
- /*dbg_ep0(2, "address: %d %d %d", */
- /* request->wValue, le16_to_cpu(request->wValue), device->address); */
-
- return 0;
-
- case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */
- dbg_ep0 (0, "set descriptor: NOT SUPPORTED");
- return -1;
-
- case USB_REQ_SET_CONFIGURATION:
- /* c.f. 9.4.7 - the top half of wValue is reserved */
- device->configuration = le16_to_cpu(request->wValue) & 0xff;
-
- /* reset interface and alternate settings */
- device->interface = device->alternate = 0;
-
- /*dbg_ep0(2, "set configuration: %d", device->configuration); */
- /*dbg_ep0(2, "DEVICE_CONFIGURED.. event?\n"); */
- return 0;
-
- case USB_REQ_SET_INTERFACE:
- device->interface = le16_to_cpu (request->wIndex);
- device->alternate = le16_to_cpu (request->wValue);
- /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */
- dbg_ep0(2, "DEVICE_SET_INTERFACE.. event?\n");
- return 0;
-
- case USB_REQ_GET_STATUS:
- case USB_REQ_GET_DESCRIPTOR:
- case USB_REQ_GET_CONFIGURATION:
- case USB_REQ_GET_INTERFACE:
- case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
- return -1;
- }
- }
- return -1;
-}
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index f18c6a0a761..8f7256069f5 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -238,18 +238,21 @@ static int acm_bind(struct usb_configuration *c, struct usb_function *f)
return -ENODEV;
f_acm->ep_in = ep;
+ ep->driver_data = c->cdev; /* claim */
ep = usb_ep_autoconfig(gadget, &acm_fs_out_desc);
if (!ep)
return -ENODEV;
f_acm->ep_out = ep;
+ ep->driver_data = c->cdev; /* claim */
ep = usb_ep_autoconfig(gadget, &acm_fs_notify_desc);
if (!ep)
return -ENODEV;
f_acm->ep_notify = ep;
+ ep->driver_data = c->cdev; /* claim */
if (gadget_is_dualspeed(gadget)) {
/* Assume endpoint addresses are the same for both speeds */
@@ -660,6 +663,7 @@ static int acm_stdio_stop(struct stdio_dev *dev)
{
g_dnl_unregister();
g_dnl_clear_detach();
+ dev->priv = NULL;
return 0;
}
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/f_thor.c b/drivers/usb/gadget/f_thor.c
index 54372118348..540b9f88237 100644
--- a/drivers/usb/gadget/f_thor.c
+++ b/drivers/usb/gadget/f_thor.c
@@ -138,6 +138,7 @@ static int process_rqt_cmd(struct udevice *udc, const struct rqt_box *rqt)
case RQT_CMD_POWEROFF:
case RQT_CMD_EFSCLEAR:
send_rsp(udc, rsp);
+ fallthrough;
default:
printf("Command not supported -> cmd: %d\n", rqt->rqt_data);
return -EINVAL;
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/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index c020d13c43d..234a6f3645d 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1361,7 +1361,8 @@ pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len,
wLength));
databuf = root_hub_str_index1;
OK(len);
- }
+ }
+ fallthrough;
default:
stat = USB_ST_STALLED;
}
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index bfec303e7af..7247245a702 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -9,6 +9,7 @@
#define LOG_CATEGORY UCLASS_USB
#include <bootdev.h>
+#include <uthread.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
@@ -17,6 +18,7 @@
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
+#include <time.h>
static bool asynch_allowed;
@@ -172,6 +174,10 @@ int usb_get_max_xfer_size(struct usb_device *udev, size_t *size)
return ops->get_max_xfer_size(bus, size);
}
+#if CONFIG_IS_ENABLED(UTHREAD)
+static struct uthread_mutex mutex = UTHREAD_MUTEX_INITIALIZER;
+#endif
+
int usb_stop(void)
{
struct udevice *bus;
@@ -180,10 +186,14 @@ int usb_stop(void)
struct usb_uclass_priv *uc_priv;
int err = 0, ret;
+ uthread_mutex_lock(&mutex);
+
/* De-activate any devices that have been activated */
ret = uclass_get(UCLASS_USB, &uc);
- if (ret)
+ if (ret) {
+ uthread_mutex_unlock(&mutex);
return ret;
+ }
uc_priv = uclass_get_priv(uc);
@@ -218,28 +228,23 @@ int usb_stop(void)
uc_priv->companion_device_count = 0;
usb_started = 0;
+ uthread_mutex_unlock(&mutex);
+
return err;
}
-static void usb_scan_bus(struct udevice *bus, bool recurse)
+static void _usb_scan_bus(void *arg)
{
+ struct udevice *bus = (struct udevice *)arg;
struct usb_bus_priv *priv;
struct udevice *dev;
int ret;
priv = dev_get_uclass_priv(bus);
- assert(recurse); /* TODO: Support non-recusive */
-
- printf("scanning bus %s for devices... ", bus->name);
- debug("\n");
ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
if (ret)
- printf("failed, error %d\n", ret);
- else if (priv->next_addr == 0)
- printf("No USB Device found\n");
- else
- printf("%d USB Device(s) found\n", priv->next_addr);
+ printf("Scanning bus %s failed, error %d\n", bus->name, ret);
}
static void remove_inactive_children(struct uclass *uc, struct udevice *bus)
@@ -287,64 +292,127 @@ static int usb_probe_companion(struct udevice *bus)
return 0;
}
+static void _usb_init_bus(void *arg)
+{
+ struct udevice *bus = (struct udevice *)arg;
+ int ret;
+
+ /* init low_level USB */
+
+ /*
+ * For Sandbox, we need scan the device tree each time when we
+ * start the USB stack, in order to re-create the emulated USB
+ * devices and bind drivers for them before we actually do the
+ * driver probe.
+ *
+ * For USB onboard HUB, we need to do some non-trivial init
+ * like enabling a power regulator, before enumeration.
+ */
+ if (IS_ENABLED(CONFIG_SANDBOX) ||
+ IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) {
+ ret = dm_scan_fdt_dev(bus);
+ if (ret) {
+ printf("Bus %s: USB device scan from fdt failed (%d)\n",
+ bus->name, ret);
+ return;
+ }
+ }
+
+ ret = device_probe(bus);
+ if (ret == -ENODEV) { /* No such device. */
+ printf("Bus %s: Port not available.\n", bus->name);
+ return;
+ }
+
+ if (ret) { /* Other error. */
+ printf("Bus %s: probe failed, error %d\n", bus->name, ret);
+ return;
+ }
+
+ usb_probe_companion(bus);
+}
+
+static int nthr;
+static int grp_id;
+
+static void usb_init_bus(struct udevice *bus)
+{
+ if (!grp_id)
+ grp_id = uthread_grp_new_id();
+ if (!uthread_create(NULL, _usb_init_bus, (void *)bus, 0, grp_id))
+ nthr++;
+}
+
+static void usb_scan_bus(struct udevice *bus, bool recurse)
+{
+ if (!grp_id)
+ grp_id = uthread_grp_new_id();
+ if (!uthread_create(NULL, _usb_scan_bus, (void *)bus, 0, grp_id))
+ nthr++;
+}
+
+static void usb_report_devices(struct uclass *uc)
+{
+ struct usb_bus_priv *priv;
+ struct udevice *bus;
+
+ uclass_foreach_dev(bus, uc) {
+ if (!device_active(bus))
+ continue;
+ priv = dev_get_uclass_priv(bus);
+ printf("Bus %s: ", bus->name);
+ if (priv->next_addr == 0)
+ printf("No USB Device found\n");
+ else
+ printf("%d USB Device(s) found\n", priv->next_addr);
+ }
+}
+
+static void run_threads(void)
+{
+#if CONFIG_IS_ENABLED(UTHREAD)
+ if (!nthr)
+ return;
+ while (!uthread_grp_done(grp_id))
+ uthread_schedule();
+ nthr = 0;
+ grp_id = 0;
+#endif
+}
+
int usb_init(void)
{
int controllers_initialized = 0;
+ unsigned long t0 = timer_get_us();
struct usb_uclass_priv *uc_priv;
struct usb_bus_priv *priv;
struct udevice *bus;
struct uclass *uc;
int ret;
+ uthread_mutex_lock(&mutex);
+
+ if (usb_started) {
+ ret = 0;
+ goto out;
+ }
+
asynch_allowed = 1;
ret = uclass_get(UCLASS_USB, &uc);
if (ret)
- return ret;
+ goto out;
uc_priv = uclass_get_priv(uc);
uclass_foreach_dev(bus, uc) {
- /* init low_level USB */
- printf("Bus %s: ", bus->name);
-
- /*
- * For Sandbox, we need scan the device tree each time when we
- * start the USB stack, in order to re-create the emulated USB
- * devices and bind drivers for them before we actually do the
- * driver probe.
- *
- * For USB onboard HUB, we need to do some non-trivial init
- * like enabling a power regulator, before enumeration.
- */
- if (IS_ENABLED(CONFIG_SANDBOX) ||
- IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) {
- ret = dm_scan_fdt_dev(bus);
- if (ret) {
- printf("USB device scan from fdt failed (%d)", ret);
- continue;
- }
- }
-
- ret = device_probe(bus);
- if (ret == -ENODEV) { /* No such device. */
- puts("Port not available.\n");
- controllers_initialized++;
- continue;
- }
-
- if (ret) { /* Other error. */
- printf("probe failed, error %d\n", ret);
- continue;
- }
+ usb_init_bus(bus);
+ }
- ret = usb_probe_companion(bus);
- if (ret)
- continue;
+ if (CONFIG_IS_ENABLED(UTHREAD))
+ run_threads();
- controllers_initialized++;
- usb_started = true;
- }
+ usb_started = true;
/*
* lowlevel init done, now scan the bus for devices i.e. search HUBs
@@ -354,11 +422,16 @@ int usb_init(void)
if (!device_active(bus))
continue;
+ controllers_initialized++;
+
priv = dev_get_uclass_priv(bus);
if (!priv->companion)
usb_scan_bus(bus, true);
}
+ if (CONFIG_IS_ENABLED(UTHREAD))
+ run_threads();
+
/*
* Now that the primary controllers have been scanned and have handed
* over any devices they do not understand to their companions, scan
@@ -375,21 +448,34 @@ int usb_init(void)
}
}
- debug("scan end\n");
+ if (CONFIG_IS_ENABLED(UTHREAD))
+ run_threads();
+
+ usb_report_devices(uc);
/* Remove any devices that were not found on this scan */
remove_inactive_children(uc, bus);
ret = uclass_get(UCLASS_USB_HUB, &uc);
if (ret)
- return ret;
+ goto out;
+
remove_inactive_children(uc, bus);
/* if we were not able to find at least one working bus, bail out */
if (controllers_initialized == 0)
printf("No USB controllers found\n");
+ debug("USB initialized in %ld ms\n",
+ (timer_get_us() - t0) / 1000);
+
+ uthread_mutex_unlock(&mutex);
+
return usb_started ? 0 : -ENOENT;
+out:
+ uthread_mutex_unlock(&mutex);
+
+ return ret;
}
int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index d30725d3fca..3ee1f67190f 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -352,7 +352,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
* since it uses the same rules as low speed interrupt
* endpoints.
*/
-
+ fallthrough;
case USB_SPEED_LOW:
if (usb_endpoint_xfer_int(endpt_desc) ||
usb_endpoint_xfer_isoc(endpt_desc)) {
diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig
index c52afd41a75..ad9072a5327 100644
--- a/drivers/usb/musb-new/Kconfig
+++ b/drivers/usb/musb-new/Kconfig
@@ -22,6 +22,13 @@ config USB_MUSB_GADGET
Enables the MUSB USB dual-role controller in gadget mode.
if USB_MUSB_HOST || USB_MUSB_GADGET
+config USB_MUSB_SC5XX
+ bool "Analog Devices MUSB support"
+ depends on (SC57X || SC58X)
+ help
+ Say y here to enable support for the USB controller on
+ ADI SC57X/SC58X processors.
+
config USB_MUSB_DA8XX
bool "Enable DA8xx MUSB Controller"
depends on ARCH_DAVINCI
diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile
index 396ff02654b..6638772daca 100644
--- a/drivers/usb/musb-new/Makefile
+++ b/drivers/usb/musb-new/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
+obj-$(CONFIG_USB_MUSB_SC5XX) += sc5xx.o
ccflags-y := $(call cc-option,-Wno-unused-variable) \
$(call cc-option,-Wno-unused-but-set-variable) \
diff --git a/drivers/usb/musb-new/sc5xx.c b/drivers/usb/musb-new/sc5xx.c
new file mode 100644
index 00000000000..16201480b43
--- /dev/null
+++ b/drivers/usb/musb-new/sc5xx.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * ADI SC5XX MUSB "glue layer"
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Loosely ported from Linux driver:
+ * Author: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ *
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/usb/musb.h>
+#include "linux-compat.h"
+#include "musb_core.h"
+#include "musb_uboot.h"
+
+#define MUSB_SOFTRST 0x7f
+#define MUSB_SOFTRST_NRST BIT(0)
+#define MUSB_SOFTRST_NRSTX BIT(1)
+
+#define REG_USB_VBUS_CTL 0x380
+#define REG_USB_ID_CTL 0x382
+#define REG_USB_PHY_CTL 0x394
+#define REG_USB_PLL_OSC 0x398
+#define REG_USB_UTMI_CTL 0x39c
+
+/* controller data */
+struct sc5xx_musb_data {
+ struct musb_host_data mdata;
+ struct device dev;
+};
+
+#define to_sc5xx_musb_data(d) \
+ container_of(d, struct sc5xx_musb_data, dev)
+
+static void sc5xx_musb_disable(struct musb *musb)
+{
+ /* no way to shut the controller */
+}
+
+static int sc5xx_musb_enable(struct musb *musb)
+{
+ /* soft reset by NRSTx */
+ musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX);
+ /* set mode */
+ musb_platform_set_mode(musb, musb->board_mode);
+
+ return 0;
+}
+
+static irqreturn_t sc5xx_interrupt(int irq, void *hci)
+{
+ struct musb *musb = hci;
+ irqreturn_t ret = IRQ_NONE;
+ u8 devctl;
+
+ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+ musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+ musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+
+ if (musb->int_usb & MUSB_INTR_VBUSERROR) {
+ musb->int_usb &= ~MUSB_INTR_VBUSERROR;
+ devctl = musb_readw(musb->mregs, MUSB_DEVCTL);
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ }
+ if (musb->int_usb || musb->int_tx || musb->int_rx) {
+ musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
+ musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
+ musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
+ ret = musb_interrupt(musb);
+ }
+
+ if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))
+ musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
+
+ return ret;
+}
+
+static int sc5xx_musb_set_mode(struct musb *musb, u8 mode)
+{
+ struct device *dev = musb->controller;
+ struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(dev);
+
+ switch (mode) {
+ case MUSB_HOST:
+ musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x1);
+ break;
+ case MUSB_PERIPHERAL:
+ musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x3);
+ break;
+ case MUSB_OTG:
+ musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x0);
+ break;
+ default:
+ dev_err(dev, "unsupported mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sc5xx_musb_init(struct musb *musb)
+{
+ struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(musb->controller);
+
+ musb->isr = sc5xx_interrupt;
+
+ musb_writel(musb->mregs, REG_USB_PLL_OSC, 20 << 1);
+ musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0);
+ musb_writeb(musb->mregs, REG_USB_PHY_CTL, 0x80);
+ musb_writel(musb->mregs, REG_USB_UTMI_CTL,
+ 0x40 | musb_readl(musb->mregs, REG_USB_UTMI_CTL));
+
+ return 0;
+}
+
+const struct musb_platform_ops sc5xx_musb_ops = {
+ .init = sc5xx_musb_init,
+ .set_mode = sc5xx_musb_set_mode,
+ .disable = sc5xx_musb_disable,
+ .enable = sc5xx_musb_enable,
+};
+
+static struct musb_hdrc_config sc5xx_musb_config = {
+ .multipoint = 1,
+ .dyn_fifo = 1,
+ .num_eps = 16,
+ .ram_bits = 12,
+};
+
+/* has one MUSB controller which can be host or gadget */
+static struct musb_hdrc_platform_data sc5xx_musb_plat = {
+ .mode = MUSB_HOST,
+ .config = &sc5xx_musb_config,
+ .power = 100,
+ .platform_ops = &sc5xx_musb_ops,
+};
+
+static int musb_usb_probe(struct udevice *dev)
+{
+ struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+ struct sc5xx_musb_data *pdata = dev_get_priv(dev);
+ struct musb_host_data *mdata = &pdata->mdata;
+ void __iomem *mregs;
+ int ret;
+
+ priv->desc_before_addr = true;
+
+ mregs = dev_remap_addr(dev);
+ if (!mregs)
+ return -EINVAL;
+
+ /* init controller */
+ if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) {
+ mdata->host = musb_init_controller(&sc5xx_musb_plat,
+ &pdata->dev, mregs);
+ if (!mdata->host)
+ return -EIO;
+
+ ret = musb_lowlevel_init(mdata);
+ } else {
+ sc5xx_musb_plat.mode = MUSB_PERIPHERAL;
+ mdata->host = musb_register(&sc5xx_musb_plat, &pdata->dev, mregs);
+ if (!mdata->host)
+ return -EIO;
+ }
+ return ret;
+}
+
+static int musb_usb_remove(struct udevice *dev)
+{
+ struct sc5xx_musb_data *pdata = dev_get_priv(dev);
+
+ musb_stop(pdata->mdata.host);
+
+ return 0;
+}
+
+static const struct udevice_id sc5xx_musb_ids[] = {
+ { .compatible = "adi,sc5xx-musb" },
+ { }
+};
+
+U_BOOT_DRIVER(usb_musb) = {
+ .name = "sc5xx-musb",
+ .id = UCLASS_USB,
+ .of_match = sc5xx_musb_ids,
+ .probe = musb_usb_probe,
+ .remove = musb_usb_remove,
+#ifdef CONFIG_USB_MUSB_HOST
+ .ops = &musb_usb_ops,
+#endif
+ .plat_auto = sizeof(struct usb_plat),
+ .priv_auto = sizeof(struct sc5xx_musb_data),
+};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3c3cebaacd0..73353944971 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -293,27 +293,6 @@ config PANEL_HX8238D
source "drivers/video/fonts/Kconfig"
-config VIDCONSOLE_AS_LCD
- bool "Use 'vidconsole' when CONFIG_VIDCONSOLE_AS_NAME string is seen in stdout"
- help
- This is a work-around for boards which have 'lcd' or 'vga' in their
- stdout environment variable, but have moved to use driver model for
- video. In this case the console will no-longer work. While it is
- possible to update the environment, the breakage may be confusing for
- users. This option will be removed around the end of 2020.
-
-config VIDCONSOLE_AS_NAME
- string "Use 'vidconsole' when string defined here is seen in stdout"
- depends on VIDCONSOLE_AS_LCD
- default "lcd" if LCD || TEGRA_COMMON
- default "vga" if !LCD
- help
- This is a work-around for boards which have 'lcd' or 'vga' in their
- stdout environment variable, but have moved to use driver model for
- video. In this case the console will no-longer work. While it is
- possible to update the environment, the breakage may be confusing for
- users. This option will be removed around the end of 2020.
-
config VIDEO_BOCHS
bool "Enable Bochs video emulation for QEMU"
help
@@ -550,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
@@ -599,6 +588,15 @@ config VIDEO_LCD_SAMSUNG_LTL106HL02
LCD module found in Microsoft Surface 2. The panel has a FullHD
resolution (1920x1080).
+config VIDEO_LCD_SHARP_LQ079L1SX01
+ tristate "Sharp LQ079L1SX01 1536x2048 DSI video mode panel"
+ depends on PANEL && BACKLIGHT
+ select VIDEO_MIPI_DSI
+ help
+ Say Y here if you want to enable support for Sharp LQ079L1SX01
+ LCD module found in Xiaomi Mi Pad tablet. The panel has a QXGA
+ resolution (1536x2048).
+
config VIDEO_LCD_SHARP_LQ101R1SX01
tristate "Sharp LQ101R1SX01 2560x1600 DSI video mode panel"
depends on PANEL && BACKLIGHT
@@ -712,6 +710,12 @@ config I2C_EDID
help
This enables library for accessing EDID data from an LCD panel.
+config I2C_EDID_STANDARD
+ bool "Enable standard timings EDID library expansion"
+ depends on I2C_EDID
+ help
+ This enables standard timings expansion for EDID data from an LCD panel.
+
config DISPLAY
bool "Enable Display support"
depends on DM
@@ -734,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
@@ -743,6 +756,16 @@ config BACKLIGHT_LM3533
LM3533 Lighting Power chip. Only Bank A is supported as for now.
Supported backlight level range is from 2 to 255 with step of 1.
+config BACKLIGHT_LP855x
+ bool "Backlight Driver for LP855x"
+ depends on BACKLIGHT
+ select DM_I2C
+ help
+ Say Y to enable the backlight driver for National Semiconductor / TI
+ LP8550/1/2/3/5/6/7 LED Backlight Driver. Only register driven mode is
+ supported for now, PWM mode can be added if there will be any need in
+ it. Supported backlight level range is from 0 to 255 with step of 1.
+
source "drivers/video/ti/Kconfig"
source "drivers/video/exynos/Kconfig"
@@ -805,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 5a00438ce06..fbdb058647a 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -29,12 +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
@@ -52,18 +53,20 @@ 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
obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o
obj-$(CONFIG_VIDEO_LCD_SAMSUNG_LTL106HL02) += samsung-ltl106hl02.o
+obj-$(CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01) += sharp-lq079l1sx01.o
obj-$(CONFIG_VIDEO_LCD_SHARP_LQ101R1SX01) += sharp-lq101r1sx01.o
obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o
@@ -83,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/bridge/Kconfig b/drivers/video/bridge/Kconfig
index ab917273720..be53034bd3d 100644
--- a/drivers/video/bridge/Kconfig
+++ b/drivers/video/bridge/Kconfig
@@ -9,7 +9,7 @@ config VIDEO_BRIDGE
config VIDEO_BRIDGE_PARADE_DP501
bool "Support Parade DP501 DP & DVI/HDMI dual mode transmitter"
- depends on PANEL && DM_GPIO
+ depends on VIDEO_BRIDGE && PANEL && DM_GPIO
select DM_I2C
help
The Parade DP501 is a DP & DVI/HDMI dual-mode transmitter. It
@@ -46,16 +46,23 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345
config VIDEO_BRIDGE_SOLOMON_SSD2825
bool "Solomon SSD2825 bridge driver"
- depends on PANEL && DM_GPIO
+ depends on VIDEO_BRIDGE && PANEL && DM_GPIO
select VIDEO_MIPI_DSI
help
- Solomon SSD2824 SPI RGB-DSI bridge driver wrapped into panel uClass.
+ Solomon SSD2824 SPI RGB-DSI bridge driver.
config VIDEO_BRIDGE_TOSHIBA_TC358768
bool "Support Toshiba TC358768 MIPI DSI bridge"
- depends on PANEL && DM_GPIO
+ depends on VIDEO_BRIDGE && PANEL && DM_GPIO
select VIDEO_MIPI_DSI
select DM_I2C
help
Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver.
Found in Asus Transformer Infinity TF700T.
+
+config VIDEO_BRIDGE_LVDS_CODEC
+ bool "Transparent LVDS encoders and decoders support"
+ depends on VIDEO_BRIDGE && PANEL && DM_GPIO
+ help
+ Support for transparent LVDS encoders and decoders that don't
+ require any configuration.
diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile
index 58697e3cbe9..63dc6e62c49 100644
--- a/drivers/video/bridge/Makefile
+++ b/drivers/video/bridge/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o
obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o
obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o
obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o
+obj-$(CONFIG_VIDEO_BRIDGE_LVDS_CODEC) += lvds-codec.o
diff --git a/drivers/video/bridge/dp501.c b/drivers/video/bridge/dp501.c
index 095e3e71fed..9937cfe095b 100644
--- a/drivers/video/bridge/dp501.c
+++ b/drivers/video/bridge/dp501.c
@@ -9,6 +9,7 @@
#include <log.h>
#include <backlight.h>
#include <panel.h>
+#include <video_bridge.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <power/regulator.h>
@@ -206,7 +207,6 @@ struct dp501_priv {
struct udevice *chip2;
struct udevice *vdd;
- struct gpio_desc reset_gpio;
struct gpio_desc enable_gpio;
};
@@ -484,16 +484,19 @@ static int dp501_panel_timings(struct udevice *dev,
return 0;
}
-static void dp501_hw_init(struct dp501_priv *priv)
+static void dp501_hw_init(struct udevice *dev)
{
- dm_gpio_set_value(&priv->reset_gpio, 1);
+ struct dp501_priv *priv = dev_get_priv(dev);
+ struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ dm_gpio_set_value(&uc_priv->reset, 1);
regulator_set_enable_if_allowed(priv->vdd, 1);
dm_gpio_set_value(&priv->enable_gpio, 1);
udelay(100);
- dm_gpio_set_value(&priv->reset_gpio, 0);
+ dm_gpio_set_value(&uc_priv->reset, 0);
mdelay(80);
}
@@ -521,14 +524,6 @@ static int dp501_setup(struct udevice *dev)
}
/* get gpios */
- ret = gpio_request_by_name(dev, "reset-gpios", 0,
- &priv->reset_gpio, GPIOD_IS_OUT);
- if (ret) {
- log_debug("%s: Could not decode reset-gpios (%d)\n",
- __func__, ret);
- return ret;
- }
-
ret = gpio_request_by_name(dev, "enable-gpios", 0,
&priv->enable_gpio, GPIOD_IS_OUT);
if (ret) {
@@ -544,7 +539,7 @@ static int dp501_setup(struct udevice *dev)
return ret;
}
- dp501_hw_init(priv);
+ dp501_hw_init(dev);
/* get EDID */
return panel_get_display_timing(priv->panel, &priv->timing);
@@ -558,8 +553,8 @@ static int dp501_probe(struct udevice *dev)
return dp501_setup(dev);
}
-struct panel_ops dp501_ops = {
- .enable_backlight = dp501_attach,
+static const struct video_bridge_ops dp501_ops = {
+ .attach = dp501_attach,
.set_backlight = dp501_set_backlight,
.get_display_timing = dp501_panel_timings,
};
@@ -571,7 +566,7 @@ static const struct udevice_id dp501_ids[] = {
U_BOOT_DRIVER(dp501) = {
.name = "dp501",
- .id = UCLASS_PANEL,
+ .id = UCLASS_VIDEO_BRIDGE,
.of_match = dp501_ids,
.ops = &dp501_ops,
.probe = dp501_probe,
diff --git a/drivers/video/bridge/lvds-codec.c b/drivers/video/bridge/lvds-codec.c
new file mode 100644
index 00000000000..6cd8368a39e
--- /dev/null
+++ b/drivers/video/bridge/lvds-codec.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ * Loosely based on Linux lvds-codec.c driver
+ */
+
+#include <dm.h>
+#include <dm/ofnode_graph.h>
+#include <log.h>
+#include <panel.h>
+#include <video_bridge.h>
+#include <asm/gpio.h>
+#include <power/regulator.h>
+
+struct lvds_codec_priv {
+ struct udevice *panel;
+ struct display_timing timing;
+
+ struct gpio_desc powerdown_gpio;
+ struct udevice *power;
+};
+
+static int lvds_codec_attach(struct udevice *dev)
+{
+ struct lvds_codec_priv *priv = dev_get_priv(dev);
+
+ regulator_set_enable_if_allowed(priv->power, 1);
+ dm_gpio_set_value(&priv->powerdown_gpio, 0);
+
+ return panel_enable_backlight(priv->panel);
+}
+
+static int lvds_codec_set_panel(struct udevice *dev, int percent)
+{
+ struct lvds_codec_priv *priv = dev_get_priv(dev);
+
+ return panel_set_backlight(priv->panel, percent);
+}
+
+static int lvds_codec_panel_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct lvds_codec_priv *priv = dev_get_priv(dev);
+
+ memcpy(timing, &priv->timing, sizeof(*timing));
+
+ return 0;
+}
+
+/* Function is purely for sandbox testing */
+static int lvds_codec_read_edid(struct udevice *dev, u8 *buf, int buf_size)
+{
+ return 0;
+}
+
+static int lvds_codec_get_panel(struct udevice *dev)
+{
+ struct lvds_codec_priv *priv = dev_get_priv(dev);
+ int i, ret;
+
+ u32 num = ofnode_graph_get_port_count(dev_ofnode(dev));
+
+ for (i = 0; i < num; i++) {
+ ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1);
+
+ ret = uclass_get_device_by_of_offset(UCLASS_PANEL,
+ ofnode_to_offset(remote),
+ &priv->panel);
+ if (!ret)
+ return 0;
+ }
+
+ /* If this point is reached, no panels were found */
+ return -ENODEV;
+}
+
+static int lvds_codec_probe(struct udevice *dev)
+{
+ struct lvds_codec_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = lvds_codec_get_panel(dev);
+ if (ret) {
+ log_debug("%s: cannot get panel: ret=%d\n", __func__, ret);
+ return log_ret(ret);
+ }
+
+ panel_get_display_timing(priv->panel, &priv->timing);
+
+ ret = gpio_request_by_name(dev, "powerdown-gpios", 0,
+ &priv->powerdown_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_debug("%s: could not get powerdown-gpios (%d)\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ ret = device_get_supply_regulator(dev, "power-supply", &priv->power);
+ if (ret) {
+ log_debug("%s: power regulator error: %d\n", __func__, ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ return 0;
+}
+
+static const struct video_bridge_ops lvds_codec_ops = {
+ .attach = lvds_codec_attach,
+ .set_backlight = lvds_codec_set_panel,
+ .get_display_timing = lvds_codec_panel_timings,
+ .read_edid = lvds_codec_read_edid,
+};
+
+static const struct udevice_id lvds_codec_ids[] = {
+ { .compatible = "lvds-decoder" },
+ { .compatible = "lvds-encoder" },
+ { }
+};
+
+U_BOOT_DRIVER(lvds_codec) = {
+ .name = "lvds_codec",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = lvds_codec_ids,
+ .ops = &lvds_codec_ops,
+ .probe = lvds_codec_probe,
+ .priv_auto = sizeof(struct lvds_codec_priv),
+};
diff --git a/drivers/video/bridge/ssd2825.c b/drivers/video/bridge/ssd2825.c
index f978021c860..a307993377c 100644
--- a/drivers/video/bridge/ssd2825.c
+++ b/drivers/video/bridge/ssd2825.c
@@ -5,12 +5,15 @@
#include <clk.h>
#include <dm.h>
+#include <dm/ofnode_graph.h>
#include <log.h>
#include <misc.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <backlight.h>
+#include <video_bridge.h>
#include <panel.h>
+#include <power/regulator.h>
#include <spi.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -106,6 +109,10 @@
#define SSD2825_LP_MIN_CLK 5000 /* KHz */
#define SSD2825_REF_MIN_CLK 2000 /* KHz */
+static const char * const ssd2825_supplies[] = {
+ "dvdd-supply", "avdd-supply", "vddio-supply"
+};
+
struct ssd2825_bridge_priv {
struct mipi_dsi_host host;
struct mipi_dsi_device device;
@@ -113,12 +120,16 @@ struct ssd2825_bridge_priv {
struct udevice *panel;
struct display_timing timing;
+ struct udevice *supplies[ARRAY_SIZE(ssd2825_supplies)];
+
struct gpio_desc power_gpio;
- struct gpio_desc reset_gpio;
struct clk *tx_clk;
u32 pll_freq_kbps; /* PLL in kbps */
+
+ u32 hzd; /* HS Zero Delay in ns */
+ u32 hpd; /* HS Prepare Delay is ns */
};
static int ssd2825_spi_write(struct udevice *dev, int reg,
@@ -231,7 +242,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct udevice *dev = (struct udevice *)host->dev;
- u8 buf = *(u8 *)msg->tx_buf;
u16 config;
int ret;
@@ -260,15 +270,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host,
ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000);
ssd2825_write_dsi(dev, msg->tx_buf, msg->tx_len);
- if (buf == MIPI_DCS_SET_DISPLAY_ON) {
- ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG,
- SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN |
- SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD |
- SSD2825_CONF_REG_EOT);
- ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001);
- ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000);
- }
-
return 0;
}
@@ -312,9 +313,14 @@ static void ssd2825_setup_pll(struct udevice *dev)
struct mipi_dsi_device *device = &priv->device;
struct display_timing *dt = &priv->timing;
u16 pll_config, lp_div;
+ u32 nibble_delay, nibble_freq_khz;
u32 pclk_mult, tx_freq_khz, pd_lines;
+ u8 hzd, hpd;
tx_freq_khz = clk_get_rate(priv->tx_clk) / 1000;
+ if (!tx_freq_khz || tx_freq_khz < 0)
+ tx_freq_khz = SSD2825_REF_MIN_CLK;
+
pd_lines = mipi_dsi_pixel_format_to_bpp(device->format);
pclk_mult = pd_lines / device->lanes + 1;
@@ -324,12 +330,19 @@ static void ssd2825_setup_pll(struct udevice *dev)
lp_div = priv->pll_freq_kbps / (SSD2825_LP_MIN_CLK * 8);
+ /* nibble_delay in nanoseconds */
+ nibble_freq_khz = priv->pll_freq_kbps / 4;
+ nibble_delay = 1000 * 1000 / nibble_freq_khz;
+
+ hzd = priv->hzd / nibble_delay;
+ hpd = (priv->hpd - 4 * nibble_delay) / nibble_delay;
+
/* Disable PLL */
ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0000);
ssd2825_write_register(dev, SSD2825_LINE_CTRL_REG, 0x0001);
/* Set delays */
- ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, 0x2103);
+ ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, (hzd << 8) | hpd);
/* Set PLL coeficients */
ssd2825_write_register(dev, SSD2825_PLL_CONFIGURATION_REG, pll_config);
@@ -343,11 +356,30 @@ static void ssd2825_setup_pll(struct udevice *dev)
ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000);
}
-static int ssd2825_bridge_enable_panel(struct udevice *dev)
+static int ssd2825_bridge_attach(struct udevice *dev)
{
struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
struct mipi_dsi_device *device = &priv->device;
struct display_timing *dt = &priv->timing;
+ u8 pixel_format;
+ int ret;
+
+ /* Set pixel format */
+ switch (device->format) {
+ case MIPI_DSI_FMT_RGB565:
+ pixel_format = 0x00;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ pixel_format = 0x01;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ pixel_format = 0x02;
+ break;
+ case MIPI_DSI_FMT_RGB888:
+ default:
+ pixel_format = 0x03;
+ break;
+ }
/* Perform SW reset */
ssd2825_write_register(dev, SSD2825_OPERATION_CTRL_REG, 0x0100);
@@ -367,7 +399,7 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev)
ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_6,
SSD2825_HSYNC_HIGH | SSD2825_VSYNC_HIGH |
SSD2825_PCKL_HIGH | SSD2825_NON_BURST |
- (3 - device->format));
+ pixel_format);
ssd2825_write_register(dev, SSD2825_LANE_CONFIGURATION_REG,
device->lanes - 1);
ssd2825_write_register(dev, SSD2825_TEST_REG, 0x0004);
@@ -384,7 +416,18 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev)
ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000);
/* Perform panel setup */
- return panel_enable_backlight(priv->panel);
+ ret = panel_enable_backlight(priv->panel);
+ if (ret)
+ return ret;
+
+ ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG,
+ SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN |
+ SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD |
+ SSD2825_CONF_REG_EOT);
+ ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001);
+ ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000);
+
+ return 0;
}
static int ssd2825_bridge_set_panel(struct udevice *dev, int percent)
@@ -407,7 +450,8 @@ static int ssd2825_bridge_panel_timings(struct udevice *dev,
static int ssd2825_bridge_hw_init(struct udevice *dev)
{
struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
- int ret;
+ struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
+ int i, ret;
ret = clk_prepare_enable(priv->tx_clk);
if (ret) {
@@ -416,25 +460,28 @@ static int ssd2825_bridge_hw_init(struct udevice *dev)
return ret;
}
- ret = dm_gpio_set_value(&priv->power_gpio, 1);
- if (ret) {
- log_debug("%s: error changing power-gpios (%d)\n",
- __func__, ret);
- return ret;
+ /* enable supplies */
+ for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) {
+ ret = regulator_set_enable_if_allowed(priv->supplies[i], 1);
+ if (ret) {
+ log_debug("%s: cannot enable %s %d\n", __func__,
+ ssd2825_supplies[i], ret);
+ return ret;
+ }
}
mdelay(10);
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ ret = dm_gpio_set_value(&uc_priv->reset, 1);
if (ret) {
- log_debug("%s: error changing reset-gpios (%d)\n",
+ log_debug("%s: error entering reset (%d)\n",
__func__, ret);
return ret;
}
mdelay(10);
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ ret = dm_gpio_set_value(&uc_priv->reset, 0);
if (ret) {
- log_debug("%s: error changing reset-gpios (%d)\n",
+ log_debug("%s: error exiting reset (%d)\n",
__func__, ret);
return ret;
}
@@ -443,13 +490,33 @@ static int ssd2825_bridge_hw_init(struct udevice *dev)
return 0;
}
+static int ssd2825_bridge_get_panel(struct udevice *dev)
+{
+ struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
+ int i, ret;
+
+ u32 num = ofnode_graph_get_port_count(dev_ofnode(dev));
+
+ for (i = 0; i < num; i++) {
+ ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1);
+
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote,
+ &priv->panel);
+ if (!ret)
+ return 0;
+ }
+
+ /* If this point is reached, no panels were found */
+ return -ENODEV;
+}
+
static int ssd2825_bridge_probe(struct udevice *dev)
{
struct ssd2825_bridge_priv *priv = dev_get_priv(dev);
struct spi_slave *slave = dev_get_parent_priv(dev);
struct mipi_dsi_device *device = &priv->device;
struct mipi_dsi_panel_plat *mipi_plat;
- int ret;
+ int i, ret;
ret = spi_claim_bus(slave);
if (ret) {
@@ -457,10 +524,9 @@ static int ssd2825_bridge_probe(struct udevice *dev)
return ret;
}
- ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
- "panel", &priv->panel);
+ ret = ssd2825_bridge_get_panel(dev);
if (ret) {
- log_err("cannot get panel: ret=%d\n", ret);
+ log_debug("%s: panel not found, ret %d\n", __func__, ret);
return ret;
}
@@ -477,33 +543,33 @@ static int ssd2825_bridge_probe(struct udevice *dev)
device->format = mipi_plat->format;
device->mode_flags = mipi_plat->mode_flags;
- /* get panel gpios */
- ret = gpio_request_by_name(dev, "power-gpios", 0,
- &priv->power_gpio, GPIOD_IS_OUT);
- if (ret) {
- log_err("could not decode power-gpios (%d)\n", ret);
- return ret;
- }
-
- ret = gpio_request_by_name(dev, "reset-gpios", 0,
- &priv->reset_gpio, GPIOD_IS_OUT);
- if (ret) {
- log_err("could not decode reset-gpios (%d)\n", ret);
- return ret;
+ /* get supplies */
+ for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) {
+ ret = device_get_supply_regulator(dev, ssd2825_supplies[i],
+ &priv->supplies[i]);
+ if (ret) {
+ log_debug("%s: cannot get %s %d\n", __func__,
+ ssd2825_supplies[i], ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
}
/* get clk */
- priv->tx_clk = devm_clk_get(dev, "tx_clk");
+ priv->tx_clk = devm_clk_get_optional(dev, NULL);
if (IS_ERR(priv->tx_clk)) {
log_err("cannot get tx_clk: %ld\n", PTR_ERR(priv->tx_clk));
return PTR_ERR(priv->tx_clk);
}
+ priv->hzd = dev_read_u32_default(dev, "solomon,hs-zero-delay-ns", 133);
+ priv->hpd = dev_read_u32_default(dev, "solomon,hs-prep-delay-ns", 40);
+
return ssd2825_bridge_hw_init(dev);
}
-static const struct panel_ops ssd2825_bridge_ops = {
- .enable_backlight = ssd2825_bridge_enable_panel,
+static const struct video_bridge_ops ssd2825_bridge_ops = {
+ .attach = ssd2825_bridge_attach,
.set_backlight = ssd2825_bridge_set_panel,
.get_display_timing = ssd2825_bridge_panel_timings,
};
@@ -515,9 +581,10 @@ static const struct udevice_id ssd2825_bridge_ids[] = {
U_BOOT_DRIVER(ssd2825) = {
.name = "ssd2825",
- .id = UCLASS_PANEL,
+ .id = UCLASS_VIDEO_BRIDGE,
.of_match = ssd2825_bridge_ids,
.ops = &ssd2825_bridge_ops,
+ .bind = dm_scan_fdt_dev,
.probe = ssd2825_bridge_probe,
.priv_auto = sizeof(struct ssd2825_bridge_priv),
};
diff --git a/drivers/video/bridge/tc358768.c b/drivers/video/bridge/tc358768.c
index 19b6ca29d3e..358004f30f2 100644
--- a/drivers/video/bridge/tc358768.c
+++ b/drivers/video/bridge/tc358768.c
@@ -6,12 +6,14 @@
#include <clk.h>
#include <dm.h>
+#include <dm/ofnode_graph.h>
#include <i2c.h>
#include <log.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <backlight.h>
#include <panel.h>
+#include <video_bridge.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
@@ -122,6 +124,10 @@
#define NANO 1000000000UL
#define PICO 1000000000000ULL
+static const char * const tc358768_supplies[] = {
+ "vddc-supply", "vddmipi-supply", "vddio-supply"
+};
+
struct tc358768_priv {
struct mipi_dsi_host host;
struct mipi_dsi_device device;
@@ -129,9 +135,7 @@ struct tc358768_priv {
struct udevice *panel;
struct display_timing timing;
- struct udevice *vddc;
- struct udevice *vddmipi;
- struct udevice *vddio;
+ struct udevice *supplies[ARRAY_SIZE(tc358768_supplies)];
struct clk *refclk;
@@ -265,25 +269,27 @@ static void tc358768_sw_reset(struct udevice *dev)
tc358768_write(dev, TC358768_SYSCTL, 0);
}
-static void tc358768_hw_enable(struct tc358768_priv *priv)
+static void tc358768_hw_enable(struct udevice *dev)
{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+ struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
int ret;
ret = clk_prepare_enable(priv->refclk);
if (ret)
log_debug("%s: error enabling refclk (%d)\n", __func__, ret);
- ret = regulator_set_enable_if_allowed(priv->vddc, true);
+ ret = regulator_set_enable_if_allowed(priv->supplies[0], true);
if (ret)
log_debug("%s: error enabling vddc (%d)\n", __func__, ret);
- ret = regulator_set_enable_if_allowed(priv->vddmipi, true);
+ ret = regulator_set_enable_if_allowed(priv->supplies[1], true);
if (ret)
log_debug("%s: error enabling vddmipi (%d)\n", __func__, ret);
mdelay(10);
- ret = regulator_set_enable_if_allowed(priv->vddio, true);
+ ret = regulator_set_enable_if_allowed(priv->supplies[2], true);
if (ret)
log_debug("%s: error enabling vddio (%d)\n", __func__, ret);
@@ -293,7 +299,7 @@ static void tc358768_hw_enable(struct tc358768_priv *priv)
* The RESX is active low (GPIO_ACTIVE_LOW).
* DEASSERT (value = 0) the reset_gpio to enable the chip
*/
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ ret = dm_gpio_set_value(&uc_priv->reset, 0);
if (ret)
log_debug("%s: error changing reset-gpio (%d)\n", __func__, ret);
@@ -477,7 +483,7 @@ static int tc358768_attach(struct udevice *dev)
device->mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS;
}
- tc358768_hw_enable(priv);
+ tc358768_hw_enable(dev);
tc358768_sw_reset(dev);
tc358768_setup_pll(dev);
@@ -874,12 +880,33 @@ static int tc358768_panel_timings(struct udevice *dev,
return 0;
}
+static int tc358768_get_panel(struct udevice *dev)
+{
+ struct tc358768_priv *priv = dev_get_priv(dev);
+ int i, ret;
+
+ u32 num = ofnode_graph_get_port_count(dev_ofnode(dev));
+
+ for (i = 0; i < num; i++) {
+ ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1);
+
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote,
+ &priv->panel);
+ if (!ret)
+ return 0;
+ }
+
+ /* If this point is reached, no panels were found */
+ return -ENODEV;
+}
+
static int tc358768_setup(struct udevice *dev)
{
struct tc358768_priv *priv = dev_get_priv(dev);
+ struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
struct mipi_dsi_device *device = &priv->device;
struct mipi_dsi_panel_plat *mipi_plat;
- int ret;
+ int i, ret;
/* The bridge uses 16 bit registers */
ret = i2c_set_chip_offset_len(dev, 2);
@@ -889,11 +916,10 @@ static int tc358768_setup(struct udevice *dev)
return ret;
}
- ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
- "panel", &priv->panel);
+ ret = tc358768_get_panel(dev);
if (ret) {
- log_debug("%s: Cannot get panel: ret=%d\n", __func__, ret);
- return log_ret(ret);
+ log_debug("%s: panel not found, ret %d\n", __func__, ret);
+ return ret;
}
panel_get_display_timing(priv->panel, &priv->timing);
@@ -913,44 +939,26 @@ static int tc358768_setup(struct udevice *dev)
priv->dsi_lanes = device->lanes;
/* get regulators */
- ret = device_get_supply_regulator(dev, "vddc-supply", &priv->vddc);
- if (ret) {
- log_debug("%s: vddc regulator error: %d\n", __func__, ret);
- if (ret != -ENOENT)
- return log_ret(ret);
- }
-
- ret = device_get_supply_regulator(dev, "vddmipi-supply", &priv->vddmipi);
- if (ret) {
- log_debug("%s: vddmipi regulator error: %d\n", __func__, ret);
- if (ret != -ENOENT)
- return log_ret(ret);
- }
-
- ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio);
- if (ret) {
- log_debug("%s: vddio regulator error: %d\n", __func__, ret);
- if (ret != -ENOENT)
- return log_ret(ret);
+ for (i = 0; i < ARRAY_SIZE(tc358768_supplies); i++) {
+ ret = device_get_supply_regulator(dev, tc358768_supplies[i],
+ &priv->supplies[i]);
+ if (ret) {
+ log_debug("%s: cannot get %s %d\n", __func__,
+ tc358768_supplies[i], ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
}
/* get clk */
- priv->refclk = devm_clk_get(dev, "refclk");
+ priv->refclk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->refclk)) {
log_debug("%s: Could not get refclk: %ld\n",
__func__, PTR_ERR(priv->refclk));
return PTR_ERR(priv->refclk);
}
- /* get gpios */
- ret = gpio_request_by_name(dev, "reset-gpios", 0,
- &priv->reset_gpio, GPIOD_IS_OUT);
- if (ret) {
- log_debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret);
- return ret;
- }
-
- dm_gpio_set_value(&priv->reset_gpio, 1);
+ dm_gpio_set_value(&uc_priv->reset, 1);
return 0;
}
@@ -963,8 +971,8 @@ static int tc358768_probe(struct udevice *dev)
return tc358768_setup(dev);
}
-struct panel_ops tc358768_ops = {
- .enable_backlight = tc358768_attach,
+static const struct video_bridge_ops tc358768_ops = {
+ .attach = tc358768_attach,
.set_backlight = tc358768_set_backlight,
.get_display_timing = tc358768_panel_timings,
};
@@ -977,9 +985,10 @@ static const struct udevice_id tc358768_ids[] = {
U_BOOT_DRIVER(tc358768) = {
.name = "tc358768",
- .id = UCLASS_PANEL,
+ .id = UCLASS_VIDEO_BRIDGE,
.of_match = tc358768_ids,
.ops = &tc358768_ops,
+ .bind = dm_scan_fdt_dev,
.probe = tc358768_probe,
.priv_auto = sizeof(struct tc358768_priv),
};
diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c
index 2084a2e03ee..1b8aa12b9e8 100644
--- a/drivers/video/bridge/video-bridge-uclass.c
+++ b/drivers/video/bridge/video-bridge-uclass.c
@@ -33,6 +33,17 @@ int video_bridge_attach(struct udevice *dev)
return ops->attach(dev);
}
+int video_bridge_get_display_timing(struct udevice *dev,
+ struct display_timing *timings)
+{
+ struct video_bridge_ops *ops = video_bridge_get_ops(dev);
+
+ if (!ops->get_display_timing)
+ return -ENOSYS;
+
+ return ops->get_display_timing(dev, timings);
+}
+
int video_bridge_check_attached(struct udevice *dev)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
diff --git a/drivers/video/endeavoru-panel.c b/drivers/video/endeavoru-panel.c
index d4ba4d8b6da..9950ff8bb05 100644
--- a/drivers/video/endeavoru-panel.c
+++ b/drivers/video/endeavoru-panel.c
@@ -117,6 +117,18 @@ static int endeavoru_panel_set_backlight(struct udevice *dev, int percent)
struct endeavoru_panel_priv *priv = dev_get_priv(dev);
int ret;
+ /*
+ * Due to the use of the Tegra DC backlight feature, backlight
+ * requests MUST NOT be made during probe or earlier. This is
+ * because it creates a loop, as the backlight is a DC child.
+ */
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+ "backlight", &priv->backlight);
+ if (ret) {
+ log_err("cannot get backlight: ret = %d\n", ret);
+ return ret;
+ }
+
ret = backlight_enable(priv->backlight);
if (ret)
return ret;
@@ -136,13 +148,6 @@ static int endeavoru_panel_of_to_plat(struct udevice *dev)
struct endeavoru_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_err("cannot get backlight: ret = %d\n", ret);
- return ret;
- }
-
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"vdd-supply", &priv->vdd);
if (ret) {
@@ -231,7 +236,7 @@ static int endeavoru_panel_probe(struct udevice *dev)
/* fill characteristics of DSI data link */
plat->lanes = 2;
plat->format = MIPI_DSI_FMT_RGB888;
- plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
return endeavoru_panel_hw_init(dev);
}
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/lg-ld070wx3.c b/drivers/video/lg-ld070wx3.c
index 610a06ffe7b..3676e45bc65 100644
--- a/drivers/video/lg-ld070wx3.c
+++ b/drivers/video/lg-ld070wx3.c
@@ -158,7 +158,7 @@ static int lg_ld070wx3_probe(struct udevice *dev)
/* fill characteristics of DSI data link */
plat->lanes = 4;
plat->format = MIPI_DSI_FMT_RGB888;
- plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
return lg_ld070wx3_hw_init(dev);
}
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", &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/lm3533_backlight.c b/drivers/video/lm3533_backlight.c
index 6b51fa0628e..7b87b6bd40b 100644
--- a/drivers/video/lm3533_backlight.c
+++ b/drivers/video/lm3533_backlight.c
@@ -7,6 +7,7 @@
#include <backlight.h>
#include <dm.h>
+#include <dm/ofnode.h>
#include <i2c.h>
#include <log.h>
#include <linux/delay.h>
@@ -17,56 +18,79 @@
#define LM3533_BL_MAX_BRIGHTNESS 0xFF
#define LM3533_SINK_OUTPUT_CONFIG_1 0x10
-#define LM3533_CONTROL_BANK_A_PWM 0x14
+#define LM3533_CONTROL_PWM_BASE 0x14
+#define PWM_MAX GENMASK(5, 0)
#define LM3533_CONTROL_BANK_AB_BRIGHTNESS 0x1A
-#define LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT 0x1F
+#define LM3533_CONTROL_FULLSCALE_CURRENT_BASE 0x1F
+#define MAX_CURRENT_MIN 5000
+#define MAX_CURRENT_MAX 29800
+#define MAX_CURRENT_STEP 800
#define LM3533_CONTROL_BANK_ENABLE 0x27
#define LM3533_OVP_FREQUENCY_PWM_POLARITY 0x2C
+#define BOOST_OVP_MASK GENMASK(2, 1)
+#define BOOST_OVP_SHIFT 1
+#define BOOST_FREQ_MASK BIT(0)
+#define BOOST_FREQ_SHIFT 0
#define LM3533_BRIGHTNESS_REGISTER_A 0x40
+#define LM3533_BOOST_OVP_16V 16000000UL
+#define LM3533_BOOST_FREQ_500KHZ 500000UL
+
struct lm3533_backlight_priv {
struct gpio_desc enable_gpio;
u32 def_bl_lvl;
+
+ /* Core */
+ u32 boost_ovp;
+ u32 boost_freq;
+
+ /* Backlight */
+ u32 reg;
+ u16 max_current; /* 5000 - 29800 uA (800 uA step) */
+ u8 pwm; /* 0 - 0x3f */
+ bool linear;
+ bool hvled;
};
static int lm3533_backlight_enable(struct udevice *dev)
{
struct lm3533_backlight_priv *priv = dev_get_priv(dev);
+ u8 val, id = priv->reg;
int ret;
- dm_gpio_set_value(&priv->enable_gpio, 1);
- mdelay(5);
+ if (priv->linear) {
+ ret = dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS,
+ BIT(2 * id + 1), BIT(2 * id + 1));
+ if (ret)
+ return ret;
+ }
- /* HVLED 1 & 2 are controlled by Bank A */
- ret = dm_i2c_reg_write(dev, LM3533_SINK_OUTPUT_CONFIG_1, 0x00);
- if (ret)
- return ret;
+ if (priv->hvled) {
+ ret = dm_i2c_reg_clrset(dev, LM3533_SINK_OUTPUT_CONFIG_1,
+ BIT(0) | BIT(1), id | id << 1);
+ if (ret)
+ return ret;
+ }
- /* PWM input is disabled for CABC */
- ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_PWM, 0x00);
- if (ret)
- return ret;
+ /* Set current */
+ if (priv->max_current < MAX_CURRENT_MIN || priv->max_current > MAX_CURRENT_MAX)
+ return -EINVAL;
- /* Linear & Control Bank A is configured for register Current control */
- ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, 0x02);
+ val = (priv->max_current - MAX_CURRENT_MIN) / MAX_CURRENT_STEP;
+ ret = dm_i2c_reg_write(dev, LM3533_CONTROL_FULLSCALE_CURRENT_BASE + id, val);
if (ret)
return ret;
- /* Full-Scale Current (20.2mA) */
- ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT, 0x13);
- if (ret)
- return ret;
+ /* Set PWM mask */
+ if (priv->pwm > PWM_MAX)
+ return -EINVAL;
- /* Control Bank A is enable */
- ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_ENABLE, 0x01);
+ ret = dm_i2c_reg_write(dev, LM3533_CONTROL_PWM_BASE + id, priv->pwm);
if (ret)
return ret;
- ret = dm_i2c_reg_write(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, 0x0A);
- if (ret)
- return ret;
-
- return 0;
+ /* Enable Control Bank */
+ return dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_ENABLE, BIT(id), BIT(id));
}
static int lm3533_backlight_set_brightness(struct udevice *dev, int percent)
@@ -92,6 +116,56 @@ static int lm3533_backlight_set_brightness(struct udevice *dev, int percent)
return 0;
}
+static int lm3533_backlight_of_to_plat(struct udevice *dev)
+{
+ struct lm3533_backlight_priv *priv = dev_get_priv(dev);
+ ofnode child;
+ int ret;
+
+ ret = gpio_request_by_name(dev, "enable-gpios", 0,
+ &priv->enable_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ log_err("Could not decode enable-gpios (%d)\n", ret);
+ return ret;
+ }
+
+ priv->boost_ovp = dev_read_u32_default(dev, "ti,boost-ovp-microvolt",
+ LM3533_BOOST_OVP_16V);
+
+ /* boost_ovp is defined in microvolts, convert to enum value */
+ priv->boost_ovp = priv->boost_ovp / (8 * 1000 * 1000) - 2;
+
+ priv->boost_freq = dev_read_u32_default(dev, "ti,boost-freq-hz",
+ LM3533_BOOST_FREQ_500KHZ);
+
+ /* boost_freq is defined in Hz, convert to enum value */
+ priv->boost_freq = priv->boost_freq / (500 * 1000) - 1;
+
+ /* Backlight is one of children but has no dedicated driver */
+ ofnode_for_each_subnode(child, dev_ofnode(dev)) {
+ if (ofnode_device_is_compatible(child, "ti,lm3533-backlight")) {
+ const char *node_name = ofnode_get_name(child);
+
+ if (!strcmp(&node_name[10], "1"))
+ priv->reg = 1;
+ else
+ priv->reg = 0;
+
+ priv->max_current = ofnode_read_u32_default(child, "ti,max-current-microamp",
+ 5000);
+ priv->pwm = ofnode_read_u32_default(child, "ti,pwm-config-mask", 0);
+
+ priv->def_bl_lvl = ofnode_read_u32_default(child, "default-brightness",
+ LM3533_BL_MAX_BRIGHTNESS);
+
+ priv->linear = ofnode_read_bool(child, "ti,linear-mapping-mode");
+ priv->hvled = ofnode_read_bool(child, "ti,hardware-controlled");
+ }
+ }
+
+ return 0;
+}
+
static int lm3533_backlight_probe(struct udevice *dev)
{
struct lm3533_backlight_priv *priv = dev_get_priv(dev);
@@ -100,15 +174,22 @@ static int lm3533_backlight_probe(struct udevice *dev)
if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
return -EPROTONOSUPPORT;
- ret = gpio_request_by_name(dev, "enable-gpios", 0,
- &priv->enable_gpio, GPIOD_IS_OUT);
+ dm_gpio_set_value(&priv->enable_gpio, 1);
+ mdelay(5);
+
+ ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY,
+ BOOST_FREQ_MASK, priv->boost_freq << BOOST_FREQ_SHIFT);
if (ret) {
- log_err("Could not decode enable-gpios (%d)\n", ret);
+ log_debug("%s: freq config failed %d\n", __func__, ret);
return ret;
}
- priv->def_bl_lvl = dev_read_u32_default(dev, "default-brightness-level",
- LM3533_BL_MAX_BRIGHTNESS);
+ ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY,
+ BOOST_OVP_MASK, priv->boost_ovp << BOOST_OVP_SHIFT);
+ if (ret) {
+ log_debug("%s: ovp config failed %d\n", __func__, ret);
+ return ret;
+ }
return 0;
}
@@ -127,6 +208,7 @@ U_BOOT_DRIVER(lm3533_backlight) = {
.name = "lm3533_backlight",
.id = UCLASS_PANEL_BACKLIGHT,
.of_match = lm3533_backlight_ids,
+ .of_to_plat = lm3533_backlight_of_to_plat,
.probe = lm3533_backlight_probe,
.ops = &lm3533_backlight_ops,
.priv_auto = sizeof(struct lm3533_backlight_priv),
diff --git a/drivers/video/lp855x_backlight.c b/drivers/video/lp855x_backlight.c
new file mode 100644
index 00000000000..5debc0aa453
--- /dev/null
+++ b/drivers/video/lp855x_backlight.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Texas Instruments
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
+
+#include <malloc.h>
+#include <backlight.h>
+#include <dm.h>
+#include <dm/devres.h>
+#include <i2c.h>
+#include <log.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <asm/gpio.h>
+#include <power/regulator.h>
+
+#define LP855x_MIN_BRIGHTNESS 0x00
+#define LP855x_MAX_BRIGHTNESS 0xff
+
+/* LP8550/1/2/3/6 Registers */
+#define LP855X_BRIGHTNESS_CTRL 0x00
+#define LP855X_DEVICE_CTRL 0x01
+#define LP855X_EEPROM_START 0xa0
+#define LP855X_EEPROM_END 0xa7
+#define LP8556_EPROM_START 0x98
+#define LP8556_EPROM_END 0xaf
+
+/* LP8555/7 Registers */
+#define LP8557_BL_CMD 0x00
+#define LP8557_BL_MASK 0x01
+#define LP8557_BL_ON 0x01
+#define LP8557_BL_OFF 0x00
+#define LP8557_BRIGHTNESS_CTRL 0x04
+#define LP8557_CONFIG 0x10
+#define LP8555_EPROM_START 0x10
+#define LP8555_EPROM_END 0x7a
+#define LP8557_EPROM_START 0x10
+#define LP8557_EPROM_END 0x1e
+
+struct lp855x_rom_data {
+ u8 addr;
+ u8 val;
+};
+
+struct lp855x_backlight_priv;
+
+/*
+ * struct lp855x_device_config
+ * @pre_init_device: init device function call before updating the brightness
+ * @reg_brightness: register address for brigthenss control
+ * @reg_devicectrl: register address for device control
+ * @post_init_device: late init device function call
+ */
+struct lp855x_device_config {
+ int (*pre_init_device)(struct udevice *dev);
+ u8 reg_brightness;
+ u8 reg_devicectrl;
+ u8 reg_eepromstart;
+ u8 reg_eepromend;
+ int (*post_init_device)(struct udevice *dev);
+};
+
+struct lp855x_backlight_priv {
+ struct udevice *supply; /* regulator for VDD input */
+ struct udevice *enable; /* regulator for EN/VDDIO input */
+
+ u8 device_control;
+ u8 initial_brightness;
+
+ int size_program;
+ struct lp855x_rom_data *rom_data;
+ struct lp855x_device_config *cfg;
+};
+
+static int lp855x_backlight_enable(struct udevice *dev)
+{
+ struct lp855x_backlight_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = regulator_set_enable_if_allowed(priv->supply, 1);
+ if (ret) {
+ log_debug("%s: enabling power-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->enable, 1);
+ if (ret) {
+ log_debug("%s: enabling enable-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(2);
+
+ if (priv->cfg->pre_init_device) {
+ ret = priv->cfg->pre_init_device(dev);
+ if (ret) {
+ log_debug("%s: pre init device err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ ret = dm_i2c_reg_write(dev, priv->cfg->reg_brightness,
+ priv->initial_brightness);
+ if (ret)
+ return ret;
+
+ ret = dm_i2c_reg_write(dev, priv->cfg->reg_devicectrl,
+ priv->device_control);
+ if (ret)
+ return ret;
+
+ if (priv->size_program > 0) {
+ int i;
+ u8 val, addr;
+
+ for (i = 0; i < priv->size_program; i++) {
+ addr = priv->rom_data[i].addr;
+ val = priv->rom_data[i].val;
+
+ if (addr < priv->cfg->reg_eepromstart &&
+ addr > priv->cfg->reg_eepromend)
+ continue;
+
+ ret = dm_i2c_reg_write(dev, addr, val);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (priv->cfg->post_init_device) {
+ ret = priv->cfg->post_init_device(dev);
+ if (ret) {
+ log_debug("%s: post init device err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int lp855x_backlight_set_brightness(struct udevice *dev, int percent)
+{
+ struct lp855x_backlight_priv *priv = dev_get_priv(dev);
+
+ if (percent == BACKLIGHT_DEFAULT)
+ percent = priv->initial_brightness;
+
+ if (percent < LP855x_MIN_BRIGHTNESS)
+ percent = LP855x_MIN_BRIGHTNESS;
+
+ if (percent > LP855x_MAX_BRIGHTNESS)
+ percent = LP855x_MAX_BRIGHTNESS;
+
+ /* Set brightness level */
+ return dm_i2c_reg_write(dev, priv->cfg->reg_brightness,
+ percent);
+}
+
+static int lp855x_backlight_probe(struct udevice *dev)
+{
+ struct lp855x_backlight_priv *priv = dev_get_priv(dev);
+ int rom_length, ret;
+
+ if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
+ return -EPROTONOSUPPORT;
+
+ priv->cfg = (struct lp855x_device_config *)dev_get_driver_data(dev);
+
+ dev_read_u8(dev, "dev-ctrl", &priv->device_control);
+ dev_read_u8(dev, "init-brt", &priv->initial_brightness);
+
+ /* Fill ROM platform data if defined */
+ rom_length = dev_get_child_count(dev);
+ if (rom_length > 0) {
+ struct lp855x_rom_data *rom;
+ ofnode child;
+ int i = 0;
+
+ rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL);
+ if (!rom)
+ return -ENOMEM;
+
+ dev_for_each_subnode(child, dev) {
+ ofnode_read_u8(child, "rom-addr", &rom[i].addr);
+ ofnode_read_u8(child, "rom-val", &rom[i].val);
+ i++;
+ }
+
+ priv->size_program = rom_length;
+ priv->rom_data = &rom[0];
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "power-supply", &priv->supply);
+ if (ret) {
+ log_err("%s: cannot get power-supply: ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "enable-supply", &priv->enable);
+ if (ret) {
+ log_err("%s: cannot get enable-supply: ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct backlight_ops lp855x_backlight_ops = {
+ .enable = lp855x_backlight_enable,
+ .set_brightness = lp855x_backlight_set_brightness,
+};
+
+static int lp8556_bl_rst(struct udevice *dev)
+{
+ int ret;
+
+ /* Reset backlight after updating EPROM settings */
+ ret = dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK,
+ LP8557_BL_OFF);
+ if (ret)
+ return ret;
+
+ mdelay(10);
+
+ return dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK,
+ LP8557_BL_ON);
+}
+
+static int lp8557_bl_off(struct udevice *dev)
+{
+ /* BL_ON = 0 before updating EPROM settings */
+ return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK,
+ LP8557_BL_OFF);
+}
+
+static int lp8557_bl_on(struct udevice *dev)
+{
+ /* BL_ON = 1 after updating EPROM settings */
+ return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK,
+ LP8557_BL_ON);
+}
+
+static struct lp855x_device_config lp855x_dev_cfg = {
+ .reg_brightness = LP855X_BRIGHTNESS_CTRL,
+ .reg_devicectrl = LP855X_DEVICE_CTRL,
+ .reg_eepromstart = LP855X_EEPROM_START,
+ .reg_eepromend = LP855X_EEPROM_END,
+};
+
+static struct lp855x_device_config lp8555_dev_cfg = {
+ .reg_brightness = LP8557_BRIGHTNESS_CTRL,
+ .reg_devicectrl = LP8557_CONFIG,
+ .reg_eepromstart = LP8555_EPROM_START,
+ .reg_eepromend = LP8555_EPROM_END,
+ .pre_init_device = lp8557_bl_off,
+ .post_init_device = lp8557_bl_on,
+};
+
+static struct lp855x_device_config lp8556_dev_cfg = {
+ .reg_brightness = LP855X_BRIGHTNESS_CTRL,
+ .reg_devicectrl = LP855X_DEVICE_CTRL,
+ .reg_eepromstart = LP8556_EPROM_START,
+ .reg_eepromend = LP8556_EPROM_END,
+ .post_init_device = lp8556_bl_rst,
+};
+
+static struct lp855x_device_config lp8557_dev_cfg = {
+ .reg_brightness = LP8557_BRIGHTNESS_CTRL,
+ .reg_devicectrl = LP8557_CONFIG,
+ .reg_eepromstart = LP8557_EPROM_START,
+ .reg_eepromend = LP8557_EPROM_END,
+ .pre_init_device = lp8557_bl_off,
+ .post_init_device = lp8557_bl_on,
+};
+
+static const struct udevice_id lp855x_backlight_ids[] = {
+ { .compatible = "ti,lp8550", .data = (ulong)&lp855x_dev_cfg },
+ { .compatible = "ti,lp8551", .data = (ulong)&lp855x_dev_cfg },
+ { .compatible = "ti,lp8552", .data = (ulong)&lp855x_dev_cfg },
+ { .compatible = "ti,lp8553", .data = (ulong)&lp855x_dev_cfg },
+ { .compatible = "ti,lp8555", .data = (ulong)&lp8555_dev_cfg },
+ { .compatible = "ti,lp8556", .data = (ulong)&lp8556_dev_cfg },
+ { .compatible = "ti,lp8557", .data = (ulong)&lp8557_dev_cfg },
+ { }
+};
+
+U_BOOT_DRIVER(lp855x_backlight) = {
+ .name = "lp855x_backlight",
+ .id = UCLASS_PANEL_BACKLIGHT,
+ .of_match = lp855x_backlight_ids,
+ .probe = lp855x_backlight_probe,
+ .ops = &lp855x_backlight_ops,
+ .priv_auto = sizeof(struct lp855x_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/renesas-r61307.c b/drivers/video/renesas-r61307.c
index a3697bce5ee..ef6fab1e953 100644
--- a/drivers/video/renesas-r61307.c
+++ b/drivers/video/renesas-r61307.c
@@ -254,17 +254,17 @@ static int renesas_r61307_hw_init(struct udevice *dev)
return ret;
}
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
if (ret) {
- log_debug("%s: changing reset-gpio failed (%d)\n",
+ log_debug("%s: entering reset failed (%d)\n",
__func__, ret);
return ret;
}
mdelay(5);
- ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
if (ret) {
- log_debug("%s: changing reset-gpio failed (%d)\n",
+ log_debug("%s: exiting reset failed (%d)\n",
__func__, ret);
return ret;
}
@@ -281,7 +281,8 @@ static int renesas_r61307_probe(struct udevice *dev)
/* fill characteristics of DSI data link */
plat->lanes = 4;
plat->format = MIPI_DSI_FMT_RGB888;
- plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
return renesas_r61307_hw_init(dev);
}
@@ -294,7 +295,7 @@ static const struct panel_ops renesas_r61307_ops = {
static const struct udevice_id renesas_r61307_ids[] = {
{ .compatible = "koe,tx13d100vm0eaa" },
- { .compatible = "hitachi,tx13d100vm0eaa" },
+ { .compatible = "hit,tx13d100vm0eaa" },
{ }
};
diff --git a/drivers/video/renesas-r69328.c b/drivers/video/renesas-r69328.c
index 9861c3fef11..0954b04b62e 100644
--- a/drivers/video/renesas-r69328.c
+++ b/drivers/video/renesas-r69328.c
@@ -32,9 +32,11 @@
#define R69328_POWER_SET 0xD1
struct renesas_r69328_priv {
+ struct udevice *vdd;
+ struct udevice *vddio;
+
struct udevice *backlight;
- struct gpio_desc enable_gpio;
struct gpio_desc reset_gpio;
};
@@ -159,10 +161,15 @@ static int renesas_r69328_of_to_plat(struct udevice *dev)
return ret;
}
- ret = gpio_request_by_name(dev, "enable-gpios", 0,
- &priv->enable_gpio, GPIOD_IS_OUT);
+ ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd);
if (ret) {
- log_err("could not decode enable-gpios (%d)\n", ret);
+ log_err("Cannot get vdd-supply: ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio);
+ if (ret) {
+ log_err("Cannot get vddio-supply: ret = %d\n", ret);
return ret;
}
@@ -181,25 +188,32 @@ static int renesas_r69328_hw_init(struct udevice *dev)
struct renesas_r69328_priv *priv = dev_get_priv(dev);
int ret;
- ret = dm_gpio_set_value(&priv->enable_gpio, 1);
+ ret = regulator_set_enable_if_allowed(priv->vddio, 1);
if (ret) {
- log_debug("%s: error changing enable-gpios (%d)\n",
+ log_debug("%s: enabling vddio-supply failed (%d)\n",
__func__, ret);
return ret;
}
mdelay(5);
- ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ ret = regulator_set_enable_if_allowed(priv->vdd, 1);
if (ret) {
- log_debug("%s: error changing reset-gpios (%d)\n",
+ log_debug("%s: enabling vdd-supply failed (%d)\n",
__func__, ret);
return ret;
}
- mdelay(5);
ret = dm_gpio_set_value(&priv->reset_gpio, 1);
if (ret) {
- log_debug("%s: error changing reset-gpios (%d)\n",
+ log_debug("%s: error entering reset (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(5);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error exiting reset (%d)\n",
__func__, ret);
return ret;
}
@@ -216,7 +230,8 @@ static int renesas_r69328_probe(struct udevice *dev)
/* fill characteristics of DSI data link */
plat->lanes = 4;
plat->format = MIPI_DSI_FMT_RGB888;
- plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
return renesas_r69328_hw_init(dev);
}
diff --git a/drivers/video/samsung-ltl106hl02.c b/drivers/video/samsung-ltl106hl02.c
index 5e6c11c4be3..1efc9fca610 100644
--- a/drivers/video/samsung-ltl106hl02.c
+++ b/drivers/video/samsung-ltl106hl02.c
@@ -129,7 +129,7 @@ static int samsung_ltl106hl02_probe(struct udevice *dev)
/* fill characteristics of DSI data link */
plat->lanes = 4;
plat->format = MIPI_DSI_FMT_RGB888;
- plat->mode_flags = MIPI_DSI_MODE_VIDEO;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
return samsung_ltl106hl02_hw_init(dev);
}
diff --git a/drivers/video/sharp-lq079l1sx01.c b/drivers/video/sharp-lq079l1sx01.c
new file mode 100644
index 00000000000..a8197f40fc7
--- /dev/null
+++ b/drivers/video/sharp-lq079l1sx01.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Sharp LQ079L1SX01 DSI panel driver
+ *
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <log.h>
+#include <mipi_dsi.h>
+#include <linux/delay.h>
+#include <asm/gpio.h>
+#include <power/regulator.h>
+
+struct sharp_lq079l1sx01_priv {
+ struct udevice *backlight;
+ struct udevice *panel_sec;
+
+ struct udevice *avdd;
+ struct udevice *vddio;
+ struct udevice *vsp;
+ struct udevice *vsn;
+
+ struct gpio_desc reset_gpio;
+};
+
+static struct display_timing default_timing = {
+ .pixelclock.typ = 215000000,
+ .hactive.typ = 1536,
+ .hfront_porch.typ = 136,
+ .hback_porch.typ = 28,
+ .hsync_len.typ = 28,
+ .vactive.typ = 2048,
+ .vfront_porch.typ = 14,
+ .vback_porch.typ = 8,
+ .vsync_len.typ = 2,
+};
+
+static int dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data)
+{
+ return mipi_dsi_dcs_write(dsi, cmd, &data, 1);
+}
+
+static int sharp_lq079l1sx01_configure_link(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+ struct mipi_dsi_device *dsi = plat->device;
+ int ret;
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to exit sleep mode %s: %d\n",
+ __func__, dev->parent->name, ret);
+ }
+ mdelay(120);
+
+ ret = dcs_write_one(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
+ if (ret < 0) {
+ log_debug("%s: failed to SET_DISPLAY_BRIGHTNESS %s: %d\n",
+ __func__, dev->parent->name, ret);
+ }
+ ret = dcs_write_one(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x01);
+ if (ret < 0) {
+ log_debug("%s: failed to WRITE_POWER_SAVE %s: %d\n",
+ __func__, dev->parent->name, ret);
+ }
+ ret = dcs_write_one(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c);
+ if (ret < 0) {
+ log_debug("%s: failed to WRITE_CONTROL_DISPLAY %s: %d\n",
+ __func__, dev->parent->name, ret);
+ }
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ log_debug("%s: failed to set panel on %s: %d\n",
+ __func__, dev->parent->name, ret);
+ }
+
+ return 0;
+}
+
+static int sharp_lq079l1sx01_enable_backlight(struct udevice *dev)
+{
+ struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev);
+
+ if (!priv->panel_sec)
+ return 0;
+
+ sharp_lq079l1sx01_configure_link(dev);
+ sharp_lq079l1sx01_configure_link(priv->panel_sec);
+
+ return 0;
+}
+
+static int sharp_lq079l1sx01_set_backlight(struct udevice *dev, int percent)
+{
+ struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (!priv->panel_sec)
+ return 0;
+
+ ret = backlight_enable(priv->backlight);
+ if (ret)
+ return ret;
+
+ return backlight_set_brightness(priv->backlight, percent);
+}
+
+static int sharp_lq079l1sx01_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ memcpy(timing, &default_timing, sizeof(*timing));
+ return 0;
+}
+
+static int sharp_lq079l1sx01_of_to_plat(struct udevice *dev)
+{
+ struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ /* If node has no link2 it is secondary panel */
+ if (!dev_read_bool(dev, "link2"))
+ return 0;
+
+ ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
+ "link2", &priv->panel_sec);
+ if (ret) {
+ log_debug("%s: cannot get secondary panel: ret = %d\n",
+ __func__, ret);
+ return 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 = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "avdd-supply", &priv->avdd);
+ if (ret) {
+ log_debug("%s: cannot get avdd-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "vddio-supply", &priv->vddio);
+ if (ret) {
+ log_debug("%s: cannot get vddio-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "vsp-supply", &priv->vsp);
+ if (ret) {
+ log_debug("%s: cannot get vsp-supply: ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ "vsn-supply", &priv->vsn);
+ if (ret) {
+ log_debug("%s: cannot get vsn-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: cannot get reser-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sharp_lq079l1sx01_hw_init(struct udevice *dev)
+{
+ struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (!priv->panel_sec)
+ return 0;
+
+ 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->avdd, 1);
+ if (ret) {
+ log_debug("%s: enabling avdd-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(12);
+
+ ret = regulator_set_enable_if_allowed(priv->vsp, 1);
+ if (ret) {
+ log_debug("%s: enabling vsp-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(12);
+
+ ret = regulator_set_enable_if_allowed(priv->vsn, 1);
+ if (ret) {
+ log_debug("%s: enabling vsn-supply failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mdelay(24);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error disabling reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ udelay(3);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+ if (ret) {
+ log_debug("%s: error enabling reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ udelay(3);
+
+ ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+ if (ret) {
+ log_debug("%s: error disabling reset-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(32);
+
+ return 0;
+}
+
+static int sharp_lq079l1sx01_probe(struct udevice *dev)
+{
+ struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+
+ /* fill characteristics of DSI data link */
+ plat->lanes = 4;
+ plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
+
+ return sharp_lq079l1sx01_hw_init(dev);
+}
+
+static const struct panel_ops sharp_lq079l1sx01_ops = {
+ .enable_backlight = sharp_lq079l1sx01_enable_backlight,
+ .set_backlight = sharp_lq079l1sx01_set_backlight,
+ .get_display_timing = sharp_lq079l1sx01_timings,
+};
+
+static const struct udevice_id sharp_lq079l1sx01_ids[] = {
+ { .compatible = "sharp,lq079l1sx01" },
+ { }
+};
+
+U_BOOT_DRIVER(sharp_lq079l1sx01) = {
+ .name = "sharp_lq079l1sx01",
+ .id = UCLASS_PANEL,
+ .of_match = sharp_lq079l1sx01_ids,
+ .ops = &sharp_lq079l1sx01_ops,
+ .of_to_plat = sharp_lq079l1sx01_of_to_plat,
+ .probe = sharp_lq079l1sx01_probe,
+ .plat_auto = sizeof(struct mipi_dsi_panel_plat),
+ .priv_auto = sizeof(struct sharp_lq079l1sx01_priv),
+};
diff --git a/drivers/video/sharp-lq101r1sx01.c b/drivers/video/sharp-lq101r1sx01.c
index 5d8453fd796..4fdf0da8a94 100644
--- a/drivers/video/sharp-lq101r1sx01.c
+++ b/drivers/video/sharp-lq101r1sx01.c
@@ -255,6 +255,7 @@ static int sharp_lq101r1sx01_probe(struct udevice *dev)
/* fill characteristics of DSI data link */
plat->lanes = 4;
plat->format = MIPI_DSI_FMT_RGB888;
+ plat->mode_flags = MIPI_DSI_MODE_LPM;
return sharp_lq101r1sx01_hw_init(dev);
}
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 79d8a021a3a..eff10b5563e 100644
--- a/drivers/video/tegra20/tegra-pwm-backlight.c
+++ b/drivers/video/tegra/dc-pwm-backlight.c
@@ -15,10 +15,7 @@
#include <asm/io.h>
#include <asm/gpio.h>
-#include "tegra-dc.h"
-
-#define TEGRA_DISPLAY_A_BASE 0x54200000
-#define TEGRA_DISPLAY_B_BASE 0x54240000
+#include "dc.h"
#define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10
#define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF
@@ -106,14 +103,11 @@ static int tegra_pwm_backlight_enable(struct udevice *dev)
static int tegra_pwm_backlight_probe(struct udevice *dev)
{
struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev);
+ ofnode dc = ofnode_get_parent(dev_ofnode(dev));
- if (dev_read_bool(dev, "nvidia,display-b-base"))
- priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_B_BASE;
- else
- priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_A_BASE;
-
+ priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc);
if (!priv->dc) {
- log_err("no display controller address\n");
+ log_err("%s: failed to get DC controller\n", __func__);
return -EINVAL;
}
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra/dc.c
index d24aa375b39..f0e3d2c993f 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra/dc.c
@@ -1,30 +1,25 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <backlight.h>
#include <cpu_func.h>
+#include <clk.h>
#include <dm.h>
+#include <dm/ofnode_graph.h>
#include <fdtdec.h>
#include <log.h>
#include <panel.h>
-#include <part.h>
-#include <pwm.h>
#include <video.h>
-#include <asm/cache.h>
-#include <asm/global_data.h>
+#include <video_bridge.h>
#include <asm/system.h>
-#include <asm/gpio.h>
#include <asm/io.h>
-
#include <asm/arch/clock.h>
-#include <asm/arch/funcmux.h>
-#include <asm/arch/pinmux.h>
#include <asm/arch/powergate.h>
-#include <asm/arch/pwm.h>
-#include "tegra-dc.h"
+#include "dc.h"
/* Holder of Tegra per-SOC DC differences */
struct tegra_dc_soc_info {
@@ -39,12 +34,14 @@ struct tegra_lcd_priv {
int height; /* height in pixels */
enum video_log2_bpp log2_bpp; /* colour depth */
struct display_timing timing;
- struct udevice *panel;
+ struct udevice *panel; /* Panels attached to RGB */
+ struct udevice *bridge; /* Bridge linked with DC */
struct dc_ctlr *dc; /* Display controller regmap */
const struct tegra_dc_soc_info *soc;
fdt_addr_t frame_buffer; /* Address of frame buffer */
unsigned pixel_clock; /* Pixel clock in Hz */
- int dc_clk[2]; /* Contains clk and its parent */
+ struct clk *clk;
+ struct clk *clk_parent;
ulong scdiv; /* Clock divider used by disp_clk_ctrl */
bool rotation; /* 180 degree panel turn */
int pipe; /* DC controller: 0 for A, 1 for B */
@@ -144,10 +141,9 @@ static int update_display_mode(struct tegra_lcd_priv *priv)
val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
writel(val, &disp->disp_interface_ctrl);
- }
- if (priv->soc->has_rgb)
writel(0x00010001, &disp->shift_clk_opt);
+ }
val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT;
@@ -267,7 +263,9 @@ static int setup_window(struct tegra_lcd_priv *priv,
win->out_h = priv->height;
win->phys_addr = priv->frame_buffer;
win->stride = priv->width * (1 << priv->log2_bpp) / 8;
- debug("%s: depth = %d\n", __func__, priv->log2_bpp);
+
+ log_debug("%s: depth = %d\n", __func__, priv->log2_bpp);
+
switch (priv->log2_bpp) {
case VIDEO_BPP32:
win->fmt = COLOR_DEPTH_R8G8B8A8;
@@ -279,7 +277,7 @@ static int setup_window(struct tegra_lcd_priv *priv,
break;
default:
- debug("Unsupported LCD bit depth");
+ log_debug("Unsupported LCD bit depth\n");
return -1;
}
@@ -301,7 +299,8 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv,
void *default_lcd_base)
{
struct disp_ctl_win window;
- unsigned long rate = clock_get_rate(priv->dc_clk[1]);
+ unsigned long rate = clk_get_rate(priv->clk_parent);
+ int ret;
priv->frame_buffer = (u32)default_lcd_base;
@@ -309,15 +308,10 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv,
* We halve the rate if DISP1 parent is PLLD, since actual parent
* is plld_out0 which is PLLD divided by 2.
*/
- if (priv->dc_clk[1] == CLOCK_ID_DISPLAY)
+ if (priv->clk_parent->id == CLOCK_ID_DISPLAY ||
+ priv->clk_parent->id == CLOCK_ID_DISPLAY2)
rate /= 2;
-#ifndef CONFIG_TEGRA20
- /* PLLD2 obeys same rules as PLLD but it is present only on T30+ */
- if (priv->dc_clk[1] == CLOCK_ID_DISPLAY2)
- rate /= 2;
-#endif
-
/*
* The pixel clock divider is in 7.1 format (where the bottom bit
* represents 0.5). Here we calculate the divider needed to get from
@@ -327,14 +321,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv,
if (!priv->scdiv)
priv->scdiv = ((rate * 2 + priv->pixel_clock / 2)
/ priv->pixel_clock) - 2;
- debug("Display clock %lu, divider %lu\n", rate, priv->scdiv);
+ log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv);
- /*
- * HOST1X is init by default at 150MHz with PLLC as parent
- */
- clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_CGENERAL,
- 150 * 1000000);
- clock_start_periph_pll(priv->dc_clk[0], priv->dc_clk[1],
+ clock_start_periph_pll(priv->clk->id, priv->clk_parent->id,
rate);
basic_init(&priv->dc->cmd);
@@ -348,8 +337,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv,
if (priv->pixel_clock)
update_display_mode(priv);
- if (setup_window(priv, &window))
- return -1;
+ ret = setup_window(priv, &window);
+ if (ret)
+ return ret;
update_window(priv, &window);
@@ -364,10 +354,6 @@ static int tegra_lcd_probe(struct udevice *dev)
int ret;
/* Initialize the Tegra display controller */
-#ifdef CONFIG_TEGRA20
- funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
-#endif
-
if (priv->soc->has_pgate) {
uint powergate;
@@ -378,44 +364,51 @@ static int tegra_lcd_probe(struct udevice *dev)
ret = tegra_powergate_power_off(powergate);
if (ret < 0) {
- log_err("failed to power off DISP gate: %d", ret);
+ log_debug("failed to power off DISP gate: %d", ret);
return ret;
}
ret = tegra_powergate_sequence_power_up(powergate,
- priv->dc_clk[0]);
+ priv->clk->id);
if (ret < 0) {
- log_err("failed to power up DISP gate: %d", ret);
+ log_debug("failed to power up DISP gate: %d", ret);
return ret;
}
}
/* Get shift clock divider from Tegra DSI if used */
- if (!strcmp(priv->panel->name, TEGRA_DSI_A) ||
- !strcmp(priv->panel->name, TEGRA_DSI_B)) {
- struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
+ if (priv->bridge) {
+ if (!strcmp(priv->bridge->driver->name, "tegra_dsi")) {
+ struct tegra_dc_plat *dc_plat = dev_get_plat(priv->bridge);
- priv->scdiv = dc_plat->scdiv;
+ priv->scdiv = dc_plat->scdiv;
+ }
}
/* Clean the framebuffer area */
memset((u8 *)plat->base, 0, plat->size);
flush_dcache_all();
- if (tegra_display_probe(priv, (void *)plat->base)) {
- debug("%s: Failed to probe display driver\n", __func__);
- return -1;
+ ret = tegra_display_probe(priv, (void *)plat->base);
+ if (ret) {
+ log_debug("%s: Failed to probe display driver\n", __func__);
+ return ret;
}
-#ifdef CONFIG_TEGRA20
- pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM);
- pinmux_tristate_disable(PMUX_PINGRP_GPU);
-#endif
+ if (priv->panel) {
+ ret = panel_enable_backlight(priv->panel);
+ if (ret) {
+ log_debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret);
+ return ret;
+ }
+ }
- ret = panel_enable_backlight(priv->panel);
- if (ret) {
- debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret);
- return ret;
+ if (priv->bridge) {
+ ret = video_bridge_attach(priv->bridge);
+ if (ret) {
+ log_debug("%s: Cannot attach bridge, ret=%d\n", __func__, ret);
+ return ret;
+ }
}
mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size,
@@ -427,81 +420,198 @@ static int tegra_lcd_probe(struct udevice *dev)
uc_priv->xsize = priv->width;
uc_priv->ysize = priv->height;
uc_priv->bpix = priv->log2_bpp;
- debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer,
- plat->size);
+ log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer,
+ plat->size);
- return panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
+ if (priv->panel) {
+ ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->bridge) {
+ ret = video_bridge_set_backlight(priv->bridge, BACKLIGHT_DEFAULT);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra_lcd_configure_rgb(struct udevice *dev, ofnode rgb)
+{
+ struct tegra_lcd_priv *priv = dev_get_priv(dev);
+ ofnode remote;
+ int ret;
+
+ /* DC can have only 1 port */
+ remote = ofnode_graph_get_remote_node(rgb, -1, -1);
+
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel);
+ if (!ret)
+ return 0;
+
+ ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &priv->bridge);
+ if (!ret)
+ return 0;
+
+ /* Try legacy method if graph did not work */
+ remote = ofnode_parse_phandle(rgb, "nvidia,panel", 0);
+ if (!ofnode_valid(remote))
+ return -EINVAL;
+
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel);
+ if (ret) {
+ log_debug("%s: Cannot find panel for '%s' (ret=%d)\n",
+ __func__, dev->name, ret);
+
+ ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote,
+ &priv->bridge);
+ if (ret) {
+ log_err("%s: Cannot find panel or bridge for '%s' (ret=%d)\n",
+ __func__, dev->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tegra_lcd_configure_internal(struct udevice *dev)
+{
+ struct tegra_lcd_priv *priv = dev_get_priv(dev);
+ struct tegra_dc_plat *dc_plat;
+ ofnode host1x = ofnode_get_parent(dev_ofnode(dev));
+ ofnode node;
+ int ret;
+
+ switch (priv->pipe) {
+ case 0: /* DC0 is usually used for DSI */
+ /* Check for ganged DSI configuration */
+ ofnode_for_each_subnode(node, host1x)
+ if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node) &&
+ ofnode_read_bool(node, "nvidia,ganged-mode"))
+ goto exit;
+
+ /* If no master DSI found loop for any active DSI */
+ ofnode_for_each_subnode(node, host1x)
+ if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node))
+ goto exit;
+
+ log_err("%s: failed to find DSI device for '%s'\n",
+ __func__, dev->name);
+
+ return -ENODEV;
+ case 1: /* DC1 is usually used for HDMI */
+ ofnode_for_each_subnode(node, host1x)
+ if (ofnode_name_eq(node, "hdmi"))
+ goto exit;
+
+ log_err("%s: failed to find HDMI device for '%s'\n",
+ __func__, dev->name);
+
+ return -ENODEV;
+ default:
+ log_debug("Unsupported DC selection\n");
+ return -EINVAL;
+ }
+
+exit:
+ ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, &priv->bridge);
+ if (ret) {
+ log_err("%s: failed to get DSI/HDMI device for '%s' (ret %d)\n",
+ __func__, dev->name, ret);
+ return ret;
+ }
+
+ priv->clk_parent = devm_clk_get(priv->bridge, "parent");
+ if (IS_ERR(priv->clk_parent)) {
+ log_debug("%s: Could not get DC clock parent from DSI/HDMI: %ld\n",
+ __func__, PTR_ERR(priv->clk_parent));
+ return PTR_ERR(priv->clk_parent);
+ }
+
+ dc_plat = dev_get_plat(priv->bridge);
+
+ /* Fill the platform data for internal devices */
+ dc_plat->dev = dev;
+ dc_plat->dc = priv->dc;
+ dc_plat->pipe = priv->pipe;
+
+ return 0;
}
static int tegra_lcd_of_to_plat(struct udevice *dev)
{
struct tegra_lcd_priv *priv = dev_get_priv(dev);
- const void *blob = gd->fdt_blob;
struct display_timing *timing;
- int node = dev_of_offset(dev);
- int panel_node;
- int rgb;
+ ofnode rgb;
int ret;
priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev);
if (!priv->dc) {
- debug("%s: No display controller address\n", __func__);
+ log_debug("%s: No display controller address\n", __func__);
return -EINVAL;
}
priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev);
- ret = clock_decode_pair(dev, priv->dc_clk);
- if (ret < 0) {
- debug("%s: Cannot decode clocks for '%s' (ret = %d)\n",
- __func__, dev->name, ret);
- return -EINVAL;
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ log_debug("%s: Could not get DC clock: %ld\n",
+ __func__, PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->clk_parent = devm_clk_get(dev, "parent");
+ if (IS_ERR(priv->clk_parent)) {
+ log_debug("%s: Could not get DC clock parent: %ld\n",
+ __func__, PTR_ERR(priv->clk_parent));
+ return PTR_ERR(priv->clk_parent);
}
priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");
priv->pipe = dev_read_u32_default(dev, "nvidia,head", 0);
- rgb = fdt_subnode_offset(blob, node, "rgb");
- if (rgb < 0) {
- debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n",
- __func__, dev->name, rgb);
- return -EINVAL;
- }
-
/*
- * Sadly the panel phandle is in an rgb subnode so we cannot use
- * uclass_get_device_by_phandle().
+ * Usual logic of Tegra video routing should be next:
+ * 1. Check rgb subnode for RGB/LVDS panels or bridges
+ * 2. If none found, then iterate through bridges bound,
+ * looking for DSIA or DSIB for DC0 and HDMI for DC1.
+ * If none of above is valid, then configuration is not
+ * valid.
*/
- panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel");
- if (panel_node < 0) {
- debug("%s: Cannot find panel information\n", __func__);
- return -EINVAL;
- }
- ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node,
- &priv->panel);
- if (ret) {
- debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
- dev->name, ret);
- return ret;
+ rgb = dev_read_subnode(dev, "rgb");
+ if (ofnode_valid(rgb) && ofnode_is_enabled(rgb)) {
+ /* RGB is available, use it */
+ ret = tegra_lcd_configure_rgb(dev, rgb);
+ if (ret)
+ return ret;
+ } else {
+ /* RGB is not available, check for internal devices */
+ ret = tegra_lcd_configure_internal(dev);
+ if (ret)
+ return ret;
}
- /* Fill the platform data for internal devices */
- if (!strcmp(priv->panel->name, TEGRA_DSI_A) ||
- !strcmp(priv->panel->name, TEGRA_DSI_B)) {
- struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
-
- dc_plat->dev = dev;
- dc_plat->dc = priv->dc;
- dc_plat->pipe = priv->pipe;
+ if (priv->panel) {
+ ret = panel_get_display_timing(priv->panel, &priv->timing);
+ if (ret) {
+ ret = ofnode_decode_display_timing(rgb, 0, &priv->timing);
+ if (ret) {
+ log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
+ __func__, dev->name, ret);
+ return -EINVAL;
+ }
+ }
}
- ret = panel_get_display_timing(priv->panel, &priv->timing);
- if (ret) {
- ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing);
+ if (priv->bridge) {
+ ret = video_bridge_get_display_timing(priv->bridge, &priv->timing);
if (ret) {
- debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
- __func__, dev->name, ret);
+ log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
+ __func__, dev->name, ret);
return -EINVAL;
}
}
@@ -518,23 +628,13 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
static int tegra_lcd_bind(struct udevice *dev)
{
struct video_uc_plat *plat = dev_get_uclass_plat(dev);
- const void *blob = gd->fdt_blob;
- int node = dev_of_offset(dev);
- int rgb;
-
- rgb = fdt_subnode_offset(blob, node, "rgb");
- if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb))
- return -ENODEV;
plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
(1 << LCD_MAX_LOG2_BPP) / 8;
- return 0;
+ return dm_scan_fdt_dev(dev);
}
-static const struct video_ops tegra_lcd_ops = {
-};
-
static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
.has_timer = true,
.has_rgb = true,
@@ -564,6 +664,9 @@ static const struct udevice_id tegra_lcd_ids[] = {
.compatible = "nvidia,tegra114-dc",
.data = (ulong)&tegra114_dc_soc_info
}, {
+ .compatible = "nvidia,tegra124-dc",
+ .data = (ulong)&tegra114_dc_soc_info
+ }, {
/* sentinel */
}
};
@@ -572,7 +675,6 @@ U_BOOT_DRIVER(tegra_lcd) = {
.name = "tegra_lcd",
.id = UCLASS_VIDEO,
.of_match = tegra_lcd_ids,
- .ops = &tegra_lcd_ops,
.bind = tegra_lcd_bind,
.probe = tegra_lcd_probe,
.of_to_plat = tegra_lcd_of_to_plat,
diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra/dc.h
index 7d0c189ec80..2a4013b3355 100644
--- a/drivers/video/tegra20/tegra-dc.h
+++ b/drivers/video/tegra/dc.h
@@ -14,9 +14,6 @@
/* arch-tegra/dc exists only because T124 uses it */
#include <asm/arch-tegra/dc.h>
-#define TEGRA_DSI_A "dsi@54300000"
-#define TEGRA_DSI_B "dsi@54400000"
-
struct tegra_dc_plat {
struct udevice *dev; /* Display controller device */
struct dc_ctlr *dc; /* Display controller regmap */
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra/dsi.c
index 6327266dd22..bc308869f4e 100644
--- a/drivers/video/tegra20/tegra-dsi.c
+++ b/drivers/video/tegra/dsi.c
@@ -5,11 +5,13 @@
*/
#include <dm.h>
+#include <clk.h>
#include <log.h>
#include <misc.h>
#include <mipi_display.h>
#include <mipi_dsi.h>
#include <backlight.h>
+#include <video_bridge.h>
#include <panel.h>
#include <reset.h>
#include <linux/delay.h>
@@ -22,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 */
@@ -46,10 +48,13 @@ struct tegra_dsi_priv {
enum tegra_dsi_format format;
- int dsi_clk;
+ struct clk *clk;
+ struct clk *clk_parent;
+
int video_fifo_depth;
int host_fifo_depth;
+ u32 calibration_pads;
u32 version;
/* for ganged-mode support */
@@ -246,6 +251,9 @@ static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host,
value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST |
DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC;
+ if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0)
+ value |= DSI_HOST_CONTROL_HS;
+
/*
* The host FIFO has a maximum of 64 words, so larger transmissions
* need to use the video FIFO.
@@ -515,8 +523,9 @@ static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad)
writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2));
}
-static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv)
+static void tegra_dsi_mipi_calibrate(struct udevice *dev)
{
+ struct tegra_dsi_priv *priv = dev_get_priv(dev);
struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad;
u32 value;
int ret;
@@ -545,15 +554,20 @@ static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv)
DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
writel(value, &pad->pad_ctrl_3);
- ret = misc_write(priv->mipi, 0, NULL, 0);
+ ret = misc_write(priv->mipi, priv->calibration_pads, NULL, 0);
if (ret)
log_debug("%s: MIPI calibration failed %d\n", __func__, ret);
+
+ if (priv->slave)
+ tegra_dsi_mipi_calibrate(priv->slave);
}
-static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout,
+static void tegra_dsi_set_timeout(struct udevice *dev,
unsigned long bclk,
unsigned int vrefresh)
{
+ struct tegra_dsi_priv *priv = dev_get_priv(dev);
+ struct dsi_timeout_reg *rtimeout = &priv->dsi->timeout;
unsigned int timeout;
u32 value;
@@ -569,12 +583,17 @@ static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout,
value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
writel(value, &rtimeout->dsi_to_tally);
+
+ if (priv->slave)
+ tegra_dsi_set_timeout(priv->slave, bclk, vrefresh);
}
-static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming,
+static void tegra_dsi_set_phy_timing(struct udevice *dev,
unsigned long period,
const struct mipi_dphy_timing *dphy_timing)
{
+ struct tegra_dsi_priv *priv = dev_get_priv(dev);
+ struct dsi_timing_reg *ptiming = &priv->dsi->ptiming;
u32 value;
value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 |
@@ -598,6 +617,31 @@ static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming,
DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 |
DSI_TIMING_FIELD(dphy_timing->tago, period, 1);
writel(value, &ptiming->dsi_bta_timing);
+
+ if (priv->slave)
+ tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing);
+}
+
+static u32 tegra_dsi_get_lanes(struct udevice *dev)
+{
+ struct tegra_dsi_priv *priv = dev_get_priv(dev);
+ struct mipi_dsi_device *device = &priv->device;
+
+ if (priv->master) {
+ struct tegra_dsi_priv *mpriv = dev_get_priv(priv->master);
+ struct mipi_dsi_device *mdevice = &mpriv->device;
+
+ return mdevice->lanes + device->lanes;
+ }
+
+ if (priv->slave) {
+ struct tegra_dsi_priv *spriv = dev_get_priv(priv->slave);
+ struct mipi_dsi_device *sdevice = &spriv->device;
+
+ return device->lanes + sdevice->lanes;
+ }
+
+ return device->lanes;
}
static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start,
@@ -611,7 +655,7 @@ static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start,
writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl);
}
-static void tegra_dsi_configure(struct udevice *dev,
+static void tegra_dsi_configure(struct udevice *dev, unsigned int pipe,
unsigned long mode_flags)
{
struct tegra_dsi_priv *priv = dev_get_priv(dev);
@@ -642,7 +686,7 @@ static void tegra_dsi_configure(struct udevice *dev,
value = DSI_CONTROL_CHANNEL(0) |
DSI_CONTROL_FORMAT(priv->format) |
DSI_CONTROL_LANES(device->lanes - 1) |
- DSI_CONTROL_SOURCE(0);
+ DSI_CONTROL_SOURCE(pipe);
writel(value, &misc->dsi_ctrl);
writel(priv->video_fifo_depth, &misc->dsi_max_threshold);
@@ -680,12 +724,19 @@ static void tegra_dsi_configure(struct udevice *dev,
/* horizontal back porch */
hbp = timing->hback_porch.typ * mul / div;
- if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0)
- hbp += hsw;
-
/* horizontal front porch */
hfp = timing->hfront_porch.typ * mul / div;
+ if (priv->master || priv->slave) {
+ hact /= 2;
+ hsw /= 2;
+ hbp = hbp / 2 - 1;
+ hfp /= 2;
+ }
+
+ if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0)
+ hbp += hsw;
+
/* subtract packet overhead */
hsw -= 10;
hbp -= 14;
@@ -695,9 +746,6 @@ static void tegra_dsi_configure(struct udevice *dev,
writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3);
writel(hfp, &len->dsi_pkt_len_4_5);
writel(0x0f0f << 16, &len->dsi_pkt_len_6_7);
-
- /* set SOL delay (for non-burst mode only) */
- writel(8 * mul / div, &misc->dsi_sol_delay);
} else {
if (priv->master || priv->slave) {
/*
@@ -717,32 +765,33 @@ static void tegra_dsi_configure(struct udevice *dev,
value = MIPI_DCS_WRITE_MEMORY_START << 8 |
MIPI_DCS_WRITE_MEMORY_CONTINUE;
writel(value, &len->dsi_dcs_cmds);
+ }
- /* set SOL delay */
- if (priv->master || priv->slave) {
- unsigned long delay, bclk, bclk_ganged;
- unsigned int lanes = device->lanes;
- unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ +
- timing->hback_porch.typ + timing->hsync_len.typ;
-
- /* SOL to valid, valid to FIFO and FIFO write delay */
- delay = 4 + 4 + 2;
- delay = DIV_ROUND_UP(delay * mul, div * lanes);
- /* FIFO read delay */
- delay = delay + 6;
-
- bclk = DIV_ROUND_UP(htotal * mul, div * lanes);
- bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
- value = bclk - bclk_ganged + delay + 20;
- } else {
- /* TODO: revisit for non-ganged mode */
- value = 8 * mul / div;
- }
-
- writel(value, &misc->dsi_sol_delay);
+ /* set SOL delay */
+ if (priv->master || priv->slave) {
+ unsigned long delay, bclk, bclk_ganged;
+ unsigned int lanes = tegra_dsi_get_lanes(dev);
+ unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ +
+ timing->hback_porch.typ + timing->hsync_len.typ;
+
+ /* SOL to valid, valid to FIFO and FIFO write delay */
+ delay = 4 + 4 + 2;
+ delay = DIV_ROUND_UP(delay * mul, div * lanes);
+ /* FIFO read delay */
+ delay = delay + 6;
+
+ bclk = DIV_ROUND_UP(htotal * mul, div * lanes);
+ bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
+ value = bclk - bclk_ganged + delay + 20;
+ } else {
+ /* set SOL delay (for non-burst mode only) */
+ value = 8 * mul / div;
}
+ writel(value, &misc->dsi_sol_delay);
+
if (priv->slave) {
+ tegra_dsi_configure(priv->slave, pipe, mode_flags);
/*
* TODO: Support modes other than symmetrical left-right
* split.
@@ -753,15 +802,31 @@ static void tegra_dsi_configure(struct udevice *dev,
}
}
+static void tegra_dsi_enable(struct udevice *dev)
+{
+ struct tegra_dsi_priv *priv = dev_get_priv(dev);
+ struct dsi_misc_reg *misc = &priv->dsi->misc;
+ u32 value;
+
+ /* enable DSI controller */
+ value = readl(&misc->dsi_pwr_ctrl);
+ value |= DSI_POWER_CONTROL_ENABLE;
+ writel(value, &misc->dsi_pwr_ctrl);
+
+ if (priv->slave)
+ tegra_dsi_enable(priv->slave);
+}
+
static int tegra_dsi_encoder_enable(struct udevice *dev)
{
struct tegra_dsi_priv *priv = dev_get_priv(dev);
+ struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
struct mipi_dsi_device *device = &priv->device;
struct display_timing *timing = &priv->timing;
struct dsi_misc_reg *misc = &priv->dsi->misc;
unsigned int mul, div;
unsigned long bclk, plld, period;
- u32 value;
+ u32 value, lanes;
int ret;
/* If for some reasone DSI is enabled then it needs to
@@ -780,16 +845,17 @@ static int tegra_dsi_encoder_enable(struct udevice *dev)
writel(0, &misc->int_enable);
if (priv->version)
- tegra_dsi_mipi_calibrate(priv);
+ tegra_dsi_mipi_calibrate(dev);
else
tegra_dsi_pad_calibrate(&priv->dsi->pad);
tegra_dsi_get_muldiv(device->format, &mul, &div);
/* compute byte clock */
- bclk = (timing->pixelclock.typ * mul) / (div * device->lanes);
+ lanes = tegra_dsi_get_lanes(dev);
+ bclk = (timing->pixelclock.typ * mul) / (div * lanes);
- tegra_dsi_set_timeout(&priv->dsi->timeout, bclk, 60);
+ tegra_dsi_set_timeout(dev, bclk, 60);
/*
* Compute bit clock and round up to the next MHz.
@@ -813,25 +879,18 @@ static int tegra_dsi_encoder_enable(struct udevice *dev)
* The D-PHY timing fields are expressed in byte-clock cycles, so
* multiply the period by 8.
*/
- tegra_dsi_set_phy_timing(&priv->dsi->ptiming,
- period * 8, &priv->dphy_timing);
+ tegra_dsi_set_phy_timing(dev, period * 8, &priv->dphy_timing);
/* Perform panel HW setup */
ret = panel_enable_backlight(priv->panel);
if (ret)
return ret;
- tegra_dsi_configure(dev, device->mode_flags);
+ tegra_dsi_configure(dev, dc_plat->pipe, device->mode_flags);
tegra_dc_enable_controller(dev);
- /* enable DSI controller */
- value = readl(&misc->dsi_pwr_ctrl);
- value |= DSI_POWER_CONTROL_ENABLE;
- writel(value, &misc->dsi_pwr_ctrl);
-
- if (priv->slave)
- tegra_dsi_encoder_enable(priv->slave);
+ tegra_dsi_enable(dev);
return 0;
}
@@ -859,21 +918,35 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
struct tegra_dsi_priv *priv = dev_get_priv(dev);
struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
struct mipi_dsi_device *device = &priv->device;
- unsigned int mul, div;
+ unsigned int mul, div, lanes;
unsigned long bclk, plld;
- if (!priv->slave) {
+ /* Switch parents of DSI clocks in case of not standard parent */
+ if (priv->clk->id == PERIPH_ID_DSI &&
+ priv->clk_parent->id == CLOCK_ID_DISPLAY2) {
+ /* Change DSIA clock parent to PLLD2 */
+ struct clk_rst_ctlr *clkrst =
+ (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
+ /* DSIA_CLK_SRC */
+ setbits_le32(&clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_base,
+ BIT(25));
+ }
+
+ if (priv->clk->id == PERIPH_ID_DSIB &&
+ priv->clk_parent->id == CLOCK_ID_DISPLAY) {
/* Change DSIB clock parent to match DSIA */
struct clk_rst_ctlr *clkrst =
(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
- clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); /* DSIB_CLK_SRC */
+ /* DSIB_CLK_SRC */
+ clrbits_le32(&clkrst->plld2.pll_base, BIT(25));
}
tegra_dsi_get_muldiv(device->format, &mul, &div);
- bclk = (priv->timing.pixelclock.typ * mul) /
- (div * device->lanes);
+ lanes = tegra_dsi_get_lanes(dev);
+ bclk = (priv->timing.pixelclock.typ * mul) / (div * lanes);
plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC);
@@ -893,16 +966,16 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
switch (clock_get_osc_freq()) {
case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */
- clock_set_rate(CLOCK_ID_DISPLAY, plld, 12, 0, 8);
+ clock_set_rate(priv->clk_parent->id, plld, 12, 0, 8);
break;
case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */
- clock_set_rate(CLOCK_ID_DISPLAY, plld, 26, 0, 8);
+ clock_set_rate(priv->clk_parent->id, plld, 26, 0, 8);
break;
case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */
case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */
- clock_set_rate(CLOCK_ID_DISPLAY, plld, 13, 0, 8);
+ clock_set_rate(priv->clk_parent->id, plld, 13, 0, 8);
break;
case CLOCK_OSC_FREQ_19_2:
@@ -914,11 +987,7 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
break;
}
- priv->dsi_clk = clock_decode_periph_id(dev);
-
- clock_enable(priv->dsi_clk);
- udelay(2);
- reset_set_enable(priv->dsi_clk, 0);
+ clk_enable(priv->clk);
}
static int tegra_dsi_ganged_probe(struct udevice *dev)
@@ -926,7 +995,7 @@ static int tegra_dsi_ganged_probe(struct udevice *dev)
struct tegra_dsi_priv *mpriv = dev_get_priv(dev);
struct udevice *gangster;
- uclass_get_device_by_phandle(UCLASS_PANEL, dev,
+ uclass_get_device_by_phandle(UCLASS_VIDEO_BRIDGE, dev,
"nvidia,ganged-mode", &gangster);
if (gangster) {
/* Ganged mode is set */
@@ -955,6 +1024,20 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
return -EINVAL;
}
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ log_debug("%s: Could not get DSI clock: %ld\n",
+ __func__, PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->clk_parent = devm_clk_get(dev, "parent");
+ if (IS_ERR(priv->clk_parent)) {
+ log_debug("%s: Could not get DSI clock parent: %ld\n",
+ __func__, PTR_ERR(priv->clk_parent));
+ return PTR_ERR(priv->clk_parent);
+ }
+
priv->video_fifo_depth = 1920;
priv->host_fifo_depth = 64;
@@ -973,10 +1056,22 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n",
__func__, ret);
- ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
- "panel", &priv->panel);
+ /* Check all DSI children */
+ device_foreach_child(priv->panel, dev) {
+ if (device_get_uclass_id(priv->panel) == UCLASS_PANEL)
+ break;
+ }
+
+ /* if loop exits without panel device return error */
+ if (device_get_uclass_id(priv->panel) != UCLASS_PANEL) {
+ log_debug("%s: panel not found, ret %d\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ ret = uclass_get_device_by_ofnode(UCLASS_PANEL, dev_ofnode(priv->panel),
+ &priv->panel);
if (ret) {
- printf("%s: Cannot get panel: error %d\n", __func__, ret);
+ log_debug("%s: Cannot get panel: error %d\n", __func__, ret);
return log_ret(ret);
}
@@ -988,6 +1083,14 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
log_debug("%s: cannot get MIPI: error %d\n", __func__, ret);
return ret;
}
+
+ ret = dev_read_u32_index(dev, "nvidia,mipi-calibrate", 1,
+ &priv->calibration_pads);
+ if (ret) {
+ log_debug("%s: cannot get calibration pads: error %d\n",
+ __func__, ret);
+ return ret;
+ }
}
panel_get_display_timing(priv->panel, &priv->timing);
@@ -1019,23 +1122,26 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
return 0;
}
-static const struct panel_ops tegra_dsi_bridge_ops = {
- .enable_backlight = tegra_dsi_encoder_enable,
+static const struct video_bridge_ops tegra_dsi_bridge_ops = {
+ .attach = tegra_dsi_encoder_enable,
.set_backlight = tegra_dsi_bridge_set_panel,
.get_display_timing = tegra_dsi_panel_timings,
};
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 },
{ }
};
U_BOOT_DRIVER(tegra_dsi) = {
.name = "tegra_dsi",
- .id = UCLASS_PANEL,
+ .id = UCLASS_VIDEO_BRIDGE,
.of_match = tegra_dsi_bridge_ids,
.ops = &tegra_dsi_bridge_ops,
+ .bind = dm_scan_fdt_dev,
.probe = tegra_dsi_bridge_probe,
.plat_auto = sizeof(struct tegra_dc_plat),
.priv_auto = sizeof(struct tegra_dsi_priv),
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/tegra/hdmi.c b/drivers/video/tegra/hdmi.c
new file mode 100644
index 00000000000..bfb48b25187
--- /dev/null
+++ b/drivers/video/tegra/hdmi.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <edid.h>
+#include <i2c.h>
+#include <log.h>
+#include <misc.h>
+#include <panel.h>
+#include <reset.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/time.h>
+#include <power/regulator.h>
+#include <video_bridge.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+
+#include "dc.h"
+#include "hdmi.h"
+
+#define DDCCI_ENTRY_ADDR 0x37
+#define DDCCI_SOURSE_ADDR 0x51
+#define DDCCI_COMMAND_WRITE 0x03
+#define DDCCI_CTRL_BRIGHTNESS 0x10
+
+#define HDMI_EDID_I2C_ADDR 0x50
+#define HDMI_REKEY_DEFAULT 56
+
+static const char * const hdmi_supplies[] = {
+ "hdmi-supply", "pll-supply", "vdd-supply"
+};
+
+struct tmds_config {
+ unsigned int pclk;
+ u32 pll0;
+ u32 pll1;
+ u32 pe_current;
+ u32 drive_current;
+ u32 peak_current;
+};
+
+struct tegra_hdmi_config {
+ const struct tmds_config *tmds;
+ unsigned int num_tmds;
+ unsigned int max_pclk;
+
+ /* to be filled */
+};
+
+struct tegra_hdmi_priv {
+ struct hdmi_ctlr *hdmi_regmap;
+
+ struct udevice *supplies[ARRAY_SIZE(hdmi_supplies)];
+ struct udevice *hdmi_ddc;
+
+ struct gpio_desc hpd; /* hotplug detection gpio */
+ struct display_timing timing;
+
+ struct clk *clk;
+ struct clk *clk_parent;
+
+ int panel_bits_per_colourp;
+ const struct tegra_hdmi_config *config;
+};
+
+/* 1280x720p 60hz: EIA/CEA-861-B Format 4 */
+static struct display_timing default_720p_timing = {
+ .pixelclock.typ = 74250000,
+ .hactive.typ = 1280,
+ .hfront_porch.typ = 110,
+ .hback_porch.typ = 220,
+ .hsync_len.typ = 40,
+ .vactive.typ = 720,
+ .vfront_porch.typ = 5,
+ .vback_porch.typ = 20,
+ .vsync_len.typ = 5,
+ .flags = DISPLAY_FLAGS_HSYNC_HIGH |
+ DISPLAY_FLAGS_VSYNC_HIGH,
+};
+
+static const struct tmds_config tegra20_tmds_config[] = {
+ { /* slow pixel clock modes */
+ .pclk = 27000000,
+ .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
+ SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) |
+ SOR_PLL_TX_REG_LOAD(3),
+ .pll1 = SOR_PLL_TMDS_TERM_ENABLE,
+ .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) |
+ PE_CURRENT1(PE_CURRENT_0_0_mA) |
+ PE_CURRENT2(PE_CURRENT_0_0_mA) |
+ PE_CURRENT3(PE_CURRENT_0_0_mA),
+ .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) |
+ DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) |
+ DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) |
+ DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA),
+ },
+ { /* high pixel clock modes */
+ .pclk = UINT_MAX,
+ .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
+ SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) |
+ SOR_PLL_TX_REG_LOAD(3),
+ .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN,
+ .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) |
+ PE_CURRENT1(PE_CURRENT_6_0_mA) |
+ PE_CURRENT2(PE_CURRENT_6_0_mA) |
+ PE_CURRENT3(PE_CURRENT_6_0_mA),
+ .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) |
+ DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) |
+ DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) |
+ DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA),
+ },
+};
+
+static const struct tmds_config tegra30_tmds_config[] = {
+ { /* 480p modes */
+ .pclk = 27000000,
+ .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
+ SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) |
+ SOR_PLL_TX_REG_LOAD(0),
+ .pll1 = SOR_PLL_TMDS_TERM_ENABLE,
+ .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) |
+ PE_CURRENT1(PE_CURRENT_0_0_mA) |
+ PE_CURRENT2(PE_CURRENT_0_0_mA) |
+ PE_CURRENT3(PE_CURRENT_0_0_mA),
+ .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA),
+ }, { /* 720p modes */
+ .pclk = 74250000,
+ .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
+ SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) |
+ SOR_PLL_TX_REG_LOAD(0),
+ .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN,
+ .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) |
+ PE_CURRENT1(PE_CURRENT_5_0_mA) |
+ PE_CURRENT2(PE_CURRENT_5_0_mA) |
+ PE_CURRENT3(PE_CURRENT_5_0_mA),
+ .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA),
+ }, { /* 1080p modes */
+ .pclk = UINT_MAX,
+ .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
+ SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(3) |
+ SOR_PLL_TX_REG_LOAD(0),
+ .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN,
+ .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) |
+ PE_CURRENT1(PE_CURRENT_5_0_mA) |
+ PE_CURRENT2(PE_CURRENT_5_0_mA) |
+ PE_CURRENT3(PE_CURRENT_5_0_mA),
+ .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) |
+ DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA),
+ },
+};
+
+static void tegra_dc_enable_controller(struct udevice *dev)
+{
+ struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
+ struct dc_ctlr *dc = dc_plat->dc;
+ u32 value;
+
+ value = readl(&dc->disp.disp_win_opt);
+ value |= HDMI_ENABLE;
+ writel(value, &dc->disp.disp_win_opt);
+
+ writel(GENERAL_UPDATE, &dc->cmd.state_ctrl);
+ writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl);
+}
+
+static void tegra_hdmi_setup_tmds(struct tegra_hdmi_priv *priv,
+ const struct tmds_config *tmds)
+{
+ struct hdmi_ctlr *hdmi = priv->hdmi_regmap;
+ u32 value;
+
+ writel(tmds->pll0, &hdmi->nv_pdisp_sor_pll0);
+ writel(tmds->pll1, &hdmi->nv_pdisp_sor_pll1);
+ writel(tmds->pe_current, &hdmi->nv_pdisp_pe_current);
+
+ writel(tmds->drive_current, &hdmi->nv_pdisp_sor_lane_drive_current);
+
+ value = readl(&hdmi->nv_pdisp_sor_lane_drive_current);
+ value |= BIT(31);
+ writel(value, &hdmi->nv_pdisp_sor_lane_drive_current);
+}
+
+static int tegra_hdmi_encoder_enable(struct udevice *dev)
+{
+ struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
+ struct tegra_hdmi_priv *priv = dev_get_priv(dev);
+ struct dc_ctlr *dc = dc_plat->dc;
+ struct display_timing *dt = &priv->timing;
+ struct hdmi_ctlr *hdmi = priv->hdmi_regmap;
+ unsigned long rate, div82;
+ unsigned int pulse_start, rekey;
+ int retries = 1000;
+ u32 value;
+ int i;
+
+ /* power up sequence */
+ value = readl(&hdmi->nv_pdisp_sor_pll0);
+ value &= ~SOR_PLL_PDBG;
+ writel(value, &hdmi->nv_pdisp_sor_pll0);
+
+ udelay(20);
+
+ value = readl(&hdmi->nv_pdisp_sor_pll0);
+ value &= ~SOR_PLL_PWR;
+ writel(value, &hdmi->nv_pdisp_sor_pll0);
+
+ writel(VSYNC_H_POSITION(1), &dc->disp.disp_timing_opt);
+ writel(DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE_888,
+ &dc->disp.disp_color_ctrl);
+
+ /* video_preamble uses h_pulse2 */
+ pulse_start = 1 + dt->hsync_len.typ + dt->hback_porch.typ - 10;
+
+ writel(H_PULSE2_ENABLE, &dc->disp.disp_signal_opt0);
+
+ value = PULSE_MODE_NORMAL | PULSE_POLARITY_HIGH |
+ PULSE_QUAL_VACTIVE | PULSE_LAST_END_A;
+ writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_ctrl);
+
+ value = PULSE_START(pulse_start) | PULSE_END(pulse_start + 8);
+ writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_pos[H_PULSE0_POSITION_A]);
+
+ value = VSYNC_WINDOW_END(0x210) | VSYNC_WINDOW_START(0x200) |
+ VSYNC_WINDOW_ENABLE;
+ writel(value, &hdmi->nv_pdisp_hdmi_vsync_window);
+
+ if (dc_plat->pipe)
+ value = HDMI_SRC_DISPLAYB;
+ else
+ value = HDMI_SRC_DISPLAYA;
+
+ if (dt->hactive.typ == 720 && (dt->vactive.typ == 480 ||
+ dt->vactive.typ == 576))
+ writel(value | ARM_VIDEO_RANGE_FULL,
+ &hdmi->nv_pdisp_input_control);
+ else
+ writel(value | ARM_VIDEO_RANGE_LIMITED,
+ &hdmi->nv_pdisp_input_control);
+
+ rate = clock_get_periph_rate(priv->clk->id, priv->clk_parent->id);
+ div82 = rate / USEC_PER_SEC * 4;
+ value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82);
+ writel(value, &hdmi->nv_pdisp_sor_refclk);
+
+ rekey = HDMI_REKEY_DEFAULT;
+ value = HDMI_CTRL_REKEY(rekey);
+ value |= HDMI_CTRL_MAX_AC_PACKET((dt->hsync_len.typ + dt->hback_porch.typ +
+ dt->hfront_porch.typ - rekey - 18) / 32);
+ writel(value, &hdmi->nv_pdisp_hdmi_ctrl);
+
+ /* TMDS CONFIG */
+ for (i = 0; i < priv->config->num_tmds; i++) {
+ if (dt->pixelclock.typ <= priv->config->tmds[i].pclk) {
+ tegra_hdmi_setup_tmds(priv, &priv->config->tmds[i]);
+ break;
+ }
+ }
+
+ writel(SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | SOR_SEQ_PD_PC(8) |
+ SOR_SEQ_PD_PC_ALT(8), &hdmi->nv_pdisp_sor_seq_ctl);
+
+ value = SOR_SEQ_INST_WAIT_TIME(1) | SOR_SEQ_INST_WAIT_UNITS_VSYNC |
+ SOR_SEQ_INST_HALT | SOR_SEQ_INST_PIN_A_LOW |
+ SOR_SEQ_INST_PIN_B_LOW | SOR_SEQ_INST_DRIVE_PWM_OUT_LO;
+
+ writel(value, &hdmi->nv_pdisp_sor_seq_inst0);
+ writel(value, &hdmi->nv_pdisp_sor_seq_inst8);
+
+ value = readl(&hdmi->nv_pdisp_sor_cstm);
+
+ value &= ~SOR_CSTM_ROTCLK(~0);
+ value |= SOR_CSTM_ROTCLK(2);
+ value |= SOR_CSTM_PLLDIV;
+ value &= ~SOR_CSTM_LVDS_ENABLE;
+ value &= ~SOR_CSTM_MODE_MASK;
+ value |= SOR_CSTM_MODE_TMDS;
+
+ writel(value, &hdmi->nv_pdisp_sor_cstm);
+
+ /* start SOR */
+ writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL |
+ SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_TRIGGER,
+ &hdmi->nv_pdisp_sor_pwr);
+ writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL |
+ SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_DONE,
+ &hdmi->nv_pdisp_sor_pwr);
+
+ do {
+ if (--retries < 0)
+ return -ETIME;
+ value = readl(&hdmi->nv_pdisp_sor_pwr);
+ } while (value & SOR_PWR_SETTING_NEW_PENDING);
+
+ value = SOR_STATE_ASY_CRCMODE_COMPLETE |
+ SOR_STATE_ASY_OWNER_HEAD0 |
+ SOR_STATE_ASY_SUBOWNER_BOTH |
+ SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A |
+ SOR_STATE_ASY_DEPOL_POS;
+
+ /* setup sync polarities */
+ if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ value |= SOR_STATE_ASY_HSYNCPOL_POS;
+
+ if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW)
+ value |= SOR_STATE_ASY_HSYNCPOL_NEG;
+
+ if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ value |= SOR_STATE_ASY_VSYNCPOL_POS;
+
+ if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW)
+ value |= SOR_STATE_ASY_VSYNCPOL_NEG;
+
+ writel(value, &hdmi->nv_pdisp_sor_state2);
+
+ value = SOR_STATE_ASY_HEAD_OPMODE_AWAKE | SOR_STATE_ASY_ORMODE_NORMAL;
+ writel(value, &hdmi->nv_pdisp_sor_state1);
+
+ writel(0, &hdmi->nv_pdisp_sor_state0);
+ writel(SOR_STATE_UPDATE, &hdmi->nv_pdisp_sor_state0);
+ writel(value | SOR_STATE_ATTACHED,
+ &hdmi->nv_pdisp_sor_state1);
+ writel(0, &hdmi->nv_pdisp_sor_state0);
+
+ tegra_dc_enable_controller(dev);
+
+ return 0;
+}
+
+/* DDC/CI backlight control */
+static int tegra_hdmi_set_connector(struct udevice *dev, int percent)
+{
+ struct tegra_hdmi_priv *priv = dev_get_priv(dev);
+ struct udevice *ddc_entry;
+ struct i2c_msg msg[1];
+ u8 checksum = DDCCI_ENTRY_ADDR << 1;
+ int i, ret;
+
+ ret = dm_i2c_probe(priv->hdmi_ddc, DDCCI_ENTRY_ADDR, 0, &ddc_entry);
+ if (ret) {
+ log_debug("%s: cannot probe DDC/CI entry: error %d\n",
+ __func__, ret);
+ return 0;
+ }
+
+ /*
+ * payload[1] is length: hithest bit OR last 4 bits indicate
+ * the number of following bytes (excluding checksum)
+ */
+ u8 payload[7] = { DDCCI_SOURSE_ADDR, BIT(7) | (sizeof(payload) - 3),
+ DDCCI_COMMAND_WRITE, DDCCI_CTRL_BRIGHTNESS,
+ (u8)(percent & 0xff), (u8)(percent & 0xff), 0 };
+
+ /* DDC/CI checksum is a simple XOR of all preceding bytes */
+ for (i = 0; i < (sizeof(payload) - 1); i++)
+ checksum ^= payload[i];
+
+ payload[6] = checksum;
+
+ msg->addr = DDCCI_ENTRY_ADDR;
+ msg->flags = 0;
+ msg->len = sizeof(payload);
+ msg->buf = payload;
+
+ dm_i2c_xfer(ddc_entry, msg, 1);
+
+ return 0;
+}
+
+static int tegra_hdmi_timings(struct udevice *dev,
+ struct display_timing *timing)
+{
+ struct tegra_hdmi_priv *priv = dev_get_priv(dev);
+
+ memcpy(timing, &priv->timing, sizeof(*timing));
+
+ return 0;
+}
+
+static void tegra_hdmi_init_clocks(struct udevice *dev)
+{
+ struct tegra_hdmi_priv *priv = dev_get_priv(dev);
+ u32 n = priv->timing.pixelclock.typ * 2 / USEC_PER_SEC;
+
+ switch (clock_get_osc_freq()) {
+ case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
+ case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */
+ clock_set_rate(priv->clk_parent->id, n, 12, 0, 8);
+ break;
+
+ case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */
+ clock_set_rate(priv->clk_parent->id, n, 26, 0, 8);
+ break;
+
+ case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */
+ case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */
+ clock_set_rate(priv->clk_parent->id, n, 13, 0, 8);
+ break;
+
+ case CLOCK_OSC_FREQ_19_2:
+ case CLOCK_OSC_FREQ_38_4:
+ default:
+ /*
+ * These are not supported.
+ */
+ break;
+ }
+
+ clock_start_periph_pll(priv->clk->id, priv->clk_parent->id,
+ priv->timing.pixelclock.typ);
+}
+
+static bool tegra_hdmi_mode_valid(void *hdmi_priv, const struct display_timing *timing)
+{
+ struct tegra_hdmi_priv *priv = hdmi_priv;
+
+ if (timing->pixelclock.typ > priv->config->max_pclk)
+ return false;
+
+ return true;
+}
+
+static int tegra_hdmi_decode_edid(struct udevice *dev)
+{
+ struct tegra_hdmi_priv *priv = dev_get_priv(dev);
+ struct udevice *hdmi_edid;
+ uchar edid_buf[EDID_SIZE] = { 0 };
+ int i, ret;
+
+ /* Poll for 1 sec in case EDID is not ready right after hpd */
+ for (i = 0; i < 10; i++) {
+ ret = dm_i2c_probe(priv->hdmi_ddc, HDMI_EDID_I2C_ADDR, 0,
+ &hdmi_edid);
+ if (!ret)
+ break;
+
+ mdelay(100);
+ }
+ if (ret) {
+ log_debug("%s: cannot probe EDID: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = dm_i2c_read(hdmi_edid, 0, edid_buf, sizeof(edid_buf));
+ if (ret) {
+ log_debug("%s: cannot dump EDID buffer: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = edid_get_timing_validate(edid_buf, sizeof(edid_buf), &priv->timing,
+ &priv->panel_bits_per_colourp,
+ tegra_hdmi_mode_valid, priv);
+ if (ret) {
+ log_debug("%s: cannot decode EDID info: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra_hdmi_wait_hpd(struct tegra_hdmi_priv *priv)
+{
+ int i;
+
+ /* Poll 1 second for HPD signal */
+ for (i = 0; i < 10; i++) {
+ if (dm_gpio_get_value(&priv->hpd))
+ return 0;
+
+ mdelay(100);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int tegra_hdmi_probe(struct udevice *dev)
+{
+ struct tegra_hdmi_priv *priv = dev_get_priv(dev);
+ struct reset_ctl reset_ctl;
+ int i, ret;
+
+ priv->hdmi_regmap = (struct hdmi_ctlr *)dev_read_addr_ptr(dev);
+ if (!priv->hdmi_regmap) {
+ log_debug("%s: no display controller address\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->config = (struct tegra_hdmi_config *)dev_get_driver_data(dev);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ log_debug("%s: Could not get HDMI clock: %ld\n",
+ __func__, PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+
+ priv->clk_parent = devm_clk_get(dev, "parent");
+ if (IS_ERR(priv->clk_parent)) {
+ log_debug("%s: Could not get HDMI clock parent: %ld\n",
+ __func__, PTR_ERR(priv->clk_parent));
+ return PTR_ERR(priv->clk_parent);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_supplies); i++) {
+ ret = device_get_supply_regulator(dev, hdmi_supplies[i],
+ &priv->supplies[i]);
+ if (ret) {
+ log_debug("%s: cannot get %s %d\n", __func__,
+ hdmi_supplies[i], ret);
+ if (ret != -ENOENT)
+ return log_ret(ret);
+ }
+
+ ret = regulator_set_enable_if_allowed(priv->supplies[i], true);
+ if (ret && ret != -ENOSYS) {
+ log_debug("%s: cannot enable %s: error %d\n",
+ __func__, hdmi_supplies[i], ret);
+ return ret;
+ }
+ }
+
+ ret = reset_get_by_name(dev, "hdmi", &reset_ctl);
+ if (ret) {
+ log_debug("%s: reset_get_by_name() failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_I2C, dev,
+ "nvidia,ddc-i2c-bus",
+ &priv->hdmi_ddc);
+ if (ret) {
+ log_debug("%s: cannot get hdmi ddc i2c bus: error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = gpio_request_by_name(dev, "nvidia,hpd-gpio", 0,
+ &priv->hpd, GPIOD_IS_IN);
+ if (ret) {
+ log_debug("%s: Could not decode hpd-gpios (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* wait for connector */
+ ret = tegra_hdmi_wait_hpd(priv);
+ if (ret) {
+ /* HPD failed, use default timings */
+ memcpy(&priv->timing, &default_720p_timing,
+ sizeof(default_720p_timing));
+ } else {
+ ret = tegra_hdmi_decode_edid(dev);
+ if (ret)
+ memcpy(&priv->timing, &default_720p_timing,
+ sizeof(default_720p_timing));
+ }
+
+ reset_assert(&reset_ctl);
+ tegra_hdmi_init_clocks(dev);
+
+ mdelay(2);
+ reset_deassert(&reset_ctl);
+
+ return 0;
+}
+
+static const struct tegra_hdmi_config tegra20_hdmi_config = {
+ .tmds = tegra20_tmds_config,
+ .num_tmds = ARRAY_SIZE(tegra20_tmds_config),
+ .max_pclk = 148500000, /* 1080p */
+};
+
+static const struct tegra_hdmi_config tegra30_hdmi_config = {
+ .tmds = tegra30_tmds_config,
+ .num_tmds = ARRAY_SIZE(tegra30_tmds_config),
+ .max_pclk = 148500000, /* 1080p */
+};
+
+static const struct video_bridge_ops tegra_hdmi_ops = {
+ .attach = tegra_hdmi_encoder_enable,
+ .set_backlight = tegra_hdmi_set_connector,
+ .get_display_timing = tegra_hdmi_timings,
+};
+
+static const struct udevice_id tegra_hdmi_ids[] = {
+ {
+ .compatible = "nvidia,tegra20-hdmi",
+ .data = (ulong)&tegra20_hdmi_config
+ }, {
+ .compatible = "nvidia,tegra30-hdmi",
+ .data = (ulong)&tegra30_hdmi_config
+ }, {
+ /* sentinel */
+ }
+};
+
+U_BOOT_DRIVER(tegra_hdmi) = {
+ .name = "tegra_hdmi",
+ .id = UCLASS_VIDEO_BRIDGE,
+ .of_match = tegra_hdmi_ids,
+ .ops = &tegra_hdmi_ops,
+ .probe = tegra_hdmi_probe,
+ .plat_auto = sizeof(struct tegra_dc_plat),
+ .priv_auto = sizeof(struct tegra_hdmi_priv),
+};
diff --git a/drivers/video/tegra/hdmi.h b/drivers/video/tegra/hdmi.h
new file mode 100644
index 00000000000..d17655973e3
--- /dev/null
+++ b/drivers/video/tegra/hdmi.h
@@ -0,0 +1,648 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2010
+ * NVIDIA Corporation <www.nvidia.com>
+ */
+
+#ifndef _TEGRA_HDMI_H
+#define _TEGRA_HDMI_H
+
+#ifndef __ASSEMBLY__
+#include <linux/bitops.h>
+#endif
+
+/* Register definitions for the Tegra high-definition multimedia interface */
+
+/* High-Definition Multimedia Interface (HDMI_) regs */
+struct hdmi_ctlr {
+ /* Address 0x000 ~ 0x0d2 */
+ uint ctxsw; /* _CTXSW */ /* 0x00 */
+
+ uint nv_pdisp_sor_state0; /* _NV_PDISP_SOR_STATE0 */
+ uint nv_pdisp_sor_state1; /* _NV_PDISP_SOR_STATE1 */
+ uint nv_pdisp_sor_state2; /* _NV_PDISP_SOR_STATE2 */
+
+ uint nv_pdisp_rg_hdcp_an_msb; /* _NV_PDISP_RG_HDCP_AN_MSB */
+ uint nv_pdisp_rg_hdcp_an_lsb; /* _NV_PDISP_RG_HDCP_AN_LSB */
+ uint nv_pdisp_rg_hdcp_cn_msb; /* _NV_PDISP_RG_HDCP_CN_MSB */
+ uint nv_pdisp_rg_hdcp_cn_lsb; /* _NV_PDISP_RG_HDCP_CN_LSB */
+ uint nv_pdisp_rg_hdcp_aksv_msb; /* _NV_PDISP_RG_HDCP_AKSV_MSB */
+ uint nv_pdisp_rg_hdcp_aksv_lsb; /* _NV_PDISP_RG_HDCP_AKSV_LSB */
+ uint nv_pdisp_rg_hdcp_bksv_msb; /* _NV_PDISP_RG_HDCP_BKSV_MSB */
+ uint nv_pdisp_rg_hdcp_bksv_lsb; /* _NV_PDISP_RG_HDCP_BKSV_LSB */
+ uint nv_pdisp_rg_hdcp_cksv_msb; /* _NV_PDISP_RG_HDCP_CKSV_MSB */
+ uint nv_pdisp_rg_hdcp_cksv_lsb; /* _NV_PDISP_RG_HDCP_CKSV_LSB */
+ uint nv_pdisp_rg_hdcp_dksv_msb; /* _NV_PDISP_RG_HDCP_DKSV_MSB */
+ uint nv_pdisp_rg_hdcp_dksv_lsb; /* _NV_PDISP_RG_HDCP_DKSV_LSB */
+ uint nv_pdisp_rg_hdcp_ctrl; /* _NV_PDISP_RG_HDCP_CTRL */ /* 0x10 */
+ uint nv_pdisp_rg_hdcp_cmode; /* _NV_PDISP_RG_HDCP_CMODE */
+ uint nv_pdisp_rg_hdcp_mprime_msb; /* _NV_PDISP_RG_HDCP_MPRIME_MSB */
+ uint nv_pdisp_rg_hdcp_mprime_lsb; /* _NV_PDISP_RG_HDCP_MPRIME_LSB */
+ uint nv_pdisp_rg_hdcp_sprime_msb; /* _NV_PDISP_RG_HDCP_SPRIME_MSB */
+ uint nv_pdisp_rg_hdcp_sprime_lsb2; /* _NV_PDISP_RG_HDCP_SPRIME_LSB2 */
+ uint nv_pdisp_rg_hdcp_sprime_lsb1; /* _NV_PDISP_RG_HDCP_SPRIME_LSB1 */
+ uint nv_pdisp_rg_hdcp_ri; /* _NV_PDISP_RG_HDCP_RI */
+ uint nv_pdisp_rg_hdcp_cs_msb; /* _NV_PDISP_RG_HDCP_CS_MSB */
+ uint nv_pdisp_rg_hdcp_cs_lsb; /* _NV_PDISP_RG_HDCP_CS_LSB */
+
+ uint nv_pdisp_hdmi_audio_emu0; /* _NV_PDISP_HDMI_AUDIO_EMU0 */
+ uint nv_pdisp_hdmi_audio_emu_rdata0; /* _NV_PDISP_HDMI_AUDIO_EMU_RDATA0 */
+ uint nv_pdisp_hdmi_audio_emu1; /* _NV_PDISP_HDMI_AUDIO_EMU1 */
+ uint nv_pdisp_hdmi_audio_emu2; /* _NV_PDISP_HDMI_AUDIO_EMU2 */
+ uint nv_pdisp_hdmi_audio_infoframe_ctrl; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL */
+ uint nv_pdisp_hdmi_audio_infoframe_status; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS */
+ uint nv_pdisp_hdmi_audio_infoframe_header; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER */ /* 0x20 */
+ uint nv_pdisp_hdmi_audio_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW */
+ uint nv_pdisp_hdmi_audio_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH */
+
+ uint nv_pdisp_hdmi_avi_infoframe_ctrl; /* _NV_PDISP_HDMI_AVI_INFOFRAME_CTRL */
+ uint nv_pdisp_hdmi_avi_infoframe_status; /* _NV_PDISP_HDMI_AVI_INFOFRAME_STATUS */
+ uint nv_pdisp_hdmi_avi_infoframe_header; /* _NV_PDISP_HDMI_AVI_INFOFRAME_HEADER */
+ uint nv_pdisp_hdmi_avi_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW */
+ uint nv_pdisp_hdmi_avi_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH */
+ uint nv_pdisp_hdmi_avi_infoframe_subpack1_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW */
+ uint nv_pdisp_hdmi_avi_infoframe_subpack1_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH */
+
+ uint nv_pdisp_hdmi_generic_ctrl; /* _NV_PDISP_HDMI_GENERIC_CTRL */
+ uint nv_pdisp_hdmi_generic_status; /* _NV_PDISP_HDMI_GENERIC_STATUS */
+ uint nv_pdisp_hdmi_generic_header; /* _NV_PDISP_HDMI_GENERIC_HEADER */
+ uint nv_pdisp_hdmi_generic_subpack0_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW */
+ uint nv_pdisp_hdmi_generic_subpack0_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH */
+ uint nv_pdisp_hdmi_generic_subpack1_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW */
+ uint nv_pdisp_hdmi_generic_subpack1_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH */
+ uint nv_pdisp_hdmi_generic_subpack2_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW */
+ uint nv_pdisp_hdmi_generic_subpack2_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH */
+ uint nv_pdisp_hdmi_generic_subpack3_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW */
+ uint nv_pdisp_hdmi_generic_subpack3_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH */
+
+ uint nv_pdisp_hdmi_acr_ctrl; /* _NV_PDISP_HDMI_ACR_CTRL */
+ uint nv_pdisp_hdmi_acr_0320_subpack_low; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_0320_subpack_high; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH */
+ uint nv_pdisp_hdmi_acr_0441_subpack_low; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_0441_subpack_high; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH */
+ uint nv_pdisp_hdmi_acr_0882_subpack_low; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_0882_subpack_high; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH */
+ uint nv_pdisp_hdmi_acr_1764_subpack_low; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_1764_subpack_high; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH */
+ uint nv_pdisp_hdmi_acr_0480_subpack_low; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_0480_subpack_high; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH */
+ uint nv_pdisp_hdmi_acr_0960_subpack_low; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_0960_subpack_high; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH */
+ uint nv_pdisp_hdmi_acr_1920_subpack_low; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW */
+ uint nv_pdisp_hdmi_acr_1920_subpack_high; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH */
+
+ uint nv_pdisp_hdmi_ctrl; /* _NV_PDISP_HDMI_CTRL */
+ uint nv_pdisp_hdmi_vsync_keepout; /* _NV_PDISP_HDMI_VSYNC_KEEPOUT */
+ uint nv_pdisp_hdmi_vsync_window; /* _NV_PDISP_HDMI_VSYNC_WINDOW */
+ uint nv_pdisp_hdmi_gcp_ctrl; /* _NV_PDISP_HDMI_GCP_CTRL */
+ uint nv_pdisp_hdmi_gcp_status; /* _NV_PDISP_HDMI_GCP_STATUS */
+ uint nv_pdisp_hdmi_gcp_subpack; /* _NV_PDISP_HDMI_GCP_SUBPACK */
+ uint nv_pdisp_hdmi_channel_status1; /* _NV_PDISP_HDMI_CHANNEL_STATUS1 */
+ uint nv_pdisp_hdmi_channel_status2; /* _NV_PDISP_HDMI_CHANNEL_STATUS2 */
+ uint nv_pdisp_hdmi_emu0; /* _NV_PDISP_HDMI_EMU0 */
+ uint nv_pdisp_hdmi_emu1; /* _NV_PDISP_HDMI_EMU1 */
+ uint nv_pdisp_hdmi_emu1_rdata; /* _NV_PDISP_HDMI_EMU1_RDATA */
+ uint nv_pdisp_hdmi_spare; /* _NV_PDISP_HDMI_SPARE */
+ uint nv_pdisp_hdmi_spdif_chn_status1; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS1 */
+ uint nv_pdisp_hdmi_spdif_chn_status2; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS2 */
+
+ uint nv_pdisp_hdcprif_rom_ctrl; /* _NV_PDISP_HDCPRIF_ROM_CTRL */
+
+ uint unused;
+
+ uint nv_pdisp_sor_cap; /* _NV_PDISP_SOR_CAP */
+ uint nv_pdisp_sor_pwr; /* _NV_PDISP_SOR_PWR */
+ uint nv_pdisp_sor_test; /* _NV_PDISP_SOR_TEST */
+ uint nv_pdisp_sor_pll0; /* _NV_PDISP_SOR_PLL0 */
+ uint nv_pdisp_sor_pll1; /* _NV_PDISP_SOR_PLL1 */
+ uint nv_pdisp_sor_pll2; /* _NV_PDISP_SOR_PLL2 */
+ uint nv_pdisp_sor_cstm; /* _NV_PDISP_SOR_CSTM */
+ uint nv_pdisp_sor_lvds; /* _NV_PDISP_SOR_LVDS */
+ uint nv_pdisp_sor_crca; /* _NV_PDISP_SOR_CRCA */
+ uint nv_pdisp_sor_crcb; /* _NV_PDISP_SOR_CRCB */
+ uint nv_pdisp_sor_blank; /* _NV_PDISP_SOR_BLANK */
+
+ uint nv_pdisp_sor_seq_ctl; /* _NV_PDISP_SOR_SEQ_CTL */
+ uint nv_pdisp_sor_seq_inst0; /* _NV_PDISP_SOR_SEQ_INST0 */
+ uint nv_pdisp_sor_seq_inst1; /* _NV_PDISP_SOR_SEQ_INST1 */
+ uint nv_pdisp_sor_seq_inst2; /* _NV_PDISP_SOR_SEQ_INST2 */
+ uint nv_pdisp_sor_seq_inst3; /* _NV_PDISP_SOR_SEQ_INST3 */
+ uint nv_pdisp_sor_seq_inst4; /* _NV_PDISP_SOR_SEQ_INST4 */
+ uint nv_pdisp_sor_seq_inst5; /* _NV_PDISP_SOR_SEQ_INST5 */
+ uint nv_pdisp_sor_seq_inst6; /* _NV_PDISP_SOR_SEQ_INST6 */
+ uint nv_pdisp_sor_seq_inst7; /* _NV_PDISP_SOR_SEQ_INST7 */
+ uint nv_pdisp_sor_seq_inst8; /* _NV_PDISP_SOR_SEQ_INST8 */
+ uint nv_pdisp_sor_seq_inst9; /* _NV_PDISP_SOR_SEQ_INST9 */
+ uint nv_pdisp_sor_seq_insta; /* _NV_PDISP_SOR_SEQ_INSTA */
+ uint nv_pdisp_sor_seq_instb; /* _NV_PDISP_SOR_SEQ_INSTB */
+ uint nv_pdisp_sor_seq_instc; /* _NV_PDISP_SOR_SEQ_INSTC */
+ uint nv_pdisp_sor_seq_instd; /* _NV_PDISP_SOR_SEQ_INSTD */
+ uint nv_pdisp_sor_seq_inste; /* _NV_PDISP_SOR_SEQ_INSTE */
+ uint nv_pdisp_sor_seq_instf; /* _NV_PDISP_SOR_SEQ_INSTF */
+
+ uint unused1[2];
+
+ uint nv_pdisp_sor_vcrca0; /* _NV_PDISP_SOR_VCRCA0 */
+ uint nv_pdisp_sor_vcrca1; /* _NV_PDISP_SOR_VCRCA1 */
+ uint nv_pdisp_sor_ccrca0; /* _NV_PDISP_SOR_CCRCA0 */
+ uint nv_pdisp_sor_ccrca1; /* _NV_PDISP_SOR_CCRCA1 */
+
+ uint nv_pdisp_sor_edataa0; /* _NV_PDISP_SOR_EDATAA0 */
+ uint nv_pdisp_sor_edataa1; /* _NV_PDISP_SOR_EDATAA1 */
+
+ uint nv_pdisp_sor_counta0; /* _NV_PDISP_SOR_COUNTA0 */
+ uint nv_pdisp_sor_counta1; /* _NV_PDISP_SOR_COUNTA1 */
+
+ uint nv_pdisp_sor_debuga0; /* _NV_PDISP_SOR_DEBUGA0 */
+ uint nv_pdisp_sor_debuga1; /* _NV_PDISP_SOR_DEBUGA1 */
+
+ uint nv_pdisp_sor_trig; /* _NV_PDISP_SOR_TRIG */
+ uint nv_pdisp_sor_mscheck; /* _NV_PDISP_SOR_MSCHECK */
+ uint nv_pdisp_sor_lane_drive_current; /* _NV_PDISP_SOR_LANE_DRIVE_CURRENT */
+
+ uint nv_pdisp_audio_debug0; /* _NV_PDISP_AUDIO_DEBUG0 0x7f */
+ uint nv_pdisp_audio_debug1; /* _NV_PDISP_AUDIO_DEBUG1 0x80 */
+ uint nv_pdisp_audio_debug2; /* _NV_PDISP_AUDIO_DEBUG2 0x81 */
+
+ uint nv_pdisp_audio_fs1; /* _NV_PDISP_AUDIO_FS1 0x82 */
+ uint nv_pdisp_audio_fs2; /* _NV_PDISP_AUDIO_FS2 */
+ uint nv_pdisp_audio_fs3; /* _NV_PDISP_AUDIO_FS3 */
+ uint nv_pdisp_audio_fs4; /* _NV_PDISP_AUDIO_FS4 */
+ uint nv_pdisp_audio_fs5; /* _NV_PDISP_AUDIO_FS5 */
+ uint nv_pdisp_audio_fs6; /* _NV_PDISP_AUDIO_FS6 */
+ uint nv_pdisp_audio_fs7; /* _NV_PDISP_AUDIO_FS7 0x88 */
+
+ uint nv_pdisp_audio_pulse_width; /* _NV_PDISP_AUDIO_PULSE_WIDTH */
+ uint nv_pdisp_audio_threshold; /* _NV_PDISP_AUDIO_THRESHOLD */
+ uint nv_pdisp_audio_cntrl0; /* _NV_PDISP_AUDIO_CNTRL0 */
+ uint nv_pdisp_audio_n; /* _NV_PDISP_AUDIO_N */
+ uint nv_pdisp_audio_nval[7]; /* _NV_PDISP_AUDIO_NVAL */
+
+ uint nv_pdisp_hdcprif_rom_timing; /* _NV_PDISP_HDCPRIF_ROM_TIMING */
+ uint nv_pdisp_sor_refclk; /* _NV_PDISP_SOR_REFCLK */
+ uint nv_pdisp_crc_control; /* _NV_PDISP_CRC_CONTROL */
+ uint nv_pdisp_input_control; /* _NV_PDISP_INPUT_CONTROL */
+ uint nv_pdisp_scratch; /* _NV_PDISP_SCRATCH */
+ uint nv_pdisp_pe_current; /* _NV_PDISP_PE_CURRENT */
+
+ uint nv_pdisp_key_ctrl; /* _NV_PDISP_KEY_CTRL */
+ uint nv_pdisp_key_debug0; /* _NV_PDISP_KEY_DEBUG0 */
+ uint nv_pdisp_key_debug1; /* _NV_PDISP_KEY_DEBUG1 */
+ uint nv_pdisp_key_debug2; /* _NV_PDISP_KEY_DEBUG2 */
+ uint nv_pdisp_key_hdcp_key_0; /* _NV_PDISP_KEY_HDCP_KEY_0 */
+ uint nv_pdisp_key_hdcp_key_1; /* _NV_PDISP_KEY_HDCP_KEY_1 */
+ uint nv_pdisp_key_hdcp_key_2; /* _NV_PDISP_KEY_HDCP_KEY_2 */
+ uint nv_pdisp_key_hdcp_key_3; /* _NV_PDISP_KEY_HDCP_KEY_3 */
+ uint nv_pdisp_key_hdcp_key_trig; /* _NV_PDISP_KEY_HDCP_KEY_3 */
+ uint nv_pdisp_key_skey_index; /* _NV_PDISP_KEY_HDCP_KEY_3 */ /* 0xa3 */
+
+ uint unused2[8];
+
+ uint nv_pdisp_sor_audio_cntrl0; /* _NV_PDISP_SOR_AUDIO_CNTRL0 */ /* 0xac */
+ uint nv_pdisp_sor_audio_debug; /* _NV_PDISP_SOR_AUDIO_DEBUG */
+ uint nv_pdisp_sor_audio_spare0; /* _NV_PDISP_SOR_AUDIO_SPARE0 */
+ uint nv_pdisp_sor_audio_nval[7]; /* _NV_PDISP_SOR_AUDIO_NVAL 0xaf ~ 0xb5 */
+ uint nv_pdisp_sor_audio_hda_scratch[4]; /* _NV_PDISP_SOR_AUDIO_HDA_SCRATCH 0xb6 ~ 0xb9 */
+ uint nv_pdisp_sor_audio_hda_codec_scratch[2]; /* _NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH 0xba ~ 0xbb */
+
+ uint nv_pdisp_sor_audio_hda_eld_bufwr; /* _NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR */
+ uint nv_pdisp_sor_audio_hda_presense; /* _NV_PDISP_SOR_AUDIO_HDA_PRESENSE */
+ uint nv_pdisp_sor_audio_hda_cp; /* _NV_PDISP_SOR_AUDIO_HDA_CP */
+ uint nv_pdisp_sor_audio_aval[8]; /* _NV_PDISP_SOR_AUDIO_AVAL */
+ uint nv_pdisp_sor_audio_gen_ctrl; /* _NV_PDISP_SOR_AUDIO_GEN_CTRL */
+
+ uint unused3[4];
+
+ uint nv_pdisp_int_status; /* _NV_PDISP_INT_STATUS */
+ uint nv_pdisp_int_mask; /* _NV_PDISP_INT_MASK */
+ uint nv_pdisp_int_enable; /* _NV_PDISP_INT_ENABLE */
+
+ uint unused4[2];
+
+ uint nv_pdisp_sor_io_peak_current; /* _NV_PDISP_SOR_IO_PEAK_CURRENT */
+ uint nv_pdisp_sor_pad_ctls0; /* _NV_PDISP_SOR_PAD_CTLS0 */
+};
+
+/* HDMI_NV_PDISP_SOR_STATE0 0x01 */
+#define SOR_STATE_UPDATE BIT(0)
+
+/* HDMI_NV_PDISP_SOR_STATE1 0x02 */
+#define SOR_STATE_ASY_HEAD_OPMODE_AWAKE BIT(1)
+#define SOR_STATE_ASY_ORMODE_NORMAL BIT(2)
+#define SOR_STATE_ATTACHED BIT(3)
+
+/* HDMI_NV_PDISP_SOR_STATE2 0x03 */
+#define SOR_STATE_ASY_OWNER_NONE (0 << 0)
+#define SOR_STATE_ASY_OWNER_HEAD0 (1 << 0)
+#define SOR_STATE_ASY_SUBOWNER_NONE (0 << 4)
+#define SOR_STATE_ASY_SUBOWNER_SUBHEAD0 (1 << 4)
+#define SOR_STATE_ASY_SUBOWNER_SUBHEAD1 (2 << 4)
+#define SOR_STATE_ASY_SUBOWNER_BOTH (3 << 4)
+#define SOR_STATE_ASY_CRCMODE_ACTIVE (0 << 6)
+#define SOR_STATE_ASY_CRCMODE_COMPLETE (1 << 6)
+#define SOR_STATE_ASY_CRCMODE_NON_ACTIVE (2 << 6)
+#define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (1 << 8)
+#define SOR_STATE_ASY_PROTOCOL_CUSTOM (15 << 8)
+#define SOR_STATE_ASY_HSYNCPOL_POS (0 << 12)
+#define SOR_STATE_ASY_HSYNCPOL_NEG (1 << 12)
+#define SOR_STATE_ASY_VSYNCPOL_POS (0 << 13)
+#define SOR_STATE_ASY_VSYNCPOL_NEG (1 << 13)
+#define SOR_STATE_ASY_DEPOL_POS (0 << 14)
+#define SOR_STATE_ASY_DEPOL_NEG (1 << 14)
+
+#define INFOFRAME_CTRL_ENABLE BIT(0)
+#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0)
+#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8)
+#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16)
+
+/* HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x2a */
+#define GENERIC_CTRL_ENABLE BIT(0)
+#define GENERIC_CTRL_OTHER BIT(4)
+#define GENERIC_CTRL_SINGLE BIT(8)
+#define GENERIC_CTRL_HBLANK BIT(12)
+#define GENERIC_CTRL_AUDIO BIT(16)
+
+/* HDMI_NV_PDISP_HDMI_ACR_* */
+#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8)
+#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0)
+#define ACR_ENABLE BIT(31)
+
+/* HDMI_NV_PDISP_HDMI_CTRL 0x44 */
+#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0)
+#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16)
+#define HDMI_CTRL_ENABLE BIT(30)
+
+/* HDMI_NV_PDISP_HDMI_VSYNC_* */
+#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0)
+#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16)
+#define VSYNC_WINDOW_ENABLE BIT(31)
+
+/* HDMI_NV_PDISP_HDMI_SPARE 0x4f */
+#define SPARE_HW_CTS BIT(0)
+#define SPARE_FORCE_SW_CTS BIT(1)
+#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16)
+
+/* HDMI_NV_PDISP_SOR_PWR 0x55 */
+#define SOR_PWR_NORMAL_STATE_PD (0 << 0)
+#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
+#define SOR_PWR_NORMAL_START_NORMAL (0 << 1)
+#define SOR_PWR_NORMAL_START_ALT (1 << 1)
+#define SOR_PWR_SAFE_STATE_PD (0 << 16)
+#define SOR_PWR_SAFE_STATE_PU (1 << 16)
+#define SOR_PWR_SETTING_NEW_DONE (0 << 31)
+#define SOR_PWR_SETTING_NEW_PENDING (1 << 31)
+#define SOR_PWR_SETTING_NEW_TRIGGER (1 << 31)
+
+/* HDMI_NV_PDISP_SOR_PLL0 0x57 */
+#define SOR_PLL_PWR BIT(0)
+#define SOR_PLL_PDBG BIT(1)
+#define SOR_PLL_VCAPD BIT(2)
+#define SOR_PLL_PDPORT BIT(3)
+#define SOR_PLL_RESISTORSEL BIT(4)
+#define SOR_PLL_PULLDOWN BIT(5)
+#define SOR_PLL_VCOCAP(x) (((x) & 0xf) << 8)
+#define SOR_PLL_BG_V17_S(x) (((x) & 0xf) << 12)
+#define SOR_PLL_FILTER(x) (((x) & 0xf) << 16)
+#define SOR_PLL_ICHPMP(x) (((x) & 0xf) << 24)
+#define SOR_PLL_TX_REG_LOAD(x) (((x) & 0xf) << 28)
+
+/* HDMI_NV_PDISP_SOR_PLL1 0x58 */
+#define SOR_PLL_TMDS_TERM_ENABLE BIT(8)
+#define SOR_PLL_TMDS_TERMADJ(x) (((x) & 0xf) << 9)
+#define SOR_PLL_LOADADJ(x) (((x) & 0xf) << 20)
+#define SOR_PLL_PE_EN BIT(28)
+#define SOR_PLL_HALF_FULL_PE BIT(29)
+#define SOR_PLL_S_D_PIN_PE BIT(30)
+
+/* HDMI_NV_PDISP_SOR_CSTM 0x5a */
+#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24)
+#define SOR_CSTM_PLLDIV BIT(21)
+#define SOR_CSTM_LVDS_ENABLE BIT(16)
+#define SOR_CSTM_MODE_LVDS (0 << 12)
+#define SOR_CSTM_MODE_TMDS (1 << 12)
+#define SOR_CSTM_MODE_MASK (3 << 12)
+
+/* HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f */
+#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0)
+#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4)
+#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8)
+#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12)
+#define SOR_SEQ_PC(x) (((x) & 0xf) << 16)
+#define SOR_SEQ_STATUS BIT(28)
+#define SOR_SEQ_SWITCH BIT(30)
+
+/* HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x60 + (x)) */
+#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0)
+#define SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12)
+#define SOR_SEQ_INST_HALT (1 << 15)
+#define SOR_SEQ_INST_PIN_A_LOW (0 << 21)
+#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21)
+#define SOR_SEQ_INST_PIN_B_LOW (0 << 22)
+#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22)
+#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23)
+
+/* HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x7e */
+#define DRIVE_CURRENT_LANE0(x) (((x) & 0x3f) << 0)
+#define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8)
+#define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16)
+#define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24)
+#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) << 0)
+#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) << 8)
+#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16)
+#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24)
+
+/* Drive current list */
+enum {
+ DRIVE_CURRENT_1_500_mA,
+ DRIVE_CURRENT_1_875_mA,
+ DRIVE_CURRENT_2_250_mA,
+ DRIVE_CURRENT_2_625_mA,
+ DRIVE_CURRENT_3_000_mA,
+ DRIVE_CURRENT_3_375_mA,
+ DRIVE_CURRENT_3_750_mA,
+ DRIVE_CURRENT_4_125_mA,
+ DRIVE_CURRENT_4_500_mA,
+ DRIVE_CURRENT_4_875_mA,
+ DRIVE_CURRENT_5_250_mA,
+ DRIVE_CURRENT_5_625_mA,
+ DRIVE_CURRENT_6_000_mA,
+ DRIVE_CURRENT_6_375_mA,
+ DRIVE_CURRENT_6_750_mA,
+ DRIVE_CURRENT_7_125_mA,
+ DRIVE_CURRENT_7_500_mA,
+ DRIVE_CURRENT_7_875_mA,
+ DRIVE_CURRENT_8_250_mA,
+ DRIVE_CURRENT_8_625_mA,
+ DRIVE_CURRENT_9_000_mA,
+ DRIVE_CURRENT_9_375_mA,
+ DRIVE_CURRENT_9_750_mA,
+ DRIVE_CURRENT_10_125_mA,
+ DRIVE_CURRENT_10_500_mA,
+ DRIVE_CURRENT_10_875_mA,
+ DRIVE_CURRENT_11_250_mA,
+ DRIVE_CURRENT_11_625_mA,
+ DRIVE_CURRENT_12_000_mA,
+ DRIVE_CURRENT_12_375_mA,
+ DRIVE_CURRENT_12_750_mA,
+ DRIVE_CURRENT_13_125_mA,
+ DRIVE_CURRENT_13_500_mA,
+ DRIVE_CURRENT_13_875_mA,
+ DRIVE_CURRENT_14_250_mA,
+ DRIVE_CURRENT_14_625_mA,
+ DRIVE_CURRENT_15_000_mA,
+ DRIVE_CURRENT_15_375_mA,
+ DRIVE_CURRENT_15_750_mA,
+ DRIVE_CURRENT_16_125_mA,
+ DRIVE_CURRENT_16_500_mA,
+ DRIVE_CURRENT_16_875_mA,
+ DRIVE_CURRENT_17_250_mA,
+ DRIVE_CURRENT_17_625_mA,
+ DRIVE_CURRENT_18_000_mA,
+ DRIVE_CURRENT_18_375_mA,
+ DRIVE_CURRENT_18_750_mA,
+ DRIVE_CURRENT_19_125_mA,
+ DRIVE_CURRENT_19_500_mA,
+ DRIVE_CURRENT_19_875_mA,
+ DRIVE_CURRENT_20_250_mA,
+ DRIVE_CURRENT_20_625_mA,
+ DRIVE_CURRENT_21_000_mA,
+ DRIVE_CURRENT_21_375_mA,
+ DRIVE_CURRENT_21_750_mA,
+ DRIVE_CURRENT_22_125_mA,
+ DRIVE_CURRENT_22_500_mA,
+ DRIVE_CURRENT_22_875_mA,
+ DRIVE_CURRENT_23_250_mA,
+ DRIVE_CURRENT_23_625_mA,
+ DRIVE_CURRENT_24_000_mA,
+ DRIVE_CURRENT_24_375_mA,
+ DRIVE_CURRENT_24_750_mA,
+};
+
+/* Drive current list for T114 */
+enum {
+ DRIVE_CURRENT_0_000_mA_T114,
+ DRIVE_CURRENT_0_400_mA_T114,
+ DRIVE_CURRENT_0_800_mA_T114,
+ DRIVE_CURRENT_1_200_mA_T114,
+ DRIVE_CURRENT_1_600_mA_T114,
+ DRIVE_CURRENT_2_000_mA_T114,
+ DRIVE_CURRENT_2_400_mA_T114,
+ DRIVE_CURRENT_2_800_mA_T114,
+ DRIVE_CURRENT_3_200_mA_T114,
+ DRIVE_CURRENT_3_600_mA_T114,
+ DRIVE_CURRENT_4_000_mA_T114,
+ DRIVE_CURRENT_4_400_mA_T114,
+ DRIVE_CURRENT_4_800_mA_T114,
+ DRIVE_CURRENT_5_200_mA_T114,
+ DRIVE_CURRENT_5_600_mA_T114,
+ DRIVE_CURRENT_6_000_mA_T114,
+ DRIVE_CURRENT_6_400_mA_T114,
+ DRIVE_CURRENT_6_800_mA_T114,
+ DRIVE_CURRENT_7_200_mA_T114,
+ DRIVE_CURRENT_7_600_mA_T114,
+ DRIVE_CURRENT_8_000_mA_T114,
+ DRIVE_CURRENT_8_400_mA_T114,
+ DRIVE_CURRENT_8_800_mA_T114,
+ DRIVE_CURRENT_9_200_mA_T114,
+ DRIVE_CURRENT_9_600_mA_T114,
+ DRIVE_CURRENT_10_000_mA_T114,
+ DRIVE_CURRENT_10_400_mA_T114,
+ DRIVE_CURRENT_10_800_mA_T114,
+ DRIVE_CURRENT_11_200_mA_T114,
+ DRIVE_CURRENT_11_600_mA_T114,
+ DRIVE_CURRENT_12_000_mA_T114,
+ DRIVE_CURRENT_12_400_mA_T114,
+ DRIVE_CURRENT_12_800_mA_T114,
+ DRIVE_CURRENT_13_200_mA_T114,
+ DRIVE_CURRENT_13_600_mA_T114,
+ DRIVE_CURRENT_14_000_mA_T114,
+ DRIVE_CURRENT_14_400_mA_T114,
+ DRIVE_CURRENT_14_800_mA_T114,
+ DRIVE_CURRENT_15_200_mA_T114,
+ DRIVE_CURRENT_15_600_mA_T114,
+ DRIVE_CURRENT_16_000_mA_T114,
+ DRIVE_CURRENT_16_400_mA_T114,
+ DRIVE_CURRENT_16_800_mA_T114,
+ DRIVE_CURRENT_17_200_mA_T114,
+ DRIVE_CURRENT_17_600_mA_T114,
+ DRIVE_CURRENT_18_000_mA_T114,
+ DRIVE_CURRENT_18_400_mA_T114,
+ DRIVE_CURRENT_18_800_mA_T114,
+ DRIVE_CURRENT_19_200_mA_T114,
+ DRIVE_CURRENT_19_600_mA_T114,
+ DRIVE_CURRENT_20_000_mA_T114,
+ DRIVE_CURRENT_20_400_mA_T114,
+ DRIVE_CURRENT_20_800_mA_T114,
+ DRIVE_CURRENT_21_200_mA_T114,
+ DRIVE_CURRENT_21_600_mA_T114,
+ DRIVE_CURRENT_22_000_mA_T114,
+ DRIVE_CURRENT_22_400_mA_T114,
+ DRIVE_CURRENT_22_800_mA_T114,
+ DRIVE_CURRENT_23_200_mA_T114,
+ DRIVE_CURRENT_23_600_mA_T114,
+ DRIVE_CURRENT_24_000_mA_T114,
+ DRIVE_CURRENT_24_400_mA_T114,
+ DRIVE_CURRENT_24_800_mA_T114,
+ DRIVE_CURRENT_25_200_mA_T114,
+ DRIVE_CURRENT_25_400_mA_T114,
+ DRIVE_CURRENT_25_800_mA_T114,
+ DRIVE_CURRENT_26_200_mA_T114,
+ DRIVE_CURRENT_26_600_mA_T114,
+ DRIVE_CURRENT_27_000_mA_T114,
+ DRIVE_CURRENT_27_400_mA_T114,
+ DRIVE_CURRENT_27_800_mA_T114,
+ DRIVE_CURRENT_28_200_mA_T114,
+};
+
+/* HDMI_NV_PDISP_AUDIO_FS */
+#define AUDIO_FS_LOW(x) (((x) & 0xfff) << 0)
+#define AUDIO_FS_HIGH(x) (((x) & 0xfff) << 16)
+
+/* HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b */
+#define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0)
+#define AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20)
+#define AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20)
+#define AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20)
+#define AUDIO_CNTRL0_FRAMES_PER_BLOCK(x) (((x) & 0xff) << 24)
+
+/* HDMI_NV_PDISP_AUDIO_N 0x8c */
+#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0)
+#define AUDIO_N_RESETF (1 << 20)
+#define AUDIO_N_GENERATE_NORMAL (0 << 24)
+#define AUDIO_N_GENERATE_ALTERNATE (1 << 24)
+
+/* HDMI_NV_PDISP_SOR_REFCLK 0x95 */
+#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8)
+#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6)
+
+/* HDMI_NV_PDISP_INPUT_CONTROL 0x97 */
+#define HDMI_SRC_DISPLAYA (0 << 0)
+#define HDMI_SRC_DISPLAYB (1 << 0)
+#define ARM_VIDEO_RANGE_FULL (0 << 1)
+#define ARM_VIDEO_RANGE_LIMITED (1 << 1)
+
+/* HDMI_NV_PDISP_PE_CURRENT 0x99 */
+#define PE_CURRENT0(x) (((x) & 0xf) << 0)
+#define PE_CURRENT1(x) (((x) & 0xf) << 8)
+#define PE_CURRENT2(x) (((x) & 0xf) << 16)
+#define PE_CURRENT3(x) (((x) & 0xf) << 24)
+
+enum {
+ PE_CURRENT_0_0_mA,
+ PE_CURRENT_0_5_mA,
+ PE_CURRENT_1_0_mA,
+ PE_CURRENT_1_5_mA,
+ PE_CURRENT_2_0_mA,
+ PE_CURRENT_2_5_mA,
+ PE_CURRENT_3_0_mA,
+ PE_CURRENT_3_5_mA,
+ PE_CURRENT_4_0_mA,
+ PE_CURRENT_4_5_mA,
+ PE_CURRENT_5_0_mA,
+ PE_CURRENT_5_5_mA,
+ PE_CURRENT_6_0_mA,
+ PE_CURRENT_6_5_mA,
+ PE_CURRENT_7_0_mA,
+ PE_CURRENT_7_5_mA,
+};
+
+enum {
+ PE_CURRENT_0_mA_T114,
+ PE_CURRENT_1_mA_T114,
+ PE_CURRENT_2_mA_T114,
+ PE_CURRENT_3_mA_T114,
+ PE_CURRENT_4_mA_T114,
+ PE_CURRENT_5_mA_T114,
+ PE_CURRENT_6_mA_T114,
+ PE_CURRENT_7_mA_T114,
+ PE_CURRENT_8_mA_T114,
+ PE_CURRENT_9_mA_T114,
+ PE_CURRENT_10_mA_T114,
+ PE_CURRENT_11_mA_T114,
+ PE_CURRENT_12_mA_T114,
+ PE_CURRENT_13_mA_T114,
+ PE_CURRENT_14_mA_T114,
+ PE_CURRENT_15_mA_T114,
+};
+
+/* HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0xac */
+#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20)
+#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20)
+#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20)
+#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29)
+
+/* HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0xae */
+#define SOR_AUDIO_SPARE0_HBR_ENABLE BIT(27)
+
+/* HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0xba */
+#define SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID BIT(30)
+#define SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK 0xffff
+
+/* HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0xbd */
+#define SOR_AUDIO_HDA_PRESENSE_VALID BIT(1)
+#define SOR_AUDIO_HDA_PRESENSE_PRESENT BIT(0)
+
+/* HDMI_NV_PDISP_INT_STATUS 0xcc */
+#define INT_SCRATCH BIT(3)
+#define INT_CP_REQUEST BIT(2)
+#define INT_CODEC_SCRATCH1 BIT(1)
+#define INT_CODEC_SCRATCH0 BIT(0)
+
+/* HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1 */
+#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0)
+#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8)
+#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16)
+#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24)
+
+enum {
+ PEAK_CURRENT_0_000_mA,
+ PEAK_CURRENT_0_200_mA,
+ PEAK_CURRENT_0_400_mA,
+ PEAK_CURRENT_0_600_mA,
+ PEAK_CURRENT_0_800_mA,
+ PEAK_CURRENT_1_000_mA,
+ PEAK_CURRENT_1_200_mA,
+ PEAK_CURRENT_1_400_mA,
+ PEAK_CURRENT_1_600_mA,
+ PEAK_CURRENT_1_800_mA,
+ PEAK_CURRENT_2_000_mA,
+ PEAK_CURRENT_2_200_mA,
+ PEAK_CURRENT_2_400_mA,
+ PEAK_CURRENT_2_600_mA,
+ PEAK_CURRENT_2_800_mA,
+ PEAK_CURRENT_3_000_mA,
+ PEAK_CURRENT_3_200_mA,
+ PEAK_CURRENT_3_400_mA,
+ PEAK_CURRENT_3_600_mA,
+ PEAK_CURRENT_3_800_mA,
+ PEAK_CURRENT_4_000_mA,
+ PEAK_CURRENT_4_200_mA,
+ PEAK_CURRENT_4_400_mA,
+ PEAK_CURRENT_4_600_mA,
+ PEAK_CURRENT_4_800_mA,
+ PEAK_CURRENT_5_000_mA,
+ PEAK_CURRENT_5_200_mA,
+ PEAK_CURRENT_5_400_mA,
+ PEAK_CURRENT_5_600_mA,
+ PEAK_CURRENT_5_800_mA,
+ PEAK_CURRENT_6_000_mA,
+ PEAK_CURRENT_6_200_mA,
+ PEAK_CURRENT_6_400_mA,
+ PEAK_CURRENT_6_600_mA,
+ PEAK_CURRENT_6_800_mA,
+ PEAK_CURRENT_7_000_mA,
+ PEAK_CURRENT_7_200_mA,
+ PEAK_CURRENT_7_400_mA,
+ PEAK_CURRENT_7_600_mA,
+ PEAK_CURRENT_7_800_mA,
+ PEAK_CURRENT_8_000_mA,
+ PEAK_CURRENT_8_200_mA,
+ PEAK_CURRENT_8_400_mA,
+ PEAK_CURRENT_8_600_mA,
+ PEAK_CURRENT_8_800_mA,
+ PEAK_CURRENT_9_000_mA,
+ PEAK_CURRENT_9_200_mA,
+ PEAK_CURRENT_9_400_mA,
+};
+
+#endif /* _TEGRA_HDMI_H */
diff --git a/drivers/video/tegra/host1x.c b/drivers/video/tegra/host1x.c
new file mode 100644
index 00000000000..58ab871a3b4
--- /dev/null
+++ b/drivers/video/tegra/host1x.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <clk.h>
+#include <log.h>
+#include <reset.h>
+#include <linux/delay.h>
+
+#include <asm/arch/clock.h>
+#include <asm/arch-tegra/clk_rst.h>
+
+struct tegra_host1x_info {
+ u32 clk_parent;
+ u32 rate;
+};
+
+static int tegra_host1x_probe(struct udevice *dev)
+{
+ struct clk *clk;
+ struct reset_ctl reset_ctl;
+ const struct tegra_host1x_info *info;
+ int ret;
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ log_debug("%s: cannot get HOST1X clock: %ld\n",
+ __func__, PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ ret = reset_get_by_name(dev, "host1x", &reset_ctl);
+ if (ret) {
+ log_debug("%s: cannot get HOST1X reset: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ info = (struct tegra_host1x_info *)dev_get_driver_data(dev);
+
+ reset_assert(&reset_ctl);
+ clock_start_periph_pll(clk->id, info->clk_parent, info->rate);
+
+ mdelay(2);
+ reset_deassert(&reset_ctl);
+
+ return 0;
+}
+
+static const struct tegra_host1x_info tegra20_host1x_info = {
+ .clk_parent = CLOCK_ID_CGENERAL,
+ .rate = 150000000, /* 150 MHz */
+};
+
+static const struct tegra_host1x_info tegra114_host1x_info = {
+ .clk_parent = CLOCK_ID_PERIPH,
+ .rate = 136000000, /* 136 MHz */
+};
+
+static const struct udevice_id tegra_host1x_ids[] = {
+ {
+ .compatible = "nvidia,tegra20-host1x",
+ .data = (ulong)&tegra20_host1x_info
+ }, {
+ .compatible = "nvidia,tegra30-host1x",
+ .data = (ulong)&tegra20_host1x_info
+ }, {
+ .compatible = "nvidia,tegra114-host1x",
+ .data = (ulong)&tegra114_host1x_info
+ }, {
+ .compatible = "nvidia,tegra124-host1x",
+ .data = (ulong)&tegra114_host1x_info
+ }, {
+ /* sentinel */
+ }
+};
+
+U_BOOT_DRIVER(tegra_host1x) = {
+ .name = "tegra_host1x",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = tegra_host1x_ids,
+ .probe = tegra_host1x_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
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 2df3c1a9942..a4f4343d008 100644
--- a/drivers/video/tegra20/tegra-mipi.c
+++ b/drivers/video/tegra/mipi.c
@@ -10,9 +10,10 @@
#include <linux/delay.h>
#include <linux/iopoll.h>
+#include <asm/arch/clock.h>
#include <asm/io.h>
-/* MIPI control registers 0x00 ~ 0x60 */
+/* MIPI control registers 0x00 ~ 0x74 */
struct mipi_ctlr {
uint mipi_cal_ctrl;
uint mipi_cal_autocal_ctrl;
@@ -38,8 +39,17 @@ struct mipi_ctlr {
uint mipi_cal_bias_pad_cfg0;
uint mipi_cal_bias_pad_cfg1;
uint mipi_cal_bias_pad_cfg2;
+
+ uint mipi_cal_dsia_config_2;
+ uint mipi_cal_dsib_config_2;
+ uint mipi_cal_cilc_config_2;
+ uint mipi_cal_cild_config_2;
+ uint mipi_cal_csie_config_2;
};
+#define MIPI_DSIA_PADS 0x60
+#define MIPI_DSIB_PADS 0x180
+
#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4)
@@ -64,26 +74,25 @@ struct mipi_ctlr {
#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1)
+#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0)
+
struct tegra_mipi_priv {
struct mipi_ctlr *mipi;
struct clk *mipi_cal;
+ u32 version;
};
-static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf,
- int size)
+enum {
+ T114,
+ T124,
+};
+
+static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv,
+ int calibration_pads)
{
- struct tegra_mipi_priv *priv = dev_get_priv(dev);
u32 value;
- value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) |
- MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0);
- writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1);
-
- value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2);
- value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
- value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
- writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2);
-
value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) |
MIPI_CAL_TERMOS(0x5);
@@ -99,6 +108,95 @@ static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf
value = readl(&priv->mipi->mipi_cal_config_dsid);
value &= ~(MIPI_CAL_SEL(0x1));
writel(value, &priv->mipi->mipi_cal_config_dsid);
+}
+
+static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv,
+ int calibration_pads)
+{
+ u32 value;
+
+ /* Calibrate DSI-A */
+ if (calibration_pads == MIPI_DSIA_PADS) {
+ printf("Calibrating DSI-A pads\n");
+
+ value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
+ MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) |
+ MIPI_CAL_TERMOS(0x0);
+ writel(value, &priv->mipi->mipi_cal_config_dsia);
+ writel(value, &priv->mipi->mipi_cal_config_dsib);
+
+ value = MIPI_CAL_SEL(0x1) |
+ MIPI_CAL_HSCLKPDOSDSI(0x1) |
+ MIPI_CAL_HSCLKPUOSDSI(0x2);
+ writel(value, &priv->mipi->mipi_cal_dsia_config_2);
+ writel(value, &priv->mipi->mipi_cal_dsib_config_2);
+
+ /* Deselect PAD C */
+ value = readl(&priv->mipi->mipi_cal_cilc_config_2);
+ value &= ~(MIPI_CAL_SEL(0x1));
+ writel(value, &priv->mipi->mipi_cal_cilc_config_2);
+
+ /* Deselect PAD D */
+ value = readl(&priv->mipi->mipi_cal_cild_config_2);
+ value &= ~(MIPI_CAL_SEL(0x1));
+ writel(value, &priv->mipi->mipi_cal_cild_config_2);
+ }
+
+ /* Calibrate DSI-B */
+ if (calibration_pads == MIPI_DSIB_PADS) {
+ printf("Calibrating DSI-B pads\n");
+
+ value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
+ MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) |
+ MIPI_CAL_TERMOS(0x0);
+ writel(value, &priv->mipi->mipi_cal_config_csic);
+ writel(value, &priv->mipi->mipi_cal_config_csid);
+
+ value = MIPI_CAL_SEL(0x1) |
+ MIPI_CAL_HSCLKPDOSDSI(0x1) |
+ MIPI_CAL_HSCLKPUOSDSI(0x2);
+ writel(value, &priv->mipi->mipi_cal_cilc_config_2);
+ writel(value, &priv->mipi->mipi_cal_cild_config_2);
+
+ /* Deselect PAD A */
+ value = readl(&priv->mipi->mipi_cal_dsia_config_2);
+ value &= ~(MIPI_CAL_SEL(0x1));
+ writel(value, &priv->mipi->mipi_cal_dsia_config_2);
+
+ /* Deselect PAD B */
+ value = readl(&priv->mipi->mipi_cal_dsib_config_2);
+ value &= ~(MIPI_CAL_SEL(0x1));
+ writel(value, &priv->mipi->mipi_cal_dsib_config_2);
+ }
+}
+
+static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf,
+ int size)
+{
+ struct tegra_mipi_priv *priv = dev_get_priv(dev);
+ u32 value;
+
+ value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) |
+ MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0);
+ writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1);
+
+ value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2);
+ value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
+ value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
+ writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2);
+
+ switch (priv->version) {
+ case T114:
+ tegra114_mipi_pads_cal(priv, offset);
+ break;
+
+ case T124:
+ tegra124_mipi_pads_cal(priv, offset);
+ break;
+
+ default:
+ return -EINVAL;
+ }
value = readl(&priv->mipi->mipi_cal_ctrl);
value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
@@ -134,6 +232,11 @@ static int tegra_mipi_enable(struct udevice *dev, bool val)
struct tegra_mipi_priv *priv = dev_get_priv(dev);
u32 value;
+ reset_set_enable(priv->mipi_cal->id, 1);
+ mdelay(100);
+ reset_set_enable(priv->mipi_cal->id, 0);
+ mdelay(1);
+
clk_enable(priv->mipi_cal);
value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0);
@@ -157,6 +260,8 @@ static int tegra_mipi_probe(struct udevice *dev)
{
struct tegra_mipi_priv *priv = dev_get_priv(dev);
+ priv->version = dev_get_driver_data(dev);
+
priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev);
if (!priv->mipi) {
log_debug("%s: no MIPI controller address\n", __func__);
@@ -174,7 +279,8 @@ static int tegra_mipi_probe(struct udevice *dev)
}
static const struct udevice_id tegra_mipi_ids[] = {
- { .compatible = "nvidia,tegra114-mipi" },
+ { .compatible = "nvidia,tegra114-mipi", .data = T114 },
+ { .compatible = "nvidia,tegra124-mipi", .data = T124 },
{ }
};
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 f5c4843e119..00000000000
--- a/drivers/video/tegra20/Kconfig
+++ /dev/null
@@ -1,24 +0,0 @@
-config VIDEO_TEGRA20
- bool "Enable Display Controller support on Tegra20 and Tegra 30"
- depends on OF_CONTROL
- 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 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 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 a75aea2a875..00000000000
--- a/drivers/video/tegra20/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0+
-
-obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o
-obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o
-obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o
diff --git a/drivers/video/ti/tilcdc.c b/drivers/video/ti/tilcdc.c
index 493e2f18cd2..340c79299ba 100644
--- a/drivers/video/ti/tilcdc.c
+++ b/drivers/video/ti/tilcdc.c
@@ -234,7 +234,7 @@ static int tilcdc_probe(struct udevice *dev)
return -EINVAL;
}
- err = uclass_get_device_by_name(UCLASS_CLK, "lcd_gclk@534", &clk_dev);
+ err = uclass_get_device_by_name(UCLASS_CLK, "clock-lcd-gclk@534", &clk_dev);
if (err) {
dev_err(dev, "failed to get lcd_gclk device\n");
return err;
@@ -252,7 +252,7 @@ static int tilcdc_probe(struct udevice *dev)
return rate;
}
- err = uclass_get_device_by_name(UCLASS_CLK, "dpll_disp_m2_ck@4a4",
+ err = uclass_get_device_by_name(UCLASS_CLK, "clock-dpll-disp-m2@4a4",
&clk_dev);
if (err) {
dev_err(dev, "failed to get dpll_disp_m2 clock device\n");
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
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index ff4f2199585..c684c994b61 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -245,6 +245,7 @@ int video_fill(struct udevice *dev, u32 colour)
*ppix++ = colour;
break;
}
+ fallthrough;
case VIDEO_BPP32:
if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
u32 *ppix = priv->fb;
@@ -254,6 +255,7 @@ int video_fill(struct udevice *dev, u32 colour)
*ppix++ = colour;
break;
}
+ fallthrough;
default:
memset(priv->fb, colour, priv->fb_size);
break;
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b39b2546e5c..1bb67f50352 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -95,6 +95,15 @@ config WDT_APPLE
The watchdog will perform a full SoC reset resulting in a
reboot of the entire system.
+config WDT_ADI
+ bool "Analog Devices watchdog timer support"
+ select WDT
+ select SPL_WDT if SPL
+ depends on ARCH_SC5XX
+ help
+ Enable this to support Watchdog Timer on ADI SC57X, SC58X, SC59X,
+ and SC59X_64 processors
+
config WDT_ARMADA_37XX
bool "Marvell Armada 37xx watchdog timer support"
depends on WDT && ARMADA_3700
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 9b6b1a8e8ad..e6bd4c587af 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o
+obj-$(CONFIG_WDT_ADI) += adi_wdt.o
diff --git a/drivers/watchdog/adi_wdt.c b/drivers/watchdog/adi_wdt.c
new file mode 100644
index 00000000000..6f5b3d5d042
--- /dev/null
+++ b/drivers/watchdog/adi_wdt.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Converted to driver model by Nathan Barrett-Morrison
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * adi_wtd.c - driver for ADI on-chip watchdog
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <wdt.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+
+#define WDOG_CTL 0x0
+#define WDOG_CNT 0x4
+#define WDOG_STAT 0x8
+
+#define RCU_CTL 0x0
+#define RCU_STAT 0x4
+
+#define SEC_GCTL 0x0
+#define SEC_FCTL 0x10
+#define SEC_SCTL0 0x800
+
+#define WDEN 0x0010
+#define WDDIS 0x0AD0
+
+struct adi_wdt_priv {
+ void __iomem *rcu_base;
+ void __iomem *sec_base;
+ void __iomem *wdt_base;
+ struct clk clock;
+};
+
+static int adi_wdt_reset(struct udevice *dev)
+{
+ struct adi_wdt_priv *priv = dev_get_priv(dev);
+
+ iowrite32(0, priv->wdt_base + WDOG_STAT);
+
+ return 0;
+}
+
+static int adi_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
+{
+ struct adi_wdt_priv *priv = dev_get_priv(dev);
+
+ /* Disable SYSCD_RESETb input and clear the RCU0 reset status */
+ iowrite32(0xf, priv->rcu_base + RCU_STAT);
+ iowrite32(0x0, priv->rcu_base + RCU_CTL);
+
+ /* reset the SEC controller */
+ iowrite32(0x2, priv->sec_base + SEC_GCTL);
+ iowrite32(0x2, priv->sec_base + SEC_FCTL);
+
+ udelay(50);
+
+ /* enable SEC fault event */
+ iowrite32(0x1, priv->sec_base + SEC_GCTL);
+
+ /* ANOMALY 36100004 Spurious External Fault event occurs when FCTL
+ * is re-programmed when currently active fault is not cleared
+ */
+ iowrite32(0xc0, priv->sec_base + SEC_FCTL);
+ iowrite32(0xc1, priv->sec_base + SEC_FCTL);
+
+ /* enable SEC fault source for watchdog0 */
+ setbits_32(priv->sec_base + SEC_SCTL0 + (3*8), 0x6);
+
+ /* Enable SYSCD_RESETb input */
+ iowrite32(0x100, priv->rcu_base + RCU_CTL);
+
+ /* enable watchdog0 */
+ iowrite32(WDDIS, priv->wdt_base + WDOG_CTL);
+
+ iowrite32(timeout_ms / 1000 *
+ (clk_get_rate(&priv->clock) / (IS_ENABLED(CONFIG_SC58X) ? 2 : 1)),
+ priv->wdt_base + WDOG_CNT);
+
+ iowrite32(0, priv->wdt_base + WDOG_STAT);
+ iowrite32(WDEN, priv->wdt_base + WDOG_CTL);
+
+ return 0;
+}
+
+static int adi_wdt_probe(struct udevice *dev)
+{
+ struct adi_wdt_priv *priv = dev_get_priv(dev);
+ int ret;
+ struct resource res;
+
+ ret = dev_read_resource_byname(dev, "rcu", &res);
+ if (ret)
+ return ret;
+ priv->rcu_base = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "sec", &res);
+ if (ret)
+ return ret;
+ priv->sec_base = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "wdt", &res);
+ if (ret)
+ return ret;
+ priv->wdt_base = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = clk_get_by_name(dev, "sclk0", &priv->clock);
+ if (ret < 0) {
+ printf("Can't get WDT clk: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct wdt_ops adi_wdt_ops = {
+ .start = adi_wdt_start,
+ .reset = adi_wdt_reset,
+};
+
+static const struct udevice_id adi_wdt_ids[] = {
+ { .compatible = "adi,wdt" },
+ {}
+};
+
+U_BOOT_DRIVER(adi_wdt) = {
+ .name = "adi_wdt",
+ .id = UCLASS_WDT,
+ .of_match = adi_wdt_ids,
+ .probe = adi_wdt_probe,
+ .ops = &adi_wdt_ops,
+ .priv_auto = sizeof(struct adi_wdt_priv),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/watchdog/da9063-wdt.c b/drivers/watchdog/da9063-wdt.c
index b7216b57863..ec9bc033011 100644
--- a/drivers/watchdog/da9063-wdt.c
+++ b/drivers/watchdog/da9063-wdt.c
@@ -145,5 +145,4 @@ U_BOOT_DRIVER(da9063_wdt) = {
.id = UCLASS_WDT,
.of_match = da9063_wdt_ids,
.ops = &da9063_wdt_ops,
- .flags = DM_FLAG_PROBE_AFTER_BIND,
};
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
index 8eeac935760..467db5fe9bf 100644
--- a/drivers/watchdog/sunxi_wdt.c
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -153,10 +153,21 @@ static const struct sunxi_wdt_reg sun20i_wdt_reg = {
.wdt_key_val = 0x16aa0000,
};
+static const struct sunxi_wdt_reg sun55i_wdt_reg = {
+ .wdt_ctrl = 0x0c,
+ .wdt_cfg = 0x10,
+ .wdt_mode = 0x14,
+ .wdt_timeout_shift = 4,
+ .wdt_reset_mask = 0x03,
+ .wdt_reset_val = 0x01,
+ .wdt_key_val = 0x16aa0000,
+};
+
static const struct udevice_id sunxi_wdt_ids[] = {
{ .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
{ .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
{ .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
+ { .compatible = "allwinner,sun55i-a523-wdt", .data = (ulong)&sun55i_wdt_reg },
{ /* sentinel */ }
};