MSM平台RPM

转载https://blog.csdn.net/loongembedded/article/details/51024610

oftware Component Block Diagram

RPM(Resource Power Manager)是高通MSM平台另外加的一块芯片,虽然与AP芯片打包在一起,但其是一个独立的ARM Core。之所以加这个东西,就是要控制整个电源相关的shared resources,比如ldo,clock。负责与SMP,MPM交互进入睡眠或者唤醒整个系统。 
以下是高通文档中对各个功能模块的说明。 
这里写图片描述

  • Kernel – DALSys-based lightweight kernel
  • RPM handler – RPM handler abstracts the RPM message protocol away 
    from other software
  • Drivers – Drivers for each of the resources supported by the RPM will 
    register with RPM handler to request notification when requests are 
    received for the resource which the driver controls
  • NPA – A driver may use the Node Power Architecture (NPA) to represent 
    resources controlled by the driver
  • Clock driver – RPM clock driver directly handles aggregating requests 
    from each of the masters for any of the systemwide clock resources 
    controlled by the RPM
  • Bus arbitration driver – RPM bus arbiter driver takes bus arbiter settings 
    as requests from the different masters in the system and aggregates them 
    to represent the frequency-independent system settings
  • PMIC – RPM PMIC driver directly aggregates requests from each of the 
    masters for any of the systemwide PMIC resources controlled by the RPM
  • Watch Dog driver – Watch Dog driver is a fail-safe for incorrect or stuck 
    code
  • MPM driver – Used to program the MPM hardware block during 
    systemwide sleep
  • RPM message driver – RPM message protocol driver abstracts the RPM 
    message protocol away from other subsystem software

Messaging Masters

与RPM通过shared memory region交互进行dynamic and static resource/power management的可以有很多种。 
这个可以查看smd_type.h中的smd_channel_type。但目前看只有AP,MODEM,RIVA,TZ与RPM有交互,这个可以看message_ram_malloc()函数中的设置。 
其实也可以间接从rpm_config.c文件中的SystempData temp_config_data这个变量中看出来到底有几个部分是与RPM进行交互的。

RPM Initialization

在main.c文件中会逐个调用init_fcns[]变量中的函数进行初始化。当然也包括上面的资源的初始化。

 
  1. const init_fcn init_fcns[] =

  2. {

  3. populate_image_header,

  4. npa_init,

  5.  
  6. #if (!defined(MSM8909_STUBS) )

  7. railway_init_v2,

  8. #endif

  9. PlatformInfo_Init, /* pm_init is using PlatformInfo APIs */

  10. pm_init,//LDO等资源的注册

  11. acc_init,

  12.  
  13. #if (!defined(MSM8909_STUBS) )

  14. railway_init_early_proxy_votes,

  15. #endif

  16. // xpu_init, /* cookie set here also indicates to SBL that railway is ready */

  17. (init_fcn)Clock_Init, //Clock资源的注册

  18. __init_stack_chk_guard,

  19. ddr_init,

  20. smem_init,

  21. init_smdl,

  22. version_init, /* Needs to be after smem_init */

  23. rpmserver_init,

  24. rpm_server_init_done,

  25. railway_init_proxies_and_pins,

  26.  
  27. #if (!defined(MSM8909_STUBS) )

  28. rbcpr_init,

  29. #endif

  30. svs_init,

  31. vmpm_init,

  32. sleep_init,

  33.  
  34. #if (!defined(MSM8909_STUBS) )

  35. QDSSInit,

  36. #endif

  37. exceptions_enable,

  38. swevent_qdss_init,

  39. icb_init,

  40.  
  41. #if (!defined(MSM8909_STUBS) )

  42. debug_init,

  43. system_db_init,

  44. zqcal_task_init,

  45. #endif

  46. rpm_settling_timer_init,

  47. gpio_toggle_init,

  48. rpm_set_changer_common_init,

  49. }

pm_init : LDO等资源的注册,然后接收rpm_message。接收rpm_message的部分高通代码没有给,所以看不到,但从rpm log来看,是有接收处理并反馈的过程的。

  1. 107.764902: rpm_message_received (master: "APSS") (message id: 723)

  2. 107.764908: rpm_svs (mode: RPM_SVS_FAST) (reason: imminent processing)

  3. 107.764924: rpm_svs (mode: RPM_SVS_FAST) (reason: imminent processing)

  4. 107.764939: rpm_process_request (master: "APSS") (resource type: ldoa) (id: 14)

  5. 107.764942: rpm_xlate_request (resource type: ldoa) (resource id: 14)

  6. 107.764946: rpm_apply_request (resource type: ldoa) (resource id: 14)

  7. 107.765033: rpm_send_message_response (master: "APSS")

  • ldoa对应的resource type为RPM_LDO_A_REQ。这个在pm_rom_device_init()里的pm_rpm_ldo_register_resources(RPM_LDO_A_REQ, num_of_ldoa); 这里被注册,所以看进去最后xlate和apply最终都会被pm_rpm_ldo_translation()和pm_rpm_ldo_apply进行处理。pm_rpm_ldo_tranlation()读取kvp内容,pm_rpm_ldo_apply最终把request的内容设置上去。
  1. kernel端在msm-pm8916-rpm-regulator.dtsi文件中定如下ldo内容

2. (init_fcn)Clock_Init:Clock资源的注册,这个过程和上面的也差不多 
3. rpmserver_init:启动接收message的进程

比如要设置LDO3的电压和电流,kvp的内容如下:

  1. {

  2. “req\0” : {

  3. {“rsrc” : “ldo\0”}

  4. {“id” : 3}

  5. {“set” : 0}

  6. {“data” : {

  7. {“uv\0\0” : 1100000}

  8. {“mA\0\0” : 130}

  9. }

  10. }

  11. }

  12. }

RPM log里边显示是APSS,这个在kernel里边也是有设置的,在msm-pm8916-rpm-regulator.dtsi里的

 
  1. rpm-regulator-ldoa14 {

  2. compatible = "qcom,rpm-smd-regulator-resource";

  3. qcom,resource-name = "ldoa";

  4. qcom,resource-id = <14>;

  5. qcom,regulator-type = <0>;

  6. qcom,hpm-min-load = <5000>;

  7. status = "disabled";

  8.  
  9. regulator-l14 {

  10. compatible = "qcom,rpm-smd-regulator";

  11. regulator-name = "8916_l14";

  12. qcom,set = <3>;

  13. status = "disabled";

  14. };

  15. };

MSM的retulator相关的驱动会读取这个,如果有需要发请求的话,会根据这个dtsi的内容,组织kvp,然后通过rpm_request通道发过去。 
rpm_request通道在rpm里边打开的地方是在rpm_handler.cpp里边的Hander::init()函数

 
  1. void Handler::init()

  2. {

  3. smdlPort_ = smdl_open("rpm_requests",

  4. rpm->ees[ee_].edge,

  5. SMDL_OPEN_FLAGS_MODE_PACKET,

  6. rpm->ees[ee_].smd_fifo_sz,

  7. (smdl_callback_t)rpm_smd_handler,

  8. this);

  9. }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在对应的kernel端也有打开,可以参考msm8916.dtsi文件里边的设置。

 
  1. rpm_bus: qcom,rpm-smd {

  2. compatible = "qcom,rpm-smd";

  3. rpm-channel-name = "rpm_requests";

  4. rpm-channel-type = <15>; /* SMD_APPS_RPM */

  5. };

  • 1
  • 2
  • 3
  • 4
  • 5

以下是注册资源的函数和资源的类型: 
所有的资源都通过rpm_register_resource()函数注册。这些资源包括clock, ldo等。具体可以看下面的类型定义。

 
  1. typedef enum

  2. {

  3. RPM_TEST_REQ = 0x74736574, // 'test' in little endiant

  4. RPM_CLOCK_0_REQ = 0x306b6c63, // 'clk0' in little endian; misc clocks [CXO, QDSS, dcvs.ena]

  5. RPM_CLOCK_1_REQ = 0x316b6c63, // 'clk1' in little endian; bus clocks [pcnoc, snoc, sysmmnoc]

  6. RPM_CLOCK_2_REQ = 0x326b6c63, // 'clk2' in little endian; memory clocks [bimc]

  7. RPM_CLOCK_QPIC_REQ = 0x63697071, // 'qpic' in little endian; clock [qpic]

  8. RPM_BUS_SLAVE_REQ = 0x766c7362, // 'bslv' in little endian

  9. RPM_BUS_MASTER_REQ = 0x73616d62, // 'bmas' in little endian

  10. RPM_BUS_SPDM_CLK_REQ = 0x63707362, // 'bspc' in little endian

  11. RPM_BUS_MASTER_LATENCY_REQ = 0x74616c62, // 'blat' in little endian

  12. RPM_SMPS_A_REQ = 0x61706D73, // 'smpa' in little endian

  13. RPM_LDO_A_REQ = 0x616F646C, // 'ldoa' in little endian

  14. RPM_NCP_A_REQ = 0x6170636E, // 'ncpa' in little endian

  15. RPM_VS_A_REQ = 0x617376, // 'vsa' in little endian

  16. RPM_CLK_BUFFER_A_REQ = 0x616B6C63, // 'clka' in little endian

  17. RPM_BOOST_A_REQ = 0x61747362, // 'bsta' in little endian

  18. RPM_SMPS_B_REQ = 0x62706D73, // 'smpb' in little endian

  19. RPM_LDO_B_REQ = 0x626F646C, // 'ldob' in little endian

  20. RPM_NCP_B_REQ = 0x6270636E, // 'ncpb' in little endian

  21. RPM_VS_B_REQ = 0x627376, // 'vsb' in little endian

  22. RPM_CLK_BUFFER_B_REQ = 0x626B6C63, // 'clk' in little endian

  23. RPM_BOOST_B_REQ = 0x62747362, // 'bstb' in little endian

  24. RPM_SWEVENT_REQ = 0x76657773, // 'swev' in little endian

  25. RPM_OCMEM_POWER_REQ = 0x706d636f, // 憃cmp?in little endian

  26. RPM_RBCPR_REQ = 0x727063, // 'cpr'in little endian

  27. RPM_GPIO_TOGGLE_REQ = 0x6F697067, // 'gpio' in little endian:[gpio0,gpio1,gpio2]

  28. } rpm_resource_type;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

RPM Resouce Registration

  1. rpm_register_resource():
 
  1. Gpio_toggle.c (x:\j3_ctc\nhlos\rpm_proc\core\power\rpm\common): rpm_register_resource(RPM_GPIO_TOGGLE_REQ, 3, sizeof(gpio_toggle_inrep), gpio_toggle_xlate, gpio_toggle_apply, 0);

  2.  
  3. Icb_rpm_spdm_req.c (x:\j3_ctc\nhlos\rpm_proc\core\buses\icb\src\common): rpm_register_resource( RPM_BUS_SPDM_CLK_REQ,

  4.  
  5. Ocmem_resource.c (x:\j3_ctc\nhlos\rpm_proc\core\power\ocmem\src): rpm_register_resource(RPM_OCMEM_POWER_REQ, 3, sizeof(rpm_ocmem_vote_int_rep), rpm_ocmem_xlate, rpm_ocmem_apply, 0);

  6.  
  7. Pm_rpm_boost_trans_apply.c (x:\j3_ctc\nhlos\rpm_proc\core\systemdrivers\pmic\npa\src\rpm): rpm_register_resource(resourceType, num_npa_resources + 1 , sizeof(pm_npa_boost_int_rep), pm_rpm_boost_translation, pm_rpm_boost_apply, (void *)boost_data);

  8. Pm_rpm_clk_buffer_trans_apply.c (x:\j3_ctc\nhlos\rpm_proc\core\systemdrivers\pmic\npa\src\rpm): rpm_register_resource(resourceType, num_npa_resources + 1,

  9.  
  10. Pm_rpm_ldo_trans_apply.c (x:\j3_ctc\nhlos\rpm_proc\core\systemdrivers\pmic\npa\src\rpm): rpm_register_resource(resourceType, num_npa_resources + 1, sizeof(pm_npa_ldo_int_rep), pm_rpm_ldo_translation, pm_rpm_ldo_apply, (void *)ldo_data);

  11.  
  12. Pm_rpm_smps_trans_apply.c (x:\j3_ctc\nhlos\rpm_proc\core\systemdrivers\pmic\npa\src\rpm): rpm_register_resource(resourceType, num_npa_resources + 1, sizeof(pm_npa_smps_int_rep), pm_rpm_smps_translation, pm_rpm_smps_apply, (void *)smps_data);

  13.  
  14. Pm_rpm_vs_trans_apply.c (x:\j3_ctc\nhlos\rpm_proc\core\systemdrivers\pmic\npa\src\rpm): rpm_register_resource(resourceType, num_npa_resources + 1, sizeof(pm_npa_vs_int_rep), pm_rpm_vs_translation, pm_rpm_vs_apply, (void *)vs_data);

  15.  
  16. Rpmserver.cpp (x:\j3_ctc\nhlos\rpm_proc\core\power\rpm\server):void rpm_register_resource

  17.  
  18. Rpmserver.h (x:\j3_ctc\nhlos\rpm_proc\core\api\power):void rpm_register_resource

  19.  
  20. Rpm_npa.cpp (x:\j3_ctc\nhlos\rpm_proc\core\power\rpm\server): rpm_register_resource(resource, num_npa_resources, sizeof(npa_request_data_t), rpm_npa_xlate, rpm_npa_apply, adapter);

  21.  
  22. Rpm_npa.cpp (x:\j3_ctc\nhlos\rpm_proc\core\power\rpm\server): rpm_register_resource(resource, num_npa_resources, sizeof(npa_request_data_t), rpm_npa_xlate, rpm_npa_apply, adapter);

  23.  
  24. Rpm_test_resource.cpp (x:\j3_ctc\nhlos\rpm_proc\core\power\rpm\server): rpm_register_resource(RPM_TEST_REQ, 1, 4, rpm_test_xlate, rpm_test_apply, 0);

  25.  
  26. Swevent.c (x:\j3_ctc\nhlos\rpm_proc\core\power\rpm\common): rpm_register_resource(RPM_SWEVENT_REQ, 1, sizeof(swevent_inrep), rpm_swevent_xlate, rpm_trace_control, 0);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

所有的资源都是通过rpm_register_resource函数注册。可以看到像RPM_BUS_SPDM_CLK_REQ,RPM_GPIO_TOGGLE_REQ都是通过这个接口直接注册的。

  1. NPA相关也是通过rpm_register_resource注册,但是用了NPA的driver去实际注册资源和使用资源。 
    NPA相关的两个注册接口是:rpm_create_npa_adapter(),rpm_create_npa_settling_adapter()。 
    可以看到这两个就注册了三个CLOCK相关的资源,
 
  1. ---- rpm_create_npa_adapter Matches (8 in 5 files) ----

  2. ClockRPM.c : clk0_adapter = rpm_create_npa_adapter(RPM_CLOCK_0_REQ, 3); // Misc clocks: [CXO, QDSS, dcvs.ena]

  3. ClockRPM.c : clk1_adapter = rpm_create_npa_adapter(RPM_CLOCK_1_REQ, 2); // Bus clocks: [pcnoc, snoc]

  4. Rpmserver.h :rpm_npa_adapter rpm_create_npa_adapter(rpm_resource_type resource, unsigned num_npa_resources);

  5. Rpm_npa.cpp :rpm_npa_adapter rpm_create_npa_adapter(rpm_resource_type resource, unsigned num_npa_resources)

  6.  
  7. ---- rpm_create_npa_settling_adapter Matches (5 in 5 files) ----

  8. ClockRPM.c : clk2_adapter = rpm_create_npa_settling_adapter(RPM_CLOCK_2_REQ, 1); // Memory clocks: [bimc ]

  9. Rpmserver.h :rpm_npa_adapter rpm_create_npa_settling_adapter(rpm_resource_type resource, unsigned num_npa_resources);

  10. Rpm_npa.cpp :rpm_npa_adapter rpm_create_npa_settling_adapter(rpm_resource_type resource, unsigned num_npa_resources)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

NPA client的创建函数是:npa_create_sync_client() 
NPA clien request的函数是:npa_issue_required_request() 
NPA client创建并request这个资源,必须要适用像下面这样的npa node。这个可以直接像下面这样定义。然后再使用。步骤如下:

1.定义npa node definition

 
  1. static npa_resource_definition sleep_uber_resource[] =

  2. {

  3. {

  4. "/sleep/uber", /* Name */

  5. "on/off", /* Units */

  6. 0x7, /* Max State */

  7. &npa_or_plugin, /* Plugin */

  8. NPA_RESOURCE_DEFAULT, /* Attributes */

  9. NULL, /* User Data */

  10. }

  11. };

  12.  
  13. npa_node_definition sleep_uber_node =

  14. {

  15. "/node/sleep/uber", /* name */

  16. sleep_uber_driver, /* driver_fcn */

  17. NPA_NODE_DEFAULT, /* attributes */

  18. NULL, /* data */

  19. 0, NULL, /* dependency count, dependency list */

  20. NPA_ARRAY(sleep_uber_resource)

  21. };

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2.npa_define_node(&sleep_uber_node, initial_state, NULL),调用这个函数初始化这个NPA node 
3.创建Client

 
  1. uber_node_handle = npa_create_sync_client("/sleep/uber",

  2. "sleep",

  3. NPA_CLIENT_REQUIRED);

  • 1
  • 2
  • 3

4.npa_issue_required_request(uber_node_handle, request) : request

RPM Resource handle

当子系统通过share memory发送请求给RPM。RPM负责处理这些请求并设置。RPM处理请求的流程如下: 
SMD IRQ ->smd_isr()->rpm_smd_handler() [在Handler::init函数中注册的,rpm_request channel的SMD处理函数]->Handler::queue()->schedule_me()->Handler::execute_until()->Handler::processMessage()->resource_ee_request()->发到每个资源注册xlate然后再调用apply等

下面以高通控制DDR频率(BIMC)的过程为例,看一下kernel这边怎么发送请求给RPM的。 
device tree设置如下:

 
  1. cpubw: qcom,cpubw@0 {

  2. reg = <0 4>;

  3. compatible = "qcom,devbw";

  4. governor = "cpufreq";

  5. qcom,src-dst-ports = <1 512>;

  6. qcom,active-only;

  7. qcom,bw-tbl = //这个频率表对应RPM中BIMC频率表,不过删掉了50MHz和9.6MHz。

  8. /* 73 9.60 MHz */

  9. /* 381 50MHz */

  10. < 762 /* 100 MHz */>,

  11. < 1525 /* 200 MHz */>,

  12. < 3051 /* 400 MHz */>,

  13. < 4066 /* 533 MHz */>;

  14. };

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

对应的kernel代码在devfeq_devbw.c文件。根据算法算出当前应该设定的ddr总线频率之后,最后通过以下顺序发消息给RPM。这里暂时不讨论按什么规则选择需要的频率的,只看按什么路径发频率给RPM的。 
set_bw()->msm_bus_scale_client_update_request()->update_request_adhoc()[msm_bus_arb_adhoc.c]->update_path()->msm_bus_commit_data()->flush_bw_data()->send_rpm_msg()->msm_rpm_send_message()

System Sleep Overview

RPM除了对Clock和LDO等资源的管理之外,还管理整个系统睡眠。 
这里写图片描述

可以看到睡眠并不是通过shared memory发送消息给RPM的,而是子系统通过设置对应的SPM,SPM触发RPM相应的中断来完成的。 
kernel这边设置spm的模式的设置如下:

 
  1. enum {

  2. MSM_SPM_MODE_DISABLED,

  3. MSM_SPM_MODE_CLOCK_GATING,

  4. MSM_SPM_MODE_RETENTION,

  5. MSM_SPM_MODE_GDHS,

  6. MSM_SPM_MODE_POWER_COLLAPSE,//设置成这个状态之后,对应的SPM应该就会触发RPM的对应的shutdown中断

  7. MSM_SPM_MODE_NR

  8. };

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

rpm中,与哪几个子系统传递接收message,然后和哪几个子系统的SPM进行交互,交互的中断号是多少的设置如下:

 
  1. static SystemData temp_config_data =

  2. {

  3. .num_ees = 4, // 4 EE's, [apps, modem, pronto, tz]

  4. .ees = (EEData[] ) {

  5. [0] = {

  6. .edge = SMD_APPS_RPM,

  7. .smd_fifo_sz = 1024,

  8. .ee_buflen = 256,

  9. .priority = 4,

  10. .wakeupInt = (1 << 5) | (1 << 7),

  11. .spm = {

  12. .numCores = 1,

  13. .bringupInts = (unsigned[]) { 15 },

  14. .bringupAcks = (unsigned[]) { 20 },

  15. .shutdownInts = (unsigned[]) { 14 },//这个应该是SPM睡眠的中断号

  16. .shutdownAcks = (unsigned[]) { 4 },

  17. },

  18. },

  19. [1] = {

  20. .edge = SMD_MODEM_RPM,

  21. .smd_fifo_sz = 1024,

  22. .ee_buflen = 1024,

  23. .priority = 2,

  24. .wakeupInt = (1 << 13) | (1 << 15),

  25. .spm = {

  26. .numCores = 1,

  27. .bringupInts = (unsigned[]) { 25 },

  28. .bringupAcks = (unsigned[]) { 22 },

  29. .shutdownInts = (unsigned[]) { 24 },

  30. .shutdownAcks = (unsigned[]) { 6 },

  31. },

  32. },

  33. [2] = {

  34. .edge = SMD_RIVA_RPM,

  35. .smd_fifo_sz = 1024,

  36. .ee_buflen = 256,

  37. .priority = 1,

  38. .wakeupInt = (1 << 17) | (1 << 19),

  39. .spm = {

  40. .numCores = 1,

  41. .bringupInts = (unsigned[]) { 31 },

  42. .bringupAcks = (unsigned[]) { 23 },

  43. .shutdownInts = (unsigned[]) { 30 },

  44. .shutdownAcks = (unsigned[]) { 7 },

  45. },

  46. },

  47. [3] = {

  48. .edge = SMD_RPM_TZ,

  49. .smd_fifo_sz = 1024,

  50. .ee_buflen = 256,

  51. .priority = 5,

  52. .wakeupInt = 0,

  53. .spm = {

  54. .numCores = 0,

  55. .bringupInts = (unsigned[]) { 31 },

  56. .bringupAcks = (unsigned[]) { 23 },

  57. .shutdownInts = (unsigned[]) { 30 },

  58. .shutdownAcks = (unsigned[]) { 7 },

  59. },

  60. },

  61. },

  62. .supported_classes = SUPPORTED_CLASSES,

  63. .supported_resources = SUPPORTED_RESOURCES,

  64. .classes = (ResourceClassData[SUPPORTED_CLASSES]) { 0 },

  65. .resources = (ResourceData[SUPPORTED_RESOURCES]) { 0 },

  66. .resource_seeds = (int16_t[SUPPORTED_RESOURCES]) { 0 },

  67. }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

但还不知道这些中断号到底怎么来的,,中断的个数都是1个,,这个怎么来的也还是不知~ 
这些内容在rpm_spm_init中会读取,然后设置中断。shutdownISR对应的中断处理函数为rpm_spm_shutdown_high_isr。 
这个中断函数会调用rpm_spm_state_machine()处理RPM状态机,进入或者阻止进入睡眠模式等。

 
  1. void rpm_spm_state_machine(unsigned ee, rpm_spm_entry_reason reason)

  2. {

  3. INTLOCK();

  4.  
  5. bool changed_state = false;

  6. EEData *ee_state = &(rpm->ees[ee]);

  7. SetChanger *changer = ee_state->changer;

  8.  
  9. do

  10. {

  11. switch(ee_state->subsystem_status)

  12. {

  13. case SPM_AWAKE:

  14. changed_state = FALSE;

  15.  
  16. if(0 == ee_state->num_active_cores)

  17. {//等待所有的core都进入睡眠!!之后才能走到GOITONG TO SLEEP状态!!

  18. SPM_CHANGE_STATE(SPM_GOING_TO_SLEEP);

  19. }

  20. else

  21. {

  22. // We're awake, so make sure we keep up with any incoming bringup reqs.

  23. rpm_acknowledge_spm_handshakes(ee);

  24. }

  25. break;

  26.  
  27. case SPM_GOING_TO_SLEEP:

  28. if(changed_state)

  29. {

  30. // check for scheduled wakeup

  31. uint64_t deadline = 0;

  32.  
  33. if(! rpm_get_wakeup_deadline(ee, deadline))

  34. {

  35. deadline = 0;

  36. }

  37. changer->setWakeTime (deadline);

  38. // enqueue immediate set transition to sleep

  39. changer->enqueue(RPM_SLEEP_SET, 0);

  40. }

  41. changed_state = FALSE;

  42.  
  43. // When we've finished selecting the sleep set, we're officially asleep.

  44. if((SPM_TRANSITION_COMPLETE == reason) && (RPM_SLEEP_SET == changer->currentSet()))

  45. {

  46. SPM_CHANGE_STATE(SPM_SLEEPING);

  47. }

  48. // However, we might get a wakeup request before we've made it all the way to sleep.

  49. if(SPM_BRINGUP_REQ == reason)

  50. {

  51. // Set the preempt flag; this will force the set change to recycle if

  52. // it's currently running. It will notice the processor has woken up

  53. // and stop performing its work.

  54. theSchedule().preempt();

  55. }

  56. break;

  57.  
  58. case SPM_SLEEPING:

  59. if(changed_state)

  60. {

  61. // check for scheduled wakeup

  62. uint64_t deadline = changer->getWakeTime ();

  63.  
  64. // enqueue scheduled wakeup request

  65. changer->enqueue(RPM_ACTIVE_SET, deadline);

  66. }

  67. changed_state = FALSE;

  68.  
  69. if(ee_state->num_active_cores > 0)

  70. {

  71. SPM_CHANGE_STATE(SPM_WAKING_UP);

  72. }

  73. break;

  74.  
  75. case SPM_WAKING_UP:

  76. if(changed_state)

  77. {

  78. // work our way back to the active set

  79. if(RPM_SLEEP_SET == changer->currentSet() || changer->inTransition())

  80. {

  81. changer->enqueue(RPM_ACTIVE_SET, 0);

  82. }

  83. }

  84. changed_state = FALSE;

  85.  
  86. // check for completion

  87. if(RPM_ACTIVE_SET == changer->currentSet() && !changer->inTransition())

  88. {

  89. SPM_CHANGE_STATE(SPM_AWAKE);

  90. }

  91. break;

  92. }

  93. } while(changed_state);

  94.  
  95. INTFREE();

  96. }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

最终会跑到SetChanger::enqueue()里。

RPM与MPM的交互

这里写图片描述

RPM 状态读取

rpm的状态读取,在/d/rpm_stats里边可以读到AP这边设置的几个APSS,MPSS,PRONTO等几个对应的RPM状态。 
读的内容当然也是从rpm_request这个shared memory里边读的。相应的设置在msm8916-pm.dts文件里边有。

 
  1. qcom,rpm-stats@29dba0 {

  2. compatible = "qcom,rpm-stats";

  3. reg = <0x29dba0 0x1000>;

  4. reg-names = "phys_addr_base";

  5. qcom,sleep-stats-version = <2>;

  6. };

  7. qcom,rpm-master-stats@60150 {

  8. compatible = "qcom,rpm-master-stats";

  9. reg = <0x60150 0x2030>;

  10. qcom,masters = "APSS", "MPSS", "PRONTO";

  11. qcom,master-stats-version = <2>;

  12. qcom,master-offset = <4096>;

  13. };

  14. qcom,rpm-rbcpr-stats@0x29daa0 {

  15. compatible = "qcom,rpmrbcpr-stats";

  16. reg = <0x29daa0 0x1a0000>;

  17. qcom,start-offset = <0x190010>;

  18. };

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值