AMP相关:1 Linux remoteproc子系统(基于STM32MP157)概览
remoteproc(Remote Processor Framework)用于管理异构远程处理器设备。这些设备通常在非对称多处理(Asymmetric MultiProcessing,AMP)配置中,可能运行不同的操作系统实例,包括Linux或其他实时操作系统的变体。
remoteproc框架允许不同平台或架构控制远程处理器(例如,开启电源、加载固件、关闭电源)。此外,该框架还为支持该通信类型的远程处理器添加了rpmsg virtio设备,从而使得特定于平台的远程处理器驱动程序只需提供一些低级处理器,而所有rpmsg驱动程序就可以正常工作。1 remoteproc配置
remoteproc配置如下:
Device Drivers ->Remoteproc drivers
->Support for Remote Processor subsystem
->Remoteproc System Resource Manager core--支持SRM。
->Remoteproc System Resource Manager device
->STM32 remoteproc support
2 remoteproc文件
remoteproc子系统以及驱动文件如下:
drivers/remoteproc/
├── remoteproc_core.c--remoteproc框架核心。 ├── remoteproc_debugfs.c--创建remoteproc目录,以及为每个remoteproc设备创建调试节点。 ├── remoteproc_elf_loader.c--加载ELF格式固件的接口。 ├── remoteproc_sysfs.c--remoteproc设备属性节点。 ├── remoteproc_virtio.c--创建remoteproc相关的virtio设备。 ├── rproc_srm_core.c--SRM核心。 ├── rproc_srm_dev.c--SystemResourceManager是一个管理remoteproc所需资源的组件,此处用于创建资源设备。 └── stm32_rproc.c--STM32 remoteproc驱动。
3 remoteproc子系统以及API
3.1 remoteproc子系统初始化
remoteproc子系统初始化:
remoteproc_init
rproc_init_sysfs
class_register--创建remoteproc类。
rproc_init_debugfs
debugfs_initialized--判断debugfs似乎否初始化。
debugfs_create_dir--创建/sys/kernel/debug/remoteproc/。
每个remoteproc类型设备都会创建如下属性节点:
static struct attribute *rproc_attrs[] = { &dev_attr_firmware.attr, &dev_attr_state.attr, &dev_attr_name.attr, NULL };
实例如下:
/sys/class/remoteproc/remoteproc0/ |-- firmware--配置/lib/firmware目录下的固件名称。 |-- name--远端处理器名称。 |-- state--仅支持start(rproc_boot)/stop(rproc_shutdown)两个写状态。读状态包括offline/suspended/running/crashed/deleted/invalid。 `-- uevent
remoteproc子系统的SRM Core初始化:
rproc_srm_core_driver
rproc_srm_core_probe
rproc_add_subdev--注册rproc subdevice。
remoteproc子系统的SRM Device初始化:
rproc_srm_dev_driver
rproc_srm_dev_probe
rproc_srm_dev_clocks_get
rproc_srm_dev_regus_get
rproc_srm_dev_irqs_get
rproc_srm_core_register_notifier
component_add--添加component,操作函数为rproc_srm_dev_ops。
Linux内核中的component框架是一种设计用于管理设备和驱动程序加载顺序的机制,特别是在处理具有复杂依赖关系的驱动时非常有用。
Component框架包含两种基本概念:master和component。Master通常被视为一个“超级设备”,负责管理一组普通设备(component)。Component是被master管理的普通设备,需要先于master初始化。
在component架构中,master和component通过特定的操作集(ops)进行交互。Master负责统筹component的加载顺序,而component则包含关键步骤的回调。
3.2 remoteproc API
struct rproc是remoteproc框架中对一个Remote Processor的抽象:
struct rproc { struct list_head node; struct iommu_domain *domain; const char *name; char *firmware;--对应固件名称。 void *priv; struct rproc_ops *ops; struct device dev; atomic_t power; unsigned int state; struct mutex lock; struct dentry *dbg_dir; struct list_head traces; int num_traces; struct list_head carveouts; struct list_head mappings; u32 bootaddr;--rproc执行的第一条指令地址。 struct list_head rvdevs; struct list_head subdevs; struct idr notifyids; int index; struct work_struct crash_handler; unsigned int crash_cnt; bool recovery_disabled; int max_notifyid; struct resource_table *table_ptr; struct resource_table *cached_table; size_t table_sz; bool has_iommu; bool auto_boot; struct list_head dump_segments; int nb_vdev; bool early_boot; };
struct rproc_ops是特定remoteproc设备的操作函数:
struct rproc_ops { int (*start)(struct rproc *rproc);--给remoteproc上电并且启动。 int (*stop)(struct rproc *rproc);--给remoteproc下电。 void (*kick)(struct rproc *rproc, int vqid); void * (*da_to_va)(struct rproc *rproc, u64 da, int len); int (*parse_fw)(struct rproc *rproc, const struct firmware *fw); int (*handle_rsc)(struct rproc *rproc, u32 rsc_type, void *rsc, int offset, int avail); struct resource_table *(*find_loaded_rsc_table)( struct rproc *rproc, const struct firmware *fw); int (*load)(struct rproc *rproc, const struct firmware *fw);--加载固件到内存,给remoteproc使用。 int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);--对固件进行合理性检查。 u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);--获取启动地址。 };
remoteproc创建者相关API:
struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, const char *firmware, int len);--分配一个struct rproc,并对其进行初始化。 void rproc_put(struct rproc *rproc);--减小remoteproc引用计数。 int rproc_add(struct rproc *rproc);--将struct rproc注册到remoteproc框架中,创建remoteproc相关设备。 int rproc_del(struct rproc *rproc);--移除remoteproc设备。 void rproc_free(struct rproc *rproc);--释放struct rproc。
rproc_alloc:
rproc_alloc
device_initialize--初始化设备。
dev_set_name--设置设备名称。
rproc_crash_handler_work
rproc_add:
rproc_add
device_add--创建rproc设备。
rproc_create_debug_dir--创建特定remoteproc的调试节点。
devm_of_platform_populate
rproc_boot--在early_boot为true时调用。
rproc_trigger_auto_boot--支持autoboot的情况下调用。
rproc_boot启动远程处理器:
- 为固件申请资源。
- 对固件进行合法性检查。
- 将固件(ELF)内容加载到内存中。
- 给处理器上电。
- 释放固件资源。
- 提供监视和调试远程服务。
rproc_boot
request_firmware--根据rproc->firmware名称,到fw_path目录下查找固件并加载。参考《Linux下固件加载器Firmware Loader》。
rproc_fw_boot
rproc_fw_sanity_check--调用rproc_ops的sanity_check对固件进行基本检查。对于ELF文件则是调用rproc_elf_sanity_check()进行检查。
rproc_enable_iommu--如果存在IOMMU的话,使能IOMMU。
rproc_get_boot_addr--调用rproc_ops的get_boot_addr函数获取启动地址。对于ELF文件则是调用rproc_elf_get_boot_addr()。
rproc_parse_fw--调用rproc_ops的parse_fw函数。
rproc_handle_resources--遍历resource_table,处理rproc的resource_table中资源。
rproc_alloc_registered_carveouts--分配rproc所有注册的carveouts。
rproc_start
rproc_load_segments--调用rproc_ops的load函数。对于ELF文件则是调用rproc_elf_load_segments(),加载ELF的Segment到内存中。
rproc_find_loaded_rsc_table--调用rproc_ops的find_loaded_src_table,从固件中加载resource table。
rproc_prepare_subdevices--遍历rproc的subdevs并调用prepare函数。
rproc->ops->start--给远程处理器上电。
rproc_start_subdevices--调用subdevs的start函数。
release_firmware--释放固件相关资源。
对于vdev资源,调用rproc_handle_vdev进行处理:
rproc_handle_resources
rproc_handle_rsc--对于vendor自定义资源,调用rproc_ops的handle_rsc处理。
rproc_handle_vdev--适用于vdev类型资源。
device_register--注册vdev设备。
dma_coerce_mask_and_coherent
rproc_parse_vring
rproc_alloc_vring
rproc_add_subdev--添加subdev到remoteproc。
rproc_vdev_do_start--subdev的start函数。
rproc_add_virtio_dev
register_virtio_device--注册virtio_device。
rproc_vdev_do_stop
rproc_handle_trace
rproc_handle_devmem
rproc_handle_carveout
remoteproc使用者相关API:
int rproc_boot(struct rproc *rproc);--启动remote processor,加载固件并上电启动。 void rproc_shutdown(struct rproc *rproc);--对remote processor下电。 void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size); int rproc_coredump_add_custom_segment(struct rproc *rproc, dma_addr_t da, size_t size, void (*dumpfn)(struct rproc *rproc, struct rproc_dump_segment *segment, void *dest), void *priv);
4 STM32 remoteproc驱动
4.1 STM32 remoteproc dts
remoteproc dts定义了如下资源:
- DMA区间。
- 寄存器、复位等。
- 使用的Mailbox Channel。
- 共享系统资源。
其中Mailbox Channel使用如下:
mlahb { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; dma-ranges = <0x00000000 0x38000000 0x10000>,--分别对应DeviceAddress、BusAddress、Size。 <0x10000000 0x10000000 0x60000>, <0x30000000 0x30000000 0x60000>; m4_rproc: m4@10000000 { compatible = "st,stm32mp1-m4"; reg = <0x10000000 0x40000>, <0x30000000 0x40000>, <0x38000000 0x10000>; resets = <&scmi0_reset RST_SCMI0_MCU>; st,syscfg-holdboot = <&rcc 0x10C 0x1>; st,syscfg-tz = <&rcc 0x000 0x1>; st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>; st,syscfg-copro-state = <&tamp 0x148 0xFFFFFFFF>; st,syscfg-pdds = <&pwr_mcu 0x0 0x1>; status = "disabled"; m4_system_resources { compatible = "rproc-srm-core"; status = "disabled"; }; }; }; &m4_rproc { memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>, <&vdev0vring1>, <&vdev0buffer>;--M4所使用到的内存空间。 mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>;--remoteproc使用到的Mailbox硬件。 mbox-names = "vq0", "vq1", "shutdown"; interrupt-parent = <&exti>; interrupts = <68 1>; wakeup-source;--具备唤醒功能。 status = "okay"; }; &m4_rproc { m4_system_resources { #address-cells = <1>; #size-cells = <0>; m4_timers2: timer@40000000 { compatible = "rproc-srm-dev"; reg = <0x40000000 0x400>; clocks = <&rcc TIM2_K>; clock-names = "int"; status = "disabled"; }; m4_timers3: timer@40001000 { compatible = "rproc-srm-dev"; reg = <0x40001000 0x400>; clocks = <&rcc TIM3_K>; clock-names = "int"; status = "disabled"; }; ...
}; };
结合M4的LinkerScript:
MEMORY { m_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000298 m_text (RX) : ORIGIN = 0x10000000, LENGTH = 0x00020000 m_data (RW) : ORIGIN = 0x10020000, LENGTH = 0x00020000 m_ipc_shm (RW) : ORIGIN = 0x10040000, LENGTH = 0x00008000 }
Linux下对应的reserved-memory如下:
reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; mcuram2: mcuram2@10000000 { compatible = "shared-dma-pool"; reg = <0x10000000 0x40000>;--存放M4的m_text和m_data两块内存。 no-map; }; vdev0vring0: vdev0vring0@10040000 { compatible = "shared-dma-pool"; reg = <0x10040000 0x1000>;--和M4的共享内存。 no-map; }; vdev0vring1: vdev0vring1@10041000 { compatible = "shared-dma-pool"; reg = <0x10041000 0x1000>;--和M4的共享内存。 no-map; }; vdev0buffer: vdev0buffer@10042000 { compatible = "shared-dma-pool"; reg = <0x10042000 0x4000>;--和M4的共享内存。 no-map; }; mcuram: mcuram@30000000 { compatible = "shared-dma-pool"; reg = <0x30000000 0x40000>; no-map; }; retram: retram@38000000 { compatible = "shared-dma-pool"; reg = <0x38000000 0x10000>; no-map; }; };
4.2 remoteproc驱动(ST32MP157)
STM32MP157的remoteproc驱动:
- 分配一个struct rproc。
- 解析dts并初始化。
- 申请Mailbox。
- 将remoteproc加入到remoteproc框架中。
stm32_rproc_driver
stm32_rproc_probe
rproc_alloc--分配一个struct rproc,对应的struct rproc_ops为st_rproc_ops。
create_workqueue
stm32_rproc_parse_dt
devm_request_irq
stm32_rproc_wdg
device_init_wakeup--支持wakeup-source时,使能中断唤醒设备功能。
dev_pm_set_wake_irq--配置唤醒中断。
devm_reset_control_get_by_index
stm32_rproc_of_memory_translations--解析dma-ranges节点。
stm32_rproc_stop
mbox_send_message--通过STM32_MBX_SHUTDOWN Channel发送关闭M4消息。
stm32_rproc_request_mbox--初始化rproc相关Mailbox Channel。
rproc_add--将M4的remoteproc注册到remoteproc框架中,创建了设备以及debugfs调试节点。
stm32_rproc_remove
rproc_shutdown
rproc_del
stm32_rproc_free_mbox
destroy_workqueue
dev_pm_clear_wake_irq
device_init_wakeup
rproc_free
st_rproc_ops提供了平台特有的remoteproc操作函数集:
static struct rproc_ops st_rproc_ops = { .start = stm32_rproc_start, .stop = stm32_rproc_stop, .kick = stm32_rproc_kick, .load = stm32_rproc_elf_load_segments, .parse_fw = stm32_rproc_parse_fw, .find_loaded_rsc_table = stm32_rproc_elf_find_loaded_rsc_table, .sanity_check = stm32_rproc_elf_sanity_check, .get_boot_addr = stm32_rproc_elf_get_boot_addr, };
stm32_rproc_start通过SMCCC配置启动M4的寄存器:
stm32_rproc_start
stm32_rproc_add_coredump_trace
stm32_rproc_set_hold_boot--根据安全需求通过SMCCC(安全)或者regmap(非安全)配置寄存器释放复位,启动M4。
stm32_rproc_elf_load_segments根据ELF头将内容加载到内存中:
stm32_rproc_elf_load_segments
rproc_elf_load_segments
--遍历PT_LOAD段,将其加载到内存中。
stm32_rproc_kick
stm32_rproc_kick
mbox_send_message--如果需要发送,在准备好virtqueue内容之后,通知对端数据就绪;如果是接收,在处理完之后,通知对端处理完成。
4.2.1 remoteproc作为Mailbox client
stm32_rproc_request_mbox--初始化rproc相关Mailbox Channel。 mbox_request_channel_byname--根据名称获取Channel。 stm32_rproc_mb_vq_work--对于使用vring queue的Channel初始化work_struct。
rproc_vq_interrupt--告诉remoteproc其所属的id为notifyid的virtqueue有更新。
remoteproc用到的Mailbox Channel如下:
static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = { { .name = STM32_MBX_VQ0, .vq_id = STM32_MBX_VQ0_ID, .client = { .rx_callback = stm32_rproc_mb_callback, .tx_block = false, }, }, { .name = STM32_MBX_VQ1, .vq_id = STM32_MBX_VQ1_ID, .client = { .rx_callback = stm32_rproc_mb_callback, .tx_block = false, }, }, { .name = STM32_MBX_SHUTDOWN, .vq_id = -1, .client = { .tx_block = true, .tx_done = NULL, .tx_tout = 500, /* 500 ms time out */ }, } };
Mailbox Channel的读回调函数:
stm32_rproc_mb_callback
queue_work--将vq_work放到workqueue调度。
stm32_rproc_mb_vq_work
rproc_vq_interrupt
4.3 通过remoteproc启动M4
查看M4状态:
cat /sys/class/remoteproc/remoteproc0/state offline
将LED_CM4.elf固件放入/lib/firmware中,配置M4固件名称:
echo LED_CM4.elf > /sys/class/remoteproc/remoteproc0/firmware
启动M4:
echo start > /sys/class/remoteproc/remoteproc0/state
停止M4:
echo stop > /sys/class/remoteproc/remoteproc0/state