如何编写适用于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
相关产品推荐
相关产品推荐

