diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/arch/arm/mach-sunxi | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/arch/arm/mach-sunxi')
35 files changed, 10447 insertions, 0 deletions
diff --git a/roms/u-boot/arch/arm/mach-sunxi/Kconfig b/roms/u-boot/arch/arm/mach-sunxi/Kconfig new file mode 100644 index 000000000..bc8509b72 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/Kconfig @@ -0,0 +1,1100 @@ +if ARCH_SUNXI + +config SPL_LDSCRIPT + default "arch/arm/cpu/armv7/sunxi/u-boot-spl.lds" if !ARM64 + +config IDENT_STRING + default " Allwinner Technology" + +config DRAM_SUN4I + bool + help + Select this dram controller driver for Sun4/5/7i platforms, + like A10/A13/A20. + +config DRAM_SUN6I + bool + help + Select this dram controller driver for Sun6i platforms, + like A31/A31s. + +config DRAM_SUN8I_A23 + bool + help + Select this dram controller driver for Sun8i platforms, + for A23 SOC. + +config DRAM_SUN8I_A33 + bool + help + Select this dram controller driver for Sun8i platforms, + for A33 SOC. + +config DRAM_SUN8I_A83T + bool + help + Select this dram controller driver for Sun8i platforms, + for A83T SOC. + +config DRAM_SUN9I + bool + help + Select this dram controller driver for Sun9i platforms, + like A80. + +config DRAM_SUN50I_H6 + bool + help + Select this dram controller driver for some sun50i platforms, + like H6. + +config DRAM_SUN50I_H616 + bool + help + Select this dram controller driver for some sun50i platforms, + like H616. + +if DRAM_SUN50I_H616 +config DRAM_SUN50I_H616_WRITE_LEVELING + bool "H616 DRAM write leveling" + ---help--- + Select this when DRAM on your H616 board needs write leveling. + +config DRAM_SUN50I_H616_READ_CALIBRATION + bool "H616 DRAM read calibration" + ---help--- + Select this when DRAM on your H616 board needs read calibration. + +config DRAM_SUN50I_H616_READ_TRAINING + bool "H616 DRAM read training" + ---help--- + Select this when DRAM on your H616 board needs read training. + +config DRAM_SUN50I_H616_WRITE_TRAINING + bool "H616 DRAM write training" + ---help--- + Select this when DRAM on your H616 board needs write training. + +config DRAM_SUN50I_H616_BIT_DELAY_COMPENSATION + bool "H616 DRAM bit delay compensation" + ---help--- + Select this when DRAM on your H616 board needs bit delay + compensation. + +config DRAM_SUN50I_H616_UNKNOWN_FEATURE + bool "H616 DRAM unknown feature" + ---help--- + Select this when DRAM on your H616 board needs this unknown + feature. +endif + +config SUN6I_P2WI + bool "Allwinner sun6i internal P2WI controller" + help + If you say yes to this option, support will be included for the + P2WI (Push/Pull 2 Wire Interface) controller embedded in some sunxi + SOCs. + The P2WI looks like an SMBus controller (which supports only byte + accesses), except that it only supports one slave device. + This interface is used to connect to specific PMIC devices (like the + AXP221). + +config SUN6I_PRCM + bool + help + Support for the PRCM (Power/Reset/Clock Management) unit available + in A31 SoC. + +config AXP_PMIC_BUS + bool "Sunxi AXP PMIC bus access helpers" + help + Select this PMIC bus access helpers for Sunxi platform PRCM or other + AXP family PMIC devices. + +config SUN8I_RSB + bool "Allwinner sunXi Reduced Serial Bus Driver" + help + Say y here to enable support for Allwinner's Reduced Serial Bus + (RSB) support. This controller is responsible for communicating + with various RSB based devices, such as AXP223, AXP8XX PMICs, + and AC100/AC200 ICs. + +config SUNXI_SRAM_ADDRESS + hex + default 0x10000 if MACH_SUN9I || MACH_SUN50I || MACH_SUN50I_H5 + default 0x20000 if SUN50I_GEN_H6 + default 0x0 + ---help--- + Older Allwinner SoCs have their mask boot ROM mapped just below 4GB, + with the first SRAM region being located at address 0. + Some newer SoCs map the boot ROM at address 0 instead and move the + SRAM to a different address. + +config SUNXI_A64_TIMER_ERRATUM + bool + +# Note only one of these may be selected at a time! But hidden choices are +# not supported by Kconfig +config SUNXI_GEN_SUN4I + bool + ---help--- + Select this for sunxi SoCs which have resets and clocks set up + as the original A10 (mach-sun4i). + +config SUNXI_GEN_SUN6I + bool + ---help--- + Select this for sunxi SoCs which have sun6i like periphery, like + separate ahb reset control registers, custom pmic bus, new style + watchdog, etc. + +config SUN50I_GEN_H6 + bool + select FIT + select SPL_LOAD_FIT + select SUPPORT_SPL + ---help--- + Select this for sunxi SoCs which have H6 like peripherals, clocks + and memory map. + +config SUNXI_DRAM_DW + bool + ---help--- + Select this for sunxi SoCs which uses a DRAM controller like the + DesignWare controller used in H3, mainly SoCs after H3, which do + not have official open-source DRAM initialization code, but can + use modified H3 DRAM initialization code. + +if SUNXI_DRAM_DW +config SUNXI_DRAM_DW_16BIT + bool + ---help--- + Select this for sunxi SoCs with DesignWare DRAM controller and + have only 16-bit memory buswidth. + +config SUNXI_DRAM_DW_32BIT + bool + ---help--- + Select this for sunxi SoCs with DesignWare DRAM controller with + 32-bit memory buswidth. +endif + +config MACH_SUNXI_H3_H5 + bool + select DM_I2C + select PHY_SUN4I_USB + select SUNXI_DE2 + select SUNXI_DRAM_DW + select SUNXI_DRAM_DW_32BIT + select SUNXI_GEN_SUN6I + select SUPPORT_SPL + +# TODO: try out A80's 8GiB DRAM space +# TODO: H616 supports 4 GiB DRAM space +config SUNXI_DRAM_MAX_SIZE + hex + default 0xC0000000 if MACH_SUN50I || MACH_SUN50I_H5 || MACH_SUN50I_H6 || MACH_SUN50I_H616 + default 0x80000000 + +choice + prompt "Sunxi SoC Variant" + optional + +config MACH_SUN4I + bool "sun4i (Allwinner A10)" + select CPU_V7A + select ARM_CORTEX_CPU_IS_UP + select PHY_SUN4I_USB + select DRAM_SUN4I + select SUNXI_GEN_SUN4I + select SUPPORT_SPL + +config MACH_SUN5I + bool "sun5i (Allwinner A13)" + select CPU_V7A + select ARM_CORTEX_CPU_IS_UP + select DRAM_SUN4I + select PHY_SUN4I_USB + select SUNXI_GEN_SUN4I + select SUPPORT_SPL + imply CONS_INDEX_2 if !DM_SERIAL + +config MACH_SUN6I + bool "sun6i (Allwinner A31)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select DRAM_SUN6I + select PHY_SUN4I_USB + select SUN6I_P2WI + select SUN6I_PRCM + select SUNXI_GEN_SUN6I + select SUPPORT_SPL + select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT + +config MACH_SUN7I + bool "sun7i (Allwinner A20)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select DRAM_SUN4I + select PHY_SUN4I_USB + select SUNXI_GEN_SUN4I + select SUPPORT_SPL + select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT + +config MACH_SUN8I_A23 + bool "sun8i (Allwinner A23)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select DRAM_SUN8I_A23 + select PHY_SUN4I_USB + select SUNXI_GEN_SUN6I + select SUPPORT_SPL + select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT + imply CONS_INDEX_5 if !DM_SERIAL + +config MACH_SUN8I_A33 + bool "sun8i (Allwinner A33)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select DRAM_SUN8I_A33 + select PHY_SUN4I_USB + select SUNXI_GEN_SUN6I + select SUPPORT_SPL + select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT + imply CONS_INDEX_5 if !DM_SERIAL + +config MACH_SUN8I_A83T + bool "sun8i (Allwinner A83T)" + select CPU_V7A + select DRAM_SUN8I_A83T + select PHY_SUN4I_USB + select SUNXI_GEN_SUN6I + select MMC_SUNXI_HAS_NEW_MODE + select MMC_SUNXI_HAS_MODE_SWITCH + select SUPPORT_SPL + +config MACH_SUN8I_H3 + bool "sun8i (Allwinner H3)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select MACH_SUNXI_H3_H5 + select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT + +config MACH_SUN8I_R40 + bool "sun8i (Allwinner R40)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select SUNXI_GEN_SUN6I + select SUPPORT_SPL + select SUNXI_DRAM_DW + select SUNXI_DRAM_DW_32BIT + select PHY_SUN4I_USB + +config MACH_SUN8I_V3S + bool "sun8i (Allwinner V3/V3s/S3/S3L)" + select CPU_V7A + select CPU_V7_HAS_NONSEC + select CPU_V7_HAS_VIRT + select ARCH_SUPPORT_PSCI + select SUNXI_GEN_SUN6I + select SUNXI_DRAM_DW + select SUNXI_DRAM_DW_16BIT + select SUPPORT_SPL + select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT + +config MACH_SUN9I + bool "sun9i (Allwinner A80)" + select CPU_V7A + select DRAM_SUN9I + select SUN6I_PRCM + select SUNXI_GEN_SUN6I + select SUN8I_RSB + select SUPPORT_SPL + +config MACH_SUN50I + bool "sun50i (Allwinner A64)" + select ARM64 + select SPI + select DM_I2C + select DM_SPI if SPI + select DM_SPI_FLASH + select PHY_SUN4I_USB + select SUN6I_PRCM + select SUNXI_DE2 + select SUNXI_GEN_SUN6I + select MMC_SUNXI_HAS_NEW_MODE + select SUPPORT_SPL + select SUNXI_DRAM_DW + select SUNXI_DRAM_DW_32BIT + select FIT + select SPL_LOAD_FIT + select SUNXI_A64_TIMER_ERRATUM + +config MACH_SUN50I_H5 + bool "sun50i (Allwinner H5)" + select ARM64 + select MACH_SUNXI_H3_H5 + select FIT + select SPL_LOAD_FIT + +config MACH_SUN50I_H6 + bool "sun50i (Allwinner H6)" + select ARM64 + select PHY_SUN4I_USB + select DRAM_SUN50I_H6 + select SUN50I_GEN_H6 + +config MACH_SUN50I_H616 + bool "sun50i (Allwinner H616)" + select ARM64 + select DRAM_SUN50I_H616 + select SUN50I_GEN_H6 + +endchoice + +# The sun8i SoCs share a lot, this helps to avoid a lot of "if A23 || A33" +config MACH_SUN8I + bool + select SUN8I_RSB + select SUN6I_PRCM + default y if MACH_SUN8I_A23 + default y if MACH_SUN8I_A33 + default y if MACH_SUN8I_A83T + default y if MACH_SUNXI_H3_H5 + default y if MACH_SUN8I_R40 + default y if MACH_SUN8I_V3S + +config RESERVE_ALLWINNER_BOOT0_HEADER + bool "reserve space for Allwinner boot0 header" + select ENABLE_ARM_SOC_BOOT0_HOOK + ---help--- + Prepend a 1536 byte (empty) header to the U-Boot image file, to be + filled with magic values post build. The Allwinner provided boot0 + blob relies on this information to load and execute U-Boot. + Only needed on 64-bit Allwinner boards so far when using boot0. + +config ARM_BOOT_HOOK_RMR + bool + depends on ARM64 + default y + select ENABLE_ARM_SOC_BOOT0_HOOK + ---help--- + Insert some ARM32 code at the very beginning of the U-Boot binary + which uses an RMR register write to bring the core into AArch64 mode. + The very first instruction acts as a switch, since it's carefully + chosen to be a NOP in one mode and a branch in the other, so the + code would only be executed if not already in AArch64. + This allows both the SPL and the U-Boot proper to be entered in + either mode and switch to AArch64 if needed. + +if SUNXI_DRAM_DW || DRAM_SUN50I_H6 +config SUNXI_DRAM_DDR3 + bool + +config SUNXI_DRAM_DDR2 + bool + +config SUNXI_DRAM_LPDDR3 + bool + +choice + prompt "DRAM Type and Timing" + default SUNXI_DRAM_DDR3_1333 if !MACH_SUN8I_V3S + default SUNXI_DRAM_DDR2_V3S if MACH_SUN8I_V3S + +config SUNXI_DRAM_DDR3_1333 + bool "DDR3 1333" + select SUNXI_DRAM_DDR3 + ---help--- + This option is the original only supported memory type, which suits + many H3/H5/A64 boards available now. + +config SUNXI_DRAM_LPDDR3_STOCK + bool "LPDDR3 with Allwinner stock configuration" + select SUNXI_DRAM_LPDDR3 + ---help--- + This option is the LPDDR3 timing used by the stock boot0 by + Allwinner. + +config SUNXI_DRAM_H6_LPDDR3 + bool "LPDDR3 DRAM chips on the H6 DRAM controller" + select SUNXI_DRAM_LPDDR3 + depends on DRAM_SUN50I_H6 + ---help--- + This option is the LPDDR3 timing used by the stock boot0 by + Allwinner. + +config SUNXI_DRAM_H6_DDR3_1333 + bool "DDR3-1333 boot0 timings on the H6 DRAM controller" + select SUNXI_DRAM_DDR3 + depends on DRAM_SUN50I_H6 + ---help--- + This option is the DDR3 timing used by the boot0 on H6 TV boxes + which use a DDR3-1333 timing. + +config SUNXI_DRAM_DDR2_V3S + bool "DDR2 found in V3s chip" + select SUNXI_DRAM_DDR2 + depends on MACH_SUN8I_V3S + ---help--- + This option is only for the DDR2 memory chip which is co-packaged in + Allwinner V3s SoC. + +endchoice +endif + +config DRAM_TYPE + int "sunxi dram type" + depends on MACH_SUN8I_A83T + default 3 + ---help--- + Set the dram type, 3: DDR3, 7: LPDDR3 + +config DRAM_CLK + int "sunxi dram clock speed" + default 792 if MACH_SUN9I + default 648 if MACH_SUN8I_R40 + default 312 if MACH_SUN6I || MACH_SUN8I + default 360 if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || \ + MACH_SUN8I_V3S + default 672 if MACH_SUN50I + default 744 if MACH_SUN50I_H6 + default 720 if MACH_SUN50I_H616 + ---help--- + Set the dram clock speed, valid range 240 - 480 (prior to sun9i), + must be a multiple of 24. For the sun9i (A80), the tested values + (for DDR3-1600) are 312 to 792. + +if MACH_SUN5I || MACH_SUN7I +config DRAM_MBUS_CLK + int "sunxi mbus clock speed" + default 300 + ---help--- + Set the mbus clock speed. The maximum on sun5i hardware is 300MHz. + +endif + +config DRAM_ZQ + int "sunxi dram zq value" + depends on !MACH_SUN50I_H616 + default 123 if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I || \ + MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_A83T + default 127 if MACH_SUN7I + default 14779 if MACH_SUN8I_V3S + default 3881979 if MACH_SUNXI_H3_H5 || MACH_SUN8I_R40 || MACH_SUN50I_H6 + default 4145117 if MACH_SUN9I + default 3881915 if MACH_SUN50I + ---help--- + Set the dram zq value. + +config DRAM_ODT_EN + bool "sunxi dram odt enable" + default y if MACH_SUN8I_A23 + default y if MACH_SUNXI_H3_H5 + default y if MACH_SUN8I_R40 + default y if MACH_SUN50I + default y if MACH_SUN50I_H6 + default y if MACH_SUN50I_H616 + ---help--- + Select this to enable dram odt (on die termination). + +if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I +config DRAM_EMR1 + int "sunxi dram emr1 value" + default 0 if MACH_SUN4I + default 4 if MACH_SUN5I || MACH_SUN7I + ---help--- + Set the dram controller emr1 value. + +config DRAM_TPR3 + hex "sunxi dram tpr3 value" + default 0 + ---help--- + Set the dram controller tpr3 parameter. This parameter configures + the delay on the command lane and also phase shifts, which are + applied for sampling incoming read data. The default value 0 + means that no phase/delay adjustments are necessary. Properly + configuring this parameter increases reliability at high DRAM + clock speeds. + +config DRAM_DQS_GATING_DELAY + hex "sunxi dram dqs_gating_delay value" + default 0 + ---help--- + Set the dram controller dqs_gating_delay parmeter. Each byte + encodes the DQS gating delay for each byte lane. The delay + granularity is 1/4 cycle. For example, the value 0x05060606 + means that the delay is 5 quarter-cycles for one lane (1.25 + cycles) and 6 quarter-cycles (1.5 cycles) for 3 other lanes. + The default value 0 means autodetection. The results of hardware + autodetection are not very reliable and depend on the chip + temperature (sometimes producing different results on cold start + and warm reboot). But the accuracy of hardware autodetection + is usually good enough, unless running at really high DRAM + clocks speeds (up to 600MHz). If unsure, keep as 0. + +choice + prompt "sunxi dram timings" + default DRAM_TIMINGS_VENDOR_MAGIC + ---help--- + Select the timings of the DDR3 chips. + +config DRAM_TIMINGS_VENDOR_MAGIC + bool "Magic vendor timings from Android" + ---help--- + The same DRAM timings as in the Allwinner boot0 bootloader. + +config DRAM_TIMINGS_DDR3_1066F_1333H + bool "JEDEC DDR3-1333H with down binning to DDR3-1066F" + ---help--- + Use the timings of the standard JEDEC DDR3-1066F speed bin for + DRAM_CLK <= 533MHz and the timings of the DDR3-1333H speed bin + for DRAM_CLK > 533MHz. This covers the majority of DDR3 chips + used in Allwinner A10/A13/A20 devices. In the case of DDR3-1333 + or DDR3-1600 chips, be sure to check the DRAM datasheet to confirm + that down binning to DDR3-1066F is supported (because DDR3-1066F + uses a bit faster timings than DDR3-1333H). + +config DRAM_TIMINGS_DDR3_800E_1066G_1333J + bool "JEDEC DDR3-800E / DDR3-1066G / DDR3-1333J" + ---help--- + Use the timings of the slowest possible JEDEC speed bin for the + selected DRAM_CLK. Depending on the DRAM_CLK value, it may be + DDR3-800E, DDR3-1066G or DDR3-1333J. + +endchoice + +endif + +if MACH_SUN8I_A23 +config DRAM_ODT_CORRECTION + int "sunxi dram odt correction value" + default 0 + ---help--- + Set the dram odt correction value (range -255 - 255). In allwinner + fex files, this option is found in bits 8-15 of the u32 odt_en variable + in the [dram] section. When bit 31 of the odt_en variable is set + then the correction is negative. Usually the value for this is 0. +endif + +config SYS_CLK_FREQ + default 1008000000 if MACH_SUN4I + default 1008000000 if MACH_SUN5I + default 1008000000 if MACH_SUN6I + default 912000000 if MACH_SUN7I + default 816000000 if MACH_SUN50I || MACH_SUN50I_H5 + default 1008000000 if MACH_SUN8I + default 1008000000 if MACH_SUN9I + default 888000000 if MACH_SUN50I_H6 + default 1008000000 if MACH_SUN50I_H616 + +config SYS_CONFIG_NAME + default "sun4i" if MACH_SUN4I + default "sun5i" if MACH_SUN5I + default "sun6i" if MACH_SUN6I + default "sun7i" if MACH_SUN7I + default "sun8i" if MACH_SUN8I + default "sun9i" if MACH_SUN9I + default "sun50i" if MACH_SUN50I + default "sun50i" if MACH_SUN50I_H6 + default "sun50i" if MACH_SUN50I_H616 + +config SYS_BOARD + default "sunxi" + +config SYS_SOC + default "sunxi" + +config UART0_PORT_F + bool "UART0 on MicroSD breakout board" + default n + ---help--- + Repurpose the SD card slot for getting access to the UART0 serial + console. Primarily useful only for low level u-boot debugging on + tablets, where normal UART0 is difficult to access and requires + device disassembly and/or soldering. As the SD card can't be used + at the same time, the system can be only booted in the FEL mode. + Only enable this if you really know what you are doing. + +config OLD_SUNXI_KERNEL_COMPAT + bool "Enable workarounds for booting old kernels" + default n + ---help--- + Set this to enable various workarounds for old kernels, this results in + sub-optimal settings for newer kernels, only enable if needed. + +config MACPWR + string "MAC power pin" + default "" + help + Set the pin used to power the MAC. This takes a string in the format + understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config MMC0_CD_PIN + string "Card detect pin for mmc0" + default "PF6" if MACH_SUN8I_A83T || MACH_SUNXI_H3_H5 || MACH_SUN50I + default "" + ---help--- + Set the card detect pin for mmc0, leave empty to not use cd. This + takes a string in the format understood by sunxi_name_to_gpio, e.g. + PH1 for pin 1 of port H. + +config MMC1_CD_PIN + string "Card detect pin for mmc1" + default "" + ---help--- + See MMC0_CD_PIN help text. + +config MMC2_CD_PIN + string "Card detect pin for mmc2" + default "" + ---help--- + See MMC0_CD_PIN help text. + +config MMC3_CD_PIN + string "Card detect pin for mmc3" + default "" + ---help--- + See MMC0_CD_PIN help text. + +config MMC1_PINS + string "Pins for mmc1" + default "" + ---help--- + Set the pins used for mmc1, when applicable. This takes a string in the + format understood by sunxi_name_to_gpio_bank, e.g. PH for port H. + +config MMC2_PINS + string "Pins for mmc2" + default "" + ---help--- + See MMC1_PINS help text. + +config MMC3_PINS + string "Pins for mmc3" + default "" + ---help--- + See MMC1_PINS help text. + +config MMC_SUNXI_SLOT_EXTRA + int "mmc extra slot number" + default -1 + ---help--- + sunxi builds always enable mmc0, some boards also have a second sdcard + slot or emmc on mmc1 - mmc3. Setting this to 1, 2 or 3 will enable + support for this. + +config INITIAL_USB_SCAN_DELAY + int "delay initial usb scan by x ms to allow builtin devices to init" + default 0 + ---help--- + Some boards have on board usb devices which need longer than the + USB spec's 1 second to connect from board powerup. Set this config + option to a non 0 value to add an extra delay before the first usb + bus scan. + +config USB0_VBUS_PIN + string "Vbus enable pin for usb0 (otg)" + default "" + ---help--- + Set the Vbus enable pin for usb0 (otg). This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config USB0_VBUS_DET + string "Vbus detect pin for usb0 (otg)" + default "" + ---help--- + Set the Vbus detect pin for usb0 (otg). This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config USB0_ID_DET + string "ID detect pin for usb0 (otg)" + default "" + ---help--- + Set the ID detect pin for usb0 (otg). This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config USB1_VBUS_PIN + string "Vbus enable pin for usb1 (ehci0)" + default "PH6" if MACH_SUN4I || MACH_SUN7I + default "PH27" if MACH_SUN6I + ---help--- + Set the Vbus enable pin for usb1 (ehci0, usb0 is the otg). This takes + a string in the format understood by sunxi_name_to_gpio, e.g. + PH1 for pin 1 of port H. + +config USB2_VBUS_PIN + string "Vbus enable pin for usb2 (ehci1)" + default "PH3" if MACH_SUN4I || MACH_SUN7I + default "PH24" if MACH_SUN6I + ---help--- + See USB1_VBUS_PIN help text. + +config USB3_VBUS_PIN + string "Vbus enable pin for usb3 (ehci2)" + default "" + ---help--- + See USB1_VBUS_PIN help text. + +config I2C0_ENABLE + bool "Enable I2C/TWI controller 0" + default y if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I_R40 + default n if MACH_SUN6I || MACH_SUN8I + select CMD_I2C + ---help--- + This allows enabling I2C/TWI controller 0 by muxing its pins, enabling + its clock and setting up the bus. This is especially useful on devices + with slaves connected to the bus or with pins exposed through e.g. an + expansion port/header. + +config I2C1_ENABLE + bool "Enable I2C/TWI controller 1" + default n + select CMD_I2C + ---help--- + See I2C0_ENABLE help text. + +config I2C2_ENABLE + bool "Enable I2C/TWI controller 2" + default n + select CMD_I2C + ---help--- + See I2C0_ENABLE help text. + +if MACH_SUN6I || MACH_SUN7I +config I2C3_ENABLE + bool "Enable I2C/TWI controller 3" + default n + select CMD_I2C + ---help--- + See I2C0_ENABLE help text. +endif + +if SUNXI_GEN_SUN6I || SUN50I_GEN_H6 +config R_I2C_ENABLE + bool "Enable the PRCM I2C/TWI controller" + # This is used for the pmic on H3 + default y if SY8106A_POWER + select CMD_I2C + ---help--- + Set this to y to enable the I2C controller which is part of the PRCM. +endif + +if MACH_SUN7I +config I2C4_ENABLE + bool "Enable I2C/TWI controller 4" + default n + select CMD_I2C + ---help--- + See I2C0_ENABLE help text. +endif + +config AXP_GPIO + bool "Enable support for gpio-s on axp PMICs" + default n + ---help--- + Say Y here to enable support for the gpio pins of the axp PMIC ICs. + +config VIDEO_SUNXI + bool "Enable graphical uboot console on HDMI, LCD or VGA" + depends on !MACH_SUN8I_A83T + depends on !MACH_SUNXI_H3_H5 + depends on !MACH_SUN8I_R40 + depends on !MACH_SUN8I_V3S + depends on !MACH_SUN9I + depends on !MACH_SUN50I + depends on !SUN50I_GEN_H6 + select DM_VIDEO + select DISPLAY + imply VIDEO_DT_SIMPLEFB + default y + ---help--- + Say Y here to add support for using a graphical console on the HDMI, + LCD or VGA output found on older sunxi devices. This will also provide + a simple_framebuffer device for Linux. + +config VIDEO_HDMI + bool "HDMI output support" + depends on VIDEO_SUNXI && !MACH_SUN8I + default y + ---help--- + Say Y here to add support for outputting video over HDMI. + +config VIDEO_VGA + bool "VGA output support" + depends on VIDEO_SUNXI && (MACH_SUN4I || MACH_SUN7I) + default n + ---help--- + Say Y here to add support for outputting video over VGA. + +config VIDEO_VGA_VIA_LCD + bool "VGA via LCD controller support" + depends on VIDEO_SUNXI && (MACH_SUN5I || MACH_SUN6I || MACH_SUN8I) + default n + ---help--- + Say Y here to add support for external DACs connected to the parallel + LCD interface driving a VGA connector, such as found on the + Olimex A13 boards. + +config VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH + bool "Force sync active high for VGA via LCD controller support" + depends on VIDEO_VGA_VIA_LCD + default n + ---help--- + Say Y here if you've a board which uses opendrain drivers for the vga + hsync and vsync signals. Opendrain drivers cannot generate steep enough + positive edges for a stable video output, so on boards with opendrain + drivers the sync signals must always be active high. + +config VIDEO_VGA_EXTERNAL_DAC_EN + string "LCD panel power enable pin" + depends on VIDEO_VGA_VIA_LCD + default "" + ---help--- + Set the enable pin for the external VGA DAC. This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config VIDEO_COMPOSITE + bool "Composite video output support" + depends on VIDEO_SUNXI && (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I) + default n + ---help--- + Say Y here to add support for outputting composite video. + +config VIDEO_LCD_MODE + string "LCD panel timing details" + depends on VIDEO_SUNXI + default "" + ---help--- + LCD panel timing details string, leave empty if there is no LCD panel. + This is in drivers/video/videomodes.c: video_get_params() format, e.g. + x:800,y:480,depth:18,pclk_khz:33000,le:16,ri:209,up:22,lo:22,hs:30,vs:1,sync:0,vmode:0 + Also see: http://linux-sunxi.org/LCD + +config VIDEO_LCD_DCLK_PHASE + int "LCD panel display clock phase" + depends on VIDEO_SUNXI || DM_VIDEO + default 1 + ---help--- + Select LCD panel display clock phase shift, range 0-3. + +config VIDEO_LCD_POWER + string "LCD panel power enable pin" + depends on VIDEO_SUNXI + default "" + ---help--- + Set the power enable pin for the LCD panel. This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_RESET + string "LCD panel reset pin" + depends on VIDEO_SUNXI + default "" + ---help--- + Set the reset pin for the LCD panel. This takes a string in the format + understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_BL_EN + string "LCD panel backlight enable pin" + depends on VIDEO_SUNXI + default "" + ---help--- + Set the backlight enable pin for the LCD panel. This takes a string in the + the format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of + port H. + +config VIDEO_LCD_BL_PWM + string "LCD panel backlight pwm pin" + depends on VIDEO_SUNXI + default "" + ---help--- + Set the backlight pwm pin for the LCD panel. This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_BL_PWM_ACTIVE_LOW + bool "LCD panel backlight pwm is inverted" + depends on VIDEO_SUNXI + default y + ---help--- + Set this if the backlight pwm output is active low. + +config VIDEO_LCD_PANEL_I2C + bool "LCD panel needs to be configured via i2c" + depends on VIDEO_SUNXI + default n + select CMD_I2C + ---help--- + Say y here if the LCD panel needs to be configured via i2c. This + will add a bitbang i2c controller using gpios to talk to the LCD. + +config VIDEO_LCD_PANEL_I2C_SDA + string "LCD panel i2c interface SDA pin" + depends on VIDEO_LCD_PANEL_I2C + default "PG12" + ---help--- + Set the SDA pin for the LCD i2c interface. This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + +config VIDEO_LCD_PANEL_I2C_SCL + string "LCD panel i2c interface SCL pin" + depends on VIDEO_LCD_PANEL_I2C + default "PG10" + ---help--- + Set the SCL pin for the LCD i2c interface. This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. + + +# Note only one of these may be selected at a time! But hidden choices are +# not supported by Kconfig +config VIDEO_LCD_IF_PARALLEL + bool + +config VIDEO_LCD_IF_LVDS + bool + +config SUNXI_DE2 + bool + default n + +config VIDEO_DE2 + bool "Display Engine 2 video driver" + depends on SUNXI_DE2 + select DM_VIDEO + select DISPLAY + select VIDEO_DW_HDMI + imply VIDEO_DT_SIMPLEFB + default y + ---help--- + Say y here if you want to build DE2 video driver which is present on + newer SoCs. Currently only HDMI output is supported. + + +choice + prompt "LCD panel support" + depends on VIDEO_SUNXI + ---help--- + Select which type of LCD panel to support. + +config VIDEO_LCD_PANEL_PARALLEL + bool "Generic parallel interface LCD panel" + select VIDEO_LCD_IF_PARALLEL + +config VIDEO_LCD_PANEL_LVDS + bool "Generic lvds interface LCD panel" + select VIDEO_LCD_IF_LVDS + +config VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828 + bool "MIPI 4-lane, 513Mbps LCD panel via SSD2828 bridge chip" + select VIDEO_LCD_SSD2828 + select VIDEO_LCD_IF_PARALLEL + ---help--- + 7.85" 768x1024 LCD panels, such as LG LP079X01 or AUO B079XAN01.0 + +config VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804 + bool "eDP 4-lane, 1.62G LCD panel via ANX9804 bridge chip" + select VIDEO_LCD_ANX9804 + select VIDEO_LCD_IF_PARALLEL + select VIDEO_LCD_PANEL_I2C + ---help--- + Select this for eDP LCD panels with 4 lanes running at 1.62G, + connected via an ANX9804 bridge chip. + +config VIDEO_LCD_PANEL_HITACHI_TX18D42VM + bool "Hitachi tx18d42vm LCD panel" + select VIDEO_LCD_HITACHI_TX18D42VM + select VIDEO_LCD_IF_LVDS + ---help--- + 7.85" 1024x768 Hitachi tx18d42vm LCD panel support + +config VIDEO_LCD_TL059WV5C0 + bool "tl059wv5c0 LCD panel" + select VIDEO_LCD_PANEL_I2C + select VIDEO_LCD_IF_PARALLEL + ---help--- + 6" 480x800 tl059wv5c0 panel support, as used on the Utoo P66 and + Aigo M60/M608/M606 tablets. + +endchoice + +config SATAPWR + string "SATA power pin" + default "" + help + Set the pins used to power the SATA. This takes a string in the + format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of + port H. + +config GMAC_TX_DELAY + int "GMAC Transmit Clock Delay Chain" + default 0 + ---help--- + Set the GMAC Transmit Clock Delay Chain value. + +config SPL_STACK_R_ADDR + default 0x4fe00000 if MACH_SUN4I + default 0x4fe00000 if MACH_SUN5I + default 0x4fe00000 if MACH_SUN6I + default 0x4fe00000 if MACH_SUN7I + default 0x4fe00000 if MACH_SUN8I + default 0x2fe00000 if MACH_SUN9I + default 0x4fe00000 if MACH_SUN50I + default 0x4fe00000 if SUN50I_GEN_H6 + +config SPL_SPI_SUNXI + bool "Support for SPI Flash on Allwinner SoCs in SPL" + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 || MACH_SUN50I || MACH_SUN8I_R40 || MACH_SUN50I_H6 + help + Enable support for SPI Flash. This option allows SPL to read from + sunxi SPI Flash. It uses the same method as the boot ROM, so does + not need any extra configuration. + +config PINE64_DT_SELECTION + bool "Enable Pine64 device tree selection code" + depends on MACH_SUN50I + help + The original Pine A64 and Pine A64+ are similar but different + boards and can be differed by the DRAM size. Pine A64 has + 512MiB DRAM, and Pine A64+ has 1GiB or 2GiB. By selecting this + option, the device tree selection code specific to Pine64 which + utilizes the DRAM size will be enabled. + +config PINEPHONE_DT_SELECTION + bool "Enable PinePhone device tree selection code" + depends on MACH_SUN50I + help + Enable this option to automatically select the device tree for the + correct PinePhone hardware revision during boot. + +config BLUETOOTH_DT_DEVICE_FIXUP + string "Fixup the Bluetooth controller address" + default "" + help + This option specifies the DT compatible name of the Bluetooth + controller for which to set the "local-bd-address" property. + Set this option if your device ships with the Bluetooth controller + default address. + The used address is "bdaddr" if set, and "ethaddr" with the LSB + flipped elsewise. + +endif + +config CHIP_DIP_SCAN + bool "Enable DIPs detection for CHIP board" + select SUPPORT_EXTENSION_SCAN + select W1 + select W1_GPIO + select W1_EEPROM + select W1_EEPROM_DS24XXX + select CMD_EXTENSION diff --git a/roms/u-boot/arch/arm/mach-sunxi/Makefile b/roms/u-boot/arch/arm/mach-sunxi/Makefile new file mode 100644 index 000000000..3f081d92f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/Makefile @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net> +# +# Based on some other Makefile +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +obj-y += board.o +obj-y += clock.o +obj-y += cpu_info.o +obj-y += dram_helpers.o +obj-y += pinmux.o +obj-$(CONFIG_SUN6I_P2WI) += p2wi.o +obj-$(CONFIG_SUN6I_PRCM) += prcm.o +obj-$(CONFIG_AXP_PMIC_BUS) += pmic_bus.o +obj-$(CONFIG_SUN8I_RSB) += rsb.o +obj-$(CONFIG_MACH_SUN4I) += clock_sun4i.o +obj-$(CONFIG_MACH_SUN5I) += clock_sun4i.o +obj-$(CONFIG_MACH_SUN6I) += clock_sun6i.o +obj-$(CONFIG_MACH_SUN7I) += clock_sun4i.o +obj-$(CONFIG_MACH_SUN50I) += clock_sun6i.o +ifdef CONFIG_MACH_SUN8I_A83T +obj-y += clock_sun8i_a83t.o +else +obj-$(CONFIG_MACH_SUN8I) += clock_sun6i.o +endif +obj-$(CONFIG_MACH_SUN9I) += clock_sun9i.o gtbus_sun9i.o +obj-$(CONFIG_SUN50I_GEN_H6) += clock_sun50i_h6.o + +ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_DRAM_SUN4I) += dram_sun4i.o +obj-$(CONFIG_DRAM_SUN6I) += dram_sun6i.o +obj-$(CONFIG_DRAM_SUN8I_A23) += dram_sun8i_a23.o +obj-$(CONFIG_DRAM_SUN8I_A33) += dram_sun8i_a33.o +obj-$(CONFIG_DRAM_SUN8I_A83T) += dram_sun8i_a83t.o +obj-$(CONFIG_DRAM_SUN9I) += dram_sun9i.o +obj-$(CONFIG_SPL_SPI_SUNXI) += spl_spi_sunxi.o +obj-$(CONFIG_SUNXI_DRAM_DW) += dram_sunxi_dw.o +obj-$(CONFIG_SUNXI_DRAM_DW) += dram_timings/ +obj-$(CONFIG_DRAM_SUN50I_H6) += dram_sun50i_h6.o +obj-$(CONFIG_DRAM_SUN50I_H6) += dram_timings/ +obj-$(CONFIG_DRAM_SUN50I_H616) += dram_sun50i_h616.o +obj-$(CONFIG_DRAM_SUN50I_H616) += dram_timings/ +endif diff --git a/roms/u-boot/arch/arm/mach-sunxi/board.c b/roms/u-boot/arch/arm/mach-sunxi/board.c new file mode 100644 index 000000000..9b84132ed --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/board.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net> + * + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * Some init for sunxi platform. + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <log.h> +#include <mmc.h> +#include <i2c.h> +#include <serial.h> +#include <spl.h> +#include <asm/cache.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/gpio.h> +#include <asm/arch/spl.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/timer.h> +#include <asm/arch/tzpc.h> +#include <asm/arch/mmc.h> + +#include <linux/compiler.h> + +struct fel_stash { + uint32_t sp; + uint32_t lr; + uint32_t cpsr; + uint32_t sctlr; + uint32_t vbar; + uint32_t cr; +}; + +struct fel_stash fel_stash __section(".data"); + +#ifdef CONFIG_ARM64 +#include <asm/armv8/mmu.h> + +static struct mm_region sunxi_mem_map[] = { + { + /* SRAM, MMIO regions */ + .virt = 0x0UL, + .phys = 0x0UL, + .size = 0x40000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE + }, { + /* RAM */ + .virt = 0x40000000UL, + .phys = 0x40000000UL, + .size = 0xC0000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + /* List terminator */ + 0, + } +}; +struct mm_region *mem_map = sunxi_mem_map; +#endif + +static int gpio_init(void) +{ + __maybe_unused uint val; +#if CONFIG_CONS_INDEX == 1 && defined(CONFIG_UART0_PORT_F) +#if defined(CONFIG_MACH_SUN4I) || \ + defined(CONFIG_MACH_SUN7I) || \ + defined(CONFIG_MACH_SUN8I_R40) + /* disable GPB22,23 as uart0 tx,rx to avoid conflict */ + sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUNXI_GPIO_INPUT); + sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUNXI_GPIO_INPUT); +#endif +#if defined(CONFIG_MACH_SUN8I) && !defined(CONFIG_MACH_SUN8I_R40) + sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUN8I_GPF_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUN8I_GPF_UART0); +#else + sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF_UART0); +#endif + sunxi_gpio_set_pull(SUNXI_GPF(4), 1); +#elif CONFIG_CONS_INDEX == 1 && (defined(CONFIG_MACH_SUN4I) || \ + defined(CONFIG_MACH_SUN7I) || \ + defined(CONFIG_MACH_SUN8I_R40)) + sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(23), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN5I) + sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(20), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN6I) + sunxi_gpio_set_cfgpin(SUNXI_GPH(20), SUN6I_GPH_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPH(21), SUN6I_GPH_UART0); + sunxi_gpio_set_pull(SUNXI_GPH(21), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A33) + sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_A33_GPB_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_A33_GPB_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUNXI_H3_H5) + sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0); + sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I) + sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_GPB_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_GPB_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_H6) + sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0); + sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN50I_H616) + sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0); + sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_A83T) + sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_A83T_GPB_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_A83T_GPB_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN8I_V3S) + sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN8I_V3S_GPB_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V3S_GPB_UART0); + sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 1 && defined(CONFIG_MACH_SUN9I) + sunxi_gpio_set_cfgpin(SUNXI_GPH(12), SUN9I_GPH_UART0); + sunxi_gpio_set_cfgpin(SUNXI_GPH(13), SUN9I_GPH_UART0); + sunxi_gpio_set_pull(SUNXI_GPH(13), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUN5I) + sunxi_gpio_set_cfgpin(SUNXI_GPG(3), SUN5I_GPG_UART1); + sunxi_gpio_set_cfgpin(SUNXI_GPG(4), SUN5I_GPG_UART1); + sunxi_gpio_set_pull(SUNXI_GPG(4), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I) + sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_GPB_UART2); + sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2); + sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 5 && defined(CONFIG_MACH_SUN8I) + sunxi_gpio_set_cfgpin(SUNXI_GPL(2), SUN8I_GPL_R_UART); + sunxi_gpio_set_cfgpin(SUNXI_GPL(3), SUN8I_GPL_R_UART); + sunxi_gpio_set_pull(SUNXI_GPL(3), SUNXI_GPIO_PULL_UP); +#elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUN8I) && \ + !defined(CONFIG_MACH_SUN8I_R40) + sunxi_gpio_set_cfgpin(SUNXI_GPG(6), SUN8I_GPG_UART1); + sunxi_gpio_set_cfgpin(SUNXI_GPG(7), SUN8I_GPG_UART1); + sunxi_gpio_set_pull(SUNXI_GPG(7), SUNXI_GPIO_PULL_UP); +#else +#error Unsupported console port number. Please fix pin mux settings in board.c +#endif + +#ifdef CONFIG_SUN50I_GEN_H6 + /* Update PIO power bias configuration by copy hardware detected value */ + val = readl(SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL); + writel(val, SUNXI_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL); + val = readl(SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_VAL); + writel(val, SUNXI_R_PIO_BASE + SUN50I_H6_GPIO_POW_MOD_SEL); +#endif + + return 0; +} + +#if defined(CONFIG_SPL_BOARD_LOAD_IMAGE) && defined(CONFIG_SPL_BUILD) +static int spl_board_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + debug("Returning to FEL sp=%x, lr=%x\n", fel_stash.sp, fel_stash.lr); + return_to_fel(fel_stash.sp, fel_stash.lr); + + return 0; +} +SPL_LOAD_IMAGE_METHOD("FEL", 0, BOOT_DEVICE_BOARD, spl_board_load_image); +#endif + +void s_init(void) +{ + /* + * Undocumented magic taken from boot0, without this DRAM + * access gets messed up (seems cache related). + * The boot0 sources describe this as: "config ema for cache sram" + */ +#if defined CONFIG_MACH_SUN6I + setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800); +#elif defined CONFIG_MACH_SUN8I + __maybe_unused uint version; + + /* Unlock sram version info reg, read it, relock */ + setbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15)); + version = readl(SUNXI_SRAMC_BASE + 0x24) >> 16; + clrbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15)); + + /* + * Ideally this would be a switch case, but we do not know exactly + * which versions there are and which version needs which settings, + * so reproduce the per SoC code from the BSP. + */ +#if defined CONFIG_MACH_SUN8I_A23 + if (version == 0x1650) + setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0x1800); + else /* 0x1661 ? */ + setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0xc0); +#elif defined CONFIG_MACH_SUN8I_A33 + if (version != 0x1667) + setbits_le32(SUNXI_SRAMC_BASE + 0x44, 0xc0); +#endif + /* A83T BSP never modifies SUNXI_SRAMC_BASE + 0x44 */ + /* No H3 BSP, boot0 seems to not modify SUNXI_SRAMC_BASE + 0x44 */ +#endif + +#if !defined(CONFIG_ARM_CORTEX_CPU_IS_UP) && !defined(CONFIG_ARM64) + /* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */ + asm volatile( + "mrc p15, 0, r0, c1, c0, 1\n" + "orr r0, r0, #1 << 6\n" + "mcr p15, 0, r0, c1, c0, 1\n" + ::: "r0"); +#endif +#if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I_H3 + /* Enable non-secure access to some peripherals */ + tzpc_init(); +#endif + + clock_init(); + timer_init(); + gpio_init(); +#if !CONFIG_IS_ENABLED(DM_I2C) + i2c_init_board(); +#endif + eth_init_board(); +} + +#define SUNXI_INVALID_BOOT_SOURCE -1 + +static int sunxi_get_boot_source(void) +{ + if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */ + return SUNXI_INVALID_BOOT_SOURCE; + + return readb(SPL_ADDR + 0x28); +} + +/* The sunxi internal brom will try to loader external bootloader + * from mmc0, nand flash, mmc2. + */ +uint32_t sunxi_get_boot_device(void) +{ + int boot_source = sunxi_get_boot_source(); + + /* + * When booting from the SD card or NAND memory, the "eGON.BT0" + * signature is expected to be found in memory at the address 0x0004 + * (see the "mksunxiboot" tool, which generates this header). + * + * When booting in the FEL mode over USB, this signature is patched in + * memory and replaced with something else by the 'fel' tool. This other + * signature is selected in such a way, that it can't be present in a + * valid bootable SD card image (because the BROM would refuse to + * execute the SPL in this case). + * + * This checks for the signature and if it is not found returns to + * the FEL code in the BROM to wait and receive the main u-boot + * binary over USB. If it is found, it determines where SPL was + * read from. + */ + switch (boot_source) { + case SUNXI_INVALID_BOOT_SOURCE: + return BOOT_DEVICE_BOARD; + case SUNXI_BOOTED_FROM_MMC0: + case SUNXI_BOOTED_FROM_MMC0_HIGH: + return BOOT_DEVICE_MMC1; + case SUNXI_BOOTED_FROM_NAND: + return BOOT_DEVICE_NAND; + case SUNXI_BOOTED_FROM_MMC2: + case SUNXI_BOOTED_FROM_MMC2_HIGH: + return BOOT_DEVICE_MMC2; + case SUNXI_BOOTED_FROM_SPI: + return BOOT_DEVICE_SPI; + } + + panic("Unknown boot source %d\n", boot_source); + return -1; /* Never reached */ +} + +#ifdef CONFIG_SPL_BUILD +static u32 sunxi_get_spl_size(void) +{ + if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */ + return 0; + + return readl(SPL_ADDR + 0x10); +} + +/* + * The eGON SPL image can be located at 8KB or at 128KB into an SD card or + * an eMMC device. The boot source has bit 4 set in the latter case. + * By adding 120KB to the normal offset when booting from a "high" location + * we can support both cases. + * Also U-Boot proper is located at least 32KB after the SPL, but will + * immediately follow the SPL if that is bigger than that. + */ +unsigned long spl_mmc_get_uboot_raw_sector(struct mmc *mmc, + unsigned long raw_sect) +{ + unsigned long spl_size = sunxi_get_spl_size(); + unsigned long sector; + + sector = max(raw_sect, spl_size / 512); + + switch (sunxi_get_boot_source()) { + case SUNXI_BOOTED_FROM_MMC0_HIGH: + case SUNXI_BOOTED_FROM_MMC2_HIGH: + sector += (128 - 8) * 2; + break; + } + + return sector; +} + +u32 spl_boot_device(void) +{ + return sunxi_get_boot_device(); +} + +void board_init_f(ulong dummy) +{ + spl_init(); + preloader_console_init(); + +#ifdef CONFIG_SPL_I2C_SUPPORT + /* Needed early by sunxi_board_init if PMU is enabled */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + sunxi_board_init(); +} +#endif + +void reset_cpu(void) +{ +#if defined(CONFIG_SUNXI_GEN_SUN4I) || defined(CONFIG_MACH_SUN8I_R40) + static const struct sunxi_wdog *wdog = + &((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog; + + /* Set the watchdog for its shortest interval (.5s) and wait */ + writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode); + writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl); + + while (1) { + /* sun5i sometimes gets stuck without this */ + writel(WDT_MODE_RESET_EN | WDT_MODE_EN, &wdog->mode); + } +#elif defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) +#if defined(CONFIG_MACH_SUN50I_H6) + /* WDOG is broken for some H6 rev. use the R_WDOG instead */ + static const struct sunxi_wdog *wdog = + (struct sunxi_wdog *)SUNXI_R_WDOG_BASE; +#else + static const struct sunxi_wdog *wdog = + ((struct sunxi_timer_reg *)SUNXI_TIMER_BASE)->wdog; +#endif + /* Set the watchdog for its shortest interval (.5s) and wait */ + writel(WDT_CFG_RESET, &wdog->cfg); + writel(WDT_MODE_EN, &wdog->mode); + writel(WDT_CTRL_KEY | WDT_CTRL_RESTART, &wdog->ctl); + while (1) { } +#endif +} + +#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) && !defined(CONFIG_ARM64) +void enable_caches(void) +{ + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); +} +#endif diff --git a/roms/u-boot/arch/arm/mach-sunxi/clock.c b/roms/u-boot/arch/arm/mach-sunxi/clock.c new file mode 100644 index 000000000..f591affeb --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/clock.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/gpio.h> +#include <asm/arch/prcm.h> +#include <asm/arch/gtbus.h> +#include <asm/arch/sys_proto.h> + +__weak void clock_init_sec(void) +{ +} + +__weak void gtbus_init(void) +{ +} + +int clock_init(void) +{ +#ifdef CONFIG_SPL_BUILD + clock_init_safe(); + gtbus_init(); +#endif + clock_init_uart(); + clock_init_sec(); + + return 0; +} + +/* These functions are shared between various SoCs so put them here. */ +#if defined CONFIG_SUNXI_GEN_SUN6I && !defined CONFIG_MACH_SUN9I +int clock_twi_onoff(int port, int state) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (port == 5) { + if (state) + prcm_apb0_enable( + PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_I2C); + else + prcm_apb0_disable( + PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_I2C); + return 0; + } + + /* set the apb clock gate and reset for twi */ + if (state) { + setbits_le32(&ccm->apb2_gate, + CLK_GATE_OPEN << (APB2_GATE_TWI_SHIFT + port)); + setbits_le32(&ccm->apb2_reset_cfg, + 1 << (APB2_RESET_TWI_SHIFT + port)); + } else { + clrbits_le32(&ccm->apb2_reset_cfg, + 1 << (APB2_RESET_TWI_SHIFT + port)); + clrbits_le32(&ccm->apb2_gate, + CLK_GATE_OPEN << (APB2_GATE_TWI_SHIFT + port)); + } + + return 0; +} +#endif diff --git a/roms/u-boot/arch/arm/mach-sunxi/clock_sun4i.c b/roms/u-boot/arch/arm/mach-sunxi/clock_sun4i.c new file mode 100644 index 000000000..57ee018ea --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/clock_sun4i.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun4i, sun5i and sun7i specific clock code + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/gpio.h> +#include <asm/arch/sys_proto.h> + +#ifdef CONFIG_SPL_BUILD +void clock_init_safe(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* Set safe defaults until PMU is configured */ + writel(AXI_DIV_1 << AXI_DIV_SHIFT | + AHB_DIV_2 << AHB_DIV_SHIFT | + APB0_DIV_1 << APB0_DIV_SHIFT | + CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT, + &ccm->cpu_ahb_apb0_cfg); + writel(PLL1_CFG_DEFAULT, &ccm->pll1_cfg); + sdelay(200); + writel(AXI_DIV_1 << AXI_DIV_SHIFT | + AHB_DIV_2 << AHB_DIV_SHIFT | + APB0_DIV_1 << APB0_DIV_SHIFT | + CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT, + &ccm->cpu_ahb_apb0_cfg); +#ifdef CONFIG_MACH_SUN7I + setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_DMA); +#endif + writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg); +#ifdef CONFIG_SUNXI_AHCI + setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_SATA); + setbits_le32(&ccm->pll6_cfg, 0x1 << CCM_PLL6_CTRL_SATA_EN_SHIFT); +#endif +} +#endif + +void clock_init_uart(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* uart clock source is apb1 */ + writel(APB1_CLK_SRC_OSC24M| + APB1_CLK_RATE_N_1| + APB1_CLK_RATE_M(1), + &ccm->apb1_clk_div_cfg); + + /* open the clock for uart */ + setbits_le32(&ccm->apb1_gate, + CLK_GATE_OPEN << (APB1_GATE_UART_SHIFT+CONFIG_CONS_INDEX - 1)); +} + +int clock_twi_onoff(int port, int state) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* set the apb clock gate for twi */ + if (state) + setbits_le32(&ccm->apb1_gate, + CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port)); + else + clrbits_le32(&ccm->apb1_gate, + CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port)); + + return 0; +} + +#ifdef CONFIG_SPL_BUILD +#define PLL1_CFG(N, K, M, P) ( 1 << CCM_PLL1_CFG_ENABLE_SHIFT | \ + 0 << CCM_PLL1_CFG_VCO_RST_SHIFT | \ + 8 << CCM_PLL1_CFG_VCO_BIAS_SHIFT | \ + 0 << CCM_PLL1_CFG_PLL4_EXCH_SHIFT | \ + 16 << CCM_PLL1_CFG_BIAS_CUR_SHIFT | \ + (P)<< CCM_PLL1_CFG_DIVP_SHIFT | \ + 2 << CCM_PLL1_CFG_LCK_TMR_SHIFT | \ + (N)<< CCM_PLL1_CFG_FACTOR_N_SHIFT | \ + (K)<< CCM_PLL1_CFG_FACTOR_K_SHIFT | \ + 0 << CCM_PLL1_CFG_SIG_DELT_PAT_IN_SHIFT | \ + 0 << CCM_PLL1_CFG_SIG_DELT_PAT_EN_SHIFT | \ + (M)<< CCM_PLL1_CFG_FACTOR_M_SHIFT) + +static struct { + u32 pll1_cfg; + unsigned int freq; +} pll1_para[] = { + /* This array must be ordered by frequency. */ + { PLL1_CFG(31, 1, 0, 0), 1488000000}, + { PLL1_CFG(30, 1, 0, 0), 1440000000}, + { PLL1_CFG(29, 1, 0, 0), 1392000000}, + { PLL1_CFG(28, 1, 0, 0), 1344000000}, + { PLL1_CFG(27, 1, 0, 0), 1296000000}, + { PLL1_CFG(26, 1, 0, 0), 1248000000}, + { PLL1_CFG(25, 1, 0, 0), 1200000000}, + { PLL1_CFG(24, 1, 0, 0), 1152000000}, + { PLL1_CFG(23, 1, 0, 0), 1104000000}, + { PLL1_CFG(22, 1, 0, 0), 1056000000}, + { PLL1_CFG(21, 1, 0, 0), 1008000000}, + { PLL1_CFG(20, 1, 0, 0), 960000000 }, + { PLL1_CFG(19, 1, 0, 0), 912000000 }, + { PLL1_CFG(16, 1, 0, 0), 768000000 }, + /* Final catchall entry 384MHz*/ + { PLL1_CFG(16, 0, 0, 0), 0 }, + +}; + +void clock_set_pll1(unsigned int hz) +{ + int i = 0; + int axi, ahb, apb0; + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* Find target frequency */ + while (pll1_para[i].freq > hz) + i++; + + hz = pll1_para[i].freq; + if (! hz) + hz = 384000000; + + /* Calculate system clock divisors */ + axi = DIV_ROUND_UP(hz, 432000000); /* Max 450MHz */ + ahb = DIV_ROUND_UP(hz/axi, 204000000); /* Max 250MHz */ + apb0 = 2; /* Max 150MHz */ + + printf("CPU: %uHz, AXI/AHB/APB: %d/%d/%d\n", hz, axi, ahb, apb0); + + /* Map divisors to register values */ + axi = axi - 1; + if (ahb > 4) + ahb = 3; + else if (ahb > 2) + ahb = 2; + else if (ahb > 1) + ahb = 1; + else + ahb = 0; + + apb0 = apb0 - 1; + + /* Switch to 24MHz clock while changing PLL1 */ + writel(AXI_DIV_1 << AXI_DIV_SHIFT | + AHB_DIV_2 << AHB_DIV_SHIFT | + APB0_DIV_1 << APB0_DIV_SHIFT | + CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT, + &ccm->cpu_ahb_apb0_cfg); + sdelay(20); + + /* Configure sys clock divisors */ + writel(axi << AXI_DIV_SHIFT | + ahb << AHB_DIV_SHIFT | + apb0 << APB0_DIV_SHIFT | + CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT, + &ccm->cpu_ahb_apb0_cfg); + + /* Configure PLL1 at the desired frequency */ + writel(pll1_para[i].pll1_cfg, &ccm->pll1_cfg); + sdelay(200); + + /* Switch CPU to PLL1 */ + writel(axi << AXI_DIV_SHIFT | + ahb << AHB_DIV_SHIFT | + apb0 << APB0_DIV_SHIFT | + CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT, + &ccm->cpu_ahb_apb0_cfg); + sdelay(20); +} +#endif + +void clock_set_pll3(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (clk == 0) { + clrbits_le32(&ccm->pll3_cfg, CCM_PLL3_CTRL_EN); + return; + } + + /* PLL3 rate = 3000000 * m */ + writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE | + CCM_PLL3_CTRL_M(clk / 3000000), &ccm->pll3_cfg); +} + +unsigned int clock_get_pll3(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->pll3_cfg); + int m = ((rval & CCM_PLL3_CTRL_M_MASK) >> CCM_PLL3_CTRL_M_SHIFT); + return 3000000 * m; +} + +unsigned int clock_get_pll5p(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->pll5_cfg); + int n = ((rval & CCM_PLL5_CTRL_N_MASK) >> CCM_PLL5_CTRL_N_SHIFT); + int k = ((rval & CCM_PLL5_CTRL_K_MASK) >> CCM_PLL5_CTRL_K_SHIFT) + 1; + int p = ((rval & CCM_PLL5_CTRL_P_MASK) >> CCM_PLL5_CTRL_P_SHIFT); + return (24000000 * n * k) >> p; +} + +unsigned int clock_get_pll6(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->pll6_cfg); + int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT); + int k = ((rval & CCM_PLL6_CTRL_K_MASK) >> CCM_PLL6_CTRL_K_SHIFT) + 1; + return 24000000 * n * k / 2; +} + +void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz) +{ + int pll = clock_get_pll5p(); + int div = 1; + + while ((pll / div) > hz) + div++; + + writel(CCM_DE_CTRL_GATE | CCM_DE_CTRL_RST | CCM_DE_CTRL_PLL5P | + CCM_DE_CTRL_M(div), clk_cfg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/clock_sun50i_h6.c b/roms/u-boot/arch/arm/mach-sunxi/clock_sun50i_h6.c new file mode 100644 index 000000000..492fc4a3f --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/clock_sun50i_h6.c @@ -0,0 +1,132 @@ +#include <common.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clock.h> +#include <asm/arch/prcm.h> + +#ifdef CONFIG_SPL_BUILD +void clock_init_safe(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* this seems to enable PLLs on H616 */ + if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) + setbits_le32(SUNXI_PRCM_BASE + 0x250, 0x10); + + clock_set_pll1(408000000); + + writel(CCM_PLL6_DEFAULT, &ccm->pll6_cfg); + while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_LOCK)) + ; + + clrsetbits_le32(&ccm->cpu_axi_cfg, CCM_CPU_AXI_APB_MASK | CCM_CPU_AXI_AXI_MASK, + CCM_CPU_AXI_DEFAULT_FACTORS); + + writel(CCM_PSI_AHB1_AHB2_DEFAULT, &ccm->psi_ahb1_ahb2_cfg); + writel(CCM_AHB3_DEFAULT, &ccm->ahb3_cfg); + writel(CCM_APB1_DEFAULT, &ccm->apb1_cfg); + + /* + * The mux and factor are set, but the clock will be enabled in + * DRAM initialization code. + */ + writel(MBUS_CLK_SRC_PLL6X2 | MBUS_CLK_M(3), &ccm->mbus_cfg); +} +#endif + +void clock_init_uart(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* uart clock source is apb2 */ + writel(APB2_CLK_SRC_OSC24M| + APB2_CLK_RATE_N_1| + APB2_CLK_RATE_M(1), + &ccm->apb2_cfg); + + /* open the clock for uart */ + setbits_le32(&ccm->uart_gate_reset, + 1 << (CONFIG_CONS_INDEX - 1)); + + /* deassert uart reset */ + setbits_le32(&ccm->uart_gate_reset, + 1 << (RESET_SHIFT + CONFIG_CONS_INDEX - 1)); +} + +#ifdef CONFIG_SPL_BUILD +void clock_set_pll1(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + u32 val; + + /* Do not support clocks < 288MHz as they need factor P */ + if (clk < 288000000) clk = 288000000; + + /* Switch to 24MHz clock while changing PLL1 */ + val = readl(&ccm->cpu_axi_cfg); + val &= ~CCM_CPU_AXI_MUX_MASK; + val |= CCM_CPU_AXI_MUX_OSC24M; + writel(val, &ccm->cpu_axi_cfg); + + /* clk = 24*n/p, p is ignored if clock is >288MHz */ + writel(CCM_PLL1_CTRL_EN | CCM_PLL1_LOCK_EN | CCM_PLL1_CLOCK_TIME_2 | +#ifdef CONFIG_MACH_SUN50I_H616 + CCM_PLL1_OUT_EN | +#endif + CCM_PLL1_CTRL_N(clk / 24000000), &ccm->pll1_cfg); + while (!(readl(&ccm->pll1_cfg) & CCM_PLL1_LOCK)) {} + + /* Switch CPU to PLL1 */ + val = readl(&ccm->cpu_axi_cfg); + val &= ~CCM_CPU_AXI_MUX_MASK; + val |= CCM_CPU_AXI_MUX_PLL_CPUX; + writel(val, &ccm->cpu_axi_cfg); +} +#endif + +unsigned int clock_get_pll6(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int m = IS_ENABLED(CONFIG_MACH_SUN50I_H6) ? 4 : 2; + + uint32_t rval = readl(&ccm->pll6_cfg); + int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT); + int div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >> + CCM_PLL6_CTRL_DIV1_SHIFT) + 1; + int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >> + CCM_PLL6_CTRL_DIV2_SHIFT) + 1; + /* The register defines PLL6-2X or PLL6-4X, not plain PLL6 */ + return 24000000 / m * n / div1 / div2; +} + +int clock_twi_onoff(int port, int state) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_prcm_reg *const prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + u32 value, *ptr; + int shift; + + value = BIT(GATE_SHIFT) | BIT (RESET_SHIFT); + + if (port == 5) { + shift = 0; + ptr = &prcm->twi_gate_reset; + } else { + shift = port; + ptr = &ccm->twi_gate_reset; + } + + /* set the apb clock gate and reset for twi */ + if (state) + setbits_le32(ptr, value << shift); + else + clrbits_le32(ptr, value << shift); + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/clock_sun6i.c b/roms/u-boot/arch/arm/mach-sunxi/clock_sun6i.c new file mode 100644 index 000000000..8e84062bd --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/clock_sun6i.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun6i specific clock code + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/prcm.h> +#include <asm/arch/sys_proto.h> +#include <linux/bitops.h> +#include <linux/delay.h> + +#ifdef CONFIG_SPL_BUILD +void clock_init_safe(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + +#if !defined(CONFIG_MACH_SUNXI_H3_H5) && !defined(CONFIG_MACH_SUN50I) + struct sunxi_prcm_reg * const prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + /* Set PLL ldo voltage without this PLL6 does not work properly */ + clrsetbits_le32(&prcm->pll_ctrl1, PRCM_PLL_CTRL_LDO_KEY_MASK, + PRCM_PLL_CTRL_LDO_KEY); + clrsetbits_le32(&prcm->pll_ctrl1, ~PRCM_PLL_CTRL_LDO_KEY_MASK, + PRCM_PLL_CTRL_LDO_DIGITAL_EN | PRCM_PLL_CTRL_LDO_ANALOG_EN | + PRCM_PLL_CTRL_EXT_OSC_EN | PRCM_PLL_CTRL_LDO_OUT_L(1140)); + clrbits_le32(&prcm->pll_ctrl1, PRCM_PLL_CTRL_LDO_KEY_MASK); +#endif + +#if defined(CONFIG_MACH_SUN8I_R40) || defined(CONFIG_MACH_SUN50I) + /* Set PLL lock enable bits and switch to old lock mode */ + writel(GENMASK(12, 0), &ccm->pll_lock_ctrl); +#endif + + clock_set_pll1(408000000); + + writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg); + while (!(readl(&ccm->pll6_cfg) & CCM_PLL6_CTRL_LOCK)) + ; + + writel(AHB1_ABP1_DIV_DEFAULT, &ccm->ahb1_apb1_div); + + writel(MBUS_CLK_DEFAULT, &ccm->mbus0_clk_cfg); + if (IS_ENABLED(CONFIG_MACH_SUN6I)) + writel(MBUS_CLK_DEFAULT, &ccm->mbus1_clk_cfg); + +#if defined(CONFIG_MACH_SUN8I_R40) && defined(CONFIG_SUNXI_AHCI) + setbits_le32(&ccm->sata_pll_cfg, CCM_SATA_PLL_DEFAULT); + setbits_le32(&ccm->ahb_reset0_cfg, 0x1 << AHB_GATE_OFFSET_SATA); + setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_SATA); + setbits_le32(&ccm->sata_clk_cfg, CCM_SATA_CTRL_ENABLE); +#endif +} +#endif + +void clock_init_sec(void) +{ +#ifdef CONFIG_MACH_SUNXI_H3_H5 + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_prcm_reg * const prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + setbits_le32(&ccm->ccu_sec_switch, + CCM_SEC_SWITCH_MBUS_NONSEC | + CCM_SEC_SWITCH_BUS_NONSEC | + CCM_SEC_SWITCH_PLL_NONSEC); + setbits_le32(&prcm->prcm_sec_switch, + PRCM_SEC_SWITCH_APB0_CLK_NONSEC | + PRCM_SEC_SWITCH_PLL_CFG_NONSEC | + PRCM_SEC_SWITCH_PWR_GATE_NONSEC); +#endif +} + +void clock_init_uart(void) +{ +#if CONFIG_CONS_INDEX < 5 + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* uart clock source is apb2 */ + writel(APB2_CLK_SRC_OSC24M| + APB2_CLK_RATE_N_1| + APB2_CLK_RATE_M(1), + &ccm->apb2_div); + + /* open the clock for uart */ + setbits_le32(&ccm->apb2_gate, + CLK_GATE_OPEN << (APB2_GATE_UART_SHIFT + + CONFIG_CONS_INDEX - 1)); + + /* deassert uart reset */ + setbits_le32(&ccm->apb2_reset_cfg, + 1 << (APB2_RESET_UART_SHIFT + + CONFIG_CONS_INDEX - 1)); +#else + /* enable R_PIO and R_UART clocks, and de-assert resets */ + prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_UART); +#endif +} + +#ifdef CONFIG_SPL_BUILD +void clock_set_pll1(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int p = 0; + int k = 1; + int m = 1; + + if (clk > 1152000000) { + k = 2; + } else if (clk > 768000000) { + k = 4; + m = 2; + } + + /* Switch to 24MHz clock while changing PLL1 */ + writel(AXI_DIV_3 << AXI_DIV_SHIFT | + ATB_DIV_2 << ATB_DIV_SHIFT | + CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT, + &ccm->cpu_axi_cfg); + + /* + * sun6i: PLL1 rate = ((24000000 * n * k) >> 0) / m (p is ignored) + * sun8i: PLL1 rate = ((24000000 * n * k) >> p) / m + */ + writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | + CCM_PLL1_CTRL_N(clk / (24000000 * k / m)) | + CCM_PLL1_CTRL_K(k) | CCM_PLL1_CTRL_M(m), &ccm->pll1_cfg); + sdelay(200); + + /* Switch CPU to PLL1 */ + writel(AXI_DIV_3 << AXI_DIV_SHIFT | + ATB_DIV_2 << ATB_DIV_SHIFT | + CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT, + &ccm->cpu_axi_cfg); +} +#endif + +void clock_set_pll3(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; +#ifdef CONFIG_SUNXI_DE2 + const int m = 4; /* 6 MHz steps to allow higher frequency for DE2 */ +#else + const int m = 8; /* 3 MHz steps just like sun4i, sun5i and sun7i */ +#endif + + if (clk == 0) { + clrbits_le32(&ccm->pll3_cfg, CCM_PLL3_CTRL_EN); + return; + } + + /* PLL3 rate = 24000000 * n / m */ + writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE | + CCM_PLL3_CTRL_N(clk / (24000000 / m)) | CCM_PLL3_CTRL_M(m), + &ccm->pll3_cfg); +} + +#ifdef CONFIG_SUNXI_DE2 +void clock_set_pll3_factors(int m, int n) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* PLL3 rate = 24000000 * n / m */ + writel(CCM_PLL3_CTRL_EN | CCM_PLL3_CTRL_INTEGER_MODE | + CCM_PLL3_CTRL_N(n) | CCM_PLL3_CTRL_M(m), + &ccm->pll3_cfg); + + while (!(readl(&ccm->pll3_cfg) & CCM_PLL3_CTRL_LOCK)) + ; +} +#endif + +void clock_set_pll5(unsigned int clk, bool sigma_delta_enable) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int max_n = 32; + int k = 1, m = 2; + +#ifdef CONFIG_MACH_SUNXI_H3_H5 + clrsetbits_le32(&ccm->pll5_tuning_cfg, CCM_PLL5_TUN_LOCK_TIME_MASK | + CCM_PLL5_TUN_INIT_FREQ_MASK, + CCM_PLL5_TUN_LOCK_TIME(2) | CCM_PLL5_TUN_INIT_FREQ(16)); +#endif + + if (sigma_delta_enable) + writel(CCM_PLL5_PATTERN, &ccm->pll5_pattern_cfg); + + /* PLL5 rate = 24000000 * n * k / m */ + if (clk > 24000000 * k * max_n / m) { + m = 1; + if (clk > 24000000 * k * max_n / m) + k = 2; + } + writel(CCM_PLL5_CTRL_EN | + (sigma_delta_enable ? CCM_PLL5_CTRL_SIGMA_DELTA_EN : 0) | + CCM_PLL5_CTRL_UPD | + CCM_PLL5_CTRL_N(clk / (24000000 * k / m)) | + CCM_PLL5_CTRL_K(k) | CCM_PLL5_CTRL_M(m), &ccm->pll5_cfg); + + udelay(5500); +} + +#ifdef CONFIG_MACH_SUN6I +void clock_set_mipi_pll(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + unsigned int k, m, n, value, diff; + unsigned best_k = 0, best_m = 0, best_n = 0, best_diff = 0xffffffff; + unsigned int src = clock_get_pll3(); + + /* All calculations are in KHz to avoid overflows */ + clk /= 1000; + src /= 1000; + + /* Pick the closest lower clock */ + for (k = 1; k <= 4; k++) { + for (m = 1; m <= 16; m++) { + for (n = 1; n <= 16; n++) { + value = src * n * k / m; + if (value > clk) + continue; + + diff = clk - value; + if (diff < best_diff) { + best_diff = diff; + best_k = k; + best_m = m; + best_n = n; + } + if (diff == 0) + goto done; + } + } + } + +done: + writel(CCM_MIPI_PLL_CTRL_EN | CCM_MIPI_PLL_CTRL_LDO_EN | + CCM_MIPI_PLL_CTRL_N(best_n) | CCM_MIPI_PLL_CTRL_K(best_k) | + CCM_MIPI_PLL_CTRL_M(best_m), &ccm->mipi_pll_cfg); +} +#endif + +#ifdef CONFIG_SUNXI_DE2 +void clock_set_pll10(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int m = 2; /* 12 MHz steps */ + + if (clk == 0) { + clrbits_le32(&ccm->pll10_cfg, CCM_PLL10_CTRL_EN); + return; + } + + /* PLL10 rate = 24000000 * n / m */ + writel(CCM_PLL10_CTRL_EN | CCM_PLL10_CTRL_INTEGER_MODE | + CCM_PLL10_CTRL_N(clk / (24000000 / m)) | CCM_PLL10_CTRL_M(m), + &ccm->pll10_cfg); + + while (!(readl(&ccm->pll10_cfg) & CCM_PLL10_CTRL_LOCK)) + ; +} +#endif + +#if defined(CONFIG_MACH_SUN8I_A33) || \ + defined(CONFIG_MACH_SUN8I_R40) || \ + defined(CONFIG_MACH_SUN50I) +void clock_set_pll11(unsigned int clk, bool sigma_delta_enable) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (sigma_delta_enable) + writel(CCM_PLL11_PATTERN, &ccm->pll11_pattern_cfg0); + + writel(CCM_PLL11_CTRL_EN | CCM_PLL11_CTRL_UPD | + (sigma_delta_enable ? CCM_PLL11_CTRL_SIGMA_DELTA_EN : 0) | + CCM_PLL11_CTRL_N(clk / 24000000), &ccm->pll11_cfg); + + while (readl(&ccm->pll11_cfg) & CCM_PLL11_CTRL_UPD) + ; +} +#endif + +unsigned int clock_get_pll3(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->pll3_cfg); + int n = ((rval & CCM_PLL3_CTRL_N_MASK) >> CCM_PLL3_CTRL_N_SHIFT) + 1; + int m = ((rval & CCM_PLL3_CTRL_M_MASK) >> CCM_PLL3_CTRL_M_SHIFT) + 1; + + /* Multiply by 1000 after dividing by m to avoid integer overflows */ + return (24000 * n / m) * 1000; +} + +unsigned int clock_get_pll6(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->pll6_cfg); + int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT) + 1; + int k = ((rval & CCM_PLL6_CTRL_K_MASK) >> CCM_PLL6_CTRL_K_SHIFT) + 1; + return 24000000 * n * k / 2; +} + +unsigned int clock_get_mipi_pll(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->mipi_pll_cfg); + unsigned int n = ((rval & CCM_MIPI_PLL_CTRL_N_MASK) >> CCM_MIPI_PLL_CTRL_N_SHIFT) + 1; + unsigned int k = ((rval & CCM_MIPI_PLL_CTRL_K_MASK) >> CCM_MIPI_PLL_CTRL_K_SHIFT) + 1; + unsigned int m = ((rval & CCM_MIPI_PLL_CTRL_M_MASK) >> CCM_MIPI_PLL_CTRL_M_SHIFT) + 1; + unsigned int src = clock_get_pll3(); + + /* Multiply by 1000 after dividing by m to avoid integer overflows */ + return ((src / 1000) * n * k / m) * 1000; +} + +void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz) +{ + int pll = clock_get_pll6() * 2; + int div = 1; + + while ((pll / div) > hz) + div++; + + writel(CCM_DE_CTRL_GATE | CCM_DE_CTRL_PLL6_2X | CCM_DE_CTRL_M(div), + clk_cfg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/clock_sun8i_a83t.c b/roms/u-boot/arch/arm/mach-sunxi/clock_sun8i_a83t.c new file mode 100644 index 000000000..31e428152 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/clock_sun8i_a83t.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * A83 specific clock code + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/prcm.h> +#include <asm/arch/sys_proto.h> +#include <linux/delay.h> + +#ifdef CONFIG_SPL_BUILD +void clock_init_safe(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + clock_set_pll1(408000000); + /* enable pll_hsic, default is 480M */ + writel(PLL8_CFG_DEFAULT, &ccm->pll8_cfg); + writel(readl(&ccm->pll8_cfg) | (0x1 << 31), &ccm->pll8_cfg); + while (!(readl(&ccm->pll_stable_status) & (1 << 8))) {} + + /* switch to default 24MHz before changing to hsic */ + writel(0x0, &ccm->cci400_cfg); + sdelay(50); + writel(CCM_CCI400_CLK_SEL_HSIC, &ccm->cci400_cfg); + sdelay(100); + + /* switch before changing pll6 */ + clrsetbits_le32(&ccm->ahb1_apb1_div, AHB1_CLK_SRC_MASK, + AHB1_CLK_SRC_OSC24M); + writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg); + while (!(readl(&ccm->pll_stable_status) & (1 << 6))) {} + + writel(AHB1_ABP1_DIV_DEFAULT, &ccm->ahb1_apb1_div); + writel(CCM_MBUS_RESET_RESET, &ccm->mbus_reset); + writel(MBUS_CLK_DEFAULT, &ccm->mbus_clk_cfg); + + /* timestamp */ + writel(1, 0x01720000); +} +#endif + +void clock_init_uart(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* uart clock source is apb2 */ + writel(APB2_CLK_SRC_OSC24M| + APB2_CLK_RATE_N_1| + APB2_CLK_RATE_M(1), + &ccm->apb2_div); + + /* open the clock for uart */ + setbits_le32(&ccm->apb2_gate, + CLK_GATE_OPEN << (APB2_GATE_UART_SHIFT + + CONFIG_CONS_INDEX - 1)); + + /* deassert uart reset */ + setbits_le32(&ccm->apb2_reset_cfg, + 1 << (APB2_RESET_UART_SHIFT + + CONFIG_CONS_INDEX - 1)); +} + +#ifdef CONFIG_SPL_BUILD +void clock_set_pll1(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int p = 0; + + /* Switch to 24MHz clock while changing PLL1 */ + writel(AXI_DIV_2 << AXI0_DIV_SHIFT | + AXI_DIV_2 << AXI1_DIV_SHIFT | + CPU_CLK_SRC_OSC24M << C0_CPUX_CLK_SRC_SHIFT | + CPU_CLK_SRC_OSC24M << C1_CPUX_CLK_SRC_SHIFT, + &ccm->cpu_axi_cfg); + + /* clk = 24*n/p, p is ignored if clock is >288MHz */ + writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | CMM_PLL1_CLOCK_TIME_2 | + CCM_PLL1_CTRL_N(clk / 24000000), + &ccm->pll1_c0_cfg); + while (!(readl(&ccm->pll_stable_status) & 0x01)) {} + + writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | CMM_PLL1_CLOCK_TIME_2 | + CCM_PLL1_CTRL_N(clk / (24000000)), + &ccm->pll1_c1_cfg); + while (!(readl(&ccm->pll_stable_status) & 0x02)) {} + + /* Switch CPU to PLL1 */ + writel(AXI_DIV_2 << AXI0_DIV_SHIFT | + AXI_DIV_2 << AXI1_DIV_SHIFT | + CPU_CLK_SRC_PLL1 << C0_CPUX_CLK_SRC_SHIFT | + CPU_CLK_SRC_PLL1 << C1_CPUX_CLK_SRC_SHIFT, + &ccm->cpu_axi_cfg); +} +#endif + +void clock_set_pll5(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + unsigned int div1 = 0, div2 = 0; + + /* A83T PLL5 DDR rate = 24000000 * (n+1)/(div1+1)/(div2+1) */ + writel(CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD | + CCM_PLL5_CTRL_N(clk / (24000000)) | + div2 << CCM_PLL5_DIV2_SHIFT | + div1 << CCM_PLL5_DIV1_SHIFT, &ccm->pll5_cfg); + + udelay(5500); +} + + +unsigned int clock_get_pll6(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + uint32_t rval = readl(&ccm->pll6_cfg); + int n = ((rval & CCM_PLL6_CTRL_N_MASK) >> CCM_PLL6_CTRL_N_SHIFT); + int div1 = ((rval & CCM_PLL6_CTRL_DIV1_MASK) >> + CCM_PLL6_CTRL_DIV1_SHIFT) + 1; + int div2 = ((rval & CCM_PLL6_CTRL_DIV2_MASK) >> + CCM_PLL6_CTRL_DIV2_SHIFT) + 1; + return 24000000 * n / div1 / div2; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/clock_sun9i.c b/roms/u-boot/arch/arm/mach-sunxi/clock_sun9i.c new file mode 100644 index 000000000..8ba4802f3 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/clock_sun9i.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * sun9i specific clock code + * + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + * + * (C) Copyright 2016 Theobroma Systems Design und Consulting GmbH + * Philipp Tomsich <philipp.tomsich@theobroma-systems.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/prcm.h> +#include <asm/arch/sys_proto.h> + + +#ifdef CONFIG_SPL_BUILD + +void clock_init_safe(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* Set up PLL12 (peripheral 1) */ + clock_set_pll12(1200000000); + + /* Set up PLL1 (cluster 0) and PLL2 (cluster 1) */ + clock_set_pll1(408000000); + clock_set_pll2(408000000); + + /* Set up PLL4 (peripheral 0) */ + clock_set_pll4(960000000); + + /* Set up dividers for AXI0 and APB0 on cluster 0: PLL1 / 2 = 204MHz */ + writel(C0_CFG_AXI0_CLK_DIV_RATIO(2) | + C0_CFG_APB0_CLK_DIV_RATIO(2), &ccm->c0_cfg); + + /* AHB0: 120 MHz (PLL_PERIPH0 / 8) */ + writel(AHBx_SRC_PLL_PERIPH0 | AHBx_CLK_DIV_RATIO(8), + &ccm->ahb0_cfg); + /* AHB1: 240 MHz (PLL_PERIPH0 / 4) */ + writel(AHBx_SRC_PLL_PERIPH0 | AHBx_CLK_DIV_RATIO(4), + &ccm->ahb1_cfg); + /* AHB2: 120 MHz (PLL_PERIPH0 / 8) */ + writel(AHBx_SRC_PLL_PERIPH0 | AHBx_CLK_DIV_RATIO(8), + &ccm->ahb2_cfg); + /* APB0: 120 MHz (PLL_PERIPH0 / 8) */ + writel(APB0_SRC_PLL_PERIPH0 | APB0_CLK_DIV_RATIO(8), + &ccm->apb0_cfg); + + /* GTBUS: 400MHz (PERIPH0 div 3) */ + writel(GTBUS_SRC_PLL_PERIPH1 | GTBUS_CLK_DIV_RATIO(3), + &ccm->gtbus_cfg); + /* CCI400: 480MHz (PERIPH1 div 2) */ + writel(CCI400_SRC_PLL_PERIPH0 | CCI400_CLK_DIV_RATIO(2), + &ccm->cci400_cfg); + + /* Deassert DMA reset and open clock gating for DMA */ + setbits_le32(&ccm->ahb_reset1_cfg, (1 << 24)); + setbits_le32(&ccm->apb1_gate, (1 << 24)); + + /* set enable-bit in TSTAMP_CTRL_REG */ + writel(1, 0x01720000); +} +#endif + +void clock_init_uart(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* open the clock for uart */ + setbits_le32(&ccm->apb1_gate, + CLK_GATE_OPEN << (APB1_GATE_UART_SHIFT + + CONFIG_CONS_INDEX - 1)); + /* deassert uart reset */ + setbits_le32(&ccm->apb1_reset_cfg, + 1 << (APB1_RESET_UART_SHIFT + + CONFIG_CONS_INDEX - 1)); +} + +#ifdef CONFIG_SPL_BUILD +void clock_set_pll1(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int p = 0; + + /* Switch cluster 0 to 24MHz clock while changing PLL1 */ + clrsetbits_le32(&ccm->cpu_clk_source, C0_CPUX_CLK_SRC_MASK, + C0_CPUX_CLK_SRC_OSC24M); + + writel(CCM_PLL1_CTRL_EN | CCM_PLL1_CTRL_P(p) | + CCM_PLL1_CLOCK_TIME_2 | + CCM_PLL1_CTRL_N(clk / 24000000), + &ccm->pll1_c0_cfg); + /* + * Don't bother with the stable-time registers, as it doesn't + * wait until the PLL is stable. Note, that even Allwinner + * just uses a delay loop (or rather the AVS timer) for this + * instead of the PLL_STABLE_STATUS register. + */ + sdelay(2000); + + /* Switch cluster 0 back to PLL1 */ + clrsetbits_le32(&ccm->cpu_clk_source, C0_CPUX_CLK_SRC_MASK, + C0_CPUX_CLK_SRC_PLL1); +} + +void clock_set_pll2(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int p = 0; + + /* Switch cluster 1 to 24MHz clock while changing PLL2 */ + clrsetbits_le32(&ccm->cpu_clk_source, C1_CPUX_CLK_SRC_MASK, + C1_CPUX_CLK_SRC_OSC24M); + + writel(CCM_PLL2_CTRL_EN | CCM_PLL2_CTRL_P(p) | + CCM_PLL2_CLOCK_TIME_2 | CCM_PLL2_CTRL_N(clk / 24000000), + &ccm->pll2_c1_cfg); + + sdelay(2000); + + /* Switch cluster 1 back to PLL2 */ + clrsetbits_le32(&ccm->cpu_clk_source, C1_CPUX_CLK_SRC_MASK, + C1_CPUX_CLK_SRC_PLL2); +} + +void clock_set_pll6(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int p = 0; + + writel(CCM_PLL6_CTRL_EN | CCM_PLL6_CFG_UPDATE | CCM_PLL6_CTRL_P(p) + | CCM_PLL6_CTRL_N(clk / 24000000), + &ccm->pll6_ddr_cfg); + do { } while (!(readl(&ccm->pll_stable_status) & PLL_DDR_STATUS)); + + sdelay(2000); +} + +void clock_set_pll12(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (readl(&ccm->pll12_periph1_cfg) & CCM_PLL12_CTRL_EN) + return; + + writel(CCM_PLL12_CTRL_EN | CCM_PLL12_CTRL_N(clk / 24000000), + &ccm->pll12_periph1_cfg); + + sdelay(2000); +} + + +void clock_set_pll4(unsigned int clk) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + writel(CCM_PLL4_CTRL_EN | CCM_PLL4_CTRL_N(clk / 24000000), + &ccm->pll4_periph0_cfg); + + sdelay(2000); +} +#endif + +int clock_twi_onoff(int port, int state) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + if (port > 4) + return -1; + + /* set the apb reset and clock gate for twi */ + if (state) { + setbits_le32(&ccm->apb1_gate, + CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port)); + setbits_le32(&ccm->apb1_reset_cfg, + 1 << (APB1_RESET_TWI_SHIFT + port)); + } else { + clrbits_le32(&ccm->apb1_reset_cfg, + 1 << (APB1_RESET_TWI_SHIFT + port)); + clrbits_le32(&ccm->apb1_gate, + CLK_GATE_OPEN << (APB1_GATE_TWI_SHIFT + port)); + } + + return 0; +} + +unsigned int clock_get_pll4_periph0(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + uint32_t rval = readl(&ccm->pll4_periph0_cfg); + int n = ((rval & CCM_PLL4_CTRL_N_MASK) >> CCM_PLL4_CTRL_N_SHIFT); + int p = ((rval & CCM_PLL4_CTRL_P_MASK) >> CCM_PLL4_CTRL_P_SHIFT); + int m = ((rval & CCM_PLL4_CTRL_M_MASK) >> CCM_PLL4_CTRL_M_SHIFT) + 1; + const int k = 1; + + return ((24000000 * n * k) >> p) / m; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/cpu_info.c b/roms/u-boot/arch/arm/mach-sunxi/cpu_info.c new file mode 100644 index 000000000..ba33ef243 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/cpu_info.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clock.h> +#include <axp_pmic.h> +#include <errno.h> + +#ifdef CONFIG_MACH_SUN6I +int sunxi_get_ss_bonding_id(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + static int bonding_id = -1; + + if (bonding_id != -1) + return bonding_id; + + /* Enable Security System */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_SS); + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_SS); + + bonding_id = readl(SUNXI_SS_BASE); + bonding_id = (bonding_id >> 16) & 0x7; + + /* Disable Security System again */ + clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_SS); + clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_SS); + + return bonding_id; +} +#endif + +#ifdef CONFIG_MACH_SUN8I +uint sunxi_get_sram_id(void) +{ + uint id; + + /* Unlock sram info reg, read it, relock */ + setbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15)); + id = readl(SUNXI_SRAMC_BASE + 0x24) >> 16; + clrbits_le32(SUNXI_SRAMC_BASE + 0x24, (1 << 15)); + + return id; +} +#endif + +#ifdef CONFIG_DISPLAY_CPUINFO +int print_cpuinfo(void) +{ +#ifdef CONFIG_MACH_SUN4I + puts("CPU: Allwinner A10 (SUN4I)\n"); +#elif defined CONFIG_MACH_SUN5I + u32 val = readl(SUNXI_SID_BASE + 0x08); + switch ((val >> 12) & 0xf) { + case 0: puts("CPU: Allwinner A12 (SUN5I)\n"); break; + case 3: puts("CPU: Allwinner A13 (SUN5I)\n"); break; + case 7: puts("CPU: Allwinner A10s (SUN5I)\n"); break; + default: puts("CPU: Allwinner A1X (SUN5I)\n"); + } +#elif defined CONFIG_MACH_SUN6I + switch (sunxi_get_ss_bonding_id()) { + case SUNXI_SS_BOND_ID_A31: + puts("CPU: Allwinner A31 (SUN6I)\n"); + break; + case SUNXI_SS_BOND_ID_A31S: + puts("CPU: Allwinner A31s (SUN6I)\n"); + break; + default: + printf("CPU: Allwinner A31? (SUN6I, id: %d)\n", + sunxi_get_ss_bonding_id()); + } +#elif defined CONFIG_MACH_SUN7I + puts("CPU: Allwinner A20 (SUN7I)\n"); +#elif defined CONFIG_MACH_SUN8I_A23 + printf("CPU: Allwinner A23 (SUN8I %04x)\n", sunxi_get_sram_id()); +#elif defined CONFIG_MACH_SUN8I_A33 + printf("CPU: Allwinner A33 (SUN8I %04x)\n", sunxi_get_sram_id()); +#elif defined CONFIG_MACH_SUN8I_A83T + printf("CPU: Allwinner A83T (SUN8I %04x)\n", sunxi_get_sram_id()); +#elif defined CONFIG_MACH_SUN8I_H3 + printf("CPU: Allwinner H3 (SUN8I %04x)\n", sunxi_get_sram_id()); +#elif defined CONFIG_MACH_SUN8I_R40 + printf("CPU: Allwinner R40 (SUN8I %04x)\n", sunxi_get_sram_id()); +#elif defined CONFIG_MACH_SUN8I_V3S + printf("CPU: Allwinner V3s (SUN8I %04x)\n", sunxi_get_sram_id()); +#elif defined CONFIG_MACH_SUN9I + puts("CPU: Allwinner A80 (SUN9I)\n"); +#elif defined CONFIG_MACH_SUN50I + puts("CPU: Allwinner A64 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN50I_H5 + puts("CPU: Allwinner H5 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN50I_H6 + puts("CPU: Allwinner H6 (SUN50I)\n"); +#elif defined CONFIG_MACH_SUN50I_H616 + puts("CPU: Allwinner H616 (SUN50I)\n"); +#else +#warning Please update cpu_info.c with correct CPU information + puts("CPU: SUNXI Family\n"); +#endif + return 0; +} +#endif + +#ifdef CONFIG_MACH_SUN8I_H3 + +#define SIDC_PRCTL 0x40 +#define SIDC_RDKEY 0x60 + +#define SIDC_OP_LOCK 0xAC + +uint32_t sun8i_efuse_read(uint32_t offset) +{ + uint32_t reg_val; + + reg_val = readl(SUNXI_SIDC_BASE + SIDC_PRCTL); + reg_val &= ~(((0x1ff) << 16) | 0x3); + reg_val |= (offset << 16); + writel(reg_val, SUNXI_SIDC_BASE + SIDC_PRCTL); + + reg_val &= ~(((0xff) << 8) | 0x3); + reg_val |= (SIDC_OP_LOCK << 8) | 0x2; + writel(reg_val, SUNXI_SIDC_BASE + SIDC_PRCTL); + + while (readl(SUNXI_SIDC_BASE + SIDC_PRCTL) & 0x2); + + reg_val &= ~(((0x1ff) << 16) | ((0xff) << 8) | 0x3); + writel(reg_val, SUNXI_SIDC_BASE + SIDC_PRCTL); + + reg_val = readl(SUNXI_SIDC_BASE + SIDC_RDKEY); + return reg_val; +} +#endif + +int sunxi_get_sid(unsigned int *sid) +{ +#ifdef CONFIG_AXP221_POWER + return axp_get_sid(sid); +#elif defined CONFIG_MACH_SUN8I_H3 + /* + * H3 SID controller has a bug, which makes the initial value of + * SUNXI_SID_BASE at boot wrong. + * Read the value directly from SID controller, in order to get + * the correct value, and also refresh the wrong value at + * SUNXI_SID_BASE. + */ + int i; + + for (i = 0; i< 4; i++) + sid[i] = sun8i_efuse_read(i * 4); + + return 0; +#elif defined SUNXI_SID_BASE + int i; + + for (i = 0; i< 4; i++) + sid[i] = readl((ulong)SUNXI_SID_BASE + 4 * i); + + return 0; +#else + return -ENODEV; +#endif +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_helpers.c b/roms/u-boot/arch/arm/mach-sunxi/dram_helpers.c new file mode 100644 index 000000000..520b597fc --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_helpers.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DRAM init helper functions + * + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + */ + +#include <common.h> +#include <time.h> +#include <asm/barriers.h> +#include <asm/io.h> +#include <asm/arch/dram.h> + +/* + * Wait up to 1s for value to be set in given part of reg. + */ +void mctl_await_completion(u32 *reg, u32 mask, u32 val) +{ + unsigned long tmo = timer_get_us() + 1000000; + + while ((readl(reg) & mask) != val) { + if (timer_get_us() > tmo) + panic("Timeout initialising DRAM\n"); + } +} + +/* + * Test if memory at offset offset matches memory at begin of DRAM + */ +bool mctl_mem_matches(u32 offset) +{ + /* Try to write different values to RAM at two addresses */ + writel(0, CONFIG_SYS_SDRAM_BASE); + writel(0xaa55aa55, (ulong)CONFIG_SYS_SDRAM_BASE + offset); + dsb(); + /* Check if the same value is actually observed when reading back */ + return readl(CONFIG_SYS_SDRAM_BASE) == + readl((ulong)CONFIG_SYS_SDRAM_BASE + offset); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun4i.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun4i.c new file mode 100644 index 000000000..76d698214 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun4i.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sunxi DRAM controller initialization + * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net> + * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net> + * + * Based on sun4i Linux kernel sources mach-sunxi/pm/standby/dram*.c + * and earlier U-Boot Allwinner A10 SPL work + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing <bergxing@allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + */ + +/* + * Unfortunately the only documentation we have on the sun7i DRAM + * controller is Allwinner boot0 + boot1 code, and that code uses + * magic numbers & shifts with no explanations. Hence this code is + * rather undocumented and full of magic. + */ + +#include <common.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/timer.h> +#include <asm/arch/sys_proto.h> +#include <linux/delay.h> + +#define CPU_CFG_CHIP_VER(n) ((n) << 6) +#define CPU_CFG_CHIP_VER_MASK CPU_CFG_CHIP_VER(0x3) +#define CPU_CFG_CHIP_REV_A 0x0 +#define CPU_CFG_CHIP_REV_C1 0x1 +#define CPU_CFG_CHIP_REV_C2 0x2 +#define CPU_CFG_CHIP_REV_B 0x3 + +/* + * Wait up to 1s for mask to be clear in given reg. + */ +static inline void await_bits_clear(u32 *reg, u32 mask) +{ + mctl_await_completion(reg, mask, 0); +} + +/* + * Wait up to 1s for mask to be set in given reg. + */ +static inline void await_bits_set(u32 *reg, u32 mask) +{ + mctl_await_completion(reg, mask, mask); +} + +/* + * This performs the external DRAM reset by driving the RESET pin low and + * then high again. According to the DDR3 spec, the RESET pin needs to be + * kept low for at least 200 us. + */ +static void mctl_ddr3_reset(void) +{ + struct sunxi_dram_reg *dram = + (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + +#ifdef CONFIG_MACH_SUN4I + struct sunxi_timer_reg *timer = + (struct sunxi_timer_reg *)SUNXI_TIMER_BASE; + u32 reg_val; + + writel(0, &timer->cpu_cfg); + reg_val = readl(&timer->cpu_cfg); + + if ((reg_val & CPU_CFG_CHIP_VER_MASK) != + CPU_CFG_CHIP_VER(CPU_CFG_CHIP_REV_A)) { + setbits_le32(&dram->mcr, DRAM_MCR_RESET); + udelay(200); + clrbits_le32(&dram->mcr, DRAM_MCR_RESET); + } else +#endif + { + clrbits_le32(&dram->mcr, DRAM_MCR_RESET); + udelay(200); + setbits_le32(&dram->mcr, DRAM_MCR_RESET); + } + /* After the RESET pin is de-asserted, the DDR3 spec requires to wait + * for additional 500 us before driving the CKE pin (Clock Enable) + * high. The duration of this delay can be configured in the SDR_IDCR + * (Initialization Delay Configuration Register) and applied + * automatically by the DRAM controller during the DDR3 initialization + * step. But SDR_IDCR has limited range on sun4i/sun5i hardware and + * can't provide sufficient delay at DRAM clock frequencies higher than + * 524 MHz (while Allwinner A13 supports DRAM clock frequency up to + * 533 MHz according to the datasheet). Additionally, there is no + * official documentation for the SDR_IDCR register anywhere, and + * there is always a chance that we are interpreting it wrong. + * Better be safe than sorry, so add an explicit delay here. */ + udelay(500); +} + +static void mctl_set_drive(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + +#ifdef CONFIG_MACH_SUN7I + clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3) | (0x3 << 28), +#else + clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3), +#endif + DRAM_MCR_MODE_EN(0x3) | + 0xffc); +} + +static void mctl_itm_disable(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrsetbits_le32(&dram->ccr, DRAM_CCR_INIT, DRAM_CCR_ITM_OFF); +} + +static void mctl_itm_enable(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF); +} + +static void mctl_itm_reset(void) +{ + mctl_itm_disable(); + udelay(1); /* ITM reset needs a bit of delay */ + mctl_itm_enable(); + udelay(1); +} + +static void mctl_enable_dll0(u32 phase) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, + ((phase >> 16) & 0x3f) << 6); + clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET, DRAM_DLLCR_DISABLE); + udelay(2); + + clrbits_le32(&dram->dllcr[0], DRAM_DLLCR_NRESET | DRAM_DLLCR_DISABLE); + udelay(22); + + clrsetbits_le32(&dram->dllcr[0], DRAM_DLLCR_DISABLE, DRAM_DLLCR_NRESET); + udelay(22); +} + +/* Get the number of DDR byte lanes */ +static u32 mctl_get_number_of_lanes(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + if ((readl(&dram->dcr) & DRAM_DCR_BUS_WIDTH_MASK) == + DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) + return 4; + else + return 2; +} + +/* + * Note: This differs from pm/standby in that it checks the bus width + */ +static void mctl_enable_dllx(u32 phase) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 i, number_of_lanes; + + number_of_lanes = mctl_get_number_of_lanes(); + + for (i = 1; i <= number_of_lanes; i++) { + clrsetbits_le32(&dram->dllcr[i], 0xf << 14, + (phase & 0xf) << 14); + clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET, + DRAM_DLLCR_DISABLE); + phase >>= 4; + } + udelay(2); + + for (i = 1; i <= number_of_lanes; i++) + clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET | + DRAM_DLLCR_DISABLE); + udelay(22); + + for (i = 1; i <= number_of_lanes; i++) + clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE, + DRAM_DLLCR_NRESET); + udelay(22); +} + +static u32 hpcr_value[32] = { +#ifdef CONFIG_MACH_SUN5I + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0, + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0x0301, 0 +#endif +#ifdef CONFIG_MACH_SUN4I + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x5031, + 0x1035, 0x0731, 0x1031, 0x0735, + 0x1035, 0x1031, 0x0731, 0x1035, + 0x1031, 0x0301, 0x0301, 0x0731 +#endif +#ifdef CONFIG_MACH_SUN7I + 0x0301, 0x0301, 0x0301, 0x0301, + 0x0301, 0x0301, 0x0301, 0x0301, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0x1031, 0x1031, 0x0735, 0x1035, + 0x1035, 0x0731, 0x1031, 0x0735, + 0x1035, 0x1031, 0x0731, 0x1035, + 0x0001, 0x1031, 0, 0x1031 + /* last row differs from boot0 source table + * 0x1031, 0x0301, 0x0301, 0x0731 + * but boot0 code skips #28 and #30, and sets #29 and #31 to the + * value from #28 entry (0x1031) + */ +#endif +}; + +static void mctl_configure_hostport(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 i; + + for (i = 0; i < 32; i++) + writel(hpcr_value[i], &dram->hpcr[i]); +} + +static void mctl_setup_dram_clock(u32 clk, u32 mbus_clk) +{ + u32 reg_val; + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + u32 pll5p_clk, pll6x_clk; + u32 pll5p_div, pll6x_div; + u32 pll5p_rate, pll6x_rate; + + /* setup DRAM PLL */ + reg_val = readl(&ccm->pll5_cfg); + reg_val &= ~CCM_PLL5_CTRL_M_MASK; /* set M to 0 (x1) */ + reg_val &= ~CCM_PLL5_CTRL_K_MASK; /* set K to 0 (x1) */ + reg_val &= ~CCM_PLL5_CTRL_N_MASK; /* set N to 0 (x0) */ + reg_val &= ~CCM_PLL5_CTRL_P_MASK; /* set P to 0 (x1) */ +#ifdef CONFIG_OLD_SUNXI_KERNEL_COMPAT + /* Old kernels are hardcoded to P=1 (divide by 2) */ + reg_val |= CCM_PLL5_CTRL_P(1); +#endif + if (clk >= 540 && clk < 552) { + /* dram = 540MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(15)); + } else if (clk >= 512 && clk < 528) { + /* dram = 512MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(4)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(16)); + } else if (clk >= 496 && clk < 504) { + /* dram = 496MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(31)); + } else if (clk >= 468 && clk < 480) { + /* dram = 468MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(13)); + } else if (clk >= 396 && clk < 408) { + /* dram = 396MHz */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(11)); + } else { + /* any other frequency that is a multiple of 24 */ + reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); + reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); + reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(clk / 24)); + } + reg_val &= ~CCM_PLL5_CTRL_VCO_GAIN; /* PLL VCO Gain off */ + reg_val |= CCM_PLL5_CTRL_EN; /* PLL On */ + writel(reg_val, &ccm->pll5_cfg); + udelay(5500); + + setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_DDR_CLK); + +#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I) + /* reset GPS */ + clrbits_le32(&ccm->gps_clk_cfg, CCM_GPS_CTRL_RESET | CCM_GPS_CTRL_GATE); + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); + udelay(1); + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); +#endif + + /* setup MBUS clock */ + if (!mbus_clk) + mbus_clk = 300; + + /* PLL5P and PLL6 are the potential clock sources for MBUS */ + pll6x_clk = clock_get_pll6() / 1000000; +#ifdef CONFIG_MACH_SUN7I + pll6x_clk *= 2; /* sun7i uses PLL6*2, sun5i uses just PLL6 */ +#endif + pll5p_clk = clock_get_pll5p() / 1000000; + pll6x_div = DIV_ROUND_UP(pll6x_clk, mbus_clk); + pll5p_div = DIV_ROUND_UP(pll5p_clk, mbus_clk); + pll6x_rate = pll6x_clk / pll6x_div; + pll5p_rate = pll5p_clk / pll5p_div; + + if (pll6x_div <= 16 && pll6x_rate > pll5p_rate) { + /* use PLL6 as the MBUS clock source */ + reg_val = CCM_MBUS_CTRL_GATE | + CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) | + CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll6x_div)); + } else if (pll5p_div <= 16) { + /* use PLL5P as the MBUS clock source */ + reg_val = CCM_MBUS_CTRL_GATE | + CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) | + CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll5p_div)); + } else { + panic("Bad mbus_clk\n"); + } + writel(reg_val, &ccm->mbus_clk_cfg); + + /* + * open DRAMC AHB & DLL register clock + * close it first + */ +#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); +#else + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM); +#endif + udelay(22); + + /* then open it */ +#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL); +#else + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM); +#endif + udelay(22); +} + +/* + * The data from rslrX and rdgrX registers (X=rank) is stored + * in a single 32-bit value using the following format: + * bits [31:26] - DQS gating system latency for byte lane 3 + * bits [25:24] - DQS gating phase select for byte lane 3 + * bits [23:18] - DQS gating system latency for byte lane 2 + * bits [17:16] - DQS gating phase select for byte lane 2 + * bits [15:10] - DQS gating system latency for byte lane 1 + * bits [ 9:8 ] - DQS gating phase select for byte lane 1 + * bits [ 7:2 ] - DQS gating system latency for byte lane 0 + * bits [ 1:0 ] - DQS gating phase select for byte lane 0 + */ +static void mctl_set_dqs_gating_delay(int rank, u32 dqs_gating_delay) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 lane, number_of_lanes = mctl_get_number_of_lanes(); + /* rank0 gating system latency (3 bits per lane: cycles) */ + u32 slr = readl(rank == 0 ? &dram->rslr0 : &dram->rslr1); + /* rank0 gating phase select (2 bits per lane: 90, 180, 270, 360) */ + u32 dgr = readl(rank == 0 ? &dram->rdgr0 : &dram->rdgr1); + for (lane = 0; lane < number_of_lanes; lane++) { + u32 tmp = dqs_gating_delay >> (lane * 8); + slr &= ~(7 << (lane * 3)); + slr |= ((tmp >> 2) & 7) << (lane * 3); + dgr &= ~(3 << (lane * 2)); + dgr |= (tmp & 3) << (lane * 2); + } + writel(slr, rank == 0 ? &dram->rslr0 : &dram->rslr1); + writel(dgr, rank == 0 ? &dram->rdgr0 : &dram->rdgr1); +} + +static int dramc_scan_readpipe(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + + /* data training trigger */ + clrbits_le32(&dram->csr, DRAM_CSR_FAILED); + setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING); + + /* check whether data training process has completed */ + await_bits_clear(&dram->ccr, DRAM_CCR_DATA_TRAINING); + + /* check data training result */ + reg_val = readl(&dram->csr); + if (reg_val & DRAM_CSR_FAILED) + return -1; + + return 0; +} + +static void dramc_clock_output_en(u32 on) +{ +#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I) + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + if (on) + setbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT); + else + clrbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT); +#endif +#ifdef CONFIG_MACH_SUN4I + struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + if (on) + setbits_le32(&ccm->dram_clk_gate, CCM_DRAM_CTRL_DCLK_OUT); + else + clrbits_le32(&ccm->dram_clk_gate, CCM_DRAM_CTRL_DCLK_OUT); +#endif +} + +/* tRFC in nanoseconds for different densities (from the DDR3 spec) */ +static const u16 tRFC_DDR3_table[6] = { + /* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */ + 90, 90, 110, 160, 300, 350 +}; + +static void dramc_set_autorefresh_cycle(u32 clk, u32 density) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 tRFC, tREFI; + + tRFC = (tRFC_DDR3_table[density] * clk + 999) / 1000; + tREFI = (7987 * clk) >> 10; /* <= 7.8us */ + + writel(DRAM_DRR_TREFI(tREFI) | DRAM_DRR_TRFC(tRFC), &dram->drr); +} + +/* Calculate the value for A11, A10, A9 bits in MR0 (write recovery) */ +static u32 ddr3_write_recovery(u32 clk) +{ + u32 twr_ns = 15; /* DDR3 spec says that it is 15ns for all speed bins */ + u32 twr_ck = (twr_ns * clk + 999) / 1000; + if (twr_ck < 5) + return 1; + else if (twr_ck <= 8) + return twr_ck - 4; + else if (twr_ck <= 10) + return 5; + else + return 6; +} + +/* + * If the dram->ppwrsctl (SDR_DPCR) register has the lowest bit set to 1, this + * means that DRAM is currently in self-refresh mode and retaining the old + * data. Since we have no idea what to do in this situation yet, just set this + * register to 0 and initialize DRAM in the same way as on any normal reboot + * (discarding whatever was stored there). + * + * Note: on sun7i hardware, the highest 16 bits need to be set to 0x1651 magic + * value for this write operation to have any effect. On sun5i hadware this + * magic value is not necessary. And on sun4i hardware the writes to this + * register seem to have no effect at all. + */ +static void mctl_disable_power_save(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + writel(0x16510000, &dram->ppwrsctl); +} + +/* + * After the DRAM is powered up or reset, the DDR3 spec requires to wait at + * least 500 us before driving the CKE pin (Clock Enable) high. The dram->idct + * (SDR_IDCR) register appears to configure this delay, which gets applied + * right at the time when the DRAM initialization is activated in the + * 'mctl_ddr3_initialize' function. + */ +static void mctl_set_cke_delay(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + /* The CKE delay is represented in DRAM clock cycles, multiplied by N + * (where N=2 for sun4i/sun5i and N=3 for sun7i). Here it is set to + * the maximum possible value 0x1ffff, just like in the Allwinner's + * boot0 bootloader. The resulting delay value is somewhere between + * ~0.4 ms (sun5i with 648 MHz DRAM clock speed) and ~1.1 ms (sun7i + * with 360 MHz DRAM clock speed). */ + setbits_le32(&dram->idcr, 0x1ffff); +} + +/* + * This triggers the DRAM initialization. It performs sending the mode registers + * to the DRAM among other things. Very likely the ZQCL command is also getting + * executed (to do the initial impedance calibration on the DRAM side of the + * wire). The memory controller and the PHY must be already configured before + * calling this function. + */ +static void mctl_ddr3_initialize(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + setbits_le32(&dram->ccr, DRAM_CCR_INIT); + await_bits_clear(&dram->ccr, DRAM_CCR_INIT); +} + +/* + * Perform impedance calibration on the DRAM controller side of the wire. + */ +static void mctl_set_impedance(u32 zq, bool odt_en) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 zprog = zq & 0xFF, zdata = (zq >> 8) & 0xFFFFF; + +#ifndef CONFIG_MACH_SUN7I + /* Appears that some kind of automatically initiated default + * ZQ calibration is already in progress at this point on sun4i/sun5i + * hardware, but not on sun7i. So it is reasonable to wait for its + * completion before doing anything else. */ + await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); +#endif + + /* ZQ calibration is not really useful unless ODT is enabled */ + if (!odt_en) + return; + +#ifdef CONFIG_MACH_SUN7I + /* Enabling ODT in SDR_IOCR on sun7i hardware results in a deadlock + * unless bit 24 is set in SDR_ZQCR1. Not much is known about the + * SDR_ZQCR1 register, but there are hints indicating that it might + * be related to periodic impedance re-calibration. This particular + * magic value is borrowed from the Allwinner boot0 bootloader, and + * using it helps to avoid troubles */ + writel((1 << 24) | (1 << 1), &dram->zqcr1); +#endif + + /* Needed at least for sun5i, because it does not self clear there */ + clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); + + if (zdata) { + /* Set the user supplied impedance data */ + reg_val = DRAM_ZQCR0_ZDEN | zdata; + writel(reg_val, &dram->zqcr0); + /* no need to wait, this takes effect immediately */ + } else { + /* Do the calibration using the external resistor */ + reg_val = DRAM_ZQCR0_ZCAL | DRAM_ZQCR0_IMP_DIV(zprog); + writel(reg_val, &dram->zqcr0); + /* Wait for the new impedance configuration to settle */ + await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); + } + + /* Needed at least for sun5i, because it does not self clear there */ + clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); + + /* Set I/O configure register */ + writel(DRAM_IOCR_ODT_EN, &dram->iocr); +} + +static unsigned long dramc_init_helper(struct dram_para *para) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 density; + int ret_val; + + /* + * only single rank DDR3 is supported by this code even though the + * hardware can theoretically support DDR2 and up to two ranks + */ + if (para->type != DRAM_MEMORY_TYPE_DDR3 || para->rank_num != 1) + return 0; + + /* setup DRAM relative clock */ + mctl_setup_dram_clock(para->clock, para->mbus_clock); + + /* Disable any pad power save control */ + mctl_disable_power_save(); + + mctl_set_drive(); + + /* dram clock off */ + dramc_clock_output_en(0); + +#ifdef CONFIG_MACH_SUN4I + /* select dram controller 1 */ + writel(DRAM_CSEL_MAGIC, &dram->csel); +#endif + + mctl_itm_disable(); + mctl_enable_dll0(para->tpr3); + + /* configure external DRAM */ + reg_val = DRAM_DCR_TYPE_DDR3; + reg_val |= DRAM_DCR_IO_WIDTH(para->io_width >> 3); + + if (para->density == 256) + density = DRAM_DCR_CHIP_DENSITY_256M; + else if (para->density == 512) + density = DRAM_DCR_CHIP_DENSITY_512M; + else if (para->density == 1024) + density = DRAM_DCR_CHIP_DENSITY_1024M; + else if (para->density == 2048) + density = DRAM_DCR_CHIP_DENSITY_2048M; + else if (para->density == 4096) + density = DRAM_DCR_CHIP_DENSITY_4096M; + else if (para->density == 8192) + density = DRAM_DCR_CHIP_DENSITY_8192M; + else + density = DRAM_DCR_CHIP_DENSITY_256M; + + reg_val |= DRAM_DCR_CHIP_DENSITY(density); + reg_val |= DRAM_DCR_BUS_WIDTH((para->bus_width >> 3) - 1); + reg_val |= DRAM_DCR_RANK_SEL(para->rank_num - 1); + reg_val |= DRAM_DCR_CMD_RANK_ALL; + reg_val |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE); + writel(reg_val, &dram->dcr); + + dramc_clock_output_en(1); + + mctl_set_impedance(para->zq, para->odt_en); + + mctl_set_cke_delay(); + + mctl_ddr3_reset(); + + udelay(1); + + await_bits_clear(&dram->ccr, DRAM_CCR_INIT); + + mctl_enable_dllx(para->tpr3); + + /* set refresh period */ + dramc_set_autorefresh_cycle(para->clock, density); + + /* set timing parameters */ + writel(para->tpr0, &dram->tpr0); + writel(para->tpr1, &dram->tpr1); + writel(para->tpr2, &dram->tpr2); + + reg_val = DRAM_MR_BURST_LENGTH(0x0); +#if (defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUN7I)) + reg_val |= DRAM_MR_POWER_DOWN; +#endif + reg_val |= DRAM_MR_CAS_LAT(para->cas - 4); + reg_val |= DRAM_MR_WRITE_RECOVERY(ddr3_write_recovery(para->clock)); + writel(reg_val, &dram->mr); + + writel(para->emr1, &dram->emr); + writel(para->emr2, &dram->emr2); + writel(para->emr3, &dram->emr3); + + /* disable drift compensation and set passive DQS window mode */ + clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE); + +#ifdef CONFIG_MACH_SUN7I + /* Command rate timing mode 2T & 1T */ + if (para->tpr4 & 0x1) + setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T); +#endif + /* initialize external DRAM */ + mctl_ddr3_initialize(); + + /* scan read pipe value */ + mctl_itm_enable(); + + /* Hardware DQS gate training */ + ret_val = dramc_scan_readpipe(); + + if (ret_val < 0) + return 0; + + /* allow to override the DQS training results with a custom delay */ + if (para->dqs_gating_delay) + mctl_set_dqs_gating_delay(0, para->dqs_gating_delay); + + /* set the DQS gating window type */ + if (para->active_windowing) + clrbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); + else + setbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); + + mctl_itm_reset(); + + /* configure all host port */ + mctl_configure_hostport(); + + return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE); +} + +unsigned long dramc_init(struct dram_para *para) +{ + unsigned long dram_size, actual_density; + + /* If the dram configuration is not provided, use a default */ + if (!para) + return 0; + + /* if everything is known, then autodetection is not necessary */ + if (para->io_width && para->bus_width && para->density) + return dramc_init_helper(para); + + /* try to autodetect the DRAM bus width and density */ + para->io_width = 16; + para->bus_width = 32; +#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN5I) + /* only A0-A14 address lines on A10/A13, limiting max density to 4096 */ + para->density = 4096; +#else + /* all A0-A15 address lines on A20, which allow density 8192 */ + para->density = 8192; +#endif + + dram_size = dramc_init_helper(para); + if (!dram_size) { + /* if 32-bit bus width failed, try 16-bit bus width instead */ + para->bus_width = 16; + dram_size = dramc_init_helper(para); + if (!dram_size) { + /* if 16-bit bus width also failed, then bail out */ + return dram_size; + } + } + + /* check if we need to adjust the density */ + actual_density = (dram_size >> 17) * para->io_width / para->bus_width; + + if (actual_density != para->density) { + /* update the density and re-initialize DRAM again */ + para->density = actual_density; + dram_size = dramc_init_helper(para); + } + + return dram_size; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun50i_h6.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun50i_h6.c new file mode 100644 index 000000000..32ec0bc4c --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun50i_h6.c @@ -0,0 +1,699 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun50i H6 platform dram controller init + * + * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io> + * + */ +#include <common.h> +#include <init.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/kconfig.h> + +/* + * The DRAM controller structure on H6 is similar to the ones on A23/A80: + * they all contains 3 parts, COM, CTL and PHY. (As a note on A33/A83T/H3/A64 + * /H5/R40 CTL and PHY is composed). + * + * COM is allwinner-specific. On H6, the address mapping function is moved + * from COM to CTL (with the standard ADDRMAP registers on DesignWare memory + * controller). + * + * CTL (controller) and PHY is from DesignWare. + * + * The CTL part is a bit similar to the one on A23/A80 (because they all + * originate from DesignWare), but gets more registers added. + * + * The PHY part is quite new, not seen in any previous Allwinner SoCs, and + * not seen on other SoCs in U-Boot. The only SoC that is also known to have + * similar PHY is ZynqMP. + */ + +static void mctl_sys_init(struct dram_para *para); +static void mctl_com_init(struct dram_para *para); +static bool mctl_channel_init(struct dram_para *para); + +static bool mctl_core_init(struct dram_para *para) +{ + mctl_sys_init(para); + mctl_com_init(para); + switch (para->type) { + case SUNXI_DRAM_TYPE_LPDDR3: + case SUNXI_DRAM_TYPE_DDR3: + mctl_set_timing_params(para); + break; + default: + panic("Unsupported DRAM type!"); + }; + return mctl_channel_init(para); +} + +/* PHY initialisation */ +static void mctl_phy_pir_init(u32 val) +{ + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + + writel(val, &mctl_phy->pir); + writel(val | BIT(0), &mctl_phy->pir); /* Start initialisation. */ + mctl_await_completion(&mctl_phy->pgsr[0], BIT(0), BIT(0)); +} + +enum { + MBUS_PORT_CPU = 0, + MBUS_PORT_GPU = 1, + MBUS_PORT_MAHB = 2, + MBUS_PORT_DMA = 3, + MBUS_PORT_VE = 4, + MBUS_PORT_CE = 5, + MBUS_PORT_TSC0 = 6, + MBUS_PORT_NDFC0 = 8, + MBUS_PORT_CSI0 = 11, + MBUS_PORT_DI0 = 14, + MBUS_PORT_DI1 = 15, + MBUS_PORT_DE300 = 16, + MBUS_PORT_IOMMU = 25, + MBUS_PORT_VE2 = 26, + MBUS_PORT_USB3 = 37, + MBUS_PORT_PCIE = 38, + MBUS_PORT_VP9 = 39, + MBUS_PORT_HDCP2 = 40, +}; + +enum { + MBUS_QOS_LOWEST = 0, + MBUS_QOS_LOW, + MBUS_QOS_HIGH, + MBUS_QOS_HIGHEST +}; +inline void mbus_configure_port(u8 port, + bool bwlimit, + bool priority, + u8 qos, + u8 waittime, + u8 acs, + u16 bwl0, + u16 bwl1, + u16 bwl2) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0) + | (priority ? (1 << 1) : 0) + | ((qos & 0x3) << 2) + | ((waittime & 0xf) << 4) + | ((acs & 0xff) << 8) + | (bwl0 << 16) ); + const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff); + + debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1); + writel(cfg0, &mctl_com->master[port].cfg0); + writel(cfg1, &mctl_com->master[port].cfg1); +} + +#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2) \ + mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \ + MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2) + +static void mctl_set_master_priority(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel(399, &mctl_com->tmr); + writel(BIT(16), &mctl_com->bwcr); + + MBUS_CONF( CPU, true, HIGHEST, 0, 256, 128, 100); + MBUS_CONF( GPU, true, HIGH, 0, 1536, 1400, 256); + MBUS_CONF( MAHB, true, HIGHEST, 0, 512, 256, 96); + MBUS_CONF( DMA, true, HIGH, 0, 256, 100, 80); + MBUS_CONF( VE, true, HIGH, 2, 8192, 5500, 5000); + MBUS_CONF( CE, true, HIGH, 2, 100, 64, 32); + MBUS_CONF( TSC0, true, HIGH, 2, 100, 64, 32); + MBUS_CONF(NDFC0, true, HIGH, 0, 256, 128, 64); + MBUS_CONF( CSI0, true, HIGH, 0, 256, 128, 100); + MBUS_CONF( DI0, true, HIGH, 0, 1024, 256, 64); + MBUS_CONF(DE300, true, HIGHEST, 6, 8192, 2800, 2400); + MBUS_CONF(IOMMU, true, HIGHEST, 0, 100, 64, 32); + MBUS_CONF( VE2, true, HIGH, 2, 8192, 5500, 5000); + MBUS_CONF( USB3, true, HIGH, 0, 256, 128, 64); + MBUS_CONF( PCIE, true, HIGH, 2, 100, 64, 32); + MBUS_CONF( VP9, true, HIGH, 2, 8192, 5500, 5000); + MBUS_CONF(HDCP2, true, HIGH, 2, 100, 64, 32); +} + +static void mctl_sys_init(struct dram_para *para) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + /* Put all DRAM-related blocks to reset state */ + clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE | MBUS_RESET); + clrbits_le32(&ccm->dram_gate_reset, BIT(0)); + udelay(5); + writel(0, &ccm->dram_gate_reset); + clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN); + clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET); + + udelay(5); + + /* Set PLL5 rate to doubled DRAM clock rate */ + writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN | + CCM_PLL5_CTRL_N(para->clk * 2 / 24 - 1), &ccm->pll5_cfg); + mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK); + + /* Configure DRAM mod clock */ + writel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg); + setbits_le32(&ccm->dram_clk_cfg, DRAM_CLK_UPDATE); + writel(BIT(RESET_SHIFT), &ccm->dram_gate_reset); + udelay(5); + setbits_le32(&ccm->dram_gate_reset, BIT(0)); + + /* Disable all channels */ + writel(0, &mctl_com->maer0); + writel(0, &mctl_com->maer1); + writel(0, &mctl_com->maer2); + + /* Configure MBUS and enable DRAM mod reset */ + setbits_le32(&ccm->mbus_cfg, MBUS_RESET); + setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE); + setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET); + udelay(5); + + /* Unknown hack from the BSP, which enables access of mctl_ctl regs */ + writel(0x8000, &mctl_ctl->unk_0x00c); +} + +static void mctl_set_addrmap(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + u8 cols = para->cols; + u8 rows = para->rows; + u8 ranks = para->ranks; + + if (!para->bus_full_width) + cols -= 1; + + /* Ranks */ + if (ranks == 2) + mctl_ctl->addrmap[0] = rows + cols - 3; + else + mctl_ctl->addrmap[0] = 0x1F; + + /* Banks, hardcoded to 8 banks now */ + mctl_ctl->addrmap[1] = (cols - 2) | (cols - 2) << 8 | (cols - 2) << 16; + + /* Columns */ + mctl_ctl->addrmap[2] = 0; + switch (cols) { + case 7: + mctl_ctl->addrmap[3] = 0x1F1F1F00; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 8: + mctl_ctl->addrmap[3] = 0x1F1F0000; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 9: + mctl_ctl->addrmap[3] = 0x1F000000; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 10: + mctl_ctl->addrmap[3] = 0; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 11: + mctl_ctl->addrmap[3] = 0; + mctl_ctl->addrmap[4] = 0x1F00; + break; + case 12: + mctl_ctl->addrmap[3] = 0; + mctl_ctl->addrmap[4] = 0; + break; + default: + panic("Unsupported DRAM configuration: column number invalid\n"); + } + + /* Rows */ + mctl_ctl->addrmap[5] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + switch (rows) { + case 13: + mctl_ctl->addrmap[6] = (cols - 3) | 0x0F0F0F00; + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 14: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | 0x0F0F0000; + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 15: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | 0x0F000000; + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 16: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 17: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + mctl_ctl->addrmap[7] = (cols - 3) | 0x0F00; + break; + case 18: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + mctl_ctl->addrmap[7] = (cols - 3) | ((cols - 3) << 8); + break; + default: + panic("Unsupported DRAM configuration: row number invalid\n"); + } + + /* Bank groups, DDR4 only */ + mctl_ctl->addrmap[8] = 0x3F3F; +} + +static void mctl_com_init(struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + u32 reg_val, tmp; + + mctl_set_addrmap(para); + + setbits_le32(&mctl_com->cr, BIT(31)); + + /* The bonding ID seems to be always 7. */ + if (readl(SUNXI_SIDC_BASE + 0x100) == 7) /* bonding ID */ + clrbits_le32(&mctl_com->cr, BIT(27)); + else if (readl(SUNXI_SIDC_BASE + 0x100) == 3) + setbits_le32(&mctl_com->cr, BIT(27)); + + if (para->clk > 408) + reg_val = 0xf00; + else if (para->clk > 246) + reg_val = 0x1f00; + else + reg_val = 0x3f00; + clrsetbits_le32(&mctl_com->unk_0x008, 0x3f00, reg_val); + + /* TODO: DDR4 */ + reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + reg_val |= MSTR_DEVICETYPE_LPDDR3; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; + if (para->bus_full_width) + reg_val |= MSTR_BUSWIDTH_FULL; + else + reg_val |= MSTR_BUSWIDTH_HALF; + writel(reg_val | BIT(31), &mctl_ctl->mstr); + + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + reg_val = DCR_LPDDR3 | DCR_DDR8BANK; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + reg_val = DCR_DDR3 | DCR_DDR8BANK | DCR_DDR2T; + writel(reg_val | 0x400, &mctl_phy->dcr); + + if (para->ranks == 2) + writel(0x0303, &mctl_ctl->odtmap); + else + writel(0x0201, &mctl_ctl->odtmap); + + /* TODO: DDR4 */ + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + tmp = para->clk * 7 / 2000; + reg_val = 0x0400; + reg_val |= (tmp + 7) << 24; + reg_val |= (((para->clk < 400) ? 3 : 4) - tmp) << 16; + } else if (para->type == SUNXI_DRAM_TYPE_DDR3) { + reg_val = 0x06000400; /* TODO?: Use CL - CWL value in [7:0] */ + } else { + panic("Only (LP)DDR3 supported (type = %d)\n", para->type); + } + writel(reg_val, &mctl_ctl->odtcfg); + + if (!para->bus_full_width) { + writel(0x0, &mctl_phy->dx[2].gcr[0]); + writel(0x0, &mctl_phy->dx[3].gcr[0]); + } +} + +static void mctl_bit_delay_set(struct dram_para *para) +{ + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i, j; + u32 val; + + for (i = 0; i < 4; i++) { + val = readl(&mctl_phy->dx[i].bdlr0); + for (j = 0; j < 4; j++) + val += para->dx_write_delays[i][j] << (j * 8); + writel(val, &mctl_phy->dx[i].bdlr0); + + val = readl(&mctl_phy->dx[i].bdlr1); + for (j = 0; j < 4; j++) + val += para->dx_write_delays[i][j + 4] << (j * 8); + writel(val, &mctl_phy->dx[i].bdlr1); + + val = readl(&mctl_phy->dx[i].bdlr2); + for (j = 0; j < 4; j++) + val += para->dx_write_delays[i][j + 8] << (j * 8); + writel(val, &mctl_phy->dx[i].bdlr2); + } + clrbits_le32(&mctl_phy->pgcr[0], BIT(26)); + + for (i = 0; i < 4; i++) { + val = readl(&mctl_phy->dx[i].bdlr3); + for (j = 0; j < 4; j++) + val += para->dx_read_delays[i][j] << (j * 8); + writel(val, &mctl_phy->dx[i].bdlr3); + + val = readl(&mctl_phy->dx[i].bdlr4); + for (j = 0; j < 4; j++) + val += para->dx_read_delays[i][j + 4] << (j * 8); + writel(val, &mctl_phy->dx[i].bdlr4); + + val = readl(&mctl_phy->dx[i].bdlr5); + for (j = 0; j < 4; j++) + val += para->dx_read_delays[i][j + 8] << (j * 8); + writel(val, &mctl_phy->dx[i].bdlr5); + + val = readl(&mctl_phy->dx[i].bdlr6); + val += (para->dx_read_delays[i][12] << 8) | + (para->dx_read_delays[i][13] << 16); + writel(val, &mctl_phy->dx[i].bdlr6); + } + setbits_le32(&mctl_phy->pgcr[0], BIT(26)); + udelay(1); + + if (para->type != SUNXI_DRAM_TYPE_LPDDR3) + return; + + for (i = 1; i < 14; i++) { + val = readl(&mctl_phy->acbdlr[i]); + val += 0x0a0a0a0a; + writel(val, &mctl_phy->acbdlr[i]); + } +} + +static bool mctl_channel_init(struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i; + u32 val; + + setbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30)); + writel(0x2f05, &mctl_ctl->sched[0]); + setbits_le32(&mctl_ctl->rfshctl3, BIT(0)); + setbits_le32(&mctl_ctl->dfimisc, BIT(0)); + setbits_le32(&mctl_ctl->unk_0x00c, BIT(8)); + clrsetbits_le32(&mctl_phy->pgcr[1], 0x180, 0xc0); + /* TODO: non-LPDDR3 types */ + clrsetbits_le32(&mctl_phy->pgcr[2], GENMASK(17, 0), ns_to_t(7800)); + clrbits_le32(&mctl_phy->pgcr[6], BIT(0)); + clrsetbits_le32(&mctl_phy->dxccr, 0xee0, 0x220); + /* TODO: VT compensation */ + clrsetbits_le32(&mctl_phy->dsgcr, BIT(0), 0x440060); + clrbits_le32(&mctl_phy->vtcr[1], BIT(1)); + + for (i = 0; i < 4; i++) + clrsetbits_le32(&mctl_phy->dx[i].gcr[0], 0xe00, 0x800); + for (i = 0; i < 4; i++) + clrsetbits_le32(&mctl_phy->dx[i].gcr[2], 0xffff, 0x5555); + for (i = 0; i < 4; i++) + clrsetbits_le32(&mctl_phy->dx[i].gcr[3], 0x3030, 0x1010); + + udelay(100); + + if (para->ranks == 2) + setbits_le32(&mctl_phy->dtcr[1], 0x30000); + else + clrsetbits_le32(&mctl_phy->dtcr[1], 0x30000, 0x10000); + + if (sunxi_dram_is_lpddr(para->type)) + clrbits_le32(&mctl_phy->dtcr[1], BIT(1)); + if (para->ranks == 2) { + writel(0x00010001, &mctl_phy->rankidr); + writel(0x20000, &mctl_phy->odtcr); + } else { + writel(0x0, &mctl_phy->rankidr); + writel(0x10000, &mctl_phy->odtcr); + } + + /* set bits [3:0] to 1? 0 not valid in ZynqMP d/s */ + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) + clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000040); + else + clrsetbits_le32(&mctl_phy->dtcr[0], 0xF0000000, 0x10000000); + if (para->clk <= 792) { + if (para->clk <= 672) { + if (para->clk <= 600) + val = 0x300; + else + val = 0x400; + } else { + val = 0x500; + } + } else { + val = 0x600; + } + /* FIXME: NOT REVIEWED YET */ + clrsetbits_le32(&mctl_phy->zq[0].zqcr, 0x700, val); + clrsetbits_le32(&mctl_phy->zq[0].zqpr[0], 0xff, + CONFIG_DRAM_ZQ & 0xff); + clrbits_le32(&mctl_phy->zq[0].zqor[0], 0xfffff); + setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ >> 8) & 0xff); + setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ & 0xf00) - 0x100); + setbits_le32(&mctl_phy->zq[0].zqor[0], (CONFIG_DRAM_ZQ & 0xff00) << 4); + clrbits_le32(&mctl_phy->zq[1].zqpr[0], 0xfffff); + setbits_le32(&mctl_phy->zq[1].zqpr[0], (CONFIG_DRAM_ZQ >> 16) & 0xff); + setbits_le32(&mctl_phy->zq[1].zqpr[0], ((CONFIG_DRAM_ZQ >> 8) & 0xf00) - 0x100); + setbits_le32(&mctl_phy->zq[1].zqpr[0], (CONFIG_DRAM_ZQ & 0xff0000) >> 4); + if (para->type == SUNXI_DRAM_TYPE_LPDDR3) { + for (i = 1; i < 14; i++) + writel(0x06060606, &mctl_phy->acbdlr[i]); + } + + val = PIR_ZCAL | PIR_DCAL | PIR_PHYRST | PIR_DRAMINIT | PIR_QSGATE | + PIR_RDDSKW | PIR_WRDSKW | PIR_RDEYE | PIR_WREYE; + if (para->type == SUNXI_DRAM_TYPE_DDR3) + val |= PIR_DRAMRST | PIR_WL; + mctl_phy_pir_init(val); + + /* TODO: DDR4 types ? */ + for (i = 0; i < 4; i++) + writel(0x00000909, &mctl_phy->dx[i].gcr[5]); + + for (i = 0; i < 4; i++) { + if (IS_ENABLED(CONFIG_DRAM_ODT_EN)) + val = 0x0; + else + val = 0xaaaa; + clrsetbits_le32(&mctl_phy->dx[i].gcr[2], 0xffff, val); + + if (IS_ENABLED(CONFIG_DRAM_ODT_EN)) + val = 0x0; + else + val = 0x2020; + clrsetbits_le32(&mctl_phy->dx[i].gcr[3], 0x3030, val); + } + + mctl_bit_delay_set(para); + udelay(1); + + setbits_le32(&mctl_phy->pgcr[6], BIT(0)); + clrbits_le32(&mctl_phy->pgcr[6], 0xfff8); + for (i = 0; i < 4; i++) + clrbits_le32(&mctl_phy->dx[i].gcr[3], ~0x3ffff); + udelay(10); + + if (readl(&mctl_phy->pgsr[0]) & 0xff00000) { + /* Oops! There's something wrong! */ + debug("PLL = %x\n", readl(0x3001010)); + debug("DRAM PHY PGSR0 = %x\n", readl(&mctl_phy->pgsr[0])); + for (i = 0; i < 4; i++) + debug("DRAM PHY DX%dRSR0 = %x\n", i, readl(&mctl_phy->dx[i].rsr[0])); + debug("Error while initializing DRAM PHY!\n"); + + return false; + } + + if (sunxi_dram_is_lpddr(para->type)) + clrsetbits_le32(&mctl_phy->dsgcr, 0xc0, 0x40); + clrbits_le32(&mctl_phy->pgcr[1], 0x40); + clrbits_le32(&mctl_ctl->dfimisc, BIT(0)); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, 1, 1); + clrbits_le32(&mctl_ctl->rfshctl3, BIT(0)); + + setbits_le32(&mctl_com->unk_0x014, BIT(31)); + writel(0xffffffff, &mctl_com->maer0); + writel(0x7ff, &mctl_com->maer1); + writel(0xffff, &mctl_com->maer2); + + return true; +} + +static void mctl_auto_detect_rank_width(struct dram_para *para) +{ + /* this is minimum size that it's supported */ + para->cols = 8; + para->rows = 13; + + /* + * Previous versions of this driver tried to auto detect the rank + * and width by looking at controller registers. However this proved + * to be not reliable, so this approach here is the more robust + * solution. Check the git history for details. + * + * Strategy here is to test most demanding combination first and least + * demanding last, otherwise HW might not be fully utilized. For + * example, half bus width and rank = 1 combination would also work + * on HW with full bus width and rank = 2, but only 1/4 RAM would be + * visible. + */ + + debug("testing 32-bit width, rank = 2\n"); + para->bus_full_width = 1; + para->ranks = 2; + if (mctl_core_init(para)) + return; + + debug("testing 32-bit width, rank = 1\n"); + para->bus_full_width = 1; + para->ranks = 1; + if (mctl_core_init(para)) + return; + + debug("testing 16-bit width, rank = 2\n"); + para->bus_full_width = 0; + para->ranks = 2; + if (mctl_core_init(para)) + return; + + debug("testing 16-bit width, rank = 1\n"); + para->bus_full_width = 0; + para->ranks = 1; + if (mctl_core_init(para)) + return; + + panic("This DRAM setup is currently not supported.\n"); +} + +static void mctl_auto_detect_dram_size(struct dram_para *para) +{ + /* TODO: non-(LP)DDR3 */ + + /* detect row address bits */ + para->cols = 8; + para->rows = 18; + mctl_core_init(para); + + for (para->rows = 13; para->rows < 18; para->rows++) { + /* 8 banks, 8 bit per byte and 16/32 bit width */ + if (mctl_mem_matches((1 << (para->rows + para->cols + + 4 + para->bus_full_width)))) + break; + } + + /* detect column address bits */ + para->cols = 11; + mctl_core_init(para); + + for (para->cols = 8; para->cols < 11; para->cols++) { + /* 8 bits per byte and 16/32 bit width */ + if (mctl_mem_matches(1 << (para->cols + 1 + + para->bus_full_width))) + break; + } +} + +unsigned long mctl_calc_size(struct dram_para *para) +{ + u8 width = para->bus_full_width ? 4 : 2; + + /* TODO: non-(LP)DDR3 */ + + /* 8 banks */ + return (1ULL << (para->cols + para->rows + 3)) * width * para->ranks; +} + +#define SUN50I_H6_LPDDR3_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN50I_H6_LPDDR3_DX_READ_DELAYS \ + {{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }} + +#define SUN50I_H6_DDR3_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN50I_H6_DDR3_DX_READ_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct dram_para para = { + .clk = CONFIG_DRAM_CLK, +#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3 + .type = SUNXI_DRAM_TYPE_LPDDR3, + .dx_read_delays = SUN50I_H6_LPDDR3_DX_READ_DELAYS, + .dx_write_delays = SUN50I_H6_LPDDR3_DX_WRITE_DELAYS, +#elif defined(CONFIG_SUNXI_DRAM_H6_DDR3_1333) + .type = SUNXI_DRAM_TYPE_DDR3, + .dx_read_delays = SUN50I_H6_DDR3_DX_READ_DELAYS, + .dx_write_delays = SUN50I_H6_DDR3_DX_WRITE_DELAYS, +#endif + }; + + unsigned long size; + + /* RES_CAL_CTRL_REG in BSP U-boot*/ + setbits_le32(0x7010310, BIT(8)); + clrbits_le32(0x7010318, 0x3f); + + mctl_auto_detect_rank_width(¶); + mctl_auto_detect_dram_size(¶); + + mctl_core_init(¶); + + size = mctl_calc_size(¶); + + clrsetbits_le32(&mctl_com->cr, 0xf0, (size >> (10 + 10 + 4)) & 0xf0); + + mctl_set_master_priority(); + + return size; +}; diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun50i_h616.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun50i_h616.c new file mode 100644 index 000000000..ef5876971 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -0,0 +1,1023 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun50i H616 platform dram controller driver + * + * While controller is very similar to that in H6, PHY is completely + * unknown. That's why this driver has plenty of magic numbers. Some + * meaning was nevertheless deduced from strings found in boot0 and + * known meaning of some dram parameters. + * This driver only supports DDR3 memory and omits logic for all + * other supported types supported by hardware. + * + * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net> + * + */ +#include <common.h> +#include <init.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/kconfig.h> + +enum { + MBUS_QOS_LOWEST = 0, + MBUS_QOS_LOW, + MBUS_QOS_HIGH, + MBUS_QOS_HIGHEST +}; + +inline void mbus_configure_port(u8 port, + bool bwlimit, + bool priority, + u8 qos, + u8 waittime, + u8 acs, + u16 bwl0, + u16 bwl1, + u16 bwl2) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0) + | (priority ? (1 << 1) : 0) + | ((qos & 0x3) << 2) + | ((waittime & 0xf) << 4) + | ((acs & 0xff) << 8) + | (bwl0 << 16) ); + const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff); + + debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1); + writel_relaxed(cfg0, &mctl_com->master[port].cfg0); + writel_relaxed(cfg1, &mctl_com->master[port].cfg1); +} + +#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2) \ + mbus_configure_port(port, bwlimit, false, \ + MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2) + +static void mctl_set_master_priority(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel(399, &mctl_com->tmr); + writel(BIT(16), &mctl_com->bwcr); + + MBUS_CONF( 0, true, HIGHEST, 0, 256, 128, 100); + MBUS_CONF( 1, true, HIGH, 0, 1536, 1400, 256); + MBUS_CONF( 2, true, HIGHEST, 0, 512, 256, 96); + MBUS_CONF( 3, true, HIGH, 0, 256, 100, 80); + MBUS_CONF( 4, true, HIGH, 2, 8192, 5500, 5000); + MBUS_CONF( 5, true, HIGH, 2, 100, 64, 32); + MBUS_CONF( 6, true, HIGH, 2, 100, 64, 32); + MBUS_CONF( 8, true, HIGH, 0, 256, 128, 64); + MBUS_CONF(11, true, HIGH, 0, 256, 128, 100); + MBUS_CONF(14, true, HIGH, 0, 1024, 256, 64); + MBUS_CONF(16, true, HIGHEST, 6, 8192, 2800, 2400); + MBUS_CONF(21, true, HIGHEST, 6, 2048, 768, 512); + MBUS_CONF(25, true, HIGHEST, 0, 100, 64, 32); + MBUS_CONF(26, true, HIGH, 2, 8192, 5500, 5000); + MBUS_CONF(37, true, HIGH, 0, 256, 128, 64); + MBUS_CONF(38, true, HIGH, 2, 100, 64, 32); + MBUS_CONF(39, true, HIGH, 2, 8192, 5500, 5000); + MBUS_CONF(40, true, HIGH, 2, 100, 64, 32); + + dmb(); +} + +static void mctl_sys_init(struct dram_para *para) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + /* Put all DRAM-related blocks to reset state */ + clrbits_le32(&ccm->mbus_cfg, MBUS_ENABLE); + clrbits_le32(&ccm->mbus_cfg, MBUS_RESET); + clrbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT)); + udelay(5); + clrbits_le32(&ccm->dram_gate_reset, BIT(RESET_SHIFT)); + clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN); + clrbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET); + + udelay(5); + + /* Set PLL5 rate to doubled DRAM clock rate */ + writel(CCM_PLL5_CTRL_EN | CCM_PLL5_LOCK_EN | CCM_PLL5_OUT_EN | + CCM_PLL5_CTRL_N(para->clk * 2 / 24 - 1), &ccm->pll5_cfg); + mctl_await_completion(&ccm->pll5_cfg, CCM_PLL5_LOCK, CCM_PLL5_LOCK); + + /* Configure DRAM mod clock */ + writel(DRAM_CLK_SRC_PLL5, &ccm->dram_clk_cfg); + writel(BIT(RESET_SHIFT), &ccm->dram_gate_reset); + udelay(5); + setbits_le32(&ccm->dram_gate_reset, BIT(GATE_SHIFT)); + + /* Disable all channels */ + writel(0, &mctl_com->maer0); + writel(0, &mctl_com->maer1); + writel(0, &mctl_com->maer2); + + /* Configure MBUS and enable DRAM mod reset */ + setbits_le32(&ccm->mbus_cfg, MBUS_RESET); + setbits_le32(&ccm->mbus_cfg, MBUS_ENABLE); + + clrbits_le32(&mctl_com->unk_0x500, BIT(25)); + + setbits_le32(&ccm->dram_clk_cfg, DRAM_MOD_RESET); + udelay(5); + + /* Unknown hack, which enables access of mctl_ctl regs */ + writel(0x8000, &mctl_ctl->clken); +} + +static void mctl_set_addrmap(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + u8 cols = para->cols; + u8 rows = para->rows; + u8 ranks = para->ranks; + + if (!para->bus_full_width) + cols -= 1; + + /* Ranks */ + if (ranks == 2) + mctl_ctl->addrmap[0] = rows + cols - 3; + else + mctl_ctl->addrmap[0] = 0x1F; + + /* Banks, hardcoded to 8 banks now */ + mctl_ctl->addrmap[1] = (cols - 2) | (cols - 2) << 8 | (cols - 2) << 16; + + /* Columns */ + mctl_ctl->addrmap[2] = 0; + switch (cols) { + case 7: + mctl_ctl->addrmap[3] = 0x1F1F1F00; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 8: + mctl_ctl->addrmap[3] = 0x1F1F0000; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 9: + mctl_ctl->addrmap[3] = 0x1F000000; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 10: + mctl_ctl->addrmap[3] = 0; + mctl_ctl->addrmap[4] = 0x1F1F; + break; + case 11: + mctl_ctl->addrmap[3] = 0; + mctl_ctl->addrmap[4] = 0x1F00; + break; + case 12: + mctl_ctl->addrmap[3] = 0; + mctl_ctl->addrmap[4] = 0; + break; + default: + panic("Unsupported DRAM configuration: column number invalid\n"); + } + + /* Rows */ + mctl_ctl->addrmap[5] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + switch (rows) { + case 13: + mctl_ctl->addrmap[6] = (cols - 3) | 0x0F0F0F00; + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 14: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | 0x0F0F0000; + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 15: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | 0x0F000000; + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 16: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + mctl_ctl->addrmap[7] = 0x0F0F; + break; + case 17: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + mctl_ctl->addrmap[7] = (cols - 3) | 0x0F00; + break; + case 18: + mctl_ctl->addrmap[6] = (cols - 3) | ((cols - 3) << 8) | ((cols - 3) << 16) | ((cols - 3) << 24); + mctl_ctl->addrmap[7] = (cols - 3) | ((cols - 3) << 8); + break; + default: + panic("Unsupported DRAM configuration: row number invalid\n"); + } + + /* Bank groups, DDR4 only */ + mctl_ctl->addrmap[8] = 0x3F3F; +} + +static const u8 phy_init[] = { + 0x07, 0x0b, 0x02, 0x16, 0x0d, 0x0e, 0x14, 0x19, + 0x0a, 0x15, 0x03, 0x13, 0x04, 0x0c, 0x10, 0x06, + 0x0f, 0x11, 0x1a, 0x01, 0x12, 0x17, 0x00, 0x08, + 0x09, 0x05, 0x18 +}; + +static void mctl_phy_configure_odt(void) +{ + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x388); + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x38c); + + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x3c8); + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x3cc); + + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x408); + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x40c); + + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x448); + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x44c); + + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x340); + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x344); + + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x348); + writel_relaxed(0xe, SUNXI_DRAM_PHY0_BASE + 0x34c); + + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x380); + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x384); + + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x3c0); + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x3c4); + + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x400); + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x404); + + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x440); + writel_relaxed(0x8, SUNXI_DRAM_PHY0_BASE + 0x444); + + dmb(); +} + +static bool mctl_phy_write_leveling(struct dram_para *para) +{ + bool result = true; + u32 val; + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0, 0x80); + writel(4, SUNXI_DRAM_PHY0_BASE + 0xc); + writel(0x40, SUNXI_DRAM_PHY0_BASE + 0x10); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4); + + if (para->bus_full_width) + val = 0xf; + else + val = 3; + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x188), val, val); + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4); + + val = readl(SUNXI_DRAM_PHY0_BASE + 0x258); + if (val == 0 || val == 0x3f) + result = false; + val = readl(SUNXI_DRAM_PHY0_BASE + 0x25c); + if (val == 0 || val == 0x3f) + result = false; + val = readl(SUNXI_DRAM_PHY0_BASE + 0x318); + if (val == 0 || val == 0x3f) + result = false; + val = readl(SUNXI_DRAM_PHY0_BASE + 0x31c); + if (val == 0 || val == 0x3f) + result = false; + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0); + + if (para->ranks == 2) { + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0, 0x40); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4); + + if (para->bus_full_width) + val = 0xf; + else + val = 3; + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x188), val, val); + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 4); + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0xc0); + + return result; +} + +static bool mctl_phy_read_calibration(struct dram_para *para) +{ + bool result = true; + u32 val, tmp; + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x20); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + + if (para->bus_full_width) + val = 0xf; + else + val = 3; + + while ((readl(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) { + if (readl(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) { + result = false; + break; + } + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30); + + if (para->ranks == 2) { + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30, 0x10); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + + while ((readl(SUNXI_DRAM_PHY0_BASE + 0x184) & val) != val) { + if (readl(SUNXI_DRAM_PHY0_BASE + 0x184) & 0x20) { + result = false; + break; + } + } + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 1); + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 0x30); + + val = readl(SUNXI_DRAM_PHY0_BASE + 0x274) & 7; + tmp = readl(SUNXI_DRAM_PHY0_BASE + 0x26c) & 7; + if (val < tmp) + val = tmp; + tmp = readl(SUNXI_DRAM_PHY0_BASE + 0x32c) & 7; + if (val < tmp) + val = tmp; + tmp = readl(SUNXI_DRAM_PHY0_BASE + 0x334) & 7; + if (val < tmp) + val = tmp; + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x38, 0x7, (val + 2) & 7); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 0x20); + + return result; +} + +static bool mctl_phy_read_training(struct dram_para *para) +{ + u32 val1, val2, *ptr1, *ptr2; + bool result = true; + int i; + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2); + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x804, 0x3f, 0xf); + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x808, 0x3f, 0xf); + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa04, 0x3f, 0xf); + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0xa08, 0x3f, 0xf); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 6); + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 1); + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x840), 0xc, 0xc); + if (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3) + result = false; + + if (para->bus_full_width) { + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc); + if (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3) + result = false; + } + + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x898); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x850); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x8bc); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x874); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + + if (para->bus_full_width) { + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xa98); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xa50); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xabc); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xa74); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3); + + if (para->ranks == 2) { + /* maybe last parameter should be 1? */ + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3, 2); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 6); + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 1); + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x840), 0xc, 0xc); + if (readl(SUNXI_DRAM_PHY0_BASE + 0x840) & 3) + result = false; + + if (para->bus_full_width) { + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xa40), 0xc, 0xc); + if (readl(SUNXI_DRAM_PHY0_BASE + 0xa40) & 3) + result = false; + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 3); + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 3); + + return result; +} + +static bool mctl_phy_write_training(struct dram_para *para) +{ + u32 val1, val2, *ptr1, *ptr2; + bool result = true; + int i; + + writel(0, SUNXI_DRAM_PHY0_BASE + 0x134); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x138); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x19c); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x1a0); + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 8); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10); + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x20); + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x8e0), 3, 3); + if (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc) + result = false; + + if (para->bus_full_width) { + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3); + if (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc) + result = false; + } + + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x938); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x8f0); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x95c); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x914); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + + if (para->bus_full_width) { + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xb38); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xaf0); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + ptr1 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xb5c); + ptr2 = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xb14); + for (i = 0; i < 9; i++) { + val1 = readl(&ptr1[i]); + val2 = readl(&ptr2[i]); + if (val1 - val2 <= 6) + result = false; + } + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60); + + if (para->ranks == 2) { + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc, 4); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10); + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x20); + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x8e0), 3, 3); + if (readl(SUNXI_DRAM_PHY0_BASE + 0x8e0) & 0xc) + result = false; + + if (para->bus_full_width) { + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0xae0), 3, 3); + if (readl(SUNXI_DRAM_PHY0_BASE + 0xae0) & 0xc) + result = false; + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x60); + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x198, 0xc); + + return result; +} + +static bool mctl_phy_bit_delay_compensation(struct dram_para *para) +{ + u32 *ptr; + int i; + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1); + setbits_le32(SUNXI_DRAM_PHY0_BASE + 8, 8); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 0x10); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x484); + for (i = 0; i < 9; i++) { + writel_relaxed(0x16, ptr); + writel_relaxed(0x16, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x4d0); + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x590); + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x4cc); + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x58c); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x4d8); + for (i = 0; i < 9; i++) { + writel_relaxed(0x1a, ptr); + writel_relaxed(0x1a, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x524); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x5e4); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x520); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x5e0); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x604); + for (i = 0; i < 9; i++) { + writel_relaxed(0x1a, ptr); + writel_relaxed(0x1a, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x650); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x710); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x64c); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x70c); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x658); + for (i = 0; i < 9; i++) { + writel_relaxed(0x1a, ptr); + writel_relaxed(0x1a, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x6a4); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x764); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x6a0); + writel_relaxed(0x1e, SUNXI_DRAM_PHY0_BASE + 0x760); + + dmb(); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 1); + + /* second part */ + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, 0x80); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x190, 4); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x480); + for (i = 0; i < 9; i++) { + writel_relaxed(0x10, ptr); + writel_relaxed(0x10, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x528); + writel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x5e8); + writel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x4c8); + writel_relaxed(0x18, SUNXI_DRAM_PHY0_BASE + 0x588); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x4d4); + for (i = 0; i < 9; i++) { + writel_relaxed(0x12, ptr); + writel_relaxed(0x12, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x52c); + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x5ec); + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x51c); + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x5dc); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x600); + for (i = 0; i < 9; i++) { + writel_relaxed(0x12, ptr); + writel_relaxed(0x12, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x6a8); + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x768); + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x648); + writel_relaxed(0x1a, SUNXI_DRAM_PHY0_BASE + 0x708); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x654); + for (i = 0; i < 9; i++) { + writel_relaxed(0x14, ptr); + writel_relaxed(0x14, ptr + 0x30); + ptr += 2; + } + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x6ac); + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x76c); + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x69c); + writel_relaxed(0x1c, SUNXI_DRAM_PHY0_BASE + 0x75c); + + dmb(); + + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x54, 0x80); + + return true; +} + +static bool mctl_phy_init(struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + u32 val, *ptr; + int i; + + if (para->bus_full_width) + val = 0xf; + else + val = 3; + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x3c, 0xf, val); + + writel(0xd, SUNXI_DRAM_PHY0_BASE + 0x14); + writel(0xd, SUNXI_DRAM_PHY0_BASE + 0x35c); + writel(0xd, SUNXI_DRAM_PHY0_BASE + 0x368); + writel(0xd, SUNXI_DRAM_PHY0_BASE + 0x374); + + writel(0, SUNXI_DRAM_PHY0_BASE + 0x18); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x360); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x36c); + writel(0, SUNXI_DRAM_PHY0_BASE + 0x378); + + writel(9, SUNXI_DRAM_PHY0_BASE + 0x1c); + writel(9, SUNXI_DRAM_PHY0_BASE + 0x364); + writel(9, SUNXI_DRAM_PHY0_BASE + 0x370); + writel(9, SUNXI_DRAM_PHY0_BASE + 0x37c); + + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0xc0); + for (i = 0; i < ARRAY_SIZE(phy_init); i++) + writel(phy_init[i], &ptr[i]); + + if (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_UNKNOWN_FEATURE)) { + ptr = (u32*)(SUNXI_DRAM_PHY0_BASE + 0x780); + for (i = 0; i < 32; i++) + writel(0x16, &ptr[i]); + writel(0xe, SUNXI_DRAM_PHY0_BASE + 0x78c); + writel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7a4); + writel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7b8); + writel(0x8, SUNXI_DRAM_PHY0_BASE + 0x7d4); + writel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7dc); + writel(0xe, SUNXI_DRAM_PHY0_BASE + 0x7e0); + } + + writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x3dc); + writel(0x80, SUNXI_DRAM_PHY0_BASE + 0x45c); + + if (IS_ENABLED(DRAM_ODT_EN)) + mctl_phy_configure_odt(); + + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 4, 7, 0xa); + + if (para->clk <= 672) + writel(0xf, SUNXI_DRAM_PHY0_BASE + 0x20); + if (para->clk > 500) { + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x144, BIT(7)); + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 0xe0); + } else { + setbits_le32(SUNXI_DRAM_PHY0_BASE + 0x144, BIT(7)); + clrsetbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 0xe0, 0x20); + } + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x14c, 8); + + mctl_await_completion((u32*)(SUNXI_DRAM_PHY0_BASE + 0x180), 4, 4); + + writel(0x37, SUNXI_DRAM_PHY0_BASE + 0x58); + clrbits_le32(&mctl_com->unk_0x500, 0x200); + + writel(0, &mctl_ctl->swctl); + setbits_le32(&mctl_ctl->dfimisc, 1); + + /* start DFI init */ + setbits_le32(&mctl_ctl->dfimisc, 0x20); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, 1, 1); + /* poll DFI init complete */ + mctl_await_completion(&mctl_ctl->dfistat, 1, 1); + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->dfimisc, 0x20); + + clrbits_le32(&mctl_ctl->pwrctl, 0x20); + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, 1, 1); + mctl_await_completion(&mctl_ctl->statr, 3, 1); + + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->dfimisc, 1); + + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, 1, 1); + + writel(0x1f14, &mctl_ctl->mrctrl1); + writel(0x80000030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(4, &mctl_ctl->mrctrl1); + writel(0x80001030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(0x20, &mctl_ctl->mrctrl1); + writel(0x80002030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(0, &mctl_ctl->mrctrl1); + writel(0x80003030, &mctl_ctl->mrctrl0); + mctl_await_completion(&mctl_ctl->mrctrl0, BIT(31), 0); + + writel(0, SUNXI_DRAM_PHY0_BASE + 0x54); + + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->rfshctl3, 1); + writel(1, &mctl_ctl->swctl); + + if (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_WRITE_LEVELING)) { + for (i = 0; i < 5; i++) + if (mctl_phy_write_leveling(para)) + break; + if (i == 5) { + debug("write leveling failed!\n"); + return false; + } + } + + if (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_READ_CALIBRATION)) { + for (i = 0; i < 5; i++) + if (mctl_phy_read_calibration(para)) + break; + if (i == 5) { + debug("read calibration failed!\n"); + return false; + } + } + + if (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_READ_TRAINING)) { + for (i = 0; i < 5; i++) + if (mctl_phy_read_training(para)) + break; + if (i == 5) { + debug("read training failed!\n"); + return false; + } + } + + if (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_WRITE_TRAINING)) { + for (i = 0; i < 5; i++) + if (mctl_phy_write_training(para)) + break; + if (i == 5) { + debug("write training failed!\n"); + return false; + } + } + + if (IS_ENABLED(CONFIG_DRAM_SUN50I_H616_BIT_DELAY_COMPENSATION)) + mctl_phy_bit_delay_compensation(para); + + clrbits_le32(SUNXI_DRAM_PHY0_BASE + 0x60, 4); + + return true; +} + +static bool mctl_ctrl_init(struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + u32 reg_val; + + clrsetbits_le32(&mctl_com->unk_0x500, BIT(24), 0x200); + writel(0x8000, &mctl_ctl->clken); + + setbits_le32(&mctl_com->unk_0x008, 0xff00); + + clrsetbits_le32(&mctl_ctl->sched[0], 0xff00, 0x3000); + + writel(0, &mctl_ctl->hwlpctl); + + setbits_le32(&mctl_com->unk_0x008, 0xff00); + + reg_val = MSTR_BURST_LENGTH(8) | MSTR_ACTIVE_RANKS(para->ranks); + reg_val |= MSTR_DEVICETYPE_DDR3 | MSTR_2TMODE; + if (para->bus_full_width) + reg_val |= MSTR_BUSWIDTH_FULL; + else + reg_val |= MSTR_BUSWIDTH_HALF; + writel(BIT(31) | BIT(30) | reg_val, &mctl_ctl->mstr); + + if (para->ranks == 2) + writel(0x0303, &mctl_ctl->odtmap); + else + writel(0x0201, &mctl_ctl->odtmap); + + writel(0x06000400, &mctl_ctl->odtcfg); + writel(0x06000400, &mctl_ctl->unk_0x2240); + writel(0x06000400, &mctl_ctl->unk_0x3240); + writel(0x06000400, &mctl_ctl->unk_0x4240); + + setbits_le32(&mctl_com->cr, BIT(31)); + + mctl_set_addrmap(para); + + mctl_set_timing_params(para); + + writel(0, &mctl_ctl->pwrctl); + + setbits_le32(&mctl_ctl->dfiupd[0], BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->zqctl[0], BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->unk_0x2180, BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->unk_0x3180, BIT(31) | BIT(30)); + setbits_le32(&mctl_ctl->unk_0x4180, BIT(31) | BIT(30)); + + setbits_le32(&mctl_ctl->rfshctl3, BIT(0)); + clrbits_le32(&mctl_ctl->dfimisc, BIT(0)); + + writel(0, &mctl_com->maer0); + writel(0, &mctl_com->maer1); + writel(0, &mctl_com->maer2); + + writel(0x20, &mctl_ctl->pwrctl); + setbits_le32(&mctl_ctl->clken, BIT(8)); + + clrsetbits_le32(&mctl_com->unk_0x500, BIT(24), 0x300); + /* this write seems to enable PHY MMIO region */ + setbits_le32(&mctl_com->unk_0x500, BIT(24)); + + if (!mctl_phy_init(para)) + return false; + + writel(0, &mctl_ctl->swctl); + clrbits_le32(&mctl_ctl->rfshctl3, BIT(0)); + + setbits_le32(&mctl_com->unk_0x014, BIT(31)); + writel(0xffffffff, &mctl_com->maer0); + writel(0x7ff, &mctl_com->maer1); + writel(0xffff, &mctl_com->maer2); + + writel(1, &mctl_ctl->swctl); + mctl_await_completion(&mctl_ctl->swstat, 1, 1); + + return true; +} + +static bool mctl_core_init(struct dram_para *para) +{ + mctl_sys_init(para); + + return mctl_ctrl_init(para); +} + +static void mctl_auto_detect_rank_width(struct dram_para *para) +{ + /* this is minimum size that it's supported */ + para->cols = 8; + para->rows = 13; + + /* + * Strategy here is to test most demanding combination first and least + * demanding last, otherwise HW might not be fully utilized. For + * example, half bus width and rank = 1 combination would also work + * on HW with full bus width and rank = 2, but only 1/4 RAM would be + * visible. + */ + + debug("testing 32-bit width, rank = 2\n"); + para->bus_full_width = 1; + para->ranks = 2; + if (mctl_core_init(para)) + return; + + debug("testing 32-bit width, rank = 1\n"); + para->bus_full_width = 1; + para->ranks = 1; + if (mctl_core_init(para)) + return; + + debug("testing 16-bit width, rank = 2\n"); + para->bus_full_width = 0; + para->ranks = 2; + if (mctl_core_init(para)) + return; + + debug("testing 16-bit width, rank = 1\n"); + para->bus_full_width = 0; + para->ranks = 1; + if (mctl_core_init(para)) + return; + + panic("This DRAM setup is currently not supported.\n"); +} + +static void mctl_auto_detect_dram_size(struct dram_para *para) +{ + /* detect row address bits */ + para->cols = 8; + para->rows = 18; + mctl_core_init(para); + + for (para->rows = 13; para->rows < 18; para->rows++) { + /* 8 banks, 8 bit per byte and 16/32 bit width */ + if (mctl_mem_matches((1 << (para->rows + para->cols + + 4 + para->bus_full_width)))) + break; + } + + /* detect column address bits */ + para->cols = 11; + mctl_core_init(para); + + for (para->cols = 8; para->cols < 11; para->cols++) { + /* 8 bits per byte and 16/32 bit width */ + if (mctl_mem_matches(1 << (para->cols + 1 + + para->bus_full_width))) + break; + } +} + +static unsigned long mctl_calc_size(struct dram_para *para) +{ + u8 width = para->bus_full_width ? 4 : 2; + + /* 8 banks */ + return (1ULL << (para->cols + para->rows + 3)) * width * para->ranks; +} + +unsigned long sunxi_dram_init(void) +{ + struct dram_para para = { + .clk = CONFIG_DRAM_CLK, + .type = SUNXI_DRAM_TYPE_DDR3, + }; + unsigned long size; + + setbits_le32(0x7010310, BIT(8)); + clrbits_le32(0x7010318, 0x3f); + + mctl_auto_detect_rank_width(¶); + mctl_auto_detect_dram_size(¶); + + mctl_core_init(¶); + + size = mctl_calc_size(¶); + + mctl_set_master_priority(); + + return size; +}; diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun6i.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun6i.c new file mode 100644 index 000000000..0590110d4 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun6i.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sun6i platform dram controller init. + * + * (C) Copyright 2007-2012 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing <bergxing@allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + * + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + */ +#include <common.h> +#include <errno.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> +#include <linux/delay.h> + +#define DRAM_CLK (CONFIG_DRAM_CLK * 1000000) + +struct dram_sun6i_para { + u8 bus_width; + u8 chan; + u8 rank; + u8 rows; + u16 page_size; +}; + +static void mctl_sys_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + const int dram_clk_div = 2; + + clock_set_pll5(DRAM_CLK * dram_clk_div, false); + + clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV0_MASK, + CCM_DRAMCLK_CFG_DIV0(dram_clk_div) | CCM_DRAMCLK_CFG_RST | + CCM_DRAMCLK_CFG_UPD); + mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); + + writel(MDFS_CLK_DEFAULT, &ccm->mdfs_clk_cfg); + + /* deassert mctl reset */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + + /* enable mctl clock */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); +} + +static void mctl_dll_init(int ch_index, struct dram_sun6i_para *para) +{ + struct sunxi_mctl_phy_reg *mctl_phy; + + if (ch_index == 0) + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + else + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + + /* disable + reset dlls */ + writel(MCTL_DLLCR_DISABLE, &mctl_phy->acdllcr); + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx0dllcr); + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx1dllcr); + if (para->bus_width == 32) { + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx2dllcr); + writel(MCTL_DLLCR_DISABLE, &mctl_phy->dx3dllcr); + } + udelay(2); + + /* enable + reset dlls */ + writel(0, &mctl_phy->acdllcr); + writel(0, &mctl_phy->dx0dllcr); + writel(0, &mctl_phy->dx1dllcr); + if (para->bus_width == 32) { + writel(0, &mctl_phy->dx2dllcr); + writel(0, &mctl_phy->dx3dllcr); + } + udelay(22); + + /* enable and release reset of dlls */ + writel(MCTL_DLLCR_NRESET, &mctl_phy->acdllcr); + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx0dllcr); + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx1dllcr); + if (para->bus_width == 32) { + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx2dllcr); + writel(MCTL_DLLCR_NRESET, &mctl_phy->dx3dllcr); + } + udelay(22); +} + +static bool mctl_rank_detect(u32 *gsr0, int rank) +{ + const u32 done = MCTL_DX_GSR0_RANK0_TRAIN_DONE << rank; + const u32 err = MCTL_DX_GSR0_RANK0_TRAIN_ERR << rank; + + mctl_await_completion(gsr0, done, done); + mctl_await_completion(gsr0 + 0x10, done, done); + + return !(readl(gsr0) & err) && !(readl(gsr0 + 0x10) & err); +} + +static void mctl_channel_init(int ch_index, struct dram_sun6i_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg *mctl_ctl; + struct sunxi_mctl_phy_reg *mctl_phy; + + if (ch_index == 0) { + mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + } else { + mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL1_BASE; + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + } + + writel(MCTL_MCMD_NOP, &mctl_ctl->mcmd); + mctl_await_completion(&mctl_ctl->mcmd, MCTL_MCMD_BUSY, 0); + + /* PHY initialization */ + writel(MCTL_PGCR, &mctl_phy->pgcr); + writel(MCTL_MR0, &mctl_phy->mr0); + writel(MCTL_MR1, &mctl_phy->mr1); + writel(MCTL_MR2, &mctl_phy->mr2); + writel(MCTL_MR3, &mctl_phy->mr3); + + writel((MCTL_TITMSRST << 18) | (MCTL_TDLLLOCK << 6) | MCTL_TDLLSRST, + &mctl_phy->ptr0); + + writel((MCTL_TDINIT1 << 19) | MCTL_TDINIT0, &mctl_phy->ptr1); + writel((MCTL_TDINIT3 << 17) | MCTL_TDINIT2, &mctl_phy->ptr2); + + writel((MCTL_TCCD << 31) | (MCTL_TRC << 25) | (MCTL_TRRD << 21) | + (MCTL_TRAS << 16) | (MCTL_TRCD << 12) | (MCTL_TRP << 8) | + (MCTL_TWTR << 5) | (MCTL_TRTP << 2) | (MCTL_TMRD << 0), + &mctl_phy->dtpr0); + + writel((MCTL_TDQSCKMAX << 27) | (MCTL_TDQSCK << 24) | + (MCTL_TRFC << 16) | (MCTL_TRTODT << 11) | + ((MCTL_TMOD - 12) << 9) | (MCTL_TFAW << 3) | (0 << 2) | + (MCTL_TAOND << 0), &mctl_phy->dtpr1); + + writel((MCTL_TDLLK << 19) | (MCTL_TCKE << 15) | (MCTL_TXPDLL << 10) | + (MCTL_TEXSR << 0), &mctl_phy->dtpr2); + + writel(1, &mctl_ctl->dfitphyupdtype0); + writel(MCTL_DCR_DDR3, &mctl_phy->dcr); + writel(MCTL_DSGCR, &mctl_phy->dsgcr); + writel(MCTL_DXCCR, &mctl_phy->dxccr); + writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx0gcr); + writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx1gcr); + writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx2gcr); + writel(MCTL_DX_GCR | MCTL_DX_GCR_EN, &mctl_phy->dx3gcr); + + mctl_await_completion(&mctl_phy->pgsr, 0x03, 0x03); + + writel(CONFIG_DRAM_ZQ, &mctl_phy->zq0cr1); + + setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); + writel(MCTL_PIR_STEP1, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr, 0x1f, 0x1f); + + /* rank detect */ + if (!mctl_rank_detect(&mctl_phy->dx0gsr0, 1)) { + para->rank = 1; + clrbits_le32(&mctl_phy->pgcr, MCTL_PGCR_RANK); + } + + /* + * channel detect, check channel 1 dx0 and dx1 have rank 0, if not + * assume nothing is connected to channel 1. + */ + if (ch_index == 1 && !mctl_rank_detect(&mctl_phy->dx0gsr0, 0)) { + para->chan = 1; + clrbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN); + return; + } + + /* bus width detect, if dx2 and dx3 don't have rank 0, assume 16 bit */ + if (!mctl_rank_detect(&mctl_phy->dx2gsr0, 0)) { + para->bus_width = 16; + para->page_size = 2048; + setbits_le32(&mctl_phy->dx2dllcr, MCTL_DLLCR_DISABLE); + setbits_le32(&mctl_phy->dx3dllcr, MCTL_DLLCR_DISABLE); + clrbits_le32(&mctl_phy->dx2gcr, MCTL_DX_GCR_EN); + clrbits_le32(&mctl_phy->dx3gcr, MCTL_DX_GCR_EN); + } + + setbits_le32(&mctl_phy->pir, MCTL_PIR_CLEAR_STATUS); + writel(MCTL_PIR_STEP2, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr, 0x11, 0x11); + + if (readl(&mctl_phy->pgsr) & MCTL_PGSR_TRAIN_ERR_MASK) + panic("Training error initialising DRAM\n"); + + /* Move to configure state */ + writel(MCTL_SCTL_CONFIG, &mctl_ctl->sctl); + mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x01); + + /* Set number of clks per micro-second */ + writel(DRAM_CLK / 1000000, &mctl_ctl->togcnt1u); + /* Set number of clks per 100 nano-seconds */ + writel(DRAM_CLK / 10000000, &mctl_ctl->togcnt100n); + /* Set memory timing registers */ + writel(MCTL_TREFI, &mctl_ctl->trefi); + writel(MCTL_TMRD, &mctl_ctl->tmrd); + writel(MCTL_TRFC, &mctl_ctl->trfc); + writel((MCTL_TPREA << 16) | MCTL_TRP, &mctl_ctl->trp); + writel(MCTL_TRTW, &mctl_ctl->trtw); + writel(MCTL_TAL, &mctl_ctl->tal); + writel(MCTL_TCL, &mctl_ctl->tcl); + writel(MCTL_TCWL, &mctl_ctl->tcwl); + writel(MCTL_TRAS, &mctl_ctl->tras); + writel(MCTL_TRC, &mctl_ctl->trc); + writel(MCTL_TRCD, &mctl_ctl->trcd); + writel(MCTL_TRRD, &mctl_ctl->trrd); + writel(MCTL_TRTP, &mctl_ctl->trtp); + writel(MCTL_TWR, &mctl_ctl->twr); + writel(MCTL_TWTR, &mctl_ctl->twtr); + writel(MCTL_TEXSR, &mctl_ctl->texsr); + writel(MCTL_TXP, &mctl_ctl->txp); + writel(MCTL_TXPDLL, &mctl_ctl->txpdll); + writel(MCTL_TZQCS, &mctl_ctl->tzqcs); + writel(MCTL_TZQCSI, &mctl_ctl->tzqcsi); + writel(MCTL_TDQS, &mctl_ctl->tdqs); + writel(MCTL_TCKSRE, &mctl_ctl->tcksre); + writel(MCTL_TCKSRX, &mctl_ctl->tcksrx); + writel(MCTL_TCKE, &mctl_ctl->tcke); + writel(MCTL_TMOD, &mctl_ctl->tmod); + writel(MCTL_TRSTL, &mctl_ctl->trstl); + writel(MCTL_TZQCL, &mctl_ctl->tzqcl); + writel(MCTL_TMRR, &mctl_ctl->tmrr); + writel(MCTL_TCKESR, &mctl_ctl->tckesr); + writel(MCTL_TDPD, &mctl_ctl->tdpd); + + /* Unknown magic performed by boot0 */ + setbits_le32(&mctl_ctl->dfiodtcfg, 1 << 3); + clrbits_le32(&mctl_ctl->dfiodtcfg1, 0x1f); + + /* Select 16/32-bits mode for MCTL */ + if (para->bus_width == 16) + setbits_le32(&mctl_ctl->ppcfg, 1); + + /* Set DFI timing registers */ + writel(MCTL_TCWL, &mctl_ctl->dfitphywrl); + writel(MCTL_TCL - 1, &mctl_ctl->dfitrdden); + writel(MCTL_DFITPHYRDL, &mctl_ctl->dfitphyrdl); + writel(MCTL_DFISTCFG0, &mctl_ctl->dfistcfg0); + + writel(MCTL_MCFG_DDR3, &mctl_ctl->mcfg); + + /* DFI update configuration register */ + writel(MCTL_DFIUPDCFG_UPD, &mctl_ctl->dfiupdcfg); + + /* Move to access state */ + writel(MCTL_SCTL_ACCESS, &mctl_ctl->sctl); + mctl_await_completion(&mctl_ctl->sstat, 0x07, 0x03); +} + +static void mctl_com_init(struct dram_sun6i_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy1 = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + struct sunxi_prcm_reg * const prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + writel(MCTL_CR_UNKNOWN | MCTL_CR_CHANNEL(para->chan) | MCTL_CR_DDR3 | + ((para->bus_width == 32) ? MCTL_CR_BUSW32 : MCTL_CR_BUSW16) | + MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) | + MCTL_CR_BANK(1) | MCTL_CR_RANK(para->rank), &mctl_com->cr); + + /* Unknown magic performed by boot0 */ + setbits_le32(&mctl_com->dbgcr, (1 << 6)); + + if (para->chan == 1) { + /* Shutdown channel 1 */ + setbits_le32(&mctl_phy1->aciocr, MCTL_ACIOCR_DISABLE); + setbits_le32(&mctl_phy1->dxccr, MCTL_DXCCR_DISABLE); + clrbits_le32(&mctl_phy1->dsgcr, MCTL_DSGCR_ENABLE); + /* + * CH0 ?? this is what boot0 does. Leave as is until we can + * confirm this. + */ + setbits_le32(&prcm->vdd_sys_pwroff, + PRCM_VDD_SYS_DRAM_CH0_PAD_HOLD_PWROFF); + } +} + +static void mctl_port_cfg(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable DRAM AXI clock for CPU access */ + setbits_le32(&ccm->axi_gate, 1 << AXI_GATE_OFFSET_DRAM); + + /* Bunch of magic writes performed by boot0 */ + writel(0x00400302, &mctl_com->rmcr[0]); + writel(0x01000307, &mctl_com->rmcr[1]); + writel(0x00400302, &mctl_com->rmcr[2]); + writel(0x01000307, &mctl_com->rmcr[3]); + writel(0x01000307, &mctl_com->rmcr[4]); + writel(0x01000303, &mctl_com->rmcr[6]); + writel(0x01000303, &mctl_com->mmcr[0]); + writel(0x00400310, &mctl_com->mmcr[1]); + writel(0x01000307, &mctl_com->mmcr[2]); + writel(0x01000303, &mctl_com->mmcr[3]); + writel(0x01800303, &mctl_com->mmcr[4]); + writel(0x01800303, &mctl_com->mmcr[5]); + writel(0x01800303, &mctl_com->mmcr[6]); + writel(0x01800303, &mctl_com->mmcr[7]); + writel(0x01000303, &mctl_com->mmcr[8]); + writel(0x00000002, &mctl_com->mmcr[15]); + writel(0x00000310, &mctl_com->mbagcr[0]); + writel(0x00400310, &mctl_com->mbagcr[1]); + writel(0x00400310, &mctl_com->mbagcr[2]); + writel(0x00000307, &mctl_com->mbagcr[3]); + writel(0x00000317, &mctl_com->mbagcr[4]); + writel(0x00000307, &mctl_com->mbagcr[5]); +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + u32 offset; + int bank, bus, columns; + + /* Set initial parameters, these get modified by the autodetect code */ + struct dram_sun6i_para para = { + .bus_width = 32, + .chan = 2, + .rank = 2, + .page_size = 4096, + .rows = 16, + }; + + /* A31s only has one channel */ + if (sunxi_get_ss_bonding_id() == SUNXI_SS_BOND_ID_A31S) + para.chan = 1; + + mctl_sys_init(); + + mctl_dll_init(0, ¶); + setbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN); + + if (para.chan == 2) { + mctl_dll_init(1, ¶); + setbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN); + } + + setbits_le32(&mctl_com->ccr, MCTL_CCR_MASTER_CLK_EN); + + mctl_channel_init(0, ¶); + if (para.chan == 2) + mctl_channel_init(1, ¶); + + mctl_com_init(¶); + mctl_port_cfg(); + + /* + * Change to 1 ch / sequence / 8192 byte pages / 16 rows / + * 8 bit banks / 1 rank mode. + */ + clrsetbits_le32(&mctl_com->cr, + MCTL_CR_CHANNEL_MASK | MCTL_CR_PAGE_SIZE_MASK | + MCTL_CR_ROW_MASK | MCTL_CR_BANK_MASK | MCTL_CR_RANK_MASK, + MCTL_CR_CHANNEL(1) | MCTL_CR_SEQUENCE | + MCTL_CR_PAGE_SIZE(8192) | MCTL_CR_ROW(16) | + MCTL_CR_BANK(1) | MCTL_CR_RANK(1)); + + /* Detect and set page size */ + for (columns = 7; columns < 20; columns++) { + if (mctl_mem_matches(1 << columns)) + break; + } + bus = (para.bus_width == 32) ? 2 : 1; + columns -= bus; + para.page_size = (1 << columns) * (bus << 1); + clrsetbits_le32(&mctl_com->cr, MCTL_CR_PAGE_SIZE_MASK, + MCTL_CR_PAGE_SIZE(para.page_size)); + + /* Detect and set rows */ + for (para.rows = 11; para.rows < 16; para.rows++) { + offset = 1 << (para.rows + columns + bus); + if (mctl_mem_matches(offset)) + break; + } + clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK, + MCTL_CR_ROW(para.rows)); + + /* Detect bank size */ + offset = 1 << (para.rows + columns + bus + 2); + bank = mctl_mem_matches(offset) ? 0 : 1; + + /* Restore interleave, chan and rank values, set bank size */ + clrsetbits_le32(&mctl_com->cr, + MCTL_CR_CHANNEL_MASK | MCTL_CR_SEQUENCE | + MCTL_CR_BANK_MASK | MCTL_CR_RANK_MASK, + MCTL_CR_CHANNEL(para.chan) | MCTL_CR_BANK(bank) | + MCTL_CR_RANK(para.rank)); + + return 1 << (para.rank + para.rows + bank + columns + para.chan + bus); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a23.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a23.c new file mode 100644 index 000000000..3ed97b59a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a23.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sun8i platform dram controller init. + * + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + */ + +/* + * Note this code uses a lot of magic hex values, that is because this code + * simply replays the init sequence as done by the Allwinner boot0 code, so + * we do not know what these values mean. There are no symbolic constants for + * these magic values, since we do not know how to name them and making up + * names for them is not useful. + * + * The register-layout of the sunxi_mctl_phy_reg-s looks a lot like the one + * found in the TI Keystone2 documentation: + * http://www.ti.com/lit/ug/spruhn7a/spruhn7a.pdf + * "Table4-2 DDR3 PHY Registers" + * This may be used as a (possible) reference for future work / cleanups. + */ + +#include <common.h> +#include <errno.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> +#include <linux/delay.h> + +static const struct dram_para dram_para = { + .clock = CONFIG_DRAM_CLK, + .type = 3, + .zq = CONFIG_DRAM_ZQ, + .odt_en = IS_ENABLED(CONFIG_DRAM_ODT_EN), + .odt_correction = CONFIG_DRAM_ODT_CORRECTION, + .para1 = 0, /* not used (only used when tpr13 bit 31 is set */ + .para2 = 0, /* not used (only used when tpr13 bit 31 is set */ + .mr0 = 6736, + .mr1 = 4, + .mr2 = 16, + .mr3 = 0, + /* tpr0 - 10 contain timing constants or-ed together in u32 vals */ + .tpr0 = 0x2ab83def, + .tpr1 = 0x18082356, + .tpr2 = 0x00034156, + .tpr3 = 0x448c5533, + .tpr4 = 0x08010d00, + .tpr5 = 0x0340b20f, + .tpr6 = 0x20d118cc, + .tpr7 = 0x14062485, + .tpr8 = 0x220d1d52, + .tpr9 = 0x1e078c22, + .tpr10 = 0x3c, + .tpr11 = 0, /* not used */ + .tpr12 = 0, /* not used */ + .tpr13 = 0x30000, +}; + +static void mctl_sys_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + /* enable pll5, note the divide by 2 is deliberate! */ + clock_set_pll5(dram_para.clock * 1000000 / 2, + dram_para.tpr13 & 0x40000); + + /* deassert ahb mctl reset */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + + /* enable ahb mctl clock */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); +} + +static void mctl_apply_odt_correction(u32 *reg, int correction) +{ + int val; + + val = (readl(reg) >> 8) & 0xff; + val += correction; + + /* clamp */ + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + + clrsetbits_le32(reg, 0xff00, val << 8); +} + +static void mctl_init(u32 *bus_width) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + + if (dram_para.tpr13 & 0x20) + writel(0x40b, &mctl_phy->dcr); + else + writel(0x1000040b, &mctl_phy->dcr); + + if (dram_para.clock >= 480) + writel(0x5c000, &mctl_phy->dllgcr); + else + writel(0xdc000, &mctl_phy->dllgcr); + + writel(0x0a003e3f, &mctl_phy->pgcr0); + writel(0x03008421, &mctl_phy->pgcr1); + + writel(dram_para.mr0, &mctl_phy->mr0); + writel(dram_para.mr1, &mctl_phy->mr1); + writel(dram_para.mr2, &mctl_phy->mr2); + writel(dram_para.mr3, &mctl_phy->mr3); + + if (!(dram_para.tpr13 & 0x10000)) { + clrsetbits_le32(&mctl_phy->dx0gcr, 0x3800, 0x2000); + clrsetbits_le32(&mctl_phy->dx1gcr, 0x3800, 0x2000); + } + + /* + * All the masking and shifting below converts what I assume are DDR + * timing constants from Allwinner dram_para tpr format to the actual + * timing registers format. + */ + + writel((dram_para.tpr0 & 0x000fffff), &mctl_phy->ptr2); + writel((dram_para.tpr1 & 0x1fffffff), &mctl_phy->ptr3); + writel((dram_para.tpr0 & 0x3ff00000) >> 2 | + (dram_para.tpr2 & 0x0003ffff), &mctl_phy->ptr4); + + writel(dram_para.tpr3, &mctl_phy->dtpr0); + writel(dram_para.tpr4, &mctl_phy->dtpr2); + + writel(0x01000081, &mctl_phy->dtcr); + + if (dram_para.clock <= 240 || !dram_para.odt_en) { + clrbits_le32(&mctl_phy->dx0gcr, 0x600); + clrbits_le32(&mctl_phy->dx1gcr, 0x600); + } + if (dram_para.clock <= 240) { + writel(0, &mctl_phy->odtcr); + writel(0, &mctl_ctl->odtmap); + } + + writel(((dram_para.tpr5 & 0x0f00) << 12) | + ((dram_para.tpr5 & 0x00f8) << 9) | + ((dram_para.tpr5 & 0x0007) << 8), + &mctl_ctl->rfshctl0); + + writel(((dram_para.tpr5 & 0x0003f000) << 12) | + ((dram_para.tpr5 & 0x00fc0000) >> 2) | + ((dram_para.tpr5 & 0x3f000000) >> 16) | + ((dram_para.tpr6 & 0x0000003f) >> 0), + &mctl_ctl->dramtmg0); + + writel(((dram_para.tpr6 & 0x000007c0) << 10) | + ((dram_para.tpr6 & 0x0000f800) >> 3) | + ((dram_para.tpr6 & 0x003f0000) >> 16), + &mctl_ctl->dramtmg1); + + writel(((dram_para.tpr6 & 0x0fc00000) << 2) | + ((dram_para.tpr7 & 0x0000001f) << 16) | + ((dram_para.tpr7 & 0x000003e0) << 3) | + ((dram_para.tpr7 & 0x0000fc00) >> 10), + &mctl_ctl->dramtmg2); + + writel(((dram_para.tpr7 & 0x03ff0000) >> 16) | + ((dram_para.tpr6 & 0xf0000000) >> 16), + &mctl_ctl->dramtmg3); + + writel(((dram_para.tpr7 & 0x3c000000) >> 2 ) | + ((dram_para.tpr8 & 0x00000007) << 16) | + ((dram_para.tpr8 & 0x00000038) << 5) | + ((dram_para.tpr8 & 0x000003c0) >> 6), + &mctl_ctl->dramtmg4); + + writel(((dram_para.tpr8 & 0x00003c00) << 14) | + ((dram_para.tpr8 & 0x0003c000) << 2) | + ((dram_para.tpr8 & 0x00fc0000) >> 10) | + ((dram_para.tpr8 & 0x0f000000) >> 24), + &mctl_ctl->dramtmg5); + + writel(0x00000008, &mctl_ctl->dramtmg8); + + writel(((dram_para.tpr8 & 0xf0000000) >> 4) | + ((dram_para.tpr9 & 0x00007c00) << 6) | + ((dram_para.tpr9 & 0x000003e0) << 3) | + ((dram_para.tpr9 & 0x0000001f) >> 0), + &mctl_ctl->pitmg0); + + setbits_le32(&mctl_ctl->pitmg1, 0x80000); + + writel(((dram_para.tpr9 & 0x003f8000) << 9) | 0x2001, + &mctl_ctl->sched); + + writel((dram_para.mr0 << 16) | dram_para.mr1, &mctl_ctl->init3); + writel((dram_para.mr2 << 16) | dram_para.mr3, &mctl_ctl->init4); + + writel(0x00000000, &mctl_ctl->pimisc); + writel(0x80000000, &mctl_ctl->upd0); + + writel(((dram_para.tpr9 & 0xffc00000) >> 22) | + ((dram_para.tpr10 & 0x00000fff) << 16), + &mctl_ctl->rfshtmg); + + if (dram_para.tpr13 & 0x20) + writel(0x01040001, &mctl_ctl->mstr); + else + writel(0x01040401, &mctl_ctl->mstr); + + if (!(dram_para.tpr13 & 0x20000)) { + writel(0x00000002, &mctl_ctl->pwrctl); + writel(0x00008001, &mctl_ctl->pwrtmg); + } + + writel(0x00000001, &mctl_ctl->rfshctl3); + writel(0x00000001, &mctl_ctl->pimisc); + + /* deassert dram_clk_cfg reset */ + setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST); + + setbits_le32(&mctl_com->ccr, 0x80000); + + /* zq stuff */ + writel((dram_para.zq >> 8) & 0xff, &mctl_phy->zqcr1); + + writel(0x00000003, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr0, 0x09, 0x09); + + writel(readl(&mctl_phy->zqsr0) | 0x10000000, &mctl_phy->zqcr2); + writel(dram_para.zq & 0xff, &mctl_phy->zqcr1); + + /* A23-v1.0 SDK uses 0xfdf3, A23-v2.0 SDK uses 0x5f3 */ + writel(0x000005f3, &mctl_phy->pir); + udelay(10); + mctl_await_completion(&mctl_phy->pgsr0, 0x03, 0x03); + + if (readl(&mctl_phy->dx1gsr0) & 0x1000000) { + *bus_width = 8; + writel(0, &mctl_phy->dx1gcr); + writel(dram_para.zq & 0xff, &mctl_phy->zqcr1); + writel(0x5f3, &mctl_phy->pir); + udelay(10000); + setbits_le32(&mctl_ctl->mstr, 0x1000); + } else + *bus_width = 16; + + if (dram_para.odt_correction) { + mctl_apply_odt_correction(&mctl_phy->dx0lcdlr1, + dram_para.odt_correction); + mctl_apply_odt_correction(&mctl_phy->dx1lcdlr1, + dram_para.odt_correction); + } + + mctl_await_completion(&mctl_ctl->statr, 0x01, 0x01); + + writel(0x08003e3f, &mctl_phy->pgcr0); + writel(0x00000000, &mctl_ctl->rfshctl3); +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + const u32 columns = 13; + u32 bus, bus_width, offset, page_size, rows; + + mctl_sys_init(); + mctl_init(&bus_width); + + if (bus_width == 16) { + page_size = 8; + bus = 1; + } else { + page_size = 7; + bus = 0; + } + + if (!(dram_para.tpr13 & 0x80000000)) { + /* Detect and set rows */ + writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size), + &mctl_com->cr); + setbits_le32(&mctl_com->swonr, 0x0003ffff); + for (rows = 11; rows < 16; rows++) { + offset = 1 << (rows + columns + bus); + if (mctl_mem_matches(offset)) + break; + } + clrsetbits_le32(&mctl_com->cr, MCTL_CR_ROW_MASK, + MCTL_CR_ROW(rows)); + } else { + rows = (dram_para.para1 >> 16) & 0xff; + writel(((dram_para.para2 & 0x000000f0) << 11) | + ((rows - 1) << 4) | + ((dram_para.para1 & 0x0f000000) >> 22) | + 0x31000 | MCTL_CR_PAGE_SIZE(page_size), + &mctl_com->cr); + setbits_le32(&mctl_com->swonr, 0x0003ffff); + } + + /* Setup DRAM master priority? If this is left out things still work */ + writel(0x00000008, &mctl_com->mcr0_0); + writel(0x0001000d, &mctl_com->mcr1_0); + writel(0x00000004, &mctl_com->mcr0_1); + writel(0x00000080, &mctl_com->mcr1_1); + writel(0x00000004, &mctl_com->mcr0_2); + writel(0x00000019, &mctl_com->mcr1_2); + writel(0x00000004, &mctl_com->mcr0_3); + writel(0x00000080, &mctl_com->mcr1_3); + writel(0x00000004, &mctl_com->mcr0_4); + writel(0x01010040, &mctl_com->mcr1_4); + writel(0x00000004, &mctl_com->mcr0_5); + writel(0x0001002f, &mctl_com->mcr1_5); + writel(0x00000004, &mctl_com->mcr0_6); + writel(0x00010020, &mctl_com->mcr1_6); + writel(0x00000004, &mctl_com->mcr0_7); + writel(0x00010020, &mctl_com->mcr1_7); + writel(0x00000008, &mctl_com->mcr0_8); + writel(0x00000001, &mctl_com->mcr1_8); + writel(0x00000008, &mctl_com->mcr0_9); + writel(0x00000005, &mctl_com->mcr1_9); + writel(0x00000008, &mctl_com->mcr0_10); + writel(0x00000003, &mctl_com->mcr1_10); + writel(0x00000008, &mctl_com->mcr0_11); + writel(0x00000005, &mctl_com->mcr1_11); + writel(0x00000008, &mctl_com->mcr0_12); + writel(0x00000003, &mctl_com->mcr1_12); + writel(0x00000008, &mctl_com->mcr0_13); + writel(0x00000004, &mctl_com->mcr1_13); + writel(0x00000008, &mctl_com->mcr0_14); + writel(0x00000002, &mctl_com->mcr1_14); + writel(0x00000008, &mctl_com->mcr0_15); + writel(0x00000003, &mctl_com->mcr1_15); + writel(0x00010138, &mctl_com->bwcr); + + return 1 << (rows + columns + bus); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a33.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a33.c new file mode 100644 index 000000000..d99a38b10 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a33.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sun8i a33 platform dram controller init. + * + * (C) Copyright 2007-2015 Allwinner Technology Co. + * Jerry Wang <wangflord@allwinnertech.com> + * (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com> + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + */ +#include <common.h> +#include <errno.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> +#include <linux/delay.h> + +/* PLL runs at 2x dram-clk, controller runs at PLL / 4 (dram-clk / 2) */ +#define DRAM_CLK_MUL 2 +#define DRAM_CLK_DIV 4 +#define DRAM_SIGMA_DELTA_ENABLE 1 + +struct dram_para { + u8 cs1; + u8 seq; + u8 bank; + u8 rank; + u8 rows; + u8 bus_width; + u16 page_size; +}; + +static void mctl_set_cr(struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + writel(MCTL_CR_CS1_CONTROL(para->cs1) | MCTL_CR_UNKNOWN | + MCTL_CR_CHANNEL(1) | MCTL_CR_DDR3 | + (para->seq ? MCTL_CR_SEQUENCE : 0) | + ((para->bus_width == 16) ? MCTL_CR_BUSW16 : MCTL_CR_BUSW8) | + MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) | + MCTL_CR_BANK(para->bank) | MCTL_CR_RANK(para->rank), + &mctl_com->cr); +} + +static void auto_detect_dram_size(struct dram_para *para) +{ + u8 orig_rank = para->rank; + int rows, columns; + + /* Row detect */ + para->page_size = 512; + para->seq = 1; + para->rows = 16; + para->rank = 1; + mctl_set_cr(para); + for (rows = 11 ; rows < 16 ; rows++) { + if (mctl_mem_matches(1 << (rows + 9))) /* row-column */ + break; + } + + /* Column (page size) detect */ + para->rows = 11; + para->page_size = 8192; + mctl_set_cr(para); + for (columns = 9 ; columns < 13 ; columns++) { + if (mctl_mem_matches(1 << columns)) + break; + } + + para->seq = 0; + para->rank = orig_rank; + para->rows = rows; + para->page_size = 1 << columns; + mctl_set_cr(para); +} + +static inline int ns_to_t(int nanoseconds) +{ + const unsigned int ctrl_freq = + CONFIG_DRAM_CLK * DRAM_CLK_MUL / DRAM_CLK_DIV; + + return (ctrl_freq * nanoseconds + 999) / 1000; +} + +static void auto_set_timing_para(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + u32 reg_val; + + u8 tccd = 2; + u8 tfaw = ns_to_t(50); + u8 trrd = max(ns_to_t(10), 4); + u8 trcd = ns_to_t(15); + u8 trc = ns_to_t(53); + u8 txp = max(ns_to_t(8), 3); + u8 twtr = max(ns_to_t(8), 4); + u8 trtp = max(ns_to_t(8), 4); + u8 twr = max(ns_to_t(15), 3); + u8 trp = ns_to_t(15); + u8 tras = ns_to_t(38); + + u16 trefi = ns_to_t(7800) / 32; + u16 trfc = ns_to_t(350); + + /* Fixed timing parameters */ + u8 tmrw = 0; + u8 tmrd = 4; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 4; + u8 trasmax = 24; + u8 tcl = 6; /* CL 12 */ + u8 tcwl = 4; /* CWL 8 */ + u8 t_rdata_en = 4; + u8 wr_latency = 2; + + u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */ + u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; /* 360ns */ + u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */ + u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */ + u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */ + + /* Set work mode register */ + mctl_set_cr(para); + /* Set mode register */ + writel(MCTL_MR0, &mctl_ctl->mr0); + writel(MCTL_MR1, &mctl_ctl->mr1); + writel(MCTL_MR2, &mctl_ctl->mr2); + writel(MCTL_MR3, &mctl_ctl->mr3); + /* Set dram timing */ + reg_val = (twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0); + writel(reg_val, &mctl_ctl->dramtmg0); + reg_val = (txp << 16) | (trtp << 8) | (trc << 0); + writel(reg_val, &mctl_ctl->dramtmg1); + reg_val = (tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0); + writel(reg_val, &mctl_ctl->dramtmg2); + reg_val = (tmrw << 16) | (tmrd << 12) | (tmod << 0); + writel(reg_val, &mctl_ctl->dramtmg3); + reg_val = (trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0); + writel(reg_val, &mctl_ctl->dramtmg4); + reg_val = (tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | (tcke << 0); + writel(reg_val, &mctl_ctl->dramtmg5); + /* Set two rank timing and exit self-refresh timing */ + reg_val = readl(&mctl_ctl->dramtmg8); + reg_val &= ~(0xff << 8); + reg_val &= ~(0xff << 0); + reg_val |= (0x33 << 8); + reg_val |= (0x10 << 0); + writel(reg_val, &mctl_ctl->dramtmg8); + /* Set phy interface time */ + reg_val = (0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) + | (wr_latency << 0); + /* PHY interface write latency and read latency configure */ + writel(reg_val, &mctl_ctl->pitmg0); + /* Set phy time PTR0-2 use default */ + writel(((tdinit0 << 0) | (tdinit1 << 20)), &mctl_ctl->ptr3); + writel(((tdinit2 << 0) | (tdinit3 << 20)), &mctl_ctl->ptr4); + /* Set refresh timing */ + reg_val = (trefi << 16) | (trfc << 0); + writel(reg_val, &mctl_ctl->rfshtmg); +} + +static void mctl_set_pir(u32 val) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + writel(val, &mctl_ctl->pir); + mctl_await_completion(&mctl_ctl->pgsr0, 0x1, 0x1); +} + +static void mctl_data_train_cfg(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + if (para->rank == 2) + clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x3 << 24); + else + clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x1 << 24); +} + +static int mctl_train_dram(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + mctl_data_train_cfg(para); + mctl_set_pir(0x5f3); + + return ((readl(&mctl_ctl->pgsr0) >> 20) & 0xff) ? -EIO : 0; +} + +static int mctl_channel_init(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + u32 low_data_lines_status; /* Training status of datalines 0 - 7 */ + u32 high_data_lines_status; /* Training status of datalines 8 - 15 */ + + auto_set_timing_para(para); + + /* Disable dram VTC */ + clrbits_le32(&mctl_ctl->pgcr0, 0x3f << 0); + + /* Set ODT */ + if ((CONFIG_DRAM_CLK > 400) && IS_ENABLED(CONFIG_DRAM_ODT_EN)) { + setbits_le32(DXnGCR0(0), 0x3 << 9); + setbits_le32(DXnGCR0(1), 0x3 << 9); + } else { + clrbits_le32(DXnGCR0(0), 0x3 << 9); + clrbits_le32(DXnGCR0(1), 0x3 << 9); + } + + /* set PLL configuration */ + if (CONFIG_DRAM_CLK >= 480) + setbits_le32(&mctl_ctl->pllgcr, 0x1 << 18); + else + setbits_le32(&mctl_ctl->pllgcr, 0x3 << 18); + + /* Auto detect dram config, set 2 rank and 16bit bus-width */ + para->cs1 = 0; + para->rank = 2; + para->bus_width = 16; + mctl_set_cr(para); + + /* Open DQS gating */ + clrbits_le32(&mctl_ctl->pgcr2, (0x3 << 6)); + clrbits_le32(&mctl_ctl->dqsgmr, (0x1 << 8) | (0x7)); + + mctl_data_train_cfg(para); + + /* ZQ calibration */ + writel(CONFIG_DRAM_ZQ & 0xff, &mctl_ctl->zqcr1); + /* CA calibration */ + mctl_set_pir(0x00000003); + /* More ZQ calibration */ + writel(readl(&mctl_ctl->zqsr0) | 0x10000000, &mctl_ctl->zqcr2); + writel((CONFIG_DRAM_ZQ >> 8) & 0xff, &mctl_ctl->zqcr1); + + /* DQS gate training */ + if (mctl_train_dram(para) != 0) { + low_data_lines_status = (readl(DXnGSR0(0)) >> 24) & 0x03; + high_data_lines_status = (readl(DXnGSR0(1)) >> 24) & 0x03; + + if (low_data_lines_status == 0x3) + return -EIO; + + /* DRAM has only one rank */ + para->rank = 1; + mctl_set_cr(para); + + if (low_data_lines_status == high_data_lines_status) + goto done; /* 16 bit bus, 1 rank */ + + if (!(low_data_lines_status & high_data_lines_status)) { + /* Retry 16 bit bus-width with CS1 set */ + para->cs1 = 1; + mctl_set_cr(para); + if (mctl_train_dram(para) == 0) + goto done; + } + + /* Try 8 bit bus-width */ + writel(0x0, DXnGCR0(1)); /* Disable high DQ */ + para->cs1 = 0; + para->bus_width = 8; + mctl_set_cr(para); + if (mctl_train_dram(para) != 0) + return -EIO; + } +done: + /* Check the dramc status */ + mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1); + + /* Close DQS gating */ + setbits_le32(&mctl_ctl->pgcr2, 0x3 << 6); + + /* Enable master access */ + writel(0xffffffff, &mctl_com->maer); + + return 0; +} + +static void mctl_sys_init(struct dram_para *para) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + clrsetbits_le32(&ccm->dram_pll_cfg, CCM_DRAMPLL_CFG_SRC_MASK, + CCM_DRAMPLL_CFG_SRC_PLL11); + + clock_set_pll11(CONFIG_DRAM_CLK * 1000000 * DRAM_CLK_MUL, + DRAM_SIGMA_DELTA_ENABLE); + + clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV_MASK, + CCM_DRAMCLK_CFG_DIV(DRAM_CLK_DIV) | + CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD); + mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); + + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET); + setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE); + + /* Set dram master access priority */ + writel(0x0, &mctl_com->mapr); + writel(0x0f802f01, &mctl_ctl->sched); + writel(0x0000400f, &mctl_ctl->clken); /* normal */ + + udelay(250); +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + struct dram_para para = { + .cs1 = 0, + .bank = 1, + .rank = 2, + .rows = 15, + .bus_width = 16, + .page_size = 2048, + }; + + mctl_sys_init(¶); + + if (mctl_channel_init(¶) != 0) + return 0; + + auto_detect_dram_size(¶); + + /* Enable master software clk */ + writel(readl(&mctl_com->swonr) | 0x3ffff, &mctl_com->swonr); + + /* Set DRAM ODT MAP */ + if (para.rank == 2) + writel(0x00000303, &mctl_ctl->odtmap); + else + writel(0x00000201, &mctl_ctl->odtmap); + + return para.page_size * (para.bus_width / 8) * + (1 << (para.bank + para.rank + para.rows)); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a83t.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a83t.c new file mode 100644 index 000000000..a3f833dd3 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun8i_a83t.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sun8i a33 platform dram controller init. + * + * (C) Copyright 2007-2015 Allwinner Technology Co. + * Jerry Wang <wangflord@allwinnertech.com> + * (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com> + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + */ +#include <common.h> +#include <errno.h> +#include <init.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/prcm.h> +#include <linux/delay.h> + +#define DRAM_CLK_MUL 2 +#define DRAM_CLK_DIV 1 + +struct dram_para { + u8 cs1; + u8 seq; + u8 bank; + u8 rank; + u8 rows; + u8 bus_width; + u8 dram_type; + u16 page_size; +}; + +static void mctl_set_cr(struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + writel(MCTL_CR_CS1_CONTROL(para->cs1) | MCTL_CR_UNKNOWN | + MCTL_CR_CHANNEL(1) | MCTL_CR_DRAM_TYPE(para->dram_type) | + (para->seq ? MCTL_CR_SEQUENCE : 0) | + ((para->bus_width == 16) ? MCTL_CR_BUSW16 : MCTL_CR_BUSW8) | + MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_ROW(para->rows) | + MCTL_CR_BANK(para->bank) | MCTL_CR_RANK(para->rank), + &mctl_com->cr); +} + +static void auto_detect_dram_size(struct dram_para *para) +{ + u8 orig_rank = para->rank; + int rows, columns; + + /* Row detect */ + para->page_size = 512; + para->seq = 1; + para->rows = 16; + para->rank = 1; + mctl_set_cr(para); + for (rows = 11 ; rows < 16 ; rows++) { + if (mctl_mem_matches(1 << (rows + 9))) /* row-column */ + break; + } + + /* Column (page size) detect */ + para->rows = 11; + para->page_size = 8192; + mctl_set_cr(para); + for (columns = 9 ; columns < 13 ; columns++) { + if (mctl_mem_matches(1 << columns)) + break; + } + + para->seq = 0; + para->rank = orig_rank; + para->rows = rows; + para->page_size = 1 << columns; + mctl_set_cr(para); +} + +static inline int ns_to_t(int nanoseconds) +{ + const unsigned int ctrl_freq = + CONFIG_DRAM_CLK * DRAM_CLK_MUL / DRAM_CLK_DIV; + + return (ctrl_freq * nanoseconds + 999) / 1000; +} + +static void auto_set_timing_para(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u32 reg_val; + + u8 tccd = 2; + u8 tfaw = ns_to_t(50); + u8 trrd = max(ns_to_t(10), 4); + u8 trcd = ns_to_t(15); + u8 trc = ns_to_t(53); + u8 txp = max(ns_to_t(8), 3); + u8 twtr = max(ns_to_t(8), 4); + u8 trtp = max(ns_to_t(8), 4); + u8 twr = max(ns_to_t(15), 3); + u8 trp = ns_to_t(15); + u8 tras = ns_to_t(38); + + u16 trefi = ns_to_t(7800) / 32; + u16 trfc = ns_to_t(350); + + /* Fixed timing parameters */ + u8 tmrw = 0; + u8 tmrd = 4; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 4; + u8 trasmax = 24; + u8 tcl = 6; /* CL 12 */ + u8 tcwl = 4; /* CWL 8 */ + u8 t_rdata_en = 4; + u8 wr_latency = 2; + + u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */ + u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; /* 360ns */ + u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */ + u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */ + u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */ + + /* Set work mode register */ + mctl_set_cr(para); + /* Set mode register */ + if (para->dram_type == DRAM_TYPE_DDR3) { + writel(MCTL_MR0, &mctl_ctl->mr0); + writel(MCTL_MR1, &mctl_ctl->mr1); + writel(MCTL_MR2, &mctl_ctl->mr2); + writel(MCTL_MR3, &mctl_ctl->mr3); + } else if (para->dram_type == DRAM_TYPE_LPDDR3) { + writel(MCTL_LPDDR3_MR0, &mctl_ctl->mr0); + writel(MCTL_LPDDR3_MR1, &mctl_ctl->mr1); + writel(MCTL_LPDDR3_MR2, &mctl_ctl->mr2); + writel(MCTL_LPDDR3_MR3, &mctl_ctl->mr3); + + /* timing parameters for LPDDR3 */ + tfaw = max(ns_to_t(50), 4); + trrd = max(ns_to_t(10), 2); + trcd = max(ns_to_t(24), 2); + trc = ns_to_t(70); + txp = max(ns_to_t(8), 2); + twtr = max(ns_to_t(8), 2); + trtp = max(ns_to_t(8), 2); + trp = max(ns_to_t(27), 2); + tras = ns_to_t(42); + trefi = ns_to_t(3900) / 32; + trfc = ns_to_t(210); + tmrw = 5; + tmrd = 5; + tckesr = 5; + tcwl = 3; /* CWL 8 */ + t_rdata_en = 5; + tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */ + tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 200us */ + tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + twtp = tcwl + 4 + twr + 1; /* CWL + BL/2 + tWR */ + twr2rd = tcwl + 4 + 1 + twtr; /* WL + BL / 2 + tWTR */ + trd2wr = tcl + 4 + 5 - tcwl + 1; /* RL + BL / 2 + 2 - WL */ + } + /* Set dram timing */ + reg_val = (twtp << 24) | (tfaw << 16) | (trasmax << 8) | (tras << 0); + writel(reg_val, &mctl_ctl->dramtmg0); + reg_val = (txp << 16) | (trtp << 8) | (trc << 0); + writel(reg_val, &mctl_ctl->dramtmg1); + reg_val = (tcwl << 24) | (tcl << 16) | (trd2wr << 8) | (twr2rd << 0); + writel(reg_val, &mctl_ctl->dramtmg2); + reg_val = (tmrw << 16) | (tmrd << 12) | (tmod << 0); + writel(reg_val, &mctl_ctl->dramtmg3); + reg_val = (trcd << 24) | (tccd << 16) | (trrd << 8) | (trp << 0); + writel(reg_val, &mctl_ctl->dramtmg4); + reg_val = (tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | (tcke << 0); + writel(reg_val, &mctl_ctl->dramtmg5); + /* Set two rank timing and exit self-refresh timing */ + reg_val = readl(&mctl_ctl->dramtmg8); + reg_val &= ~(0xff << 8); + reg_val &= ~(0xff << 0); + reg_val |= (0x33 << 8); + reg_val |= (0x8 << 0); + writel(reg_val, &mctl_ctl->dramtmg8); + /* Set phy interface time */ + reg_val = (0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) + | (wr_latency << 0); + /* PHY interface write latency and read latency configure */ + writel(reg_val, &mctl_ctl->pitmg0); + /* Set phy time PTR0-2 use default */ + writel(((tdinit0 << 0) | (tdinit1 << 20)), &mctl_ctl->ptr3); + writel(((tdinit2 << 0) | (tdinit3 << 20)), &mctl_ctl->ptr4); + /* Set refresh timing */ + reg_val = (trefi << 16) | (trfc << 0); + writel(reg_val, &mctl_ctl->rfshtmg); +} + +static void mctl_set_pir(u32 val) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + writel(val, &mctl_ctl->pir); + mctl_await_completion(&mctl_ctl->pgsr0, 0x1, 0x1); +} + +static void mctl_data_train_cfg(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + if (para->rank == 2) + clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x3 << 24); + else + clrsetbits_le32(&mctl_ctl->dtcr, 0x3 << 24, 0x1 << 24); +} + +static int mctl_train_dram(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + mctl_data_train_cfg(para); + mctl_set_pir(0x5f3); + + return ((readl(&mctl_ctl->pgsr0) >> 20) & 0xff) ? -EIO : 0; +} + +static void set_master_priority(void) +{ + writel(0x00a0000d, MCTL_MASTER_CFG0(0)); + writel(0x00500064, MCTL_MASTER_CFG1(0)); + writel(0x07000009, MCTL_MASTER_CFG0(1)); + writel(0x00000600, MCTL_MASTER_CFG1(1)); + writel(0x01000009, MCTL_MASTER_CFG0(3)); + writel(0x00000064, MCTL_MASTER_CFG1(3)); + writel(0x08000009, MCTL_MASTER_CFG0(4)); + writel(0x00000640, MCTL_MASTER_CFG1(4)); + writel(0x20000308, MCTL_MASTER_CFG0(8)); + writel(0x00001000, MCTL_MASTER_CFG1(8)); + writel(0x02800009, MCTL_MASTER_CFG0(9)); + writel(0x00000100, MCTL_MASTER_CFG1(9)); + writel(0x01800009, MCTL_MASTER_CFG0(5)); + writel(0x00000100, MCTL_MASTER_CFG1(5)); + writel(0x01800009, MCTL_MASTER_CFG0(7)); + writel(0x00000100, MCTL_MASTER_CFG1(7)); + writel(0x00640009, MCTL_MASTER_CFG0(6)); + writel(0x00000032, MCTL_MASTER_CFG1(6)); + writel(0x0100000d, MCTL_MASTER_CFG0(2)); + writel(0x00500080, MCTL_MASTER_CFG1(2)); +} + +static int mctl_channel_init(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + u32 low_data_lines_status; /* Training status of datalines 0 - 7 */ + u32 high_data_lines_status; /* Training status of datalines 8 - 15 */ + u32 i, rval; + + auto_set_timing_para(para); + + /* Set dram master access priority */ + writel(0x000101a0, &mctl_com->bwcr); + /* set cpu high priority */ + writel(0x1, &mctl_com->mapr); + set_master_priority(); + udelay(250); + + /* Disable dram VTC */ + clrbits_le32(&mctl_ctl->pgcr0, 0x3f << 0 | 0x1 << 30); + clrsetbits_le32(&mctl_ctl->pgcr1, 0x1 << 24, 0x1 << 26); + + writel(0x94be6fa3, MCTL_PROTECT); + udelay(100); + clrsetbits_le32(MX_UPD2, 0xfff << 16, 0x50 << 16); + writel(0x0, MCTL_PROTECT); + udelay(100); + + + /* Set ODT */ + if (IS_ENABLED(CONFIG_DRAM_ODT_EN)) + rval = 0x0; + else + rval = 0x2; + + for (i = 0 ; i < 11 ; i++) { + clrsetbits_le32(DATX0IOCR(i), (0x3 << 24) | (0x3 << 16), + rval << 24); + clrsetbits_le32(DATX1IOCR(i), (0x3 << 24) | (0x3 << 16), + rval << 24); + clrsetbits_le32(DATX2IOCR(i), (0x3 << 24) | (0x3 << 16), + rval << 24); + clrsetbits_le32(DATX3IOCR(i), (0x3 << 24) | (0x3 << 16), + rval << 24); + } + + for (i = 0; i < 31; i++) + clrsetbits_le32(CAIOCR(i), 0x3 << 26 | 0x3 << 16, 0x2 << 26); + + /* set PLL configuration */ + if (CONFIG_DRAM_CLK >= 480) + setbits_le32(&mctl_ctl->pllgcr, 0x1 << 19); + else + setbits_le32(&mctl_ctl->pllgcr, 0x3 << 19); + + /* Auto detect dram config, set 2 rank and 16bit bus-width */ + para->cs1 = 0; + para->rank = 2; + para->bus_width = 16; + mctl_set_cr(para); + + /* Open DQS gating */ + clrbits_le32(&mctl_ctl->pgcr2, (0x3 << 6)); + clrbits_le32(&mctl_ctl->dqsgmr, (0x1 << 8) | (0x7)); + + if (para->dram_type == DRAM_TYPE_LPDDR3) + clrsetbits_le32(&mctl_ctl->dxccr, (0x1 << 27) | (0x3<<6) , + 0x1 << 31); + if (readl(&mctl_com->cr) & 0x1) + writel(0x00000303, &mctl_ctl->odtmap); + else + writel(0x00000201, &mctl_ctl->odtmap); + + mctl_data_train_cfg(para); + /* ZQ calibration */ + clrsetbits_le32(ZQnPR(0), 0x000000ff, CONFIG_DRAM_ZQ & 0xff); + clrsetbits_le32(ZQnPR(1), 0x000000ff, (CONFIG_DRAM_ZQ >> 8) & 0xff); + /* CA calibration */ + + if (para->dram_type == DRAM_TYPE_DDR3) + mctl_set_pir(0x0201f3 | 0x1<<10); + else + mctl_set_pir(0x020173 | 0x1<<10); + + /* DQS gate training */ + if (mctl_train_dram(para) != 0) { + low_data_lines_status = (readl(DXnGSR0(0)) >> 24) & 0x03; + high_data_lines_status = (readl(DXnGSR0(1)) >> 24) & 0x03; + + if (low_data_lines_status == 0x3) + return -EIO; + + /* DRAM has only one rank */ + para->rank = 1; + mctl_set_cr(para); + + if (low_data_lines_status == high_data_lines_status) + goto done; /* 16 bit bus, 1 rank */ + + if (!(low_data_lines_status & high_data_lines_status)) { + /* Retry 16 bit bus-width with CS1 set */ + para->cs1 = 1; + mctl_set_cr(para); + if (mctl_train_dram(para) == 0) + goto done; + } + + /* Try 8 bit bus-width */ + writel(0x0, DXnGCR0(1)); /* Disable high DQ */ + para->cs1 = 0; + para->bus_width = 8; + mctl_set_cr(para); + if (mctl_train_dram(para) != 0) + return -EIO; + } +done: + /* Check the dramc status */ + mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1); + + /* Close DQS gating */ + setbits_le32(&mctl_ctl->pgcr2, 0x3 << 6); + + /* set PGCR3,CKE polarity */ + writel(0x00aa0060, &mctl_ctl->pgcr3); + /* Enable master access */ + writel(0xffffffff, &mctl_com->maer); + + return 0; +} + +static void mctl_sys_init(struct dram_para *para) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + clrbits_le32(&ccm->mbus_clk_cfg, MBUS_CLK_GATE); + clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET); + clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN); + udelay(1000); + clrbits_le32(&ccm->dram_clk_cfg, 0x01<<31); + + clock_set_pll5(CONFIG_DRAM_CLK * 1000000 * DRAM_CLK_MUL); + + clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV_MASK, + CCM_DRAMCLK_CFG_DIV(DRAM_CLK_DIV) | + CCM_DRAMCLK_CFG_RST | CCM_DRAMCLK_CFG_UPD); + mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); + + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET); + setbits_le32(&ccm->mbus_clk_cfg, MBUS_CLK_GATE); + + para->rank = 2; + para->bus_width = 16; + mctl_set_cr(para); + + /* Set dram master access priority */ + writel(0x0000e00f, &mctl_ctl->clken); /* normal */ + + udelay(250); +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + struct dram_para para = { + .cs1 = 0, + .bank = 1, + .rank = 1, + .rows = 15, + .bus_width = 16, + .page_size = 2048, + }; + +#if defined(CONFIG_MACH_SUN8I_A83T) +#if (CONFIG_DRAM_TYPE == 3) || (CONFIG_DRAM_TYPE == 7) + para.dram_type = CONFIG_DRAM_TYPE; +#else +#error Unsupported DRAM type, Please set DRAM type (3:DDR3, 7:LPDDR3) +#endif +#endif + setbits_le32(SUNXI_PRCM_BASE + 0x1e0, 0x1 << 8); + + writel(0, (SUNXI_PRCM_BASE + 0x1e8)); + udelay(10); + + mctl_sys_init(¶); + + if (mctl_channel_init(¶) != 0) + return 0; + + auto_detect_dram_size(¶); + + /* Enable master software clk */ + writel(readl(&mctl_com->swonr) | 0x3ffff, &mctl_com->swonr); + + /* Set DRAM ODT MAP */ + if (para.rank == 2) + writel(0x00000303, &mctl_ctl->odtmap); + else + writel(0x00000201, &mctl_ctl->odtmap); + + return para.page_size * (para.bus_width / 8) * + (1 << (para.bank + para.rank + para.rows)); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sun9i.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sun9i.c new file mode 100644 index 000000000..14be212e8 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sun9i.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun9i dram controller initialisation + * + * (C) Copyright 2007-2015 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Jerry Wang <wangflord@allwinnertech.com> + * + * (C) Copyright 2016 Theobroma Systems Design und Consulting GmbH + * Philipp Tomsich <philipp.tomsich@theobroma-systems.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <init.h> +#include <log.h> +#include <ram.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/sys_proto.h> + +#define DRAM_CLK (CONFIG_DRAM_CLK * 1000000) + +/* + * The following amounts to an extensive rewrite of the code received from + * Allwinner as part of the open-source bootloader release (refer to + * https://github.com/allwinner-zh/bootloader.git) and augments the upstream + * sources (which act as the primary reference point for the inner workings + * of the 'underdocumented' DRAM controller in the A80) using the following + * documentation for other memory controllers based on the (Synopsys) + * Designware IP (DDR memory protocol controller and DDR PHY) + * * TI Keystone II Architecture: DDR3 Memory Controller, User's Guide + * Document 'SPRUHN7C', Oct 2013 (revised March 2015) + * * Xilinx Zynq UltraScale+ MPSoC Register Reference + * document ug1087 (v1.0) + * Note that the Zynq-documentation provides a very close match for the DDR + * memory protocol controller (and provides a very good guide to the rounding + * rules for various timings), whereas the TI Keystone II document should be + * referred to for DDR PHY specifics only. + * + * The DRAM controller in the A80 runs at half the frequency of the DDR PHY + * (i.e. the rules for MEMC_FREQ_RATIO=2 from the Zynq-documentation apply). + * + * Known limitations + * ================= + * In the current state, the following features are not fully supported and + * a number of simplifying assumptions have been made: + * 1) Only DDR3 support is implemented, as our test platform (the A80-Q7 + * module) is designed to accomodate DDR3/DDR3L. + * 2) Only 2T-mode has been implemented and tested. + * 3) The controller supports two different clocking strategies (PLL6 can + * either be 2*CK or CK/2)... we only support the 2*CK clock at this + * time and haven't verified whether the alternative clocking strategy + * works. If you are interested in porting this over/testing this, + * please refer to cases where bit 0 of 'dram_tpr8' is tested in the + * original code from Allwinner. + * 4) Support for 2 ranks per controller is not implemented (as we don't + * the hardware to test it). + * + * Future directions + * ================= + * The driver should be driven from a device-tree based configuration that + * can dynamically provide the necessary timing parameters (i.e. target + * frequency and speed-bin information)---the data structures used in the + * calculation of the timing parameters are already designed to capture + * similar information as the device tree would provide. + * + * To enable a device-tree based configuration of the sun9i platform, we + * will need to enable CONFIG_TPL and bootstrap in 3 stages: initially + * into SRAM A1 (40KB) and next into SRAM A2 (160KB)---which would be the + * stage to initialise the platform via the device-tree---before having + * the full U-Boot run from DDR. + */ + +/* + * A number of DDR3 timings are given as "the greater of a fixed number of + * clock cycles (CK) or nanoseconds. We express these using a structure + * that holds a cycle count and a duration in picoseconds (so we can model + * sub-ns timings, such as 7.5ns without losing precision or resorting to + * rounding up early. + */ +struct dram_sun9i_timing { + u32 ck; + u32 ps; +}; + +/* */ +struct dram_sun9i_cl_cwl_timing { + u32 CL; + u32 CWL; + u32 tCKmin; /* in ps */ + u32 tCKmax; /* in ps */ +}; + +struct dram_sun9i_para { + u32 dram_type; + + u8 bus_width; + u8 chan; + u8 rank; + u8 rows; + u16 page_size; + + /* Timing information for each speed-bin */ + struct dram_sun9i_cl_cwl_timing *cl_cwl_table; + u32 cl_cwl_numentries; + + /* + * For the timings, we try to keep the order and grouping used in + * JEDEC Standard No. 79-3F + */ + + /* timings */ + u32 tREFI; /* in ns */ + u32 tRFC; /* in ns */ + + u32 tRAS; /* in ps */ + + /* command and address timing */ + u32 tDLLK; /* in nCK */ + struct dram_sun9i_timing tRTP; + struct dram_sun9i_timing tWTR; + u32 tWR; /* in nCK */ + u32 tMRD; /* in nCK */ + struct dram_sun9i_timing tMOD; + u32 tRCD; /* in ps */ + u32 tRP; /* in ps */ + u32 tRC; /* in ps */ + u32 tCCD; /* in nCK */ + struct dram_sun9i_timing tRRD; + u32 tFAW; /* in ps */ + + /* calibration timing */ + /* struct dram_sun9i_timing tZQinit; */ + struct dram_sun9i_timing tZQoper; + struct dram_sun9i_timing tZQCS; + + /* reset timing */ + /* struct dram_sun9i_timing tXPR; */ + + /* self-refresh timings */ + struct dram_sun9i_timing tXS; + u32 tXSDLL; /* in nCK */ + /* struct dram_sun9i_timing tCKESR; */ + struct dram_sun9i_timing tCKSRE; + struct dram_sun9i_timing tCKSRX; + + /* power-down timings */ + struct dram_sun9i_timing tXP; + struct dram_sun9i_timing tXPDLL; + struct dram_sun9i_timing tCKE; + + /* write leveling timings */ + u32 tWLMRD; /* min, in nCK */ + /* u32 tWLDQSEN; min, in nCK */ + u32 tWLO; /* max, in ns */ + /* u32 tWLOE; max, in ns */ + + /* u32 tCKDPX; in nCK */ + /* u32 tCKCSX; in nCK */ +}; + +static void mctl_sys_init(void); + +#define SCHED_RDWR_IDLE_GAP(n) ((n & 0xff) << 24) +#define SCHED_GO2CRITICAL_HYSTERESIS(n) ((n & 0xff) << 16) +#define SCHED_LPR_NUM_ENTRIES(n) ((n & 0xff) << 8) +#define SCHED_PAGECLOSE (1 << 2) +#define SCHED_PREFER_WRITE (1 << 1) +#define SCHED_FORCE_LOW_PRI_N (1 << 0) + +#define SCHED_CONFIG (SCHED_RDWR_IDLE_GAP(0xf) | \ + SCHED_GO2CRITICAL_HYSTERESIS(0x80) | \ + SCHED_LPR_NUM_ENTRIES(0x20) | \ + SCHED_FORCE_LOW_PRI_N) +#define PERFHPR0_CONFIG 0x0000001f +#define PERFHPR1_CONFIG 0x1f00001f +#define PERFLPR0_CONFIG 0x000000ff +#define PERFLPR1_CONFIG 0x0f0000ff +#define PERFWR0_CONFIG 0x000000ff +#define PERFWR1_CONFIG 0x0f0001ff + +static void mctl_ctl_sched_init(unsigned long base) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl = + (struct sunxi_mctl_ctl_reg *)base; + + /* Needs to be done before the global clk enable... */ + writel(SCHED_CONFIG, &mctl_ctl->sched); + writel(PERFHPR0_CONFIG, &mctl_ctl->perfhpr0); + writel(PERFHPR1_CONFIG, &mctl_ctl->perfhpr1); + writel(PERFLPR0_CONFIG, &mctl_ctl->perflpr0); + writel(PERFLPR1_CONFIG, &mctl_ctl->perflpr1); + writel(PERFWR0_CONFIG, &mctl_ctl->perfwr0); + writel(PERFWR1_CONFIG, &mctl_ctl->perfwr1); +} + +static void mctl_sys_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + debug("Setting PLL6 to %d\n", DRAM_CLK * 2); + clock_set_pll6(DRAM_CLK * 2); + + /* Original dram init code which may come in handy later + ******************************************************** + clock_set_pll6(use_2channelPLL ? (DRAM_CLK * 2) : + (DRAM_CLK / 2), false); + + if ((para->dram_clk <= 400)|((para->dram_tpr8 & 0x1)==0)) { + * PLL6 should be 2*CK * + * ccm_setup_pll6_ddr_clk(PLL6_DDR_CLK); * + ccm_setup_pll6_ddr_clk((1000000 * (para->dram_clk) * 2), 0); + } else { + * PLL6 should be CK/2 * + ccm_setup_pll6_ddr_clk((1000000 * (para->dram_clk) / 2), 1); + } + + if (para->dram_tpr13 & (0xf<<18)) { + * + * bit21:bit18=0001:pll swing 0.4 + * bit21:bit18=0010:pll swing 0.3 + * bit21:bit18=0100:pll swing 0.2 + * bit21:bit18=1000:pll swing 0.1 + * + dram_dbg("DRAM fre extend open !\n"); + reg_val=mctl_read_w(CCM_PLL6_DDR_REG); + reg_val&=(0x1<<16); + reg_val=reg_val>>16; + + if(para->dram_tpr13 & (0x1<<18)) + { + mctl_write_w(CCM_PLL_BASE + 0x114, + (0x3333U|(0x3<<17)|(reg_val<<19)|(0x120U<<20)| + (0x2U<<29)|(0x1U<<31))); + } + else if(para->dram_tpr13 & (0x1<<19)) + { + mctl_write_w(CCM_PLL_BASE + 0x114, + (0x6666U|(0x3U<<17)|(reg_val<<19)|(0xD8U<<20)| + (0x2U<<29)|(0x1U<<31))); + } + else if(para->dram_tpr13 & (0x1<<20)) + { + mctl_write_w(CCM_PLL_BASE + 0x114, + (0x9999U|(0x3U<<17)|(reg_val<<19)|(0x90U<<20)| + (0x2U<<29)|(0x1U<<31))); + } + else if(para->dram_tpr13 & (0x1<<21)) + { + mctl_write_w(CCM_PLL_BASE + 0x114, + (0xccccU|(0x3U<<17)|(reg_val<<19)|(0x48U<<20)| + (0x2U<<29)|(0x1U<<31))); + } + + //frequency extend open + reg_val = mctl_read_w(CCM_PLL6_DDR_REG); + reg_val |= ((0x1<<24)|(0x1<<30)); + mctl_write_w(CCM_PLL6_DDR_REG, reg_val); + + + while(mctl_read_w(CCM_PLL6_DDR_REG) & (0x1<<30)); + } + + aw_delay(0x20000); //make some delay + ******************************************************** + */ + + /* assert mctl reset */ + clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + /* stop mctl clock */ + clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + + sdelay(2000); + + /* deassert mctl reset */ + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + /* enable mctl clock */ + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + + /* set up the transactions scheduling before enabling the global clk */ + mctl_ctl_sched_init(SUNXI_DRAM_CTL0_BASE); + mctl_ctl_sched_init(SUNXI_DRAM_CTL1_BASE); + sdelay(1000); + + debug("2\n"); + + /* (3 << 12): PLL_DDR */ + writel((3 << 12) | (1 << 16), &ccm->dram_clk_cfg); + do { + debug("Waiting for DRAM_CLK_CFG\n"); + sdelay(10000); + } while (readl(&ccm->dram_clk_cfg) & (1 << 16)); + setbits_le32(&ccm->dram_clk_cfg, (1 << 31)); + + /* TODO: we only support the common case ... i.e. 2*CK */ + setbits_le32(&mctl_com->ccr, (1 << 14) | (1 << 30)); + writel(2, &mctl_com->rmcr); /* controller clock is PLL6/4 */ + + sdelay(2000); + + /* Original dram init code which may come in handy later + ******************************************************** + if ((para->dram_clk <= 400) | ((para->dram_tpr8 & 0x1) == 0)) { + * PLL6 should be 2*CK * + * gating 2 channel pll * + reg_val = mctl_read_w(MC_CCR); + reg_val |= ((0x1 << 14) | (0x1U << 30)); + mctl_write_w(MC_CCR, reg_val); + mctl_write_w(MC_RMCR, 0x2); * controller clock use pll6/4 * + } else { + * enable 2 channel pll * + reg_val = mctl_read_w(MC_CCR); + reg_val &= ~((0x1 << 14) | (0x1U << 30)); + mctl_write_w(MC_CCR, reg_val); + mctl_write_w(MC_RMCR, 0x0); * controller clock use pll6 * + } + + reg_val = mctl_read_w(MC_CCR); + reg_val &= ~((0x1<<15)|(0x1U<<31)); + mctl_write_w(MC_CCR, reg_val); + aw_delay(20); + //aw_delay(0x10); + ******************************************************** + */ + + clrbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN | MCTL_CCR_CH1_CLK_EN); + sdelay(1000); + + setbits_le32(&mctl_com->ccr, MCTL_CCR_CH0_CLK_EN); + /* TODO if (para->chan == 2) */ + setbits_le32(&mctl_com->ccr, MCTL_CCR_CH1_CLK_EN); +} + +static void mctl_com_init(struct dram_sun9i_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* TODO: hard-wired for DDR3 now */ + writel(((para->chan == 2) ? MCTL_CR_CHANNEL_DUAL : + MCTL_CR_CHANNEL_SINGLE) + | MCTL_CR_DRAMTYPE_DDR3 | MCTL_CR_BANK(1) + | MCTL_CR_ROW(para->rows) + | ((para->bus_width == 32) ? MCTL_CR_BUSW32 : MCTL_CR_BUSW16) + | MCTL_CR_PAGE_SIZE(para->page_size) | MCTL_CR_RANK(para->rank), + &mctl_com->cr); + + debug("CR: %d\n", readl(&mctl_com->cr)); +} + +static u32 mctl_channel_init(u32 ch_index, struct dram_sun9i_para *para) +{ + struct sunxi_mctl_ctl_reg *mctl_ctl; + struct sunxi_mctl_phy_reg *mctl_phy; + + u32 CL = 0; + u32 CWL = 0; + u16 mr[4] = { 0, }; + +#define PS2CYCLES_FLOOR(n) ((n * CONFIG_DRAM_CLK) / 1000000) +#define PS2CYCLES_ROUNDUP(n) ((n * CONFIG_DRAM_CLK + 999999) / 1000000) +#define NS2CYCLES_FLOOR(n) ((n * CONFIG_DRAM_CLK) / 1000) +#define NS2CYCLES_ROUNDUP(n) ((n * CONFIG_DRAM_CLK + 999) / 1000) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + + /* + * Convert the values to cycle counts (nCK) from what is provided + * by the definition of each speed bin. + */ + /* const u32 tREFI = NS2CYCLES_FLOOR(para->tREFI); */ + const u32 tREFI = NS2CYCLES_FLOOR(para->tREFI); + const u32 tRFC = NS2CYCLES_ROUNDUP(para->tRFC); + const u32 tRCD = PS2CYCLES_ROUNDUP(para->tRCD); + const u32 tRP = PS2CYCLES_ROUNDUP(para->tRP); + const u32 tRC = PS2CYCLES_ROUNDUP(para->tRC); + const u32 tRAS = PS2CYCLES_ROUNDUP(para->tRAS); + + /* command and address timing */ + const u32 tDLLK = para->tDLLK; + const u32 tRTP = MAX(para->tRTP.ck, PS2CYCLES_ROUNDUP(para->tRTP.ps)); + const u32 tWTR = MAX(para->tWTR.ck, PS2CYCLES_ROUNDUP(para->tWTR.ps)); + const u32 tWR = NS2CYCLES_FLOOR(para->tWR); + const u32 tMRD = para->tMRD; + const u32 tMOD = MAX(para->tMOD.ck, PS2CYCLES_ROUNDUP(para->tMOD.ps)); + const u32 tCCD = para->tCCD; + const u32 tRRD = MAX(para->tRRD.ck, PS2CYCLES_ROUNDUP(para->tRRD.ps)); + const u32 tFAW = PS2CYCLES_ROUNDUP(para->tFAW); + + /* calibration timings */ + /* const u32 tZQinit = MAX(para->tZQinit.ck, + PS2CYCLES_ROUNDUP(para->tZQinit.ps)); */ + const u32 tZQoper = MAX(para->tZQoper.ck, + PS2CYCLES_ROUNDUP(para->tZQoper.ps)); + const u32 tZQCS = MAX(para->tZQCS.ck, + PS2CYCLES_ROUNDUP(para->tZQCS.ps)); + + /* reset timing */ + /* const u32 tXPR = MAX(para->tXPR.ck, + PS2CYCLES_ROUNDUP(para->tXPR.ps)); */ + + /* power-down timings */ + const u32 tXP = MAX(para->tXP.ck, PS2CYCLES_ROUNDUP(para->tXP.ps)); + const u32 tXPDLL = MAX(para->tXPDLL.ck, + PS2CYCLES_ROUNDUP(para->tXPDLL.ps)); + const u32 tCKE = MAX(para->tCKE.ck, PS2CYCLES_ROUNDUP(para->tCKE.ps)); + + /* + * self-refresh timings (keep below power-down timings, as tCKESR + * needs to be calculated based on the nCK value of tCKE) + */ + const u32 tXS = MAX(para->tXS.ck, PS2CYCLES_ROUNDUP(para->tXS.ps)); + const u32 tXSDLL = para->tXSDLL; + const u32 tCKSRE = MAX(para->tCKSRE.ck, + PS2CYCLES_ROUNDUP(para->tCKSRE.ps)); + const u32 tCKESR = tCKE + 1; + const u32 tCKSRX = MAX(para->tCKSRX.ck, + PS2CYCLES_ROUNDUP(para->tCKSRX.ps)); + + /* write leveling timings */ + const u32 tWLMRD = para->tWLMRD; + /* const u32 tWLDQSEN = para->tWLDQSEN; */ + const u32 tWLO = PS2CYCLES_FLOOR(para->tWLO); + /* const u32 tWLOE = PS2CYCLES_FLOOR(para->tWLOE); */ + + const u32 tRASmax = tREFI * 9; + int i; + + for (i = 0; i < para->cl_cwl_numentries; ++i) { + const u32 tCK = 1000000 / CONFIG_DRAM_CLK; + + if ((para->cl_cwl_table[i].tCKmin <= tCK) && + (tCK < para->cl_cwl_table[i].tCKmax)) { + CL = para->cl_cwl_table[i].CL; + CWL = para->cl_cwl_table[i].CWL; + + debug("found CL/CWL: CL = %d, CWL = %d\n", CL, CWL); + break; + } + } + + if ((CL == 0) && (CWL == 0)) { + printf("failed to find valid CL/CWL for operating point %d MHz\n", + CONFIG_DRAM_CLK); + return 0; + } + + if (ch_index == 0) { + mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + } else { + mctl_ctl = (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL1_BASE; + mctl_phy = (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY1_BASE; + } + + if (para->dram_type == DRAM_TYPE_DDR3) { + mr[0] = DDR3_MR0_PPD_FAST_EXIT | DDR3_MR0_WR(tWR) | + DDR3_MR0_CL(CL); + mr[1] = DDR3_MR1_RTT120OHM; + mr[2] = DDR3_MR2_TWL(CWL); + mr[3] = 0; + + /* + * DRAM3 initialisation requires holding CKE LOW for + * at least 500us prior to starting the initialisation + * sequence and at least 10ns after driving CKE HIGH + * before the initialisation sequence may be started). + * + * Refer to Micron document "TN-41-07: DDR3 Power-Up, + * Initialization, and Reset DDR3 Initialization + * Routine" for details). + */ + writel(MCTL_INIT0_POST_CKE_x1024(1) | + MCTL_INIT0_PRE_CKE_x1024( + (500 * CONFIG_DRAM_CLK + 1023) / 1024), /* 500us */ + &mctl_ctl->init[0]); + writel(MCTL_INIT1_DRAM_RSTN_x1024(1), + &mctl_ctl->init[1]); + /* INIT2 is not used for DDR3 */ + writel(MCTL_INIT3_MR(mr[0]) | MCTL_INIT3_EMR(mr[1]), + &mctl_ctl->init[3]); + writel(MCTL_INIT4_EMR2(mr[2]) | MCTL_INIT4_EMR3(mr[3]), + &mctl_ctl->init[4]); + writel(MCTL_INIT5_DEV_ZQINIT_x32(512 / 32), /* 512 cycles */ + &mctl_ctl->init[5]); + } else { + /* !!! UNTESTED !!! */ + /* + * LPDDR2 and/or LPDDR3 require a 200us minimum delay + * after driving CKE HIGH in the initialisation sequence. + */ + writel(MCTL_INIT0_POST_CKE_x1024( + (200 * CONFIG_DRAM_CLK + 1023) / 1024), + &mctl_ctl->init[0]); + writel(MCTL_INIT1_DRAM_RSTN_x1024(1), + &mctl_ctl->init[1]); + writel(MCTL_INIT2_IDLE_AFTER_RESET_x32( + (CONFIG_DRAM_CLK + 31) / 32) /* 1us */ + | MCTL_INIT2_MIN_STABLE_CLOCK_x1(5), /* 5 cycles */ + &mctl_ctl->init[2]); + writel(MCTL_INIT3_MR(mr[1]) | MCTL_INIT3_EMR(mr[2]), + &mctl_ctl->init[3]); + writel(MCTL_INIT4_EMR2(mr[3]), + &mctl_ctl->init[4]); + writel(MCTL_INIT5_DEV_ZQINIT_x32( + (CONFIG_DRAM_CLK + 31) / 32) /* 1us */ + | MCTL_INIT5_MAX_AUTO_INIT_x1024( + (10 * CONFIG_DRAM_CLK + 1023) / 1024), + &mctl_ctl->init[5]); + } + + /* (DDR3) We always use a burst-length of 8. */ +#define MCTL_BL 8 + /* wr2pre: WL + BL/2 + tWR */ +#define WR2PRE (MCTL_BL/2 + CWL + tWTR) + /* wr2rd = CWL + BL/2 + tWTR */ +#define WR2RD (MCTL_BL/2 + CWL + tWTR) + /* + * rd2wr = RL + BL/2 + 2 - WL (for DDR3) + * rd2wr = RL + BL/2 + RU(tDQSCKmax/tCK) + 1 - WL (for LPDDR2/LPDDR3) + */ +#define RD2WR (CL + MCTL_BL/2 + 2 - CWL) +#define MCTL_PHY_TRTW 0 +#define MCTL_PHY_TRTODT 0 + +#define MCTL_DIV2(n) ((n + 1)/2) +#define MCTL_DIV32(n) (n/32) +#define MCTL_DIV1024(n) (n/1024) + + writel((MCTL_DIV2(WR2PRE) << 24) | (MCTL_DIV2(tFAW) << 16) | + (MCTL_DIV1024(tRASmax) << 8) | (MCTL_DIV2(tRAS) << 0), + &mctl_ctl->dramtmg[0]); + writel((MCTL_DIV2(tXP) << 16) | (MCTL_DIV2(tRTP) << 8) | + (MCTL_DIV2(tRC) << 0), + &mctl_ctl->dramtmg[1]); + writel((MCTL_DIV2(CWL) << 24) | (MCTL_DIV2(CL) << 16) | + (MCTL_DIV2(RD2WR) << 8) | (MCTL_DIV2(WR2RD) << 0), + &mctl_ctl->dramtmg[2]); + /* + * Note: tMRW is located at bit 16 (and up) in DRAMTMG3... + * this is only relevant for LPDDR2/LPDDR3 + */ + writel((MCTL_DIV2(tMRD) << 12) | (MCTL_DIV2(tMOD) << 0), + &mctl_ctl->dramtmg[3]); + writel((MCTL_DIV2(tRCD) << 24) | (MCTL_DIV2(tCCD) << 16) | + (MCTL_DIV2(tRRD) << 8) | (MCTL_DIV2(tRP) << 0), + &mctl_ctl->dramtmg[4]); + writel((MCTL_DIV2(tCKSRX) << 24) | (MCTL_DIV2(tCKSRE) << 16) | + (MCTL_DIV2(tCKESR) << 8) | (MCTL_DIV2(tCKE) << 0), + &mctl_ctl->dramtmg[5]); + + /* These timings are relevant for LPDDR2/LPDDR3 only */ + /* writel((MCTL_TCKDPDE << 24) | (MCTL_TCKDPX << 16) | + (MCTL_TCKCSX << 0), &mctl_ctl->dramtmg[6]); */ + + /* printf("DRAMTMG7 reset value: 0x%x\n", + readl(&mctl_ctl->dramtmg[7])); */ + /* DRAMTMG7 reset value: 0x202 */ + /* DRAMTMG7 should contain t_ckpde and t_ckpdx: check reset values!!! */ + /* printf("DRAMTMG8 reset value: 0x%x\n", + readl(&mctl_ctl->dramtmg[8])); */ + /* DRAMTMG8 reset value: 0x44 */ + + writel((MCTL_DIV32(tXSDLL) << 0), &mctl_ctl->dramtmg[8]); + + writel((MCTL_DIV32(tREFI) << 16) | (MCTL_DIV2(tRFC) << 0), + &mctl_ctl->rfshtmg); + + if (para->dram_type == DRAM_TYPE_DDR3) { + writel((2 << 24) | ((MCTL_DIV2(CL) - 2) << 16) | + (1 << 8) | ((MCTL_DIV2(CWL) - 2) << 0), + &mctl_ctl->dfitmg[0]); + } else { + /* TODO */ + } + + /* TODO: handle the case of the write latency domain going to 0 ... */ + + /* + * Disable dfi_init_complete_en (the triggering of the SDRAM + * initialisation when the PHY initialisation completes). + */ + clrbits_le32(&mctl_ctl->dfimisc, MCTL_DFIMISC_DFI_INIT_COMPLETE_EN); + /* Disable the automatic generation of DLL calibration requests */ + setbits_le32(&mctl_ctl->dfiupd[0], MCTL_DFIUPD0_DIS_AUTO_CTRLUPD); + + /* A80-Q7: 2T, 1 rank, DDR3, full-32bit-DQ */ + /* TODO: make 2T and BUSWIDTH configurable */ + writel(MCTL_MSTR_DEVICETYPE(para->dram_type) | + MCTL_MSTR_BURSTLENGTH(para->dram_type) | + MCTL_MSTR_ACTIVERANKS(para->rank) | + MCTL_MSTR_2TMODE | MCTL_MSTR_BUSWIDTH32, + &mctl_ctl->mstr); + + if (para->dram_type == DRAM_TYPE_DDR3) { + writel(MCTL_ZQCTRL0_TZQCL(MCTL_DIV2(tZQoper)) | + (MCTL_DIV2(tZQCS)), &mctl_ctl->zqctrl[0]); + /* + * TODO: is the following really necessary as the bottom + * half should already be 0x100 and the upper half should + * be ignored for a DDR3 device??? + */ + writel(MCTL_ZQCTRL1_TZQSI_x1024(0x100), + &mctl_ctl->zqctrl[1]); + } else { + writel(MCTL_ZQCTRL0_TZQCL(0x200) | MCTL_ZQCTRL0_TZQCS(0x40), + &mctl_ctl->zqctrl[0]); + writel(MCTL_ZQCTRL1_TZQRESET(0x28) | + MCTL_ZQCTRL1_TZQSI_x1024(0x100), + &mctl_ctl->zqctrl[1]); + } + + /* Assert dfi_init_complete signal */ + setbits_le32(&mctl_ctl->dfimisc, MCTL_DFIMISC_DFI_INIT_COMPLETE_EN); + /* Disable auto-refresh */ + setbits_le32(&mctl_ctl->rfshctl3, MCTL_RFSHCTL3_DIS_AUTO_REFRESH); + + /* PHY initialisation */ + + /* TODO: make 2T and 8-bank mode configurable */ + writel(MCTL_PHY_DCR_BYTEMASK | MCTL_PHY_DCR_2TMODE | + MCTL_PHY_DCR_DDR8BNK | MCTL_PHY_DRAMMODE_DDR3, + &mctl_phy->dcr); + + /* For LPDDR2 or LPDDR3, set DQSGX to 0 before training. */ + if (para->dram_type != DRAM_TYPE_DDR3) + clrbits_le32(&mctl_phy->dsgcr, (3 << 6)); + + writel(mr[0], &mctl_phy->mr0); + writel(mr[1], &mctl_phy->mr1); + writel(mr[2], &mctl_phy->mr2); + writel(mr[3], &mctl_phy->mr3); + + /* + * The DFI PHY is running at full rate. We thus use the actual + * timings in clock cycles here. + */ + writel((tRC << 26) | (tRRD << 22) | (tRAS << 16) | + (tRCD << 12) | (tRP << 8) | (tWTR << 4) | (tRTP << 0), + &mctl_phy->dtpr[0]); + writel((tMRD << 0) | ((tMOD - 12) << 2) | (tFAW << 5) | + (tRFC << 11) | (tWLMRD << 20) | (tWLO << 26), + &mctl_phy->dtpr[1]); + writel((tXS << 0) | (MAX(tXP, tXPDLL) << 10) | + (tCKE << 15) | (tDLLK << 19) | + (MCTL_PHY_TRTODT << 29) | (MCTL_PHY_TRTW << 30) | + (((tCCD - 4) & 0x1) << 31), + &mctl_phy->dtpr[2]); + + /* tDQSCK and tDQSCKmax are used LPDDR2/LPDDR3 */ + /* writel((tDQSCK << 0) | (tDQSCKMAX << 3), &mctl_phy->dtpr[3]); */ + + /* + * We use the same values used by Allwinner's Boot0 for the PTR + * (PHY timing register) configuration that is tied to the PHY + * implementation. + */ + writel(0x42C21590, &mctl_phy->ptr[0]); + writel(0xD05612C0, &mctl_phy->ptr[1]); + if (para->dram_type == DRAM_TYPE_DDR3) { + const unsigned int tdinit0 = 500 * CONFIG_DRAM_CLK; /* 500us */ + const unsigned int tdinit1 = (360 * CONFIG_DRAM_CLK + 999) / + 1000; /* 360ns */ + const unsigned int tdinit2 = 200 * CONFIG_DRAM_CLK; /* 200us */ + const unsigned int tdinit3 = CONFIG_DRAM_CLK; /* 1us */ + + writel((tdinit1 << 20) | tdinit0, &mctl_phy->ptr[3]); + writel((tdinit3 << 18) | tdinit2, &mctl_phy->ptr[4]); + } else { + /* LPDDR2 or LPDDR3 */ + const unsigned int tdinit0 = (100 * CONFIG_DRAM_CLK + 999) / + 1000; /* 100ns */ + const unsigned int tdinit1 = 200 * CONFIG_DRAM_CLK; /* 200us */ + const unsigned int tdinit2 = 22 * CONFIG_DRAM_CLK; /* 11us */ + const unsigned int tdinit3 = 2 * CONFIG_DRAM_CLK; /* 2us */ + + writel((tdinit1 << 20) | tdinit0, &mctl_phy->ptr[3]); + writel((tdinit3 << 18) | tdinit2, &mctl_phy->ptr[4]); + } + + /* TEST ME */ + writel(0x00203131, &mctl_phy->acmdlr); + + /* TODO: can we enable this for 2 ranks, even when we don't know yet */ + writel(MCTL_DTCR_DEFAULT | MCTL_DTCR_RANKEN(para->rank), + &mctl_phy->dtcr); + + /* TODO: half width */ + debug("DX2GCR0 reset: 0x%x\n", readl(&mctl_phy->dx[2].gcr[0])); + writel(0x7C000285, &mctl_phy->dx[2].gcr[0]); + writel(0x7C000285, &mctl_phy->dx[3].gcr[0]); + + clrsetbits_le32(&mctl_phy->zq[0].pr, 0xff, + (CONFIG_DRAM_ZQ >> 0) & 0xff); /* CK/CA */ + clrsetbits_le32(&mctl_phy->zq[1].pr, 0xff, + (CONFIG_DRAM_ZQ >> 8) & 0xff); /* DX0/DX1 */ + clrsetbits_le32(&mctl_phy->zq[2].pr, 0xff, + (CONFIG_DRAM_ZQ >> 16) & 0xff); /* DX2/DX3 */ + + /* TODO: make configurable & implement non-ODT path */ + if (1) { + int lane; + for (lane = 0; lane < 4; ++lane) { + clrbits_le32(&mctl_phy->dx[lane].gcr[2], 0xffff); + clrbits_le32(&mctl_phy->dx[lane].gcr[3], + (0x3<<12) | (0x3<<4)); + } + } else { + /* TODO: check */ + int lane; + for (lane = 0; lane < 4; ++lane) { + clrsetbits_le32(&mctl_phy->dx[lane].gcr[2], 0xffff, + 0xaaaa); + if (para->dram_type == DRAM_TYPE_DDR3) + setbits_le32(&mctl_phy->dx[lane].gcr[3], + (0x3<<12) | (0x3<<4)); + else + setbits_le32(&mctl_phy->dx[lane].gcr[3], + 0x00000012); + } + } + + writel(0x04058D02, &mctl_phy->zq[0].cr); /* CK/CA */ + writel(0x04058D02, &mctl_phy->zq[1].cr); /* DX0/DX1 */ + writel(0x04058D02, &mctl_phy->zq[2].cr); /* DX2/DX3 */ + + /* Disable auto-refresh prior to data training */ + setbits_le32(&mctl_ctl->rfshctl3, MCTL_RFSHCTL3_DIS_AUTO_REFRESH); + + setbits_le32(&mctl_phy->dsgcr, 0xf << 24); /* unclear what this is... */ + /* TODO: IODDRM (IO DDR-MODE) for DDR3L */ + clrsetbits_le32(&mctl_phy->pgcr[1], + MCTL_PGCR1_ZCKSEL_MASK, + MCTL_PGCR1_IODDRM_DDR3 | MCTL_PGCR1_INHVT_EN); + + setbits_le32(&mctl_phy->pllcr, 0x3 << 19); /* PLL frequency select */ + /* TODO: single-channel PLL mode??? missing */ + setbits_le32(&mctl_phy->pllcr, + MCTL_PLLGCR_PLL_BYPASS | MCTL_PLLGCR_PLL_POWERDOWN); + /* setbits_le32(&mctl_phy->pir, MCTL_PIR_PLL_BYPASS); included below */ + + /* Disable VT compensation */ + clrbits_le32(&mctl_phy->pgcr[0], 0x3f); + + /* TODO: "other" PLL mode ... 0x20000 seems to be the PLL Bypass */ + if (para->dram_type == DRAM_TYPE_DDR3) + clrsetbits_le32(&mctl_phy->pir, MCTL_PIR_MASK, 0x20df3); + else + clrsetbits_le32(&mctl_phy->pir, MCTL_PIR_MASK, 0x2c573); + + sdelay(10000); /* XXX necessary? */ + + /* Wait for the INIT bit to clear itself... */ + while ((readl(&mctl_phy->pir) & MCTL_PIR_INIT) != MCTL_PIR_INIT) { + /* not done yet -- keep spinning */ + debug("MCTL_PIR_INIT not set\n"); + sdelay(1000); + /* TODO: implement timeout */ + } + + /* TODO: not used --- there's a "2rank debug" section here */ + + /* Original dram init code which may come in handy later + ******************************************************** + * LPDDR2 and LPDDR3 * + if ((para->dram_type) == 6 || (para->dram_type) == 7) { + reg_val = mctl_read_w(P0_DSGCR + ch_offset); + reg_val &= (~(0x3<<6)); * set DQSGX to 1 * + reg_val |= (0x1<<6); * dqs gate extend * + mctl_write_w(P0_DSGCR + ch_offset, reg_val); + dram_dbg("DQS Gate Extend Enable!\n", ch_index); + } + + * Disable ZCAL after initial--for nand dma debug--20140330 by YSZ * + if (para->dram_tpr13 & (0x1<<31)) { + reg_val = mctl_read_w(P0_ZQ0CR + ch_offset); + reg_val |= (0x7<<11); + mctl_write_w(P0_ZQ0CR + ch_offset, reg_val); + } + ******************************************************** + */ + + /* + * TODO: more 2-rank support + * (setting the "dqs gate delay to average between 2 rank") + */ + + /* check if any errors are set */ + if (readl(&mctl_phy->pgsr[0]) & MCTL_PGSR0_ERRORS) { + debug("Channel %d unavailable!\n", ch_index); + return 0; + } else{ + /* initial OK */ + debug("Channel %d OK!\n", ch_index); + /* return 1; */ + } + + while ((readl(&mctl_ctl->stat) & 0x1) != 0x1) { + debug("Waiting for INIT to be done (controller to come up into 'normal operating' mode\n"); + sdelay(100000); + /* init not done */ + /* TODO: implement time-out */ + } + debug("done\n"); + + /* "DDR is controller by contoller" */ + clrbits_le32(&mctl_phy->pgcr[3], (1 << 25)); + + /* TODO: is the following necessary? */ + debug("DFIMISC before writing 0: 0x%x\n", readl(&mctl_ctl->dfimisc)); + writel(0, &mctl_ctl->dfimisc); + + /* Enable auto-refresh */ + clrbits_le32(&mctl_ctl->rfshctl3, MCTL_RFSHCTL3_DIS_AUTO_REFRESH); + + debug("channel_init complete\n"); + return 1; +} + +signed int DRAMC_get_dram_size(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + unsigned int reg_val; + unsigned int dram_size; + unsigned int temp; + + reg_val = readl(&mctl_com->cr); + + temp = (reg_val >> 8) & 0xf; /* page size code */ + dram_size = (temp - 6); /* (1 << dram_size) * 512Bytes */ + + temp = (reg_val >> 4) & 0xf; /* row width code */ + dram_size += (temp + 1); /* (1 << dram_size) * 512Bytes */ + + temp = (reg_val >> 2) & 0x3; /* bank number code */ + dram_size += (temp + 2); /* (1 << dram_size) * 512Bytes */ + + temp = reg_val & 0x3; /* rank number code */ + dram_size += temp; /* (1 << dram_size) * 512Bytes */ + + temp = (reg_val >> 19) & 0x1; /* channel number code */ + dram_size += temp; /* (1 << dram_size) * 512Bytes */ + + dram_size = dram_size - 11; /* (1 << dram_size) MBytes */ + + return 1 << dram_size; +} + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + struct dram_sun9i_cl_cwl_timing cl_cwl[] = { + { .CL = 5, .CWL = 5, .tCKmin = 3000, .tCKmax = 3300 }, + { .CL = 6, .CWL = 5, .tCKmin = 2500, .tCKmax = 3300 }, + { .CL = 8, .CWL = 6, .tCKmin = 1875, .tCKmax = 2500 }, + { .CL = 10, .CWL = 7, .tCKmin = 1500, .tCKmax = 1875 }, + { .CL = 11, .CWL = 8, .tCKmin = 1250, .tCKmax = 1500 } + }; + + /* Set initial parameters, these get modified by the autodetect code */ + struct dram_sun9i_para para = { + .dram_type = DRAM_TYPE_DDR3, + .bus_width = 32, + .chan = 2, + .rank = 1, + /* .rank = 2, */ + .page_size = 4096, + /* .rows = 16, */ + .rows = 15, + + /* CL/CWL table for the speed bin */ + .cl_cwl_table = cl_cwl, + .cl_cwl_numentries = sizeof(cl_cwl) / + sizeof(struct dram_sun9i_cl_cwl_timing), + + /* timings */ + .tREFI = 7800, /* 7.8us (up to 85 degC) */ + .tRFC = 260, /* 260ns for 4GBit devices */ + /* 350ns @ 8GBit */ + + .tRCD = 13750, + .tRP = 13750, + .tRC = 48750, + .tRAS = 35000, + + .tDLLK = 512, + .tRTP = { .ck = 4, .ps = 7500 }, + .tWTR = { .ck = 4, .ps = 7500 }, + .tWR = 15, + .tMRD = 4, + .tMOD = { .ck = 12, .ps = 15000 }, + .tCCD = 4, + .tRRD = { .ck = 4, .ps = 7500 }, + .tFAW = 40, + + /* calibration timing */ + /* .tZQinit = { .ck = 512, .ps = 640000 }, */ + .tZQoper = { .ck = 256, .ps = 320000 }, + .tZQCS = { .ck = 64, .ps = 80000 }, + + /* reset timing */ + /* .tXPR = { .ck = 5, .ps = 10000 }, */ + + /* self-refresh timings */ + .tXS = { .ck = 5, .ps = 10000 }, + .tXSDLL = 512, + .tCKSRE = { .ck = 5, .ps = 10000 }, + .tCKSRX = { .ck = 5, .ps = 10000 }, + + /* power-down timings */ + .tXP = { .ck = 3, .ps = 6000 }, + .tXPDLL = { .ck = 10, .ps = 24000 }, + .tCKE = { .ck = 3, .ps = 5000 }, + + /* write leveling timings */ + .tWLMRD = 40, + /* .tWLDQSEN = 25, */ + .tWLO = 7500, + /* .tWLOE = 2000, */ + }; + + /* + * Disable A80 internal 240 ohm resistor. + * + * This code sequence is adapated from Allwinner's Boot0 (see + * https://github.com/allwinner-zh/bootloader.git), as there + * is no documentation for these two registers in the R_PRCM + * block. + */ + setbits_le32(SUNXI_PRCM_BASE + 0x1e0, (0x3 << 8)); + writel(0, SUNXI_PRCM_BASE + 0x1e8); + + mctl_sys_init(); + + if (!mctl_channel_init(0, ¶)) + return 0; + + /* dual-channel */ + if (!mctl_channel_init(1, ¶)) { + /* disable channel 1 */ + clrsetbits_le32(&mctl_com->cr, MCTL_CR_CHANNEL_MASK, + MCTL_CR_CHANNEL_SINGLE); + /* disable channel 1 global clock */ + clrbits_le32(&mctl_com->cr, MCTL_CCR_CH1_CLK_EN); + } + + mctl_com_init(¶); + + /* return the proper RAM size */ + return DRAMC_get_dram_size() << 20; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_sunxi_dw.c b/roms/u-boot/arch/arm/mach-sunxi/dram_sunxi_dw.c new file mode 100644 index 000000000..9107b114d --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_sunxi_dw.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * sun8i H3 platform dram controller init + * + * (C) Copyright 2007-2015 Allwinner Technology Co. + * Jerry Wang <wangflord@allwinnertech.com> + * (C) Copyright 2015 Vishnu Patekar <vishnupatekar0510@gmail.com> + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + * (C) Copyright 2015 Jens Kuske <jenskuske@gmail.com> + */ +#include <common.h> +#include <init.h> +#include <log.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> +#include <linux/delay.h> +#include <linux/kconfig.h> + +static void mctl_phy_init(u32 val) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + writel(val | PIR_INIT, &mctl_ctl->pir); + mctl_await_completion(&mctl_ctl->pgsr[0], PGSR_INIT_DONE, 0x1); +} + +static void mctl_set_bit_delays(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + int i, j; + + clrbits_le32(&mctl_ctl->pgcr[0], 1 << 26); + + for (i = 0; i < NR_OF_BYTE_LANES; i++) + for (j = 0; j < LINES_PER_BYTE_LANE; j++) + writel(DXBDLR_WRITE_DELAY(para->dx_write_delays[i][j]) | + DXBDLR_READ_DELAY(para->dx_read_delays[i][j]), + &mctl_ctl->dx[i].bdlr[j]); + + for (i = 0; i < 31; i++) + writel(ACBDLR_WRITE_DELAY(para->ac_delays[i]), + &mctl_ctl->acbdlr[i]); + +#ifdef CONFIG_MACH_SUN8I_R40 + /* DQSn, DMn, DQn output enable bit delay */ + for (i = 0; i < 4; i++) + writel(0x6 << 24, &mctl_ctl->dx[i].sdlr); +#endif + + setbits_le32(&mctl_ctl->pgcr[0], 1 << 26); +} + +enum { + MBUS_PORT_CPU = 0, + MBUS_PORT_GPU = 1, + MBUS_PORT_UNUSED = 2, + MBUS_PORT_DMA = 3, + MBUS_PORT_VE = 4, + MBUS_PORT_CSI = 5, + MBUS_PORT_NAND = 6, + MBUS_PORT_SS = 7, + MBUS_PORT_DE_V3S = 8, + MBUS_PORT_DE_CFD_V3S = 9, + MBUS_PORT_TS = 8, + MBUS_PORT_DI = 9, + MBUS_PORT_DE = 10, + MBUS_PORT_DE_CFD = 11, + MBUS_PORT_UNKNOWN1 = 12, + MBUS_PORT_UNKNOWN2 = 13, + MBUS_PORT_UNKNOWN3 = 14, +}; + +enum { + MBUS_QOS_LOWEST = 0, + MBUS_QOS_LOW, + MBUS_QOS_HIGH, + MBUS_QOS_HIGHEST +}; + +static inline void mbus_configure_port(u8 port, + bool bwlimit, + bool priority, + u8 qos, /* MBUS_QOS_LOWEST .. MBUS_QOS_HIGEST */ + u8 waittime, /* 0 .. 0xf */ + u8 acs, /* 0 .. 0xff */ + u16 bwl0, /* 0 .. 0xffff, bandwidth limit in MB/s */ + u16 bwl1, + u16 bwl2) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + const u32 cfg0 = ( (bwlimit ? (1 << 0) : 0) + | (priority ? (1 << 1) : 0) + | ((qos & 0x3) << 2) + | ((waittime & 0xf) << 4) + | ((acs & 0xff) << 8) + | (bwl0 << 16) ); + const u32 cfg1 = ((u32)bwl2 << 16) | (bwl1 & 0xffff); + + debug("MBUS port %d cfg0 %08x cfg1 %08x\n", port, cfg0, cfg1); + writel(cfg0, &mctl_com->mcr[port][0]); + writel(cfg1, &mctl_com->mcr[port][1]); +} + +#define MBUS_CONF(port, bwlimit, qos, acs, bwl0, bwl1, bwl2) \ + mbus_configure_port(MBUS_PORT_ ## port, bwlimit, false, \ + MBUS_QOS_ ## qos, 0, acs, bwl0, bwl1, bwl2) + +static void mctl_set_master_priority_h3(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel((1 << 16) | (400 << 0), &mctl_com->bwcr); + + /* set cpu high priority */ + writel(0x00000001, &mctl_com->mapr); + + MBUS_CONF( CPU, true, HIGHEST, 0, 512, 256, 128); + MBUS_CONF( GPU, true, HIGH, 0, 1536, 1024, 256); + MBUS_CONF(UNUSED, true, HIGHEST, 0, 512, 256, 96); + MBUS_CONF( DMA, true, HIGHEST, 0, 256, 128, 32); + MBUS_CONF( VE, true, HIGH, 0, 1792, 1600, 256); + MBUS_CONF( CSI, true, HIGHEST, 0, 256, 128, 32); + MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64); + MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64); + MBUS_CONF( DE, true, HIGHEST, 3, 8192, 6120, 1024); + MBUS_CONF(DE_CFD, true, HIGH, 0, 1024, 288, 64); +} + +static void mctl_set_master_priority_v3s(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel((1 << 16) | (400 << 0), &mctl_com->bwcr); + + /* set cpu high priority */ + writel(0x00000001, &mctl_com->mapr); + + MBUS_CONF( CPU, true, HIGHEST, 0, 160, 100, 80); + MBUS_CONF( GPU, true, HIGH, 0, 1792, 1536, 0); + MBUS_CONF( UNUSED, true, HIGHEST, 0, 256, 128, 80); + MBUS_CONF( DMA, true, HIGH, 0, 256, 100, 0); + MBUS_CONF( VE, true, HIGH, 0, 2048, 1600, 0); + MBUS_CONF( CSI, true, HIGHEST, 0, 384, 256, 0); + MBUS_CONF( NAND, true, HIGH, 0, 100, 50, 0); + MBUS_CONF( SS, true, HIGH, 0, 384, 256, 0); + MBUS_CONF( DE_V3S, false, HIGH, 0, 8192, 4096, 0); + MBUS_CONF(DE_CFD_V3S, true, HIGH, 0, 640, 256, 0); +} + +static void mctl_set_master_priority_a64(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel(399, &mctl_com->tmr); + writel((1 << 16), &mctl_com->bwcr); + + /* Port 2 is reserved per Allwinner's linux-3.10 source, yet they + * initialise it */ + MBUS_CONF( CPU, true, HIGHEST, 0, 160, 100, 80); + MBUS_CONF( GPU, false, HIGH, 0, 1536, 1400, 256); + MBUS_CONF(UNUSED, true, HIGHEST, 0, 512, 256, 96); + MBUS_CONF( DMA, true, HIGH, 0, 256, 80, 100); + MBUS_CONF( VE, true, HIGH, 0, 1792, 1600, 256); + MBUS_CONF( CSI, true, HIGH, 0, 256, 128, 0); + MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64); + MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64); + MBUS_CONF( DE, true, HIGH, 2, 8192, 6144, 2048); + MBUS_CONF(DE_CFD, true, HIGH, 0, 1280, 144, 64); + + writel(0x81000004, &mctl_com->mdfs_bwlr[2]); +} + +static void mctl_set_master_priority_h5(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel(399, &mctl_com->tmr); + writel((1 << 16), &mctl_com->bwcr); + + /* set cpu high priority */ + writel(0x00000001, &mctl_com->mapr); + + /* Port 2 is reserved per Allwinner's linux-3.10 source, yet + * they initialise it */ + MBUS_CONF( CPU, true, HIGHEST, 0, 300, 260, 150); + MBUS_CONF( GPU, true, HIGHEST, 0, 600, 400, 200); + MBUS_CONF(UNUSED, true, HIGHEST, 0, 512, 256, 96); + MBUS_CONF( DMA, true, HIGHEST, 0, 256, 128, 32); + MBUS_CONF( VE, true, HIGHEST, 0, 1900, 1500, 1000); + MBUS_CONF( CSI, true, HIGHEST, 0, 150, 120, 100); + MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64); + MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64); + MBUS_CONF( DE, true, HIGHEST, 3, 3400, 2400, 1024); + MBUS_CONF(DE_CFD, true, HIGHEST, 0, 600, 400, 200); +} + +static void mctl_set_master_priority_r40(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel(399, &mctl_com->tmr); + writel((1 << 16), &mctl_com->bwcr); + + /* set cpu high priority */ + writel(0x00000001, &mctl_com->mapr); + + /* Port 2 is reserved per Allwinner's linux-3.10 source, yet + * they initialise it */ + MBUS_CONF( CPU, true, HIGHEST, 0, 300, 260, 150); + MBUS_CONF( GPU, true, HIGHEST, 0, 600, 400, 200); + MBUS_CONF( UNUSED, true, HIGHEST, 0, 512, 256, 96); + MBUS_CONF( DMA, true, HIGHEST, 0, 256, 128, 32); + MBUS_CONF( VE, true, HIGHEST, 0, 1900, 1500, 1000); + MBUS_CONF( CSI, true, HIGHEST, 0, 150, 120, 100); + MBUS_CONF( NAND, true, HIGH, 0, 256, 128, 64); + MBUS_CONF( SS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( TS, true, HIGHEST, 0, 256, 128, 64); + MBUS_CONF( DI, true, HIGH, 0, 1024, 256, 64); + + /* + * The port names are probably wrong, but no correct sources + * are available. + */ + MBUS_CONF( DE, true, HIGH, 0, 128, 48, 0); + MBUS_CONF( DE_CFD, true, HIGH, 0, 384, 256, 0); + MBUS_CONF(UNKNOWN1, true, HIGHEST, 0, 512, 384, 256); + MBUS_CONF(UNKNOWN2, true, HIGHEST, 2, 8192, 6144, 1024); + MBUS_CONF(UNKNOWN3, true, HIGH, 0, 1280, 144, 64); +} + +static void mctl_set_master_priority(uint16_t socid) +{ + switch (socid) { + case SOCID_H3: + mctl_set_master_priority_h3(); + return; + case SOCID_V3S: + mctl_set_master_priority_v3s(); + return; + case SOCID_A64: + mctl_set_master_priority_a64(); + return; + case SOCID_H5: + mctl_set_master_priority_h5(); + return; + case SOCID_R40: + mctl_set_master_priority_r40(); + return; + } +} + +static u32 bin_to_mgray(int val) +{ + static const u8 lookup_table[32] = { + 0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, + 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09, + 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x1f, 0x1c, 0x1d, + 0x14, 0x15, 0x16, 0x17, 0x12, 0x13, 0x10, 0x11, + }; + + return lookup_table[clamp(val, 0, 31)]; +} + +static int mgray_to_bin(u32 val) +{ + static const u8 lookup_table[32] = { + 0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, + 0x0e, 0x0f, 0x0c, 0x0d, 0x08, 0x09, 0x0a, 0x0b, + 0x1e, 0x1f, 0x1c, 0x1d, 0x18, 0x19, 0x1a, 0x1b, + 0x10, 0x11, 0x12, 0x13, 0x16, 0x17, 0x14, 0x15, + }; + + return lookup_table[val & 0x1f]; +} + +static void mctl_h3_zq_calibration_quirk(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + int zq_count; + +#if defined CONFIG_SUNXI_DRAM_DW_16BIT + zq_count = 4; +#else + zq_count = 6; +#endif + + if ((readl(SUNXI_SRAMC_BASE + 0x24) & 0xff) == 0 && + (readl(SUNXI_SRAMC_BASE + 0xf0) & 0x1) == 0) { + u32 reg_val; + + clrsetbits_le32(&mctl_ctl->zqcr, 0xffff, + CONFIG_DRAM_ZQ & 0xffff); + + writel(PIR_CLRSR, &mctl_ctl->pir); + mctl_phy_init(PIR_ZCAL); + + reg_val = readl(&mctl_ctl->zqdr[0]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[0]); + + reg_val = readl(&mctl_ctl->zqdr[1]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[1]); + writel(reg_val, &mctl_ctl->zqdr[2]); + } else { + int i; + u16 zq_val[6]; + u8 val; + + writel(0x0a0a0a0a, &mctl_ctl->zqdr[2]); + + for (i = 0; i < zq_count; i++) { + u8 zq = (CONFIG_DRAM_ZQ >> (i * 4)) & 0xf; + + writel((zq << 20) | (zq << 16) | (zq << 12) | + (zq << 8) | (zq << 4) | (zq << 0), + &mctl_ctl->zqcr); + + writel(PIR_CLRSR, &mctl_ctl->pir); + mctl_phy_init(PIR_ZCAL); + + zq_val[i] = readl(&mctl_ctl->zqdr[0]) & 0xff; + writel(REPEAT_BYTE(zq_val[i]), &mctl_ctl->zqdr[2]); + + writel(PIR_CLRSR, &mctl_ctl->pir); + mctl_phy_init(PIR_ZCAL); + + val = readl(&mctl_ctl->zqdr[0]) >> 24; + zq_val[i] |= bin_to_mgray(mgray_to_bin(val) - 1) << 8; + } + + writel((zq_val[1] << 16) | zq_val[0], &mctl_ctl->zqdr[0]); + writel((zq_val[3] << 16) | zq_val[2], &mctl_ctl->zqdr[1]); + if (zq_count > 4) + writel((zq_val[5] << 16) | zq_val[4], + &mctl_ctl->zqdr[2]); + } +} + +static void mctl_v3s_zq_calibration_quirk(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u32 reg_val; + + clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, + CONFIG_DRAM_ZQ & 0xffffff); + mctl_phy_init(PIR_ZCAL); + + reg_val = readl(&mctl_ctl->zqdr[0]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[0]); + + reg_val = readl(&mctl_ctl->zqdr[1]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[1]); +} + +static void mctl_set_cr(uint16_t socid, struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + writel(MCTL_CR_BL8 | MCTL_CR_INTERLEAVED | +#if defined CONFIG_SUNXI_DRAM_DDR3 + MCTL_CR_DDR3 | MCTL_CR_2T | +#elif defined CONFIG_SUNXI_DRAM_DDR2 + MCTL_CR_DDR2 | MCTL_CR_2T | +#elif defined CONFIG_SUNXI_DRAM_LPDDR3 + MCTL_CR_LPDDR3 | MCTL_CR_1T | +#else +#error Unsupported DRAM type! +#endif + (para->ranks[0].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) | + MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) | + (para->dual_rank ? MCTL_CR_DUAL_RANK : MCTL_CR_SINGLE_RANK) | + MCTL_CR_PAGE_SIZE(para->ranks[0].page_size) | + MCTL_CR_ROW_BITS(para->ranks[0].row_bits), &mctl_com->cr); + + if (para->dual_rank && (socid == SOCID_A64 || socid == SOCID_R40)) { + writel((para->ranks[1].bank_bits == 3 ? MCTL_CR_EIGHT_BANKS : MCTL_CR_FOUR_BANKS) | + MCTL_CR_BUS_FULL_WIDTH(para->bus_full_width) | + MCTL_CR_DUAL_RANK | + MCTL_CR_PAGE_SIZE(para->ranks[1].page_size) | + MCTL_CR_ROW_BITS(para->ranks[1].row_bits), &mctl_com->cr_r1); + } + + if (socid == SOCID_R40) { + /* Mux pin to A15 address line for single rank memory. */ + if (!para->dual_rank) + setbits_le32(&mctl_com->cr_r1, MCTL_CR_R1_MUX_A15); + } +} + +static void mctl_sys_init(uint16_t socid, struct dram_para *para) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + clrbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE); + clrbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET); + clrbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + clrbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + clrbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_EN); + if (socid == SOCID_A64 || socid == SOCID_R40) + clrbits_le32(&ccm->pll11_cfg, CCM_PLL11_CTRL_EN); + udelay(10); + + clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST); + udelay(1000); + + if (socid == SOCID_A64 || socid == SOCID_R40) { + clock_set_pll11(CONFIG_DRAM_CLK * 2 * 1000000, false); + clrsetbits_le32(&ccm->dram_clk_cfg, + CCM_DRAMCLK_CFG_DIV_MASK | + CCM_DRAMCLK_CFG_SRC_MASK, + CCM_DRAMCLK_CFG_DIV(1) | + CCM_DRAMCLK_CFG_SRC_PLL11 | + CCM_DRAMCLK_CFG_UPD); + } else if (socid == SOCID_H3 || socid == SOCID_H5 || socid == SOCID_V3S) { + clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false); + clrsetbits_le32(&ccm->dram_clk_cfg, + CCM_DRAMCLK_CFG_DIV_MASK | + CCM_DRAMCLK_CFG_SRC_MASK, + CCM_DRAMCLK_CFG_DIV(1) | + CCM_DRAMCLK_CFG_SRC_PLL5 | + CCM_DRAMCLK_CFG_UPD); + } + mctl_await_completion(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_UPD, 0); + + setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MCTL); + setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MCTL); + setbits_le32(&ccm->mbus_reset, CCM_MBUS_RESET_RESET); + setbits_le32(&ccm->mbus0_clk_cfg, MBUS_CLK_GATE); + + setbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_RST); + udelay(10); + + writel(socid == SOCID_H5 ? 0x8000 : 0xc00e, &mctl_ctl->clken); + udelay(500); +} + +/* These are more guessed based on some Allwinner code. */ +#define DX_GCR_ODT_DYNAMIC (0x0 << 4) +#define DX_GCR_ODT_ALWAYS_ON (0x1 << 4) +#define DX_GCR_ODT_OFF (0x2 << 4) + +static int mctl_channel_init(uint16_t socid, struct dram_para *para) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + unsigned int i; + + mctl_set_cr(socid, para); + mctl_set_timing_params(socid, para); + mctl_set_master_priority(socid); + + /* setting VTC, default disable all VT */ + clrbits_le32(&mctl_ctl->pgcr[0], (1 << 30) | 0x3f); + if (socid == SOCID_H5) + setbits_le32(&mctl_ctl->pgcr[1], (1 << 24) | (1 << 26)); + else + clrsetbits_le32(&mctl_ctl->pgcr[1], 1 << 24, 1 << 26); + + /* increase DFI_PHY_UPD clock */ + writel(PROTECT_MAGIC, &mctl_com->protect); + udelay(100); + clrsetbits_le32(&mctl_ctl->upd2, 0xfff << 16, 0x50 << 16); + writel(0x0, &mctl_com->protect); + udelay(100); + + /* set dramc odt */ + for (i = 0; i < 4; i++) { + u32 clearmask = (0x3 << 4) | (0x1 << 1) | (0x3 << 2) | + (0x3 << 12) | (0x3 << 14); + u32 setmask = IS_ENABLED(CONFIG_DRAM_ODT_EN) ? + DX_GCR_ODT_DYNAMIC : DX_GCR_ODT_OFF; + + if (socid == SOCID_H5) { + clearmask |= 0x2 << 8; + setmask |= 0x4 << 8; + } + clrsetbits_le32(&mctl_ctl->dx[i].gcr, clearmask, setmask); + } + + /* AC PDR should always ON */ + clrsetbits_le32(&mctl_ctl->aciocr, socid == SOCID_H5 ? (0x1 << 11) : 0, + 0x1 << 1); + + /* set DQS auto gating PD mode */ + setbits_le32(&mctl_ctl->pgcr[2], 0x3 << 6); + + if (socid == SOCID_H3) { + /* dx ddr_clk & hdr_clk dynamic mode */ + clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12)); + + /* dphy & aphy phase select 270 degree */ + clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), + (0x1 << 10) | (0x2 << 8)); + } else if (socid == SOCID_V3S) { + /* dx ddr_clk & hdr_clk dynamic mode */ + clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12)); + + /* dphy & aphy phase select 270 degree */ + clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), + (0x1 << 10) | (0x1 << 8)); + } else if (socid == SOCID_A64 || socid == SOCID_H5) { + /* dphy & aphy phase select ? */ + clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), + (0x0 << 10) | (0x3 << 8)); + } else if (socid == SOCID_R40) { + /* dx ddr_clk & hdr_clk dynamic mode (tpr13[9] == 0) */ + clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12)); + + /* dphy & aphy phase select ? */ + clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), + (0x0 << 10) | (0x3 << 8)); + } + + /* set half DQ */ + if (!para->bus_full_width) { +#if defined CONFIG_SUNXI_DRAM_DW_32BIT + writel(0x0, &mctl_ctl->dx[2].gcr); + writel(0x0, &mctl_ctl->dx[3].gcr); +#elif defined CONFIG_SUNXI_DRAM_DW_16BIT + writel(0x0, &mctl_ctl->dx[1].gcr); +#else +#error Unsupported DRAM bus width! +#endif + } + + /* data training configuration */ + clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, + (para->dual_rank ? 0x3 : 0x1) << 24); + + mctl_set_bit_delays(para); + udelay(50); + + if (socid == SOCID_V3S) { + mctl_v3s_zq_calibration_quirk(para); + + mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | + PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE); + } else if (socid == SOCID_H3) { + mctl_h3_zq_calibration_quirk(para); + + mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | + PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE); + } else if (socid == SOCID_A64 || socid == SOCID_H5) { + clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ); + + mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | + PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE); + /* no PIR_QSGATE for H5 ???? */ + } else if (socid == SOCID_R40) { + clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, CONFIG_DRAM_ZQ); + + mctl_phy_init(PIR_ZCAL | PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | + PIR_DRAMRST | PIR_DRAMINIT); + } + + /* detect ranks and bus width */ + if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20)) { + /* only one rank */ + if (((readl(&mctl_ctl->dx[0].gsr[0]) >> 24) & 0x2) +#if defined CONFIG_SUNXI_DRAM_DW_32BIT + || ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x2) +#endif + ) { + clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24); + para->dual_rank = 0; + } + + /* only half DQ width */ +#if defined CONFIG_SUNXI_DRAM_DW_32BIT + if (((readl(&mctl_ctl->dx[2].gsr[0]) >> 24) & 0x1) || + ((readl(&mctl_ctl->dx[3].gsr[0]) >> 24) & 0x1)) { + writel(0x0, &mctl_ctl->dx[2].gcr); + writel(0x0, &mctl_ctl->dx[3].gcr); + para->bus_full_width = 0; + } +#elif defined CONFIG_SUNXI_DRAM_DW_16BIT + if ((readl(&mctl_ctl->dx[1].gsr[0]) >> 24) & 0x1) { + writel(0x0, &mctl_ctl->dx[1].gcr); + para->bus_full_width = 0; + } +#endif + + mctl_set_cr(socid, para); + udelay(20); + + /* re-train */ + mctl_phy_init(PIR_QSGATE); + if (readl(&mctl_ctl->pgsr[0]) & (0xfe << 20)) + return 1; + } + + /* check the dramc status */ + mctl_await_completion(&mctl_ctl->statr, 0x1, 0x1); + + /* liuke added for refresh debug */ + setbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31); + udelay(10); + clrbits_le32(&mctl_ctl->rfshctl0, 0x1 << 31); + udelay(10); + + /* set PGCR3, CKE polarity */ + if (socid == SOCID_H3 || socid == SOCID_V3S) + writel(0x00aa0060, &mctl_ctl->pgcr[3]); + else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) + writel(0xc0aa0060, &mctl_ctl->pgcr[3]); + + /* power down zq calibration module for power save */ + setbits_le32(&mctl_ctl->zqcr, ZQCR_PWRDOWN); + + /* enable master access */ + writel(0xffffffff, &mctl_com->maer); + + return 0; +} + +/* + * Test if memory at offset offset matches memory at a certain base + */ +static bool mctl_mem_matches_base(u32 offset, ulong base) +{ + /* Try to write different values to RAM at two addresses */ + writel(0, base); + writel(0xaa55aa55, base + offset); + dsb(); + /* Check if the same value is actually observed when reading back */ + return readl(base) == + readl(base + offset); +} + +static void mctl_auto_detect_dram_size_rank(uint16_t socid, struct dram_para *para, ulong base, struct rank_para *rank) +{ + /* detect row address bits */ + rank->page_size = 512; + rank->row_bits = 16; + rank->bank_bits = 2; + mctl_set_cr(socid, para); + + for (rank->row_bits = 11; rank->row_bits < 16; rank->row_bits++) + if (mctl_mem_matches_base((1 << (rank->row_bits + rank->bank_bits)) * rank->page_size, base)) + break; + + /* detect bank address bits */ + rank->bank_bits = 3; + mctl_set_cr(socid, para); + + for (rank->bank_bits = 2; rank->bank_bits < 3; rank->bank_bits++) + if (mctl_mem_matches_base((1 << rank->bank_bits) * rank->page_size, base)) + break; + + /* detect page size */ + rank->page_size = 8192; + mctl_set_cr(socid, para); + + for (rank->page_size = 512; rank->page_size < 8192; rank->page_size *= 2) + if (mctl_mem_matches_base(rank->page_size, base)) + break; +} + +static unsigned long mctl_calc_rank_size(struct rank_para *rank) +{ + return (1UL << (rank->row_bits + rank->bank_bits)) * rank->page_size; +} + +/* + * Because we cannot do mctl_phy_init(PIR_QSGATE) on R40 now (which leads + * to failure), it's needed to detect the rank count of R40 in another way. + * + * The code here is modelled after time_out_detect() in BSP, which tries to + * access the memory and check for error code. + * + * TODO: auto detect half DQ width here + */ +static void mctl_r40_detect_rank_count(struct dram_para *para) +{ + ulong rank1_base = (ulong) CONFIG_SYS_SDRAM_BASE + + mctl_calc_rank_size(¶->ranks[0]); + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + /* Enable read time out */ + setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 25); + + (void) readl((void *) rank1_base); + udelay(10); + + if (readl(&mctl_ctl->pgsr[0]) & (0x1 << 13)) { + clrsetbits_le32(&mctl_ctl->dtcr, 0xf << 24, 0x1 << 24); + para->dual_rank = 0; + } + + /* Reset PHY FIFO to clear it */ + clrbits_le32(&mctl_ctl->pgcr[0], 0x1 << 26); + udelay(100); + setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 26); + + /* Clear error status */ + setbits_le32(&mctl_ctl->pgcr[0], 0x1 << 24); + + /* Clear time out flag */ + clrbits_le32(&mctl_ctl->pgsr[0], 0x1 << 13); + + /* Disable read time out */ + clrbits_le32(&mctl_ctl->pgcr[0], 0x1 << 25); +} + +static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para) +{ + mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE, ¶->ranks[0]); + + if ((socid == SOCID_A64 || socid == SOCID_R40) && para->dual_rank) { + mctl_auto_detect_dram_size_rank(socid, para, (ulong)CONFIG_SYS_SDRAM_BASE + mctl_calc_rank_size(¶->ranks[0]), ¶->ranks[1]); + } +} + +/* + * The actual values used here are taken from Allwinner provided boot0 + * binaries, though they are probably board specific, so would likely benefit + * from invidual tuning for each board. Apparently a lot of boards copy from + * some Allwinner reference design, so we go with those generic values for now + * in the hope that they are reasonable for most (all?) boards. + */ +#define SUN8I_H3_DX_READ_DELAYS \ + {{ 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0 }, \ + { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ + { 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0 }, \ + { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }} +#define SUN8I_H3_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6 }} +#define SUN8I_H3_AC_DELAYS \ + { 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0 } + +#define SUN8I_V3S_DX_READ_DELAYS \ + {{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0 }, \ + { 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN8I_V3S_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN8I_V3S_AC_DELAYS \ + { 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0 } + +#define SUN8I_R40_DX_READ_DELAYS \ + {{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ + { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ + { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ + { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 } } +#define SUN8I_R40_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 } } +#define SUN8I_R40_AC_DELAYS \ + { 0, 0, 3, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0 } + +#define SUN50I_A64_DX_READ_DELAYS \ + {{ 16, 16, 16, 16, 17, 16, 16, 17, 16, 1, 0 }, \ + { 17, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0 }, \ + { 16, 17, 17, 16, 16, 16, 16, 16, 16, 0, 0 }, \ + { 17, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0 }} +#define SUN50I_A64_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15 }, \ + { 0, 0, 0, 0, 1, 1, 1, 1, 0, 10, 10 }, \ + { 1, 0, 1, 1, 1, 1, 1, 1, 0, 11, 11 }, \ + { 1, 0, 0, 1, 1, 1, 1, 1, 0, 12, 12 }} +#define SUN50I_A64_AC_DELAYS \ + { 5, 5, 13, 10, 2, 5, 3, 3, \ + 0, 3, 3, 3, 1, 0, 0, 0, \ + 3, 4, 0, 3, 4, 1, 4, 0, \ + 1, 1, 0, 1, 13, 5, 4 } + +#define SUN8I_H5_DX_READ_DELAYS \ + {{ 14, 15, 17, 17, 17, 17, 17, 18, 17, 3, 3 }, \ + { 21, 21, 12, 22, 21, 21, 21, 21, 21, 3, 3 }, \ + { 16, 19, 19, 17, 22, 22, 21, 22, 19, 3, 3 }, \ + { 21, 21, 22, 22, 20, 21, 19, 19, 19, 3, 3 } } +#define SUN8I_H5_DX_WRITE_DELAYS \ + {{ 1, 2, 3, 4, 3, 4, 4, 4, 6, 6, 6 }, \ + { 6, 6, 6, 5, 5, 5, 5, 5, 6, 6, 6 }, \ + { 0, 2, 4, 2, 6, 5, 5, 5, 6, 6, 6 }, \ + { 3, 3, 3, 2, 2, 1, 1, 1, 4, 4, 4 } } +#define SUN8I_H5_AC_DELAYS \ + { 0, 0, 5, 5, 0, 0, 0, 0, \ + 0, 0, 0, 0, 3, 3, 3, 3, \ + 3, 3, 3, 3, 3, 3, 3, 3, \ + 3, 3, 3, 3, 2, 0, 0 } + +unsigned long sunxi_dram_init(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + unsigned long size; + + struct dram_para para = { + .dual_rank = 1, + .bus_full_width = 1, + .ranks = { + { + .row_bits = 15, + .bank_bits = 3, + .page_size = 4096, + }, + { + .row_bits = 15, + .bank_bits = 3, + .page_size = 4096, + } + }, + +#if defined(CONFIG_MACH_SUN8I_H3) + .dx_read_delays = SUN8I_H3_DX_READ_DELAYS, + .dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS, + .ac_delays = SUN8I_H3_AC_DELAYS, +#elif defined(CONFIG_MACH_SUN8I_V3S) + .dx_read_delays = SUN8I_V3S_DX_READ_DELAYS, + .dx_write_delays = SUN8I_V3S_DX_WRITE_DELAYS, + .ac_delays = SUN8I_V3S_AC_DELAYS, +#elif defined(CONFIG_MACH_SUN8I_R40) + .dx_read_delays = SUN8I_R40_DX_READ_DELAYS, + .dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS, + .ac_delays = SUN8I_R40_AC_DELAYS, +#elif defined(CONFIG_MACH_SUN50I) + .dx_read_delays = SUN50I_A64_DX_READ_DELAYS, + .dx_write_delays = SUN50I_A64_DX_WRITE_DELAYS, + .ac_delays = SUN50I_A64_AC_DELAYS, +#elif defined(CONFIG_MACH_SUN50I_H5) + .dx_read_delays = SUN8I_H5_DX_READ_DELAYS, + .dx_write_delays = SUN8I_H5_DX_WRITE_DELAYS, + .ac_delays = SUN8I_H5_AC_DELAYS, +#endif + }; +/* + * Let the compiler optimize alternatives away by passing this value into + * the static functions. This saves us #ifdefs, but still keeps the binary + * small. + */ +#if defined(CONFIG_MACH_SUN8I_H3) + uint16_t socid = SOCID_H3; +#elif defined(CONFIG_MACH_SUN8I_R40) + uint16_t socid = SOCID_R40; +#elif defined(CONFIG_MACH_SUN8I_V3S) + uint16_t socid = SOCID_V3S; +#elif defined(CONFIG_MACH_SUN50I) + uint16_t socid = SOCID_A64; +#elif defined(CONFIG_MACH_SUN50I_H5) + uint16_t socid = SOCID_H5; +#endif + + mctl_sys_init(socid, ¶); + if (mctl_channel_init(socid, ¶)) + return 0; + + if (para.dual_rank) + writel(0x00000303, &mctl_ctl->odtmap); + else + writel(0x00000201, &mctl_ctl->odtmap); + udelay(1); + + /* odt delay */ + if (socid == SOCID_H3) + writel(0x0c000400, &mctl_ctl->odtcfg); + + if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) { + /* VTF enable (tpr13[8] == 1) */ + setbits_le32(&mctl_ctl->vtfcr, + (socid != SOCID_A64 ? 3 : 2) << 8); + /* DQ hold disable (tpr13[26] == 1) */ + clrbits_le32(&mctl_ctl->pgcr[2], (1 << 13)); + } + + /* clear credit value */ + setbits_le32(&mctl_com->cccr, 1 << 31); + udelay(10); + + if (socid == SOCID_R40) { + mctl_r40_detect_rank_count(¶); + mctl_set_cr(SOCID_R40, ¶); + } + + mctl_auto_detect_dram_size(socid, ¶); + mctl_set_cr(socid, ¶); + + size = mctl_calc_rank_size(¶.ranks[0]); + if (socid == SOCID_A64 || socid == SOCID_R40) { + if (para.dual_rank) + size += mctl_calc_rank_size(¶.ranks[1]); + } else if (para.dual_rank) { + size *= 2; + } + + return size; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/Makefile b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/Makefile new file mode 100644 index 000000000..39a8756c2 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_SUNXI_DRAM_DDR3_1333) += ddr3_1333.o +obj-$(CONFIG_SUNXI_DRAM_LPDDR3_STOCK) += lpddr3_stock.o +obj-$(CONFIG_SUNXI_DRAM_DDR2_V3S) += ddr2_v3s.o +obj-$(CONFIG_SUNXI_DRAM_H6_LPDDR3) += h6_lpddr3.o +obj-$(CONFIG_SUNXI_DRAM_H6_DDR3_1333) += h6_ddr3_1333.o +# currently only DDR3 is supported on H616 +obj-$(CONFIG_MACH_SUN50I_H616) += h616_ddr3_1333.o diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/ddr2_v3s.c b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/ddr2_v3s.c new file mode 100644 index 000000000..9077f86a8 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/ddr2_v3s.c @@ -0,0 +1,84 @@ +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +void mctl_set_timing_params(uint16_t socid, struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 tccd = 1; + u8 tfaw = ns_to_t(50); + u8 trrd = max(ns_to_t(10), 2); + u8 trcd = ns_to_t(20); + u8 trc = ns_to_t(65); + u8 txp = 2; + u8 twtr = max(ns_to_t(8), 2); + u8 trtp = max(ns_to_t(8), 2); + u8 twr = max(ns_to_t(15), 3); + u8 trp = ns_to_t(15); + u8 tras = ns_to_t(45); + u16 trefi = ns_to_t(7800) / 32; + u16 trfc = ns_to_t(328); + + u8 tmrw = 0; + u8 tmrd = 2; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 4; + u8 trasmax = 27; + + u8 tcl = 3; /* CL 6 */ + u8 tcwl = 3; /* CWL 6 */ + u8 t_rdata_en = 1; + u8 wr_latency = 1; + + u32 tdinit0 = (400 * CONFIG_DRAM_CLK) + 1; /* 400us */ + u32 tdinit1 = (500 * CONFIG_DRAM_CLK) / 1000 + 1; /* 500ns */ + u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */ + u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */ + u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */ + + /* set mode register */ + writel(0x263, &mctl_ctl->mr[0]); + writel(0x4, &mctl_ctl->mr[1]); + writel(0x0, &mctl_ctl->mr[2]); + writel(0x0, &mctl_ctl->mr[3]); + + /* set DRAM timing */ + writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) | + DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras), + &mctl_ctl->dramtmg[0]); + writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc), + &mctl_ctl->dramtmg[1]); + writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) | + DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd), + &mctl_ctl->dramtmg[2]); + writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod), + &mctl_ctl->dramtmg[3]); + writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) | + DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]); + writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) | + DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke), + &mctl_ctl->dramtmg[5]); + + /* set two rank timing */ + clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0), + (0x66 << 8) | (0x10 << 0)); + + /* set PHY interface timing, write latency and read latency configure */ + writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) | + (wr_latency << 0), &mctl_ctl->pitmg[0]); + + /* set PHY timing, PTR0-2 use default */ + writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]); + writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]); + + /* set refresh timing */ + writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/ddr3_1333.c b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/ddr3_1333.c new file mode 100644 index 000000000..0471e8a49 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/ddr3_1333.c @@ -0,0 +1,87 @@ +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +void mctl_set_timing_params(uint16_t socid, struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 tccd = 2; + u8 tfaw = ns_to_t(50); + u8 trrd = max(ns_to_t(10), 4); + u8 trcd = ns_to_t(15); + u8 trc = ns_to_t(53); + u8 txp = max(ns_to_t(8), 3); + u8 twtr = max(ns_to_t(8), 4); + u8 trtp = max(ns_to_t(8), 4); + u8 twr = max(ns_to_t(15), 3); + u8 trp = ns_to_t(15); + u8 tras = ns_to_t(38); + u16 trefi = ns_to_t(7800) / 32; + u16 trfc = ns_to_t(350); + + u8 tmrw = 0; + u8 tmrd = 4; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 4; + u8 trasmax = 24; + + u8 tcl = 6; /* CL 12 */ + u8 tcwl = 4; /* CWL 8 */ + u8 t_rdata_en = 4; + u8 wr_latency = 2; + + u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */ + u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; /* 360ns */ + u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 2 + twr; /* WL + BL / 2 + tWR */ + u8 twr2rd = tcwl + 2 + twtr; /* WL + BL / 2 + tWTR */ + u8 trd2wr = tcl + 2 + 1 - tcwl; /* RL + BL / 2 + 2 - WL */ + + /* set mode register */ + writel(0x1c70, &mctl_ctl->mr[0]); /* CL=11, WR=12 */ + writel(0x40, &mctl_ctl->mr[1]); + writel(0x18, &mctl_ctl->mr[2]); /* CWL=8 */ + writel(0x0, &mctl_ctl->mr[3]); + + if (socid == SOCID_R40) + writel(0x3, &mctl_ctl->lp3mr11); /* odt_en[7:4] */ + + /* set DRAM timing */ + writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) | + DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras), + &mctl_ctl->dramtmg[0]); + writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc), + &mctl_ctl->dramtmg[1]); + writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) | + DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd), + &mctl_ctl->dramtmg[2]); + writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod), + &mctl_ctl->dramtmg[3]); + writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) | + DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]); + writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) | + DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke), + &mctl_ctl->dramtmg[5]); + + /* set two rank timing */ + clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0), + ((socid == SOCID_H5 ? 0x33 : 0x66) << 8) | (0x10 << 0)); + + /* set PHY interface timing, write latency and read latency configure */ + writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) | + (wr_latency << 0), &mctl_ctl->pitmg[0]); + + /* set PHY timing, PTR0-2 use default */ + writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]); + writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]); + + /* set refresh timing */ + writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c new file mode 100644 index 000000000..8f508344b --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h616_ddr3_1333.c @@ -0,0 +1,94 @@ +/* + * sun50i H616 DDR3-1333 timings, as programmed by Allwinner's boot0 + * + * The chips are probably able to be driven by a faster clock, but boot0 + * uses a more conservative timing (as usual). + * + * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net> + * Based on H6 DDR3 timings: + * (C) Copyright 2018,2019 Arm Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +void mctl_set_timing_params(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 tccd = 2; /* JEDEC: 4nCK */ + u8 tfaw = ns_to_t(50); /* JEDEC: 30 ns w/ 1K pages */ + u8 trrd = max(ns_to_t(6), 4); /* JEDEC: max(6 ns, 4nCK) */ + u8 trcd = ns_to_t(15); /* JEDEC: 13.5 ns */ + u8 trc = ns_to_t(53); /* JEDEC: 49.5 ns */ + u8 txp = max(ns_to_t(6), 3); /* JEDEC: max(6 ns, 3nCK) */ + u8 trtp = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */ + u8 trp = ns_to_t(15); /* JEDEC: >= 13.75 ns */ + u8 tras = ns_to_t(38); /* JEDEC >= 36 ns, <= 9*trefi */ + u16 trefi = ns_to_t(7800) / 32; /* JEDEC: 7.8us@Tcase <= 85C */ + u16 trfc = ns_to_t(350); /* JEDEC: 160 ns for 2Gb */ + u16 txsr = 4; /* ? */ + + u8 tmrw = 0; /* ? */ + u8 tmrd = 4; /* JEDEC: 4nCK */ + u8 tmod = max(ns_to_t(15), 12); /* JEDEC: max(15 ns, 12nCK) */ + u8 tcke = max(ns_to_t(6), 3); /* JEDEC: max(5.625 ns, 3nCK) */ + u8 tcksrx = max(ns_to_t(10), 4); /* JEDEC: max(10 ns, 5nCK) */ + u8 tcksre = max(ns_to_t(10), 4); /* JEDEC: max(10 ns, 5nCK) */ + u8 tckesr = tcke + 1; /* JEDEC: tCKE(min) + 1nCK */ + u8 trasmax = (para->clk / 2) / 15; /* JEDEC: tREFI * 9 */ + u8 txs = ns_to_t(360) / 32; /* JEDEC: max(5nCK,tRFC+10ns) */ + u8 txsdll = 16; /* JEDEC: 512 nCK */ + u8 txsabort = 4; /* ? */ + u8 txsfast = 4; /* ? */ + u8 tcl = 7; /* JEDEC: CL / 2 => 6 */ + u8 tcwl = 5; /* JEDEC: 8 */ + u8 t_rdata_en = 9; /* ? */ + + u8 twtp = 14; /* (WL + BL / 2 + tWR) / 2 */ + u8 twr2rd = trtp + 7; /* (WL + BL / 2 + tWTR) / 2 */ + u8 trd2wr = 5; /* (RL + BL / 2 + 2 - WL) / 2 */ + + /* set DRAM timing */ + writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras, + &mctl_ctl->dramtmg[0]); + writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]); + writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd, + &mctl_ctl->dramtmg[2]); + writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]); + writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp, + &mctl_ctl->dramtmg[4]); + writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke, + &mctl_ctl->dramtmg[5]); + /* Value suggested by ZynqMP manual and used by libdram */ + writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]); + writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs, + &mctl_ctl->dramtmg[8]); + writel(0x00020208, &mctl_ctl->dramtmg[9]); + writel(0xE0C05, &mctl_ctl->dramtmg[10]); + writel(0x440C021C, &mctl_ctl->dramtmg[11]); + writel(8, &mctl_ctl->dramtmg[12]); + writel(0xA100002, &mctl_ctl->dramtmg[13]); + writel(txsr, &mctl_ctl->dramtmg[14]); + + clrbits_le32(&mctl_ctl->init[0], 3 << 30); + writel(0x420000, &mctl_ctl->init[1]); + writel(5, &mctl_ctl->init[2]); + writel(0x1f140004, &mctl_ctl->init[3]); + writel(0x00200000, &mctl_ctl->init[4]); + + writel(0, &mctl_ctl->dfimisc); + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + + /* Configure DFI timing */ + writel((tcl - 2) | 0x2000000 | (t_rdata_en << 16) | 0x808000, + &mctl_ctl->dfitmg0); + writel(0x100202, &mctl_ctl->dfitmg1); + + /* set refresh timing */ + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c new file mode 100644 index 000000000..611eaa302 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h6_ddr3_1333.c @@ -0,0 +1,144 @@ +/* + * sun50i H6 DDR3-1333 timings, as programmed by Allwinner's boot0 + * for some TV boxes with the H6 and DDR3 memory. + * + * The chips are probably able to be driven by a faster clock, but boot0 + * uses a more conservative timing (as usual). + * + * (C) Copyright 2018,2019 Arm Ltd. + * based on previous work by: + * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io> + * + * References used: + * - JEDEC DDR3 SDRAM standard: JESD79-3F.pdf + * - Samsung K4B2G0446D datasheet + * - ZynqMP UG1087 register DDRC/PHY documentation + * + * Many thanks to Jernej Skrabec for contributing some fixes! + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +/* + * Only the first four are used for DDR3(?) + * MR0: BL8, seq. read burst, no test, fast exit (DLL on), no DLL reset, + * CAS latency (CL): 11, write recovery (WR): 12 + * MR1: DLL enabled, output strength RZQ/6, Rtt_norm RZQ/2, + * write levelling disabled, TDQS disabled, output buffer enabled + * MR2: manual full array self refresh, dynamic ODT off, + * CAS write latency (CWL): 8 + */ +static u32 mr_ddr3[7] = { + 0x00001c70, 0x00000040, 0x00000018, 0x00000000, + 0x00000000, 0x00000400, 0x00000848, +}; + +/* TODO: flexible timing */ +void mctl_set_timing_params(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i; + + u8 tccd = 2; /* JEDEC: 4nCK */ + u8 tfaw = ns_to_t(50); /* JEDEC: 30 ns w/ 1K pages */ + u8 trrd = max(ns_to_t(6), 4); /* JEDEC: max(6 ns, 4nCK) */ + u8 trcd = ns_to_t(15); /* JEDEC: 13.5 ns */ + u8 trc = ns_to_t(53); /* JEDEC: 49.5 ns */ + u8 txp = max(ns_to_t(6), 3); /* JEDEC: max(6 ns, 3nCK) */ + u8 twtr = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */ + u8 trtp = max(ns_to_t(8), 2); /* JEDEC: max(7.5 ns, 4nCK) */ + u8 twr = ns_to_t(15); /* JEDEC: 15 ns */ + u8 trp = ns_to_t(15); /* JEDEC: >= 13.75 ns */ + u8 tras = ns_to_t(38); /* JEDEC >= 36 ns, <= 9*trefi */ + u8 twtr_sa = 2; /* ? */ + u8 tcksrea = 4; /* ? */ + u16 trefi = ns_to_t(7800) / 32; /* JEDEC: 7.8us@Tcase <= 85C */ + u16 trfc = ns_to_t(350); /* JEDEC: 160 ns for 2Gb */ + u16 txsr = 4; /* ? */ + + u8 tmrw = 0; /* ? */ + u8 tmrd = 4; /* JEDEC: 4nCK */ + u8 tmod = max(ns_to_t(15), 12); /* JEDEC: max(15 ns, 12nCK) */ + u8 tcke = max(ns_to_t(6), 3); /* JEDEC: max(5.625 ns, 3nCK) */ + u8 tcksrx = max(ns_to_t(10), 5); /* JEDEC: max(10 ns, 5nCK) */ + u8 tcksre = max(ns_to_t(10), 5); /* JEDEC: max(10 ns, 5nCK) */ + u8 tckesr = tcke + 1; /* JEDEC: tCKE(min) + 1nCK */ + u8 trasmax = 24; /* JEDEC: tREFI * 9 */ + u8 txs = ns_to_t(360) / 32; /* JEDEC: max(5nCK,tRFC+10ns) */ + u8 txsdll = 4; /* JEDEC: 512 nCK */ + u8 txsabort = 4; /* ? */ + u8 txsfast = 4; /* ? */ + u8 tcl = 6; /* JEDEC: CL / 2 => 6 */ + u8 tcwl = 4; /* JEDEC: 8 */ + u8 t_rdata_en = 7; /* ? */ + + u32 tdinit0 = (500 * CONFIG_DRAM_CLK) + 1; /* 500us */ + u32 tdinit1 = (360 * CONFIG_DRAM_CLK) / 1000 + 1; + u32 tdinit2 = (200 * CONFIG_DRAM_CLK) + 1; + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 2 + twr; /* (WL + BL / 2 + tWR) / 2 */ + u8 twr2rd = tcwl + 2 + twtr; /* (WL + BL / 2 + tWTR) / 2 */ + u8 trd2wr = 5; /* (RL + BL / 2 + 2 - WL) / 2 */ + + if (tcl + 1 >= trtp + trp) + trtp = tcl + 2 - trp; + + /* set mode registers */ + for (i = 0; i < ARRAY_SIZE(mr_ddr3); i++) + writel(mr_ddr3[i], &mctl_phy->mr[i]); + + /* set DRAM timing */ + writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras, + &mctl_ctl->dramtmg[0]); + writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]); + writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd, + &mctl_ctl->dramtmg[2]); + writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]); + writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp, + &mctl_ctl->dramtmg[4]); + writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke, + &mctl_ctl->dramtmg[5]); + /* Value suggested by ZynqMP manual and used by libdram */ + writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]); + writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs, + &mctl_ctl->dramtmg[8]); + writel(txsr, &mctl_ctl->dramtmg[14]); + + clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30)); + writel(0, &mctl_ctl->dfimisc); + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + + /* + * Set timing registers of the PHY. + * Note: the PHY is clocked 2x from the DRAM frequency. + */ + writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1), + &mctl_phy->dtpr[0]); + writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]); + writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]); + writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8), + &mctl_phy->dtpr[3]); + writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]); + writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]); + writel(0x0505, &mctl_phy->dtpr[6]); + + /* Configure DFI timing */ + writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000, + &mctl_ctl->dfitmg0); + writel(0x040201, &mctl_ctl->dfitmg1); + + /* Configure PHY timing. Zynq uses different registers. */ + writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]); + writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]); + + /* set refresh timing */ + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h6_lpddr3.c b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h6_lpddr3.c new file mode 100644 index 000000000..100086011 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/h6_lpddr3.c @@ -0,0 +1,132 @@ +/* + * sun50i H6 LPDDR3 timings + * + * (C) Copyright 2017 Icenowy Zheng <icenowy@aosc.io> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +static u32 mr_lpddr3[12] = { + 0x00000000, 0x00000043, 0x0000001a, 0x00000001, + 0x00000000, 0x00000000, 0x00000048, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000003, +}; + +/* TODO: flexible timing */ +void mctl_set_timing_params(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + struct sunxi_mctl_phy_reg * const mctl_phy = + (struct sunxi_mctl_phy_reg *)SUNXI_DRAM_PHY0_BASE; + int i; + + u8 tccd = 2; + u8 tfaw = max(ns_to_t(50), 4); + u8 trrd = max(ns_to_t(10), 2); + u8 trcd = max(ns_to_t(24), 2); + u8 trc = ns_to_t(70); + u8 txp = max(ns_to_t(8), 2); + u8 twtr = max(ns_to_t(8), 2); + u8 trtp = max(ns_to_t(8), 2); + u8 twr = max(ns_to_t(15), 2); + u8 trp = ns_to_t(18); + u8 tras = ns_to_t(42); + u8 twtr_sa = ns_to_t(5); + u8 tcksrea = ns_to_t(11); + u16 trefi = ns_to_t(3900) / 32; + u16 trfc = ns_to_t(210); + u16 txsr = ns_to_t(220); + + if (CONFIG_DRAM_CLK % 400 == 0) { + /* Round up these parameters */ + twtr_sa++; + tcksrea++; + } + + u8 tmrw = 5; + u8 tmrd = 5; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 5; + u8 trasmax = CONFIG_DRAM_CLK / 60; + u8 txs = 4; + u8 txsdll = 4; + u8 txsabort = 4; + u8 txsfast = 4; + + u8 tcl = 5; /* CL 10 */ + u8 tcwl = 3; /* CWL 6 */ + u8 t_rdata_en = twtr_sa + 8; + + u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */ + u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 4 + twr + 1; + /* + * The code below for twr2rd and trd2wr follows the IP core's + * document from ZynqMP and i.MX7. The BSP has both number + * substracted by 2. + */ + u8 twr2rd = tcwl + 4 + 1 + twtr; + u8 trd2wr = tcl + 4 + (tcksrea >> 1) - tcwl + 1; + + /* set mode registers */ + for (i = 0; i < ARRAY_SIZE(mr_lpddr3); i++) + writel(mr_lpddr3[i], &mctl_phy->mr[i]); + + /* set DRAM timing */ + writel((twtp << 24) | (tfaw << 16) | (trasmax << 8) | tras, + &mctl_ctl->dramtmg[0]); + writel((txp << 16) | (trtp << 8) | trc, &mctl_ctl->dramtmg[1]); + writel((tcwl << 24) | (tcl << 16) | (trd2wr << 8) | twr2rd, + &mctl_ctl->dramtmg[2]); + writel((tmrw << 20) | (tmrd << 12) | tmod, &mctl_ctl->dramtmg[3]); + writel((trcd << 24) | (tccd << 16) | (trrd << 8) | trp, + &mctl_ctl->dramtmg[4]); + writel((tcksrx << 24) | (tcksre << 16) | (tckesr << 8) | tcke, + &mctl_ctl->dramtmg[5]); + /* Value suggested by ZynqMP manual and used by libdram */ + writel((txp + 2) | 0x02020000, &mctl_ctl->dramtmg[6]); + writel((txsfast << 24) | (txsabort << 16) | (txsdll << 8) | txs, + &mctl_ctl->dramtmg[8]); + writel(txsr, &mctl_ctl->dramtmg[14]); + + clrsetbits_le32(&mctl_ctl->init[0], (3 << 30), (1 << 30)); + writel(0, &mctl_ctl->dfimisc); + clrsetbits_le32(&mctl_ctl->rankctl, 0xff0, 0x660); + + /* + * Set timing registers of the PHY. + * Note: the PHY is clocked 2x from the DRAM frequency. + */ + writel((trrd << 25) | (tras << 17) | (trp << 9) | (trtp << 1), + &mctl_phy->dtpr[0]); + writel((tfaw << 17) | 0x28000400 | (tmrd << 1), &mctl_phy->dtpr[1]); + writel(((txs << 6) - 1) | (tcke << 17), &mctl_phy->dtpr[2]); + writel(((txsdll << 22) - (0x1 << 16)) | twtr_sa | (tcksrea << 8), + &mctl_phy->dtpr[3]); + writel((txp << 1) | (trfc << 17) | 0x800, &mctl_phy->dtpr[4]); + writel((trc << 17) | (trcd << 9) | (twtr << 1), &mctl_phy->dtpr[5]); + writel(0x0505, &mctl_phy->dtpr[6]); + + /* Configure DFI timing */ + writel(tcl | 0x2000200 | (t_rdata_en << 16) | 0x808000, + &mctl_ctl->dfitmg0); + writel(0x040201, &mctl_ctl->dfitmg1); + + /* Configure PHY timing */ + writel(tdinit0 | (tdinit1 << 20), &mctl_phy->ptr[3]); + writel(tdinit2 | (tdinit3 << 18), &mctl_phy->ptr[4]); + + /* set refresh timing */ + writel((trefi << 16) | trfc, &mctl_ctl->rfshtmg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/dram_timings/lpddr3_stock.c b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/lpddr3_stock.c new file mode 100644 index 000000000..bd57e2f6a --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/dram_timings/lpddr3_stock.c @@ -0,0 +1,83 @@ +#include <common.h> +#include <asm/arch/dram.h> +#include <asm/arch/cpu.h> + +void mctl_set_timing_params(uint16_t socid, struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u8 tccd = 2; + u8 tfaw = max(ns_to_t(50), 4); + u8 trrd = max(ns_to_t(10), 2); + u8 trcd = max(ns_to_t(24), 2); + u8 trc = ns_to_t(70); + u8 txp = max(ns_to_t(8), 2); + u8 twtr = max(ns_to_t(8), 2); + u8 trtp = max(ns_to_t(8), 2); + u8 twr = max(ns_to_t(15), 3); + u8 trp = max(ns_to_t(27), 2); + u8 tras = ns_to_t(42); + u16 trefi = ns_to_t(3900) / 32; + u16 trfc = ns_to_t(210); + + u8 tmrw = 5; + u8 tmrd = 5; + u8 tmod = 12; + u8 tcke = 3; + u8 tcksrx = 5; + u8 tcksre = 5; + u8 tckesr = 5; + u8 trasmax = 24; + + u8 tcl = 6; /* CL 12 */ + u8 tcwl = 3; /* CWL 6 */ + u8 t_rdata_en = 5; + u8 wr_latency = 2; + + u32 tdinit0 = (200 * CONFIG_DRAM_CLK) + 1; /* 200us */ + u32 tdinit1 = (100 * CONFIG_DRAM_CLK) / 1000 + 1; /* 100ns */ + u32 tdinit2 = (11 * CONFIG_DRAM_CLK) + 1; /* 11us */ + u32 tdinit3 = (1 * CONFIG_DRAM_CLK) + 1; /* 1us */ + + u8 twtp = tcwl + 4 + twr + 1; + u8 twr2rd = tcwl + 4 + 1 + twtr; + u8 trd2wr = tcl + 4 + 5 - tcwl + 1; + + /* set mode register */ + writel(0xc3, &mctl_ctl->mr[1]); /* nWR=8, BL8 */ + writel(0xa, &mctl_ctl->mr[2]); /* RL=12, WL=6 */ + writel(0x2, &mctl_ctl->mr[3]); /* 40 0hms PD/PU */ + + /* set DRAM timing */ + writel(DRAMTMG0_TWTP(twtp) | DRAMTMG0_TFAW(tfaw) | + DRAMTMG0_TRAS_MAX(trasmax) | DRAMTMG0_TRAS(tras), + &mctl_ctl->dramtmg[0]); + writel(DRAMTMG1_TXP(txp) | DRAMTMG1_TRTP(trtp) | DRAMTMG1_TRC(trc), + &mctl_ctl->dramtmg[1]); + writel(DRAMTMG2_TCWL(tcwl) | DRAMTMG2_TCL(tcl) | + DRAMTMG2_TRD2WR(trd2wr) | DRAMTMG2_TWR2RD(twr2rd), + &mctl_ctl->dramtmg[2]); + writel(DRAMTMG3_TMRW(tmrw) | DRAMTMG3_TMRD(tmrd) | DRAMTMG3_TMOD(tmod), + &mctl_ctl->dramtmg[3]); + writel(DRAMTMG4_TRCD(trcd) | DRAMTMG4_TCCD(tccd) | DRAMTMG4_TRRD(trrd) | + DRAMTMG4_TRP(trp), &mctl_ctl->dramtmg[4]); + writel(DRAMTMG5_TCKSRX(tcksrx) | DRAMTMG5_TCKSRE(tcksre) | + DRAMTMG5_TCKESR(tckesr) | DRAMTMG5_TCKE(tcke), + &mctl_ctl->dramtmg[5]); + + /* set two rank timing */ + clrsetbits_le32(&mctl_ctl->dramtmg[8], (0xff << 8) | (0xff << 0), + (0x66 << 8) | (0x10 << 0)); + + /* set PHY interface timing, write latency and read latency configure */ + writel((0x2 << 24) | (t_rdata_en << 16) | (0x1 << 8) | + (wr_latency << 0), &mctl_ctl->pitmg[0]); + + /* set PHY timing, PTR0-2 use default */ + writel(PTR3_TDINIT0(tdinit0) | PTR3_TDINIT1(tdinit1), &mctl_ctl->ptr[3]); + writel(PTR4_TDINIT2(tdinit2) | PTR4_TDINIT3(tdinit3), &mctl_ctl->ptr[4]); + + /* set refresh timing */ + writel(RFSHTMG_TREFI(trefi) | RFSHTMG_TRFC(trfc), &mctl_ctl->rfshtmg); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/gtbus_sun9i.c b/roms/u-boot/arch/arm/mach-sunxi/gtbus_sun9i.c new file mode 100644 index 000000000..cf011c4cf --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/gtbus_sun9i.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * GTBUS initialisation for sun9i + * + * (C) Copyright 2016 Theobroma Systems Design und Consulting GmbH + * Philipp Tomsich <philipp.tomsich@theobroma-systems.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/gtbus_sun9i.h> +#include <asm/arch/sys_proto.h> + +#ifdef CONFIG_SPL_BUILD + +void gtbus_init(void) +{ + struct sunxi_gtbus_reg * const gtbus = + (struct sunxi_gtbus_reg *)SUNXI_GTBUS_BASE; + + /* + * We use the same setting that Allwinner used in Boot0 for now. + * It may be advantageous to adjust these for various workloads + * (e.g. headless use cases that focus on IO throughput). + */ + writel((GT_PRIO_HIGH << GT_PORT_FE0) | + (GT_PRIO_HIGH << GT_PORT_BE1) | + (GT_PRIO_HIGH << GT_PORT_BE2) | + (GT_PRIO_HIGH << GT_PORT_IEP0) | + (GT_PRIO_HIGH << GT_PORT_FE1) | + (GT_PRIO_HIGH << GT_PORT_BE0) | + (GT_PRIO_HIGH << GT_PORT_FE2) | + (GT_PRIO_HIGH << GT_PORT_IEP1), + >bus->mst_read_prio_cfg[0]); + + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_FE0]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_FE0]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_BE1]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_BE2]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_IEP0]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_FE1]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_BE0]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_FE2]); + writel(GP_MST_CFG_DEFAULT, >bus->mst_cfg[GT_PORT_IEP1]); +} + +#endif diff --git a/roms/u-boot/arch/arm/mach-sunxi/p2wi.c b/roms/u-boot/arch/arm/mach-sunxi/p2wi.c new file mode 100644 index 000000000..7c5c12254 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/p2wi.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sunxi A31 Power Management Unit + * + * (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl> + * http://linux-sunxi.org + * + * Based on sun6i sources and earlier U-Boot Allwinner A10 SPL work + * + * (C) Copyright 2006-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing <bergxing@allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + */ + +#include <common.h> +#include <errno.h> +#include <time.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/p2wi.h> +#include <asm/arch/prcm.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> + +void p2wi_init(void) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; + + /* Enable p2wi and PIO clk, and de-assert their resets */ + prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI); + + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA); + + /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */ + writel(P2WI_CTRL_RESET, &p2wi->ctrl); + sdelay(0x100); + writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8), + &p2wi->cc); +} + +int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; + unsigned long tmo = timer_get_us() + 1000000; + + writel(P2WI_PM_DEV_ADDR(slave_addr) | + P2WI_PM_CTRL_ADDR(ctrl_reg) | + P2WI_PM_INIT_DATA(init_data) | + P2WI_PM_INIT_SEND, + &p2wi->pm); + + while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) { + if (timer_get_us() > tmo) + return -ETIME; + } + + return 0; +} + +static int p2wi_await_trans(void) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; + unsigned long tmo = timer_get_us() + 1000000; + int ret; + u8 reg; + + while (1) { + reg = readl(&p2wi->status); + if (reg & P2WI_STAT_TRANS_ERR) { + ret = -EIO; + break; + } + if (reg & P2WI_STAT_TRANS_DONE) { + ret = 0; + break; + } + if (timer_get_us() > tmo) { + ret = -ETIME; + break; + } + } + writel(reg, &p2wi->status); /* Clear status bits */ + return ret; +} + +int p2wi_read(const u8 addr, u8 *data) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; + int ret; + + writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); + writel(P2WI_DATA_NUM_BYTES(1) | + P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes); + writel(P2WI_STAT_TRANS_DONE, &p2wi->status); + writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl); + + ret = p2wi_await_trans(); + + *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK; + return ret; +} + +int p2wi_write(const u8 addr, u8 data) +{ + struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE; + + writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); + writel(P2WI_DATA_BYTE_1(data), &p2wi->data0); + writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes); + writel(P2WI_STAT_TRANS_DONE, &p2wi->status); + writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl); + + return p2wi_await_trans(); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/pinmux.c b/roms/u-boot/arch/arm/mach-sunxi/pinmux.c new file mode 100644 index 000000000..642483f06 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/pinmux.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/gpio.h> + +void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val) +{ + u32 index = GPIO_CFG_INDEX(bank_offset); + u32 offset = GPIO_CFG_OFFSET(bank_offset); + + clrsetbits_le32(&pio->cfg[0] + index, 0xf << offset, val << offset); +} + +void sunxi_gpio_set_cfgpin(u32 pin, u32 val) +{ + u32 bank = GPIO_BANK(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + sunxi_gpio_set_cfgbank(pio, pin, val); +} + +int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset) +{ + u32 index = GPIO_CFG_INDEX(bank_offset); + u32 offset = GPIO_CFG_OFFSET(bank_offset); + u32 cfg; + + cfg = readl(&pio->cfg[0] + index); + cfg >>= offset; + + return cfg & 0xf; +} + +int sunxi_gpio_get_cfgpin(u32 pin) +{ + u32 bank = GPIO_BANK(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + return sunxi_gpio_get_cfgbank(pio, pin); +} + +int sunxi_gpio_set_drv(u32 pin, u32 val) +{ + u32 bank = GPIO_BANK(pin); + u32 index = GPIO_DRV_INDEX(pin); + u32 offset = GPIO_DRV_OFFSET(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + clrsetbits_le32(&pio->drv[0] + index, 0x3 << offset, val << offset); + + return 0; +} + +int sunxi_gpio_set_pull(u32 pin, u32 val) +{ + u32 bank = GPIO_BANK(pin); + u32 index = GPIO_PULL_INDEX(pin); + u32 offset = GPIO_PULL_OFFSET(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + clrsetbits_le32(&pio->pull[0] + index, 0x3 << offset, val << offset); + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/pmic_bus.c b/roms/u-boot/arch/arm/mach-sunxi/pmic_bus.c new file mode 100644 index 000000000..0394ce856 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/pmic_bus.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com> + * + * Sunxi PMIC bus access helpers + * + * The axp152 & axp209 use an i2c bus, the axp221 uses the p2wi bus and the + * axp223 uses the rsb bus, these functions abstract this. + */ + +#include <common.h> +#include <asm/arch/p2wi.h> +#include <asm/arch/rsb.h> +#include <i2c.h> +#include <asm/arch/pmic_bus.h> + +#define AXP152_I2C_ADDR 0x30 + +#define AXP209_I2C_ADDR 0x34 + +#define AXP305_I2C_ADDR 0x36 + +#define AXP221_CHIP_ADDR 0x68 +#define AXP221_CTRL_ADDR 0x3e +#define AXP221_INIT_DATA 0x3e + +/* AXP818 device and runtime addresses are same as AXP223 */ +#define AXP223_DEVICE_ADDR 0x3a3 +#define AXP223_RUNTIME_ADDR 0x2d + +int pmic_bus_init(void) +{ + /* This cannot be 0 because it is used in SPL before BSS is ready */ + static int needs_init = 1; + __maybe_unused int ret; + + if (!needs_init) + return 0; + +#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER +# ifdef CONFIG_MACH_SUN6I + p2wi_init(); + ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR, + AXP221_INIT_DATA); +# elif defined CONFIG_MACH_SUN8I_R40 + /* Nothing. R40 uses the AXP221s in I2C mode */ + ret = 0; +# else + ret = rsb_init(); + if (ret) + return ret; + + ret = rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR); +# endif + if (ret) + return ret; +#endif + + needs_init = 0; + return 0; +} + +int pmic_bus_read(u8 reg, u8 *data) +{ +#ifdef CONFIG_AXP152_POWER + return i2c_read(AXP152_I2C_ADDR, reg, 1, data, 1); +#elif defined CONFIG_AXP209_POWER + return i2c_read(AXP209_I2C_ADDR, reg, 1, data, 1); +#elif defined CONFIG_AXP305_POWER + return i2c_read(AXP305_I2C_ADDR, reg, 1, data, 1); +#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER +# ifdef CONFIG_MACH_SUN6I + return p2wi_read(reg, data); +# elif defined CONFIG_MACH_SUN8I_R40 + return i2c_read(AXP209_I2C_ADDR, reg, 1, data, 1); +# else + return rsb_read(AXP223_RUNTIME_ADDR, reg, data); +# endif +#endif +} + +int pmic_bus_write(u8 reg, u8 data) +{ +#ifdef CONFIG_AXP152_POWER + return i2c_write(AXP152_I2C_ADDR, reg, 1, &data, 1); +#elif defined CONFIG_AXP209_POWER + return i2c_write(AXP209_I2C_ADDR, reg, 1, &data, 1); +#elif defined CONFIG_AXP305_POWER + return i2c_write(AXP305_I2C_ADDR, reg, 1, &data, 1); +#elif defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER +# ifdef CONFIG_MACH_SUN6I + return p2wi_write(reg, data); +# elif defined CONFIG_MACH_SUN8I_R40 + return i2c_write(AXP209_I2C_ADDR, reg, 1, &data, 1); +# else + return rsb_write(AXP223_RUNTIME_ADDR, reg, data); +# endif +#endif +} + +int pmic_bus_setbits(u8 reg, u8 bits) +{ + int ret; + u8 val; + + ret = pmic_bus_read(reg, &val); + if (ret) + return ret; + + if ((val & bits) == bits) + return 0; + + val |= bits; + return pmic_bus_write(reg, val); +} + +int pmic_bus_clrbits(u8 reg, u8 bits) +{ + int ret; + u8 val; + + ret = pmic_bus_read(reg, &val); + if (ret) + return ret; + + if (!(val & bits)) + return 0; + + val &= ~bits; + return pmic_bus_write(reg, val); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/prcm.c b/roms/u-boot/arch/arm/mach-sunxi/prcm.c new file mode 100644 index 000000000..71a2e4491 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/prcm.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sunxi A31 Power Management Unit + * + * (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl> + * http://linux-sunxi.org + * + * Based on sun6i sources and earlier U-Boot Allwinner A10 SPL work + * + * (C) Copyright 2006-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Berg Xing <bergxing@allwinnertech.com> + * Tom Cubie <tangliang@allwinnertech.com> + */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/prcm.h> +#include <asm/arch/sys_proto.h> + +/* APB0 clock gate and reset bit offsets are the same. */ +void prcm_apb0_enable(u32 flags) +{ + struct sunxi_prcm_reg *prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + /* open the clock for module */ + setbits_le32(&prcm->apb0_gate, flags); + + /* deassert reset for module */ + setbits_le32(&prcm->apb0_reset, flags); +} + +void prcm_apb0_disable(u32 flags) +{ + struct sunxi_prcm_reg *prcm = + (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE; + + /* assert reset for module */ + clrbits_le32(&prcm->apb0_reset, flags); + + /* close the clock for module */ + clrbits_le32(&prcm->apb0_gate, flags); +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/rmr_switch.S b/roms/u-boot/arch/arm/mach-sunxi/rmr_switch.S new file mode 100644 index 000000000..33e55d496 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/rmr_switch.S @@ -0,0 +1,47 @@ +@ +@ ARMv8 RMR reset sequence on Allwinner SoCs. +@ +@ All 64-bit capable Allwinner SoCs reset in AArch32 (and continue to +@ exectute the Boot ROM in this state), so we need to switch to AArch64 +@ at some point. +@ Section G6.2.133 of the ARMv8 ARM describes the Reset Management Register +@ (RMR), which triggers a warm-reset of a core and can request to switch +@ into a different execution state (AArch32 or AArch64). +@ The address at which execution starts after the reset is held in the +@ RVBAR system register, which is architecturally read-only. +@ Allwinner provides a writable alias of this register in MMIO space, so +@ we can easily set the start address of AArch64 code. +@ This code below switches to AArch64 and starts execution at the specified +@ start address. It needs to be assembled by an ARM(32) assembler and +@ the machine code must be inserted as verbatim .word statements into the +@ beginning of the AArch64 U-Boot code. +@ To get the encoded bytes, use: +@ ${CROSS_COMPILE}gcc -c -o rmr_switch.o rmr_switch.S +@ ${CROSS_COMPILE}objdump -d rmr_switch.o +@ +@ The resulting words should be inserted into the U-Boot file at +@ arch/arm/include/asm/arch-sunxi/boot0.h. +@ +@ This file is not build by the U-Boot build system, but provided only as a +@ reference and to be able to regenerate a (probably fixed) version of this +@ code found in encoded form in boot0.h. + +#include <config.h> + +.text + +#ifndef CONFIG_SUN50I_GEN_H6 + ldr r1, =0x017000a0 @ MMIO mapped RVBAR[0] register +#else + ldr r1, =0x09010040 @ MMIO mapped RVBAR[0] register +#endif + ldr r0, =0x57aA7add @ start address, to be replaced + str r0, [r1] + dsb sy + isb sy + mrc 15, 0, r0, cr12, cr0, 2 @ read RMR register + orr r0, r0, #3 @ request reset in AArch64 + mcr 15, 0, r0, cr12, cr0, 2 @ write RMR register + isb sy +1: wfi + b 1b diff --git a/roms/u-boot/arch/arm/mach-sunxi/rsb.c b/roms/u-boot/arch/arm/mach-sunxi/rsb.c new file mode 100644 index 000000000..01bb09b74 --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/rsb.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * Based on allwinner u-boot sources rsb code which is: + * (C) Copyright 2007-2013 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * lixiang <lixiang@allwinnertech.com> + */ + +#include <common.h> +#include <errno.h> +#include <time.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/prcm.h> +#include <asm/arch/rsb.h> + +static int rsb_set_device_mode(void); + +static void rsb_cfg_io(void) +{ +#ifdef CONFIG_MACH_SUN8I + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB); + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB); + sunxi_gpio_set_pull(SUNXI_GPL(0), 1); + sunxi_gpio_set_pull(SUNXI_GPL(1), 1); + sunxi_gpio_set_drv(SUNXI_GPL(0), 2); + sunxi_gpio_set_drv(SUNXI_GPL(1), 2); +#elif defined CONFIG_MACH_SUN9I + sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB); + sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB); + sunxi_gpio_set_pull(SUNXI_GPN(0), 1); + sunxi_gpio_set_pull(SUNXI_GPN(1), 1); + sunxi_gpio_set_drv(SUNXI_GPN(0), 2); + sunxi_gpio_set_drv(SUNXI_GPN(1), 2); +#else +#error unsupported MACH_SUNXI +#endif +} + +static void rsb_set_clk(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + u32 div = 0; + u32 cd_odly = 0; + + /* Source is Hosc24M, set RSB clk to 3Mhz */ + div = 24000000 / 3000000 / 2 - 1; + cd_odly = div >> 1; + if (!cd_odly) + cd_odly = 1; + + writel((cd_odly << 8) | div, &rsb->ccr); +} + +int rsb_init(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + /* Enable RSB and PIO clk, and de-assert their resets */ + prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB); + + /* Setup external pins */ + rsb_cfg_io(); + + writel(RSB_CTRL_SOFT_RST, &rsb->ctrl); + rsb_set_clk(); + + return rsb_set_device_mode(); +} + +static int rsb_await_trans(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + unsigned long tmo = timer_get_us() + 1000000; + u32 stat; + int ret; + + while (1) { + stat = readl(&rsb->stat); + if (stat & RSB_STAT_LBSY_INT) { + ret = -EBUSY; + break; + } + if (stat & RSB_STAT_TERR_INT) { + ret = -EIO; + break; + } + if (stat & RSB_STAT_TOVER_INT) { + ret = 0; + break; + } + if (timer_get_us() > tmo) { + ret = -ETIME; + break; + } + } + writel(stat, &rsb->stat); /* Clear status bits */ + + return ret; +} + +static int rsb_set_device_mode(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + unsigned long tmo = timer_get_us() + 1000000; + + writel(RSB_DMCR_DEVICE_MODE_START | RSB_DMCR_DEVICE_MODE_DATA, + &rsb->dmcr); + + while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) { + if (timer_get_us() > tmo) + return -ETIME; + } + + return rsb_await_trans(); +} + +static int rsb_do_trans(void) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS); + return rsb_await_trans(); +} + +int rsb_set_device_address(u16 device_addr, u16 runtime_addr) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) | + RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr); + writel(RSB_CMD_SET_RTSADDR, &rsb->cmd); + + return rsb_do_trans(); +} + +int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); + writel(reg_addr, &rsb->addr); + writel(data, &rsb->data); + writel(RSB_CMD_BYTE_WRITE, &rsb->cmd); + + return rsb_do_trans(); +} + +int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data) +{ + struct sunxi_rsb_reg * const rsb = + (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; + int ret; + + writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); + writel(reg_addr, &rsb->addr); + writel(RSB_CMD_BYTE_READ, &rsb->cmd); + + ret = rsb_do_trans(); + if (ret) + return ret; + + *data = readl(&rsb->data) & 0xff; + + return 0; +} diff --git a/roms/u-boot/arch/arm/mach-sunxi/spl_spi_sunxi.c b/roms/u-boot/arch/arm/mach-sunxi/spl_spi_sunxi.c new file mode 100644 index 000000000..15e86cbac --- /dev/null +++ b/roms/u-boot/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com> + */ + +#include <common.h> +#include <image.h> +#include <log.h> +#include <spl.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/libfdt.h> + +#ifdef CONFIG_SPL_OS_BOOT +#error CONFIG_SPL_OS_BOOT is not supported yet +#endif + +/* + * This is a very simple U-Boot image loading implementation, trying to + * replicate what the boot ROM is doing when loading the SPL. Because we + * know the exact pins where the SPI Flash is connected and also know + * that the Read Data Bytes (03h) command is supported, the hardware + * configuration is very simple and we don't need the extra flexibility + * of the SPI framework. Moreover, we rely on the default settings of + * the SPI controler hardware registers and only adjust what needs to + * be changed. This is good for the code size and this implementation + * adds less than 400 bytes to the SPL. + * + * There are two variants of the SPI controller in Allwinner SoCs: + * A10/A13/A20 (sun4i variant) and everything else (sun6i variant). + * Both of them are supported. + * + * The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are + * supported at the moment. + */ + +/*****************************************************************************/ +/* SUN4I variant of the SPI controller */ +/*****************************************************************************/ + +#define SUN4I_SPI0_CCTL 0x1C +#define SUN4I_SPI0_CTL 0x08 +#define SUN4I_SPI0_RX 0x00 +#define SUN4I_SPI0_TX 0x04 +#define SUN4I_SPI0_FIFO_STA 0x28 +#define SUN4I_SPI0_BC 0x20 +#define SUN4I_SPI0_TC 0x24 + +#define SUN4I_CTL_ENABLE BIT(0) +#define SUN4I_CTL_MASTER BIT(1) +#define SUN4I_CTL_TF_RST BIT(8) +#define SUN4I_CTL_RF_RST BIT(9) +#define SUN4I_CTL_XCH BIT(10) + +/*****************************************************************************/ +/* SUN6I variant of the SPI controller */ +/*****************************************************************************/ + +#define SUN6I_SPI0_CCTL 0x24 +#define SUN6I_SPI0_GCR 0x04 +#define SUN6I_SPI0_TCR 0x08 +#define SUN6I_SPI0_FIFO_STA 0x1C +#define SUN6I_SPI0_MBC 0x30 +#define SUN6I_SPI0_MTC 0x34 +#define SUN6I_SPI0_BCC 0x38 +#define SUN6I_SPI0_TXD 0x200 +#define SUN6I_SPI0_RXD 0x300 + +#define SUN6I_CTL_ENABLE BIT(0) +#define SUN6I_CTL_MASTER BIT(1) +#define SUN6I_CTL_SRST BIT(31) +#define SUN6I_TCR_XCH BIT(31) + +/*****************************************************************************/ + +#define CCM_AHB_GATING0 (0x01C20000 + 0x60) +#define CCM_H6_SPI_BGR_REG (0x03001000 + 0x96c) +#ifdef CONFIG_MACH_SUN50I_H6 +#define CCM_SPI0_CLK (0x03001000 + 0x940) +#else +#define CCM_SPI0_CLK (0x01C20000 + 0xA0) +#endif +#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0) + +#define AHB_RESET_SPI0_SHIFT 20 +#define AHB_GATE_OFFSET_SPI0 20 + +#define SPI0_CLK_DIV_BY_2 0x1000 +#define SPI0_CLK_DIV_BY_4 0x1001 + +/*****************************************************************************/ + +/* + * Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting + * from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3. + * The H6 uses PC0, PC2, PC3, PC5. + */ +static void spi0_pinmux_setup(unsigned int pin_function) +{ + /* All chips use PC0 and PC2. */ + sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function); + sunxi_gpio_set_cfgpin(SUNXI_GPC(2), pin_function); + + /* All chips except H6 use PC1, and only H6 uses PC5. */ + if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + sunxi_gpio_set_cfgpin(SUNXI_GPC(1), pin_function); + else + sunxi_gpio_set_cfgpin(SUNXI_GPC(5), pin_function); + + /* Older generations use PC23 for CS, newer ones use PC3. */ + if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I) || + IS_ENABLED(CONFIG_MACH_SUN8I_R40)) + sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function); + else + sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function); +} + +static bool is_sun6i_gen_spi(void) +{ + return IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) || + IS_ENABLED(CONFIG_MACH_SUN50I_H6); +} + +static uintptr_t spi0_base_address(void) +{ + if (IS_ENABLED(CONFIG_MACH_SUN8I_R40)) + return 0x01C05000; + + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + return 0x05010000; + + if (!is_sun6i_gen_spi()) + return 0x01C05000; + + return 0x01C68000; +} + +/* + * Setup 6 MHz from OSC24M (because the BROM is doing the same). + */ +static void spi0_enable_clock(void) +{ + uintptr_t base = spi0_base_address(); + + /* Deassert SPI0 reset on SUN6I */ + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + setbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1); + else if (is_sun6i_gen_spi()) + setbits_le32(SUN6I_BUS_SOFT_RST_REG0, + (1 << AHB_RESET_SPI0_SHIFT)); + + /* Open the SPI0 gate */ + if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); + + /* Divide by 4 */ + writel(SPI0_CLK_DIV_BY_4, base + (is_sun6i_gen_spi() ? + SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL)); + /* 24MHz from OSC24M */ + writel((1 << 31), CCM_SPI0_CLK); + + if (is_sun6i_gen_spi()) { + /* Enable SPI in the master mode and do a soft reset */ + setbits_le32(base + SUN6I_SPI0_GCR, SUN6I_CTL_MASTER | + SUN6I_CTL_ENABLE | SUN6I_CTL_SRST); + /* Wait for completion */ + while (readl(base + SUN6I_SPI0_GCR) & SUN6I_CTL_SRST) + ; + } else { + /* Enable SPI in the master mode and reset FIFO */ + setbits_le32(base + SUN4I_SPI0_CTL, SUN4I_CTL_MASTER | + SUN4I_CTL_ENABLE | + SUN4I_CTL_TF_RST | + SUN4I_CTL_RF_RST); + } +} + +static void spi0_disable_clock(void) +{ + uintptr_t base = spi0_base_address(); + + /* Disable the SPI0 controller */ + if (is_sun6i_gen_spi()) + clrbits_le32(base + SUN6I_SPI0_GCR, SUN6I_CTL_MASTER | + SUN6I_CTL_ENABLE); + else + clrbits_le32(base + SUN4I_SPI0_CTL, SUN4I_CTL_MASTER | + SUN4I_CTL_ENABLE); + + /* Disable the SPI0 clock */ + writel(0, CCM_SPI0_CLK); + + /* Close the SPI0 gate */ + if (!IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0)); + + /* Assert SPI0 reset on SUN6I */ + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + clrbits_le32(CCM_H6_SPI_BGR_REG, (1U << 16) | 0x1); + else if (is_sun6i_gen_spi()) + clrbits_le32(SUN6I_BUS_SOFT_RST_REG0, + (1 << AHB_RESET_SPI0_SHIFT)); +} + +static void spi0_init(void) +{ + unsigned int pin_function = SUNXI_GPC_SPI0; + + if (IS_ENABLED(CONFIG_MACH_SUN50I) || + IS_ENABLED(CONFIG_MACH_SUN50I_H6)) + pin_function = SUN50I_GPC_SPI0; + + spi0_pinmux_setup(pin_function); + spi0_enable_clock(); +} + +static void spi0_deinit(void) +{ + /* New SoCs can disable pins, older could only set them as input */ + unsigned int pin_function = SUNXI_GPIO_INPUT; + + if (is_sun6i_gen_spi()) + pin_function = SUNXI_GPIO_DISABLE; + + spi0_disable_clock(); + spi0_pinmux_setup(pin_function); +} + +/*****************************************************************************/ + +#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */ + +static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize, + ulong spi_ctl_reg, + ulong spi_ctl_xch_bitmask, + ulong spi_fifo_reg, + ulong spi_tx_reg, + ulong spi_rx_reg, + ulong spi_bc_reg, + ulong spi_tc_reg, + ulong spi_bcc_reg) +{ + writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */ + writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */ + if (spi_bcc_reg) + writel(4, spi_bcc_reg); /* SUN6I also needs this */ + + /* Send the Read Data Bytes (03h) command header */ + writeb(0x03, spi_tx_reg); + writeb((u8)(addr >> 16), spi_tx_reg); + writeb((u8)(addr >> 8), spi_tx_reg); + writeb((u8)(addr), spi_tx_reg); + + /* Start the data transfer */ + setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask); + + /* Wait until everything is received in the RX FIFO */ + while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize) + ; + + /* Skip 4 bytes */ + readl(spi_rx_reg); + + /* Read the data */ + while (bufsize-- > 0) + *buf++ = readb(spi_rx_reg); + + /* tSHSL time is up to 100 ns in various SPI flash datasheets */ + udelay(1); +} + +static void spi0_read_data(void *buf, u32 addr, u32 len) +{ + u8 *buf8 = buf; + u32 chunk_len; + uintptr_t base = spi0_base_address(); + + while (len > 0) { + chunk_len = len; + if (chunk_len > SPI_READ_MAX_SIZE) + chunk_len = SPI_READ_MAX_SIZE; + + if (is_sun6i_gen_spi()) { + sunxi_spi0_read_data(buf8, addr, chunk_len, + base + SUN6I_SPI0_TCR, + SUN6I_TCR_XCH, + base + SUN6I_SPI0_FIFO_STA, + base + SUN6I_SPI0_TXD, + base + SUN6I_SPI0_RXD, + base + SUN6I_SPI0_MBC, + base + SUN6I_SPI0_MTC, + base + SUN6I_SPI0_BCC); + } else { + sunxi_spi0_read_data(buf8, addr, chunk_len, + base + SUN4I_SPI0_CTL, + SUN4I_CTL_XCH, + base + SUN4I_SPI0_FIFO_STA, + base + SUN4I_SPI0_TX, + base + SUN4I_SPI0_RX, + base + SUN4I_SPI0_BC, + base + SUN4I_SPI0_TC, + 0); + } + + len -= chunk_len; + buf8 += chunk_len; + addr += chunk_len; + } +} + +static ulong spi_load_read(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + spi0_read_data(buf, sector, count); + + return count; +} + +/*****************************************************************************/ + +static int spl_spi_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int ret = 0; + struct image_header *header; + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); + + spi0_init(); + + spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40); + + if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && + image_get_magic(header) == FDT_MAGIC) { + struct spl_load_info load; + + debug("Found FIT image\n"); + load.dev = NULL; + load.priv = NULL; + load.filename = NULL; + load.bl_len = 1; + load.read = spi_load_read; + ret = spl_load_simple_fit(spl_image, &load, + CONFIG_SYS_SPI_U_BOOT_OFFS, header); + } else { + ret = spl_parse_image_header(spl_image, header); + if (ret) + return ret; + + spi0_read_data((void *)spl_image->load_addr, + CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size); + } + + spi0_deinit(); + + return ret; +} +/* Use priorty 0 to override the default if it happens to be linked in */ +SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image); |