|
| 1 | +From 482c5a7b46fd39c2df42c47f96a817c2d50b9c75 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Vadim Pasternak < [email protected]> |
| 3 | +Date: Tue, 3 Mar 2020 14:33:59 +0200 |
| 4 | +Subject: [hwmon v1] hwmon: (pmbus/core) Add support for vid mode detection per |
| 5 | + page bases |
| 6 | + |
| 7 | +Add support for VID protocol detection per page bases, instead of |
| 8 | +detecting it based on PMBU_VOUT readout from page 0. |
| 9 | +The reason that some devices allows to configure different VID modes |
| 10 | +per page within the same device. |
| 11 | + |
| 12 | +Extend "vrm_version" with the type for Intel IMVP9 and AMD 6.25mV VID |
| 13 | +modes. |
| 14 | +Add calculation for those types. |
| 15 | + |
| 16 | +Add support for devices XDPE12254, XDPE12284. |
| 17 | +All these device support two pages. |
| 18 | +The below lists of VOUT_MODE command readout with their related VID |
| 19 | +protocols, Digital to Analog Converter steps, supported by these |
| 20 | +devices: |
| 21 | +VR12.0 mode, 5-mV DAC - 0x01; |
| 22 | +VR12.5 mode, 10-mV DAC - 0x02; |
| 23 | +IMVP9 mode, 5-mV DAC - 0x03; |
| 24 | +AMD mode 6.25mV - 0x10. |
| 25 | + |
| 26 | +Signed-off-by: Vadim Pasternak < [email protected]> |
| 27 | +--- |
| 28 | + drivers/hwmon/pmbus/Kconfig | 9 +++ |
| 29 | + drivers/hwmon/pmbus/Makefile | 1 + |
| 30 | + drivers/hwmon/pmbus/max20751.c | 2 +- |
| 31 | + drivers/hwmon/pmbus/pmbus.c | 5 +- |
| 32 | + drivers/hwmon/pmbus/pmbus.h | 4 +- |
| 33 | + drivers/hwmon/pmbus/pmbus_core.c | 10 ++- |
| 34 | + drivers/hwmon/pmbus/tps53679.c | 44 +++++----- |
| 35 | + drivers/hwmon/pmbus/xdpe12284.c | 171 +++++++++++++++++++++++++++++++++++++++ |
| 36 | + 8 files changed, 219 insertions(+), 27 deletions(-) |
| 37 | + create mode 100644 drivers/hwmon/pmbus/xdpe12284.c |
| 38 | + |
| 39 | +diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig |
| 40 | +index 61938ea22208..79fd67e0c940 100644 |
| 41 | +--- a/drivers/hwmon/pmbus/Kconfig |
| 42 | ++++ b/drivers/hwmon/pmbus/Kconfig |
| 43 | +@@ -156,6 +156,15 @@ config SENSORS_UCD9200 |
| 44 | + This driver can also be built as a module. If so, the module will |
| 45 | + be called ucd9200. |
| 46 | + |
| 47 | ++config SENSORS_XDPE122 |
| 48 | ++ tristate "Infineon XDPE122 family" |
| 49 | ++ help |
| 50 | ++ If you say yes here you get hardware monitoring support for Infineon |
| 51 | ++ XDPE12254, XDPE12284, device. |
| 52 | ++ |
| 53 | ++ This driver can also be built as a module. If so, the module will |
| 54 | ++ be called xdpe12284. |
| 55 | ++ |
| 56 | + config SENSORS_ZL6100 |
| 57 | + tristate "Intersil ZL6100 and compatibles" |
| 58 | + default n |
| 59 | +diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile |
| 60 | +index b912fec9876d..e0fe17121afb 100644 |
| 61 | +--- a/drivers/hwmon/pmbus/Makefile |
| 62 | ++++ b/drivers/hwmon/pmbus/Makefile |
| 63 | +@@ -16,4 +16,5 @@ obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o |
| 64 | + obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o |
| 65 | + obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o |
| 66 | + obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o |
| 67 | ++obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o |
| 68 | + obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o |
| 69 | +diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c |
| 70 | +index ab74aeae8cf2..394c662c811b 100644 |
| 71 | +--- a/drivers/hwmon/pmbus/max20751.c |
| 72 | ++++ b/drivers/hwmon/pmbus/max20751.c |
| 73 | +@@ -25,7 +25,7 @@ static struct pmbus_driver_info max20751_info = { |
| 74 | + .pages = 1, |
| 75 | + .format[PSC_VOLTAGE_IN] = linear, |
| 76 | + .format[PSC_VOLTAGE_OUT] = vid, |
| 77 | +- .vrm_version = vr12, |
| 78 | ++ .vrm_version[0] = vr12, |
| 79 | + .format[PSC_TEMPERATURE] = linear, |
| 80 | + .format[PSC_CURRENT_OUT] = linear, |
| 81 | + .format[PSC_POWER] = linear, |
| 82 | +diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c |
| 83 | +index 44ca8a94873d..ad360a88a53a 100644 |
| 84 | +--- a/drivers/hwmon/pmbus/pmbus.c |
| 85 | ++++ b/drivers/hwmon/pmbus/pmbus.c |
| 86 | +@@ -121,7 +121,7 @@ static int pmbus_identify(struct i2c_client *client, |
| 87 | + } |
| 88 | + |
| 89 | + if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { |
| 90 | +- int vout_mode; |
| 91 | ++ int vout_mode, i; |
| 92 | + |
| 93 | + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); |
| 94 | + if (vout_mode >= 0 && vout_mode != 0xff) { |
| 95 | +@@ -130,7 +130,8 @@ static int pmbus_identify(struct i2c_client *client, |
| 96 | + break; |
| 97 | + case 1: |
| 98 | + info->format[PSC_VOLTAGE_OUT] = vid; |
| 99 | +- info->vrm_version = vr11; |
| 100 | ++ for (i = 0; i < info->pages; i++) |
| 101 | ++ info->vrm_version[i] = vr11; |
| 102 | + break; |
| 103 | + case 2: |
| 104 | + info->format[PSC_VOLTAGE_OUT] = direct; |
| 105 | +diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h |
| 106 | +index 4efa2bd4f6d8..881156491581 100644 |
| 107 | +--- a/drivers/hwmon/pmbus/pmbus.h |
| 108 | ++++ b/drivers/hwmon/pmbus/pmbus.h |
| 109 | +@@ -341,12 +341,12 @@ enum pmbus_sensor_classes { |
| 110 | + #define PMBUS_HAVE_STATUS_VMON BIT(19) |
| 111 | + |
| 112 | + enum pmbus_data_format { linear = 0, direct, vid }; |
| 113 | +-enum vrm_version { vr11 = 0, vr12, vr13 }; |
| 114 | ++enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; |
| 115 | + |
| 116 | + struct pmbus_driver_info { |
| 117 | + int pages; /* Total number of pages */ |
| 118 | + enum pmbus_data_format format[PSC_NUM_CLASSES]; |
| 119 | +- enum vrm_version vrm_version; |
| 120 | ++ enum vrm_version vrm_version[PMBUS_PAGES]; /* vrm version per page */ |
| 121 | + /* |
| 122 | + * Support one set of coefficients for each sensor type |
| 123 | + * Used for chips providing data in direct mode. |
| 124 | +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c |
| 125 | +index 682b01624908..764a28105831 100644 |
| 126 | +--- a/drivers/hwmon/pmbus/pmbus_core.c |
| 127 | ++++ b/drivers/hwmon/pmbus/pmbus_core.c |
| 128 | +@@ -522,7 +522,7 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, |
| 129 | + long val = sensor->data; |
| 130 | + long rv = 0; |
| 131 | + |
| 132 | +- switch (data->info->vrm_version) { |
| 133 | ++ switch (data->info->vrm_version[sensor->page]) { |
| 134 | + case vr11: |
| 135 | + if (val >= 0x02 && val <= 0xb2) |
| 136 | + rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); |
| 137 | +@@ -535,6 +535,14 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, |
| 138 | + if (val >= 0x01) |
| 139 | + rv = 500 + (val - 1) * 10; |
| 140 | + break; |
| 141 | ++ case imvp9: |
| 142 | ++ if (val >= 0x01) |
| 143 | ++ rv = 200 + (val - 1) * 10; |
| 144 | ++ break; |
| 145 | ++ case amd625mv: |
| 146 | ++ if (val >= 0x0 && val <= 0xd8) |
| 147 | ++ rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100); |
| 148 | ++ break; |
| 149 | + } |
| 150 | + return rv; |
| 151 | + } |
| 152 | +diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c |
| 153 | +index 45eacc504e2f..28d3de029b91 100644 |
| 154 | +--- a/drivers/hwmon/pmbus/tps53679.c |
| 155 | ++++ b/drivers/hwmon/pmbus/tps53679.c |
| 156 | +@@ -33,27 +33,29 @@ static int tps53679_identify(struct i2c_client *client, |
| 157 | + struct pmbus_driver_info *info) |
| 158 | + { |
| 159 | + u8 vout_params; |
| 160 | +- int ret; |
| 161 | +- |
| 162 | +- /* Read the register with VOUT scaling value.*/ |
| 163 | +- ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); |
| 164 | +- if (ret < 0) |
| 165 | +- return ret; |
| 166 | +- |
| 167 | +- vout_params = ret & GENMASK(4, 0); |
| 168 | +- |
| 169 | +- switch (vout_params) { |
| 170 | +- case TPS53679_PROT_VR13_10MV: |
| 171 | +- case TPS53679_PROT_VR12_5_10MV: |
| 172 | +- info->vrm_version = vr13; |
| 173 | +- break; |
| 174 | +- case TPS53679_PROT_VR13_5MV: |
| 175 | +- case TPS53679_PROT_VR12_5MV: |
| 176 | +- case TPS53679_PROT_IMVP8_5MV: |
| 177 | +- info->vrm_version = vr12; |
| 178 | +- break; |
| 179 | +- default: |
| 180 | +- return -EINVAL; |
| 181 | ++ int i, ret; |
| 182 | ++ |
| 183 | ++ for (i = 0; i < TPS53679_PAGE_NUM; i++) { |
| 184 | ++ /* Read the register with VOUT scaling value.*/ |
| 185 | ++ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); |
| 186 | ++ if (ret < 0) |
| 187 | ++ return ret; |
| 188 | ++ |
| 189 | ++ vout_params = ret & GENMASK(4, 0); |
| 190 | ++ |
| 191 | ++ switch (vout_params) { |
| 192 | ++ case TPS53679_PROT_VR13_10MV: |
| 193 | ++ case TPS53679_PROT_VR12_5_10MV: |
| 194 | ++ info->vrm_version[i] = vr13; |
| 195 | ++ break; |
| 196 | ++ case TPS53679_PROT_VR13_5MV: |
| 197 | ++ case TPS53679_PROT_VR12_5MV: |
| 198 | ++ case TPS53679_PROT_IMVP8_5MV: |
| 199 | ++ info->vrm_version[i] = vr12; |
| 200 | ++ break; |
| 201 | ++ default: |
| 202 | ++ return -EINVAL; |
| 203 | ++ } |
| 204 | + } |
| 205 | + |
| 206 | + return 0; |
| 207 | +diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c |
| 208 | +new file mode 100644 |
| 209 | +index 000000000000..660556b89e9f |
| 210 | +--- /dev/null |
| 211 | ++++ b/drivers/hwmon/pmbus/xdpe12284.c |
| 212 | +@@ -0,0 +1,171 @@ |
| 213 | ++// SPDX-License-Identifier: GPL-2.0-or-later |
| 214 | ++/* |
| 215 | ++ * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers |
| 216 | ++ * |
| 217 | ++ * Copyright (c) 2020 Mellanox Technologies. All rights reserved. |
| 218 | ++ */ |
| 219 | ++ |
| 220 | ++#include <linux/err.h> |
| 221 | ++#include <linux/i2c.h> |
| 222 | ++#include <linux/init.h> |
| 223 | ++#include <linux/kernel.h> |
| 224 | ++#include <linux/module.h> |
| 225 | ++#include "pmbus.h" |
| 226 | ++ |
| 227 | ++#define XDPE122_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ |
| 228 | ++#define XDPE122_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ |
| 229 | ++#define XDPE122_PROT_IMVP9_10MV 0x03 /* IMVP9 mode, 10-mV DAC */ |
| 230 | ++#define XDPE122_AMD_625MV 0x10 /* AMD mode 6.25mV */ |
| 231 | ++#define XDPE122_PAGE_NUM 2 |
| 232 | ++ |
| 233 | ++static int xdpe122_read_word_data(struct i2c_client *client, int page, int reg) |
| 234 | ++{ |
| 235 | ++ const struct pmbus_driver_info *info = pmbus_get_driver_info(client); |
| 236 | ++ long val; |
| 237 | ++ s16 exponent; |
| 238 | ++ s32 mantissa; |
| 239 | ++ int ret; |
| 240 | ++ |
| 241 | ++ switch (reg) { |
| 242 | ++ case PMBUS_VOUT_OV_FAULT_LIMIT: |
| 243 | ++ case PMBUS_VOUT_UV_FAULT_LIMIT: |
| 244 | ++ ret = pmbus_read_word_data(client, page, reg); |
| 245 | ++ if (ret < 0) |
| 246 | ++ return ret; |
| 247 | ++ |
| 248 | ++ /* Convert register value to LINEAR11 data. */ |
| 249 | ++ exponent = ((s16)ret) >> 11; |
| 250 | ++ mantissa = ((s16)((ret & GENMASK(10, 0)) << 5)) >> 5; |
| 251 | ++ val = mantissa * 1000L; |
| 252 | ++ if (exponent >= 0) |
| 253 | ++ val <<= exponent; |
| 254 | ++ else |
| 255 | ++ val >>= -exponent; |
| 256 | ++ |
| 257 | ++ /* Convert data to VID register. */ |
| 258 | ++ switch (info->vrm_version[page]) { |
| 259 | ++ case vr13: |
| 260 | ++ if (val >= 500) |
| 261 | ++ return 1 + DIV_ROUND_CLOSEST(val - 500, 10); |
| 262 | ++ return 0; |
| 263 | ++ case vr12: |
| 264 | ++ if (val >= 250) |
| 265 | ++ return 1 + DIV_ROUND_CLOSEST(val - 250, 5); |
| 266 | ++ return 0; |
| 267 | ++ case imvp9: |
| 268 | ++ if (val >= 200) |
| 269 | ++ return 1 + DIV_ROUND_CLOSEST(val - 200, 10); |
| 270 | ++ return 0; |
| 271 | ++ case amd625mv: |
| 272 | ++ if (val >= 200 && val <= 1550) |
| 273 | ++ return DIV_ROUND_CLOSEST((1550 - val) * 100, |
| 274 | ++ 625); |
| 275 | ++ return 0; |
| 276 | ++ default: |
| 277 | ++ return -EINVAL; |
| 278 | ++ } |
| 279 | ++ default: |
| 280 | ++ return -ENODATA; |
| 281 | ++ } |
| 282 | ++ |
| 283 | ++ return 0; |
| 284 | ++} |
| 285 | ++ |
| 286 | ++static int xdpe122_identify(struct i2c_client *client, |
| 287 | ++ struct pmbus_driver_info *info) |
| 288 | ++{ |
| 289 | ++ u8 vout_params; |
| 290 | ++ int i, ret; |
| 291 | ++ |
| 292 | ++ for (i = 0; i < XDPE122_PAGE_NUM; i++) { |
| 293 | ++ /* Read the register with VOUT scaling value.*/ |
| 294 | ++ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); |
| 295 | ++ if (ret < 0) |
| 296 | ++ return ret; |
| 297 | ++ |
| 298 | ++ vout_params = ret & GENMASK(4, 0); |
| 299 | ++ |
| 300 | ++ switch (vout_params) { |
| 301 | ++ case XDPE122_PROT_VR12_5_10MV: |
| 302 | ++ info->vrm_version[i] = vr13; |
| 303 | ++ break; |
| 304 | ++ case XDPE122_PROT_VR12_5MV: |
| 305 | ++ info->vrm_version[i] = vr12; |
| 306 | ++ break; |
| 307 | ++ case XDPE122_PROT_IMVP9_10MV: |
| 308 | ++ info->vrm_version[i] = imvp9; |
| 309 | ++ break; |
| 310 | ++ case XDPE122_AMD_625MV: |
| 311 | ++ info->vrm_version[i] = amd625mv; |
| 312 | ++ break; |
| 313 | ++ default: |
| 314 | ++ return -EINVAL; |
| 315 | ++ } |
| 316 | ++ } |
| 317 | ++ |
| 318 | ++ return 0; |
| 319 | ++} |
| 320 | ++ |
| 321 | ++static struct pmbus_driver_info xdpe122_info = { |
| 322 | ++ .pages = XDPE122_PAGE_NUM, |
| 323 | ++ .format[PSC_VOLTAGE_IN] = linear, |
| 324 | ++ .format[PSC_VOLTAGE_OUT] = vid, |
| 325 | ++ .format[PSC_TEMPERATURE] = linear, |
| 326 | ++ .format[PSC_CURRENT_IN] = linear, |
| 327 | ++ .format[PSC_CURRENT_OUT] = linear, |
| 328 | ++ .format[PSC_POWER] = linear, |
| 329 | ++ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
| 330 | ++ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
| 331 | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
| 332 | ++ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, |
| 333 | ++ .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
| 334 | ++ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
| 335 | ++ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
| 336 | ++ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, |
| 337 | ++ .identify = xdpe122_identify, |
| 338 | ++ .read_word_data = xdpe122_read_word_data, |
| 339 | ++}; |
| 340 | ++ |
| 341 | ++static int xdpe122_probe(struct i2c_client *client, |
| 342 | ++ const struct i2c_device_id *id) |
| 343 | ++{ |
| 344 | ++ struct pmbus_driver_info *info; |
| 345 | ++ |
| 346 | ++ info = devm_kmemdup(&client->dev, &xdpe122_info, sizeof(*info), |
| 347 | ++ GFP_KERNEL); |
| 348 | ++ if (!info) |
| 349 | ++ return -ENOMEM; |
| 350 | ++ |
| 351 | ++ return pmbus_do_probe(client, id, info); |
| 352 | ++} |
| 353 | ++ |
| 354 | ++static const struct i2c_device_id xdpe122_id[] = { |
| 355 | ++ {"xdpe12254", 0}, |
| 356 | ++ {"xdpe12284", 0}, |
| 357 | ++ {} |
| 358 | ++}; |
| 359 | ++ |
| 360 | ++MODULE_DEVICE_TABLE(i2c, xdpe122_id); |
| 361 | ++ |
| 362 | ++static const struct of_device_id __maybe_unused xdpe122_of_match[] = { |
| 363 | ++ {.compatible = "infineon,xdpe12254"}, |
| 364 | ++ {.compatible = "infineon,xdpe12284"}, |
| 365 | ++ {} |
| 366 | ++}; |
| 367 | ++MODULE_DEVICE_TABLE(of, xdpe122_of_match); |
| 368 | ++ |
| 369 | ++static struct i2c_driver xdpe122_driver = { |
| 370 | ++ .driver = { |
| 371 | ++ .name = "xdpe12284", |
| 372 | ++ .of_match_table = of_match_ptr(xdpe122_of_match), |
| 373 | ++ }, |
| 374 | ++ .probe = xdpe122_probe, |
| 375 | ++ .remove = pmbus_do_remove, |
| 376 | ++ .id_table = xdpe122_id, |
| 377 | ++}; |
| 378 | ++ |
| 379 | ++module_i2c_driver(xdpe122_driver); |
| 380 | ++ |
| 381 | ++MODULE_AUTHOR("Vadim Pasternak <[email protected]>"); |
| 382 | ++MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); |
| 383 | ++MODULE_LICENSE("GPL"); |
| 384 | +-- |
| 385 | +2.11.0 |
| 386 | + |
0 commit comments