Linux Command Topics (1) - Sensors

目录

1. Linux Command - Sensors

1.1  Example

1.1.1 x86 desktop

1.1.2 ARM server

1.2  How to install

 2. Source code

2.1 Sensors 

2.2  Linux hwmon driver

2.2.1 Definition

2.2.1.1 struct acpi_pcct_shared_memory {}

2.2.2 xgene_hwmon_driver {}

2.2.2.1 xgene_hwmon_probe(struct platform_device *pdev)

2.2.2.2 pcc_mbox_request_channel()

2.2.3 Operations

2.2.3.1 xgene_hwmon_get_cpu_pwr()

2.2.3.2 xgene_hwmon_get_io_pwr()

2.2.3.3 xgene_hwmon_get_temp()

2.2.4  xgene_hwmon_pcc_rd()

2.2.3  xgene_hwmon_pcc_rx_cb()

2.3 Low level FW Summary

3. Log

3.1 Boot Phase

3.1.1 Device register log

3.1.2  Device/Driver Binding log


1. Linux Command - Sensors

1.1  Example


1.1.1 x86 desktop

  • x86 desktop:

    # sensors
    coretemp-isa-0000
    Adapter: ISA adapter
    Package id 0:  +61.0°C  (high = +84.0°C, crit = +100.0°C)
    Core 0:        +59.0°C  (high = +84.0°C, crit = +100.0°C)
    Core 1:        +60.0°C  (high = +84.0°C, crit = +100.0°C)
    Core 2:        +61.0°C  (high = +84.0°C, crit = +100.0°C)
    Core 3:        +61.0°C  (high = +84.0°C, crit = +100.0°C)
    Core 4:        +60.0°C  (high = +84.0°C, crit = +100.0°C)
    Core 5:        +60.0°C  (high = +84.0°C, crit = +100.0°C)

    dell_smm-isa-0000
    Adapter: ISA adapter
    fan1:        2353 RPM

    acpitz-acpi-0
    Adapter: ACPI interface
    temp1:        +27.8°C  (crit = +119.0°C)

    nvme-pci-0100
    Adapter: PCI adapter
    Composite:    +46.9°C  (low  =  -0.1°C, high = +81.8°C)
                           (crit = +85.8°C)

1.1.2 ARM server

  • ARM server
    #sensors

    nvme-pci-10300
    Adapter: PCI adapter
    Composite:    +28.9°C  (low  =  -0.1°C, high = +69.8°C)
                           (crit = +79.8°C)

    apm_xgene-isa-0000
    Adapter: ISA adapter
    SoC Temperature:  +42.0°C
    CPU power:        37.27 W
    IO power:         61.75 W

    nvme-pci-10100
    Adapter: PCI adapter
    Composite:    +25.9°C  (low  = -273.1°C, high = +76.8°C)
                           (crit = +84.8°C)
    Sensor 1:     +25.9°C  (low  = -273.1°C, high = +65261.8°C)
    Sensor 2:     +46.9°C  (low  = -273.1°C, high = +65261.8°C)

    apm_xgene-isa-0000
    Adapter: ISA adapter
    SoC Temperature:  +55.0°C
    CPU power:        39.50 W
    IO power:         62.89 W

1.2  How to install

  • Fedora 38
    • sudo dnf install lm_sensors

  • Ubuntu 20.04
    • sudo apt install lm-sensors

 2. Source code

 hwmon 是用来监控Processor 本身信息, 用来获取 CPU power, CPU temp, IO Power, 通过PCC & mailbox  跟 CPU 内部监控单元交互。

 

涉及到几个知识点

1. PCCT table, PCC channel  - TBD

2. Mailbox 通信 - TBD

2.1 Sensors 

TBD

2.2  Linux hwmon driver

2.2.1 Definition

2.2.1.1 struct acpi_pcct_shared_memory {}

struct acpi_pcct_shared_memory {

    u32 signature;

    u16 command;

    u16 status;

//  u32  msg[0];

//  u32  msg[1];

//  u32  msg[2];

//  u32  msg[3];

};

2.2.2 xgene_hwmon_driver {}

#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_hwmon_acpi_match[] = {
    {"APMC0D29", XGENE_HWMON_V1},
    {"APMC0D8A", XGENE_HWMON_V2},
    {},
};
MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
#endif

static const struct of_device_id xgene_hwmon_of_match[] = {
    {.compatible = "apm,xgene-slimpro-hwmon"},
    {}
};
MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);

static struct platform_driver xgene_hwmon_driver = {
    .probe = xgene_hwmon_probe,
    .remove = xgene_hwmon_remove,
    .driver = {
        .name = "xgene-slimpro-hwmon",
        .of_match_table = xgene_hwmon_of_match,
        .acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
    },
};
module_platform_driver(xgene_hwmon_driver);

MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
MODULE_LICENSE("GPL");

2.2.2.1 xgene_hwmon_probe(struct platform_device *pdev)

1. 设备驱动进行绑定

2. 申请 PCC channel & mbox channel 进行通信, PCC shared region 是一块共享内存

     映射内存用户读写操作

static int xgene_hwmon_probe(struct platform_device *pdev)
{
    struct xgene_hwmon_dev *ctx;
    struct mbox_client *cl;
    int rc;

    ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
    if (!ctx)
        return -ENOMEM;

    ctx->dev = &pdev->dev;
    platform_set_drvdata(pdev, ctx);
    cl = &ctx->mbox_client;

    spin_lock_init(&ctx->kfifo_lock);
    mutex_init(&ctx->rd_mutex);

    rc = kfifo_alloc(&ctx->async_msg_fifo, sizeof(struct slimpro_resp_msg) *                                   

           SYNC_MSG_FIFO_SIZE, GFP_KERNEL);
    if (rc)
        return -ENOMEM;

    INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);

    /* Request mailbox channel */
    cl->dev = &pdev->dev;
    cl->tx_done = xgene_hwmon_tx_done;
    cl->tx_block = false;
    cl->tx_tout = MBOX_OP_TIMEOUTMS;
    cl->knows_txdone = false;
    if (acpi_disabled) {
        cl->rx_callback = xgene_hwmon_rx_cb;
        ctx->mbox_chan = mbox_request_channel(cl, 0);
    } else {
        struct pcc_mbox_chan *pcc_chan;
        const struct acpi_device_id *acpi_id;
        int version;

        acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,   &pdev->dev);
        if (!acpi_id) {
            rc = -EINVAL;
            goto out_mbox_free;
        }

        version = (int)acpi_id->driver_data;

        if (device_property_read_u32(&pdev->dev, "pcc-channel",    &ctx->mbox_idx)) {
            dev_err(&pdev->dev, "no pcc-channel property\n");
            rc = -ENODEV;
            goto out_mbox_free;
        }

        cl->rx_callback = xgene_hwmon_pcc_rx_cb;
        pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
 

        ctx->pcc_chan = pcc_chan;
        ctx->mbox_chan = pcc_chan->mchan;

...

       /*
         * This is the shared communication region
         * for the OS and Platform to communicate over.
         */

        ctx->comm_base_addr = pcc_chan->shmem_base_addr;
        if (ctx->comm_base_addr) {
            if (version == XGENE_HWMON_V2)
                ctx->pcc_comm_addr = (void __force *)devm_ioremap(&pdev->dev,
                                  ctx->comm_base_addr,   pcc_chan->shmem_size);
            else
                ctx->pcc_comm_addr = devm_memremap(&pdev->dev,
                   ctx->comm_base_addr, pcc_chan->shmem_size,    MEMREMAP_WB);
        } else {
            dev_err(&pdev->dev, "Failed to get PCC comm region\n");
            rc = -ENODEV;
            goto out;
        }

        /*
         * pcc_chan->latency is just a Nominal value. In reality
         * the remote processor could be much slower to reply.
         * So add an arbitrary amount of wait on top of Nominal.
         */
        ctx->usecs_lat = PCC_NUM_RETRIES * pcc_chan->latency;
    }

    ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
                               "apm_xgene",          ctx,             xgene_hwmon_groups);
    /*
     * Schedule the bottom handler if there is a pending message.
     */

    schedule_work(&ctx->workq);

    dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");

    return 0;

out:
    if (acpi_disabled)
        mbox_free_channel(ctx->mbox_chan);
    else
        pcc_mbox_free_channel(ctx->pcc_chan);
out_mbox_free:
    kfifo_free(&ctx->async_msg_fifo);

    return rc;
}
 

2.2.2.2 pcc_mbox_request_channel()

2.2.3 Operations

//drivers\hwmon\xgene-hwmon.c

#define DBG_SUBTYPE_SENSOR_READ        4
#define SENSOR_RD_MSG            0x04FFE902
#define SENSOR_RD_EN_ADDR(a)    ((a) & 0x000FFFFF)
#define PMD_PWR_REG                        0x20
#define PMD_PWR_MW_REG                0x26
#define SOC_PWR_REG                        0x21
#define SOC_PWR_MW_REG                0x27
#define SOC_TEMP_REG                       0x10

2.2.3.1 xgene_hwmon_get_cpu_pwr()

static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)

{

    u32 watt, mwatt;

    int rc;

    rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);

    rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);

    *val = WATT_TO_mWATT(watt) + mwatt;

    return 0;

}

static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
                  u32 *data)
{
    u32 msg[3];
    int rc;

    msg[0] = SENSOR_RD_MSG;
    msg[1] = SENSOR_RD_EN_ADDR(addr);

    msg[2] = 0;

    if (acpi_disabled)
        rc = xgene_hwmon_rd(ctx, msg);
    else
        rc = xgene_hwmon_pcc_rd(ctx, msg);

    if (rc < 0)
        return rc;

    /*
     * Check if sensor data is valid.
     */
    if (msg[1] & SENSOR_INVALID_DATA)
        return -ENODATA;

    *data = msg[1];

    return rc;
}

2.2.3.2 xgene_hwmon_get_io_pwr()

TBD

2.2.3.3 xgene_hwmon_get_temp()

TBD

2.2.4  xgene_hwmon_pcc_rd()

1. u32 *ptr = (void *)(generic_comm_base + 1):  指向payload(msg)

2. 配置acpi_pcct_shared_memory{ }头结构数据 的状态信息

3. WRITE_ONCE(ptr[i], cpu_to_le32(msg[i])):  Copy 操作请求数据

4. mbox_send_message(ctx->mbox_chan, msg): Mailbox 发送数据
        msg[0] = SENSOR_RD_MSG;
        msg[1] = SENSOR_RD_EN_ADDR(addr);
        msg[2] = 0;

5. wait_for_completion_timeout(&ctx->rd_complete:等待数据返回,任务挂起

        5.1 但有数据返回的时候,xgene_hwmon_pcc_rx_cb() 会被调用,然后继续处理数据

static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)

{

    struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;

    u32 *ptr = (void *)(generic_comm_base + 1);

    int rc, i;

    u16 val;

    mutex_lock(&ctx->rd_mutex);

    init_completion(&ctx->rd_complete);

    ctx->resp_pending = true;

    /* Write signature for subspace */

    WRITE_ONCE(generic_comm_base->signature,

          cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));

    /* Write to the shared command region */

    WRITE_ONCE(generic_comm_base->command,

          cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));

    /* Flip CMD COMPLETE bit */

    val = le16_to_cpu(READ_ONCE(generic_comm_base->status));

    val &= ~PCCS_CMD_COMPLETE;

    WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));

    /* Copy the message to the PCC comm space */

    for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)

       WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));

    /* Ring the doorbell */

    rc = mbox_send_message(ctx->mbox_chan, msg);

    if (rc < 0) {

       dev_err(ctx->dev, "Mailbox send error %d\n", rc);

       goto err;

    }

    if (!wait_for_completion_timeout(&ctx->rd_complete,

                   usecs_to_jiffies(ctx->usecs_lat))) {

       dev_err(ctx->dev, "Mailbox operation timed out\n");

       rc = -ETIMEDOUT;

       goto err;

    }

    /* Check for error message */

    if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {

       rc = -EINVAL;

       goto err;

    }

    msg[0] = ctx->sync_msg.msg;

    msg[1] = ctx->sync_msg.param1;

    msg[2] = ctx->sync_msg.param2;

err:

    mbox_chan_txdone(ctx->mbox_chan, 0);

    ctx->resp_pending = false;

    mutex_unlock(&ctx->rd_mutex);

    return rc;

}

2.2.3  xgene_hwmon_pcc_rx_cb()

等待 mailbox 消息callback, 等有正确消息发来后, 会 complete(&ctx->rd_complete); 

这样一个读操作就结束了。

static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)

{

    struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);

    struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;

    struct slimpro_resp_msg amsg;

    /*

     * While the driver registers with the mailbox framework, an interrupt

     * can be pending before the probe function completes its

     * initialization. If such condition occurs, just queue up the message

     * as the driver is not ready for servicing the callback.

     */

    if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)

       return;

    msg = generic_comm_base + 1;

    /* Check if platform sends interrupt */

    if (!xgene_word_tst_and_clr(&generic_comm_base->status, PCCS_SCI_DOORBEL))

       return;

    /*

     * Response message format:

     * msg[0] is the return code of the operation

     * msg[1] is the first parameter word

     * msg[2] is the second parameter word

     * As message only supports dword size, just assign it.

     */

    /* Check for sync query */

    if (ctx->resp_pending &&

        ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||

         (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&

          MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||

         (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&

          MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&

          TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {

       /* Check if platform completes command */

       if (xgene_word_tst_and_clr(&generic_comm_base->status,

                     PCCS_CMD_COMPLETE)) {

           ctx->sync_msg.msg = ((u32 *)msg)[0];

           ctx->sync_msg.param1 = ((u32 *)msg)[1];

           ctx->sync_msg.param2 = ((u32 *)msg)[2];

           /* Operation waiting for response */

           complete(&ctx->rd_complete);

           return;

       }

    }

    /*

     * Platform notifies interrupt to OSPM.

     * OPSM schedules a consumer command to get this information

     * in a workqueue. Platform must wait until OSPM has issued

     * a consumer command that serves this notification.

     */

    /* Enqueue to the FIFO */

    kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,

               sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);

    /* Schedule the bottom handler */

    schedule_work(&ctx->workq);

}

2.3 Low level FW Summary

1. 初始化CPPC/PCC, 配置共享内存地址,注册 DB device 侦听请求

2. 收到host mailbox请求,判断是不是 PCC Message

3. 调用 hwmon_cb 处理请求, 根据 sensor_id (Payload[1])处理sensor 数据读取

4. 返回数据到 PCC share memory, 同时通知 host

      

3. Log

3.1 Boot Phase

3.1.1 Device register log

  [    0.435532] ACPI: Device [HM00] status [0000000f]
  [    0.435535] device: 'APMC0D8A:00': device_add, parent is ACPI0004:00
  [    0.435539] bus: 'acpi': add device APMC0D8A:00
  [    0.435543] PM: Adding info for acpi:APMC0D8A:00
 

3.1.2  Device/Driver Binding log

  [  126.202906] bus: 'platform': add driver xgene-slimpro-hwmon
  [  126.212860] bus: 'platform': __driver_probe_device: matched device APMC0D8A:00 with driver xgene-slimpro-hwmon
  [  126.222858] bus: 'platform': really_probe: probing driver xgene-slimpro-hwmon with device APMC0D8A:00
  [  126.232090] platform_probe(): APMC0D8A:00

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值