You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写适用于LPC总线芯片的基础内核hwmon驱动模块?

LPC总线hwmon驱动开发相关问题解答

核心疑问解答

  • 现有基于设备树ID匹配的方案是否适用于LPC设备?
    现有代码中的of_device_id匹配仅适用于使用设备树枚举硬件的平台(多为嵌入式ARM/RISC-V平台),x86平台下绝大多数LPC总线设备由ACPI枚举,需要补充acpi_device_id匹配表。如果你的LPC设备无法被固件(ACPI/设备树)自动枚举(比如老式SuperIO芯片),才需要放弃自动匹配逻辑,改用__init入口手动扫描I/O端口检测设备。
  • 用__init/__exit作为驱动入口是否可以使用devm_hwmon_device_register_with_info?
    可以。devm_开头的资源管理函数仅依赖绑定的struct device实例是否合法,和驱动入口形式无关。不管是内核自动枚举触发probe调用,还是你在__init函数中手动创建platform设备,只要传入的dev指针有效,devm机制都可以正常自动管理资源释放。
  • IT87驱动使用platform_driver_register的原因?
    IT87属于老式SuperIO芯片,多数固件不会主动枚举该类LPC设备,因此驱动需要在__init函数中主动扫描指定I/O端口范围,匹配到设备后手动创建platform_device实例,再注册platform_driver完成绑定,无法直接靠module_platform_driver的自动枚举逻辑触发probe。

极简LPC hwmon驱动骨架

场景1:可被固件枚举的LPC设备(推荐优先使用该方案)

#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>

umode_t qnap_ec_is_visible(const void* data, enum hwmon_sensor_types type, u32 attr, int channel)
{
    /* 补充权限控制逻辑 */
    return 0444;
}

int qnap_ec_read(struct device* dev, enum hwmon_sensor_types type, u32 attr, int channel, long* val)
{
    /* 补充LPC端口读逻辑 */
    *val = 0;
    return 0;
}

int qnap_ec_write(struct device* dev, enum hwmon_sensor_types type, u32 attr, int channel, long val)
{
    /* 补充LPC端口写逻辑 */
    return 0;
}

static const struct hwmon_ops qnap_ec_ops = {
    .is_visible = qnap_ec_is_visible,
    .read = qnap_ec_read,
    .write = qnap_ec_write
};

static const struct hwmon_channel_info *qnap_ec_channel_info[] = {
    HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
    HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
    HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
    NULL
};

static const struct hwmon_chip_info qnap_ec_chip_info = {
    .ops = &qnap_ec_ops,
    .info = qnap_ec_channel_info
};

static int qnap_ec_probe(struct platform_device* pdev)
{
    struct device* hwmon_dev;

    /* 这里补充LPC端口请求、设备初始化逻辑 */

    hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "qnap_ec_hwmon", NULL,
                                                     &qnap_ec_chip_info, NULL);
    return PTR_ERR_OR_ZERO(hwmon_dev);
}

/* x86 ACPI匹配表,替换为你设备的ACPI ID */
static const struct acpi_device_id qnap_ec_acpi_match[] = {
    { "QNAPEC01", 0 },
    { }
};
MODULE_DEVICE_TABLE(acpi, qnap_ec_acpi_match);

/* 设备树匹配表,仅设备树平台需要 */
static const struct of_device_id qnap_ec_of_match[] = {
    { .compatible = "qnap,ec-lpc" },
    { }
};
MODULE_DEVICE_TABLE(of, qnap_ec_of_match);

static struct platform_driver qnap_ec_driver = {
    .driver = {
        .name = "qnap_ec_hwmon",
        .acpi_match_table = qnap_ec_acpi_match,
        .of_match_table = qnap_ec_of_match,
    },
    .probe = qnap_ec_probe,
};

module_platform_driver(qnap_ec_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("QNAP EC LPC HWMON Driver");

场景2:无法被固件枚举的LPC设备(需手动扫描端口)

#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

/* 替换为你设备的I/O端口范围 */
#define EC_BASE_PORT 0x2e
#define EC_PORT_SIZE 2

static struct platform_device *ec_pdev;

/* 以下hwmon相关回调和上面场景1完全一致 */
umode_t qnap_ec_is_visible(const void* data, enum hwmon_sensor_types type, u32 attr, int channel)
{
    return 0444;
}

int qnap_ec_read(struct device* dev, enum hwmon_sensor_types type, u32 attr, int channel, long* val)
{
    *val = 0;
    return 0;
}

int qnap_ec_write(struct device* dev, enum hwmon_sensor_types type, u32 attr, int channel, long val)
{
    return 0;
}

static const struct hwmon_ops qnap_ec_ops = {
    .is_visible = qnap_ec_is_visible,
    .read = qnap_ec_read,
    .write = qnap_ec_write
};

static const struct hwmon_channel_info *qnap_ec_channel_info[] = {
    HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
    HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
    HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
    NULL
};

static const struct hwmon_chip_info qnap_ec_chip_info = {
    .ops = &qnap_ec_ops,
    .info = qnap_ec_channel_info
};

static int qnap_ec_probe(struct platform_device* pdev)
{
    struct device* hwmon_dev;

    if (!devm_request_region(&pdev->dev, EC_BASE_PORT, EC_PORT_SIZE, "qnap_ec"))
        return -EBUSY;

    /* 补充设备识别、初始化逻辑 */

    hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "qnap_ec_hwmon", NULL,
                                                     &qnap_ec_chip_info, NULL);
    return PTR_ERR_OR_ZERO(hwmon_dev);
}

static struct platform_driver qnap_ec_driver = {
    .driver = {
        .name = "qnap_ec_hwmon",
    },
    .probe = qnap_ec_probe,
};

static int __init qnap_ec_init(void)
{
    int ret;

    /* 补充I/O端口扫描、设备识别逻辑,确认设备存在再往下走 */
    if (!request_region(EC_BASE_PORT, EC_PORT_SIZE, "qnap_ec_probe"))
        return -EBUSY;

    /* 这里做设备有效性检测,检测不通过就释放端口返回错误
    if (ec_detect() < 0) {
        release_region(EC_BASE_PORT, EC_PORT_SIZE);
        return -ENODEV;
    }
    */
    release_region(EC_BASE_PORT, EC_PORT_SIZE);

    ret = platform_driver_register(&qnap_ec_driver);
    if (ret)
        return ret;

    ec_pdev = platform_device_alloc("qnap_ec_hwmon", PLATFORM_DEVID_AUTO);
    if (!ec_pdev) {
        platform_driver_unregister(&qnap_ec_driver);
        return -ENOMEM;
    }

    ret = platform_device_add(ec_pdev);
    if (ret) {
        platform_device_put(ec_pdev);
        platform_driver_unregister(&qnap_ec_driver);
        return ret;
    }

    return 0;
}

static void __exit qnap_ec_exit(void)
{
    platform_device_unregister(ec_pdev);
    platform_driver_unregister(&qnap_ec_driver);
}

module_init(qnap_ec_init);
module_exit(qnap_ec_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("QNAP EC LPC HWMON Driver");

内容的提问来源于stack exchange,提问作者Harry Muscle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 09:57:02