Skip to main content

[TOC]

Driver Modules

Interrupt​

Framework Support​

U-Boot's native code does not have an interrupt framework. RK implemented its own set to support GICv2/v3, enabled by default.

Currently, interrupt scenarios include:

  • Pwrkey: When U-Boot is charging, the CPU enters low-power sleep and needs to be woken up via Pwrkey interrupt;
  • Timer: Timer interrupts are used in U-Boot charging and test cases;
  • Debug: Enable the CONFIG_ROCKCHIP_DEBUGGER debugging feature;

Configuration:

CONFIG_IRQ
CONFIG_GICV2
CONFIG_GICV3

Framework code:

./drivers/irq/irq-gpio-switch.c
./drivers/irq/irq-gpio.c
./drivers/irq/irq-generic.c
./drivers/irq/irq-gic.c
./drivers/irq/virq.c
./include/irq-generic.h
// CPU local interrupt control
void enable_interrupts(void);
int disable_interrupts(void);

// GPIO to interrupt number conversion
int gpio_to_irq(struct gpio_desc *gpio);
int phandle_gpio_to_irq(u32 gpio_phandle, u32 pin);
int hard_gpio_to_irq(unsigned gpio);

// Register/release interrupt callbacks
void irq_install_handler(int irq, interrupt_handler_t *handler, void *data);
void irq_free_handler(int irq);

// Enable/disable interrupt
int irq_handler_enable(int irq);
int irq_handler_disable(int irq);

// Interrupt trigger type
int irq_set_irq_type(int irq, unsigned int type);

Requesting IRQ

  • Peripherals with dedicated hardware interrupt numbers do not require additional conversion, e.g., pwm, timer, etc.
  • GPIO pins do not have dedicated hardware interrupt numbers and require additional conversion.

There are 3 ways to request a GPIO pin interrupt number:

(1) Passing struct gpio_desc

// This method can dynamically parse dts configuration, making it flexible and commonly used.
int gpio_to_irq(struct gpio_desc *gpio);

Example:

battery {
compatible = "battery,rk817";
......
dc_det_gpio = <&gpio2 7 GPIO_ACTIVE_LOW>;
......
};
struct gpio_desc dc_det;
int ret, irq;

ret = gpio_request_by_name_nodev(dev_ofnode(dev), "dc_det_gpio", 0,
&dc_det, GPIOD_IS_IN);
// Return value check omitted for simplicity
if (!ret) {
irq = gpio_to_irq(&dc_det);
irq_install_handler(irq, ...);
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
irq_handler_enable(irq);
}

(2) Passing the gpio phandle and pin

// This method can dynamically parse dts configuration, making it flexible and commonly used.
int phandle_gpio_to_irq(u32 gpio_phandle, u32 pin);

Example (rk817 interrupt pin is GPIO0_A7):

rk817: pmic@20 {
compatible = "rockchip,rk817";
reg = <0x20>;
......
interrupt-parent = <&gpio0>; // "&gpio0": points to the phandle of the gpio0 node;
interrupts = <7 IRQ_TYPE_LEVEL_LOW>; // "7": pin number;
......
};
u32 interrupt[2], phandle;
int irq, ret;

phandle = dev_read_u32_default(dev->parent, "interrupt-parent", -1);
if (phandle < 0) {
printf("failed get 'interrupt-parent', ret=%d\n", phandle);
return phandle;
}

ret = dev_read_u32_array(dev->parent, "interrupts", interrupt, 2);
if (ret) {
printf("failed get 'interrupt', ret=%d\n", ret);
return ret;
}

// Return value check omitted for simplicity
irq = phandle_gpio_to_irq(phandle, interrupt[0]);
irq_install_handler(irq, pwrkey_irq_handler, dev);
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
irq_handler_enable(irq);

(3) Forcing a specific gpio

// This method directly forces a specific gpio. The passed gpio must be declared using special macros, which is not flexible and is not recommended.
int hard_gpio_to_irq(unsigned gpio);

Example (GPIO0_A0 requesting interrupt):

int gpio0_a0, irq;

// Return value check omitted for simplicity
gpio0_a0 = RK_IRQ_GPIO(RK_GPIO0, RK_PA0);
irq = hard_gpio_to_irq(gpio0_a0);
irq_install_handler(irq, ...);
irq_handler_enable(irq);

Clock​

Framework Support​

The clock driver uses the clk-uclass framework and standard interfaces.

Configuration:

CONFIG_CLK

Framework code:

./drivers/clk/clk-uclass.c

Platform driver code:

./drivers/clk/rockchip/...
// Request clock
int clk_get_by_index(struct udevice *dev, int index, struct clk *clk);
int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk);

// Enable/disable clock
int clk_enable(struct clk *clk);
int clk_disable(struct clk *clk);

// Get/set rate
ulong (*get_rate)(struct clk *clk);
ulong (*set_rate)(struct clk *clk, ulong rate);

// Get/set phase
int (*get_phase)(struct clk *clk);
int (*set_phase)(struct clk *clk, int degrees);

Clock Initialization​

There are three types of interfaces related to clock initialization. The cru node information is listed below for reference in the subsequent introduction.

cru: clock-controller@ff2b0000 {
compatible = "rockchip,px30-cru";
......
assigned-clocks =
<&pmucru PLL_GPLL>, <&pmucru PCLK_PMU_PRE>,
<&pmucru SCLK_WIFI_PMU>, <&cru ARMCLK>,
<&cru ACLK_BUS_PRE>, <&cru ACLK_PERI_PRE>,
<&cru HCLK_BUS_PRE>, <&cru HCLK_PERI_PRE>,
<&cru PCLK_BUS_PRE>, <&cru SCLK_GPU>;
assigned-clock-rates =
<1200000000>, <100000000>,
<26000000>, <600000000>,
<200000000>, <200000000>,
<150000000>, <150000000>,
<100000000>, <200000000>;
......
}

Type 1, Platform base clock default initialization: rkclk_init()

Each platform's cru driver probe calls rkclk_init() to complete pll/cpu/bus frequency initialization, with frequencies defined in cru_rkxxx.h. For example RK3399:

#define APLL_HZ (600 * MHz)
#define GPLL_HZ (800 * MHz)
#define CPLL_HZ (384 * MHz)
#define NPLL_HZ (600 * MHz)
#define PPLL_HZ (676 * MHz)
#define PMU_PCLK_HZ ( 48 * MHz)
#define ACLKM_CORE_HZ (300 * MHz)
#define ATCLK_CORE_HZ (300 * MHz)
#define PCLK_DBG_HZ (100 * MHz)
#define PERIHP_ACLK_HZ (150 * MHz)
#define PERIHP_HCLK_HZ ( 75 * MHz)
#define PERIHP_PCLK_HZ (37500 * KHz)
#define PERILP0_ACLK_HZ (300 * MHz)
#define PERILP0_HCLK_HZ (100 * MHz)
#define PERILP0_PCLK_HZ ( 50 * MHz)
#define PERILP1_HCLK_HZ (100 * MHz)
#define PERILP1_PCLK_HZ ( 50 * MHz)
......

Type 2, Platform base clock secondary initialization: clk_set_defaults()

Each platform's cru driver probe may call clk_set_defaults() to parse and configure the frequencies specified by assigned-clocks/assigned-clock-parents/assigned-clock-rates within the cru node (i.e., reconfigure the frequencies), excluding the ARM frequency. It is only configured when set_armclk_rate() is implemented.

In addition to cru, peripherals with such requirements can also actively call clk_set_defaults() in their own probe, e.g., vop, gmac.

Type 3, Individual module clock initialization: clk_set_rate()

Most peripheral modules use clk_set_rate() to configure their own frequencies.

CPU Frequency Scaling​

The implementation flow for CPU frequency scaling at boot:

  • Step 1: Specify the ARM target frequency in the assigned-clocks of the cru node;
  • Step 2: The cru driver probe calls clk_set_defaults() to obtain (but not configure) the ARM target frequency from Step 1;
  • Step 3: Implement set_armclk_rate(), which is a __weak function that configures the ARM target frequency obtained from Step 2;
  • Step 4: Add regulator-init-microvolt = <...> in the ARM regulator node to specify the init voltage to avoid insufficient voltage;

Clock Tree​

The U-Boot framework does not provide clock tree management. Each platform adds soc_clk_dump() for simple clock information printing. For example:

CLK: (sync kernel. arm: enter 1200000 KHz, init 1200000 KHz, kernel 800000 KHz)
apll 800000 KHz
dpll 392000 KHz
cpll 1000000 KHz
gpll 1188000 KHz
npll 24000 KHz
ppll 100000 KHz
hsclk_bus 297000 KHz
msclk_bus 198000 KHz
lsclk_bus 99000 KHz
msclk_peri 198000 KHz
lsclk_peri 99000 KHz

Meaning of the first line:

  • sync kernel: The cru driver configures the bus frequencies specified in the kernel cru node via clk_set_defaults() (excluding ARM frequency); otherwise, it displays sync uboot;
  • enter 1200000 KHz: The ARM frequency when the previous stage Loader enters U-Boot;
  • init 1200000 KHz: The ARM init frequency in U-Boot, i.e., the frequency defined by APLL_HZ;
  • kernel 800000 KHz: set_armclk_rate() is implemented and sets the ARM frequency specified in assigned-clocks of the kernel cru node; otherwise, it displays "kernel 0N/A";

GPIO​

Framework Support​

The GPIO driver uses the gpio-uclass framework and standard interfaces. Users must access gpio through struct gpio_desc.

Configuration:

CONFIG_DM_GPIO
CONFIG_ROCKCHIP_GPIO

Framework code:

./drivers/gpio/gpio-uclass.c

Driver code:

./drivers/gpio/rk_gpio.c
// Request/release GPIO
int gpio_request_by_name(struct udevice *dev, const char *list_name,
int index, struct gpio_desc *desc, int flags);
int gpio_request_by_name_nodev(ofnode node, const char *list_name, int index,
struct gpio_desc *desc, int flags);
int gpio_request_list_by_name(struct udevice *dev, const char *list_name,
struct gpio_desc *desc_list, int max_count, int flags);
int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
struct gpio_desc *desc_list, int max_count,
int flags);
int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc)

// Configure GPIO direction. @flags: GPIOD_IS_OUT (output) and GPIOD_IS_IN (input)
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);

// Get/set GPIO level
int dm_gpio_get_value(const struct gpio_desc *desc)
int dm_gpio_set_value(const struct gpio_desc *desc, int value)

Notes:

The return value of dm_gpio_get_value() represents the active state, not the electrical high or low level. For example:

  • gpios = <&gpio2 RK_PD0 GPIO_ACTIVE_LOW>, returns 1 when low, 0 when high.

  • gpios = <&gpio2 RK_PD0 GPIO_ACTIVE_HIGH>, returns 0 when low, 1 when high.

The value parameter of dm_gpio_set_value() follows the same logic: 1 = active, 0 = inactive.

Pinctrl​

Framework Support​

The pinctrl driver uses the pinctrl-uclass framework and standard interfaces.

Configuration:

CONFIG_PINCTRL_GENERIC
CONFIG_PINCTRL_ROCKCHIP

Framework code:

./drivers/pinctrl/pinctrl-uclass.c

Driver code:

./drivers/pinctrl/pinctrl-rockchip.c
int pinctrl_select_state(struct udevice *dev, const char *statename) // Set state
int pinctrl_get_gpio_mux(struct udevice *dev, int banknum, int index) // Get state

The pinctrl framework automatically sets the "default" state for each driver during probe. Users generally do not need to call the pinctrl interface.

I2C​

Framework Support​

The i2c driver uses the i2c-uclass framework and standard interfaces.

Configuration:

CONFIG_DM_I2C
CONFIG_SYS_I2C_ROCKCHIP

Framework code:

./drivers/i2c/i2c-uclass.c

Driver code:

./drivers/i2c/rk_i2c.c
./drivers/i2c/i2c-gpio.c // GPIO simulated i2c communication, currently not used
// i2c read/write
int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len)
int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len)

// Wrappers for the above interfaces
int dm_i2c_reg_read(struct udevice *dev, uint offset)
int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val);

Display​

Framework Support​

RK U-Boot currently supports the following display interfaces: RGB, LVDS, EDP, MIPI and HDMI. CVBS, DP, etc. will be added in the future. The U-Boot display logo images come from logo.bmp and logo_kernel.bmp in the kernel root directory, which are packaged into resource.img.

Image requirements:

  • 8bit or 24bit BMP format;

  • logo.bmp and logo_kernel.bmp must have the same resolution;

  • For chips based on the vop lite architecture such as rk312x/px30/rk3308, since VOP does not support mirroring and 24bit BMP images are stored mirrored, if the displayed image appears mirrored in the y-direction, please mirror the image in the y-direction on the PC side beforehand.

Configuration:

CONFIG_DM_VIDEO
CONFIG_DISPLAY
CONFIG_DRM_ROCKCHIP
CONFIG_DRM_ROCKCHIP_PANEL
CONFIG_DRM_ROCKCHIP_DW_MIPI_DSI
CONFIG_DRM_ROCKCHIP_DW_HDMI
CONFIG_DRM_ROCKCHIP_LVDS
CONFIG_DRM_ROCKCHIP_RGB
CONFIG_DRM_ROCKCHIP_RK618
CONFIG_ROCKCHIP_DRM_TVE
CONFIG_DRM_ROCKCHIP_ANALOGIX_DP
CONFIG_DRM_ROCKCHIP_INNO_VIDEO_COMBO_PHY
CONFIG_DRM_ROCKCHIP_INNO_VIDEO_PHY

Framework code:

drivers/video/drm/rockchip_display.c
drivers/video/drm/rockchip_display.h

Driver files:

vop:
drivers/video/drm/rockchip_crtc.c
drivers/video/drm/rockchip_crtc.h
drivers/video/drm/rockchip_vop.c
drivers/video/drm/rockchip_vop.h
drivers/video/drm/rockchip_vop_reg.c
drivers/video/drm/rockchip_vop_reg.h

rgb:
drivers/video/drm/rockchip_rgb.c
drivers/video/drm/rockchip_rgb.h

lvds:
drivers/video/drm/rockchip_lvds.c
drivers/video/drm/rockchip_lvds.h

mipi:
drivers/video/drm/rockchip_mipi_dsi.c
drivers/video/drm/rockchip_mipi_dsi.h
drivers/video/drm/rockchip-inno-mipi-dphy.c

edp:
drivers/video/drm/rockchip_analogix_dp.c
drivers/video/drm/rockchip_analogix_dp.h
drivers/video/drm/rockchip_analogix_dp_reg.c
drivers/video/drm/rockchip_analogix_dp_reg.h

hdmi:
drivers/video/drm/dw_hdmi.c
drivers/video/drm/dw_hdmi.h
drivers/video/drm/rockchip_dw_hdmi.c
drivers/video/drm/rockchip_dw_hdmi.h

panel:
drivers/video/drm/rockchip_panel.c
drivers/video/drm/rockchip_panel.h
// Display U-Boot logo and kernel logo:
void rockchip_show_logo(void);

// Display bmp image, currently mainly used for charging image display:
void rockchip_show_bmp(const char *bmp);

// Pass some variables in U-Boot to the kernel via dtb.
// Includes kernel logo size, address, format, crtc output scan timing and overscan configuration.
// BCSH and other related variable configurations will be added in the future.
void rockchip_display_fixup(void *blob);

DTS Configuration​

reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;

drm_logo: drm-logo@00000000 {
compatible = "rockchip,drm-logo";
// Reserved buffer for kernel logo storage. Specific address and size will be modified in U-Boot.
reg = <0x0 0x0 0x0 0x0>;
};
};

&route-edp {
status = "okay"; // Enable U-Boot logo display function
logo,uboot = "logo.bmp"; // Specify the image for U-Boot logo display
logo,kernel = "logo_kernel.bmp"; // Specify the image for kernel logo display
logo,mode = "center"; // center: centered display, fullscreen: full screen display
charge_logo,mode = "center"; // center: centered display, fullscreen: full screen display
connect = <&vopb_out_edp>; // Determine display path, vopb->edp->panel
};

&edp {
status = "okay"; // Enable edp
};

&vopb {
status = "okay"; // Enable vopb
};

&panel {
"simple-panel";
...
status = "okay";

disp_timings: display-timings {
native-mode = <&timing0>;
timing0: timing0 {
...
};
};
};

defconfig​

Currently, for platforms other than RK3308, U-Boot's defconfig already supports display by default. Simply configure the display-related information in dts. Due to boot speed considerations and other reasons, RK3308 does not support display by default. The following modifications need to be added to defconfig:

--- a/configs/evb-rk3308_defconfig
+++ b/configs/evb-rk3308_defconfig
@@ -4,7 +4,6 @@ CONFIG_SYS_MALLOC_F_LEN=0x2000
CONFIG_ROCKCHIP_RK3308=y
CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0x0
CONFIG_RKIMG_BOOTLOADER=y
-# CONFIG_USING_KERNEL_DTB is not set
CONFIG_TARGET_EVB_RK3308=y
CONFIG_DEFAULT_DEVICE_TREE="rk3308-evb"
CONFIG_DEBUG_UART=y
@@ -55,6 +54,11 @@ CONFIG_USB_GADGET_DOWNLOAD=y
CONFIG_G_DNL_MANUFACTURER="Rockchip"
CONFIG_G_DNL_VENDOR_NUM=0x2207
CONFIG_G_DNL_PRODUCT_NUM=0x330d
+CONFIG_DM_VIDEO=y
+CONFIG_DISPLAY=y
+CONFIG_DRM_ROCKCHIP=y
+CONFIG_DRM_ROCKCHIP_RGB=y
+CONFIG_LCD=y
CONFIG_USE_TINY_PRINTF=y
CONFIG_SPL_TINY_MEMSET=y
CONFIG_ERRNO_STR=y

Note on upstream defconfig configuration:

The upstream maintains a set of Rockchip U-Boot display drivers, currently mainly supporting RK3288 and RK3399:

./drivers/video/rockchip/

To use this driver set, enable CONFIG_VIDEO_ROCKCHIP and disable CONFIG_DRM_ROCKCHIP. Compared to the display driver currently used in our SDK, the latter has the following advantages:

  • More comprehensive platform and display interface support;

  • Display interfaces such as HDMI and DP can output specified resolutions according to user settings, overscan effect, display adjustment effect, etc.

  • U-Boot logo can smoothly transition to kernel logo until the system is up;

LOGO Partition​

If users need to dynamically update the boot LOGO (typically initiated at the application layer), this can be achieved through an independent LOGO partition.

Operation steps:

  • Add an independent LOGO partition in the partition table.

  • Dynamically update the image in the LOGO partition as needed. When updating, users can directly write the raw image to the LOGO partition without any packaging. If the image in the LOGO partition is invalid, the default image from the resource file will be used instead.

LOGO partition support:

If the code only includes the following commit, the LOGO partition supports only 1 image, which can only replace the default logo.bmp:

1d30bcc rockchip: resource: support parse "logo" partition picture

If the code includes the following commit, the LOGO partition supports 2 images: image 1 replaces logo.bmp, image 2 replaces logo_kernel.bmp. The two images are adjacent, with 512-byte alignment between them, and the order must not be swapped.

commit 07f987d8d495380787203e2bc2accd44100e6051
Author: Joseph Chen <chenjh@rock-chips.com>
Date: Sun Dec 8 18:00:37 2019 +0800

rockchip: resource: support parse logo_kernel.bmp from logo partition

"logo" partition layout, not change order:

|----------------------| 0x00
| raw logo.bmp |
|----------------------| N*512-byte aligned
| raw logo_kernel.bmp |
|----------------------|

N: the sector count of logo.bmp

Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
Change-Id: I2deba013d3963c99664c5bfd69693835a46ba48f

Assuming the images are logo.bmp and logo_kernel.bmp respectively. logo.img packaging command:

cat logo.bmp > logo.img && truncate -s %512 logo.img && cat logo_kernel.bmp >> logo.img

Flash the generated logo.img to the logo partition. After booting, you will see the "LOGO:" print:

U-Boot 2017.09-g042c01531e-210512-dirty #cjh (May 14 2021 - 11:25:03 +0800)

Model: Rockchip RK3568 Evaluation Board
PreSerial: 2, raw, 0xfe660000
DRAM: 2 GiB
Sysmem: init
Relocation Offset: 7d34f000, fdt: 7b9f8758
Using default environment

dwmmc@fe2b0000: 1, dwmmc@fe2c0000: 2, sdhci@fe310000: 0
Bootdev(atags): mmc 0
MMC0: HS200, 200Mhz
PartType: EFI
boot mode: normal
FIT: No fdt blob
Android 11.0, Build 2021.4, v2
Found DTB in boot part
// The following print indicates that the image in the logo.img partition is correctly recognized.
LOGO: logo.bmp
LOGO: logo_kernel.bmp
DTB: rk-kernel.dtb
HASH(c): OK
......

Pmic/Regulator​

Framework Support​

PMIC/Regulator drivers use the pmic-uclass and regulator-uclass frameworks with standard interfaces.

PMIC support:

rk805/rk808/rk809/rk816/rk817/rk818

Regulator support:

rk805/rk808/rk809/rk816/rk817/rk818/syr82x/tcs452x/fan53555/pwm/gpio/fixed

Configuration:

CONFIG_DM_PMIC
CONFIG_PMIC_CHILDREN
CONFIG_PMIC_RK8XX // Applicable to all current RK8XX series chips
CONFIG_DM_REGULATOR
CONFIG_REGULATOR_PWM
CONFIG_REGULATOR_RK8XX // Applicable to all current RK8XX series chips
CONFIG_REGULATOR_FAN53555

Framework code:

./drivers/power/pmic/pmic-uclass.c
./drivers/power/regulator/regulator-uclass.c

Driver files:

./drivers/power/pmic/rk8xx.c
./drivers/power/regulator/rk8xx.c
./drivers/power/regulator/fixed.c
./drivers/power/regulator/gpio-regulator.c
./drivers/power/regulator/pwm_regulator.c
./drivers/power/regulator/fan53555_regulator.c
// Get regulator. @platname: the name specified by "regulator-name", e.g., vdd_arm, vdd_logic;
int regulator_get_by_platname(const char *platname, struct udevice **devp);

// Enable/disable
int regulator_get_enable(struct udevice *dev);
int regulator_set_enable(struct udevice *dev, bool enable);
int regulator_set_suspend_enable(struct udevice *dev, bool enable);
int regulator_get_suspend_enable(struct udevice *dev);

// Get/set voltage
int regulator_get_value(struct udevice *dev);
int regulator_set_value(struct udevice *dev, int uV);
int regulator_set_suspend_value(struct udevice *dev, int uV);
int regulator_get_suspend_value(struct udevice *dev);

Init Voltage​

There are currently two ways to set the initial voltage output for a regulator, provided that regulator-boot-on is configured:

  • Set regulator-min-microvolt and regulator-max-microvolt to the same value;

  • Configure regulator-init-microvolt = <...>

vdd_arm: DCDC_REG1 {
regulator-name = "vdd_arm";
regulator-boot-on; // Must be configured
regulator-min-microvolt = <712500>;
regulator-max-microvolt = <1450000>;
regulator-init-microvolt = <1100000>; // Set initial voltage to 1.1v
......
};

Skipping Initialization​

To skip initialization of a particular regulator, add regulator-loader-ignore.

vdd_arm: DCDC_REG1 {
regulator-name = "vdd_arm";
regulator-loader-ignore;// Only effective for regulator initialization in U-Boot stage, not kernel
......
};

Charge​

Framework Support​

U-Boot native code does not support charging functionality. RK implemented its own set.

The charging process involves multiple modules: Display, PMIC, Fuel Gauge, Charge Animation, Pwrkey, LED, CPU Low-Power Sleep, Timer, etc.

Fuel gauge support:

RK809/RK816/RK817/RK818/cw201x.

Configuration:

// Framework
CONFIG_DM_CHARGE_DISPLAY
CONFIG_CHARGE_ANIMATION
CONFIG_DM_FUEL_GAUGE
// Driver
CONFIG_POWER_FG_CW201X
CONFIG_POWER_FG_RK818
CONFIG_POWER_FG_RK817
CONFIG_POWER_FG_RK816

Charging framework:

./drivers/power/charge-display-uclass.c

Charge animation driver:

// Responsible for managing the entire charging process. It obtains battery level, charging type, key events, initiates low-power sleep, etc.
./drivers/power/charge_animation.c

Fuel gauge framework:

./drivers/power/fuel_gauge/fuel_gauge_uclass.c

Fuel gauge drivers:

./drivers/power/fuel_gauge/fg_rk818.c
./drivers/power/fuel_gauge/fg_rk817.c // rk809 reuse
./drivers/power/fuel_gauge/fg_rk816.c
......

Logic flow:

charge-display-uclass.c
=> charge_animation.c
=> fuel_gauge_uclass.c
=> fg_rkxx.c

Packaging Images​

Charging images are located in the ./tools/images/ directory and must be packaged into resource.img to be displayed by the charging framework.

The resource.img obtained from kernel compilation does not include charging images by default. They need to be packaged separately in U-Boot.

$ ls tools/images/
battery_0.bmp battery_1.bmp battery_2.bmp battery_3.bmp battery_4.bmp battery_bmp battery_fail.bmp

Packaging command:

./pack_resource.sh <input resource.img> or
./scripts/pack_resource.sh <input resource.img>

Packaging info:

./pack_resource.sh /home/cjh/3399/kernel/resource.img

Pack ./tools/images/ & /home/guest/3399/kernel/resource.img to resource.img ...
Unpacking old image(/home/guest/3399/kernel/resource.img):
rk-kernel.dtb logo.bmp logo_kernel.bmp
Pack to resource.img successed!
Packed resources:
rk-kernel.dtb battery_1.bmp battery_2.bmp battery_3.bmp battery_4.bmp battery_bmp battery_fail.bmp logo.bmp logo_kernel.bmp battery_0.bmp

resource.img is packed ready

After successful packaging, resource.img containing the images will be generated in the U-Boot root directory. Verify the content using the hd command:

hd resource.img | less

00000000 52 53 43 45 00 00 00 00 01 01 01 00 0a 00 00 00 |RSCE............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
......
*
00000400 45 4e 54 52 62 61 74 74 65 72 79 5f 31 2e 62 6d |ENTRbattery_1.bm| // Image 1
00000410 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|
00000420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000500 00 00 00 00 4d 00 00 00 9c 18 00 00 00 00 00 00 |....M...........|
00000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000600 45 4e 54 52 62 61 74 74 65 72 79 5f 32 2e 62 6d |ENTRbattery_2.bm| // Image 2
00000610 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |p...............|
00000620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
......

DTS Configuration​

DTS charging node:

charge-animation {
compatible = "rockchip,uboot-charge";
status = "okay";

rockchip,uboot-charge-on = <0>; // Whether to enable U-Boot charging
rockchip,android-charge-on = <1>; // Whether to enable Android charging

rockchip,uboot-exit-charge-level = <5>; // Minimum battery level to allow boot during U-Boot charging
rockchip,uboot-exit-charge-voltage = <3650>;// Minimum voltage to allow boot during U-Boot charging
rockchip,screen-on-voltage = <3400>; // Minimum voltage to allow screen-on during U-Boot charging

rockchip,uboot-low-power-voltage = <3350>; // Minimum voltage to unconditionally force U-Boot into charging mode

rockchip,system-suspend = <1>; // Whether to enter trust low-power standby when screen off (requires ATF support)
rockchip,auto-off-screen-interval = <20>; // Auto screen-off timeout, in seconds, default 15s
rockchip,auto-wakeup-interval = <10>; // Sleep auto-wakeup interval, in seconds. If value is 0 or this
// property is absent, auto wakeup is disabled, generally used for
// stress testing
rockchip,auto-wakeup-screen-invert = <1>; // Whether to turn screen on/off during auto wakeup from sleep
};

System Sleep​

Pwrkey button:

  • Short press pwrkey can turn the screen on/off. When the screen is off, the system enters low-power mode;

  • Long press pwrkey can boot into the system

There are 2 low-power modes, selected via rockchip,system-suspend = <VAL>:

  • VAL is 0: cpu wfi mode. In this mode, peripheral handling is not performed, only the cpu enters low-power mode;
  • VAL is 1: system suspend mode, requires ATF support to be effective. Similar to kernel system deep standby, the entire SoC enters standby.

ATF version requirements for low-power support:

ChipMinimum Version
RK3399rk3399_bl31_v1.32.elf
RK3368rk3368h_bl31_v2.22.elf
PX30px30_bl31_v1.05.elf
RK3326rk3326_bl31_v1.05.elf
RK3308rk3308_bl31_v2.00.elf、rk3308_bl31_aarch32_v2.20.elf
RK312Xrk3126_tee_ta_v1.39.bin
RK3288rk3288_tee_ta_v1.43.bin

Replacing Images​

  1. Replace the images in the ./tools/images/ directory (using 8bit or 24bit bmp). Use the command ls | sort to verify the image order is from low battery to high battery. Use the pack_resource.sh script to package the images into resource.img;
  2. Modify the image and battery level relationship in ./drivers/power/charge_animation.c;
/*
* IF you want to use your own charge images, please:
*
* 1. Update the following 'image[]' to point to your own images;
* 2. You must set the failed image as last one and soc = -1 !!!
*/
static const struct charge_image image[] = {
{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
{ .name = "battery_bmp", .soc = 100, .period = 600 },
{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
};

// @name: name of the image;
// @soc: battery level corresponding to the image;
// @period: image refresh time (unit: ms);
// Note: The last image must be the fail image, and "soc=-1" cannot be changed !!

Charging LED​

In actual products, users' requirements for LED control vary. Therefore, the charging framework only supports 2 LEDs: charging LED and full LED:

  • Charging LED: During charging, the LED display toggles only when the battery level changes;
  • Full LED: The LED lights up only when the battery is 100% full;

The above 2 LEDs are just a demo. Users should modify the code according to their own requirements.

Configuration options:

CONFIG_LED_CHARGING_NAME
CONFIG_LED_CHARGING_FULL_NAME

These two configuration options are used to specify the label attribute of the LED. Please refer to the Led section.

Storage​

The storage driver uses the standard storage framework, with access interfaces connected to the BLK layer for file system support. Currently supported storage devices: eMMC, Nand flash, SPI Nand flash, SPI Nor flash. The flash-related frameworks are as follows:

AbbreviationMain Supported Particle TypesController DriverFlash FrameworkRegistered Device TypeMain Supported File Systems
rknand schemeMLC TLC Nanddrivers/rkanddrivers/rkandblock deviceFAT、EXT、SquashFS
rkflash schemeSLC Nand, SPI Nanddrivers/rkflashdrivers/rkflashblock deviceFAT、EXT、SquashFS
rkflash scheme (SPI Nor)SPI Nordrivers/rkflashdrivers/rkflashblock or mtd deviceSquashFS、JFFS2
SLC Nand open-source schemeSLC Nanddrivers/mtd/nand/rawdrivers/mtd/nand/rawmtd deviceUBI
SPI Nand open-source schemeSPI Nanddrivers/spi/rockchip_sfc.cdrivers/mtd/nand/rawmtd deviceUBI
SPI Nor open-source schemeSPI Nordrivers/spi/rockchip_sfc.cdrivers/mtd/spimtd or mtd block deviceSquashFS、JFFS2

Note:

  1. The main difference between rkflash and the open-source scheme for Nand flash support is: rkflash integrates rk ftl (Flash Transfer Layer) in the storage driver, while the open-source scheme's ftl part relies on the file system's own flash management, such as the UBI file system supporting bad block management, wear leveling, and other Nand flash-friendly file system features.

Framework Support​

rknand

rknand is a storage driver designed for large-capacity Nand flash devices. It communicates with Nand flash devices through the Nandc host. For specific particle selection, please refer to the document "RKNandFlashSupportList". It applies to the following particles:

  • SLC, MLC, TLC Nand flash

Configuration:

CONFIG_RKNAND

Driver files:

./drivers/rknand/

rkflash

rkflash is a storage driver designed for devices using small-capacity storage. Nand flash support is achieved through communication between the Nandc host and Nand flash devices. SPI flash support is achieved through communication between the SFC host and SPI flash devices. For specific particle selection, please refer to the document "RK SpiNor and SLC Nand SupportList". It applies to the following particles:

  • 128MB, 256MB and 512MB SLC Nand flash
  • Some SPI Nand flash
  • Some SPI Nor flash particles

Configuration:

CONFIG_RKFLASH

CONFIG_RKNANDC_NAND /* Small capacity parallel Nand flash */
CONFIG_RKSFC_NOR /* SPI Nor flash */
CONFIG_RKSFC_NAND /* SPI Nand flash */

Driver files:

./drivers/rkflash/

Note:

  1. SFC (serial flash controller) is a dedicated module designed by Rockchip to conveniently support spi flash;
  2. Since the ftl part of the rknand driver and the rkflash driver Nand code is incompatible:
  • CONFIG_RKNAND and CONFIG_RKNANDC_NAND cannot be configured simultaneously
  • CONFIG_RKNAND and CONFIG_RKSFC_NAND cannot be configured simultaneously

MMC & SD

MMC refers to multimedia cards, such as eMMC; SD is a new generation of storage devices based on semiconductor flash memory. On Rockchip platforms, they share a dw_mmc controller (except rk3399, rk3399pro).

Configuration:

CONFIG_MMC_DW=y
CONFIG_MMC_DW_ROCKCHIP=y
CONFIG_CMD_MMC=y

Driver files:

./drivers/mmc/

SLC Nand & SPI Nand & SPI Nor Open-Source Scheme

With the continuous improvement of the open-source community and the feasibility of the UBI file system, RK has also improved the flash solution combined with more open-source code. The open-source scheme defaults to using SPL as the pre-loader boot scheme, so most configurations are combined with SPL-related configurations.

Configuration:

  1. SPL, MTD, open-source storage driver and MTD block configuration: Refer to CH10-SPL section 4.2

  2. Remove rkflash macro configuration:

CONFIG_RKFLASH=n

Driver files:

./drivers/mtd/nand/raw // SLC Nand controller driver and protocol layer
./drivers/mtd/nand/spi // SPI Nand protocol layer
./drivers/spi/rockchip_sfc.c // SPI Flash controller driver
./drivers/mtd/spi // SPI Nor protocol layer

The access interfaces of storage drivers are all mounted to the BLK layer, so regardless of the storage type, they are accessed through the following interfaces:

// Get storage handle
struct blk_desc *rockchip_get_bootdev(void)

// Access interfaces
unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer)
unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer)
unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt)

DTS Configuration​

eMMC configuration:

// rkxxxx.dtsi configuration
emmc: dwmmc@ff390000 {
compatible = "rockchip,px30-dw-mshc", "rockchip,rk3288-dw-mshc";
reg = <0x0 0xff390000 0x0 0x4000>; // Controller register base address and length
max-frequency = <150000000>; // eMMC normal mode clock is 50MHz. When configured as eMMC
// HS200 mode, this max-frequency takes effect
clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>,
<&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>; // Controller corresponding clock IDs
clock-names = "biu", "ciu", "ciu-drv", "ciu-sample"; // Controller clock names
fifo-depth = <0x100>; // fifo depth, default configuration
interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>; // Interrupt configuration
status = "disabled";
};

// rkxxxx-u-boot.dtsi
&emmc {
u-boot,dm-pre-reloc;
status = "okay";
}

// rkxxxx.dts
&emmc {
bus-width = <8>; // Device bus width
cap-mmc-highspeed; // Indicates this slot supports highspeed mmc
mmc-hs200-1_8v; // Supports HS200
supports-emmc; // Indicates this slot is for eMMC functionality. Must be added, otherwise the peripheral cannot be initialized
disable-wp; // For configurations without physical WP pin, this needs to be configured
non-removable; // Indicates this slot is a non-removable device. This must be added
num-slots = <1>; // Indicates which slot number
status = "okay";
};

Nandc configuration:

&nandc {
u-boot,dm-pre-reloc;
status = "okay";
};

SFC configuration:

&sfc {
u-boot,dm-pre-reloc;
status = "okay";
spi_nand: flash@0 {
u-boot,dm-spl;
compatible = "spi-nand";
reg = <0>;
spi-tx-bus-width = <1>;
spi-rx-bus-width = <4>;
spi-max-frequency = <96000000>;
};
spi_nor: flash@1 {
u-boot,dm-spl;
compatible = "jedec,spi-nor";
reg = <0>;
spi-tx-bus-width = <1>;
spi-rx-bus-width = <4>;
spi-max-frequency = <96000000>;
};
};

Note:

  1. For software compatibility considerations, u-boot only supports spi-tx-bus-width = <1> single-line SPI flash transmission;

Uart​

serial uses the serial-uclass.c framework and standard interfaces, currently mainly used for UART debug.

Configuration:

// Enable configuration
CONFIG_DEBUG_UART
CONFIG_SYS_NS16550

// Parameter configuration
CONFIG_DEBUG_UART_BASE
CONFIG_DEBUG_UART_CLOCK
CONFIG_BAUDRATE

Framework code:

./drivers/serial/serial-uclass.c

Driver code:

./drivers/serial/ns16550.c

Individual Replacement​

The process for individually replacing the UART debug in the U-Boot stage is as follows (uart2 as an example):

  • Disable CONFIG_ROCKCHIP_PRELOADER_SERIAL;

  • Configure uart iomux in board_debug_uart_init() (note: some platforms have m0, m1 modes that need to be configured);

  • Configure uart clock in board_debug_uart_init(), ensuring the clock source is 24Mhz;

  • Update CONFIG_BAUDRATE in defconfig;

  • Update CONFIG_DEBUG_UART_BASE in defconfig;

  • Add 2 required attributes to the U-Boot uart node and enable it:

&uart2 {
u-boot,dm-pre-reloc;
clock-frequency = <24000000>;
status = "okay";
};
  • Specify stdout-path in the U-Boot chosen node:
chosen {
stdout-path = &uart2;
};

Global Replacement​

Pre-loader serial is a mechanism that enables the previous stage firmware to share UART debug configuration. These firmware include: ddr, miniloader, bl31, op-tee, U-Boot. Principle: The earliest stage ddr bin configures the UART debug and passes it down through the ATAGS parameter passing mechanism. Each stage firmware obtains and uses the UART debug configuration (excluding kernel).

Users can achieve global replacement of UART debug by modifying the serial port configuration in the ddr bin. Steps:

DDR bin Configuration

The rkbin repository provides tools for users to configure different parameters, including serial port replacement:

tools/ddrbin_tool
tools/ddrbin_param.txt
tools/ddrbin_tool_user_guide.txt

U-Boot Configuration

1 Enable configuration:

CONFIG_ROCKCHIP_PRELOADER_SERIAL // Already enabled by default

2 Add the attribute "u-boot,dm-pre-reloc;" to the used uart node in rkxx-u-boot.dtsi;

3 Create serial aliases in aliases, because U-Boot finds the target node through aliases and initializes it.

For example, for convenience in ./arch/arm/dts/rk1808-u-boot.dtsi, all uarts have aliases created:

aliases {
mmc0 = &emmc;
mmc1 = &sdmmc;

// Must create aliases
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
serial3 = &uart3;
serial4 = &uart4;
serial5 = &uart5;
serial6 = &uart6;
serial7 = &uart7;
};

.....

// Must add the u-boot,dm-pre-reloc attribute
&uart0 {
u-boot,dm-pre-reloc;
};
&uart1 {
u-boot,dm-pre-reloc;
};
&uart2 {
u-boot,dm-pre-reloc;
clock-frequency = <24000000>;
status = "okay";
};
&uart3 {
u-boot,dm-pre-reloc;
};
&uart4 {
u-boot,dm-pre-reloc;
};

Disabling Print​

CONFIG_DISABLE_CONSOLE=y
// UART debug interfaces
void putc(const char c);
void puts(const char *s);
int printf(const char *fmt, ...);
void flushc(void);

// Regular UART interfaces for communication with peripherals
int serial_dev_getc(struct udevice *dev);
int serial_dev_tstc(struct udevice *dev);
void serial_dev_putc(struct udevice *dev, char ch);
void serial_dev_puts(struct udevice *dev, const char *str);
void serial_dev_setbrg(struct udevice *dev, int baudrate);
void serial_dev_clear(struct udevice *dev);

Key​

Framework Support​

The U-Boot framework does not support key functionality by default. RK implemented its own key framework.

Implementation rules:

  • All keys are specified through the kernel and U-Boot DTS. U-Boot does not use hard-coded methods to define any keys;
  • U-Boot preferentially looks for keys in the kernel dts, and if not found, then looks in the U-Boot dts.
  • Only the burn key is defined in the U-Boot dts.
  • If users need to update the burn key definition, please update both the kernel and U-Boot dts.

Configuration:

CONFIG_DM_KEY
CONFIG_RK8XX_PWRKEY
CONFIG_ADC_KEY
CONFIG_GPIO_KEY
CONFIG_RK_KEY

Framework code:

./include/dt-bindings/input/linux-event-codes.h
./drivers/input/key-uclass.c
./include/key.h

Driver code:

./drivers/input/rk8xx_pwrkey.c // Supports PMIC pwrkey (RK805/RK809/RK816/RK817)
./drivers/input/rk_key.c // Supports compatible = "rockchip,key"
./drivers/input/gpio_key.c // Supports compatible = "gpio-keys"
./drivers/input/adc_key.c // Supports compatible = "adc-keys"

The pwrkey is only identified via interrupt, while other gpio keys are identified via polling.

Interface:

int key_read(int code)

code definition:

/include/dt-bindings/input/linux-event-codes.h

Return value:

enum key_state {
KEY_PRESS_NONE, // Not a complete short press (key not released) or not a complete long press (press time insufficient);
KEY_PRESS_DOWN, // A complete short press (press => release);
KEY_PRESS_LONG_DOWN, // A complete long press (release not required);
KEY_NOT_EXIST, // Key does not exist
};

KEY_PRESS_LONG_DOWN default duration is 2000ms, currently only used for the pwrkey long press event in U-Boot charging.

#define KEY_LONG_DOWN_MS 2000

Example:

int ret;

ret = key_read(KEY_VOLUMEUP);
...

Vendor Storage​

Vendor Storage is used to store small data that does not require encryption, such as SN, MAC, etc. The data is stored in reserved partitions of NVM (eMMC, NAND, etc.), with multiple backups. Data is not lost when updating, ensuring high reliability.

For detailed information, refer to the document "appnote rk vendor storage".

Principle Overview​

The vendor storage block is divided into 4 partitions: vendor0, vendor1, vendor2, vendor3. The hdr of each vendorX (X=0, 1, 2, 3) contains a monotonically increasing version field indicating when vendorX was last updated. Each read operation only reads the latest vendorX (i.e., with the highest version). During a write operation, the version is updated and the entire existing information along with the new information is moved to the vendorX+1 partition. For example, if information is currently read from vendor2, after modification and writing back, it is written to vendor3. This is done for simple security protection.

Framework Support​

The U-Boot framework does not support Vendor Storage functionality. Rockchip implemented its own Vendor Storage driver.

Configuration:

CONFIG_ROCKCHIP_VENDOR_PARTITION

Driver files:

./arch/arm/mach-rockchip/vendor.c
./arch/arm/include/asm/arch-rockchip/vendor.h
int vendor_storage_read(u16 id, void *pbuf, u16 size)
int vendor_storage_write(u16 id, void *pbuf, u16 size)

For id definitions and usage, please refer to "appnote rk vendor storage".

Self-Test​

Use the "rktest vendor" command in the U-Boot serial console to perform a Vendor Storage self-test.

OPTEE Client​

U-Boot belongs to the Non-Secure World in ARM TrustZone and requires the OPTEE Client to access secure resources.

Framework Support​

The U-Boot framework does not support OPTEE Client functionality by default. RK implemented its own set.

Configuration:

// Master enable
CONFIG_OPTEE_CLIENT

// Used by older platforms, e.g., RK312x, RK322x, RK3288, RK3228H, RK3368, RK3399
CONFIG_OPTEE_V1
// Used by newer platforms, e.g., RK3326, RK3308
CONFIG_OPTEE_V2
// When eMMC RPMB is unavailable, this configuration must be enabled, i.e., enable the security partition!
CONFIG_OPTEE_ALWAYS_USE_SECURITY_PARTITION

Framework and driver:

lib/optee_clientApi/

Firmware Notes​

The trust.img used must have the TA function enabled; otherwise, interaction with OPTEE Client is not possible.

Interface Documentation​

The Optee client driver is located in the lib/optee_client directory. For the Optee Client API, please refer to "TEE_Client_API_Specification-V1.0_c.pdf". Download address: https://globalplatform.org/specs-library/tee-client-api-specification/

Shared Memory​

Data for communication between U-Boot and Optee must be placed in shared memory. Users can use TEEC_AllocateSharedMemory() to allocate shared memory, but it is recommended not to exceed 1M. If exceeding, it is recommended to split the data for multiple transfers. After use, call TEEC_ReleaseSharedMemory() to release the shared memory.

Test Command​

Purpose: Test secure storage functionality. U-Boot command line:

=> mmc testsecurestorage

This test case will loop-test secure storage read/write functions. When using emmc, it will test both rpmb and security partition secure storage methods; when using nand, it will only test the security partition secure storage.

Common Error Messages​

  • No emmc or nand device found. Please check if U-Boot is missing configuration or if the hardware is damaged.

    "TEEC: Could not find device"
  • No security partition found. When RPMB is not available, define the security partition in parameter.txt.

    "TEEC: Could not find security partition"
  • This message appears when using the security partition for secure storage for the first time or when the security partition data has been illegally modified.

    "TEEC: verify [%d] fail, cleaning ...."
  • Insufficient secure storage space. Please check if the stored data is too large, or if large amounts of previously stored data have not been deleted.

    "TEEC: Not enough space available in secure storage !"

DVFS​

The DVFS in this chapter differs from the kernel. It is a dynamic frequency and voltage scaling mechanism specifically designed for wide-temperature chips.

Wide-Temperature Strategy​

The U-Boot framework does not support DVFS. To support the wide-temperature function of certain chips, RK implemented a DVFS wide-temperature driver that adjusts the cpu/dmc frequency and voltage based on the chip temperature. However, unlike the kernel DVFS driver, this wide-temperature driver only performs control when the high/low temperature thresholds are triggered.

Wide-temperature strategy:

  1. The wide-temperature driver is used to adjust the cpu/dmc frequency and voltage. The control strategy can take effect for both cpu and dmc simultaneously, or only one of them, as determined by dts configuration; the cpu and dmc control strategies are the same;
  2. The wide-temperature driver parses the opp table, regulator, clock, and thermal zone's "trip-point-0" of the cpu/dmc node to obtain frequency-voltage levels, high/low temperature thresholds, maximum allowed voltage, etc.;
  3. If the cpu/dmc opp table specifies rockchip,low-temp = <...> or rockchip,high-temp = <...>, or the cpu/dmc references the trip node of the thermal zone, then the cpu/dmc wide-temperature control strategy will take effect;
  4. Key attributes:
  • rockchip,low-temp: Minimum temperature threshold, denoted as TEMP_min below;
  • rockchip,high-temp and thermal zone: Maximum temperature threshold, denoted as TEMP_max below (if both are valid, the current temperature will be compared against both in the strategy);
  • rockchip,max-volt: Maximum allowed voltage value, denoted as V_max below;
  1. Threshold trigger handling:
  • If the temperature is above TEMP_max, reduce both frequency and voltage to the lowest level;
  • If the temperature is below TEMP_min, increase voltage by 50mv by default. If a 50mv increase would cause the voltage to exceed V_max, set the voltage to V_max and reduce the frequency by 2 levels;
  1. Currently, the wide-temperature strategy is applied at 2 time points:
  • After the regulator and clk framework initialization is complete, the wide-temperature driver initializes and executes the wide-temperature strategy once. This is called in board_init() in the board.c file;
  • During the preboot phase (i.e., before loading firmware), the wide-temperature strategy is executed again: if the dts node specifies "repeat" or related properties (see below), and after executing this wide-temperature strategy the chip temperature is still not within the temperature threshold range, system startup will be halted and the wide-temperature strategy will be continuously executed until the chip temperature returns to the threshold range before continuing system startup. If there are no "repeat" or related properties, the system starts directly after executing this wide-temperature strategy. Currently, the repeat attribute is generally not needed.

Framework Support​

Framework code:

./drivers/power/dvfs/dvfs-uclass.c
./include/dvfs.h
./cmd/dvfs.c

Driver code:

./drivers/power/dvfs/rockchip_wtemp_dvfs.c
// Execute dvfs strategy once
int dvfs_apply(struct udevice *dev);

// If the repeat attribute exists, execute the dvfs strategy in a loop when the temperature is not within the threshold range
int dvfs_repeat_apply(struct udevice *dev);

Enabling Wide-Temperature​

  1. Enable configuration:
CONFIG_DM_DVFS=y
CONFIG_ROCKCHIP_WTEMP_DVFS=y

CONFIG_DM_THERMAL=y
CONFIG_ROCKCHIP_THERMAL=y
CONFIG_USING_KERNEL_DTB=y
  1. Specify CONFIG_PREBOOT:
#ifdef CONFIG_DM_DVFS
#define CONFIG_PREBOOT "dvfs repeat"
#else
#define CONFIG_PREBOOT
#endif
  1. Configure the wide-temperature node in kernel dts:
uboot-wide-temperature {
compatible = "rockchip,uboot-wide-temperature";

// Optional. Indicates whether to stop system startup and continuously execute the wide-temperature
// handling strategy when the cpu high/low temperature threshold is triggered in the U-Boot stage,
// until the chip temperature returns to the threshold range before continuing system startup.
cpu,low-temp-repeat;
cpu,high-temp-repeat;

// Optional. Indicates whether to stop system startup and continuously execute the wide-temperature
// handling strategy when the dmc high/low temperature threshold is triggered in the U-Boot stage,
// until the chip temperature returns to the threshold range before continuing system startup.
dmc,low-temp-repeat;
dmc,high-temp-repeat;

status = "okay";
};

Generally, the above repeat-related attributes do not need to be configured.

Wide-Temperature Results​

When cpu temperature control is enabled, the following print appears:

// <NULL> indicates that no low temperature threshold is specified
DVFS: cpu: low=<NULL>'c, high=95'c, Vmax=1350000uV, tz_temp=88.0'c, h_repeat=0, l_repeat=0

When cpu temperature control triggers the high temperature threshold, adjustment information is displayed:

DVFS: 90.352'c
DVFS: cpu(high): 600000000->408000000 Hz, 1050000->950000 uV

When cpu temperature control triggers the low temperature threshold, adjustment information is displayed:

DVFS: 10.352'c
DVFS: cpu(low): 600000000->600000000 Hz, 1050000->1100000 uV

Similarly, when dmc triggers high/low temperature thresholds, the above information will also be printed, with the prefix "dmc":

DVFS: dmc: ......
DVFS: dmc(high): ......
DVFS: dmc(low): ......

AMP​

Framework Support​

The U-Boot framework does not support AMP (Asymmetric Multi-Processing) by default. RK implemented its own AMP mechanism: different CPUs run different firmware.

AMP functionality requires trust cooperation, and is currently only fully supported on some platforms. AMP firmware uses the FIT format and supports sha256 firmware integrity verification by default.

Configuration:

CONFIG_AMP
CONFIG_ROCKCHIP_AMP

Framework code:

./drivers/cpu/amp-uclass.c
./drivers/cpu/rockchip_amp.c

its template:

./drivers/cpu/amp.its

Packaging tool:

./tools/mkimage // Automatically generated after a full U-Boot compilation

Minimum commit point:

commit 31f8f6ebae796097f7f10c67dff58fd907371c63
Author: Joseph Chen <chenjh@rock-chips.com>
Date: Wed Feb 24 14:34:25 2021 +0800

cpu: amp: support brought up rockchip fit image

./tools/mkimage -f amp.its -E -p 0xe00 amp.img

Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
Change-Id: Icdaf370900472622cf31df402aff84ecc6821fe4

Enabling the Feature​

  1. Making amp.img requires an its file. Please modify based on drivers/cpu/amp.its:

    /dts-v1/;
    / {
    description = "FIT source file for rockchip AMP";
    #address-cells = <1>;

    images {
    amp0 {
    description = "bare-mental-core0"; // Required: description
    data = /incbin/("../../hal0.bin"); // Required: amp0 firmware
    type = "firmware";
    compression = "none";
    arch = "arm"; // Required: "arm64": 64-bit, "arm": 32-bit
    cpu = <0x000>; // Required: cpu hardware id (mpidr)
    thumb = <0>; // Required: 0: arm or thumb2; 1: pure thumb
    hyp = <0>; // Required: 0: el1/svc; 1: el2/hyp
    load = <0xa00000>; // Required: firmware load and run address
    udelay = <500000>; // Optional: delay after starting current CPU before starting the next CPU
    linux-os; // Special optional!
    hash {
    algo = "sha256";
    };
    };

    amp1 {
    ......
    };

    amp2 {
    ......
    };

    amp3 {
    ......
    };
    };

    configurations {
    default = "conf";
    conf {
    description = "Rockchip AMP images";
    rollback-index = <0x0>;
    // Specify the firmware to be loaded and the order.
    loadables = "amp0", "amp1", "amp2", "amp3";
    signature {
    algo = "sha256,rsa2048";
    padding = "pss";
    key-name-hint = "dev";
    sign-images = "loadables";
    };
    };
    };
    };

Explanation:

  • Main CPU: The CPU that is currently running U-Boot and is responsible for starting other cores is called the "main CPU". The main CPU's state is the last to be switched.

  • data: Firmware path. This path is relative to amp.its.

  • arch: CPU 32/64-bit mode. ARMv7 can only be "arm"; ARMv8 can specify "arm64" or "arm" for AArch64 or AArch32 respectively.

  • cpu: CPU hardware ID, i.e., mpidr (Multiprocessor Affinity Register), take the (lower) 32 bits. For example:

    cpus {
    #address-cells = <2>;
    #size-cells = <0>;

    cpu0: cpu@0 {
    device_type = "cpu";
    compatible = "arm,cortex-a55";
    reg = <0x0 0x0>; // mpidr
    ......
    };

    cpu1: cpu@100 {
    device_type = "cpu";
    compatible = "arm,cortex-a55";
    reg = <0x0 0x100>; // mpidr
    ......
    };
    ......
    };
  • thumb: CPU instruction mode. Set to 1 for pure thumb, otherwise 0.

  • hyp: CPU virtualization mode.

  • load: Firmware load and run address.

  • udelay: Delay after loading, in us. After starting the current CPU, do the corresponding delay before starting the next CPU.

  • loadables: AMP firmware load order. The main CPU is loaded last and is not affected by the order specified here.

  • linux-os: The main CPU runs Linux. Optional, can only be added under the main CPU's node. If this attribute exists:

    • After the main CPU starts the other cores, it continues to run Linux (i.e., following the original flow and firmware format, loaded and started by U-Boot).

    • The main CPU node must still be specified in "loadables".

    • The "data" and "load" fields in the main CPU node have no practical effect but must still be retained, otherwise mkimage packaging will report an error.

      It is recommended to create an empty file for the data field, e.g., touch hal0.bin.

  • Special note: AMP OS must handle memory management individually to avoid memory conflicts.

  1. Firmware packaging:

    // 0xe00 is the firmware header size, not recommended to change
    ./tools/mkimage -f ./drivers/cpu/amp.its -E -p 0xe00 amp.img

    A full U-Boot compilation is required to automatically generate the mkimage tool.

  2. Add amp partition to the partition table

    Add the "amp" partition to the parameter.txt partition table file, then flash amp.img.

    U-Boot directly loads the entire content of the amp partition into memory. It is recommended to configure the amp partition size according to actual needs.

  3. Bring up

    The U-Boot framework automatically initiates all AMP bring ups at the appropriate time. For example:

    .......
    ## Loading loadables from FIT Image at 3b3f7c80 ...
    Trying 'amp0' loadables subimage
    Description: bare-mental-core0
    Type: Firmware
    Compression: uncompressed
    Data Start: 0x3b3f8a80
    Data Size: 1016690 Bytes = 992.9 KiB
    Architecture: ARM
    Load Address: 0x00a00000
    Hash algo: sha256
    Hash value: 3376b6ee20dd52a06652b2e15c688206fbec021140d37c15f1020f9ddb20b76d
    Verifying Hash Integrity ... sha256+ OK
    Loading loadables from 0x3b3f8a80 to 0x00a00000
    ## Loading loadables from FIT Image at 3b3f7c80 ...
    Trying 'amp1' loadables subimage
    Description: bare-mental-core1
    Type: Firmware
    Compression: uncompressed
    Data Start: 0x3b4f0e80
    Data Size: 1016690 Bytes = 992.9 KiB
    Architecture: ARM
    Load Address: 0x00b00000
    Hash algo: sha256
    Hash value: f3a08027d58113e9611434ac4f12dd68a40c80df2a6603c10138a2519ccc5f5d
    Verifying Hash Integrity ... sha256+ OK
    Loading loadables from 0x3b4f0e80 to 0x00b00000
    ## Loading loadables from FIT Image at 3b3f7c80 ...
    Trying 'amp2' loadables subimage
    Description: bare-mental-core2
    Type: Firmware
    Compression: uncompressed
    Data Start: 0x3b5e9280
    Data Size: 1016690 Bytes = 992.9 KiB
    Architecture: ARM
    Load Address: 0x00c00000
    Hash algo: sha256
    Hash value: 602eda361c86fb8ad19cd7db813c67bb962d12d7d7355cac015bfff018fef788
    Verifying Hash Integrity ... sha256+ OK
    Loading loadables from 0x3b5e9280 to 0x00c00000
    ## Loading loadables from FIT Image at 3b3f7c80 ...
    Trying 'amp3' loadables subimage
    Description: bare-mental-core3
    Type: Firmware
    Compression: uncompressed
    Data Start: 0x3b6e1680
    Data Size: 1016690 Bytes = 992.9 KiB
    Architecture: AArch64
    Load Address: 0x00d00000
    Hash algo: sha256
    Hash value: 1491d53be18e58165f750813291f2ce26e736bda0c3a9edb257be4614b1e00f2
    Verifying Hash Integrity ... sha256+ OK
    Loading loadables from 0x3b6e1680 to 0x00d00000
    // Success print (if failed, corresponding error print will appear)
    AMP: Brought up cpu[100] with state 0x10, entry 0x00b00000 ...OK
    AMP: Brought up cpu[200] with state 0x10, entry 0x00c00000 ...OK
    AMP: Brought up cpu[300] with state 0x10, entry 0x00d00000 ...OK
    AMP: Brought up cpu[0, self] with state 0x10, entry 0x00a00000 ...OK
    ......

IO-Domain​

Framework Support​

The U-Boot framework does not have io-domain support by default. RK implemented its own set.

Configuration:

CONFIG_IO_DOMAIN
CONFIG_ROCKCHIP_IO_DOMAIN

Framework code:

./drivers/power/io-domain/io-domain-uclass.c

Driver code:

./drivers/power/io-domain/rockchip-io-domain.c
void io_domain_init(void)

Users do not need to actively call io_domain_init(). Simply enable the above configuration, and the U-Boot framework will initialize it automatically.

Watchdog​

Framework Support​

The watchdog driver uses the wdt-uclass.c framework and standard interfaces.

Configuration:

CONFIG_WDT
CONFIG_ROCKCHIP_WATCHDOG

Framework code:

./drivers/watchdog/wdt-uclass.c

Driver code:

./drivers/watchdog/rockchip_wdt.c
// Set watchdog timeout and start wdt (@flags defaults to 0)
int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags);
// Stop wdt
int wdt_stop(struct udevice *dev);
// Kick the dog
int wdt_reset(struct udevice *dev);
// Ignored, currently no underlying driver implementation
int wdt_expire_now(struct udevice *dev, ulong flags)

Currently, U-Boot's default flow does not enable or use the wdt function. Users can enable it based on their product requirements.

Crypto​

The Crypto module is mainly used to implement hardware-level encryption and hash algorithms. There are currently two IP versions:

  • Crypto V1: rk3399/rk3368/rk3328/rk3229/rk3288/rk3128;
  • Crypto V2: rk3326/px30/rk3308/rk1808;

Framework Support​

U-Boot does not have a crypto framework by default. RK implemented its own set.

Configuration:

CONFIG_DM_CRYPTO
CONFIG_ROCKCHIP_CRYPTO_V1
CONFIG_ROCKCHIP_CRYPTO_V2

Framework code:

./drivers/crypto/crypto-uclass.c
./cmd/crypto.c

Driver code:

// crypto v1:
./drivers/crypto/rockchip/crypto_v1.c

// crypto v2:
./drivers/crypto/rockchip/crypto_v2.c
./drivers/crypto/rockchip/crypto_v2_pka.c
./drivers/crypto/rockchip/crypto_v2_util.c
// Get crypto:
struct udevice *crypto_get_device(u32 capability);
// SHA interface:
int crypto_sha_init(struct udevice *dev, sha_context *ctx);
int crypto_sha_update(struct udevice *dev, u32 *input, u32 len);
int crypto_sha_final(struct udevice *dev, sha_context *ctx, u8 *output);
int crypto_sha_csum(struct udevice *dev, sha_context *ctx,
char *input, u32 input_len, u8 *output);
// RSA interface:
int crypto_rsa_verify(struct udevice *dev, rsa_key *ctx, u8 *sign, u8 *output);
  • For interface usage, please refer to: ./cmd/crypto.c;
  • SHA usage differs between v1 and v2: v1 requires assigning the total length of the data to be calculated to ctx->length before calling crypto_sha_init(), v2 does not;

DTS Configuration​

The crypto node must be defined in the U-Boot dts. Main reasons:

  • The kernel dts of older platform SDKs does not have a crypto node, so compatibility with older SDKs must be considered;
  • Secure boot in U-Boot uses crypto, so it is safer and more reasonable for U-Boot to control crypto itself;
  1. crypto v1 configuration (RK3399 as an example):
crypto: crypto@ff8b0000 {
u-boot,dm-pre-reloc;

compatible = "rockchip,rk3399-crypto";
reg = <0x0 0xff8b0000 0x0 0x10000>;
clock-names = "sclk_crypto0", "sclk_crypto1";
clocks = <&cru SCLK_CRYPTO0>, <&cru SCLK_CRYPTO1>; // No frequency specified, default 100M
status = "disabled";
};
  1. crypto v2 configuration (px30 as an example):
crypto: crypto@ff0b0000 {
u-boot,dm-pre-reloc;

compatible = "rockchip,px30-crypto";
reg = <0x0 0xff0b0000 0x0 0x4000>;
clock-names = "sclk_crypto", "apkclk_crypto";
clocks = <&cru SCLK_CRYPTO>, <&cru SCLK_CRYPTO_APK>;
clock-frequency = <200000000>, <300000000>; // Frequency generally needs to be specified
status = "disabled";
};
  • The dts configuration difference between crypto v1 and v2 lies in the clock frequency specification.

Reset​

Framework Support​

The reset driver uses the reset-uclass.c framework and standard interfaces. On RK platforms, reset is essentially a CRU soft reset.

Configuration:

CONFIG_DM_RESET
CONFIG_RESET_ROCKCHIP

Framework code:

./drivers/reset/reset-uclass.c

Driver code:

./drivers/reset/reset-rockchip.c
// Get reset handle
int reset_get_by_index(struct udevice *dev, int index, struct reset_ctl *reset_ctl);
int reset_get_by_name(struct udevice *dev, const char *name,
struct reset_ctl *reset_ctl);
// Release reset
int reset_free(struct reset_ctl *reset_ctl);
// Request reset
int reset_request(struct reset_ctl *reset_ctl);
// Trigger reset, release reset
int reset_assert(struct reset_ctl *reset_ctl);
int reset_deassert(struct reset_ctl *reset_ctl);

Example:

struct reset_ctl reset_ctl;

ret = reset_get_by_name(dev, "mac-phy", &reset_ctl);
if (ret) {
debug("reset_get_by_name() failed: %d\n", ret);
return ret;
}

ret = reset_request(&reset_ctl);
if (ret)
return ret;

ret = reset_assert(&reset_ctl);
if (ret)
return ret;

......

ret = reset_deassert(&reset_ctl);
if (ret)
return ret;

......

ret = reset_free(&reset_ctl);
if (ret)
return ret;

DTS Configuration​

U-Boot enables reset functionality by default. Users only need to specify the reset object to operate on in the peripheral node:

// Format:
reset-names = <name-string-list>
resets = <cru-phandle-list>

For example, gmac2phy:

gmac2phy: ethernet@ff550000 {
compatible = "rockchip,rk3328-gmac";
......

// Specify reset attributes
reset-names = "stmmaceth", "mac-phy";
resets = <&cru SRST_GMAC2PHY_A>, <&cru SRST_MACPHY>;
};

Led​

Framework Support​

The Led driver uses the led-uclass.c framework and standard interfaces.

Configuration:

CONFIG_LED_GPIO

Framework code:

drivers/led/led-uclass // Compiled by default

Driver code:

drivers/led/led_gpio.c // Supports compatible = "gpio-leds"
// Get led device
int led_get_by_label(const char *label, struct udevice **devp);
// Set/get led state
int led_set_state(struct udevice *dev, enum led_state_t state);
enum led_state_t led_get_state(struct udevice *dev);
// Ignored, currently no underlying driver implementation
int led_set_period(struct udevice *dev, int period_ms);

DTS Node​

U-Boot's led_gpio.c has relatively simple functionality, only parsing 3 attributes under the led node:

  • gpios: LED control pin and active state;
  • label: LED name;
  • default-state: Default state, set during driver probe;
leds {
compatible = "gpio-leds";
status = "okay";

blue-led {
gpios = <&gpio2 RK_PA1 GPIO_ACTIVE_LOW>;
label = "battery_full";
default-state = "off";
};

green-led {
gpios = <&gpio2 RK_PA0 GPIO_ACTIVE_LOW>;
label = "greenled";
default-state = "off";
};

......
};

Efuse/Otp​

Framework Support​

The efuse/otp driver uses the misc-uclass.c framework and standard interfaces. Typically, efuse/otp is divided into secure and non-secure areas. U-Boot provides non-secure access, while U-Boot SPL provides access to certain secure otp areas.

Non-secure configuration:

CONFIG_MISC
CONFIG_ROCKCHIP_EFUSE
CONFIG_ROCKCHIP_OTP

Secure configuration:

CONFIG_SPL_MISC=y
CONFIG_SPL_ROCKCHIP_SECURE_OTP=y

Framework code:

./drivers/misc/misc-uclass.c

Driver code:

// non-secure:
./drivers/misc/rockchip-efuse.c
./drivers/misc/rockchip-otp.c
// secure:
./drivers/misc/rockchip-secure-otp.S
// non-secure:
int misc_read(struct udevice *dev, int offset, void *buf, int size)
// secure:
int misc_read(struct udevice *dev, int offset, void *buf, int size)
int misc_write(struct udevice *dev, int offset, void *buf, int size)

DTS Configuration​

Taking rk3308 as an example:

Non-secure:

otp: otp@ff210000 {
compatible = "rockchip,rk3308-otp";
reg = <0x0 0xff210000 0x0 0x4000>;
};

Secure:

secure_otp: secure_otp@0xff2a8000 {
compatible = "rockchip,rk3308-secure-otp";
reg = <0x0 0xff2a8000 0x0 0x4000>;
secure_conf = <0xff2b0004>;
mask_addr = <0xff540000>;
};

Usage Examples​

Non-secure example:

char data[10] = {0};
struct udevice *dev;

/* retrieve the device */
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_GET_DRIVER(rockchip_otp), &dev);
if (ret) {
printf("no misc-device found\n");
return 0;
}

misc_read(dev, 0x10, &data, 10);

Secure example:

char data[10] = {0};
struct udevice *dev;
int i;

/* retrieve the device */
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_GET_DRIVER(rockchip_secure_otp), &dev);
if (ret) {
printf("no misc-device found\n");
return 0;
}

for (i = 0; i < 10; i++)
data[i] = i;

misc_write(dev, 0x10, &data, 10);
memset(data, 0, 10);
misc_read(dev, 0x10, &data, 10);

Secure-Otp​

RK only allows read/write access to certain areas of secure otp, as follows:

0x0; // Rockchip defined as Secure boot enable flag
0x10-0x2f; // Rockchip defined as RSA Public key hash
0x80-0x187; // Rockchip defined as reserved for OEM

MTD​

MTD (Memory Technology Device) supports nand, spi nand, and spi nor. RK designed the MTD block layer to support read/write operations for MTD devices.

Framework Support​

U-Boot configuration:

// MTD driver
CONFIG_MTD=y
CONFIG_CMD_MTDPARTS=y
CONFIG_MTD_DEVICE=y

// MTD block device driver
CONFIG_CMD_MTD_BLK=y
CONFIG_MTD_BLK=y

// Other nand device driver config
......

SPL configuration:

CONFIG_MTD=y
CONFIG_CMD_MTDPARTS=y
CONFIG_MTD_DEVICE=y
CONFIG_SPL_MTD_SUPPORT=y

// Other nand device driver config
......

Framework code:

drivers/mtd/mtd-uclass.c
drivers/mtd/mtdcore.c
drivers/mtd/mtd_uboot.c
drivers/mtd/mtd_blk.c

The drivers are individual controller drivers that connect read/write and other interfaces to the MTD layer.

unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer)

Ethernet​

Framework Support​

Framework code:

./net/*
./drivers/net/*
./drivers/net/phy/*

Driver code:

./drivers/net/designware.c
./drivers/net/dwc_eth_qos.c
./drivers/net/gmac_rockchip.c

menuconfig configuration:

  • Driver configuration Rockchip Ethernet driver has two sets of drivers. If you have questions about driver selection, please refer to our corresponding sdk config configuration.
// designware:
CONFIG_DM_ETH=y
CONFIG_ETH_DESIGNWARE=y
CONFIG_GMAC_ROCKCHIP=y
// dwc_eth_qos:
CONFIG_DM_ETH=y
CONFIG_DM_ETH_PHY=y
CONFIG_DWC_ETH_QOS=y
CONFIG_GMAC_ROCKCHIP=y

Additionally, the dwc_eth_qos driver requires nocache memory configuration, refer to RV1126:

diff --git a/include/configs/rv1126_common.h b/include/configs/rv1126_common.h
index 933917f3f0..9d70795fb8 100644
--- a/include/configs/rv1126_common.h
+++ b/include/configs/rv1126_common.h
@@ -50,6 +50,7 @@
#define CONFIG_SYS_SDRAM_BASE 0
#define SDRAM_MAX_SIZE 0xfd000000

+#define CONFIG_SYS_NONCACHED_MEMORY (1 << 20) /* 1 MiB */
#ifndef CONFIG_SPL_BUILD

  • cmd configuration Manually configure the required functions.
Command line interface ---> Network commands --->

[*] bootp, tftpboot
[ ] tftp put
[ ] tftp download and bootm
[ ] tftp download and flash
[ ] tftpsrv
[ ] rarpboot
-*- dhcp
-*- pxe
[ ] nfs
-*- mii
-*- ping
[ ] cdp
[ ] sntp
[ ] dns
[ ] linklocal
[ ] ethsw
  • Data structure initialization interfaces
void net_init(void);
int eth_register(struct eth_device *dev);
int phy_init(void);
  • Device registration interfaces
int eth_register(struct eth_device *dev);
int phy_register(struct phy_driver *drv);
  • Network data read/write and phy read/write U-Boot's data transmission and reception require active invocation, without using interrupts or polling. Refer to NetLoop() for specific implementation.
int eth_send(void *packet, int length);
int eth_rx(void);

int phy_read(struct phy_device *phydev, int devad, int regnum);
int phy_write(struct phy_device *phydev, int devad, int regnum, u16 val);

DTS Configuration​

The DTS node is the same as the kernel. The following board-level attribute configurations need attention:

  • phy interface configuration (phy-mode)

  • phy reset pin and reset time (snps,reset-gpio) (snps,reset-delays-us)

  • Clock output direction for the controller (clock_in_out)

  • Clock source selection and frequency setting (assigned-clock-parents) (assigned-clock-rates)

  • RGMII Delayline, required for RGMII interface (tx_delay) (rx_delay)

&gmac {
phy-mode = "rgmii";
clock_in_out = "input";

snps,reset-gpio = <&gpio3 RK_PA0 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
/* Reset time is 20ms, 100ms for rtl8211f */
snps,reset-delays-us = <0 20000 100000>;

assigned-clocks = <&cru CLK_GMAC_SRC>, <&cru CLK_GMAC_TX_RX>, <&cru CLK_GMAC_ETHERNET_OUT>;
assigned-clock-parents = <&cru CLK_GMAC_SRC_M1>, <&cru RGMII_MODE_CLK>;
assigned-clock-rates = <125000000>, <0>, <25000000>;

pinctrl-names = "default";
pinctrl-0 = <&rgmiim1_pins &clk_out_ethernetm1_pins>;

tx_delay = <0x2a>;
rx_delay = <0x1a>;

phy-handle = <&phy>;
status = "okay";
};

Usage Examples​

Common network commands:

  • DHCP
Usage:
dhcp [loadAddress] [[hostIPaddr:]bootfilename]

Using this command, there is no need to set serverip, ipaddr, or gateway. After DHCP successfully obtains an IP address from the DHCP server, it will retrieve the file from the hostIPaddr via tftp.

100M environment:

=> dhcp 0x20000000 192.168.0.100:kernel.img
ethernet@ffc40000 Waiting for PHY auto negotiation to complete. done
BOOTP broadcast 1
DHCP client bound to address 192.168.0.106 (2 ms)
Using ethernet@ffc40000 device
TFTP from server 192.168.0.100; our IP address is 192.168.0.106
Filename 'kernel.img'.
Load address: 0x20000000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
###############################################################
1.5 MiB/s
done
Bytes transferred = 19054084 (122be04 hex)
  • PING
=> ping 192.168.0.1
ethernet@ffc40000 Waiting for PHY auto negotiation to complete. done
Using ethernet@ffc40000 device
host 192.168.0.1 is alive
  • TFTP

1000M environment:

Usage:
tftp [loadAddress] [[hostIPaddr:]bootfilename]

You can also set the IP address yourself:

=> setenv ipaddr 192.168.1.101
=> setenv serverip 192.168.1.100

=> tftp kernel.img 0x20000000
ethernet@ffc40000 Waiting for PHY auto negotiation to complete. done
Using ethernet@ffc40000 device
TFTP from server 192.168.1.100; our IP address is 192.168.1.101
Filename 'kernel.img'.
Load address: 0x20000000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################
12.2 MiB/s
done
Bytes transferred = 20275220 (1356014 hex)

Network Troubleshooting​

1. Network environment, common aspects:

  • Is the firewall on the PC turned off;

  • If crossing network segments, confirm whether the gateway is set;

  • Is the TFTP server configured correctly;

  • Is the TFTP function of some routers disabled.

2. Code issues, generally confirm the following 3 areas:

  • Is the pinctrl configuration correct. Check if the iomux and drive strength of the relevant pins are correct. You can also dump the relevant registers and compare with the kernel to see if they match. In most cases, we debug the kernel network first before debugging U-Boot.

  • Is the clock configuration correct. Clock configuration checking is relatively more complex. Mainly check the division ratio, clock source, and clock direction. Most registers are in CRU, but some chips have some registers in GRF. You can also dump the relevant registers and compare with the kernel to see if they match.

  • PHY reset pin. Mainly check whether the reset pin configuration is correct and whether the reset waveform meets the PHY requirements.

PCIe​

Framework Support​

Framework code:

./drivers/pci/*
./drivers/phy/*

Driver code:

drivers/pci/pcie_dw_rockchip.c
drivers/phy/phy-rockchip-snps-pcie3.c

menuconfig configuration:

  • Driver configuration

Rockchip PCIe driver currently supports platforms as listed in the compatible attribute of the pcie_dw_rockchip.c file. If you have questions about driver selection, please refer to our corresponding sdk config configuration.

CONFIG_DM_REGULATOR_GPIO=y
CONFIG_PCI=y
CONFIG_DM_PCI=y
CONFIG_DM_PCI_COMPAT=y
CONFIG_PCI_PNP=y
CONFIG_PCIE_DW_ROCKCHIP=y
CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y
CONFIG_PHY=y
CONFIG_CMD_PCI=y
// Add NVMe support
CONFIG_NVME=y
CONFIG_CMD_NVME=y
// Add PCIe to USB support
CONFIG_USB_XHCI_PCI=y

DTS Configuration​

Directly reuse the kernel DTB node. For related documentation, please refer to the kernel PCIe configuration instructions.

Usage Examples​

Common commands:

  • pci
// Initiate PCIe scan, bus enumeration identifies a Gen3 2-lane device
=> pci enum
PCIe Linking... LTSSM is 0x1
PCIe Link up, LTSSM is 0x230011
PCIE-0: Link up (Gen3-x2, Bus0)


// Scan bus0, default is a bridge device
=> pci scan
Scanning PCI devices on bus 0
BusDevFun VendorId DeviceId Device Class Sub-Class
_____________________________________________________________
00.00.00 0x1d87 0x3566 Bridge device 0x04

// Scan bus1, currently sees an NVMe attached
=> pci 1
Scanning PCI devices on bus 1
BusDevFun VendorId DeviceId Device Class Sub-Class
_____________________________________________________________
01.00.00 0x144d 0xa808 Mass storage controller 0x08

// Initiate nvme scan
=> nvme scan

// List nvme device details
=> nvme details
Blk device 0: Optional Admin Command Support:
Namespace Management/Attachment: no
Firmware Commit/Image download: yes
Format NVM: yes
Security Send/Receive: no
Blk device 0: Optional NVM Command Support:
Reservation: yes
Save/Select field in the Set/Get features: yes
Write Zeroes: yes
Dataset Management: yes
Write Uncorrectable: yes
Blk device 0: Format NVM Attributes:
Support Cryptographic Erase: No
Support erase a particular namespace: Yes
Support format a particular namespace: Yes
Blk device 0: LBA Format Support:
Blk device 0: End-to-End DataProtect Capabilities:
As last eight bytes: No
As first eight bytes: No
Support Type3: No
Support Type2: No
Support Type1: No
Blk device 0: Metadata capabilities:
As part of a separate buffer: No
As part of an extended data LBA: No

// See a 256GB NVMe. If capacity is not displayed, remove the device to ensure complete power-off and try again.
=> nvme info
Device 0: Vendor: 0x144d Rev: EXD7201Q Prod: S444NA0M384608
Type: Hard Disk
Capacity: 244198.3 MB = 238.4 GB (500118192 x 512)

// Select NVMe device with ID 0
=> nvme device 0

Device 0: Vendor: 0x144d Rev: EXD7201Q Prod: S444NA0M384608
Type: Hard Disk
Capacity: 244198.3 MB = 238.4 GB (500118192 x 512)
... is now current device

// Set 0x40000000 memory to 0x55aa55aa
=> md.l 0x40000000 1
40000000: d08ec033 3...
=> mw.l 0x40000000 0x55aa55aa
=> md.l 0x40000000 1
40000000: 55aa55aa .U.U

// Read 1 block of data from 0x40000000 memory, write to NVMe LBA 0
=> nvme write 0x40000000 0x0 0x1

nvme write: device 0 block # 0, count 1 ... 1 blocks written: OK

// Check 0x44000000 memory, confirm original data
=> md.l 0x44000000 1
44000000: ffffffff ....

// Read 1 block of data from NVMe LBA 0, write to memory 0x44000000
=> nvme read 0x44000000 0x0 0x1

nvme read: device 0 block # 0, count 1 ... 1 blocks read: OK

// Confirm 0x44000000 memory data was read back from NVMe
=> md.l 0x44000000 1
44000000: 55aa55aa .U.U