diff options
Diffstat (limited to 'drivers/hwmon/pmbus')
63 files changed, 2757 insertions, 413 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 557ae0c414b0..419469f40ba0 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -51,12 +51,22 @@ config SENSORS_ADM1275 tristate "Analog Devices ADM1275 and compatibles" help If you say yes here you get hardware monitoring support for Analog - Devices ADM1075, ADM1272, ADM1275, ADM1276, ADM1278, ADM1293, - and ADM1294 Hot-Swap Controller and Digital Power Monitors. + Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281, + ADM1293, and ADM1294 Hot-Swap Controller and Digital Power Monitors. This driver can also be built as a module. If so, the module will be called adm1275. +config SENSORS_ADP1050 + tristate "Analog Devices ADP1050 digital controller for Power Supplies" + help + If you say yes here you get hardware monitoring support for Analog + Devices ADP1050 digital controller for isolated power supply with + PMBus interface. + + This driver can also be built as a module. If so, the module will + be called adp1050. + config SENSORS_BEL_PFE tristate "Bel PFE Compatible Power Supplies" help @@ -75,6 +85,15 @@ config SENSORS_BPA_RS600 This driver can also be built as a module. If so, the module will be called bpa-rs600. +config SENSORS_CRPS + tristate "Intel Common Redundant Power Supply" + help + If you say yes here you get hardware monitoring support for the Intel + Common Redundant Power Supply. + + This driver can also be built as a module. If so, the module will + be called crps. + config SENSORS_DELTA_AHE50DC_FAN tristate "Delta AHE-50DC fan control module" help @@ -214,9 +233,9 @@ config SENSORS_LTC2978_REGULATOR depends on SENSORS_LTC2978 && REGULATOR help If you say yes here you get regulator support for Linear Technology - LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7880, - LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686, - and LTM4700. + LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841, + LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, + LTM4686, and LTM4700. config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" @@ -241,7 +260,7 @@ config SENSORS_MAX15301 tristate "Maxim MAX15301" help If you say yes here you get hardware monitoring support for Maxim - MAX15301, as well as for Flex BMR461. + MAX15301, MAX15303, as well as for Flex BMR461. This driver can also be built as a module. If so, the module will be called max15301. @@ -327,6 +346,15 @@ config SENSORS_MP2888 This driver can also be built as a module. If so, the module will be called mp2888. +config SENSORS_MP2891 + tristate "MPS MP2891" + help + If you say yes here you get hardware monitoring support for MPS + MP2891 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2891. + config SENSORS_MP2975 tristate "MPS MP2975" help @@ -336,6 +364,15 @@ config SENSORS_MP2975 This driver can also be built as a module. If so, the module will be called mp2975. +config SENSORS_MP2993 + tristate "MPS MP2993" + help + If you say yes here you get hardware monitoring support for MPS + MP2993 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2993. + config SENSORS_MP2975_REGULATOR depends on SENSORS_MP2975 && REGULATOR bool "Regulator support for MPS MP2975" @@ -352,6 +389,15 @@ config SENSORS_MP5023 This driver can also be built as a module. If so, the module will be called mp5023. +config SENSORS_MP5920 + tristate "MPS MP5920" + help + If you say yes here you get hardware monitoring support for Monolithic + MP5920. + + This driver can also be built as a module. If so, the module will + be called mp5920. + config SENSORS_MP5990 tristate "MPS MP5990" help @@ -361,6 +407,15 @@ config SENSORS_MP5990 This driver can also be built as a module. If so, the module will be called mp5990. +config SENSORS_MP9941 + tristate "MPS MP9941" + help + If you say yes here you get hardware monitoring support for MPS + MP9941. + + This driver can also be built as a module. If so, the module will + be called mp9941. + config SENSORS_MPQ7932_REGULATOR bool "Regulator support for MPQ7932" depends on SENSORS_MPQ7932 && REGULATOR @@ -464,6 +519,23 @@ config SENSORS_TDA38640_REGULATOR If you say yes here you get regulator support for Infineon TDA38640 as regulator. +config SENSORS_TPS25990 + tristate "TI TPS25990" + help + If you say yes here you get hardware monitoring support for TI + TPS25990. + + This driver can also be built as a module. If so, the module will + be called tps25990. + +config SENSORS_TPS25990_REGULATOR + bool "Regulator support for TPS25990 and compatibles" + depends on SENSORS_TPS25990 && REGULATOR + default SENSORS_TPS25990 + help + If you say yes here you get regulator support for Texas Instruments + TPS25990. + config SENSORS_TPS40422 tristate "TI TPS40422" help @@ -511,6 +583,15 @@ config SENSORS_UCD9200 This driver can also be built as a module. If so, the module will be called ucd9200. +config SENSORS_XDP710 + tristate "Infineon XDP710 family" + help + If you say yes here you get hardware monitoring support for Infineon + XDP710. + + This driver can also be built as a module. If so, the module will + be called xdp710. + config SENSORS_XDPE152 tristate "Infineon XDPE152 family" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index f14ecf03ad77..c7eb7739b7f8 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ACBEL_FSG032) += acbel-fsg032.o obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o +obj-$(CONFIG_SENSORS_ADP1050) += adp1050.o obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o @@ -35,9 +36,13 @@ obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_MP2856) += mp2856.o obj-$(CONFIG_SENSORS_MP2888) += mp2888.o +obj-$(CONFIG_SENSORS_MP2891) += mp2891.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o +obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o +obj-$(CONFIG_SENSORS_MP5920) += mp5920.o obj-$(CONFIG_SENSORS_MP5990) += mp5990.o +obj-$(CONFIG_SENSORS_MP9941) += mp9941.o obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o @@ -46,12 +51,15 @@ obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o +obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o +obj-$(CONFIG_SENSORS_XDP710) += xdp710.o obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o +obj-$(CONFIG_SENSORS_CRPS) += crps.o diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c index e0c55fd8f3a6..9f07fb4abaff 100644 --- a/drivers/hwmon/pmbus/acbel-fsg032.c +++ b/drivers/hwmon/pmbus/acbel-fsg032.c @@ -120,4 +120,4 @@ module_i2c_driver(acbel_fsg032_driver); MODULE_AUTHOR("Lakshmi Yadlapati"); MODULE_DESCRIPTION("PMBus driver for AcBel Power System power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index ed0a7b9fae4b..d90f8f80be8e 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -490,7 +490,7 @@ static const struct of_device_id adm1266_of_match[] = { MODULE_DEVICE_TABLE(of, adm1266_of_match); static const struct i2c_device_id adm1266_id[] = { - { "adm1266", 0 }, + { "adm1266" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1266_id); @@ -509,4 +509,4 @@ module_i2c_driver(adm1266_driver); MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index e2c61d6fa521..7d175baa5de2 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -18,7 +18,7 @@ #include <linux/log2.h> #include "pmbus.h" -enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 }; +enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, adm1293, adm1294 }; #define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0) #define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5) @@ -479,9 +479,11 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) static const struct i2c_device_id adm1275_id[] = { { "adm1075", adm1075 }, { "adm1272", adm1272 }, + { "adm1273", adm1273 }, { "adm1275", adm1275 }, { "adm1276", adm1276 }, { "adm1278", adm1278 }, + { "adm1281", adm1281 }, { "adm1293", adm1293 }, { "adm1294", adm1294 }, { } @@ -554,7 +556,8 @@ static int adm1275_probe(struct i2c_client *client) "Device mismatch: Configured %s, detected %s\n", client->name, mid->name); - if (mid->driver_data == adm1272 || mid->driver_data == adm1278 || + if (mid->driver_data == adm1272 || mid->driver_data == adm1273 || + mid->driver_data == adm1278 || mid->driver_data == adm1281 || mid->driver_data == adm1293 || mid->driver_data == adm1294) config_read_fn = i2c_smbus_read_word_data; else @@ -628,6 +631,7 @@ static int adm1275_probe(struct i2c_client *client) PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; break; case adm1272: + case adm1273: data->have_vout = true; data->have_pin_max = true; data->have_temp_max = true; @@ -703,6 +707,7 @@ static int adm1275_probe(struct i2c_client *client) PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; break; case adm1278: + case adm1281: data->have_vout = true; data->have_pin_max = true; data->have_temp_max = true; @@ -863,4 +868,4 @@ module_i2c_driver(adm1275_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/adp1050.c b/drivers/hwmon/pmbus/adp1050.c new file mode 100644 index 000000000000..ef46c880b168 --- /dev/null +++ b/drivers/hwmon/pmbus/adp1050.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices ADP1050 + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> + +#include "pmbus.h" + +static struct pmbus_driver_info adp1050_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_TEMP, +}; + +static int adp1050_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &adp1050_info); +} + +static const struct i2c_device_id adp1050_id[] = { + {"adp1050"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, adp1050_id); + +static const struct of_device_id adp1050_of_match[] = { + { .compatible = "adi,adp1050"}, + {} +}; +MODULE_DEVICE_TABLE(of, adp1050_of_match); + +static struct i2c_driver adp1050_driver = { + .driver = { + .name = "adp1050", + .of_match_table = adp1050_of_match, + }, + .probe = adp1050_probe, + .id_table = adp1050_id, +}; +module_i2c_driver(adp1050_driver); + +MODULE_AUTHOR("Radu Sabau <radu.sabau@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADP1050 HWMON PMBus Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index 7c5f4b10a7c1..ddf9d9a2958c 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -129,4 +129,4 @@ module_i2c_driver(pfe_pmbus_driver); MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index 0dce26c35556..6c3875ba37a0 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -205,4 +205,4 @@ module_i2c_driver(bpa_rs600_driver); MODULE_AUTHOR("Chris Packham"); MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c new file mode 100644 index 000000000000..164b33fed312 --- /dev/null +++ b/drivers/hwmon/pmbus/crps.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2024 IBM Corp. + */ + +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/pmbus.h> + +#include "pmbus.h" + +static const struct i2c_device_id crps_id[] = { + { "intel_crps185" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, crps_id); + +static struct pmbus_driver_info crps_info = { + .pages = 1, + /* PSU uses default linear data format. */ + .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_IIN | + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12, +}; + +static int crps_probe(struct i2c_client *client) +{ + int rc; + struct device *dev = &client->dev; + char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rc < 0) + return dev_err_probe(dev, rc, "Failed to read PMBUS_MFR_MODEL\n"); + + if (rc != 7 || strncmp(buf, "03NK260", 7)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, "Model '%s' not supported\n", buf); + } + + rc = pmbus_do_probe(client, &crps_info); + if (rc) + return dev_err_probe(dev, rc, "Failed to probe\n"); + + return 0; +} + +static const struct of_device_id crps_of_match[] = { + { + .compatible = "intel,crps185", + }, + {} +}; +MODULE_DEVICE_TABLE(of, crps_of_match); + +static struct i2c_driver crps_driver = { + .driver = { + .name = "crps", + .of_match_table = crps_of_match, + }, + .probe = crps_probe, + .id_table = crps_id, +}; + +module_i2c_driver(crps_driver); + +MODULE_AUTHOR("Ninad Palsule"); +MODULE_DESCRIPTION("PMBus driver for Intel Common Redundant power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c index 4dd3b6686d6a..3850eaea75da 100644 --- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c +++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c @@ -127,4 +127,4 @@ module_i2c_driver(ahe50dc_fan_driver); MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>"); MODULE_DESCRIPTION("Driver for Delta AHE-50DC power shelf fan control module"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index 04e0d598a6e5..325111a955e6 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -190,12 +190,19 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = { MODULE_DEVICE_TABLE(of, dps920ab_of_match); +static const struct i2c_device_id dps920ab_device_id[] = { + { "dps920ab" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dps920ab_device_id); + static struct i2c_driver dps920ab_driver = { .driver = { .name = "dps920ab", .of_match_table = of_match_ptr(dps920ab_of_match), }, .probe = dps920ab_probe, + .id_table = dps920ab_device_id, }; module_i2c_driver(dps920ab_driver); @@ -203,4 +210,4 @@ module_i2c_driver(dps920ab_driver); MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); MODULE_DESCRIPTION("PMBus driver for Delta DPS920AB PSU"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c index 72a7c261ef06..a4dc09e2ef75 100644 --- a/drivers/hwmon/pmbus/fsp-3y.c +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -291,4 +291,4 @@ module_i2c_driver(fsp3y_driver); MODULE_AUTHOR("Václav Kubernát"); MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 1ba4c5e95820..d05ef7a968a9 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -614,4 +614,4 @@ module_i2c_driver(ibm_cffps_driver); MODULE_AUTHOR("Eddie James"); MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index dfeae68b5e52..074e0f164ee1 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -197,7 +197,7 @@ static int ipsps_probe(struct i2c_client *client) } static const struct i2c_device_id ipsps_id[] = { - { "ipsps1", 0 }, + { "ipsps1" }, {} }; MODULE_DEVICE_TABLE(i2c, ipsps_id); @@ -224,4 +224,4 @@ module_i2c_driver(ipsps_driver); MODULE_AUTHOR("John Wang"); MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index e3ee5c1bd967..46d8f334d49a 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -126,7 +126,7 @@ static int ir35221_probe(struct i2c_client *client) } static const struct i2c_device_id ir35221_id[] = { - {"ir35221", 0}, + {"ir35221"}, {} }; @@ -145,4 +145,4 @@ module_i2c_driver(ir35221_driver); MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com"); MODULE_DESCRIPTION("PMBus driver for IR35221"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c index a263afeb8ac1..34ce15fc708b 100644 --- a/drivers/hwmon/pmbus/ir36021.c +++ b/drivers/hwmon/pmbus/ir36021.c @@ -51,7 +51,7 @@ static int ir36021_probe(struct i2c_client *client) } static const struct i2c_device_id ir36021_id[] = { - { "ir36021", 0 }, + { "ir36021" }, {}, }; MODULE_DEVICE_TABLE(i2c, ir36021_id); @@ -76,4 +76,4 @@ module_i2c_driver(ir36021_driver); MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>"); MODULE_DESCRIPTION("PMBus driver for Infineon IR36021"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index 69e18cb468f6..7b4188e8bf48 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -53,10 +53,10 @@ static int ir38064_probe(struct i2c_client *client) } static const struct i2c_device_id ir38064_id[] = { - {"ir38060", 0}, - {"ir38064", 0}, - {"ir38164", 0}, - {"ir38263", 0}, + {"ir38060"}, + {"ir38064"}, + {"ir38164"}, + {"ir38263"}, {} }; @@ -87,4 +87,4 @@ module_i2c_driver(ir38064_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon IR38064 and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index 146d32a35a7c..43674c64841d 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -44,7 +44,7 @@ static int irps5401_probe(struct i2c_client *client) } static const struct i2c_device_id irps5401_id[] = { - {"irps5401", 0}, + {"irps5401"}, {} }; @@ -63,4 +63,4 @@ module_i2c_driver(irps5401_driver); MODULE_AUTHOR("Robert Hancock"); MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 7e53fb1d5ea3..2af921039309 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/string.h> #include <linux/sysfs.h> @@ -20,6 +21,7 @@ #define ISL68137_VOUT_AVS 0x30 #define RAA_DMPVR2_READ_VMON 0xc8 +#define MAX_CHANNELS 4 enum chips { isl68137, @@ -72,6 +74,17 @@ enum variants { raa_dmpvr2_hv, }; +struct isl68137_channel { + u32 vout_voltage_divider[2]; +}; + +struct isl68137_data { + struct pmbus_driver_info info; + struct isl68137_channel channel[MAX_CHANNELS]; +}; + +#define to_isl68137_data(x) container_of(x, struct isl68137_data, info) + static const struct i2c_device_id raa_dmpvr_id[]; static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, @@ -163,13 +176,32 @@ static const struct attribute_group *isl68137_attribute_groups[] = { static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, int phase, int reg) { + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct isl68137_data *data = to_isl68137_data(info); int ret; + u64 temp; switch (reg) { case PMBUS_VIRT_READ_VMON: ret = pmbus_read_word_data(client, page, phase, RAA_DMPVR2_READ_VMON); break; + case PMBUS_READ_POUT: + case PMBUS_READ_VOUT: + /* + * In cases where a voltage divider is attached to the target + * rail between Vout and the Vsense pin, both Vout and Pout + * should be scaled by the voltage divider scaling factor. + * I.e. Vout = Vsense * Rtotal / Rout + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret > 0) { + temp = DIV_U64_ROUND_CLOSEST((u64)ret * + data->channel[page].vout_voltage_divider[1], + data->channel[page].vout_voltage_divider[0]); + ret = clamp_val(temp, 0, 0xffff); + } + break; default: ret = -ENODATA; break; @@ -178,6 +210,40 @@ static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, return ret; } +static int raa_dmpvr2_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct isl68137_data *data = to_isl68137_data(info); + int ret; + u64 temp; + + switch (reg) { + case PMBUS_VOUT_MAX: + case PMBUS_VOUT_MARGIN_HIGH: + case PMBUS_VOUT_MARGIN_LOW: + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_VOUT_COMMAND: + /* + * In cases where a voltage divider is attached to the target + * rail between Vout and the Vsense pin, Vout related PMBus + * commands should be scaled based on the expected voltage + * at the Vsense pin. + * I.e. Vsense = Vout * Rout / Rtotal + */ + temp = DIV_U64_ROUND_CLOSEST((u64)word * + data->channel[page].vout_voltage_divider[0], + data->channel[page].vout_voltage_divider[1]); + ret = clamp_val(temp, 0, 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static struct pmbus_driver_info raa_dmpvr_info = { .pages = 3, .format[PSC_VOLTAGE_IN] = direct, @@ -220,14 +286,90 @@ static struct pmbus_driver_info raa_dmpvr_info = { | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, }; +static int isl68137_probe_child_from_dt(struct device *dev, + struct device_node *child, + struct isl68137_data *data) +{ + u32 channel, rout, rtotal; + int err; + + err = of_property_read_u32(child, "reg", &channel); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + if (channel >= data->info.pages) { + dev_err(dev, "invalid reg %d of %pOFn\n", channel, child); + return -EINVAL; + } + + err = of_property_read_u32_array(child, "vout-voltage-divider", + data->channel[channel].vout_voltage_divider, + ARRAY_SIZE(data->channel[channel].vout_voltage_divider)); + if (err && err != -EINVAL) { + dev_err(dev, + "malformed vout-voltage-divider value for channel %d\n", + channel); + return err; + } + + rout = data->channel[channel].vout_voltage_divider[0]; + rtotal = data->channel[channel].vout_voltage_divider[1]; + if (rout == 0) { + dev_err(dev, + "Voltage divider output resistance must be greater than 0\n"); + return -EINVAL; + } + if (rtotal < rout) { + dev_err(dev, + "Voltage divider total resistance is less than output resistance\n"); + return -EINVAL; + } + + return 0; +} + +static int isl68137_probe_from_dt(struct device *dev, + struct isl68137_data *data) +{ + const struct device_node *np = dev->of_node; + struct device_node *child; + int err; + + for_each_child_of_node(np, child) { + if (strcmp(child->name, "channel")) + continue; + + err = isl68137_probe_child_from_dt(dev, child, data); + if (err) + return err; + } + + return 0; +} + static int isl68137_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct pmbus_driver_info *info; + struct isl68137_data *data; + int i, err; - info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); - if (!info) + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; - memcpy(info, &raa_dmpvr_info, sizeof(*info)); + + /* + * Initialize all voltage dividers to Rout=1 and Rtotal=1 to simplify + * logic in PMBus word read/write functions + */ + for (i = 0; i < MAX_CHANNELS; i++) + memset(data->channel[i].vout_voltage_divider, + 1, + sizeof(data->channel[i].vout_voltage_divider)); + + memcpy(&data->info, &raa_dmpvr_info, sizeof(data->info)); + info = &data->info; switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) { case raa_dmpvr1_2rail: @@ -237,11 +379,14 @@ static int isl68137_probe(struct i2c_client *client) info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT; + info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; info->groups = isl68137_attribute_groups; break; case raa_dmpvr2_1rail: info->pages = 1; info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; case raa_dmpvr2_2rail_nontc: info->func[0] &= ~PMBUS_HAVE_TEMP3; @@ -250,9 +395,11 @@ static int isl68137_probe(struct i2c_client *client) case raa_dmpvr2_2rail: info->pages = 2; info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; case raa_dmpvr2_3rail: info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; case raa_dmpvr2_hv: info->pages = 1; @@ -263,11 +410,16 @@ static int isl68137_probe(struct i2c_client *client) info->m[PSC_POWER] = 2; info->R[PSC_POWER] = -1; info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; default: return -ENODEV; } + err = isl68137_probe_from_dt(dev, data); + if (err) + return err; + return pmbus_do_probe(client, info); } @@ -318,11 +470,59 @@ static const struct i2c_device_id raa_dmpvr_id[] = { MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); +static const struct of_device_id isl68137_of_match[] = { + { .compatible = "isil,isl68137", .data = (void *)raa_dmpvr1_2rail }, + { .compatible = "renesas,isl68220", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68221", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68222", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68223", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68224", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68225", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68226", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68227", .data = (void *)raa_dmpvr2_1rail }, + { .compatible = "renesas,isl68229", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68233", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68239", .data = (void *)raa_dmpvr2_3rail }, + + { .compatible = "renesas,isl69222", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69223", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69224", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69225", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69227", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69228", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69234", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69236", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69239", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69242", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69243", .data = (void *)raa_dmpvr2_1rail }, + { .compatible = "renesas,isl69247", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69248", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69254", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69255", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69256", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69259", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "isil,isl69260", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69268", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "isil,isl69269", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69298", .data = (void *)raa_dmpvr2_2rail }, + + { .compatible = "renesas,raa228000", .data = (void *)raa_dmpvr2_hv }, + { .compatible = "renesas,raa228004", .data = (void *)raa_dmpvr2_hv }, + { .compatible = "renesas,raa228006", .data = (void *)raa_dmpvr2_hv }, + { .compatible = "renesas,raa228228", .data = (void *)raa_dmpvr2_2rail_nontc }, + { .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail }, + { }, +}; + +MODULE_DEVICE_TABLE(of, isl68137_of_match); + /* This is the driver that will be inserted */ static struct i2c_driver isl68137_driver = { .driver = { - .name = "isl68137", - }, + .name = "isl68137", + .of_match_table = isl68137_of_match, + }, .probe = isl68137_probe, .id_table = raa_dmpvr_id, }; @@ -332,4 +532,4 @@ module_i2c_driver(isl68137_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index cfffa4cdc0df..40b0dda32ea6 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -17,7 +17,7 @@ #include <linux/of.h> #include "pmbus.h" -enum chips { lm25056 = 1, lm25066, lm5064, lm5066, lm5066i }; +enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i }; #define LM25066_READ_VAUX 0xd0 #define LM25066_MFR_READ_IIN 0xd1 @@ -569,4 +569,4 @@ module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c index 28afc5f15ae8..9d6d50f39bd6 100644 --- a/drivers/hwmon/pmbus/lt7182s.c +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -168,7 +168,7 @@ static int lt7182s_probe(struct i2c_client *client) } static const struct i2c_device_id lt7182s_id[] = { - { "lt7182s", 0 }, + { "lt7182s" }, {} }; MODULE_DEVICE_TABLE(i2c, lt7182s_id); @@ -192,4 +192,4 @@ module_i2c_driver(lt7182s_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Analog Devices LT7182S"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 73a86f4d6472..4c306943383a 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -23,7 +23,8 @@ enum chips { /* Managers */ ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, /* Controllers */ - ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880, + ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, + ltc7841, ltc7880, /* Modules */ ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, ltm4700, @@ -50,7 +51,7 @@ enum chips { #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 -/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7880 */ +/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7841 and LTC7880 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 /* LTC2975 only */ @@ -80,6 +81,7 @@ enum chips { #define LTC3887_ID 0x4700 #define LTC3889_ID 0x4900 #define LTC7132_ID 0x4CE0 +#define LTC7841_ID 0x40D0 #define LTC7880_ID 0x49E0 #define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ #define LTM2987_ID_B 0x8020 @@ -548,6 +550,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltc3887", ltc3887}, {"ltc3889", ltc3889}, {"ltc7132", ltc7132}, + {"ltc7841", ltc7841}, {"ltc7880", ltc7880}, {"ltm2987", ltm2987}, {"ltm4664", ltm4664}, @@ -654,6 +657,8 @@ static int ltc2978_get_id(struct i2c_client *client) return ltc3889; else if (chip_id == LTC7132_ID) return ltc7132; + else if (chip_id == LTC7841_ID) + return ltc7841; else if (chip_id == LTC7880_ID) return ltc7880; else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B) @@ -854,6 +859,16 @@ static int ltc2978_probe(struct i2c_client *client) | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; break; + case ltc7841: + data->features |= FEAT_CLEAR_PEAKS; + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; default: return -ENODEV; } @@ -907,6 +922,7 @@ static const struct of_device_id ltc2978_of_match[] = { { .compatible = "lltc,ltc3887" }, { .compatible = "lltc,ltc3889" }, { .compatible = "lltc,ltc7132" }, + { .compatible = "lltc,ltc7841" }, { .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltm2987" }, { .compatible = "lltc,ltm4664" }, @@ -936,4 +952,4 @@ module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index f2023b17aa8d..824c16a75e2c 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -143,7 +143,7 @@ static int ltc3815_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc3815_id[] = { - {"ltc3815", 0}, + {"ltc3815"}, { } }; MODULE_DEVICE_TABLE(i2c, ltc3815_id); @@ -208,4 +208,4 @@ module_i2c_driver(ltc3815_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC3815"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ltc4286.c b/drivers/hwmon/pmbus/ltc4286.c index 9e7ceeb7e789..aabd0bcdfeee 100644 --- a/drivers/hwmon/pmbus/ltc4286.c +++ b/drivers/hwmon/pmbus/ltc4286.c @@ -58,8 +58,8 @@ static struct pmbus_driver_info ltc4286_info = { }; static const struct i2c_device_id ltc4286_id[] = { - { "ltc4286", 0 }, - { "ltc4287", 1 }, + { "ltc4286", }, + { "ltc4287", }, {} }; MODULE_DEVICE_TABLE(i2c, ltc4286_id); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index 2cfaa62aedd6..d5810b88ea8d 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -23,16 +23,15 @@ #include "pmbus.h" static const struct i2c_device_id max15301_id[] = { - {"bmr461", 0}, - {"max15301", 0}, + { "bmr461" }, + { "max15301" }, + { "max15303" }, {} }; MODULE_DEVICE_TABLE(i2c, max15301_id); struct max15301_data { int id; - ktime_t access; /* Chip access time */ - int delay; /* Delay between chip accesses in us */ struct pmbus_driver_info info; }; @@ -55,89 +54,6 @@ static struct max15301_data max15301_data = { } }; -/* This chip needs a delay between accesses */ -static inline void max15301_wait(const struct max15301_data *data) -{ - if (data->delay) { - s64 delta = ktime_us_delta(ktime_get(), data->access); - - if (delta < data->delay) - udelay(data->delay - delta); - } -} - -static int max15301_read_word_data(struct i2c_client *client, int page, - int phase, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - if (reg >= PMBUS_VIRT_BASE) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_read_word_data(client, page, phase, reg); - data->access = ktime_get(); - - return ret; -} - -static int max15301_read_byte_data(struct i2c_client *client, int page, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_read_byte_data(client, page, reg); - data->access = ktime_get(); - - return ret; -} - -static int max15301_write_word_data(struct i2c_client *client, int page, int reg, - u16 word) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - if (reg >= PMBUS_VIRT_BASE) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_write_word_data(client, page, reg, word); - data->access = ktime_get(); - - return ret; -} - -static int max15301_write_byte(struct i2c_client *client, int page, u8 value) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_write_byte(client, page, value); - data->access = ktime_get(); - - return ret; -} - static int max15301_probe(struct i2c_client *client) { int status; @@ -164,12 +80,7 @@ static int max15301_probe(struct i2c_client *client) return -ENODEV; } - max15301_data.delay = delay; - - info->read_byte_data = max15301_read_byte_data; - info->read_word_data = max15301_read_word_data; - info->write_byte = max15301_write_byte; - info->write_word_data = max15301_write_word_data; + info->access_delay = delay; return pmbus_do_probe(client, info); } @@ -187,4 +98,4 @@ module_i2c_driver(max15301_driver); MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index a573a0ab9e48..eb84915c2a83 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -91,7 +91,7 @@ static int max16064_probe(struct i2c_client *client) } static const struct i2c_device_id max16064_id[] = { - {"max16064", 0}, + {"max16064"}, {} }; @@ -111,4 +111,4 @@ module_i2c_driver(max16064_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index 3ab219504600..d696e506aafb 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -366,4 +366,4 @@ module_i2c_driver(max16601_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index d56ec24764fd..95869d198ecf 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -787,4 +787,4 @@ module_i2c_driver(max20730_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index 6ebd71cd081b..ac8c43122133 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -32,7 +32,7 @@ static int max20751_probe(struct i2c_client *client) } static const struct i2c_device_id max20751_id[] = { - {"max20751", 0}, + {"max20751"}, {} }; @@ -51,4 +51,4 @@ module_i2c_driver(max20751_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 5d13bbfc8f47..1f94d38a1637 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -518,9 +518,9 @@ static int max31785_probe(struct i2c_client *client) } static const struct i2c_device_id max31785_id[] = { - { "max31785", 0 }, - { "max31785a", 0 }, - { "max31785b", 0 }, + { "max31785" }, + { "max31785a" }, + { "max31785b" }, { }, }; @@ -549,4 +549,4 @@ module_i2c_driver(max31785_driver); MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index fe7f6b1b0985..c9dda33831ff 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -529,4 +529,4 @@ module_i2c_driver(max34440_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index ae8573fdf5ba..b3a2a7492bbf 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -171,7 +171,7 @@ static int max8688_probe(struct i2c_client *client) } static const struct i2c_device_id max8688_id[] = { - {"max8688", 0}, + {"max8688"}, { } }; @@ -191,4 +191,4 @@ module_i2c_driver(max8688_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c index 6969350f5d7d..e83c70a3583f 100644 --- a/drivers/hwmon/pmbus/mp2856.c +++ b/drivers/hwmon/pmbus/mp2856.c @@ -46,7 +46,7 @@ #define MP2856_PAGE_NUM 2 -enum chips { mp2856 = 1, mp2857 }; +enum chips { mp2856, mp2857 }; static const int mp2856_max_phases[][MP2856_PAGE_NUM] = { [mp2856] = { MP2856_MAX_PHASE_RAIL1, MP2856_MAX_PHASE_RAIL2 }, @@ -66,7 +66,6 @@ struct mp2856_data { int vout_format[MP2856_PAGE_NUM]; int curr_sense_gain[MP2856_PAGE_NUM]; int max_phases[MP2856_PAGE_NUM]; - enum chips chip_id; }; #define to_mp2856_data(x) container_of(x, struct mp2856_data, info) @@ -397,6 +396,7 @@ static int mp2856_probe(struct i2c_client *client) { struct pmbus_driver_info *info; struct mp2856_data *data; + enum chips chip_id; int ret; data = devm_kzalloc(&client->dev, sizeof(struct mp2856_data), @@ -404,9 +404,9 @@ static int mp2856_probe(struct i2c_client *client) if (!data) return -ENOMEM; - data->chip_id = (enum chips)(uintptr_t)i2c_get_match_data(client); + chip_id = (kernel_ulong_t)i2c_get_match_data(client); - memcpy(data->max_phases, mp2856_max_phases[data->chip_id], + memcpy(data->max_phases, mp2856_max_phases[chip_id], sizeof(data->max_phases)); memcpy(&data->info, &mp2856_info, sizeof(*info)); @@ -463,4 +463,4 @@ module_i2c_driver(mp2856_driver); MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2856/MP2857 device"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c index 50662ed8e3d5..772a623ca7d0 100644 --- a/drivers/hwmon/pmbus/mp2888.c +++ b/drivers/hwmon/pmbus/mp2888.c @@ -378,7 +378,7 @@ static int mp2888_probe(struct i2c_client *client) } static const struct i2c_device_id mp2888_id[] = { - {"mp2888", 0}, + {"mp2888"}, {} }; @@ -404,4 +404,4 @@ module_i2c_driver(mp2888_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2888 device"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c new file mode 100644 index 000000000000..f8f4c91ec23c --- /dev/null +++ b/drivers/hwmon/pmbus/mp2891.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2891) + */ + +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +/* + * Vender specific registers, the register MFR_SVI3_IOUT_PRT(0x65), + * MFR_VOUT_LOOP_CTRL(0xBD), READ_PIN_EST(0x94)and READ_IIN_EST(0x95) + * redefine the standard PMBUS register. The MFR_SVI3_IOUT_PRT(0x65) + * is used to identify the iout scale and the MFR_VOUT_LOOP_CTRL(0xBD) + * is used to identify the vout scale. The READ_PIN_EST(0x94) is used + * to read input power per rail. The MP2891 does not have standard + * READ_IIN register(0x89), the iin telemetry can be obtained through + * the vendor redefined register READ_IIN_EST(0x95). + */ +#define MFR_VOUT_LOOP_CTRL 0xBD +#define READ_PIN_EST 0x94 +#define READ_IIN_EST 0x95 +#define MFR_SVI3_IOUT_PRT 0x65 + +#define MP2891_TEMP_LIMIT_OFFSET 40 +#define MP2891_PIN_LIMIT_UINT 2 +#define MP2891_IOUT_LIMIT_UINT 8 +#define MP2891_IOUT_SCALE_DIV 32 +#define MP2891_VOUT_SCALE_DIV 100 +#define MP2891_OVUV_DELTA_SCALE 50 +#define MP2891_OV_LIMIT_SCALE 20 +#define MP2891_UV_LIMIT_SCALE 5 + +#define MP2891_PAGE_NUM 2 + +#define MP2891_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_INPUT | \ + PMBUS_HAVE_STATUS_TEMP) + +#define MP2891_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_INPUT | \ + PMBUS_HAVE_STATUS_TEMP) + +struct mp2891_data { + struct pmbus_driver_info info; + int vout_scale[MP2891_PAGE_NUM]; + int iout_scale[MP2891_PAGE_NUM]; +}; + +#define to_mp2891_data(x) container_of(x, struct mp2891_data, info) + +/* Converts a LINEAR11 value to DIRECT format */ +static u16 mp2891_reg2data_linear11(u16 word) +{ + s16 exponent; + s32 mantissa; + s64 val; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + val = mantissa; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +static int +mp2891_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_VOUT_LOOP_CTRL); + if (ret < 0) + return ret; + + /* + * The output voltage is equal to the READ_VOUT(0x8B) register value multiplied + * by vout_scale. + * Obtain vout scale from the register MFR_VOUT_LOOP_CTRL, bits 15-14,bit 13. + * If MFR_VOUT_LOOP_CTRL[13] = 1, the vout scale is below: + * 2.5mV/LSB + * If MFR_VOUT_LOOP_CTRL[13] = 0, the vout scale is decided by + * MFR_VOUT_LOOP_CTRL[15:14]: + * 00b - 6.25mV/LSB, 01b - 5mV/LSB, 10b - 2mV/LSB, 11b - 1mV + */ + if (ret & GENMASK(13, 13)) { + data->vout_scale[page] = 250; + } else { + ret = FIELD_GET(GENMASK(15, 14), ret); + if (ret == 0) + data->vout_scale[page] = 625; + else if (ret == 1) + data->vout_scale[page] = 500; + else if (ret == 2) + data->vout_scale[page] = 200; + else + data->vout_scale[page] = 100; + } + + return 0; +} + +static int +mp2891_identify_iout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_SVI3_IOUT_PRT); + if (ret < 0) + return ret; + + /* + * The output current is equal to the READ_IOUT(0x8C) register value + * multiplied by iout_scale. + * Obtain iout_scale from the register MFR_SVI3_IOUT_PRT[2:0]. + * The value is selected as below: + * 000b - 1A/LSB, 001b - (1/32)A/LSB, 010b - (1/16)A/LSB, + * 011b - (1/8)A/LSB, 100b - (1/4)A/LSB, 101b - (1/2)A/LSB + * 110b - 1A/LSB, 111b - 2A/LSB + */ + switch (ret & GENMASK(2, 0)) { + case 0: + case 6: + data->iout_scale[page] = 32; + break; + case 1: + data->iout_scale[page] = 1; + break; + case 2: + data->iout_scale[page] = 2; + break; + case 3: + data->iout_scale[page] = 4; + break; + case 4: + data->iout_scale[page] = 8; + break; + case 5: + data->iout_scale[page] = 16; + break; + default: + data->iout_scale[page] = 64; + break; + } + + return 0; +} + +static int mp2891_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + /* Identify vout scale for rail 1. */ + ret = mp2891_identify_vout_scale(client, info, 0); + if (ret < 0) + return ret; + + /* Identify vout scale for rail 2. */ + ret = mp2891_identify_vout_scale(client, info, 1); + if (ret < 0) + return ret; + + /* Identify iout scale for rail 1. */ + ret = mp2891_identify_iout_scale(client, info, 0); + if (ret < 0) + return ret; + + /* Identify iout scale for rail 2. */ + return mp2891_identify_iout_scale(client, info, 1); +} + +static int mp2891_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The MP2891 does not follow standard PMBus protocol completely, the + * PMBUS_VOUT_MODE(0x20) in MP2891 is reserved and 0x00 is always + * returned when the register is read. But the calculation of vout in + * this driver is based on direct format. As a result, the format of + * vout is enforced to direct. + */ + ret = PB_VOUT_MODE_DIRECT; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int mp2891_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret & GENMASK(9, 0); + break; + case PMBUS_READ_IIN: + /* + * The MP2891 does not have standard PMBUS_READ_IIN register(0x89), + * the iin telemetry can be obtained through the vender redefined + * register READ_IIN_EST(0x95). The MP2891 PMBUS_READ_IIN register + * is linear11 format, But the pout scale is set to 1A/Lsb(using + * r/m/b scale). As a result, the iin read from MP2891 should be + * calculated to A, then return the result to pmbus core. + */ + ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST); + if (ret < 0) + return ret; + + ret = mp2891_reg2data_linear11(ret); + break; + case PMBUS_READ_PIN: + /* + * The MP2891 has standard PMBUS_READ_PIN register(0x97), but this + * is not used to read the input power per rail. The input power + * per rail is read through the vender redefined register + * READ_PIN_EST(0x94). The MP2891 PMBUS_READ_PIN register is linear11 + * format, But the pout scale is set to 1W/Lsb(using r/m/b scale). + * As a result, the pin read from MP2891 should be calculated to W, + * then return the result to pmbus core. + */ + ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST); + if (ret < 0) + return ret; + + ret = mp2891_reg2data_linear11(ret); + break; + case PMBUS_READ_POUT: + /* + * The MP2891 PMBUS_READ_POUT register is linear11 format, and the + * exponent is not a constant value. But the pout scale is set to + * 1W/Lsb(using r/m/b scale). As a result, the pout read from MP2891 + * should be calculated to W, then return the result to pmbus core. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = mp2891_reg2data_linear11(ret); + break; + case PMBUS_READ_VOUT: + case PMBUS_VOUT_UV_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret * data->vout_scale[page], MP2891_VOUT_SCALE_DIV); + break; + case PMBUS_READ_IOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(10, 0)) * data->iout_scale[page], + MP2891_IOUT_SCALE_DIV); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT + * is 1°C/LSB and they have 40°C offset. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = (ret & GENMASK(7, 0)) - MP2891_TEMP_LIMIT_OFFSET; + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + /* + * The MP2891 PMBUS_VIN_OV_FAULT_LIMIT scale is 125mV/Lsb. + * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale). + * As a result, the limit value should be multiplied by 4. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = (ret & GENMASK(7, 0)) * 4; + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE - + (FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE; + else + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE; + + ret = ret < 0 ? 0 : ret; + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE + + (FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE; + else + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE; + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * data->iout_scale[page] * + MP2891_IOUT_LIMIT_UINT, MP2891_IOUT_SCALE_DIV); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + /* + * The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale + * is set to 1A/Lsb(using r/m/b scale), so the word data should be + * divided by 2. + */ + ret = pmbus_read_word_data(client, 0, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)), 2); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale + * is set to 1W/Lsb(using r/m/b scale), so the word data should be + * multiplied by 2. + */ + ret = pmbus_read_word_data(client, 0, phase, reg); + if (ret < 0) + return ret; + + ret = (ret & GENMASK(9, 0)) * MP2891_PIN_LIMIT_UINT; + break; + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2891_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + switch (reg) { + case PMBUS_VOUT_UV_WARN_LIMIT: + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word * MP2891_VOUT_SCALE_DIV, + data->vout_scale[page])); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + /* + * The PMBUS_VOUT_UV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word + + (FIELD_GET(GENMASK(11, 8), ret) + 1) * + MP2891_OVUV_DELTA_SCALE, + MP2891_UV_LIMIT_SCALE))); + else + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word, + MP2891_UV_LIMIT_SCALE))); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* + * The PMBUS_VOUT_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word - + (FIELD_GET(GENMASK(11, 8), ret) + 1) * + MP2891_OVUV_DELTA_SCALE, + MP2891_OV_LIMIT_SCALE))); + else + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word, + MP2891_OV_LIMIT_SCALE))); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + /* + * The PMBUS_VIN_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 + * should not be changed. The scale of PMBUS_VIN_OV_FAULT_LIMIT is 125mV/Lsb, + * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale), so the word data + * should be divided by 4. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word, 4))); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT + * have 40°C offset. The bit0-bit7 is the limit value, and bit8-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), word + MP2891_TEMP_LIMIT_OFFSET)); + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word * MP2891_IOUT_SCALE_DIV, + MP2891_IOUT_LIMIT_UINT * + data->iout_scale[page])); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + /* + * The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale + * is set to 1A/Lsb(using r/m/b scale), so the word data should be + * multiplied by 2. + */ + ret = pmbus_write_word_data(client, page, reg, word * 2); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale + * is set to 1W/Lsb(using r/m/b scale), so the word data should be + * divided by 2. + */ + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word, MP2891_PIN_LIMIT_UINT)); + break; + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct pmbus_driver_info mp2891_info = { + .pages = MP2891_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_POWER] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + + /* set vin scale 31.25mV/Lsb */ + .m[PSC_VOLTAGE_IN] = 32, + .R[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_IN] = 0, + + /* set temp scale 1000m°C/Lsb */ + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_TEMPERATURE] = 0, + + .m[PSC_CURRENT_IN] = 1, + .R[PSC_CURRENT_IN] = 0, + .b[PSC_CURRENT_IN] = 0, + + .m[PSC_CURRENT_OUT] = 1, + .R[PSC_CURRENT_OUT] = 0, + .b[PSC_CURRENT_OUT] = 0, + + .m[PSC_POWER] = 1, + .R[PSC_POWER] = 0, + .b[PSC_POWER] = 0, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP2891_RAIL1_FUNC, + .func[1] = MP2891_RAIL2_FUNC, + .read_word_data = mp2891_read_word_data, + .write_word_data = mp2891_write_word_data, + .read_byte_data = mp2891_read_byte_data, + .identify = mp2891_identify, +}; + +static int mp2891_probe(struct i2c_client *client) +{ + struct mp2891_data *data; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2891_info, sizeof(mp2891_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp2891_id[] = { + { "mp2891" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mp2891_id); + +static const struct of_device_id __maybe_unused mp2891_of_match[] = { + {.compatible = "mps,mp2891"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2891_of_match); + +static struct i2c_driver mp2891_driver = { + .driver = { + .name = "mp2891", + .of_match_table = mp2891_of_match, + }, + .probe = mp2891_probe, + .id_table = mp2891_id, +}; + +module_i2c_driver(mp2891_driver); + +MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP2891"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index e5fa10b3b8bc..c31982d85196 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -5,12 +5,14 @@ * Copyright (C) 2020 Nvidia Technologies Ltd. */ +#include <linux/bitops.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> + #include "pmbus.h" /* Vendor specific registers. */ @@ -97,6 +99,11 @@ static const int mp2975_max_phases[][MP2975_PAGE_NUM] = { [mp2971] = { MP2971_MAX_PHASE_RAIL1, MP2971_MAX_PHASE_RAIL2 }, }; +struct mp2975_driver_info { + const struct pmbus_driver_info *info; + enum chips chip_id; +}; + struct mp2975_data { struct pmbus_driver_info info; enum chips chip_id; @@ -110,15 +117,6 @@ struct mp2975_data { int curr_sense_gain[MP2975_PAGE_NUM]; }; -static const struct i2c_device_id mp2975_id[] = { - {"mp2971", mp2971}, - {"mp2973", mp2973}, - {"mp2975", mp2975}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, mp2975_id); - static const struct regulator_desc __maybe_unused mp2975_reg_desc[] = { PMBUS_REGULATOR("vout", 0), PMBUS_REGULATOR("vout", 1), @@ -392,6 +390,80 @@ static int mp2973_read_word_data(struct i2c_client *client, int page, return ret; } +static int mp2973_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + u8 target, mask; + long ret; + + if (reg != PMBUS_SMBALERT_MASK) + return -ENODATA; + + /* + * Vendor-specific SMBALERT_MASK register with 16 maskable bits. + */ + ret = pmbus_read_word_data(client, 0, 0, PMBUS_SMBALERT_MASK); + if (ret < 0) + return ret; + + target = word & 0xff; + mask = word >> 8; + +/* + * Set/Clear 'bit' in 'ret' based on condition followed by define for each bit in SMBALERT_MASK. + * Also bit 2 & 15 are reserved. + */ + +#define MP2973_TEMP_OT 0 +#define MP2973_VIN_UVLO 1 +#define MP2973_VIN_OVP 3 +#define MP2973_MTP_FAULT 4 +#define MP2973_OTHER_COMM 5 +#define MP2973_MTP_BLK_TRIG 6 +#define MP2973_PACKET_ERROR 7 +#define MP2973_INVALID_DATA 8 +#define MP2973_INVALID_COMMAND 9 +#define MP2973_IOUT_OC_LV 10 +#define MP2973_IOUT_OC 11 +#define MP2973_VOUT_MAX_MIN_WARNING 12 +#define MP2973_VOLTAGE_UV 13 +#define MP2973_VOLTAGE_OV 14 + + switch (target) { + case PMBUS_STATUS_CML: + __assign_bit(MP2973_INVALID_DATA, &ret, !(mask & PB_CML_FAULT_INVALID_DATA)); + __assign_bit(MP2973_INVALID_COMMAND, &ret, !(mask & PB_CML_FAULT_INVALID_COMMAND)); + __assign_bit(MP2973_OTHER_COMM, &ret, !(mask & PB_CML_FAULT_OTHER_COMM)); + __assign_bit(MP2973_PACKET_ERROR, &ret, !(mask & PB_CML_FAULT_PACKET_ERROR)); + break; + case PMBUS_STATUS_VOUT: + __assign_bit(MP2973_VOLTAGE_UV, &ret, !(mask & PB_VOLTAGE_UV_FAULT)); + __assign_bit(MP2973_VOLTAGE_OV, &ret, !(mask & PB_VOLTAGE_OV_FAULT)); + break; + case PMBUS_STATUS_IOUT: + __assign_bit(MP2973_IOUT_OC, &ret, !(mask & PB_IOUT_OC_FAULT)); + __assign_bit(MP2973_IOUT_OC_LV, &ret, !(mask & PB_IOUT_OC_LV_FAULT)); + break; + case PMBUS_STATUS_TEMPERATURE: + __assign_bit(MP2973_TEMP_OT, &ret, !(mask & PB_TEMP_OT_FAULT)); + break; + /* + * Map remaining bits to MFR specific to let the PMBUS core mask + * those bits by default. + */ + case PMBUS_STATUS_MFR_SPECIFIC: + __assign_bit(MP2973_VIN_UVLO, &ret, !(mask & BIT(1))); + __assign_bit(MP2973_VIN_OVP, &ret, !(mask & BIT(3))); + __assign_bit(MP2973_MTP_FAULT, &ret, !(mask & BIT(4))); + __assign_bit(MP2973_MTP_BLK_TRIG, &ret, !(mask & BIT(6))); + break; + default: + return 0; + } + + return pmbus_write_word_data(client, 0, PMBUS_SMBALERT_MASK, ret); +} + static int mp2975_read_word_data(struct i2c_client *client, int page, int phase, int reg) { @@ -867,7 +939,7 @@ mp2975_vout_per_rail_config_get(struct i2c_client *client, return 0; } -static struct pmbus_driver_info mp2975_info = { +static const struct pmbus_driver_info mp2975_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = linear, .format[PSC_VOLTAGE_OUT] = direct, @@ -892,7 +964,7 @@ static struct pmbus_driver_info mp2975_info = { #endif }; -static struct pmbus_driver_info mp2973_info = { +static const struct pmbus_driver_info mp2973_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = linear, .format[PSC_VOLTAGE_OUT] = direct, @@ -907,35 +979,41 @@ static struct pmbus_driver_info mp2973_info = { PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, .read_word_data = mp2973_read_word_data, + .write_word_data = mp2973_write_word_data, #if IS_ENABLED(CONFIG_SENSORS_MP2975_REGULATOR) .num_regulators = 1, .reg_desc = mp2975_reg_desc, #endif }; +static const struct mp2975_driver_info mp2975_ddinfo[] = { + [mp2975] = { .info = &mp2975_info, .chip_id = mp2975 }, + [mp2973] = { .info = &mp2973_info, .chip_id = mp2973 }, + [mp2971] = { .info = &mp2973_info, .chip_id = mp2971 }, +}; + static int mp2975_probe(struct i2c_client *client) { + const struct mp2975_driver_info *ddinfo; struct pmbus_driver_info *info; struct mp2975_data *data; int ret; + ddinfo = i2c_get_match_data(client); + if (!ddinfo) + return -ENODEV; + data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data), GFP_KERNEL); if (!data) return -ENOMEM; - if (client->dev.of_node) - data->chip_id = (enum chips)(unsigned long)of_device_get_match_data(&client->dev); - else - data->chip_id = i2c_match_id(mp2975_id, client)->driver_data; + data->chip_id = ddinfo->chip_id; memcpy(data->max_phases, mp2975_max_phases[data->chip_id], sizeof(data->max_phases)); - if (data->chip_id == mp2975) - memcpy(&data->info, &mp2975_info, sizeof(*info)); - else - memcpy(&data->info, &mp2973_info, sizeof(*info)); + memcpy(&data->info, ddinfo->info, sizeof(data->info)); info = &data->info; @@ -993,18 +1071,26 @@ static int mp2975_probe(struct i2c_client *client) return pmbus_do_probe(client, info); } -static const struct of_device_id __maybe_unused mp2975_of_match[] = { - {.compatible = "mps,mp2971", .data = (void *)mp2971}, - {.compatible = "mps,mp2973", .data = (void *)mp2973}, - {.compatible = "mps,mp2975", .data = (void *)mp2975}, +static const struct of_device_id mp2975_of_match[] = { + {.compatible = "mps,mp2971", .data = &mp2975_ddinfo[mp2971]}, + {.compatible = "mps,mp2973", .data = &mp2975_ddinfo[mp2973]}, + {.compatible = "mps,mp2975", .data = &mp2975_ddinfo[mp2975]}, {} }; MODULE_DEVICE_TABLE(of, mp2975_of_match); +static const struct i2c_device_id mp2975_id[] = { + {"mp2971", (kernel_ulong_t)&mp2975_ddinfo[mp2971]}, + {"mp2973", (kernel_ulong_t)&mp2975_ddinfo[mp2973]}, + {"mp2975", (kernel_ulong_t)&mp2975_ddinfo[mp2975]}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2975_id); + static struct i2c_driver mp2975_driver = { .driver = { .name = "mp2975", - .of_match_table = of_match_ptr(mp2975_of_match), + .of_match_table = mp2975_of_match, }, .probe = mp2975_probe, .id_table = mp2975_id, @@ -1015,4 +1101,4 @@ module_i2c_driver(mp2975_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c new file mode 100644 index 000000000000..81c84fc8ed47 --- /dev/null +++ b/drivers/hwmon/pmbus/mp2993.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2993) + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +#define MP2993_VOUT_OVUV_UINT 125 +#define MP2993_VOUT_OVUV_DIV 64 +#define MP2993_VIN_LIMIT_UINT 1 +#define MP2993_VIN_LIMIT_DIV 8 +#define MP2993_READ_VIN_UINT 1 +#define MP2993_READ_VIN_DIV 32 + +#define MP2993_PAGE_NUM 2 + +#define MP2993_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +#define MP2993_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +/* Converts a linear11 data exponent to a specified value */ +static u16 mp2993_linear11_exponent_transfer(u16 word, u16 expect_exponent) +{ + s16 exponent, mantissa, target_exponent; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; + + if (exponent > target_exponent) + mantissa = mantissa << (exponent - target_exponent); + else + mantissa = mantissa >> (target_exponent - exponent); + + return (mantissa & 0x7ff) | ((expect_exponent << 11) & 0xf800); +} + +static int +mp2993_set_vout_format(struct i2c_client *client, int page, int format) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, format); +} + +static int mp2993_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + /* Set vout to direct format for rail1. */ + ret = mp2993_set_vout_format(client, 0, PB_VOUT_MODE_DIRECT); + if (ret < 0) + return ret; + + /* Set vout to direct format for rail2. */ + return mp2993_set_vout_format(client, 1, PB_VOUT_MODE_DIRECT); +} + +static int mp2993_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret * MP2993_VOUT_OVUV_UINT, MP2993_VOUT_OVUV_DIV); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The MP2993 ot fault limit value and ot warn limit value + * per rail are always the same, so only PMBUS_OT_FAULT_LIMIT + * and PMBUS_OT_WARN_LIMIT register in page 0 are defined to + * indicates the limit value. + */ + ret = pmbus_read_word_data(client, 0, phase, reg); + break; + case PMBUS_READ_VIN: + /* The MP2993 vin scale is (1/32V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP2993_READ_VIN_UINT, + MP2993_READ_VIN_DIV); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* The MP2993 vin limit scale is (1/8V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP2993_VIN_LIMIT_UINT, + MP2993_VIN_LIMIT_DIV); + break; + case PMBUS_READ_IOUT: + case PMBUS_READ_IIN: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_READ_VOUT: + case PMBUS_READ_PIN: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2993_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = DIV_ROUND_CLOSEST(word * MP2993_VOUT_OVUV_DIV, MP2993_VOUT_OVUV_UINT); + ret = pmbus_write_word_data(client, 0, reg, ret); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The MP2993 ot fault limit value and ot warn limit value + * per rail are always the same, so only PMBUS_OT_FAULT_LIMIT + * and PMBUS_OT_WARN_LIMIT register in page 0 are defined to + * config the ot limit value. + */ + ret = pmbus_write_word_data(client, 0, reg, word); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* The MP2993 vin limit scale is (1/8V)/Lsb */ + ret = pmbus_write_word_data(client, 0, reg, + DIV_ROUND_CLOSEST(word * MP2993_VIN_LIMIT_DIV, + MP2993_VIN_LIMIT_UINT)); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + /* + * The PMBUS_IIN_OC_WARN_LIMIT of MP2993 is linear11 format, + * and the exponent is a constant value(5'b00000), so the + * exponent of word parameter should be converted to 5'b00000. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2993_linear11_exponent_transfer(word, 0x00)); + break; + // + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + /* + * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT + * of MP2993 can be regarded as linear11 format, and the + * exponent is a 5'b00001 or 5'b00000. To ensure a larger + * range of limit value, so the exponent of word parameter + * should be converted to 5'b00001. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2993_linear11_exponent_transfer(word, 0x01)); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct pmbus_driver_info mp2993_info = { + .pages = MP2993_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_POWER] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .m[PSC_VOLTAGE_IN] = 1, + .R[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_IN] = 0, + + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_TEMPERATURE] = 0, + + .func[0] = MP2993_RAIL1_FUNC, + .func[1] = MP2993_RAIL2_FUNC, + .read_word_data = mp2993_read_word_data, + .write_word_data = mp2993_write_word_data, + .identify = mp2993_identify, +}; + +static int mp2993_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &mp2993_info); +} + +static const struct i2c_device_id mp2993_id[] = { + { "mp2993" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mp2993_id); + +static const struct of_device_id __maybe_unused mp2993_of_match[] = { + {.compatible = "mps,mp2993"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2993_of_match); + +static struct i2c_driver mp2993_driver = { + .driver = { + .name = "mp2993", + .of_match_table = mp2993_of_match, + }, + .probe = mp2993_probe, + .id_table = mp2993_id, +}; + +module_i2c_driver(mp2993_driver); + +MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP2993"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp5023.c b/drivers/hwmon/pmbus/mp5023.c index 21acb7fd9a1a..c466d67e9a8f 100644 --- a/drivers/hwmon/pmbus/mp5023.c +++ b/drivers/hwmon/pmbus/mp5023.c @@ -64,4 +64,4 @@ module_i2c_driver(mp5023_driver); MODULE_AUTHOR("Howard Chiu <howard.chiu@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP5023 HSC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c new file mode 100644 index 000000000000..319ae2721bcf --- /dev/null +++ b/drivers/hwmon/pmbus/mp5920.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MP5920 and compatible chips. + */ + +#include <linux/i2c.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +static struct pmbus_driver_info mp5920_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 2266, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -1, + .m[PSC_VOLTAGE_OUT] = 2266, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -1, + .m[PSC_CURRENT_OUT] = 546, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -2, + .m[PSC_POWER] = 5840, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = -3, + .m[PSC_TEMPERATURE] = 1067, + .b[PSC_TEMPERATURE] = 20500, + .R[PSC_TEMPERATURE] = -2, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP, +}; + +static int mp5920_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read PMBUS_MFR_MODEL\n"); + + if (ret != 6 || strncmp(buf, "MP5920", 6)) { + return dev_err_probe(dev, -ENODEV, "Model '%.*s' not supported\n", + min_t(int, ret, sizeof(buf)), buf); + } + + return pmbus_do_probe(client, &mp5920_info); +} + +static const struct of_device_id mp5920_of_match[] = { + { .compatible = "mps,mp5920" }, + { } +}; + +MODULE_DEVICE_TABLE(of, mp5920_of_match); + +static const struct i2c_device_id mp5920_id[] = { + { "mp5920" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mp5920_id); + +static struct i2c_driver mp5920_driver = { + .driver = { + .name = "mp5920", + .of_match_table = mp5920_of_match, + }, + .probe = mp5920_probe, + .id_table = mp5920_id, +}; + +module_i2c_driver(mp5920_driver); + +MODULE_AUTHOR("Tony Ao <tony_ao@wiwynn.com>"); +MODULE_AUTHOR("Alex Vdovydchenko <xzeol@yahoo.com>"); +MODULE_DESCRIPTION("PMBus driver for MP5920 HSC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c index 1dfbab25a064..4ce381a39480 100644 --- a/drivers/hwmon/pmbus/mp5990.c +++ b/drivers/hwmon/pmbus/mp5990.c @@ -158,7 +158,7 @@ static const struct of_device_id mp5990_of_match[] = { }; static const struct i2c_device_id mp5990_id[] = { - {"mp5990", 0}, + {"mp5990"}, { } }; MODULE_DEVICE_TABLE(i2c, mp5990_id); @@ -176,4 +176,4 @@ module_i2c_driver(mp5990_driver); MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for MP5990 HSC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c new file mode 100644 index 000000000000..42ca6748777a --- /dev/null +++ b/drivers/hwmon/pmbus/mp9941.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP9941) + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +/* + * Vender specific registers. The MFR_ICC_MAX(0x02) is used to + * config the iin scale. The MFR_RESO_SET(0xC7) is used to + * config the vout format. The MFR_VR_MULTI_CONFIG_R1(0x0D) is + * used to identify the vout vid step. + */ +#define MFR_ICC_MAX 0x02 +#define MFR_RESO_SET 0xC7 +#define MFR_VR_MULTI_CONFIG_R1 0x0D + +#define MP9941_VIN_LIMIT_UINT 1 +#define MP9941_VIN_LIMIT_DIV 8 +#define MP9941_READ_VIN_UINT 1 +#define MP9941_READ_VIN_DIV 32 + +#define MP9941_PAGE_NUM 1 + +#define MP9941_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +struct mp9941_data { + struct pmbus_driver_info info; + int vid_resolution; +}; + +#define to_mp9941_data(x) container_of(x, struct mp9941_data, info) + +static int mp9941_set_vout_format(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_RESO_SET); + if (ret < 0) + return ret; + + /* + * page = 0, MFR_RESO_SET[7:6] defines the vout format + * 2'b11 set the vout format as direct + */ + ret = (ret & ~GENMASK(7, 6)) | FIELD_PREP(GENMASK(7, 6), 3); + + return i2c_smbus_write_word_data(client, MFR_RESO_SET, ret); +} + +static int +mp9941_identify_vid_resolution(struct i2c_client *client, struct pmbus_driver_info *info) +{ + struct mp9941_data *data = to_mp9941_data(info); + int ret; + + /* + * page = 2, MFR_VR_MULTI_CONFIG_R1[4:4] defines rail1 vid step value + * 1'b0 represents the vid step value is 10mV + * 1'b1 represents the vid step value is 5mV + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(4, 4), ret)) + data->vid_resolution = 5; + else + data->vid_resolution = 10; + + return 0; +} + +static int mp9941_identify_iin_scale(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_RESO_SET); + if (ret < 0) + return ret; + + ret = (ret & ~GENMASK(3, 2)) | FIELD_PREP(GENMASK(3, 2), 0); + + ret = i2c_smbus_write_word_data(client, MFR_RESO_SET, ret); + if (ret < 0) + return ret; + + /* + * page = 2, MFR_ICC_MAX[15:13] defines the iin scale + * 3'b000 set the iout scale as 0.5A/Lsb + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_ICC_MAX); + if (ret < 0) + return ret; + + ret = (ret & ~GENMASK(15, 13)) | FIELD_PREP(GENMASK(15, 13), 0); + + return i2c_smbus_write_word_data(client, MFR_ICC_MAX, ret); +} + +static int mp9941_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + ret = mp9941_identify_iin_scale(client); + if (ret < 0) + return ret; + + ret = mp9941_identify_vid_resolution(client, info); + if (ret < 0) + return ret; + + return mp9941_set_vout_format(client); +} + +static int mp9941_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp9941_data *data = to_mp9941_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VIN: + /* The MP9941 vin scale is (1/32V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP9941_READ_VIN_UINT, + MP9941_READ_VIN_DIV); + break; + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret & GENMASK(10, 0); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + /* The MP9941 vin ov limit scale is (1/8V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP9941_VIN_LIMIT_UINT, + MP9941_VIN_LIMIT_DIV); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret & GENMASK(7, 0); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + /* + * The vout scale is set to 1mV/Lsb(using r/m/b scale). + * But the vout uv limit and vout max/min scale is 1VID/Lsb, + * so the vout uv limit and vout max/min value should be + * multiplied by vid resolution. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret * data->vid_resolution; + break; + case PMBUS_READ_IOUT: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_VOUT: + case PMBUS_READ_PIN: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp9941_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp9941_data *data = to_mp9941_data(info); + int ret; + + switch (reg) { + case PMBUS_VIN_OV_FAULT_LIMIT: + /* The MP9941 vin ov limit scale is (1/8V)/Lsb */ + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word * MP9941_VIN_LIMIT_DIV, + MP9941_VIN_LIMIT_UINT)); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word, data->vid_resolution)); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct pmbus_driver_info mp9941_info = { + .pages = MP9941_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_TEMPERATURE] = 0, + + .m[PSC_VOLTAGE_IN] = 1, + .R[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_IN] = 0, + + .m[PSC_CURRENT_IN] = 2, + .R[PSC_CURRENT_IN] = 0, + .b[PSC_CURRENT_IN] = 0, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP9941_RAIL1_FUNC, + .read_word_data = mp9941_read_word_data, + .write_word_data = mp9941_write_word_data, + .identify = mp9941_identify, +}; + +static int mp9941_probe(struct i2c_client *client) +{ + struct mp9941_data *data; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp9941_info, sizeof(mp9941_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp9941_id[] = { + { "mp9941" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mp9941_id); + +static const struct of_device_id __maybe_unused mp9941_of_match[] = { + {.compatible = "mps,mp9941"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp9941_of_match); + +static struct i2c_driver mp9941_driver = { + .driver = { + .name = "mp9941", + .of_match_table = mp9941_of_match, + }, + .probe = mp9941_probe, + .id_table = mp9941_id, +}; + +module_i2c_driver(mp9941_driver); + +MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP9941"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 67487867c70f..c1e2d0cb2fd0 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -35,7 +35,7 @@ struct mpq7932_data { }; #if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) -static struct regulator_desc mpq7932_regulators_desc[] = { +static const struct regulator_desc mpq7932_regulators_desc[] = { PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES, MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES, @@ -164,4 +164,4 @@ module_i2c_driver(mpq7932_regulator_driver); MODULE_AUTHOR("Saravanan Sekar <saravanan@linumiz.com>"); MODULE_DESCRIPTION("MPQ7932 PMIC regulator driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 4e2549cc8120..331c274ca892 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -22,7 +22,7 @@ static int mpq8785_identify(struct i2c_client *client, break; case 1: case 2: - info->format[PSC_VOLTAGE_OUT] = direct, + info->format[PSC_VOLTAGE_OUT] = direct; info->m[PSC_VOLTAGE_OUT] = 64; info->b[PSC_VOLTAGE_OUT] = 0; info->R[PSC_VOLTAGE_OUT] = 1; @@ -62,7 +62,7 @@ static int mpq8785_probe(struct i2c_client *client) }; static const struct i2c_device_id mpq8785_id[] = { - { "mpq8785", 0 }, + { "mpq8785" }, { }, }; MODULE_DEVICE_TABLE(i2c, mpq8785_id); @@ -87,4 +87,4 @@ module_i2c_driver(mpq8785_driver); MODULE_AUTHOR("Charles Hsu <ythsu0511@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MPQ8785"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c index 31d9ae06379a..aa98284bbdd8 100644 --- a/drivers/hwmon/pmbus/pim4328.c +++ b/drivers/hwmon/pmbus/pim4328.c @@ -230,4 +230,4 @@ module_i2c_driver(pim4328_driver); MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c index c95433790b11..569b61dc1a32 100644 --- a/drivers/hwmon/pmbus/pli1209bc.c +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -54,30 +54,6 @@ static int pli1209bc_read_word_data(struct i2c_client *client, int page, } } -static int pli1209bc_write_byte(struct i2c_client *client, int page, u8 reg) -{ - int ret; - - switch (reg) { - case PMBUS_CLEAR_FAULTS: - ret = pmbus_write_byte(client, page, reg); - /* - * PLI1209 takes 230 usec to execute the CLEAR_FAULTS command. - * During that time it's busy and NACKs all requests on the - * SMBUS interface. It also NACKs reads on PMBUS_STATUS_BYTE - * making it impossible to poll the BUSY flag. - * - * Just wait for not BUSY unconditionally. - */ - usleep_range(250, 300); - break; - default: - ret = -ENODATA; - break; - } - return ret; -} - #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) static const struct regulator_desc pli1209bc_reg_desc = { .name = "vout2", @@ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = { | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, .read_word_data = pli1209bc_read_word_data, - .write_byte = pli1209bc_write_byte, + .write_delay = 250, #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) .num_regulators = 1, .reg_desc = &pli1209bc_reg_desc, @@ -141,7 +117,7 @@ static int pli1209bc_probe(struct i2c_client *client) } static const struct i2c_device_id pli1209bc_id[] = { - {"pli1209bc", 0}, + {"pli1209bc"}, {} }; @@ -169,4 +145,4 @@ module_i2c_driver(pli1209bc_driver); MODULE_AUTHOR("Marcello Sylvester Bauer <sylv@sylv.io>"); MODULE_DESCRIPTION("PMBus driver for Vicor PLI1209BC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c index 2a16504c85b7..c96c0aecb920 100644 --- a/drivers/hwmon/pmbus/pm6764tr.c +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -48,7 +48,7 @@ static int pm6764tr_probe(struct i2c_client *client) } static const struct i2c_device_id pm6764tr_id[] = { - {"pm6764tr", 0}, + {"pm6764tr"}, {} }; MODULE_DEVICE_TABLE(i2c, pm6764tr_id); @@ -73,4 +73,4 @@ module_i2c_driver(pm6764tr_driver); MODULE_AUTHOR("Charles Hsu"); MODULE_DESCRIPTION("PMBus driver for ST PM6764TR"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index ec40c5c59954..77cf268e7d2d 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -261,4 +261,4 @@ module_i2c_driver(pmbus_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("Generic PMBus driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index fb442fae7b3e..ddb19c9726d6 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -418,6 +418,12 @@ enum pmbus_sensor_classes { enum pmbus_data_format { linear = 0, ieee754, direct, vid }; enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; +/* PMBus revision identifiers */ +#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */ +#define PMBUS_REV_11 0x11 /* PMBus revision 1.1 */ +#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */ +#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */ + struct pmbus_driver_info { int pages; /* Total number of pages */ u8 phases[PMBUS_PAGES]; /* Number of phases per page */ @@ -466,11 +472,23 @@ struct pmbus_driver_info { /* custom attributes */ const struct attribute_group **groups; + + /* + * Some chips need a little delay between SMBus communication. When + * set, the generic PMBus helper functions will wait if necessary + * to meet this requirement. The access delay is honored after + * every SMBus operation. The write delay is only honored after + * SMBus write operations. + */ + int access_delay; /* in microseconds */ + int write_delay; /* in microseconds */ }; /* Regulator ops */ extern const struct regulator_ops pmbus_regulator_ops; +int pmbus_regulator_init_cb(struct regulator_dev *rdev, + struct regulator_config *config); /* Macros for filling in array of struct regulator_desc */ #define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step, _min_uV) \ @@ -485,6 +503,7 @@ extern const struct regulator_ops pmbus_regulator_ops; .n_voltages = _voltages, \ .uV_step = _step, \ .min_uV = _min_uV, \ + .init_cb = pmbus_regulator_init_cb, \ } #define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0) @@ -500,6 +519,7 @@ extern const struct regulator_ops pmbus_regulator_ops; .n_voltages = _voltages, \ .uV_step = _step, \ .min_uV = _min_uV, \ + .init_cb = pmbus_regulator_init_cb, \ } #define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index cb4c65a7f288..787683e83db6 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -7,6 +7,7 @@ */ #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/kernel.h> #include <linux/math64.h> #include <linux/module.h> @@ -30,6 +31,9 @@ #define PMBUS_ATTR_ALLOC_SIZE 32 #define PMBUS_NAME_SIZE 24 +static int wp = -1; +module_param(wp, int, 0444); + struct pmbus_sensor { struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ @@ -85,6 +89,8 @@ struct pmbus_data { u32 flags; /* from platform data */ + u8 revision; /* The PMBus revision the device is compliant with */ + int exponent[PMBUS_PAGES]; /* linear mode: exponent for output voltages */ @@ -108,6 +114,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 */ }; struct pmbus_debugfs_entry { @@ -145,7 +153,7 @@ void pmbus_clear_cache(struct i2c_client *client) for (sensor = data->sensors; sensor; sensor = sensor->next) sensor->data = -ENODATA; } -EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, "PMBUS"); void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) { @@ -156,7 +164,40 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) if (sensor->reg == reg) sensor->update = update; } -EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); + +/* Some chips need a delay between accesses. */ +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; + + 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); + } +} + +/* Sets the last accessed timestamp for pmbus_wait */ +static void pmbus_update_ts(struct i2c_client *client, bool write_op) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + + if (info->access_delay) { + data->access_time = ktime_get(); + } else if (info->write_delay && write_op) { + data->write_time = ktime_get(); + } +} int pmbus_set_page(struct i2c_client *client, int page, int phase) { @@ -168,11 +209,15 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && 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); if (rv < 0) return rv; + pmbus_wait(client); rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + pmbus_update_ts(client, false); if (rv < 0) return rv; @@ -183,8 +228,10 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) if (data->info->phases[page] && data->currphase != phase && !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { + pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, phase); + pmbus_update_ts(client, true); if (rv) return rv; } @@ -192,7 +239,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) return 0; } -EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_set_page, "PMBUS"); int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { @@ -202,9 +249,13 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) if (rv < 0) return rv; - return i2c_smbus_write_byte(client, value); + pmbus_wait(client); + rv = i2c_smbus_write_byte(client, value); + pmbus_update_ts(client, true); + + return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, "PMBUS"); /* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if @@ -233,9 +284,13 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, if (rv < 0) return rv; - return i2c_smbus_write_word_data(client, reg, word); + pmbus_wait(client); + rv = i2c_smbus_write_word_data(client, reg, word); + pmbus_update_ts(client, true); + + return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS"); static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, @@ -341,7 +396,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, return _pmbus_write_word_data(client, page, pmbus_fan_command_registers[id], command); } -EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, "PMBUS"); int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) { @@ -351,9 +406,13 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) if (rv < 0) return rv; - return i2c_smbus_read_word_data(client, reg); + pmbus_wait(client); + rv = i2c_smbus_read_word_data(client, reg); + pmbus_update_ts(client, false); + + return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, "PMBUS"); static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) { @@ -410,9 +469,13 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) if (rv < 0) return rv; - return i2c_smbus_read_byte_data(client, reg); + pmbus_wait(client); + rv = i2c_smbus_read_byte_data(client, reg); + pmbus_update_ts(client, false); + + return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, "PMBUS"); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) { @@ -422,9 +485,13 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) if (rv < 0) return rv; - return i2c_smbus_write_byte_data(client, reg, value); + pmbus_wait(client); + rv = i2c_smbus_write_byte_data(client, reg, value); + pmbus_update_ts(client, true); + + return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, "PMBUS"); int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, u8 mask, u8 value) @@ -443,7 +510,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, char *data_buf) @@ -454,7 +521,11 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, if (rv < 0) return rv; - return i2c_smbus_read_block_data(client, reg, data_buf); + pmbus_wait(client); + rv = i2c_smbus_read_block_data(client, reg, data_buf); + pmbus_update_ts(client, false); + + return rv; } static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, @@ -510,14 +581,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, { return pmbus_get_fan_rate(client, page, id, mode, false); } -EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, "PMBUS"); int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, enum pmbus_fan_mode mode) { return pmbus_get_fan_rate(client, page, id, mode, true); } -EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, "PMBUS"); static void pmbus_clear_fault_page(struct i2c_client *client, int page) { @@ -532,7 +603,7 @@ void pmbus_clear_faults(struct i2c_client *client) for (i = 0; i < data->info->pages; i++) pmbus_clear_fault_page(client, i); } -EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, "PMBUS"); static int pmbus_check_status_cml(struct i2c_client *client) { @@ -587,13 +658,13 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); } -EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, "PMBUS"); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, __pmbus_read_word_data, page, reg); } -EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, "PMBUS"); static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client, int page, int reg) @@ -617,7 +688,7 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) return data->info; } -EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, "PMBUS"); static int pmbus_get_status(struct i2c_client *client, int page, int reg) { @@ -1095,9 +1166,14 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, regval = status & mask; if (regval) { - ret = _pmbus_write_byte_data(client, page, reg, regval); - if (ret) - goto unlock; + if (data->revision >= PMBUS_REV_12) { + ret = _pmbus_write_byte_data(client, page, reg, regval); + if (ret) + goto unlock; + } else { + pmbus_clear_fault_page(client, page); + } + } if (s1 && s2) { s64 v1, v2; @@ -2450,9 +2526,11 @@ static int pmbus_read_coefficients(struct i2c_client *client, data.block[1] = attr->reg; data.block[2] = 0x01; + pmbus_wait(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); if (rv < 0) return rv; @@ -2590,6 +2668,56 @@ static void pmbus_remove_pec(void *dev) device_remove_file(dev, &dev_attr_pec); } +static void pmbus_init_wp(struct i2c_client *client, struct pmbus_data *data) +{ + int ret; + + switch (wp) { + case 0: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, 0); + break; + + case 1: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_VOUT); + break; + + case 2: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_OP); + break; + + case 3: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_ALL); + break; + + default: + /* Ignore the other values */ + break; + } + + ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT); + if (ret < 0) + return; + + switch (ret & PB_WP_ANY) { + case PB_WP_ALL: + data->flags |= PMBUS_OP_PROTECTED; + fallthrough; + case PB_WP_OP: + data->flags |= PMBUS_VOUT_PROTECTED; + fallthrough; + case PB_WP_VOUT: + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + break; + + default: + break; + } +} + static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, struct pmbus_driver_info *info) { @@ -2604,7 +2732,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, /* Enable PEC if the controller and bus supports it */ if (!(data->flags & PMBUS_NO_CAPABILITY)) { + pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + pmbus_update_ts(client, false); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) client->flags |= I2C_CLIENT_PEC; @@ -2617,10 +2748,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * Bail out if both registers are not supported. */ 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); + 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); + if (ret < 0 || ret == 0xff) { dev_err(dev, "PMBus status register not found\n"); return -ENODEV; @@ -2634,11 +2771,12 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * faults, and we should not try it. Also, in that case, writes into * limit registers need to be disabled. */ - if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { - ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); - if (ret > 0 && (ret & PB_WP_ANY)) - data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; - } + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) + pmbus_init_wp(client, data); + + ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); + if (ret >= 0) + data->revision = ret; if (data->info->pages) pmbus_clear_faults(client); @@ -3094,8 +3232,12 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, { struct device *dev = rdev_get_dev(rdev); struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); int val, low, high; + if (data->flags & PMBUS_VOUT_PROTECTED) + return 0; + if (selector >= rdev->desc->n_voltages || selector < rdev->desc->linear_min_sel) return -EINVAL; @@ -3128,7 +3270,23 @@ const struct regulator_ops pmbus_regulator_ops = { .set_voltage = pmbus_regulator_set_voltage, .list_voltage = pmbus_regulator_list_voltage, }; -EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, "PMBUS"); + +int pmbus_regulator_init_cb(struct regulator_dev *rdev, + struct regulator_config *config) +{ + struct pmbus_data *data = config->driver_data; + struct regulation_constraints *constraints = rdev->constraints; + + if (data->flags & PMBUS_OP_PROTECTED) + constraints->valid_ops_mask &= ~REGULATOR_CHANGE_STATUS; + + if (data->flags & PMBUS_VOUT_PROTECTED) + constraints->valid_ops_mask &= ~REGULATOR_CHANGE_VOLTAGE; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_init_cb, "PMBUS"); static int pmbus_regulator_register(struct pmbus_data *data) { @@ -3188,7 +3346,17 @@ static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event) static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val) { - return _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8)); + int ret; + + ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8)); + + /* + * Clear fault systematically in case writing PMBUS_SMBALERT_MASK + * is not supported by the chip. + */ + pmbus_clear_fault_page(client, page); + + return ret; } static irqreturn_t pmbus_fault_handler(int irq, void *pdata) @@ -3366,11 +3534,11 @@ static int pmbus_init_debugfs(struct i2c_client *client, /* * Allocate the max possible entries we need. - * 6 entries device-specific + * 7 entries device-specific * 10 entries page-specific */ entries = devm_kcalloc(data->dev, - 6 + data->info->pages * 10, sizeof(*entries), + 7 + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; @@ -3383,6 +3551,15 @@ static int pmbus_init_debugfs(struct i2c_client *client, * assume that values of the following registers are the same for all * pages and report values only for page 0. */ + if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_REVISION; + debugfs_create_file("revision", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { entries[idx].client = client; entries[idx].page = 0; @@ -3644,7 +3821,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) return 0; } -EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS"); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { @@ -3652,7 +3829,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) return data->debugfs; } -EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS"); int pmbus_lock_interruptible(struct i2c_client *client) { @@ -3660,7 +3837,7 @@ int pmbus_lock_interruptible(struct i2c_client *client) return mutex_lock_interruptible(&data->update_lock); } -EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, "PMBUS"); void pmbus_unlock(struct i2c_client *client) { @@ -3668,7 +3845,7 @@ void pmbus_unlock(struct i2c_client *client) mutex_unlock(&data->update_lock); } -EXPORT_SYMBOL_NS_GPL(pmbus_unlock, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_unlock, "PMBUS"); static int __init pmbus_core_init(void) { diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index e2790a682dc8..6a4a978eca7e 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -127,9 +127,9 @@ static int pxe1610_probe(struct i2c_client *client) } static const struct i2c_device_id pxe1610_id[] = { - {"pxe1610", 0}, - {"pxe1110", 0}, - {"pxm1310", 0}, + {"pxe1610"}, + {"pxe1110"}, + {"pxm1310"}, {} }; @@ -148,4 +148,4 @@ module_i2c_driver(pxe1610_driver); MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c index a235c1cdf4fe..4d7086d83aa3 100644 --- a/drivers/hwmon/pmbus/q54sj108a2.c +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -421,4 +421,4 @@ module_i2c_driver(q54sj108a2_driver); MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>"); MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c index be9076dcc2db..5cb905ed8ae5 100644 --- a/drivers/hwmon/pmbus/stpddc60.c +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -18,8 +18,8 @@ #define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6 static const struct i2c_device_id stpddc60_id[] = { - {"stpddc60", 0}, - {"bmr481", 0}, + {"stpddc60"}, + {"bmr481"}, {} }; MODULE_DEVICE_TABLE(i2c, stpddc60_id); @@ -246,4 +246,4 @@ module_i2c_driver(stpddc60_driver); MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); MODULE_DESCRIPTION("PMBus driver for ST STPDDC60"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index c31889a036f0..07fe58c24485 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -195,7 +195,7 @@ static int tda38640_probe(struct i2c_client *client) } static const struct i2c_device_id tda38640_id[] = { - {"tda38640", 0}, + {"tda38640"}, {} }; MODULE_DEVICE_TABLE(i2c, tda38640_id); @@ -221,4 +221,4 @@ module_i2c_driver(tda38640_driver); MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c new file mode 100644 index 000000000000..0d2655e69549 --- /dev/null +++ b/drivers/hwmon/pmbus/tps25990.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2024 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "pmbus.h" + +#define TPS25990_READ_VAUX 0xd0 +#define TPS25990_READ_VIN_MIN 0xd1 +#define TPS25990_READ_VIN_PEAK 0xd2 +#define TPS25990_READ_IIN_PEAK 0xd4 +#define TPS25990_READ_PIN_PEAK 0xd5 +#define TPS25990_READ_TEMP_AVG 0xd6 +#define TPS25990_READ_TEMP_PEAK 0xd7 +#define TPS25990_READ_VOUT_MIN 0xda +#define TPS25990_READ_VIN_AVG 0xdc +#define TPS25990_READ_VOUT_AVG 0xdd +#define TPS25990_READ_IIN_AVG 0xde +#define TPS25990_READ_PIN_AVG 0xdf +#define TPS25990_VIREF 0xe0 +#define TPS25990_PK_MIN_AVG 0xea +#define PK_MIN_AVG_RST_PEAK BIT(7) +#define PK_MIN_AVG_RST_AVG BIT(6) +#define PK_MIN_AVG_RST_MIN BIT(5) +#define PK_MIN_AVG_AVG_CNT GENMASK(2, 0) +#define TPS25990_MFR_WRITE_PROTECT 0xf8 +#define TPS25990_UNLOCKED BIT(7) + +#define TPS25990_8B_SHIFT 2 +#define TPS25990_VIN_OVF_NUM 525100 +#define TPS25990_VIN_OVF_DIV 10163 +#define TPS25990_VIN_OVF_OFF 155 +#define TPS25990_IIN_OCF_NUM 953800 +#define TPS25990_IIN_OCF_DIV 129278 +#define TPS25990_IIN_OCF_OFF 157 + +#define PK_MIN_AVG_RST_MASK (PK_MIN_AVG_RST_PEAK | \ + PK_MIN_AVG_RST_AVG | \ + PK_MIN_AVG_RST_MIN) + +/* + * Arbitrary default Rimon value: 1kOhm + * This correspond to an overcurrent limit of 55A, close to the specified limit + * of un-stacked TPS25990 and makes further calculation easier to setup in + * sensor.conf, if necessary + */ +#define TPS25990_DEFAULT_RIMON 1000000000 + +static void tps25990_set_m(int *m, u32 rimon) +{ + u64 val = ((u64)*m) * rimon; + + /* Make sure m fits the s32 type */ + *m = DIV_ROUND_CLOSEST_ULL(val, 1000000); +} + +static int tps25990_mfr_write_protect_set(struct i2c_client *client, + u8 protect) +{ + u8 val; + + switch (protect) { + case 0: + val = 0xa2; + break; + case PB_WP_ALL: + val = 0x0; + break; + default: + return -EINVAL; + } + + return pmbus_write_byte_data(client, -1, TPS25990_MFR_WRITE_PROTECT, + val); +} + +static int tps25990_mfr_write_protect_get(struct i2c_client *client) +{ + int ret = pmbus_read_byte_data(client, -1, TPS25990_MFR_WRITE_PROTECT); + + if (ret < 0) + return ret; + + return (ret & TPS25990_UNLOCKED) ? 0 : PB_WP_ALL; +} + +static int tps25990_read_word_data(struct i2c_client *client, + int page, int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_PEAK); + break; + + case PMBUS_VIRT_READ_VIN_MIN: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_MIN); + break; + + case PMBUS_VIRT_READ_VIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_AVG); + break; + + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VOUT_MIN); + break; + + case PMBUS_VIRT_READ_VOUT_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VOUT_AVG); + break; + + case PMBUS_VIRT_READ_IIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_IIN_AVG); + break; + + case PMBUS_VIRT_READ_IIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_IIN_PEAK); + break; + + case PMBUS_VIRT_READ_TEMP_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_TEMP_AVG); + break; + + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_TEMP_PEAK); + break; + + case PMBUS_VIRT_READ_PIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_PIN_AVG); + break; + + case PMBUS_VIRT_READ_PIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_PIN_PEAK); + break; + + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VAUX); + break; + + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * These registers provide an 8 bits value instead of a + * 10bits one. Just shifting twice the register value is + * enough to make the sensor type conversion work, even + * if the datasheet provides different m, b and R for + * those. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + break; + ret <<= TPS25990_8B_SHIFT; + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + break; + ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM, + TPS25990_VIN_OVF_DIV); + ret += TPS25990_VIN_OVF_OFF; + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: + /* + * VIREF directly sets the over-current limit at which the eFuse + * will turn the FET off and trigger a fault. Expose it through + * this generic property instead of a manufacturer specific one. + */ + ret = pmbus_read_byte_data(client, page, TPS25990_VIREF); + if (ret < 0) + break; + ret = DIV_ROUND_CLOSEST(ret * TPS25990_IIN_OCF_NUM, + TPS25990_IIN_OCF_DIV); + ret += TPS25990_IIN_OCF_OFF; + break; + + case PMBUS_VIRT_SAMPLES: + ret = pmbus_read_byte_data(client, page, TPS25990_PK_MIN_AVG); + if (ret < 0) + break; + ret = 1 << FIELD_GET(PK_MIN_AVG_AVG_CNT, ret); + break; + + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = 0; + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_write_word_data(struct i2c_client *client, + int page, int reg, u16 value) +{ + int ret; + + switch (reg) { + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + value >>= TPS25990_8B_SHIFT; + value = clamp_val(value, 0, 0xff); + ret = pmbus_write_word_data(client, page, reg, value); + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: + value -= TPS25990_VIN_OVF_OFF; + value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_VIN_OVF_DIV, + TPS25990_VIN_OVF_NUM); + value = clamp_val(value, 0, 0xf); + ret = pmbus_write_word_data(client, page, reg, value); + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: + value -= TPS25990_IIN_OCF_OFF; + value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_IIN_OCF_DIV, + TPS25990_IIN_OCF_NUM); + value = clamp_val(value, 0, 0x3f); + ret = pmbus_write_byte_data(client, page, TPS25990_VIREF, value); + break; + + case PMBUS_VIRT_SAMPLES: + value = clamp_val(value, 1, 1 << PK_MIN_AVG_AVG_CNT); + value = ilog2(value); + ret = pmbus_update_byte_data(client, page, TPS25990_PK_MIN_AVG, + PK_MIN_AVG_AVG_CNT, + FIELD_PREP(PK_MIN_AVG_AVG_CNT, value)); + break; + + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + /* + * TPS25990 has history resets based on MIN/AVG/PEAK instead of per + * sensor type. Exposing this quirk in hwmon is not desirable so + * reset MIN, AVG and PEAK together. Even is there effectively only + * one reset, which resets everything, expose the 5 entries so + * userspace is not required map a sensor type to another to trigger + * a reset + */ + ret = pmbus_update_byte_data(client, 0, TPS25990_PK_MIN_AVG, + PK_MIN_AVG_RST_MASK, + PK_MIN_AVG_RST_MASK); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_read_byte_data(struct i2c_client *client, + int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_WRITE_PROTECT: + ret = tps25990_mfr_write_protect_get(client); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_write_byte_data(struct i2c_client *client, + int page, int reg, u8 byte) +{ + int ret; + + switch (reg) { + case PMBUS_WRITE_PROTECT: + ret = tps25990_mfr_write_protect_set(client, byte); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) +static const struct regulator_desc tps25990_reg_desc[] = { + PMBUS_REGULATOR_ONE("vout"), +}; +#endif + +static const struct pmbus_driver_info tps25990_base_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 5251, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + .format[PSC_VOLTAGE_OUT] = direct, + .m[PSC_VOLTAGE_OUT] = 5251, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -2, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 140, + .b[PSC_TEMPERATURE] = 32100, + .R[PSC_TEMPERATURE] = -2, + /* + * Current and Power measurement depends on the ohm value + * of Rimon. m is multiplied by 1000 below to have an integer + * and -3 is added to R to compensate. + */ + .format[PSC_CURRENT_IN] = direct, + .m[PSC_CURRENT_IN] = 9538, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = -6, + .format[PSC_POWER] = direct, + .m[PSC_POWER] = 4901, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = -7, + .func[0] = (PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | + PMBUS_HAVE_VMON | + PMBUS_HAVE_IIN | + PMBUS_HAVE_PIN | + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_SAMPLES), + .read_word_data = tps25990_read_word_data, + .write_word_data = tps25990_write_word_data, + .read_byte_data = tps25990_read_byte_data, + .write_byte_data = tps25990_write_byte_data, + +#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) + .reg_desc = tps25990_reg_desc, + .num_regulators = ARRAY_SIZE(tps25990_reg_desc), +#endif +}; + +static const struct i2c_device_id tps25990_i2c_id[] = { + { "tps25990" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id); + +static const struct of_device_id tps25990_of_match[] = { + { .compatible = "ti,tps25990" }, + {} +}; +MODULE_DEVICE_TABLE(of, tps25990_of_match); + +static int tps25990_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + u32 rimon = TPS25990_DEFAULT_RIMON; + int ret; + + ret = device_property_read_u32(dev, "ti,rimon-micro-ohms", &rimon); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, "failed to get rimon\n"); + + info = devm_kmemdup(dev, &tps25990_base_info, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Adapt the current and power scale for each instance */ + tps25990_set_m(&info->m[PSC_CURRENT_IN], rimon); + tps25990_set_m(&info->m[PSC_POWER], rimon); + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver tps25990_driver = { + .driver = { + .name = "tps25990", + .of_match_table = tps25990_of_match, + }, + .probe = tps25990_probe, + .id_table = tps25990_i2c_id, +}; +module_i2c_driver(tps25990_driver); + +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_DESCRIPTION("PMBUS driver for TPS25990 eFuse"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index ea0074a6b9fc..7c9fedaa068c 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -31,7 +31,7 @@ static int tps40422_probe(struct i2c_client *client) } static const struct i2c_device_id tps40422_id[] = { - {"tps40422", 0}, + {"tps40422"}, {} }; @@ -51,4 +51,4 @@ module_i2c_driver(tps40422_driver); MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>"); MODULE_DESCRIPTION("PMBus driver for TI TPS40422"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 5c9466244d70..63524dff5e75 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -308,4 +308,4 @@ module_i2c_driver(tps53679_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c index 69bbdb6c680b..44d7a6df1dbd 100644 --- a/drivers/hwmon/pmbus/tps546d24.c +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -42,7 +42,7 @@ static int tps546d24_probe(struct i2c_client *client) } static const struct i2c_device_id tps546d24_id[] = { - {"tps546d24", 0}, + {"tps546d24"}, {} }; MODULE_DEVICE_TABLE(i2c, tps546d24_id); @@ -68,4 +68,4 @@ module_i2c_driver(tps546d24_driver); MODULE_AUTHOR("Duke Du <dukedu83@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for TI tps546d24"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 8d9d422450e5..9b0eadc81a2e 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -67,7 +67,6 @@ struct ucd9000_data { struct gpio_chip gpio; #endif struct dentry *debugfs; - ktime_t write_time; }; #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) @@ -80,68 +79,11 @@ struct ucd9000_debugfs_entry { * It has been observed that the UCD90320 randomly fails register access when * doing another access right on the back of a register write. To mitigate this * make sure that there is a minimum delay between a write access and the - * following access. The 250us is based on experimental data. At a delay of - * 200us the issue seems to go away. Add a bit of extra margin to allow for + * following access. The 500 is based on experimental data. At a delay of + * 350us the issue seems to go away. Add a bit of extra margin to allow for * system to system differences. */ -#define UCD90320_WAIT_DELAY_US 250 - -static inline void ucd90320_wait(const struct ucd9000_data *data) -{ - s64 delta = ktime_us_delta(ktime_get(), data->write_time); - - if (delta < UCD90320_WAIT_DELAY_US) - udelay(UCD90320_WAIT_DELAY_US - delta); -} - -static int ucd90320_read_word_data(struct i2c_client *client, int page, - int phase, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - - if (reg >= PMBUS_VIRT_BASE) - return -ENXIO; - - ucd90320_wait(data); - return pmbus_read_word_data(client, page, phase, reg); -} - -static int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - - ucd90320_wait(data); - return pmbus_read_byte_data(client, page, reg); -} - -static int ucd90320_write_word_data(struct i2c_client *client, int page, - int reg, u16 word) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - int ret; - - ucd90320_wait(data); - ret = pmbus_write_word_data(client, page, reg, word); - data->write_time = ktime_get(); - - return ret; -} - -static int ucd90320_write_byte(struct i2c_client *client, int page, u8 value) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - int ret; - - ucd90320_wait(data); - ret = pmbus_write_byte(client, page, value); - data->write_time = ktime_get(); - - return ret; -} +#define UCD90320_WAIT_DELAY_US 500 static int ucd9000_get_fan_config(struct i2c_client *client, int fan) { @@ -667,10 +609,8 @@ static int ucd9000_probe(struct i2c_client *client) info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; } else if (mid->driver_data == ucd90320) { - info->read_byte_data = ucd90320_read_byte_data; - info->read_word_data = ucd90320_read_word_data; - info->write_byte = ucd90320_write_byte; - info->write_word_data = ucd90320_write_word_data; + /* Delay SMBus operations after a write */ + info->write_delay = UCD90320_WAIT_DELAY_US; } ucd9000_probe_gpio(client, mid, data); @@ -702,4 +642,4 @@ module_i2c_driver(ucd9000_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index 7920d1c06df0..f68adaf4a110 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -209,4 +209,4 @@ module_i2c_driver(ucd9200_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c new file mode 100644 index 000000000000..660bbfe16e1e --- /dev/null +++ b/drivers/hwmon/pmbus/xdp710.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Infineon XDP710 Hot-Swap Controller + */ + +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +#define XDP710_REG_CFG 0xD3 +#define XDP710_V_SNS_CFG 0xD4 +#define XDP710_CS_RNG 0xD5 + +/* + * The table to map configuration register values + * to sense resistor values + */ +static const int micro_ohm_rsense[] = { + 200, 250, 300, 330, 400, 470, 500, 600, + 670, 700, 750, 800, 900, 1000, 1100, 1200, + 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, + 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 3000, 3100, 3200, 3300, 3400, 3500, 3600, + 3700, 3800, 3900, 4000, 4100, 4200, 4300, 4400, + 4500, 4600, 4700, 4800, 4900, 5000, 5500, 6000, + 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000 +}; + +static struct pmbus_driver_info xdp710_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 4653, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + .m[PSC_VOLTAGE_OUT] = 4653, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -2, + .m[PSC_CURRENT_OUT] = 23165, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -2, + .m[PSC_POWER] = 4211, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = -2, + .m[PSC_TEMPERATURE] = 52, + .b[PSC_TEMPERATURE] = 14321, + .R[PSC_TEMPERATURE] = -1, + .func[0] = + PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN | + PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, +}; + +static int xdp710_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + u8 cs_rng; + u8 vtlm_rng; + int rsense; + int ret; + int m = 0; + + info = devm_kmemdup(&client->dev, &xdp710_info, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = i2c_smbus_read_word_data(client, XDP710_CS_RNG); + if (ret < 0) { + dev_err(&client->dev, "Can't get CS_RNG"); + return ret; + } + cs_rng = (ret >> 6) & GENMASK(1, 0); + + ret = i2c_smbus_read_word_data(client, XDP710_V_SNS_CFG); + if (ret < 0) { + dev_err(&client->dev, "Can't get V_SNS_CFG"); + return ret; + } + vtlm_rng = ret & GENMASK(1, 0); + + ret = i2c_smbus_read_word_data(client, XDP710_REG_CFG); + if (ret < 0) { + dev_err(&client->dev, "Can't get REG_CFG"); + return ret; + } + ret &= GENMASK(5, 0); + rsense = micro_ohm_rsense[ret]; + + info->m[PSC_VOLTAGE_IN] <<= vtlm_rng; + info->m[PSC_VOLTAGE_OUT] <<= vtlm_rng; + + m = info->m[PSC_CURRENT_OUT]; + info->m[PSC_CURRENT_OUT] = DIV_ROUND_CLOSEST(m * rsense >> cs_rng, 1000); + + m = info->m[PSC_POWER]; + info->m[PSC_POWER] = DIV_ROUND_CLOSEST(m * rsense >> cs_rng, 1000); + + return pmbus_do_probe(client, info); +} + +static const struct of_device_id xdp710_of_match[] = { + { .compatible = "infineon,xdp710" }, + {} +}; + +static const struct i2c_device_id xdp710_id[] = { + {"xdp710"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, xdp710_id); + +static struct i2c_driver xdp710_driver = { + .driver = { + .name = "xdp710", + .of_match_table = xdp710_of_match, + }, + .probe = xdp710_probe, + .id_table = xdp710_id, +}; +module_i2c_driver(xdp710_driver); + +MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); +MODULE_DESCRIPTION("PMBus driver for XDP710 HSC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index 37d08dd427d5..f3aa6339d60d 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -164,9 +164,9 @@ static int xdpe122_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe122_id[] = { - {"xdpe11280", 0}, - {"xdpe12254", 0}, - {"xdpe12284", 0}, + {"xdpe11280"}, + {"xdpe12254"}, + {"xdpe12284"}, {} }; @@ -194,4 +194,4 @@ module_i2c_driver(xdpe122_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c index 235e6b41ae4c..67a3d5fe1daf 100644 --- a/drivers/hwmon/pmbus/xdpe152c4.c +++ b/drivers/hwmon/pmbus/xdpe152c4.c @@ -44,8 +44,8 @@ static int xdpe152_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe152_id[] = { - {"xdpe152c4", 0}, - {"xdpe15284", 0}, + {"xdpe152c4"}, + {"xdpe15284"}, {} }; @@ -72,4 +72,4 @@ module_i2c_driver(xdpe152_driver); MODULE_AUTHOR("Greg Schwendimann <greg.schwendimann@infineon.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon XDPE152 family"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 83458df0d0cf..97be69630cfb 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, struct zl6100_data { int id; - ktime_t access; /* chip access time */ - int delay; /* Delay between chip accesses in uS */ struct pmbus_driver_info info; }; @@ -122,16 +120,6 @@ static u16 zl6100_d2l(long val) return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); } -/* Some chips need a delay between accesses */ -static inline void zl6100_wait(const struct zl6100_data *data) -{ - if (data->delay) { - s64 delta = ktime_us_delta(ktime_get(), data->access); - if (delta < data->delay) - udelay(data->delay - delta); - } -} - static int zl6100_read_word_data(struct i2c_client *client, int page, int phase, int reg) { @@ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, break; } - zl6100_wait(data); ret = pmbus_read_word_data(client, page, phase, vreg); - data->access = ktime_get(); if (ret < 0) return ret; @@ -195,14 +181,11 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct zl6100_data *data = to_zl6100_data(info); int ret, status; if (page >= info->pages) return -ENXIO; - zl6100_wait(data); - switch (reg) { case PMBUS_VIRT_STATUS_VMON: ret = pmbus_read_byte_data(client, 0, @@ -225,7 +208,6 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) ret = pmbus_read_byte_data(client, page, reg); break; } - data->access = ktime_get(); return ret; } @@ -234,8 +216,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct zl6100_data *data = to_zl6100_data(info); - int ret, vreg; + int vreg; if (page >= info->pages) return -ENXIO; @@ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, vreg = reg; } - zl6100_wait(data); - ret = pmbus_write_word_data(client, page, vreg, word); - data->access = ktime_get(); - - return ret; -} - -static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct zl6100_data *data = to_zl6100_data(info); - int ret; - - if (page >= info->pages) - return -ENXIO; - - zl6100_wait(data); - ret = pmbus_write_byte(client, page, value); - data->access = ktime_get(); - - return ret; + return pmbus_write_word_data(client, page, vreg, word); } static const struct i2c_device_id zl6100_id[] = { @@ -363,14 +324,7 @@ static int zl6100_probe(struct i2c_client *client) * supported chips are known to require a wait time between I2C * accesses. */ - data->delay = delay; - - /* - * Since there was a direct I2C device access above, wait before - * accessing the chip again. - */ - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); info = &data->info; @@ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client) if (ret < 0) return ret; - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); if (ret & ZL8802_MFR_PHASES_MASK) info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; @@ -418,8 +371,7 @@ static int zl6100_probe(struct i2c_client *client) if (ret < 0) return ret; - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); if (ret < 0) @@ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client) if (ret & ZL8802_MFR_XTEMP_ENABLE_2) info->func[i] |= PMBUS_HAVE_TEMP2; - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); } ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); if (ret < 0) @@ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client) info->func[0] |= PMBUS_HAVE_TEMP2; } - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); + info->access_delay = delay; info->read_word_data = zl6100_read_word_data; info->read_byte_data = zl6100_read_byte_data; info->write_word_data = zl6100_write_word_data; - info->write_byte = zl6100_write_byte; return pmbus_do_probe(client, info); } @@ -470,4 +420,4 @@ module_i2c_driver(zl6100_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); |