FILE
isl68137.c
File preview with syntax highlighting.
linux-6.19.14/drivers/hwmon/pmbus/isl68137.c
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators 4 * 5 * Copyright (c) 2017 Google Inc 6 * Copyright (c) 2020 Renesas Electronics America 7 * 8 */ 9 10#include <linux/err.h> 11#include <linux/hwmon-sysfs.h> 12#include <linux/i2c.h> 13#include <linux/init.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/of.h> 17#include <linux/string.h> 18#include <linux/sysfs.h> 19 20#include "pmbus.h" 21 22#define ISL68137_VOUT_AVS 0x30 23#define RAA_DMPVR2_READ_VMON 0xc8 24#define MAX_CHANNELS 4 25 26enum chips { 27 isl68137, 28 isl68220, 29 isl68221, 30 isl68222, 31 isl68223, 32 isl68224, 33 isl68225, 34 isl68226, 35 isl68227, 36 isl68229, 37 isl68233, 38 isl68239, 39 isl69222, 40 isl69223, 41 isl69224, 42 isl69225, 43 isl69227, 44 isl69228, 45 isl69234, 46 isl69236, 47 isl69239, 48 isl69242, 49 isl69243, 50 isl69247, 51 isl69248, 52 isl69254, 53 isl69255, 54 isl69256, 55 isl69259, 56 isl69260, 57 isl69268, 58 isl69269, 59 isl69298, 60 raa228000, 61 raa228004, 62 raa228006, 63 raa228228, 64 raa228244, 65 raa228246, 66 raa229001, 67 raa229004, 68 raa229141, 69 raa229621, 70}; 71 72enum variants { 73 raa_dmpvr1_2rail, 74 raa_dmpvr2_1rail, 75 raa_dmpvr2_2rail, 76 raa_dmpvr2_2rail_nontc, 77 raa_dmpvr2_2rail_pmbus, 78 raa_dmpvr2_3rail, 79 raa_dmpvr2_hv, 80}; 81 82struct isl68137_channel { 83 u32 vout_voltage_divider[2]; 84}; 85 86struct isl68137_data { 87 struct pmbus_driver_info info; 88 struct isl68137_channel channel[MAX_CHANNELS]; 89}; 90 91#define to_isl68137_data(x) container_of(x, struct isl68137_data, info) 92 93static const struct i2c_device_id raa_dmpvr_id[]; 94 95static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, 96 int page, 97 char *buf) 98{ 99 int val; 100 101 val = pmbus_lock_interruptible(client); 102 if (val) 103 return val; 104 105 val = pmbus_read_byte_data(client, page, PMBUS_OPERATION); 106 107 pmbus_unlock(client); 108 109 if (val < 0) 110 return val; 111 112 return sysfs_emit(buf, "%d\n", 113 (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS); 114} 115 116static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client, 117 int page, 118 const char *buf, size_t count) 119{ 120 int rc, op_val; 121 bool result; 122 123 rc = kstrtobool(buf, &result); 124 if (rc) 125 return rc; 126 127 op_val = result ? ISL68137_VOUT_AVS : 0; 128 129 rc = pmbus_lock_interruptible(client); 130 if (rc) 131 return rc; 132 133 /* 134 * Writes to VOUT setpoint over AVSBus will persist after the VRM is 135 * switched to PMBus control. Switching back to AVSBus control 136 * restores this persisted setpoint rather than re-initializing to 137 * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before 138 * enabling AVS control is the workaround. 139 */ 140 if (op_val == ISL68137_VOUT_AVS) { 141 rc = pmbus_read_word_data(client, page, 0xff, 142 PMBUS_VOUT_COMMAND); 143 if (rc < 0) 144 goto unlock; 145 146 rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND, 147 rc); 148 if (rc < 0) 149 goto unlock; 150 } 151 152 rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION, 153 ISL68137_VOUT_AVS, op_val); 154 155unlock: 156 pmbus_unlock(client); 157 158 return (rc < 0) ? rc : count; 159} 160 161static ssize_t isl68137_avs_enable_show(struct device *dev, 162 struct device_attribute *devattr, 163 char *buf) 164{ 165 struct i2c_client *client = to_i2c_client(dev->parent); 166 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 167 168 return isl68137_avs_enable_show_page(client, attr->index, buf); 169} 170 171static ssize_t isl68137_avs_enable_store(struct device *dev, 172 struct device_attribute *devattr, 173 const char *buf, size_t count) 174{ 175 struct i2c_client *client = to_i2c_client(dev->parent); 176 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 177 178 return isl68137_avs_enable_store_page(client, attr->index, buf, count); 179} 180 181static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0); 182static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1); 183 184static struct attribute *enable_attrs[] = { 185 &sensor_dev_attr_avs0_enable.dev_attr.attr, 186 &sensor_dev_attr_avs1_enable.dev_attr.attr, 187 NULL, 188}; 189 190static const struct attribute_group enable_group = { 191 .attrs = enable_attrs, 192}; 193 194static const struct attribute_group *isl68137_attribute_groups[] = { 195 &enable_group, 196 NULL, 197}; 198 199static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, 200 int phase, int reg) 201{ 202 const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 203 const struct isl68137_data *data = to_isl68137_data(info); 204 int ret; 205 u64 temp; 206 207 switch (reg) { 208 case PMBUS_VIRT_READ_VMON: 209 ret = pmbus_read_word_data(client, page, phase, 210 RAA_DMPVR2_READ_VMON); 211 break; 212 case PMBUS_READ_POUT: 213 case PMBUS_READ_VOUT: 214 /* 215 * In cases where a voltage divider is attached to the target 216 * rail between Vout and the Vsense pin, both Vout and Pout 217 * should be scaled by the voltage divider scaling factor. 218 * I.e. Vout = Vsense * Rtotal / Rout 219 */ 220 ret = pmbus_read_word_data(client, page, phase, reg); 221 if (ret > 0) { 222 temp = DIV_U64_ROUND_CLOSEST((u64)ret * 223 data->channel[page].vout_voltage_divider[1], 224 data->channel[page].vout_voltage_divider[0]); 225 ret = clamp_val(temp, 0, 0xffff); 226 } 227 break; 228 default: 229 ret = -ENODATA; 230 break; 231 } 232 233 return ret; 234} 235 236static int raa_dmpvr2_write_word_data(struct i2c_client *client, int page, 237 int reg, u16 word) 238{ 239 const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 240 const struct isl68137_data *data = to_isl68137_data(info); 241 int ret; 242 u64 temp; 243 244 switch (reg) { 245 case PMBUS_VOUT_MAX: 246 case PMBUS_VOUT_MARGIN_HIGH: 247 case PMBUS_VOUT_MARGIN_LOW: 248 case PMBUS_VOUT_OV_FAULT_LIMIT: 249 case PMBUS_VOUT_UV_FAULT_LIMIT: 250 case PMBUS_VOUT_COMMAND: 251 /* 252 * In cases where a voltage divider is attached to the target 253 * rail between Vout and the Vsense pin, Vout related PMBus 254 * commands should be scaled based on the expected voltage 255 * at the Vsense pin. 256 * I.e. Vsense = Vout * Rout / Rtotal 257 */ 258 temp = DIV_U64_ROUND_CLOSEST((u64)word * 259 data->channel[page].vout_voltage_divider[0], 260 data->channel[page].vout_voltage_divider[1]); 261 ret = clamp_val(temp, 0, 0xffff); 262 break; 263 default: 264 ret = -ENODATA; 265 break; 266 } 267 return ret; 268} 269 270static struct pmbus_driver_info raa_dmpvr_info = { 271 .pages = 3, 272 .format[PSC_VOLTAGE_IN] = direct, 273 .format[PSC_VOLTAGE_OUT] = direct, 274 .format[PSC_CURRENT_IN] = direct, 275 .format[PSC_CURRENT_OUT] = direct, 276 .format[PSC_POWER] = direct, 277 .format[PSC_TEMPERATURE] = direct, 278 .m[PSC_VOLTAGE_IN] = 1, 279 .b[PSC_VOLTAGE_IN] = 0, 280 .R[PSC_VOLTAGE_IN] = 2, 281 .m[PSC_VOLTAGE_OUT] = 1, 282 .b[PSC_VOLTAGE_OUT] = 0, 283 .R[PSC_VOLTAGE_OUT] = 3, 284 .m[PSC_CURRENT_IN] = 1, 285 .b[PSC_CURRENT_IN] = 0, 286 .R[PSC_CURRENT_IN] = 2, 287 .m[PSC_CURRENT_OUT] = 1, 288 .b[PSC_CURRENT_OUT] = 0, 289 .R[PSC_CURRENT_OUT] = 1, 290 .m[PSC_POWER] = 1, 291 .b[PSC_POWER] = 0, 292 .R[PSC_POWER] = 0, 293 .m[PSC_TEMPERATURE] = 1, 294 .b[PSC_TEMPERATURE] = 0, 295 .R[PSC_TEMPERATURE] = 0, 296 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN 297 | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 298 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 299 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 300 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT 301 | PMBUS_HAVE_VMON, 302 .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT 303 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 304 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT 305 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 306 .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT 307 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP 308 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT 309 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, 310}; 311 312static int isl68137_probe_child_from_dt(struct device *dev, 313 struct device_node *child, 314 struct isl68137_data *data) 315{ 316 u32 channel, rout, rtotal; 317 int err; 318 319 err = of_property_read_u32(child, "reg", &channel); 320 if (err) { 321 dev_err(dev, "missing reg property of %pOFn\n", child); 322 return err; 323 } 324 if (channel >= data->info.pages) { 325 dev_err(dev, "invalid reg %d of %pOFn\n", channel, child); 326 return -EINVAL; 327 } 328 329 err = of_property_read_u32_array(child, "vout-voltage-divider", 330 data->channel[channel].vout_voltage_divider, 331 ARRAY_SIZE(data->channel[channel].vout_voltage_divider)); 332 if (err && err != -EINVAL) { 333 dev_err(dev, 334 "malformed vout-voltage-divider value for channel %d\n", 335 channel); 336 return err; 337 } 338 339 rout = data->channel[channel].vout_voltage_divider[0]; 340 rtotal = data->channel[channel].vout_voltage_divider[1]; 341 if (rout == 0) { 342 dev_err(dev, 343 "Voltage divider output resistance must be greater than 0\n"); 344 return -EINVAL; 345 } 346 if (rtotal < rout) { 347 dev_err(dev, 348 "Voltage divider total resistance is less than output resistance\n"); 349 return -EINVAL; 350 } 351 352 return 0; 353} 354 355static int isl68137_probe_from_dt(struct device *dev, 356 struct isl68137_data *data) 357{ 358 const struct device_node *np = dev->of_node; 359 int err; 360 361 for_each_child_of_node_scoped(np, child) { 362 if (strcmp(child->name, "channel")) 363 continue; 364 365 err = isl68137_probe_child_from_dt(dev, child, data); 366 if (err) 367 return err; 368 } 369 370 return 0; 371} 372 373static int isl68137_probe(struct i2c_client *client) 374{ 375 struct device *dev = &client->dev; 376 struct pmbus_driver_info *info; 377 struct isl68137_data *data; 378 int i, err; 379 380 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 381 if (!data) 382 return -ENOMEM; 383 384 /* 385 * Initialize all voltage dividers to Rout=1 and Rtotal=1 to simplify 386 * logic in PMBus word read/write functions 387 */ 388 for (i = 0; i < MAX_CHANNELS; i++) 389 memset(data->channel[i].vout_voltage_divider, 390 1, 391 sizeof(data->channel[i].vout_voltage_divider)); 392 393 memcpy(&data->info, &raa_dmpvr_info, sizeof(data->info)); 394 info = &data->info; 395 396 switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) { 397 case raa_dmpvr1_2rail: 398 info->pages = 2; 399 info->R[PSC_VOLTAGE_IN] = 3; 400 info->func[0] &= ~PMBUS_HAVE_VMON; 401 info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT 402 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT 403 | PMBUS_HAVE_POUT; 404 info->read_word_data = raa_dmpvr2_read_word_data; 405 info->write_word_data = raa_dmpvr2_write_word_data; 406 info->groups = isl68137_attribute_groups; 407 break; 408 case raa_dmpvr2_1rail: 409 info->pages = 1; 410 info->read_word_data = raa_dmpvr2_read_word_data; 411 info->write_word_data = raa_dmpvr2_write_word_data; 412 break; 413 case raa_dmpvr2_2rail_nontc: 414 info->func[0] &= ~PMBUS_HAVE_TEMP3; 415 info->func[1] &= ~PMBUS_HAVE_TEMP3; 416 fallthrough; 417 case raa_dmpvr2_2rail: 418 info->pages = 2; 419 info->read_word_data = raa_dmpvr2_read_word_data; 420 info->write_word_data = raa_dmpvr2_write_word_data; 421 break; 422 case raa_dmpvr2_2rail_pmbus: 423 info->format[PSC_VOLTAGE_IN] = linear, 424 info->format[PSC_VOLTAGE_OUT] = linear, 425 info->format[PSC_CURRENT_IN] = linear; 426 info->format[PSC_CURRENT_OUT] = linear; 427 info->format[PSC_POWER] = linear; 428 info->format[PSC_TEMPERATURE] = linear; 429 info->pages = 2; 430 info->read_word_data = raa_dmpvr2_read_word_data; 431 info->write_word_data = raa_dmpvr2_write_word_data; 432 break; 433 case raa_dmpvr2_3rail: 434 info->read_word_data = raa_dmpvr2_read_word_data; 435 info->write_word_data = raa_dmpvr2_write_word_data; 436 break; 437 case raa_dmpvr2_hv: 438 info->pages = 1; 439 info->R[PSC_VOLTAGE_IN] = 1; 440 info->m[PSC_VOLTAGE_OUT] = 2; 441 info->R[PSC_VOLTAGE_OUT] = 2; 442 info->m[PSC_CURRENT_IN] = 2; 443 info->m[PSC_POWER] = 2; 444 info->R[PSC_POWER] = -1; 445 info->read_word_data = raa_dmpvr2_read_word_data; 446 info->write_word_data = raa_dmpvr2_write_word_data; 447 break; 448 default: 449 return -ENODEV; 450 } 451 452 err = isl68137_probe_from_dt(dev, data); 453 if (err) 454 return err; 455 456 return pmbus_do_probe(client, info); 457} 458 459static const struct i2c_device_id raa_dmpvr_id[] = { 460 {"isl68137", raa_dmpvr1_2rail}, 461 {"isl68220", raa_dmpvr2_2rail}, 462 {"isl68221", raa_dmpvr2_3rail}, 463 {"isl68222", raa_dmpvr2_2rail}, 464 {"isl68223", raa_dmpvr2_2rail}, 465 {"isl68224", raa_dmpvr2_3rail}, 466 {"isl68225", raa_dmpvr2_2rail}, 467 {"isl68226", raa_dmpvr2_3rail}, 468 {"isl68227", raa_dmpvr2_1rail}, 469 {"isl68229", raa_dmpvr2_3rail}, 470 {"isl68233", raa_dmpvr2_2rail}, 471 {"isl68239", raa_dmpvr2_3rail}, 472 473 {"isl69222", raa_dmpvr2_2rail}, 474 {"isl69223", raa_dmpvr2_3rail}, 475 {"isl69224", raa_dmpvr2_2rail}, 476 {"isl69225", raa_dmpvr2_2rail}, 477 {"isl69227", raa_dmpvr2_3rail}, 478 {"isl69228", raa_dmpvr2_3rail}, 479 {"isl69234", raa_dmpvr2_2rail}, 480 {"isl69236", raa_dmpvr2_2rail}, 481 {"isl69239", raa_dmpvr2_3rail}, 482 {"isl69242", raa_dmpvr2_2rail}, 483 {"isl69243", raa_dmpvr2_1rail}, 484 {"isl69247", raa_dmpvr2_2rail}, 485 {"isl69248", raa_dmpvr2_2rail}, 486 {"isl69254", raa_dmpvr2_2rail}, 487 {"isl69255", raa_dmpvr2_2rail}, 488 {"isl69256", raa_dmpvr2_2rail}, 489 {"isl69259", raa_dmpvr2_2rail}, 490 {"isl69260", raa_dmpvr2_2rail}, 491 {"isl69268", raa_dmpvr2_2rail}, 492 {"isl69269", raa_dmpvr2_3rail}, 493 {"isl69298", raa_dmpvr2_2rail}, 494 495 {"raa228000", raa_dmpvr2_hv}, 496 {"raa228004", raa_dmpvr2_hv}, 497 {"raa228006", raa_dmpvr2_hv}, 498 {"raa228228", raa_dmpvr2_2rail_nontc}, 499 {"raa228244", raa_dmpvr2_2rail_nontc}, 500 {"raa228246", raa_dmpvr2_2rail_nontc}, 501 {"raa229001", raa_dmpvr2_2rail}, 502 {"raa229004", raa_dmpvr2_2rail}, 503 {"raa229141", raa_dmpvr2_2rail_pmbus}, 504 {"raa229621", raa_dmpvr2_2rail}, 505 {} 506}; 507 508MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); 509 510static const struct of_device_id isl68137_of_match[] = { 511 { .compatible = "isil,isl68137", .data = (void *)raa_dmpvr1_2rail }, 512 { .compatible = "renesas,isl68220", .data = (void *)raa_dmpvr2_2rail }, 513 { .compatible = "renesas,isl68221", .data = (void *)raa_dmpvr2_3rail }, 514 { .compatible = "renesas,isl68222", .data = (void *)raa_dmpvr2_2rail }, 515 { .compatible = "renesas,isl68223", .data = (void *)raa_dmpvr2_2rail }, 516 { .compatible = "renesas,isl68224", .data = (void *)raa_dmpvr2_3rail }, 517 { .compatible = "renesas,isl68225", .data = (void *)raa_dmpvr2_2rail }, 518 { .compatible = "renesas,isl68226", .data = (void *)raa_dmpvr2_3rail }, 519 { .compatible = "renesas,isl68227", .data = (void *)raa_dmpvr2_1rail }, 520 { .compatible = "renesas,isl68229", .data = (void *)raa_dmpvr2_3rail }, 521 { .compatible = "renesas,isl68233", .data = (void *)raa_dmpvr2_2rail }, 522 { .compatible = "renesas,isl68239", .data = (void *)raa_dmpvr2_3rail }, 523 524 { .compatible = "renesas,isl69222", .data = (void *)raa_dmpvr2_2rail }, 525 { .compatible = "renesas,isl69223", .data = (void *)raa_dmpvr2_3rail }, 526 { .compatible = "renesas,isl69224", .data = (void *)raa_dmpvr2_2rail }, 527 { .compatible = "renesas,isl69225", .data = (void *)raa_dmpvr2_2rail }, 528 { .compatible = "renesas,isl69227", .data = (void *)raa_dmpvr2_3rail }, 529 { .compatible = "renesas,isl69228", .data = (void *)raa_dmpvr2_3rail }, 530 { .compatible = "renesas,isl69234", .data = (void *)raa_dmpvr2_2rail }, 531 { .compatible = "renesas,isl69236", .data = (void *)raa_dmpvr2_2rail }, 532 { .compatible = "renesas,isl69239", .data = (void *)raa_dmpvr2_3rail }, 533 { .compatible = "renesas,isl69242", .data = (void *)raa_dmpvr2_2rail }, 534 { .compatible = "renesas,isl69243", .data = (void *)raa_dmpvr2_1rail }, 535 { .compatible = "renesas,isl69247", .data = (void *)raa_dmpvr2_2rail }, 536 { .compatible = "renesas,isl69248", .data = (void *)raa_dmpvr2_2rail }, 537 { .compatible = "renesas,isl69254", .data = (void *)raa_dmpvr2_2rail }, 538 { .compatible = "renesas,isl69255", .data = (void *)raa_dmpvr2_2rail }, 539 { .compatible = "renesas,isl69256", .data = (void *)raa_dmpvr2_2rail }, 540 { .compatible = "renesas,isl69259", .data = (void *)raa_dmpvr2_2rail }, 541 { .compatible = "isil,isl69260", .data = (void *)raa_dmpvr2_2rail }, 542 { .compatible = "renesas,isl69268", .data = (void *)raa_dmpvr2_2rail }, 543 { .compatible = "isil,isl69269", .data = (void *)raa_dmpvr2_3rail }, 544 { .compatible = "renesas,isl69298", .data = (void *)raa_dmpvr2_2rail }, 545 546 { .compatible = "renesas,raa228000", .data = (void *)raa_dmpvr2_hv }, 547 { .compatible = "renesas,raa228004", .data = (void *)raa_dmpvr2_hv }, 548 { .compatible = "renesas,raa228006", .data = (void *)raa_dmpvr2_hv }, 549 { .compatible = "renesas,raa228228", .data = (void *)raa_dmpvr2_2rail_nontc }, 550 { .compatible = "renesas,raa228244", .data = (void *)raa_dmpvr2_2rail_nontc }, 551 { .compatible = "renesas,raa228246", .data = (void *)raa_dmpvr2_2rail_nontc }, 552 { .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail }, 553 { .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail }, 554 { .compatible = "renesas,raa229621", .data = (void *)raa_dmpvr2_2rail }, 555 { }, 556}; 557 558MODULE_DEVICE_TABLE(of, isl68137_of_match); 559 560/* This is the driver that will be inserted */ 561static struct i2c_driver isl68137_driver = { 562 .driver = { 563 .name = "isl68137", 564 .of_match_table = isl68137_of_match, 565 }, 566 .probe = isl68137_probe, 567 .id_table = raa_dmpvr_id, 568}; 569 570module_i2c_driver(isl68137_driver); 571 572MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); 573MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); 574MODULE_LICENSE("GPL"); 575MODULE_IMPORT_NS("PMBUS");
~/
Read-only file preview.