diff options
Diffstat (limited to 'drivers/hwmon/pmbus')
-rw-r--r-- | drivers/hwmon/pmbus/Kconfig | 18 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/lm25066.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/lt3074.c | 122 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/max34440.c | 119 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/mpq7932.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/mpq8785.c | 91 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus.h | 19 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus_core.c | 69 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/tda38640.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/tps25990.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ucd9000.c | 16 |
12 files changed, 401 insertions, 64 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index c9b3c3149982..441f984a859d 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -218,6 +218,24 @@ config SENSORS_LM25066_REGULATOR If you say yes here you get regulator support for National Semiconductor LM25066, LM5064, and LM5066. +config SENSORS_LT3074 + tristate "Analog Devices LT3074" + help + If you say yes here you get hardware monitoring support for Analog + Devices LT3074. + + This driver can also be built as a module. If so, the module will + be called lt3074. + +config SENSORS_LT3074_REGULATOR + tristate "Regulator support for LT3074" + depends on SENSORS_LT3074 && REGULATOR + help + If you say yes here you get regulator support for Analog Devices + LT3074. The LT3074 is a low voltage, ultralow noise, high PSRR, + dropout linear regulator. The device supplies up to 3A with a + typical dropout voltage of 45mV. + config SENSORS_LT7182S tristate "Analog Devices LT7182S" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 56f128c4653e..29cd8a3317d2 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LT3074) += lt3074.o obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 40b0dda32ea6..dd7275a67a0a 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -437,7 +437,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, #if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) static const struct regulator_desc lm25066_reg_desc[] = { - PMBUS_REGULATOR_ONE("vout"), + PMBUS_REGULATOR_ONE_NODE("vout"), }; #endif diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c new file mode 100644 index 000000000000..3704dbe7b54a --- /dev/null +++ b/drivers/hwmon/pmbus/lt3074.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices LT3074 + * + * Copyright (C) 2025 Analog Devices, Inc. + */ +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> + +#include "pmbus.h" + +#define LT3074_MFR_READ_VBIAS 0xc6 +#define LT3074_MFR_BIAS_OV_WARN_LIMIT 0xc7 +#define LT3074_MFR_BIAS_UV_WARN_LIMIT 0xc8 +#define LT3074_MFR_SPECIAL_ID 0xe7 + +#define LT3074_SPECIAL_ID_VALUE 0x1c1d + +static const struct regulator_desc __maybe_unused lt3074_reg_desc[] = { + PMBUS_REGULATOR_ONE("regulator"), +}; + +static int lt3074_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + switch (reg) { + case PMBUS_VIRT_READ_VMON: + return pmbus_read_word_data(client, page, phase, + LT3074_MFR_READ_VBIAS); + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + return pmbus_read_word_data(client, page, phase, + LT3074_MFR_BIAS_UV_WARN_LIMIT); + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + return pmbus_read_word_data(client, page, phase, + LT3074_MFR_BIAS_OV_WARN_LIMIT); + default: + return -ENODATA; + } +} + +static int lt3074_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + switch (reg) { + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + return pmbus_write_word_data(client, 0, + LT3074_MFR_BIAS_UV_WARN_LIMIT, + word); + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + return pmbus_write_word_data(client, 0, + LT3074_MFR_BIAS_OV_WARN_LIMIT, + word); + default: + return -ENODATA; + } +} + +static struct pmbus_driver_info lt3074_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_VMON | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = lt3074_read_word_data, + .write_word_data = lt3074_write_word_data, +#if IS_ENABLED(CONFIG_SENSORS_LT3074_REGULATOR) + .num_regulators = 1, + .reg_desc = lt3074_reg_desc, +#endif +}; + +static int lt3074_probe(struct i2c_client *client) +{ + int ret; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_word_data(client, LT3074_MFR_SPECIAL_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read ID\n"); + + if (ret != LT3074_SPECIAL_ID_VALUE) + return dev_err_probe(dev, -ENODEV, "ID mismatch\n"); + + return pmbus_do_probe(client, <3074_info); +} + +static const struct i2c_device_id lt3074_id[] = { + { "lt3074", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, lt3074_id); + +static const struct of_device_id __maybe_unused lt3074_of_match[] = { + { .compatible = "adi,lt3074" }, + {} +}; +MODULE_DEVICE_TABLE(of, lt3074_of_match); + +static struct i2c_driver lt3074_driver = { + .driver = { + .name = "lt3074", + .of_match_table = of_match_ptr(lt3074_of_match), + }, + .probe = lt3074_probe, + .id_table = lt3074_id, +}; +module_i2c_driver(lt3074_driver); + +MODULE_AUTHOR("Cedric Encarnacion <cedricjustine.encarnacion@analog.com>"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices LT3074"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index c9dda33831ff..56834d26f8ef 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -12,9 +12,26 @@ #include <linux/init.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/delay.h> #include "pmbus.h" -enum chips { max34440, max34441, max34446, max34451, max34460, max34461 }; +enum chips { + adpm12160, + max34440, + max34441, + max34446, + max34451, + max34460, + max34461, +}; + +/* + * Firmware is sometimes not ready if we try and read the + * data from the page immediately after setting. Maxim + * recommends 50us delay due to the chip failing to clock + * stretch long enough here. + */ +#define MAX34440_PAGE_CHANGE_DELAY 50 #define MAX34440_MFR_VOUT_PEAK 0xd4 #define MAX34440_MFR_IOUT_PEAK 0xd5 @@ -34,16 +51,21 @@ enum chips { max34440, max34441, max34446, max34451, max34460, max34461 }; /* * The whole max344* family have IOUT_OC_WARN_LIMIT and IOUT_OC_FAULT_LIMIT * swapped from the standard pmbus spec addresses. + * For max34451, version MAX34451ETNA6+ and later has this issue fixed. */ #define MAX34440_IOUT_OC_WARN_LIMIT 0x46 #define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A +#define MAX34451ETNA6_MFR_REV 0x0012 + #define MAX34451_MFR_CHANNEL_CONFIG 0xe4 #define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f struct max34440_data { int id; struct pmbus_driver_info info; + u8 iout_oc_warn_limit; + u8 iout_oc_fault_limit; }; #define to_max34440_data(x) container_of(x, struct max34440_data, info) @@ -60,11 +82,11 @@ static int max34440_read_word_data(struct i2c_client *client, int page, switch (reg) { case PMBUS_IOUT_OC_FAULT_LIMIT: ret = pmbus_read_word_data(client, page, phase, - MAX34440_IOUT_OC_FAULT_LIMIT); + data->iout_oc_fault_limit); break; case PMBUS_IOUT_OC_WARN_LIMIT: ret = pmbus_read_word_data(client, page, phase, - MAX34440_IOUT_OC_WARN_LIMIT); + data->iout_oc_warn_limit); break; case PMBUS_VIRT_READ_VOUT_MIN: ret = pmbus_read_word_data(client, page, phase, @@ -75,7 +97,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, MAX34440_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_AVG: - if (data->id != max34446 && data->id != max34451) + if (data->id != max34446 && data->id != max34451 && + data->id != adpm12160) return -ENXIO; ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); @@ -133,11 +156,11 @@ static int max34440_write_word_data(struct i2c_client *client, int page, switch (reg) { case PMBUS_IOUT_OC_FAULT_LIMIT: - ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_FAULT_LIMIT, + ret = pmbus_write_word_data(client, page, data->iout_oc_fault_limit, word); break; case PMBUS_IOUT_OC_WARN_LIMIT: - ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_WARN_LIMIT, + ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit, word); break; case PMBUS_VIRT_RESET_POUT_HISTORY: @@ -159,7 +182,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page, case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = pmbus_write_word_data(client, page, MAX34440_MFR_IOUT_PEAK, 0); - if (!ret && (data->id == max34446 || data->id == max34451)) + if (!ret && (data->id == max34446 || data->id == max34451 || + data->id == adpm12160)) ret = pmbus_write_word_data(client, page, MAX34446_MFR_IOUT_AVG, 0); @@ -235,9 +259,29 @@ static int max34451_set_supported_funcs(struct i2c_client *client, */ int page, rv; + bool max34451_na6 = false; + + rv = i2c_smbus_read_word_data(client, PMBUS_MFR_REVISION); + if (rv < 0) + return rv; + + if (rv >= MAX34451ETNA6_MFR_REV) { + max34451_na6 = true; + data->info.format[PSC_VOLTAGE_IN] = direct; + data->info.format[PSC_CURRENT_IN] = direct; + data->info.m[PSC_VOLTAGE_IN] = 1; + data->info.b[PSC_VOLTAGE_IN] = 0; + data->info.R[PSC_VOLTAGE_IN] = 3; + data->info.m[PSC_CURRENT_IN] = 1; + data->info.b[PSC_CURRENT_IN] = 0; + data->info.R[PSC_CURRENT_IN] = 2; + data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; + } for (page = 0; page < 16; page++) { rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + fsleep(MAX34440_PAGE_CHANGE_DELAY); if (rv < 0) return rv; @@ -251,16 +295,30 @@ static int max34451_set_supported_funcs(struct i2c_client *client, case 0x20: data->info.func[page] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_VIN | + PMBUS_HAVE_STATUS_INPUT; break; case 0x21: data->info.func[page] = PMBUS_HAVE_VOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_VIN; break; case 0x22: data->info.func[page] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_IIN | + PMBUS_HAVE_STATUS_INPUT; break; case 0x23: data->info.func[page] = PMBUS_HAVE_IOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_IIN; break; default: break; @@ -271,6 +329,41 @@ static int max34451_set_supported_funcs(struct i2c_client *client, } static struct pmbus_driver_info max34440_info[] = { + [adpm12160] = { + .pages = 19, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_IN] = 1, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = 2, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 2, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + /* absent func below [18] are not for monitoring */ + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[7] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[8] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, + .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, [max34440] = { .pages = 14, .format[PSC_VOLTAGE_IN] = direct, @@ -312,6 +405,7 @@ static struct pmbus_driver_info max34440_info[] = { .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34441] = { .pages = 12, @@ -355,6 +449,7 @@ static struct pmbus_driver_info max34440_info[] = { .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34446] = { .pages = 7, @@ -392,6 +487,7 @@ static struct pmbus_driver_info max34440_info[] = { .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34451] = { .pages = 21, @@ -415,6 +511,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34460] = { .pages = 18, @@ -445,6 +542,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34461] = { .pages = 23, @@ -480,6 +578,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, }; @@ -494,17 +593,23 @@ static int max34440_probe(struct i2c_client *client) return -ENOMEM; data->id = i2c_match_id(max34440_id, client)->driver_data; data->info = max34440_info[data->id]; + data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT; if (data->id == max34451) { rv = max34451_set_supported_funcs(client, data); if (rv) return rv; + } else if (data->id == adpm12160) { + data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; } return pmbus_do_probe(client, &data->info); } static const struct i2c_device_id max34440_id[] = { + {"adpm12160", adpm12160}, {"max34440", max34440}, {"max34441", max34441}, {"max34446", max34446}, diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index c1e2d0cb2fd0..8f10e37a7a76 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -51,8 +51,8 @@ static const struct regulator_desc mpq7932_regulators_desc[] = { }; static const struct regulator_desc mpq7932_regulators_desc_one[] = { - PMBUS_REGULATOR_STEP_ONE("buck", MPQ7932_N_VOLTAGES, - MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP_ONE_NODE("buck", MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), }; #endif diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 331c274ca892..1f56aaf4dde8 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -4,10 +4,23 @@ */ #include <linux/i2c.h> +#include <linux/bitops.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/of_device.h> #include "pmbus.h" +#define MPM82504_READ_TEMPERATURE_1_SIGN_POS 9 + +enum chips { mpm3695, mpm3695_25, mpm82504, mpq8785 }; + +static u16 voltage_scale_loop_max_val[] = { + [mpm3695] = GENMASK(9, 0), + [mpm3695_25] = GENMASK(11, 0), + [mpm82504] = GENMASK(9, 0), + [mpq8785] = GENMASK(10, 0), +}; + static int mpq8785_identify(struct i2c_client *client, struct pmbus_driver_info *info) { @@ -34,6 +47,20 @@ static int mpq8785_identify(struct i2c_client *client, return 0; }; +static int mpm82504_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + ret = pmbus_read_word_data(client, page, phase, reg); + + if (ret < 0 || reg != PMBUS_READ_TEMPERATURE_1) + return ret; + + /* Fix PMBUS_READ_TEMPERATURE_1 signedness */ + return sign_extend32(ret, MPM82504_READ_TEMPERATURE_1_SIGN_POS) & 0xffff; +} + static struct pmbus_driver_info mpq8785_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = direct, @@ -53,26 +80,74 @@ static struct pmbus_driver_info mpq8785_info = { PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, - .identify = mpq8785_identify, -}; - -static int mpq8785_probe(struct i2c_client *client) -{ - return pmbus_do_probe(client, &mpq8785_info); }; static const struct i2c_device_id mpq8785_id[] = { - { "mpq8785" }, + { "mpm3695", mpm3695 }, + { "mpm3695-25", mpm3695_25 }, + { "mpm82504", mpm82504 }, + { "mpq8785", mpq8785 }, { }, }; MODULE_DEVICE_TABLE(i2c, mpq8785_id); static const struct of_device_id __maybe_unused mpq8785_of_match[] = { - { .compatible = "mps,mpq8785" }, + { .compatible = "mps,mpm3695", .data = (void *)mpm3695 }, + { .compatible = "mps,mpm3695-25", .data = (void *)mpm3695_25 }, + { .compatible = "mps,mpm82504", .data = (void *)mpm82504 }, + { .compatible = "mps,mpq8785", .data = (void *)mpq8785 }, {} }; MODULE_DEVICE_TABLE(of, mpq8785_of_match); +static int mpq8785_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + enum chips chip_id; + u32 voltage_scale; + int ret; + + info = devm_kmemdup(dev, &mpq8785_info, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (dev->of_node) + chip_id = (kernel_ulong_t)of_device_get_match_data(dev); + else + chip_id = (kernel_ulong_t)i2c_get_match_data(client); + + switch (chip_id) { + case mpm3695: + case mpm3695_25: + case mpm82504: + info->format[PSC_VOLTAGE_OUT] = direct; + info->m[PSC_VOLTAGE_OUT] = 8; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = 2; + info->read_word_data = mpm82504_read_word_data; + break; + case mpq8785: + info->identify = mpq8785_identify; + break; + default: + return -ENODEV; + } + + if (!device_property_read_u32(dev, "mps,vout-fb-divider-ratio-permille", + &voltage_scale)) { + if (voltage_scale > voltage_scale_loop_max_val[chip_id]) + return -EINVAL; + + ret = i2c_smbus_write_word_data(client, PMBUS_VOUT_SCALE_LOOP, + voltage_scale); + if (ret) + return ret; + } + + return pmbus_do_probe(client, info); +}; + static struct i2c_driver mpq8785_driver = { .driver = { .name = "mpq8785", diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index ddb19c9726d6..d2e9bfb5320f 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -482,6 +482,7 @@ struct pmbus_driver_info { */ int access_delay; /* in microseconds */ int write_delay; /* in microseconds */ + int page_change_delay; /* in microseconds */ }; /* Regulator ops */ @@ -508,11 +509,11 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev, #define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0) -#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \ +#define __PMBUS_REGULATOR_STEP_ONE(_name, _node, _voltages, _step, _min_uV) \ { \ .name = (_name), \ .of_match = of_match_ptr(_name), \ - .regulators_node = of_match_ptr("regulators"), \ + .regulators_node = of_match_ptr(_node), \ .ops = &pmbus_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ @@ -522,7 +523,19 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev, .init_cb = pmbus_regulator_init_cb, \ } -#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0) +/* + * _NODE macros are defined for historic reasons and MUST NOT be used in new + * drivers. + */ +#define PMBUS_REGULATOR_STEP_ONE_NODE(_name, _voltages, _step, _min_uV) \ + __PMBUS_REGULATOR_STEP_ONE(_name, "regulators", _voltages, _step, _min_uV) + +#define PMBUS_REGULATOR_ONE_NODE(_name) PMBUS_REGULATOR_STEP_ONE_NODE(_name, 0, 0, 0) + +#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \ + __PMBUS_REGULATOR_STEP_ONE(_name, NULL, _voltages, _step, _min_uV) + +#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0) /* Function declarations */ diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index cfeba2e4c5c3..be6d05def115 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -32,6 +32,13 @@ #define PMBUS_ATTR_ALLOC_SIZE 32 #define PMBUS_NAME_SIZE 24 +/* + * The type of operation used for picking the delay between + * successive pmbus operations. + */ +#define PMBUS_OP_WRITE BIT(0) +#define PMBUS_OP_PAGE_CHANGE BIT(1) + static int wp = -1; module_param(wp, int, 0444); @@ -113,8 +120,8 @@ struct pmbus_data { int vout_low[PMBUS_PAGES]; /* voltage low margin */ int vout_high[PMBUS_PAGES]; /* voltage high margin */ - ktime_t write_time; /* Last SMBUS write timestamp */ - ktime_t access_time; /* Last SMBUS access timestamp */ + + ktime_t next_access_backoff; /* Wait until at least this time */ }; struct pmbus_debugfs_entry { @@ -169,32 +176,26 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); static void pmbus_wait(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - s64 delta; + s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get()); - if (info->access_delay) { - delta = ktime_us_delta(ktime_get(), data->access_time); - - if (delta < info->access_delay) - fsleep(info->access_delay - delta); - } else if (info->write_delay) { - delta = ktime_us_delta(ktime_get(), data->write_time); - - if (delta < info->write_delay) - fsleep(info->write_delay - delta); - } + if (delay > 0) + fsleep(delay); } -/* Sets the last accessed timestamp for pmbus_wait */ -static void pmbus_update_ts(struct i2c_client *client, bool write_op) +/* Sets the last operation timestamp for pmbus_wait */ +static void pmbus_update_ts(struct i2c_client *client, int op) { struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; + int delay = info->access_delay; + + if (op & PMBUS_OP_WRITE) + delay = max(delay, info->write_delay); + if (op & PMBUS_OP_PAGE_CHANGE) + delay = max(delay, info->page_change_delay); - if (info->access_delay) - data->access_time = ktime_get(); - else if (info->write_delay && write_op) - data->write_time = ktime_get(); + if (delay > 0) + data->next_access_backoff = ktime_add_us(ktime_get(), delay); } int pmbus_set_page(struct i2c_client *client, int page, int phase) @@ -209,13 +210,13 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) data->info->pages > 1 && page != data->currpage) { pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE | PMBUS_OP_PAGE_CHANGE); if (rv < 0) return rv; pmbus_wait(client); rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (rv < 0) return rv; @@ -229,7 +230,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, phase); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); if (rv) return rv; } @@ -249,7 +250,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) pmbus_wait(client); rv = i2c_smbus_write_byte(client, value); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); return rv; } @@ -284,7 +285,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, pmbus_wait(client); rv = i2c_smbus_write_word_data(client, reg, word); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); return rv; } @@ -405,7 +406,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) pmbus_wait(client); rv = i2c_smbus_read_word_data(client, reg); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); return rv; } @@ -468,7 +469,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) pmbus_wait(client); rv = i2c_smbus_read_byte_data(client, reg); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); return rv; } @@ -484,7 +485,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, reg, value); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); return rv; } @@ -520,7 +521,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, pmbus_wait(client); rv = i2c_smbus_read_block_data(client, reg, data_buf); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); return rv; } @@ -2524,7 +2525,7 @@ static int pmbus_read_coefficients(struct i2c_client *client, rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, I2C_SMBUS_BLOCK_PROC_CALL, &data); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); if (rv < 0) return rv; @@ -2728,7 +2729,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, if (!(data->flags & PMBUS_NO_CAPABILITY)) { pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) @@ -2744,13 +2745,13 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, data->read_status = pmbus_read_status_word; pmbus_wait(client); ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (ret < 0 || ret == 0xffff) { data->read_status = pmbus_read_status_byte; pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (ret < 0 || ret == 0xff) { dev_err(dev, "PMBus status register not found\n"); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index 07fe58c24485..d902d39f49f4 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -15,7 +15,7 @@ #include "pmbus.h" static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = { - PMBUS_REGULATOR_ONE("vout"), + PMBUS_REGULATOR_ONE_NODE("vout"), }; struct tda38640_data { diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c index 0d2655e69549..c13edd7e1abf 100644 --- a/drivers/hwmon/pmbus/tps25990.c +++ b/drivers/hwmon/pmbus/tps25990.c @@ -333,7 +333,7 @@ static int tps25990_write_byte_data(struct i2c_client *client, #if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) static const struct regulator_desc tps25990_reg_desc[] = { - PMBUS_REGULATOR_ONE("vout"), + PMBUS_REGULATOR_ONE_NODE("vout"), }; #endif diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 9b0eadc81a2e..2bc8cccb01fd 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -212,8 +212,8 @@ static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset) return !!(ret & UCD9000_GPIO_CONFIG_STATUS); } -static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct i2c_client *client = gpiochip_get_data(gc); int ret; @@ -222,17 +222,17 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, if (ret < 0) { dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n", offset, ret); - return; + return ret; } if (value) { if (ret & UCD9000_GPIO_CONFIG_STATUS) - return; + return 0; ret |= UCD9000_GPIO_CONFIG_STATUS; } else { if (!(ret & UCD9000_GPIO_CONFIG_STATUS)) - return; + return 0; ret &= ~UCD9000_GPIO_CONFIG_STATUS; } @@ -244,7 +244,7 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, if (ret < 0) { dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n", offset, ret); - return; + return ret; } ret &= ~UCD9000_GPIO_CONFIG_ENABLE; @@ -253,6 +253,8 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, if (ret < 0) dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n", offset, ret); + + return ret; } static int ucd9000_gpio_get_direction(struct gpio_chip *gc, @@ -362,7 +364,7 @@ static void ucd9000_probe_gpio(struct i2c_client *client, data->gpio.direction_input = ucd9000_gpio_direction_input; data->gpio.direction_output = ucd9000_gpio_direction_output; data->gpio.get = ucd9000_gpio_get; - data->gpio.set = ucd9000_gpio_set; + data->gpio.set_rv = ucd9000_gpio_set; data->gpio.can_sleep = true; data->gpio.base = -1; data->gpio.parent = &client->dev; |