MSM平台RPM_resource power manager-程序员宅基地

技术标签: rpm&power  power  rpm  

FROM:https://blog.csdn.net/hongzg1982/article/details/56516147

Software 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[]变量中的函数进行初始化。当然也包括上面的资源的初始化。

const init_fcn init_fcns[] =
{
  populate_image_header,
  npa_init,

#if (!defined(MSM8909_STUBS) )
  railway_init_v2,
#endif
  PlatformInfo_Init, /* pm_init is using PlatformInfo APIs */
  pm_init,//LDO等资源的注册
  acc_init,

#if (!defined(MSM8909_STUBS) )
  railway_init_early_proxy_votes,
 #endif
  // xpu_init, /* cookie set here also indicates to SBL that railway is ready */
  (init_fcn)Clock_Init, //Clock资源的注册
  __init_stack_chk_guard,
  ddr_init,
  smem_init,
  init_smdl,
  version_init,     /* Needs to be after smem_init */
  rpmserver_init,
  rpm_server_init_done,
  railway_init_proxies_and_pins,

#if (!defined(MSM8909_STUBS) )
  rbcpr_init,
#endif  
  svs_init,
  vmpm_init,
  sleep_init,

#if (!defined(MSM8909_STUBS) )
  QDSSInit,
 #endif
  exceptions_enable,
  swevent_qdss_init,
  icb_init,

#if (!defined(MSM8909_STUBS) )
  debug_init,
  system_db_init,
  zqcal_task_init,
#endif
  rpm_settling_timer_init,
  gpio_toggle_init,
  rpm_set_changer_common_init,
}


pm_init : LDO等资源的注册,然后接收rpm_message。接收rpm_message的部分高通代码没有给,所以看不到,但从rpm log来看,是有接收处理并反馈的过程的。
107.764902: rpm_message_received (master: "APSS") (message id: 723)
107.764908: rpm_svs (mode: RPM_SVS_FAST) (reason: imminent processing)
107.764924: rpm_svs (mode: RPM_SVS_FAST) (reason: imminent processing)
107.764939: rpm_process_request (master: "APSS") (resource type: ldoa) (id: 14)
107.764942: rpm_xlate_request (resource type: ldoa) (resource id: 14)
107.764946: rpm_apply_request (resource type: ldoa) (resource id: 14)
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的内容设置上去。
kernel端在msm-pm8916-rpm-regulator.dtsi文件中定如下ldo内容
1
2
2. (init_fcn)Clock_Init:Clock资源的注册,这个过程和上面的也差不多 
3. rpmserver_init:启动接收message的进程

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

{
     “req\0” : {
         {“rsrc” : “ldo\0”}
         {“id” : 3}
         {“set” : 0}
         {“data” : {
             {“uv\0\0” : 1100000}
             {“mA\0\0” : 130}
         }
         }
     }
}

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

    rpm-regulator-ldoa14 {
        compatible = "qcom,rpm-smd-regulator-resource";
        qcom,resource-name = "ldoa";
        qcom,resource-id = <14>;
        qcom,regulator-type = <0>;
        qcom,hpm-min-load = <5000>;
        status = "disabled";

        regulator-l14 {
            compatible = "qcom,rpm-smd-regulator";
            regulator-name = "8916_l14";
            qcom,set = <3>;
            status = "disabled";
        };
    };

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

void Handler::init()
{
    smdlPort_ = smdl_open("rpm_requests",
                          rpm->ees[ee_].edge,
                          SMDL_OPEN_FLAGS_MODE_PACKET,
                          rpm->ees[ee_].smd_fifo_sz,
                          (smdl_callback_t)rpm_smd_handler,
                          this);
}

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

        rpm_bus: qcom,rpm-smd {
                compatible = "qcom,rpm-smd";
                rpm-channel-name = "rpm_requests";
                rpm-channel-type = <15>; /* SMD_APPS_RPM */
        };

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

typedef enum
{
    RPM_TEST_REQ               = 0x74736574, // 'test' in little endiant
    RPM_CLOCK_0_REQ            = 0x306b6c63, // 'clk0' in little endian; misc clocks [CXO, QDSS, dcvs.ena]
    RPM_CLOCK_1_REQ            = 0x316b6c63, // 'clk1' in little endian; bus clocks [pcnoc, snoc, sysmmnoc]
    RPM_CLOCK_2_REQ            = 0x326b6c63, // 'clk2' in little endian; memory clocks [bimc]
    RPM_CLOCK_QPIC_REQ         = 0x63697071, // 'qpic' in little endian; clock [qpic]
    RPM_BUS_SLAVE_REQ          = 0x766c7362, // 'bslv' in little endian
    RPM_BUS_MASTER_REQ         = 0x73616d62, // 'bmas' in little endian
    RPM_BUS_SPDM_CLK_REQ       = 0x63707362, // 'bspc' in little endian
    RPM_BUS_MASTER_LATENCY_REQ = 0x74616c62, // 'blat' in little endian
    RPM_SMPS_A_REQ             = 0x61706D73, // 'smpa' in little endian
    RPM_LDO_A_REQ              = 0x616F646C, // 'ldoa'  in little endian
    RPM_NCP_A_REQ              = 0x6170636E, // 'ncpa'  in little endian
    RPM_VS_A_REQ               = 0x617376,   // 'vsa'   in little endian
    RPM_CLK_BUFFER_A_REQ       = 0x616B6C63, // 'clka' in little endian
    RPM_BOOST_A_REQ            = 0x61747362, // 'bsta' in little endian
    RPM_SMPS_B_REQ             = 0x62706D73, // 'smpb' in little endian
    RPM_LDO_B_REQ              = 0x626F646C, // 'ldob'  in little endian
    RPM_NCP_B_REQ              = 0x6270636E, // 'ncpb'  in little endian
    RPM_VS_B_REQ               = 0x627376,   // 'vsb'   in little endian
    RPM_CLK_BUFFER_B_REQ       = 0x626B6C63, // 'clk' in little endian
    RPM_BOOST_B_REQ            = 0x62747362, // 'bstb' in little endian
    RPM_SWEVENT_REQ            = 0x76657773, // 'swev' in little endian
    RPM_OCMEM_POWER_REQ        = 0x706d636f, // 憃cmp?in little endian
    RPM_RBCPR_REQ              = 0x727063,   // 'cpr'in little endian
    RPM_GPIO_TOGGLE_REQ        = 0x6F697067, // 'gpio' in little endian:[gpio0,gpio1,gpio2]
} rpm_resource_type;

RPM Resouce Registration

rpm_register_resource():
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);

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

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

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

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

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

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

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

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

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

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

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

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

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

NPA相关也是通过rpm_register_resource注册,但是用了NPA的driver去实际注册资源和使用资源。 
NPA相关的两个注册接口是:rpm_create_npa_adapter(),rpm_create_npa_settling_adapter()。 
可以看到这两个就注册了三个CLOCK相关的资源,
---- rpm_create_npa_adapter Matches (8 in 5 files) ----
ClockRPM.c :  clk0_adapter = rpm_create_npa_adapter(RPM_CLOCK_0_REQ, 3); // Misc clocks: [CXO, QDSS, dcvs.ena]
ClockRPM.c :  clk1_adapter = rpm_create_npa_adapter(RPM_CLOCK_1_REQ, 2); // Bus clocks: [pcnoc, snoc]
Rpmserver.h :rpm_npa_adapter rpm_create_npa_adapter(rpm_resource_type resource, unsigned num_npa_resources);
Rpm_npa.cpp :rpm_npa_adapter rpm_create_npa_adapter(rpm_resource_type resource, unsigned num_npa_resources)

---- rpm_create_npa_settling_adapter Matches (5 in 5 files) ----
ClockRPM.c :  clk2_adapter = rpm_create_npa_settling_adapter(RPM_CLOCK_2_REQ, 1); // Memory clocks: [bimc ]
Rpmserver.h :rpm_npa_adapter rpm_create_npa_settling_adapter(rpm_resource_type resource, unsigned num_npa_resources);
Rpm_npa.cpp :rpm_npa_adapter rpm_create_npa_settling_adapter(rpm_resource_type resource, unsigned num_npa_resources)
 
NPA client的创建函数是:npa_create_sync_client() 
NPA clien request的函数是:npa_issue_required_request() 
NPA client创建并request这个资源,必须要适用像下面这样的npa node。这个可以直接像下面这样定义。然后再使用。步骤如下:

1.定义npa node definition

    static npa_resource_definition sleep_uber_resource[] = 
    { 
      {
        "/sleep/uber",        /* Name */
        "on/off",             /* Units */
        0x7,                  /* Max State */
        &npa_or_plugin,       /* Plugin */
        NPA_RESOURCE_DEFAULT, /* Attributes */
        NULL,                 /* User Data */
      }
    };

    npa_node_definition sleep_uber_node = 
    { 
      "/node/sleep/uber", /* name */
      sleep_uber_driver,  /* driver_fcn */
      NPA_NODE_DEFAULT,   /* attributes */
      NULL,               /* data */
      0, NULL,            /* dependency count, dependency list */
      NPA_ARRAY(sleep_uber_resource)
    };
 
2.npa_define_node(&sleep_uber_node, initial_state, NULL),调用这个函数初始化这个NPA node 
3.创建Client

  uber_node_handle = npa_create_sync_client("/sleep/uber",
                                            "sleep",
                                            NPA_CLIENT_REQUIRED);
 
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设置如下:

    cpubw: qcom,cpubw@0 {
        reg = <0 4>;
        compatible = "qcom,devbw";
        governor = "cpufreq";
        qcom,src-dst-ports = <1 512>;
        qcom,active-only;
        qcom,bw-tbl = //这个频率表对应RPM中BIMC频率表,不过删掉了50MHz和9.6MHz。
            /*  73 9.60 MHz */
            /* 381  50MHz */
            <  762 /* 100 MHz */>,
            < 1525 /* 200 MHz */>,
            < 3051 /* 400 MHz */>,
            < 4066 /* 533 MHz */>;
    };
 
对应的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的模式的设置如下:

enum {
    MSM_SPM_MODE_DISABLED,
    MSM_SPM_MODE_CLOCK_GATING,
    MSM_SPM_MODE_RETENTION,
    MSM_SPM_MODE_GDHS,
    MSM_SPM_MODE_POWER_COLLAPSE,//设置成这个状态之后,对应的SPM应该就会触发RPM的对应的shutdown中断
    MSM_SPM_MODE_NR
};
 
rpm中,与哪几个子系统传递接收message,然后和哪几个子系统的SPM进行交互,交互的中断号是多少的设置如下:

static SystemData temp_config_data =
{
    .num_ees = 4, // 4 EE's, [apps, modem, pronto, tz]
    .ees    = (EEData[] ) {
        [0] = {
            .edge      = SMD_APPS_RPM,
            .smd_fifo_sz = 1024,
            .ee_buflen = 256,
            .priority  = 4,
            .wakeupInt = (1 << 5) | (1 << 7),
            .spm       = {
                             .numCores = 1,
                             .bringupInts  = (unsigned[]) {  15 },
                             .bringupAcks  = (unsigned[]) {  20 },
                             .shutdownInts = (unsigned[]) {  14 },//这个应该是SPM睡眠的中断号
                             .shutdownAcks = (unsigned[]) {   4 },
                         },
        },
        [1] = {
            .edge      = SMD_MODEM_RPM,
            .smd_fifo_sz = 1024,
            .ee_buflen = 1024,
            .priority  = 2,
            .wakeupInt = (1 << 13) | (1 << 15),
            .spm       = {
                             .numCores = 1,
                             .bringupInts  = (unsigned[]) { 25 },
                             .bringupAcks  = (unsigned[]) { 22 },
                             .shutdownInts = (unsigned[]) { 24 },
                             .shutdownAcks = (unsigned[]) {  6 },
                         },
        },
        [2] = {
            .edge      = SMD_RIVA_RPM,
            .smd_fifo_sz = 1024,
            .ee_buflen = 256,
            .priority  = 1,
            .wakeupInt = (1 << 17) | (1 << 19),
            .spm       = {
                             .numCores = 1,
                             .bringupInts  = (unsigned[]) { 31 },
                             .bringupAcks  = (unsigned[]) { 23 },
                             .shutdownInts = (unsigned[]) { 30 },
                             .shutdownAcks = (unsigned[]) {  7 },
                         },
        },
        [3] = {
            .edge      = SMD_RPM_TZ,
            .smd_fifo_sz = 1024,
            .ee_buflen = 256,
            .priority  = 5,
            .wakeupInt = 0,
            .spm       = {
                             .numCores = 0,
                             .bringupInts  = (unsigned[]) { 31 },
                             .bringupAcks  = (unsigned[]) { 23 },
                             .shutdownInts = (unsigned[]) { 30 },
                             .shutdownAcks = (unsigned[]) {  7 },
                         },
        },
    },
    .supported_classes   = SUPPORTED_CLASSES,
    .supported_resources = SUPPORTED_RESOURCES,
    .classes             = (ResourceClassData[SUPPORTED_CLASSES]) { 0 },
    .resources           = (ResourceData[SUPPORTED_RESOURCES])    { 0 },
    .resource_seeds      = (int16_t[SUPPORTED_RESOURCES])         { 0 },
}
 
但还不知道这些中断号到底怎么来的,,中断的个数都是1个,,这个怎么来的也还是不知~ 
这些内容在rpm_spm_init中会读取,然后设置中断。shutdownISR对应的中断处理函数为rpm_spm_shutdown_high_isr。 
这个中断函数会调用rpm_spm_state_machine()处理RPM状态机,进入或者阻止进入睡眠模式等。

void rpm_spm_state_machine(unsigned ee, rpm_spm_entry_reason reason)
{
    INTLOCK();

    bool        changed_state = false;
    EEData     *ee_state      = &(rpm->ees[ee]);
    SetChanger *changer       = ee_state->changer;

    do
    {
        switch(ee_state->subsystem_status)
        {
            case SPM_AWAKE:
                changed_state = FALSE;

                if(0 == ee_state->num_active_cores) 
                {//等待所有的core都进入睡眠!!之后才能走到GOITONG TO SLEEP状态!!
                    SPM_CHANGE_STATE(SPM_GOING_TO_SLEEP);
                }
                else
                {
                    // We're awake, so make sure we keep up with any incoming bringup reqs.
                    rpm_acknowledge_spm_handshakes(ee);
                }
                break;

            case SPM_GOING_TO_SLEEP:
                if(changed_state)
                {
                    // check for scheduled wakeup
                    uint64_t deadline = 0;

                    if(! rpm_get_wakeup_deadline(ee, deadline))
                    {
                      deadline = 0;
                    }
                    changer->setWakeTime (deadline);
                    // enqueue immediate set transition to sleep
                    changer->enqueue(RPM_SLEEP_SET, 0);
                }
                changed_state = FALSE;

                // When we've finished selecting the sleep set, we're officially asleep.
                if((SPM_TRANSITION_COMPLETE == reason) && (RPM_SLEEP_SET == changer->currentSet()))
                {
                  SPM_CHANGE_STATE(SPM_SLEEPING);
                }
                // However, we might get a wakeup request before we've made it all the way to sleep.
                if(SPM_BRINGUP_REQ == reason)
                {
                    // Set the preempt flag; this will force the set change to recycle if
                    // it's currently running.  It will notice the processor has woken up
                    // and stop performing its work.
                    theSchedule().preempt();
                }
                break;

            case SPM_SLEEPING:
                if(changed_state)
                {
                    // check for scheduled wakeup
                    uint64_t deadline = changer->getWakeTime ();

                    // enqueue scheduled wakeup request
                    changer->enqueue(RPM_ACTIVE_SET, deadline);
                }
                changed_state = FALSE;

                if(ee_state->num_active_cores > 0)
                {
                    SPM_CHANGE_STATE(SPM_WAKING_UP);
                }
                break;

            case SPM_WAKING_UP:
                if(changed_state)
                {
                    // work our way back to the active set
                    if(RPM_SLEEP_SET == changer->currentSet() || changer->inTransition())
                    {
                        changer->enqueue(RPM_ACTIVE_SET, 0);
                    }
                }
                changed_state = FALSE;

                // check for completion
                if(RPM_ACTIVE_SET == changer->currentSet() && !changer->inTransition())
                {
                  SPM_CHANGE_STATE(SPM_AWAKE);
                }
                break;
        }
    } while(changed_state);

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

RPM与MPM的交互

RPM 状态读取

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

    qcom,rpm-stats@29dba0 {
        compatible = "qcom,rpm-stats";
        reg = <0x29dba0 0x1000>;
        reg-names = "phys_addr_base";
        qcom,sleep-stats-version = <2>;
    };
    qcom,rpm-master-stats@60150 {
        compatible = "qcom,rpm-master-stats";
        reg = <0x60150 0x2030>;
        qcom,masters = "APSS", "MPSS", "PRONTO";
        qcom,master-stats-version = <2>;
        qcom,master-offset = <4096>;
    };
    qcom,rpm-rbcpr-stats@0x29daa0  {
        compatible = "qcom,rpmrbcpr-stats";
        reg = <0x29daa0 0x1a0000>;
        qcom,start-offset = <0x190010>;
    };
--------------------- 
作者:honghong96 
来源:CSDN 
原文:https://blog.csdn.net/hongzg1982/article/details/56516147 
版权声明:本文为博主原创文章,转载请附上博文链接!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chenzhen1080/article/details/96571314

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文