IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> RockChip U-Boot v2017(next-dev)详解(三) -> 正文阅读

[嵌入式]RockChip U-Boot v2017(next-dev)详解(三)

一、驱动模块

1、Interrupt

1.1、框架支持

U-Boot原生代码没有中断框架,RK自己实现了一套用于支持GICv2/v3,默认使能。

目前用到中断的场景:

  • PwrkeyU-Boot 充电时 CPU 会进入低功耗休眠,需要通过Pwrkey 中断唤醒 CPU
  • TimerU-Boot 充电和测试用例中会用到 Timer 中断;
  • Debug:使能 CONFIG_ROCKCHIP_DEBUGGER 调试功能;

配置:

CONFIG_IRQ
CONFIG_GICV2
CONFIG_GICV3
框架代码:
./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
1.2、相关接口
// CPU本地中断开关 
void enable_interrupts(void); 
int disable_interrupts(void); 

// GPIO转换成中断号 
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); 

// 注册/释放中断回调 
void irq_install_handler(int irq, interrupt_handler_t *handler, void *data); 
void irq_free_handler(int irq); 

// 使能/关闭中断 
int irq_handler_enable(int irq); 
int irq_handler_disable(int irq);

// 中断触发类型 
int irq_set_irq_type(int irq, unsigned int type);

申请IRQ

  • 拥有独立硬件中断号的外设不需要额外转换,例如:pwm, timer等。
  • GPIO pin 脚没有独立的硬件中断号,需要额外转换申请。
一共有3种方式申请GPIO的pin脚中断号:
(1)传入 struct gpio_desc结构体
// 此方法可以动态解析dts配置,比较灵活、常用。 
int gpio_to_irq(struct gpio_desc *gpio);

范例:

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); 

// 为了示例简单,省去返回值判断 
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)传入GPIO的phandle和pin

// 此方法可以动态解析dts配置,比较灵活、常用。 
int phandle_gpio_to_irq(u32 gpio_phandle, u32 pin);
范例( rk817 的中断引脚为 GPIO0_A7 ):
rk817: pmic@20 { 
    compatible = "rockchip,rk817"; 
    reg = <0x20>; 
    ...... 
    interrupt-parent = <&gpio0>; // "&gpio0": 指向gpio0节点的phandle; 
    interrupts = <7 IRQ_TYPE_LEVEL_LOW>; // "7": pin脚; 
    ...... 
};
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; 
}

// 为了示例简单,省去返回值判断 
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)强制指定gpio

// 此方法直接强制指定 gpio,传入的 gpio 必须通过特殊的宏来声明才行,不够灵活,不建议使用。 
int hard_gpio_to_irq(unsigned gpio);

范例GPIO0_A0 申请中断):

int gpio0_a0, irq; 

// 为了示例简单,省去返回值判断 
gpio0_a0 = RK_IRQ_GPIO(RK_GPIO0, RK_PA0); 
irq = hard_gpio_to_irq(gpio0_a0); 
irq_install_handler(irq, ...); 
irq_handler_enable(irq);

2、Clock

2.1、框架支持

clock 驱动使用 clk-uclass 框架和标准接口。

配置:

CONFIG_CLK

框架代码:

./drivers/clk/clk-uclass.c

平台驱动代码:

./drivers/clk/rockchip/...

2.2、相关接口

// 申请时钟 
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); 

// 使能/关闭时钟 
int clk_enable(struct clk *clk); 
int clk_disable(struct clk *clk); 

// 配置/获取频率 
ulong (*get_rate)(struct clk *clk);
ulong (*set_rate)(struct clk *clk, ulong rate); 

// 配置/获取相位 
int (*get_phase)(struct clk *clk); 
int (*set_phase)(struct clk *clk, int degrees);

2.3、时钟初始化

一共有三类接口涉及时钟初始化,如下先列出cru节点信息:

cru: clock-controller@ff490000 {
	compatible = "rockchip,rv1126-cru";
	reg = <0xff490000 0x1000>;
	rockchip,grf = <&grf>;
	#clock-cells = <1>;
	#reset-cells = <1>;

	assigned-clocks =
		<&pmucru CLK_RTC32K>, <&pmucru PLL_GPLL>,
		<&pmucru PCLK_PDPMU>, <&cru PLL_CPLL>,
		<&cru PLL_HPLL>, <&cru ARMCLK>,
		<&cru ACLK_PDBUS>, <&cru HCLK_PDBUS>,
		<&cru PCLK_PDBUS>, <&cru ACLK_PDPHP>,
		<&cru HCLK_PDPHP>, <&cru HCLK_PDAUDIO>,
		<&cru HCLK_PDCORE_NIU>;
	assigned-clock-rates =
		<32768>, <1188000000>,
		<100000000>, <500000000>,
		<1400000000>, <600000000>,
		<500000000>, <200000000>,
		<100000000>, <300000000>,
		<200000000>, <150000000>,
		<200000000>;
	assigned-clock-parents =
		<&pmucru CLK_OSC0_DIV32K>;
};

(1)第一类,平台基础时钟默认初始化: rkclk_init()

????????各平台cru驱动probe会调用 rkclk_init() 完成 pll/cpu/bus 频率初始化,频率定义在 cru_rkxxx.h
例如RV1109:
#define MHz		1000000
#define KHz		1000
#define OSC_HZ		(24 * MHz)

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_KERNEL_BOOT)
#define APLL_HZ		(1008 * MHz)
#else
#define APLL_HZ		(816 * MHz)
#endif
#define GPLL_HZ		(1188 * MHz)
#define CPLL_HZ		(500 * MHz)
#define HPLL_HZ		(1400 * MHz)
#define PCLK_PDPMU_HZ	(100 * MHz)
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_KERNEL_BOOT)
#define ACLK_PDBUS_HZ	(396 * MHz)
#else
#define ACLK_PDBUS_HZ	(500 * MHz)
#endif
#define HCLK_PDBUS_HZ	(200 * MHz)
#define PCLK_PDBUS_HZ	(100 * MHz)
#define ACLK_PDPHP_HZ	(300 * MHz)
#define HCLK_PDPHP_HZ	(200 * MHz)
#define HCLK_PDCORE_HZ	(200 * MHz)
#define HCLK_PDAUDIO_HZ	(150 * MHz)
#define CLK_OSC0_DIV_HZ	(32768)
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_KERNEL_BOOT)
#define ACLK_PDVI_HZ	(297 * MHz)
#define CLK_ISP_HZ	(297 * MHz)
#define ACLK_PDISPP_HZ	(297 * MHz)
#define CLK_ISPP_HZ	(237 * MHz)
#define ACLK_VOP_HZ	(300 * MHz)
#define DCLK_VOP_HZ	(65 * MHz)
#endif

(2)第二类,平台基础时钟二次初始化:clk_set_defaults()

????????各平台cru驱动 probe 可能会调用 clk_set_defaults() 解析且配置 cru节点内 assigned-clocks/assigned- clock - parents/assigned - clock-rates指定的频率(即重新配置频率),但是不包括 arm 频率。仅当实现 set_armclk_rate() 时才会配置。
????????除了cru 之外,有需求的外设也可以在自己的 probe 里主动调用 clk_set_defaults() ,例如 vop 、 gmac。

(3)第三类,各模块时钟初始化:clk_set_rate()

???????? 大部分外设模块都是调用 clk_set_rate() 配置自己的频率。

2.4、CPU提频

cpu开机提频的实现流程:

  • 步骤1cru 节点的 assigned-clocks 里指定 arm 目标频率;
  • 步骤2cru 驱动probe时调用 clk_set_defaults() 获取(但不会配置)步骤1arm目标频率;
  • 步骤3:实现 set_armclk_rate() ,这是一个 __weak函数,它会配置从步骤2获取的 arm 目标频率;
  • 步骤4arm regulator 节点增加 regulator-init-microvolt = <...> 指定init电压避免电压不足;

2.5、时钟树

U-Boot框架没有提供时钟树管理,各平台增加了soc_clk_dump()用于简单打印时钟信息。例如:

?第一行打印的含义:

  • sync kernel cru 驱动通过 clk_set_defaults() 配置了 kernel cru 节点内指定的各总线频率 arm 频率除外);否则显示为sync uboot
  • enter 816000 KHz :前级 Loader 进入 U-Boot 时的 arm 频率;
  • init 816000 KHz U-Boot arm 初始化频率,即 APLL_HZ 定义的频率;
  • kernel?:实现了 set_armclk_rate() 并设置了 kernel cru 节点里 assigned-clocks 定的 arm 频率;否则显示:"kernel 0N/A"

3、GPIO

3.1、框架支持

GPIO 驱动使用 gpio-uclass 框架和标准接口,用户必须通过 struct gpio_desc 才能访问到gpio

配置:

CONFIG_DM_GPIO?
CONFIG_ROCKCHIP_GPIO

框架代码:

./drivers/gpio/gpio-uclass.c

驱动代码:

./drivers/gpio/rk_gpio.c

3.2、相关接口

// 申请/释放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) 

// 配置GPIO方向。@flags:GPIOD_IS_OUT(输出)和 GPIOD_IS_IN(输入) 
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags); 

// 设置/获取GPIO电平 
int dm_gpio_get_value(const struct gpio_desc *desc) 
int dm_gpio_set_value(const struct gpio_desc *desc, int value)

注意事项:

dm_gpio_get_value() 的返回值表示 active 状态,而非电平的高或低。例如:
  • gpios = <&gpio2 RK_PD0 GPIO_ACTIVE_LOW>,低电平时返回值为 1,高电平为 0
  • gpios = <&gpio2 RK_PD0 GPIO_ACTIVE_HIGH>,低电平时返回值为 0,高电平为 1
dm_gpio_set_value() 的参数 value 也是同理, 1 atcive 0 inactive

4、Pinctrl

4.1、框架支持

pinctrl驱动使用pinctrl_uclass框架和标准接口。

配置:

CONFIG_PINCTRL_GENERIC
CONFIG_PINCTRL_ROCKCHIP

框架代码:

./drivers/pinctrl/pinctrl-uclass.c

驱动代码:

./drivers/pinctrl/pinctrl-rockchip.c

4.2、相关接口

int pinctrl_select_state(struct udevice *dev, const char *statename) // 设置状 态
int pinctrl_get_gpio_mux(struct udevice *dev, int banknum, int index) // 获取状 态
pinctrl 框架会在各个 driver probe 时又框架自动为其设置 "default" 状态,用户一般不需要调用 pinctrl 接口。

5、I2C

i2c驱动使用i2c-uclass框架和标准接口。

5.1、框架支持

配置:

CONFIG_DM_I2C
CONFIG_SYS_I2C_ROCKCHIP

框架代码:

./drivers/i2c/i2c-uclass.c

驱动代码:

. / drivers / i2c / rk_i2c.c
. / drivers / i2c / i2c - gpio.c // gpio 模拟 i2c 通讯,目前用不到

5.2、相关接口

// i2c 读写 
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) 

// 对上面接口的封装 
int dm_i2c_reg_read(struct udevice *dev, uint offset) 
int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val);

6、Display

6.1、框架支持

RK U-Boot目前支持的显示接口包括:RGB,LVDS,EDP,MIPI,和HDMI,未来还会加入CVBS,DP等。U-Boot显示logo图片来自kernel根目录下的logo.bmp和logo_kernel.bmp,它们被打包在resource.img里。

对图片的要求:

  • 8bit 或者 24bit BMP 格式;
  • logo.bmp logo_kernel.bmp 的图片分辨率大小一致;
  • 对于 rk312x/px30/rk3308 这种基于 vop lite 结构的芯片,由于 VOP 不支持镜像,而 24bit BMP 图片是按镜像存储,所以如果发现显示的图片做了 y 方向的镜像,请在 PC 端提前将图片做好 y 方向的镜像。

配置:

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

框架代码:

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

驱动文件:

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

6.2、相关接口

// 显示 U-Boot logo 和 kernel logo: 
void rockchip_show_logo(void); 

// 显示 bmp 图片,目前主要用于充电图片显示:
void rockchip_show_bmp(const char *bmp); 

// 将 U-Boot 中的一些变量通过 dtb 传给内核。 
// 包括 kernel logo 的大小、地址、格式, crtc 输出扫描时序以及过扫描的配置。 
// 未来还会加入 BCSH 等相关变量配置。 
void rockchip_display_fixup(void *blob);

6.3、DTS配置

route_dsi: route-dsi {
	status = "disabled";                // 使能U-Boot logo显示功能
	logo,uboot = "logo.bmp";            // 指定U-Boot logo显示的图片
	logo,kernel = "logo_kernel.bmp";    // 指定kernel logo显示的图片
	logo,mode = "center";                // center:居中显示,fullscreen:全屏显示                
	charge_logo,mode = "center";        // center:居中显示,fullscreen:全屏显示                    
	connect = <&vop_out_dsi>;          // 确定显示通路,vop->dsi->panel      
};

&dsi {
    status = "okay"; // 使能dsi 
};

&vop { 
    status = "okay"; // 使能vop
};

6.4、LOGO分区

????????用户如果有动态更新开机LOGO的需求(一般在应用层发起更新),可以通过独立的LOGO分区实现。

操作步骤:

  • 分区表中增加独立的LOGO分区
  • 用户根据需要以某种方式动态更新LOGO分区中的图片。更新时,用户直接把原始图片更新到LOGO分区中即可,不需要任何打包。当LOGO分区的图片无效时,则仍旧使用resource文件中默认的图片。

7、Pmic/Regulator

7.1、框架支持

PMIC/Regulator驱动使用pmic-uclass、regulator-uclass框架和标准接口。

PMIC支持:

rk805/rk808/rk809/rk816/rk817/rk818

Regulator支持:

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

配置:

CONFIG_DM_PMIC
CONFIG_PMIC_CHILDREN
CONFIG_PMIC_RK8XX // 适用于目前所有 RK8XX 系列芯片
CONFIG_DM_REGULATOR
CONFIG_REGULATOR_PWM
CONFIG_REGULATOR_RK8XX // 适用于目前所有 RK8XX 系列芯片
CONFIG_REGULATOR_FAN53555

框架代码:

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

驱动文件:

./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

7.2、相关接口

// 获取regulator。 @platname:“regulator-name”指定的名字,如:vdd_arm、vdd_logic; 
int regulator_get_by_platname(const char *platname, struct udevice **devp); 

// 使能/关闭 
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); 

// 配置/获取电压 
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);

7.3、init电压

目前有两种方式为某路 regulator 设置初始化电压输出,前提是必须配置 regulator - boot - on :
  • 配置 regulator-min-microvolt regulator-min-microvolt 为相同值;
  • 配置 regulator-init-microvolt = <...>
vdd_arm: DCDC_REG1 { 
    regulator-name = "vdd_arm"; 
    regulator-boot-on; // 必须配置 
    regulator-min-microvolt = <712500>; 
    regulator-max-microvolt = <1450000>; 
    regulator-init-microvolt = <1100000>; // 设置初始化电压为1.1v 
    ...... 
};

7.4、跳过初始化

如果想跳过某路regulator的初始化,可增加 regulator-loader-ignore

vdd_arm: DCDC_REG1 { 
    regulator-name = "vdd_arm"; 
    regulator-loader-ignore;// 仅对U-Boot阶段的regulator初始化有效,kernel无效 
    ...... 
};

8、Charge

8.1、框架支持

U-Boot原生不支持充电功能,RK自己实现了一套。

充电设计的模块较多:Display,PMIC,电量计,充电动画,pwrkey,len,CPU低功耗休眠,Timer等。

电量计支持:

RK809/RK816/RK817/RK818/cw201x

配置:

// 框架
CONFIG_DM_CHARGE_DISPLAY
CONFIG_CHARGE_ANIMATION
CONFIG_DM_FUEL_GAUGE
// 驱动
CONFIG_POWER_FG_CW201X
CONFIG_POWER_FG_RK818
CONFIG_POWER_FG_RK817
CONFIG_POWER_FG_RK816

充电框架:

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

充电动画驱动:

// 负责管理整个充电过程,它会获取电量、充电类型、按键事件,发起低功耗休眠等。
. / drivers / power / charge_animation.c

电量计框架:

./drivers/power/fuel_gauge/fuel_gauge_uclass.c

电量计驱动:

. / drivers / power / fuel_gauge / fg_rk818.c
. / drivers / power / fuel_gauge / fg_rk817.c // rk809 复用
. / drivers / power / fuel_gauge / fg_rk816.c
......

逻辑流程:

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

8.2、打包图片

充电图片位于./tools/images/目录,需要打包进resource.img才能被充电框架显示。

内核编译得到的resource.img默认没打包充电图片,需要在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

打包命令:

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

打包信息:

./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
成功后会在U-Boot根目录下生成包含图片的resource.img,通过hd命令确认内容:
hd resource.img | less
00000000 52 53 43 45 00 00 00 00 01 01 01 00 0 a 00 00 00 | RSCE............ |
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ |
*
......
*
00000400 45 4 e 54 52 62 61 74 74 65 72 79 5f 31 2 e 62 6 d | ENTRbattery_1.bm |
// 图片 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 4 d 00 00 00 9 c 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 4 e 54 52 62 61 74 74 65 72 79 5f 32 2 e 62 6 d | ENTRbattery_2.bm |
// 图片 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 | ................ |
......

8.3、DTS配置

DTS充电节点:

charge - animation {
????????compatible = "rockchip,uboot-charge" ;
????????status = "okay" ;
????????rockchip,uboot - charge - on = < 0 > ; // 是否开启 U-Boot 充电
????????rockchip,android - charge - on = < 1 > ; // 是否开启 Android 充电
????????rockchip,uboot - exit - charge - level = < 5 > ; // U-Boot 充电时,允许开机的最低电量
????????rockchip,uboot - exit - charge - voltage = < 3650 > ; // U-Boot 充电时,允许开机的最低电压
????????rockchip,screen - on - voltage = < 3400 > ; // U-Boot 充电时,允许点亮屏幕的最低电压
????????rockchip,uboot - low - power - voltage = < 3350 > ; // U-Boot 无条件强制进入充电模式的最低
电压
????????rockchip,system - suspend = < 1 > ; // 是否灭屏时进入 trust 低功耗待机(要ATF支持)
????????rockchip, auto - off - screen - interval = < 20 > ; // 自动灭屏超时,单位秒,默认 15s
????????rockchip, auto - wakeup - interval = < 10 > ; // 休眠自动唤醒时间,单位秒。如果值为 0或没
????????// 有这个属性,则禁止休眠自动唤醒,一般用于
????????// 压力测试使用
????????rockchip, auto - wakeup - screen - invert = < 1 > ; // 休眠自动唤醒时是否需要亮 / 灭屏
};

8.4、系统休眠

Pwrkey按键:

  • 短按 pwrkey 可以亮/灭屏,灭屏时系统会进入低功耗模式;
  • 长按 pwrkey 可开机进入系统

低功模式有2种,通过 rockchip,system-suspend = <VAL> 选择:

  • VAL0cpu wfi 模式。此时不处理外设,仅仅cpu 进入低功耗模式;
  • VAL1system suspend 模式,需要ATF 支持才有效。类同kernel的系统深度待机,整个SoC进入待机。

ATF支持低功耗的版本要求:

8.5、更换图片

(1)更换 ./tools/images/ 目录下的图片(采用 8bit 24bit bmp),使用命令 ls | sort 确认图片排列顺序是低电量到高电量,使用pack_resource.sh 脚本把图片打包进 resource.img

(2)修改 ./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:图片的名字; 
// @soc:图片对应的电量; 
// @period:图片刷新时间(单位:ms); 
// 注意:最后一张图片必须是 fail 图片,且“soc=-1”不可改变 !!

9、Storage

存储驱动使用标准的存储框架,访问接口对接到 BLK 层用于支持文件系统。目前支持的存储设备:eMMC Nand flash SPI Nand flash SPI Nor flash ,其中 flash 相关的框架如下:

9.1、框架支持

(1)rknand

rknand 是针对大容量 Nand flash 设备所设计的存储驱动,通过 Nandc host Nand flash device 通信,具体适用颗粒选型参考《 RKNandFlashSupportList 》,适用以下颗粒:
  • SLCMLCTLC Nand flash

配置:

CONFIG_RKNAND

驱动文件:

. / drivers / rknand /

(2)rkflash

rkflash 是针对选用小容量存储的设备所设计的存储驱动,其中 Nand flash 的支持是通过Nand?host 与Nand flash device 通信完成,SPI flash 的支持是通过 SFC host SPI flash devices 通信完成,具体适用颗粒选型参考《RK SpiNor and SLC Nand SupportList》,适用以下颗粒:

  • 128MB256MB 512MB SLC Nand flash
  • 部分 SPI Nand flash
  • 部分 SPI Nor flash 颗粒

配置:

CONFIG_RKFLASH
CONFIG_RKNANDC_NAND /* 小容量并口 Nand flash */
CONFIG_RKSFC_NOR /* SPI Nor flash */
CONFIG_RKSFC_NAND /* SPI Nand flash */

驱动文件:

. / drivers / rkflash /

注意:

1. SFC serial flash controller )是 Rockchip 为简便支持 spi flash 所设计的专用模块;
2. 由于 rknand 驱动与 rkflash 驱动 Nand 代码中 ftl 部分不兼容,所以
  • CONFIG_RKNAND CONFIG_RKNANDC_NAND 不能同时配置
  • CONFIG_RKNAND CONFIG_RKSFC_NAND 不能同时配置

(3)MMC&SD

MMC 为多媒体卡,比如 eMMC SD 为是一种基于半导体快闪记忆器的新一代记忆设备。在 rockchip平台,它们共用一个 dw_mmc 控制器(除了 rk3399 rk3399pro )。

配置:

CONFIG_MMC_DW=y
CONFIG_MMC_DW_ROCKCHIP=y
CONFIG_CMD_MMC=y

驱动文件:

./drivers/mmc/

(4)SLC Nand & SPI Nand & SPI Nor 开源方案

由于开源社区的不断完善及 UBI 文件系统的可行性,RK 也完善 flash 结合较多开源代码的方案,且开源 方案默认选用 pre loader SPL 的启动方案,所以大部分配置都是结合 SPL 相关配置来完成。

配置:

1. SPL MTD 、开源存储驱动及 MTD block 配置:参考后续SPL相关介绍
2. 去除 rkflash 宏配置:
CONFIG_RKFLASH=n

驱动文件:

. / drivers / mtd / nand / raw //SLC Nand 主控驱动及协议层
. / drivers / mtd / nand / spi //SPI Nand 协议层
. / drivers / spi / rockchip_sfc.c //SPI Flash 主控驱动
. / drivers / mtd / spi //SPI Nor 协议层

9.2、相关接口

存储驱动的访问接口都对挂到 BLK 层,所以无论何种存储都通过如下接口访问:
// 获取存储句柄 
struct blk_desc *rockchip_get_bootdev(void) 

// 访问接口 
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)

9.3、DTS配置

eMMC配置:

// rkxxxx.dtsi配置 
emmc: dwmmc@ff390000 { 
    compatible = "rockchip,px30-dw-mshc", "rockchip,rk3288-dw-mshc"; 
    reg = <0x0 0xff390000 0x0 0x4000>; // 控制器寄存器base address及长度 
    max-frequency = <150000000>; // eMMC普通模式时钟为50MHz,当配置为eMMC
                                // HS200模式,该max-frequency生效
    clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>, <&cru SCLK_EMMC_DRV>, 
        <&cru SCLK_EMMC_SAMPLE>; // 控制器对应时钟编号 
    clock-names = "biu", "ciu", "ciu-drv", "ciu-sample"; // 控制器时钟名 
    fifo-depth = <0x100>; // fifo深度,默认配置 
    interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>; // 中断配置 status = "disabled";
};  

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

// rkxxxx.dts 
&emmc { 
    bus-width = <8>; // 设备总线位宽 
    cap-mmc-highspeed; // 标识此卡槽支持highspeed 
    mmc mmc-hs200-1_8v; // 支持HS200 
    supports-emmc; // 标识此插槽为eMMC功能,必须添加,否则无法初始 化外设
    disable-wp; // 对于无物理WP管脚,需要配置 
    non-removable; // 此项表示该插槽为不可移动设备。 此项为必须添加项 
    num-slots = <1>; // 标识为第几插槽 
    status = "okay"; 
};

Nandc配置:

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

SFC配置:

&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>;
    }; 
};

10、Uart

serial 使用 serial-uclass.c 框架和标准接口,目前主要是UART debug在使用。

配置:

// 使能配置
CONFIG_DEBUG_UART
CONFIG_SYS_NS16550
// 参数配置
CONFIG_DEBUG_UART_BASE
CONFIG_DEBUG_UART_CLOCK
CONFIG_BAUDRATE

框架代码:

./drivers/serial/serial-uclass.c

驱动代码:

./drivers/serial/ns16550.c

10.1、单独更换

单独更换 U-Boot 阶段的UART debug 流程如下(uart2 为例):

  • CONFIG_ROCKCHIP_PRELOADER_SERIAL 禁用;
  • board_debug_uart_init() 里配置 uart iomux(注意:某些平台有m0m1...模式要配置 );
  • board_debug_uart_init() 里配置 uart clock ,保证时钟源是 24Mhz
  • defconfig 更新 CONFIG_BAUDRATE
  • defconfig 更新 CONFIG_DEBUG_UART_BASE
  • U-Boot uart 节点中增加 2 个必要属性并且使能:

????????&uart2 {?
? ? ????????u-boot,dm-pre-reloc;?
? ? ????????clock-frequency = <24000000>;?
? ????????? status = "okay";?
????????};

  • U-Boot chosen 节点中指定 stdout-path
????????chosen {
????????????????stdout-path = &uart2;
????????};

10.2、全局更换

Pre-loader serial 是实现前级固件共享 UART debug 配置的机制,这些固件包括: ddr、 miniloader bl31 、 op-tee、 U-Boot 。原理:由最早阶段的 ddr bin 配置好 UART debug 并且通过 ATAGS 传参机制逐级传递下去,各级固件获取 UART debug 配置进行使用(不包括 kernel )。
用户可以通过修改 ddr bin 里的串口配置实现 UART debug 的全局替换,步骤:

DDR bin配置

rkbin 仓库里提供了工具给用户配置不同的参数,包括串口更换:
tools/ddrbin_tool
tools/ddrbin_param.txt
tools/ddrbin_tool_user_guide.txt
U-Boot配置
(1)使能配置;
CONFIG_ROCKCHIP_PRELOADER_SERIAL // 已经默认使能
(2) rkxx-u-boot.dtsi 中把使用到的 uart 节点加上属性 “u-boot,dm-pre-reloc;”
(3) aliases 建立 serial 别名,因为 U-Boot 是通过 aliaes 找到目标节点并初始化它的。
例如: ./arch/arm/dts/rk1808-u-boot.dtsi 里为了方便,为所有 uart 都建立别名;
aliases {
    ... ...
	serial0 = &uart0;
	serial1 = &uart1;
	serial2 = &uart2;
	serial3 = &uart3;
	serial4 = &uart4;
	serial5 = &uart5;
    ... ...
};

// 必须增加u-boot,dm-pre-reloc属性 
&uart0 { 
    u-boot,dm-pre-reloc; 
    clock-frequency = <24000000>;
    status = "okay";
};

10.3、关闭打印

CONFIG_DISABLE_CONSOLE=y

10.4、相关接口

// UART debug接口 
void putc(const char c); 
void puts(const char *s); 
int printf(const char *fmt, ...); 
void flushc(void); 

// 跟外设通信功能的普通UART接口 
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);

11、Key

11.1、框架支持

U-Boot框架默认没有支持按键功能,RK自己实现了一套按键框架。

实现规则:

  • 所有按键都通过 kernel U-Boot DTS 指定,U-Boot 不使用 hard code 的方式定义任何按键;
  • U-Boot 优先查找 kernel dts 中的按键,找不到再查找 U-Boot dts 中的按键。
  • U-Boot dts里仅定义了烧写按键。
  • 如果用户要更新烧写按键定义,请同时更新kernelU-Bootdts

配置:

CONFIG_DM_KEY
CONFIG_RK8XX_PWRKEY
CONFIG_ADC_KEY
CONFIG_GPIO_KEY
CONFIG_RK_KEY

框架代码:

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

驱动代码:

. / drivers / input / rk8xx_pwrkey.c // 支持 PMIC pwrkey(RK805/RK809/RK816/RK817)
. / drivers / input / rk_key.c // 支持 compatible = "rockchip,key"
. / drivers / input / gpio_key.c // 支持 compatible = "gpio-keys"
. / drivers / input / adc_key.c // 支持 compatible = "adc-keys"
pwrkey 仅以中断方式被识别,其余 gpio 按键以轮询方式被识别。

11.2、相关接口

接口:

int key_read(int code)

code定义:

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

返回值:

enum key_state {
????????KEY_PRESS_NONE, // 非完整的短按(没有释放按键)或非完整长按(按下时间不够长);
????????KEY_PRESS_DOWN, // 一次完整的短按(按下 => 释放);
????????KEY_PRESS_LONG_DOWN, // 一次完整的长按(可以不释放);
????????KEY_NOT_EXIST, // 按键不存在
};
KEY_PRESS_LONG_DOWN 默认时长 2000ms ,目前只用于 U-Boot 充电的 pwrkey 长按事件。
#define KEY_LONG_DOWN_MS 2000

范例:

int ret; 
ret = key_read(KEY_VOLUMEUP); 
...

12、Vendor Stroage

Vendor Storage 用于存放 SNMAC 等不需要加密的小数据。数据存放在 NVMeMMCNAND 等)的保留分区中,有多个备份,更新数据时数据不丢失,可靠性高。

12.1、原理概述

一共把 vendor 的存储块分成 4 个分区, vendor0 vendor1 vendor2 vendor3 。每个 vendorX X=0 、1、 2 3 )的 hdr 里都有一个单调递增的 version 字段用于表明 vendorX 被更新的时刻点。每次读操作只读取最新的 vendorX (即 version 最大),写操作的时候会更新 version 并且把整个原有信息和新增信息搬移到 vendorX+1 分区里。例如当前从 vendor2 读取到信息,经过修改后再回写,此时写入的是vendor3。这样做只是为了起到一个简单的安全防护作用。

12.2、框架支持

配置:

CONFIG_ROCKCHIP_VENDOR_PARTITION

驱动文件:

./arch/arm/mach-rockchip/vendor.c
./arch/arm/include/asm/arch-rockchip/vendor.h

12.3、相关接口

int vendor_storage_read(u16 id, void *pbuf, u16 size) 
int vendor_storage_write(u16 id, void *pbuf, u16 size)

13、Led

13.1、框架支持

Led驱动使用led-uclass.c框架和标准接口

配置:

CONFIG_LED_GPIO

框架代码:

drivers / led / led - uclass // 默认编译

驱动代码:

drivers / led / led_gpio.c // 支持 compatible = "gpio-leds"

13.2、相关接口

// 获取led device 
int led_get_by_label(const char *label, struct udevice **devp); 
// 设置/获取led状态 
int led_set_state(struct udevice *dev, enum led_state_t state); 
enum led_state_t led_get_state(struct udevice *dev); 
// 忽略,目前未做底层驱动实现 
int led_set_period(struct udevice *dev, int period_ms);

13.3、DTS节点

U-Boot led_gpio.c 功能相对简单,只解析 led 节点下的 3 个属性:
  • gpiosled 控制引脚和有效状态;
  • labelled 名字;
  • default-state:默认状态,驱动 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"; 
    };
    ...... 
};

14、MTD

MTD (Memory Technology Device) 即内存技术设备,支持 nand spi nand spi nor RK 设计了 MTD block 层用于支持 MTD 设备的读写。

14.1、框架支持

U-Boot配置:

// MTD 驱动
CONFIG_MTD = y
CONFIG_CMD_MTDPARTS = y
CONFIG_MTD_DEVICE = y
// MTD block 设备驱动
CONFIG_CMD_MTD_BLK = y
CONFIG_MTD_BLK = y
// 其他 nand 设备驱动 config
......

SPL配置:

CONFIG_MTD = y
CONFIG_CMD_MTDPARTS = y
CONFIG_MTD_DEVICE = y
CONFIG_SPL_MTD_SUPPORT = y
// 其他 nand 设备驱动 config
......

框架代码:

drivers/mtd/mtd-uclass.c
drivers/mtd/mtdcore.c
drivers/mtd/mtd_uboot.c
drivers/mtd/mtd_blk.c
驱动为各个控制器驱动,把读写等接口挂接到 MTD 层。

14.2、相关接口

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

15、以太网

15.1、框架支持

框架代码:

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

驱动代码:

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

menuconfig配置:

(1)驱动配置

Rockchip 以太网驱动有两套驱动
// 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
另外 dwc_eth_qos 驱动需要配置 nocache memory ,参考 RV1126:
diff -- git a / include / configs / rv1126_common.h b / include / configs / rv1126_common.h
index 933917f3f0. . 9 d70795fb8 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

(2)cmd配置

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

15.2、相关接口

(1)数据结构初始化接口

void net_init(void); 
int eth_register(struct eth_device *dev); 
int phy_init(void);

(2)设备注册接口

int eth_register(struct eth_device *dev); 
int phy_register(struct phy_driver *drv);

(3)网络数据读写和phy读写

U-Boot 的数据收发需要主动调用,没有采用中断或轮询方式,具体实现可参照 NetLoop().
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);

15.3、DTS配置

DTS 节点与 kernel 一样,需要关注的是以下板级相关的属性配置:
  • phy 接口配置(phy-mode)
  • phy 复位脚与复位时间(snps,reset-gpio) (snps,reset-delays-us)
  • 针对主控的时钟输出方向(clock_in_out)
  • 时钟源选择与频率设定(assigned-clock-parents) (assigned-clock-rates)
  • RGMII Delayline RGMII 接口需要(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"; 
};
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:17:11  更:2022-03-06 13:18:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:26:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码