| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * MAX77705 voltage and current hwmon driver.
- *
- * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
- */
- #include <linux/err.h>
- #include <linux/hwmon-sysfs.h>
- #include <linux/hwmon.h>
- #include <linux/kernel.h>
- #include <linux/mfd/max77705-private.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
- struct channel_desc {
- u8 reg;
- u8 avg_reg;
- const char *const label;
- // register resolution. nano Volts for voltage, nano Amperes for current
- u32 resolution;
- };
- static const struct channel_desc current_channel_desc[] = {
- {
- .reg = IIN_REG,
- .label = "IIN_REG",
- .resolution = 125000
- },
- {
- .reg = ISYS_REG,
- .avg_reg = AVGISYS_REG,
- .label = "ISYS_REG",
- .resolution = 312500
- }
- };
- static const struct channel_desc voltage_channel_desc[] = {
- {
- .reg = VBYP_REG,
- .label = "VBYP_REG",
- .resolution = 427246
- },
- {
- .reg = VSYS_REG,
- .label = "VSYS_REG",
- .resolution = 156250
- }
- };
- static int max77705_read_and_convert(struct regmap *regmap, u8 reg, u32 res,
- bool is_signed, long *val)
- {
- int ret;
- u32 regval;
- ret = regmap_read(regmap, reg, ®val);
- if (ret < 0)
- return ret;
- if (is_signed)
- *val = mult_frac((long)sign_extend32(regval, 15), res, 1000000);
- else
- *val = mult_frac((long)regval, res, 1000000);
- return 0;
- }
- static umode_t max77705_is_visible(const void *data,
- enum hwmon_sensor_types type,
- u32 attr, int channel)
- {
- switch (type) {
- case hwmon_in:
- switch (attr) {
- case hwmon_in_input:
- case hwmon_in_label:
- return 0444;
- default:
- break;
- }
- break;
- case hwmon_curr:
- switch (attr) {
- case hwmon_curr_input:
- case hwmon_in_label:
- return 0444;
- case hwmon_curr_average:
- if (current_channel_desc[channel].avg_reg)
- return 0444;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- return 0;
- }
- static int max77705_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
- int channel, const char **buf)
- {
- switch (type) {
- case hwmon_curr:
- switch (attr) {
- case hwmon_in_label:
- *buf = current_channel_desc[channel].label;
- return 0;
- default:
- return -EOPNOTSUPP;
- }
- case hwmon_in:
- switch (attr) {
- case hwmon_in_label:
- *buf = voltage_channel_desc[channel].label;
- return 0;
- default:
- return -EOPNOTSUPP;
- }
- default:
- return -EOPNOTSUPP;
- }
- }
- static int max77705_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *val)
- {
- struct regmap *regmap = dev_get_drvdata(dev);
- u8 reg;
- u32 res;
- switch (type) {
- case hwmon_curr:
- switch (attr) {
- case hwmon_curr_input:
- reg = current_channel_desc[channel].reg;
- res = current_channel_desc[channel].resolution;
- return max77705_read_and_convert(regmap, reg, res, true, val);
- case hwmon_curr_average:
- reg = current_channel_desc[channel].avg_reg;
- res = current_channel_desc[channel].resolution;
- return max77705_read_and_convert(regmap, reg, res, true, val);
- default:
- return -EOPNOTSUPP;
- }
- case hwmon_in:
- switch (attr) {
- case hwmon_in_input:
- reg = voltage_channel_desc[channel].reg;
- res = voltage_channel_desc[channel].resolution;
- return max77705_read_and_convert(regmap, reg, res, false, val);
- default:
- return -EOPNOTSUPP;
- }
- default:
- return -EOPNOTSUPP;
- }
- return 0;
- }
- static const struct hwmon_ops max77705_hwmon_ops = {
- .is_visible = max77705_is_visible,
- .read = max77705_read,
- .read_string = max77705_read_string,
- };
- static const struct hwmon_channel_info *max77705_info[] = {
- HWMON_CHANNEL_INFO(in,
- HWMON_I_INPUT | HWMON_I_LABEL,
- HWMON_I_INPUT | HWMON_I_LABEL
- ),
- HWMON_CHANNEL_INFO(curr,
- HWMON_C_INPUT | HWMON_C_LABEL,
- HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL
- ),
- NULL
- };
- static const struct hwmon_chip_info max77705_chip_info = {
- .ops = &max77705_hwmon_ops,
- .info = max77705_info,
- };
- static int max77705_hwmon_probe(struct platform_device *pdev)
- {
- struct device *hwmon_dev;
- struct regmap *regmap;
- regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!regmap)
- return -ENODEV;
- hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "max77705", regmap,
- &max77705_chip_info, NULL);
- if (IS_ERR(hwmon_dev))
- return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev),
- "Unable to register hwmon device\n");
- return 0;
- };
- static struct platform_driver max77705_hwmon_driver = {
- .driver = {
- .name = "max77705-hwmon",
- },
- .probe = max77705_hwmon_probe,
- };
- module_platform_driver(max77705_hwmon_driver);
- MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
- MODULE_DESCRIPTION("MAX77705 monitor driver");
- MODULE_LICENSE("GPL");
|