| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Hardware monitoring driver for PMBus devices |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2010, 2011 Ericsson AB. |
|---|
| 5 | 6 | * Copyright (c) 2012 Guenter Roeck |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 9 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | | - * (at your option) any later version. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | | - * GNU General Public License for more details. |
|---|
| 16 | | - * |
|---|
| 17 | | - * You should have received a copy of the GNU General Public License |
|---|
| 18 | | - * along with this program; if not, write to the Free Software |
|---|
| 19 | | - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| 20 | 7 | */ |
|---|
| 21 | 8 | |
|---|
| 22 | 9 | #include <linux/debugfs.h> |
|---|
| .. | .. |
|---|
| 29 | 16 | #include <linux/i2c.h> |
|---|
| 30 | 17 | #include <linux/hwmon.h> |
|---|
| 31 | 18 | #include <linux/hwmon-sysfs.h> |
|---|
| 32 | | -#include <linux/jiffies.h> |
|---|
| 33 | 19 | #include <linux/pmbus.h> |
|---|
| 34 | 20 | #include <linux/regulator/driver.h> |
|---|
| 35 | 21 | #include <linux/regulator/machine.h> |
|---|
| .. | .. |
|---|
| 40 | 26 | * with each call to krealloc |
|---|
| 41 | 27 | */ |
|---|
| 42 | 28 | #define PMBUS_ATTR_ALLOC_SIZE 32 |
|---|
| 43 | | - |
|---|
| 44 | | -/* |
|---|
| 45 | | - * Index into status register array, per status register group |
|---|
| 46 | | - */ |
|---|
| 47 | | -#define PB_STATUS_BASE 0 |
|---|
| 48 | | -#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) |
|---|
| 49 | | -#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) |
|---|
| 50 | | -#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) |
|---|
| 51 | | -#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) |
|---|
| 52 | | -#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) |
|---|
| 53 | | -#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) |
|---|
| 54 | | -#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) |
|---|
| 55 | | - |
|---|
| 56 | | -#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) |
|---|
| 57 | | - |
|---|
| 58 | 29 | #define PMBUS_NAME_SIZE 24 |
|---|
| 59 | 30 | |
|---|
| 60 | 31 | struct pmbus_sensor { |
|---|
| .. | .. |
|---|
| 62 | 33 | char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ |
|---|
| 63 | 34 | struct device_attribute attribute; |
|---|
| 64 | 35 | u8 page; /* page number */ |
|---|
| 36 | + u8 phase; /* phase number, 0xff for all phases */ |
|---|
| 65 | 37 | u16 reg; /* register */ |
|---|
| 66 | 38 | enum pmbus_sensor_classes class; /* sensor class */ |
|---|
| 67 | 39 | bool update; /* runtime sensor update needed */ |
|---|
| .. | .. |
|---|
| 89 | 61 | #define to_pmbus_label(_attr) \ |
|---|
| 90 | 62 | container_of(_attr, struct pmbus_label, attribute) |
|---|
| 91 | 63 | |
|---|
| 64 | +/* Macros for converting between sensor index and register/page/status mask */ |
|---|
| 65 | + |
|---|
| 66 | +#define PB_STATUS_MASK 0xffff |
|---|
| 67 | +#define PB_REG_SHIFT 16 |
|---|
| 68 | +#define PB_REG_MASK 0x3ff |
|---|
| 69 | +#define PB_PAGE_SHIFT 26 |
|---|
| 70 | +#define PB_PAGE_MASK 0x3f |
|---|
| 71 | + |
|---|
| 72 | +#define pb_reg_to_index(page, reg, mask) (((page) << PB_PAGE_SHIFT) | \ |
|---|
| 73 | + ((reg) << PB_REG_SHIFT) | (mask)) |
|---|
| 74 | + |
|---|
| 75 | +#define pb_index_to_page(index) (((index) >> PB_PAGE_SHIFT) & PB_PAGE_MASK) |
|---|
| 76 | +#define pb_index_to_reg(index) (((index) >> PB_REG_SHIFT) & PB_REG_MASK) |
|---|
| 77 | +#define pb_index_to_mask(index) ((index) & PB_STATUS_MASK) |
|---|
| 78 | + |
|---|
| 92 | 79 | struct pmbus_data { |
|---|
| 93 | 80 | struct device *dev; |
|---|
| 94 | 81 | struct device *hwmon_dev; |
|---|
| .. | .. |
|---|
| 103 | 90 | int max_attributes; |
|---|
| 104 | 91 | int num_attributes; |
|---|
| 105 | 92 | struct attribute_group group; |
|---|
| 106 | | - const struct attribute_group *groups[2]; |
|---|
| 93 | + const struct attribute_group **groups; |
|---|
| 107 | 94 | struct dentry *debugfs; /* debugfs device directory */ |
|---|
| 108 | 95 | |
|---|
| 109 | 96 | struct pmbus_sensor *sensors; |
|---|
| 110 | 97 | |
|---|
| 111 | 98 | struct mutex update_lock; |
|---|
| 112 | | - bool valid; |
|---|
| 113 | | - unsigned long last_updated; /* in jiffies */ |
|---|
| 114 | | - |
|---|
| 115 | | - /* |
|---|
| 116 | | - * A single status register covers multiple attributes, |
|---|
| 117 | | - * so we keep them all together. |
|---|
| 118 | | - */ |
|---|
| 119 | | - u16 status[PB_NUM_STATUS_REG]; |
|---|
| 120 | 99 | |
|---|
| 121 | 100 | bool has_status_word; /* device uses STATUS_WORD register */ |
|---|
| 122 | 101 | int (*read_status)(struct i2c_client *client, int page); |
|---|
| 123 | 102 | |
|---|
| 124 | | - u8 currpage; |
|---|
| 103 | + s16 currpage; /* current page, -1 for unknown/unset */ |
|---|
| 104 | + s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */ |
|---|
| 125 | 105 | }; |
|---|
| 126 | 106 | |
|---|
| 127 | 107 | struct pmbus_debugfs_entry { |
|---|
| .. | .. |
|---|
| 154 | 134 | void pmbus_clear_cache(struct i2c_client *client) |
|---|
| 155 | 135 | { |
|---|
| 156 | 136 | struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 137 | + struct pmbus_sensor *sensor; |
|---|
| 157 | 138 | |
|---|
| 158 | | - data->valid = false; |
|---|
| 139 | + for (sensor = data->sensors; sensor; sensor = sensor->next) |
|---|
| 140 | + sensor->data = -ENODATA; |
|---|
| 159 | 141 | } |
|---|
| 160 | 142 | EXPORT_SYMBOL_GPL(pmbus_clear_cache); |
|---|
| 161 | 143 | |
|---|
| 162 | | -int pmbus_set_page(struct i2c_client *client, int page) |
|---|
| 144 | +int pmbus_set_page(struct i2c_client *client, int page, int phase) |
|---|
| 163 | 145 | { |
|---|
| 164 | 146 | struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 165 | 147 | int rv; |
|---|
| 166 | 148 | |
|---|
| 167 | | - if (page < 0 || page == data->currpage) |
|---|
| 149 | + if (page < 0) |
|---|
| 168 | 150 | return 0; |
|---|
| 169 | 151 | |
|---|
| 170 | | - if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL)) { |
|---|
| 152 | + if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && |
|---|
| 153 | + data->info->pages > 1 && page != data->currpage) { |
|---|
| 171 | 154 | rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); |
|---|
| 172 | 155 | if (rv < 0) |
|---|
| 173 | 156 | return rv; |
|---|
| .. | .. |
|---|
| 179 | 162 | if (rv != page) |
|---|
| 180 | 163 | return -EIO; |
|---|
| 181 | 164 | } |
|---|
| 182 | | - |
|---|
| 183 | 165 | data->currpage = page; |
|---|
| 166 | + |
|---|
| 167 | + if (data->info->phases[page] && data->currphase != phase && |
|---|
| 168 | + !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { |
|---|
| 169 | + rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, |
|---|
| 170 | + phase); |
|---|
| 171 | + if (rv) |
|---|
| 172 | + return rv; |
|---|
| 173 | + } |
|---|
| 174 | + data->currphase = phase; |
|---|
| 184 | 175 | |
|---|
| 185 | 176 | return 0; |
|---|
| 186 | 177 | } |
|---|
| .. | .. |
|---|
| 190 | 181 | { |
|---|
| 191 | 182 | int rv; |
|---|
| 192 | 183 | |
|---|
| 193 | | - rv = pmbus_set_page(client, page); |
|---|
| 184 | + rv = pmbus_set_page(client, page, 0xff); |
|---|
| 194 | 185 | if (rv < 0) |
|---|
| 195 | 186 | return rv; |
|---|
| 196 | 187 | |
|---|
| .. | .. |
|---|
| 221 | 212 | { |
|---|
| 222 | 213 | int rv; |
|---|
| 223 | 214 | |
|---|
| 224 | | - rv = pmbus_set_page(client, page); |
|---|
| 215 | + rv = pmbus_set_page(client, page, 0xff); |
|---|
| 225 | 216 | if (rv < 0) |
|---|
| 226 | 217 | return rv; |
|---|
| 227 | 218 | |
|---|
| .. | .. |
|---|
| 299 | 290 | } |
|---|
| 300 | 291 | EXPORT_SYMBOL_GPL(pmbus_update_fan); |
|---|
| 301 | 292 | |
|---|
| 302 | | -int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg) |
|---|
| 293 | +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) |
|---|
| 303 | 294 | { |
|---|
| 304 | 295 | int rv; |
|---|
| 305 | 296 | |
|---|
| 306 | | - rv = pmbus_set_page(client, page); |
|---|
| 297 | + rv = pmbus_set_page(client, page, phase); |
|---|
| 307 | 298 | if (rv < 0) |
|---|
| 308 | 299 | return rv; |
|---|
| 309 | 300 | |
|---|
| .. | .. |
|---|
| 333 | 324 | * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if |
|---|
| 334 | 325 | * a device specific mapping function exists and calls it if necessary. |
|---|
| 335 | 326 | */ |
|---|
| 336 | | -static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) |
|---|
| 327 | +static int _pmbus_read_word_data(struct i2c_client *client, int page, |
|---|
| 328 | + int phase, int reg) |
|---|
| 337 | 329 | { |
|---|
| 338 | 330 | struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 339 | 331 | const struct pmbus_driver_info *info = data->info; |
|---|
| 340 | 332 | int status; |
|---|
| 341 | 333 | |
|---|
| 342 | 334 | if (info->read_word_data) { |
|---|
| 343 | | - status = info->read_word_data(client, page, reg); |
|---|
| 335 | + status = info->read_word_data(client, page, phase, reg); |
|---|
| 344 | 336 | if (status != -ENODATA) |
|---|
| 345 | 337 | return status; |
|---|
| 346 | 338 | } |
|---|
| .. | .. |
|---|
| 348 | 340 | if (reg >= PMBUS_VIRT_BASE) |
|---|
| 349 | 341 | return pmbus_read_virt_reg(client, page, reg); |
|---|
| 350 | 342 | |
|---|
| 351 | | - return pmbus_read_word_data(client, page, reg); |
|---|
| 343 | + return pmbus_read_word_data(client, page, phase, reg); |
|---|
| 344 | +} |
|---|
| 345 | + |
|---|
| 346 | +/* Same as above, but without phase parameter, for use in check functions */ |
|---|
| 347 | +static int __pmbus_read_word_data(struct i2c_client *client, int page, int reg) |
|---|
| 348 | +{ |
|---|
| 349 | + return _pmbus_read_word_data(client, page, 0xff, reg); |
|---|
| 352 | 350 | } |
|---|
| 353 | 351 | |
|---|
| 354 | 352 | int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) |
|---|
| 355 | 353 | { |
|---|
| 356 | 354 | int rv; |
|---|
| 357 | 355 | |
|---|
| 358 | | - rv = pmbus_set_page(client, page); |
|---|
| 356 | + rv = pmbus_set_page(client, page, 0xff); |
|---|
| 359 | 357 | if (rv < 0) |
|---|
| 360 | 358 | return rv; |
|---|
| 361 | 359 | |
|---|
| .. | .. |
|---|
| 367 | 365 | { |
|---|
| 368 | 366 | int rv; |
|---|
| 369 | 367 | |
|---|
| 370 | | - rv = pmbus_set_page(client, page); |
|---|
| 368 | + rv = pmbus_set_page(client, page, 0xff); |
|---|
| 371 | 369 | if (rv < 0) |
|---|
| 372 | 370 | return rv; |
|---|
| 373 | 371 | |
|---|
| .. | .. |
|---|
| 453 | 451 | |
|---|
| 454 | 452 | have_rpm = !!(config & pmbus_fan_rpm_mask[id]); |
|---|
| 455 | 453 | if (want_rpm == have_rpm) |
|---|
| 456 | | - return pmbus_read_word_data(client, page, |
|---|
| 454 | + return pmbus_read_word_data(client, page, 0xff, |
|---|
| 457 | 455 | pmbus_fan_command_registers[id]); |
|---|
| 458 | 456 | |
|---|
| 459 | 457 | /* Can't sensibly map between RPM and PWM, just return zero */ |
|---|
| .. | .. |
|---|
| 543 | 541 | |
|---|
| 544 | 542 | bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) |
|---|
| 545 | 543 | { |
|---|
| 546 | | - return pmbus_check_register(client, _pmbus_read_word_data, page, reg); |
|---|
| 544 | + return pmbus_check_register(client, __pmbus_read_word_data, page, reg); |
|---|
| 547 | 545 | } |
|---|
| 548 | 546 | EXPORT_SYMBOL_GPL(pmbus_check_word_register); |
|---|
| 549 | 547 | |
|---|
| .. | .. |
|---|
| 555 | 553 | } |
|---|
| 556 | 554 | EXPORT_SYMBOL_GPL(pmbus_get_driver_info); |
|---|
| 557 | 555 | |
|---|
| 558 | | -static struct _pmbus_status { |
|---|
| 559 | | - u32 func; |
|---|
| 560 | | - u16 base; |
|---|
| 561 | | - u16 reg; |
|---|
| 562 | | -} pmbus_status[] = { |
|---|
| 563 | | - { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, |
|---|
| 564 | | - { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, |
|---|
| 565 | | - { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, |
|---|
| 566 | | - PMBUS_STATUS_TEMPERATURE }, |
|---|
| 567 | | - { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, |
|---|
| 568 | | - { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, |
|---|
| 569 | | -}; |
|---|
| 570 | | - |
|---|
| 571 | | -static struct pmbus_data *pmbus_update_device(struct device *dev) |
|---|
| 556 | +static int pmbus_get_status(struct i2c_client *client, int page, int reg) |
|---|
| 572 | 557 | { |
|---|
| 573 | | - struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 574 | 558 | struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 575 | | - const struct pmbus_driver_info *info = data->info; |
|---|
| 576 | | - struct pmbus_sensor *sensor; |
|---|
| 559 | + int status; |
|---|
| 577 | 560 | |
|---|
| 578 | | - mutex_lock(&data->update_lock); |
|---|
| 579 | | - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
|---|
| 580 | | - int i, j; |
|---|
| 581 | | - |
|---|
| 582 | | - for (i = 0; i < info->pages; i++) { |
|---|
| 583 | | - data->status[PB_STATUS_BASE + i] |
|---|
| 584 | | - = data->read_status(client, i); |
|---|
| 585 | | - for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { |
|---|
| 586 | | - struct _pmbus_status *s = &pmbus_status[j]; |
|---|
| 587 | | - |
|---|
| 588 | | - if (!(info->func[i] & s->func)) |
|---|
| 589 | | - continue; |
|---|
| 590 | | - data->status[s->base + i] |
|---|
| 591 | | - = _pmbus_read_byte_data(client, i, |
|---|
| 592 | | - s->reg); |
|---|
| 593 | | - } |
|---|
| 594 | | - } |
|---|
| 595 | | - |
|---|
| 596 | | - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) |
|---|
| 597 | | - data->status[PB_STATUS_INPUT_BASE] |
|---|
| 598 | | - = _pmbus_read_byte_data(client, 0, |
|---|
| 599 | | - PMBUS_STATUS_INPUT); |
|---|
| 600 | | - |
|---|
| 601 | | - if (info->func[0] & PMBUS_HAVE_STATUS_VMON) |
|---|
| 602 | | - data->status[PB_STATUS_VMON_BASE] |
|---|
| 603 | | - = _pmbus_read_byte_data(client, 0, |
|---|
| 604 | | - PMBUS_VIRT_STATUS_VMON); |
|---|
| 605 | | - |
|---|
| 606 | | - for (sensor = data->sensors; sensor; sensor = sensor->next) { |
|---|
| 607 | | - if (!data->valid || sensor->update) |
|---|
| 608 | | - sensor->data |
|---|
| 609 | | - = _pmbus_read_word_data(client, |
|---|
| 610 | | - sensor->page, |
|---|
| 611 | | - sensor->reg); |
|---|
| 612 | | - } |
|---|
| 613 | | - pmbus_clear_faults(client); |
|---|
| 614 | | - data->last_updated = jiffies; |
|---|
| 615 | | - data->valid = 1; |
|---|
| 561 | + switch (reg) { |
|---|
| 562 | + case PMBUS_STATUS_WORD: |
|---|
| 563 | + status = data->read_status(client, page); |
|---|
| 564 | + break; |
|---|
| 565 | + default: |
|---|
| 566 | + status = _pmbus_read_byte_data(client, page, reg); |
|---|
| 567 | + break; |
|---|
| 616 | 568 | } |
|---|
| 617 | | - mutex_unlock(&data->update_lock); |
|---|
| 618 | | - return data; |
|---|
| 569 | + if (status < 0) |
|---|
| 570 | + pmbus_clear_faults(client); |
|---|
| 571 | + return status; |
|---|
| 572 | +} |
|---|
| 573 | + |
|---|
| 574 | +static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sensor *sensor) |
|---|
| 575 | +{ |
|---|
| 576 | + if (sensor->data < 0 || sensor->update) |
|---|
| 577 | + sensor->data = _pmbus_read_word_data(client, sensor->page, |
|---|
| 578 | + sensor->phase, sensor->reg); |
|---|
| 619 | 579 | } |
|---|
| 620 | 580 | |
|---|
| 621 | 581 | /* |
|---|
| 622 | 582 | * Convert linear sensor values to milli- or micro-units |
|---|
| 623 | 583 | * depending on sensor type. |
|---|
| 624 | 584 | */ |
|---|
| 625 | | -static long pmbus_reg2data_linear(struct pmbus_data *data, |
|---|
| 626 | | - struct pmbus_sensor *sensor) |
|---|
| 585 | +static s64 pmbus_reg2data_linear(struct pmbus_data *data, |
|---|
| 586 | + struct pmbus_sensor *sensor) |
|---|
| 627 | 587 | { |
|---|
| 628 | 588 | s16 exponent; |
|---|
| 629 | 589 | s32 mantissa; |
|---|
| 630 | | - long val; |
|---|
| 590 | + s64 val; |
|---|
| 631 | 591 | |
|---|
| 632 | 592 | if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ |
|---|
| 633 | 593 | exponent = data->exponent[sensor->page]; |
|---|
| .. | .. |
|---|
| 641 | 601 | |
|---|
| 642 | 602 | /* scale result to milli-units for all sensors except fans */ |
|---|
| 643 | 603 | if (sensor->class != PSC_FAN) |
|---|
| 644 | | - val = val * 1000L; |
|---|
| 604 | + val = val * 1000LL; |
|---|
| 645 | 605 | |
|---|
| 646 | 606 | /* scale result to micro-units for power sensors */ |
|---|
| 647 | 607 | if (sensor->class == PSC_POWER) |
|---|
| 648 | | - val = val * 1000L; |
|---|
| 608 | + val = val * 1000LL; |
|---|
| 649 | 609 | |
|---|
| 650 | 610 | if (exponent >= 0) |
|---|
| 651 | 611 | val <<= exponent; |
|---|
| .. | .. |
|---|
| 659 | 619 | * Convert direct sensor values to milli- or micro-units |
|---|
| 660 | 620 | * depending on sensor type. |
|---|
| 661 | 621 | */ |
|---|
| 662 | | -static long pmbus_reg2data_direct(struct pmbus_data *data, |
|---|
| 663 | | - struct pmbus_sensor *sensor) |
|---|
| 622 | +static s64 pmbus_reg2data_direct(struct pmbus_data *data, |
|---|
| 623 | + struct pmbus_sensor *sensor) |
|---|
| 664 | 624 | { |
|---|
| 665 | 625 | s64 b, val = (s16)sensor->data; |
|---|
| 666 | 626 | s32 m, R; |
|---|
| .. | .. |
|---|
| 696 | 656 | } |
|---|
| 697 | 657 | |
|---|
| 698 | 658 | val = div_s64(val - b, m); |
|---|
| 699 | | - return clamp_val(val, LONG_MIN, LONG_MAX); |
|---|
| 659 | + return val; |
|---|
| 700 | 660 | } |
|---|
| 701 | 661 | |
|---|
| 702 | 662 | /* |
|---|
| 703 | 663 | * Convert VID sensor values to milli- or micro-units |
|---|
| 704 | 664 | * depending on sensor type. |
|---|
| 705 | 665 | */ |
|---|
| 706 | | -static long pmbus_reg2data_vid(struct pmbus_data *data, |
|---|
| 707 | | - struct pmbus_sensor *sensor) |
|---|
| 666 | +static s64 pmbus_reg2data_vid(struct pmbus_data *data, |
|---|
| 667 | + struct pmbus_sensor *sensor) |
|---|
| 708 | 668 | { |
|---|
| 709 | 669 | long val = sensor->data; |
|---|
| 710 | 670 | long rv = 0; |
|---|
| 711 | 671 | |
|---|
| 712 | | - switch (data->info->vrm_version) { |
|---|
| 672 | + switch (data->info->vrm_version[sensor->page]) { |
|---|
| 713 | 673 | case vr11: |
|---|
| 714 | 674 | if (val >= 0x02 && val <= 0xb2) |
|---|
| 715 | 675 | rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); |
|---|
| .. | .. |
|---|
| 722 | 682 | if (val >= 0x01) |
|---|
| 723 | 683 | rv = 500 + (val - 1) * 10; |
|---|
| 724 | 684 | break; |
|---|
| 685 | + case imvp9: |
|---|
| 686 | + if (val >= 0x01) |
|---|
| 687 | + rv = 200 + (val - 1) * 10; |
|---|
| 688 | + break; |
|---|
| 689 | + case amd625mv: |
|---|
| 690 | + if (val >= 0x0 && val <= 0xd8) |
|---|
| 691 | + rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100); |
|---|
| 692 | + break; |
|---|
| 725 | 693 | } |
|---|
| 726 | 694 | return rv; |
|---|
| 727 | 695 | } |
|---|
| 728 | 696 | |
|---|
| 729 | | -static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) |
|---|
| 697 | +static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) |
|---|
| 730 | 698 | { |
|---|
| 731 | | - long val; |
|---|
| 699 | + s64 val; |
|---|
| 732 | 700 | |
|---|
| 733 | 701 | if (!sensor->convert) |
|---|
| 734 | 702 | return sensor->data; |
|---|
| .. | .. |
|---|
| 752 | 720 | #define MIN_MANTISSA (511 * 1000) |
|---|
| 753 | 721 | |
|---|
| 754 | 722 | static u16 pmbus_data2reg_linear(struct pmbus_data *data, |
|---|
| 755 | | - struct pmbus_sensor *sensor, long val) |
|---|
| 723 | + struct pmbus_sensor *sensor, s64 val) |
|---|
| 756 | 724 | { |
|---|
| 757 | 725 | s16 exponent = 0, mantissa; |
|---|
| 758 | 726 | bool negative = false; |
|---|
| .. | .. |
|---|
| 774 | 742 | val <<= -data->exponent[sensor->page]; |
|---|
| 775 | 743 | else |
|---|
| 776 | 744 | val >>= data->exponent[sensor->page]; |
|---|
| 777 | | - val = DIV_ROUND_CLOSEST(val, 1000); |
|---|
| 778 | | - return val & 0xffff; |
|---|
| 745 | + val = DIV_ROUND_CLOSEST_ULL(val, 1000); |
|---|
| 746 | + return clamp_val(val, 0, 0xffff); |
|---|
| 779 | 747 | } |
|---|
| 780 | 748 | |
|---|
| 781 | 749 | if (val < 0) { |
|---|
| .. | .. |
|---|
| 785 | 753 | |
|---|
| 786 | 754 | /* Power is in uW. Convert to mW before converting. */ |
|---|
| 787 | 755 | if (sensor->class == PSC_POWER) |
|---|
| 788 | | - val = DIV_ROUND_CLOSEST(val, 1000L); |
|---|
| 756 | + val = DIV_ROUND_CLOSEST_ULL(val, 1000); |
|---|
| 789 | 757 | |
|---|
| 790 | 758 | /* |
|---|
| 791 | 759 | * For simplicity, convert fan data to milli-units |
|---|
| 792 | 760 | * before calculating the exponent. |
|---|
| 793 | 761 | */ |
|---|
| 794 | 762 | if (sensor->class == PSC_FAN) |
|---|
| 795 | | - val = val * 1000; |
|---|
| 763 | + val = val * 1000LL; |
|---|
| 796 | 764 | |
|---|
| 797 | 765 | /* Reduce large mantissa until it fits into 10 bit */ |
|---|
| 798 | 766 | while (val >= MAX_MANTISSA && exponent < 15) { |
|---|
| .. | .. |
|---|
| 806 | 774 | } |
|---|
| 807 | 775 | |
|---|
| 808 | 776 | /* Convert mantissa from milli-units to units */ |
|---|
| 809 | | - mantissa = DIV_ROUND_CLOSEST(val, 1000); |
|---|
| 810 | | - |
|---|
| 811 | | - /* Ensure that resulting number is within range */ |
|---|
| 812 | | - if (mantissa > 0x3ff) |
|---|
| 813 | | - mantissa = 0x3ff; |
|---|
| 777 | + mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); |
|---|
| 814 | 778 | |
|---|
| 815 | 779 | /* restore sign */ |
|---|
| 816 | 780 | if (negative) |
|---|
| .. | .. |
|---|
| 821 | 785 | } |
|---|
| 822 | 786 | |
|---|
| 823 | 787 | static u16 pmbus_data2reg_direct(struct pmbus_data *data, |
|---|
| 824 | | - struct pmbus_sensor *sensor, long val) |
|---|
| 788 | + struct pmbus_sensor *sensor, s64 val) |
|---|
| 825 | 789 | { |
|---|
| 826 | | - s64 b, val64 = val; |
|---|
| 790 | + s64 b; |
|---|
| 827 | 791 | s32 m, R; |
|---|
| 828 | 792 | |
|---|
| 829 | 793 | m = data->info->m[sensor->class]; |
|---|
| .. | .. |
|---|
| 841 | 805 | R -= 3; /* Adjust R and b for data in milli-units */ |
|---|
| 842 | 806 | b *= 1000; |
|---|
| 843 | 807 | } |
|---|
| 844 | | - val64 = val64 * m + b; |
|---|
| 808 | + val = val * m + b; |
|---|
| 845 | 809 | |
|---|
| 846 | 810 | while (R > 0) { |
|---|
| 847 | | - val64 *= 10; |
|---|
| 811 | + val *= 10; |
|---|
| 848 | 812 | R--; |
|---|
| 849 | 813 | } |
|---|
| 850 | 814 | while (R < 0) { |
|---|
| 851 | | - val64 = div_s64(val64 + 5LL, 10L); /* round closest */ |
|---|
| 815 | + val = div_s64(val + 5LL, 10L); /* round closest */ |
|---|
| 852 | 816 | R++; |
|---|
| 853 | 817 | } |
|---|
| 854 | 818 | |
|---|
| 855 | | - return (u16)clamp_val(val64, S16_MIN, S16_MAX); |
|---|
| 819 | + return (u16)clamp_val(val, S16_MIN, S16_MAX); |
|---|
| 856 | 820 | } |
|---|
| 857 | 821 | |
|---|
| 858 | 822 | static u16 pmbus_data2reg_vid(struct pmbus_data *data, |
|---|
| 859 | | - struct pmbus_sensor *sensor, long val) |
|---|
| 823 | + struct pmbus_sensor *sensor, s64 val) |
|---|
| 860 | 824 | { |
|---|
| 861 | 825 | val = clamp_val(val, 500, 1600); |
|---|
| 862 | 826 | |
|---|
| 863 | | - return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); |
|---|
| 827 | + return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); |
|---|
| 864 | 828 | } |
|---|
| 865 | 829 | |
|---|
| 866 | 830 | static u16 pmbus_data2reg(struct pmbus_data *data, |
|---|
| 867 | | - struct pmbus_sensor *sensor, long val) |
|---|
| 831 | + struct pmbus_sensor *sensor, s64 val) |
|---|
| 868 | 832 | { |
|---|
| 869 | 833 | u16 regval; |
|---|
| 870 | 834 | |
|---|
| .. | .. |
|---|
| 909 | 873 | * If a negative value is stored in any of the referenced registers, this value |
|---|
| 910 | 874 | * reflects an error code which will be returned. |
|---|
| 911 | 875 | */ |
|---|
| 912 | | -static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, |
|---|
| 876 | +static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, |
|---|
| 913 | 877 | int index) |
|---|
| 914 | 878 | { |
|---|
| 879 | + struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 915 | 880 | struct pmbus_sensor *s1 = b->s1; |
|---|
| 916 | 881 | struct pmbus_sensor *s2 = b->s2; |
|---|
| 917 | | - u16 reg = (index >> 16) & 0xffff; |
|---|
| 918 | | - u16 mask = index & 0xffff; |
|---|
| 882 | + u16 mask = pb_index_to_mask(index); |
|---|
| 883 | + u8 page = pb_index_to_page(index); |
|---|
| 884 | + u16 reg = pb_index_to_reg(index); |
|---|
| 919 | 885 | int ret, status; |
|---|
| 920 | 886 | u16 regval; |
|---|
| 921 | 887 | |
|---|
| 922 | | - status = data->status[reg]; |
|---|
| 923 | | - if (status < 0) |
|---|
| 924 | | - return status; |
|---|
| 888 | + mutex_lock(&data->update_lock); |
|---|
| 889 | + status = pmbus_get_status(client, page, reg); |
|---|
| 890 | + if (status < 0) { |
|---|
| 891 | + ret = status; |
|---|
| 892 | + goto unlock; |
|---|
| 893 | + } |
|---|
| 894 | + |
|---|
| 895 | + if (s1) |
|---|
| 896 | + pmbus_update_sensor_data(client, s1); |
|---|
| 897 | + if (s2) |
|---|
| 898 | + pmbus_update_sensor_data(client, s2); |
|---|
| 925 | 899 | |
|---|
| 926 | 900 | regval = status & mask; |
|---|
| 927 | | - if (!s1 && !s2) { |
|---|
| 928 | | - ret = !!regval; |
|---|
| 929 | | - } else if (!s1 || !s2) { |
|---|
| 930 | | - WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); |
|---|
| 931 | | - return 0; |
|---|
| 932 | | - } else { |
|---|
| 933 | | - long v1, v2; |
|---|
| 901 | + if (regval) { |
|---|
| 902 | + ret = pmbus_write_byte_data(client, page, reg, regval); |
|---|
| 903 | + if (ret) |
|---|
| 904 | + goto unlock; |
|---|
| 905 | + } |
|---|
| 906 | + if (s1 && s2) { |
|---|
| 907 | + s64 v1, v2; |
|---|
| 934 | 908 | |
|---|
| 935 | | - if (s1->data < 0) |
|---|
| 936 | | - return s1->data; |
|---|
| 937 | | - if (s2->data < 0) |
|---|
| 938 | | - return s2->data; |
|---|
| 909 | + if (s1->data < 0) { |
|---|
| 910 | + ret = s1->data; |
|---|
| 911 | + goto unlock; |
|---|
| 912 | + } |
|---|
| 913 | + if (s2->data < 0) { |
|---|
| 914 | + ret = s2->data; |
|---|
| 915 | + goto unlock; |
|---|
| 916 | + } |
|---|
| 939 | 917 | |
|---|
| 940 | 918 | v1 = pmbus_reg2data(data, s1); |
|---|
| 941 | 919 | v2 = pmbus_reg2data(data, s2); |
|---|
| 942 | 920 | ret = !!(regval && v1 >= v2); |
|---|
| 921 | + } else { |
|---|
| 922 | + ret = !!regval; |
|---|
| 943 | 923 | } |
|---|
| 924 | +unlock: |
|---|
| 925 | + mutex_unlock(&data->update_lock); |
|---|
| 944 | 926 | return ret; |
|---|
| 945 | 927 | } |
|---|
| 946 | 928 | |
|---|
| .. | .. |
|---|
| 949 | 931 | { |
|---|
| 950 | 932 | struct sensor_device_attribute *attr = to_sensor_dev_attr(da); |
|---|
| 951 | 933 | struct pmbus_boolean *boolean = to_pmbus_boolean(attr); |
|---|
| 952 | | - struct pmbus_data *data = pmbus_update_device(dev); |
|---|
| 934 | + struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 953 | 935 | int val; |
|---|
| 954 | 936 | |
|---|
| 955 | | - val = pmbus_get_boolean(data, boolean, attr->index); |
|---|
| 937 | + val = pmbus_get_boolean(client, boolean, attr->index); |
|---|
| 956 | 938 | if (val < 0) |
|---|
| 957 | 939 | return val; |
|---|
| 958 | 940 | return snprintf(buf, PAGE_SIZE, "%d\n", val); |
|---|
| .. | .. |
|---|
| 961 | 943 | static ssize_t pmbus_show_sensor(struct device *dev, |
|---|
| 962 | 944 | struct device_attribute *devattr, char *buf) |
|---|
| 963 | 945 | { |
|---|
| 964 | | - struct pmbus_data *data = pmbus_update_device(dev); |
|---|
| 946 | + struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 965 | 947 | struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); |
|---|
| 948 | + struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 949 | + ssize_t ret; |
|---|
| 966 | 950 | |
|---|
| 951 | + mutex_lock(&data->update_lock); |
|---|
| 952 | + pmbus_update_sensor_data(client, sensor); |
|---|
| 967 | 953 | if (sensor->data < 0) |
|---|
| 968 | | - return sensor->data; |
|---|
| 969 | | - |
|---|
| 970 | | - return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor)); |
|---|
| 954 | + ret = sensor->data; |
|---|
| 955 | + else |
|---|
| 956 | + ret = snprintf(buf, PAGE_SIZE, "%lld\n", pmbus_reg2data(data, sensor)); |
|---|
| 957 | + mutex_unlock(&data->update_lock); |
|---|
| 958 | + return ret; |
|---|
| 971 | 959 | } |
|---|
| 972 | 960 | |
|---|
| 973 | 961 | static ssize_t pmbus_set_sensor(struct device *dev, |
|---|
| .. | .. |
|---|
| 978 | 966 | struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 979 | 967 | struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); |
|---|
| 980 | 968 | ssize_t rv = count; |
|---|
| 981 | | - long val = 0; |
|---|
| 969 | + s64 val; |
|---|
| 982 | 970 | int ret; |
|---|
| 983 | 971 | u16 regval; |
|---|
| 984 | 972 | |
|---|
| 985 | | - if (kstrtol(buf, 10, &val) < 0) |
|---|
| 973 | + if (kstrtos64(buf, 10, &val) < 0) |
|---|
| 986 | 974 | return -EINVAL; |
|---|
| 987 | 975 | |
|---|
| 988 | 976 | mutex_lock(&data->update_lock); |
|---|
| .. | .. |
|---|
| 1008 | 996 | { |
|---|
| 1009 | 997 | if (data->num_attributes >= data->max_attributes - 1) { |
|---|
| 1010 | 998 | int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; |
|---|
| 1011 | | - void *new_attrs = krealloc(data->group.attrs, |
|---|
| 1012 | | - new_max_attrs * sizeof(void *), |
|---|
| 1013 | | - GFP_KERNEL); |
|---|
| 999 | + void *new_attrs = devm_krealloc(data->dev, data->group.attrs, |
|---|
| 1000 | + new_max_attrs * sizeof(void *), |
|---|
| 1001 | + GFP_KERNEL); |
|---|
| 1014 | 1002 | if (!new_attrs) |
|---|
| 1015 | 1003 | return -ENOMEM; |
|---|
| 1016 | 1004 | data->group.attrs = new_attrs; |
|---|
| .. | .. |
|---|
| 1058 | 1046 | const char *name, const char *type, int seq, |
|---|
| 1059 | 1047 | struct pmbus_sensor *s1, |
|---|
| 1060 | 1048 | struct pmbus_sensor *s2, |
|---|
| 1061 | | - u16 reg, u16 mask) |
|---|
| 1049 | + u8 page, u16 reg, u16 mask) |
|---|
| 1062 | 1050 | { |
|---|
| 1063 | 1051 | struct pmbus_boolean *boolean; |
|---|
| 1064 | 1052 | struct sensor_device_attribute *a; |
|---|
| 1053 | + |
|---|
| 1054 | + if (WARN((s1 && !s2) || (!s1 && s2), "Bad s1/s2 parameters\n")) |
|---|
| 1055 | + return -EINVAL; |
|---|
| 1065 | 1056 | |
|---|
| 1066 | 1057 | boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); |
|---|
| 1067 | 1058 | if (!boolean) |
|---|
| .. | .. |
|---|
| 1073 | 1064 | name, seq, type); |
|---|
| 1074 | 1065 | boolean->s1 = s1; |
|---|
| 1075 | 1066 | boolean->s2 = s2; |
|---|
| 1076 | | - pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL, |
|---|
| 1077 | | - (reg << 16) | mask); |
|---|
| 1067 | + pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL, |
|---|
| 1068 | + pb_reg_to_index(page, reg, mask)); |
|---|
| 1078 | 1069 | |
|---|
| 1079 | 1070 | return pmbus_add_attribute(data, &a->dev_attr.attr); |
|---|
| 1080 | 1071 | } |
|---|
| 1081 | 1072 | |
|---|
| 1082 | 1073 | static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, |
|---|
| 1083 | 1074 | const char *name, const char *type, |
|---|
| 1084 | | - int seq, int page, int reg, |
|---|
| 1075 | + int seq, int page, int phase, |
|---|
| 1076 | + int reg, |
|---|
| 1085 | 1077 | enum pmbus_sensor_classes class, |
|---|
| 1086 | 1078 | bool update, bool readonly, |
|---|
| 1087 | 1079 | bool convert) |
|---|
| .. | .. |
|---|
| 1101 | 1093 | snprintf(sensor->name, sizeof(sensor->name), "%s%d", |
|---|
| 1102 | 1094 | name, seq); |
|---|
| 1103 | 1095 | |
|---|
| 1096 | + if (data->flags & PMBUS_WRITE_PROTECTED) |
|---|
| 1097 | + readonly = true; |
|---|
| 1098 | + |
|---|
| 1104 | 1099 | sensor->page = page; |
|---|
| 1100 | + sensor->phase = phase; |
|---|
| 1105 | 1101 | sensor->reg = reg; |
|---|
| 1106 | 1102 | sensor->class = class; |
|---|
| 1107 | 1103 | sensor->update = update; |
|---|
| 1108 | 1104 | sensor->convert = convert; |
|---|
| 1105 | + sensor->data = -ENODATA; |
|---|
| 1109 | 1106 | pmbus_dev_attr_init(a, sensor->name, |
|---|
| 1110 | | - readonly ? S_IRUGO : S_IRUGO | S_IWUSR, |
|---|
| 1107 | + readonly ? 0444 : 0644, |
|---|
| 1111 | 1108 | pmbus_show_sensor, pmbus_set_sensor); |
|---|
| 1112 | 1109 | |
|---|
| 1113 | 1110 | if (pmbus_add_attribute(data, &a->attr)) |
|---|
| .. | .. |
|---|
| 1121 | 1118 | |
|---|
| 1122 | 1119 | static int pmbus_add_label(struct pmbus_data *data, |
|---|
| 1123 | 1120 | const char *name, int seq, |
|---|
| 1124 | | - const char *lstring, int index) |
|---|
| 1121 | + const char *lstring, int index, int phase) |
|---|
| 1125 | 1122 | { |
|---|
| 1126 | 1123 | struct pmbus_label *label; |
|---|
| 1127 | 1124 | struct device_attribute *a; |
|---|
| .. | .. |
|---|
| 1133 | 1130 | a = &label->attribute; |
|---|
| 1134 | 1131 | |
|---|
| 1135 | 1132 | snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); |
|---|
| 1136 | | - if (!index) |
|---|
| 1137 | | - strncpy(label->label, lstring, sizeof(label->label) - 1); |
|---|
| 1138 | | - else |
|---|
| 1139 | | - snprintf(label->label, sizeof(label->label), "%s%d", lstring, |
|---|
| 1140 | | - index); |
|---|
| 1133 | + if (!index) { |
|---|
| 1134 | + if (phase == 0xff) |
|---|
| 1135 | + strncpy(label->label, lstring, |
|---|
| 1136 | + sizeof(label->label) - 1); |
|---|
| 1137 | + else |
|---|
| 1138 | + snprintf(label->label, sizeof(label->label), "%s.%d", |
|---|
| 1139 | + lstring, phase); |
|---|
| 1140 | + } else { |
|---|
| 1141 | + if (phase == 0xff) |
|---|
| 1142 | + snprintf(label->label, sizeof(label->label), "%s%d", |
|---|
| 1143 | + lstring, index); |
|---|
| 1144 | + else |
|---|
| 1145 | + snprintf(label->label, sizeof(label->label), "%s%d.%d", |
|---|
| 1146 | + lstring, index, phase); |
|---|
| 1147 | + } |
|---|
| 1141 | 1148 | |
|---|
| 1142 | | - pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL); |
|---|
| 1149 | + pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL); |
|---|
| 1143 | 1150 | return pmbus_add_attribute(data, &a->attr); |
|---|
| 1144 | 1151 | } |
|---|
| 1145 | 1152 | |
|---|
| .. | .. |
|---|
| 1176 | 1183 | bool compare; /* true if compare function needed */ |
|---|
| 1177 | 1184 | u32 func; /* sensor mask */ |
|---|
| 1178 | 1185 | u32 sfunc; /* sensor status mask */ |
|---|
| 1179 | | - int sbase; /* status base register */ |
|---|
| 1186 | + int sreg; /* status register */ |
|---|
| 1180 | 1187 | const struct pmbus_limit_attr *limit;/* limit registers */ |
|---|
| 1181 | 1188 | }; |
|---|
| 1182 | 1189 | |
|---|
| .. | .. |
|---|
| 1202 | 1209 | for (i = 0; i < nlimit; i++) { |
|---|
| 1203 | 1210 | if (pmbus_check_word_register(client, page, l->reg)) { |
|---|
| 1204 | 1211 | curr = pmbus_add_sensor(data, name, l->attr, index, |
|---|
| 1205 | | - page, l->reg, attr->class, |
|---|
| 1212 | + page, 0xff, l->reg, attr->class, |
|---|
| 1206 | 1213 | attr->update || l->update, |
|---|
| 1207 | 1214 | false, true); |
|---|
| 1208 | 1215 | if (!curr) |
|---|
| .. | .. |
|---|
| 1214 | 1221 | : NULL, |
|---|
| 1215 | 1222 | attr->compare ? l->low ? base : curr |
|---|
| 1216 | 1223 | : NULL, |
|---|
| 1217 | | - attr->sbase + page, l->sbit); |
|---|
| 1224 | + page, attr->sreg, l->sbit); |
|---|
| 1218 | 1225 | if (ret) |
|---|
| 1219 | 1226 | return ret; |
|---|
| 1220 | 1227 | have_alarm = 1; |
|---|
| .. | .. |
|---|
| 1229 | 1236 | struct pmbus_data *data, |
|---|
| 1230 | 1237 | const struct pmbus_driver_info *info, |
|---|
| 1231 | 1238 | const char *name, |
|---|
| 1232 | | - int index, int page, |
|---|
| 1239 | + int index, int page, int phase, |
|---|
| 1233 | 1240 | const struct pmbus_sensor_attr *attr, |
|---|
| 1234 | 1241 | bool paged) |
|---|
| 1235 | 1242 | { |
|---|
| .. | .. |
|---|
| 1239 | 1246 | |
|---|
| 1240 | 1247 | if (attr->label) { |
|---|
| 1241 | 1248 | ret = pmbus_add_label(data, name, index, attr->label, |
|---|
| 1242 | | - paged ? page + 1 : 0); |
|---|
| 1249 | + paged ? page + 1 : 0, phase); |
|---|
| 1243 | 1250 | if (ret) |
|---|
| 1244 | 1251 | return ret; |
|---|
| 1245 | 1252 | } |
|---|
| 1246 | | - base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, |
|---|
| 1247 | | - attr->class, true, true, true); |
|---|
| 1253 | + base = pmbus_add_sensor(data, name, "input", index, page, phase, |
|---|
| 1254 | + attr->reg, attr->class, true, true, true); |
|---|
| 1248 | 1255 | if (!base) |
|---|
| 1249 | 1256 | return -ENOMEM; |
|---|
| 1250 | | - if (attr->sfunc) { |
|---|
| 1257 | + /* No limit and alarm attributes for phase specific sensors */ |
|---|
| 1258 | + if (attr->sfunc && phase == 0xff) { |
|---|
| 1251 | 1259 | ret = pmbus_add_limit_attrs(client, data, info, name, |
|---|
| 1252 | 1260 | index, page, base, attr); |
|---|
| 1253 | 1261 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 1263 | 1271 | pmbus_check_status_register(client, page)) { |
|---|
| 1264 | 1272 | ret = pmbus_add_boolean(data, name, "alarm", index, |
|---|
| 1265 | 1273 | NULL, NULL, |
|---|
| 1266 | | - PB_STATUS_BASE + page, |
|---|
| 1274 | + page, PMBUS_STATUS_WORD, |
|---|
| 1267 | 1275 | attr->gbit); |
|---|
| 1268 | 1276 | if (ret) |
|---|
| 1269 | 1277 | return ret; |
|---|
| .. | .. |
|---|
| 1317 | 1325 | continue; |
|---|
| 1318 | 1326 | ret = pmbus_add_sensor_attrs_one(client, data, info, |
|---|
| 1319 | 1327 | name, index, page, |
|---|
| 1320 | | - attrs, paged); |
|---|
| 1328 | + 0xff, attrs, paged); |
|---|
| 1321 | 1329 | if (ret) |
|---|
| 1322 | 1330 | return ret; |
|---|
| 1323 | 1331 | index++; |
|---|
| 1332 | + if (info->phases[page]) { |
|---|
| 1333 | + int phase; |
|---|
| 1334 | + |
|---|
| 1335 | + for (phase = 0; phase < info->phases[page]; |
|---|
| 1336 | + phase++) { |
|---|
| 1337 | + if (!(info->pfunc[phase] & attrs->func)) |
|---|
| 1338 | + continue; |
|---|
| 1339 | + ret = pmbus_add_sensor_attrs_one(client, |
|---|
| 1340 | + data, info, name, index, page, |
|---|
| 1341 | + phase, attrs, paged); |
|---|
| 1342 | + if (ret) |
|---|
| 1343 | + return ret; |
|---|
| 1344 | + index++; |
|---|
| 1345 | + } |
|---|
| 1346 | + } |
|---|
| 1324 | 1347 | } |
|---|
| 1325 | 1348 | attrs++; |
|---|
| 1326 | 1349 | } |
|---|
| .. | .. |
|---|
| 1337 | 1360 | .reg = PMBUS_VIN_UV_FAULT_LIMIT, |
|---|
| 1338 | 1361 | .attr = "lcrit", |
|---|
| 1339 | 1362 | .alarm = "lcrit_alarm", |
|---|
| 1340 | | - .sbit = PB_VOLTAGE_UV_FAULT, |
|---|
| 1363 | + .sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF, |
|---|
| 1341 | 1364 | }, { |
|---|
| 1342 | 1365 | .reg = PMBUS_VIN_OV_WARN_LIMIT, |
|---|
| 1343 | 1366 | .attr = "max", |
|---|
| .. | .. |
|---|
| 1363 | 1386 | }, { |
|---|
| 1364 | 1387 | .reg = PMBUS_VIRT_RESET_VIN_HISTORY, |
|---|
| 1365 | 1388 | .attr = "reset_history", |
|---|
| 1389 | + }, { |
|---|
| 1390 | + .reg = PMBUS_MFR_VIN_MIN, |
|---|
| 1391 | + .attr = "rated_min", |
|---|
| 1392 | + }, { |
|---|
| 1393 | + .reg = PMBUS_MFR_VIN_MAX, |
|---|
| 1394 | + .attr = "rated_max", |
|---|
| 1366 | 1395 | }, |
|---|
| 1367 | 1396 | }; |
|---|
| 1368 | 1397 | |
|---|
| .. | .. |
|---|
| 1426 | 1455 | }, { |
|---|
| 1427 | 1456 | .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, |
|---|
| 1428 | 1457 | .attr = "reset_history", |
|---|
| 1429 | | - } |
|---|
| 1458 | + }, { |
|---|
| 1459 | + .reg = PMBUS_MFR_VOUT_MIN, |
|---|
| 1460 | + .attr = "rated_min", |
|---|
| 1461 | + }, { |
|---|
| 1462 | + .reg = PMBUS_MFR_VOUT_MAX, |
|---|
| 1463 | + .attr = "rated_max", |
|---|
| 1464 | + }, |
|---|
| 1430 | 1465 | }; |
|---|
| 1431 | 1466 | |
|---|
| 1432 | 1467 | static const struct pmbus_sensor_attr voltage_attributes[] = { |
|---|
| .. | .. |
|---|
| 1436 | 1471 | .label = "vin", |
|---|
| 1437 | 1472 | .func = PMBUS_HAVE_VIN, |
|---|
| 1438 | 1473 | .sfunc = PMBUS_HAVE_STATUS_INPUT, |
|---|
| 1439 | | - .sbase = PB_STATUS_INPUT_BASE, |
|---|
| 1474 | + .sreg = PMBUS_STATUS_INPUT, |
|---|
| 1440 | 1475 | .gbit = PB_STATUS_VIN_UV, |
|---|
| 1441 | 1476 | .limit = vin_limit_attrs, |
|---|
| 1442 | 1477 | .nlimit = ARRAY_SIZE(vin_limit_attrs), |
|---|
| .. | .. |
|---|
| 1446 | 1481 | .label = "vmon", |
|---|
| 1447 | 1482 | .func = PMBUS_HAVE_VMON, |
|---|
| 1448 | 1483 | .sfunc = PMBUS_HAVE_STATUS_VMON, |
|---|
| 1449 | | - .sbase = PB_STATUS_VMON_BASE, |
|---|
| 1484 | + .sreg = PMBUS_VIRT_STATUS_VMON, |
|---|
| 1450 | 1485 | .limit = vmon_limit_attrs, |
|---|
| 1451 | 1486 | .nlimit = ARRAY_SIZE(vmon_limit_attrs), |
|---|
| 1452 | 1487 | }, { |
|---|
| .. | .. |
|---|
| 1461 | 1496 | .paged = true, |
|---|
| 1462 | 1497 | .func = PMBUS_HAVE_VOUT, |
|---|
| 1463 | 1498 | .sfunc = PMBUS_HAVE_STATUS_VOUT, |
|---|
| 1464 | | - .sbase = PB_STATUS_VOUT_BASE, |
|---|
| 1499 | + .sreg = PMBUS_STATUS_VOUT, |
|---|
| 1465 | 1500 | .gbit = PB_STATUS_VOUT_OV, |
|---|
| 1466 | 1501 | .limit = vout_limit_attrs, |
|---|
| 1467 | 1502 | .nlimit = ARRAY_SIZE(vout_limit_attrs), |
|---|
| .. | .. |
|---|
| 1496 | 1531 | }, { |
|---|
| 1497 | 1532 | .reg = PMBUS_VIRT_RESET_IIN_HISTORY, |
|---|
| 1498 | 1533 | .attr = "reset_history", |
|---|
| 1499 | | - } |
|---|
| 1534 | + }, { |
|---|
| 1535 | + .reg = PMBUS_MFR_IIN_MAX, |
|---|
| 1536 | + .attr = "rated_max", |
|---|
| 1537 | + }, |
|---|
| 1500 | 1538 | }; |
|---|
| 1501 | 1539 | |
|---|
| 1502 | 1540 | static const struct pmbus_limit_attr iout_limit_attrs[] = { |
|---|
| .. | .. |
|---|
| 1530 | 1568 | }, { |
|---|
| 1531 | 1569 | .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, |
|---|
| 1532 | 1570 | .attr = "reset_history", |
|---|
| 1533 | | - } |
|---|
| 1571 | + }, { |
|---|
| 1572 | + .reg = PMBUS_MFR_IOUT_MAX, |
|---|
| 1573 | + .attr = "rated_max", |
|---|
| 1574 | + }, |
|---|
| 1534 | 1575 | }; |
|---|
| 1535 | 1576 | |
|---|
| 1536 | 1577 | static const struct pmbus_sensor_attr current_attributes[] = { |
|---|
| .. | .. |
|---|
| 1540 | 1581 | .label = "iin", |
|---|
| 1541 | 1582 | .func = PMBUS_HAVE_IIN, |
|---|
| 1542 | 1583 | .sfunc = PMBUS_HAVE_STATUS_INPUT, |
|---|
| 1543 | | - .sbase = PB_STATUS_INPUT_BASE, |
|---|
| 1584 | + .sreg = PMBUS_STATUS_INPUT, |
|---|
| 1544 | 1585 | .gbit = PB_STATUS_INPUT, |
|---|
| 1545 | 1586 | .limit = iin_limit_attrs, |
|---|
| 1546 | 1587 | .nlimit = ARRAY_SIZE(iin_limit_attrs), |
|---|
| .. | .. |
|---|
| 1551 | 1592 | .paged = true, |
|---|
| 1552 | 1593 | .func = PMBUS_HAVE_IOUT, |
|---|
| 1553 | 1594 | .sfunc = PMBUS_HAVE_STATUS_IOUT, |
|---|
| 1554 | | - .sbase = PB_STATUS_IOUT_BASE, |
|---|
| 1595 | + .sreg = PMBUS_STATUS_IOUT, |
|---|
| 1555 | 1596 | .gbit = PB_STATUS_IOUT_OC, |
|---|
| 1556 | 1597 | .limit = iout_limit_attrs, |
|---|
| 1557 | 1598 | .nlimit = ARRAY_SIZE(iout_limit_attrs), |
|---|
| .. | .. |
|---|
| 1581 | 1622 | }, { |
|---|
| 1582 | 1623 | .reg = PMBUS_VIRT_RESET_PIN_HISTORY, |
|---|
| 1583 | 1624 | .attr = "reset_history", |
|---|
| 1584 | | - } |
|---|
| 1625 | + }, { |
|---|
| 1626 | + .reg = PMBUS_MFR_PIN_MAX, |
|---|
| 1627 | + .attr = "rated_max", |
|---|
| 1628 | + }, |
|---|
| 1585 | 1629 | }; |
|---|
| 1586 | 1630 | |
|---|
| 1587 | 1631 | static const struct pmbus_limit_attr pout_limit_attrs[] = { |
|---|
| .. | .. |
|---|
| 1615 | 1659 | }, { |
|---|
| 1616 | 1660 | .reg = PMBUS_VIRT_RESET_POUT_HISTORY, |
|---|
| 1617 | 1661 | .attr = "reset_history", |
|---|
| 1618 | | - } |
|---|
| 1662 | + }, { |
|---|
| 1663 | + .reg = PMBUS_MFR_POUT_MAX, |
|---|
| 1664 | + .attr = "rated_max", |
|---|
| 1665 | + }, |
|---|
| 1619 | 1666 | }; |
|---|
| 1620 | 1667 | |
|---|
| 1621 | 1668 | static const struct pmbus_sensor_attr power_attributes[] = { |
|---|
| .. | .. |
|---|
| 1625 | 1672 | .label = "pin", |
|---|
| 1626 | 1673 | .func = PMBUS_HAVE_PIN, |
|---|
| 1627 | 1674 | .sfunc = PMBUS_HAVE_STATUS_INPUT, |
|---|
| 1628 | | - .sbase = PB_STATUS_INPUT_BASE, |
|---|
| 1675 | + .sreg = PMBUS_STATUS_INPUT, |
|---|
| 1629 | 1676 | .gbit = PB_STATUS_INPUT, |
|---|
| 1630 | 1677 | .limit = pin_limit_attrs, |
|---|
| 1631 | 1678 | .nlimit = ARRAY_SIZE(pin_limit_attrs), |
|---|
| .. | .. |
|---|
| 1636 | 1683 | .paged = true, |
|---|
| 1637 | 1684 | .func = PMBUS_HAVE_POUT, |
|---|
| 1638 | 1685 | .sfunc = PMBUS_HAVE_STATUS_IOUT, |
|---|
| 1639 | | - .sbase = PB_STATUS_IOUT_BASE, |
|---|
| 1686 | + .sreg = PMBUS_STATUS_IOUT, |
|---|
| 1640 | 1687 | .limit = pout_limit_attrs, |
|---|
| 1641 | 1688 | .nlimit = ARRAY_SIZE(pout_limit_attrs), |
|---|
| 1642 | 1689 | } |
|---|
| .. | .. |
|---|
| 1679 | 1726 | }, { |
|---|
| 1680 | 1727 | .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, |
|---|
| 1681 | 1728 | .attr = "reset_history", |
|---|
| 1682 | | - } |
|---|
| 1729 | + }, { |
|---|
| 1730 | + .reg = PMBUS_MFR_MAX_TEMP_1, |
|---|
| 1731 | + .attr = "rated_max", |
|---|
| 1732 | + }, |
|---|
| 1683 | 1733 | }; |
|---|
| 1684 | 1734 | |
|---|
| 1685 | 1735 | static const struct pmbus_limit_attr temp_limit_attrs2[] = { |
|---|
| .. | .. |
|---|
| 1717 | 1767 | }, { |
|---|
| 1718 | 1768 | .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, |
|---|
| 1719 | 1769 | .attr = "reset_history", |
|---|
| 1720 | | - } |
|---|
| 1770 | + }, { |
|---|
| 1771 | + .reg = PMBUS_MFR_MAX_TEMP_2, |
|---|
| 1772 | + .attr = "rated_max", |
|---|
| 1773 | + }, |
|---|
| 1721 | 1774 | }; |
|---|
| 1722 | 1775 | |
|---|
| 1723 | 1776 | static const struct pmbus_limit_attr temp_limit_attrs3[] = { |
|---|
| .. | .. |
|---|
| 1743 | 1796 | .attr = "crit", |
|---|
| 1744 | 1797 | .alarm = "crit_alarm", |
|---|
| 1745 | 1798 | .sbit = PB_TEMP_OT_FAULT, |
|---|
| 1746 | | - } |
|---|
| 1799 | + }, { |
|---|
| 1800 | + .reg = PMBUS_MFR_MAX_TEMP_3, |
|---|
| 1801 | + .attr = "rated_max", |
|---|
| 1802 | + }, |
|---|
| 1747 | 1803 | }; |
|---|
| 1748 | 1804 | |
|---|
| 1749 | 1805 | static const struct pmbus_sensor_attr temp_attributes[] = { |
|---|
| .. | .. |
|---|
| 1755 | 1811 | .compare = true, |
|---|
| 1756 | 1812 | .func = PMBUS_HAVE_TEMP, |
|---|
| 1757 | 1813 | .sfunc = PMBUS_HAVE_STATUS_TEMP, |
|---|
| 1758 | | - .sbase = PB_STATUS_TEMP_BASE, |
|---|
| 1814 | + .sreg = PMBUS_STATUS_TEMPERATURE, |
|---|
| 1759 | 1815 | .gbit = PB_STATUS_TEMPERATURE, |
|---|
| 1760 | 1816 | .limit = temp_limit_attrs, |
|---|
| 1761 | 1817 | .nlimit = ARRAY_SIZE(temp_limit_attrs), |
|---|
| .. | .. |
|---|
| 1767 | 1823 | .compare = true, |
|---|
| 1768 | 1824 | .func = PMBUS_HAVE_TEMP2, |
|---|
| 1769 | 1825 | .sfunc = PMBUS_HAVE_STATUS_TEMP, |
|---|
| 1770 | | - .sbase = PB_STATUS_TEMP_BASE, |
|---|
| 1826 | + .sreg = PMBUS_STATUS_TEMPERATURE, |
|---|
| 1771 | 1827 | .gbit = PB_STATUS_TEMPERATURE, |
|---|
| 1772 | 1828 | .limit = temp_limit_attrs2, |
|---|
| 1773 | 1829 | .nlimit = ARRAY_SIZE(temp_limit_attrs2), |
|---|
| .. | .. |
|---|
| 1779 | 1835 | .compare = true, |
|---|
| 1780 | 1836 | .func = PMBUS_HAVE_TEMP3, |
|---|
| 1781 | 1837 | .sfunc = PMBUS_HAVE_STATUS_TEMP, |
|---|
| 1782 | | - .sbase = PB_STATUS_TEMP_BASE, |
|---|
| 1838 | + .sreg = PMBUS_STATUS_TEMPERATURE, |
|---|
| 1783 | 1839 | .gbit = PB_STATUS_TEMPERATURE, |
|---|
| 1784 | 1840 | .limit = temp_limit_attrs3, |
|---|
| 1785 | 1841 | .nlimit = ARRAY_SIZE(temp_limit_attrs3), |
|---|
| .. | .. |
|---|
| 1824 | 1880 | struct pmbus_sensor *sensor; |
|---|
| 1825 | 1881 | |
|---|
| 1826 | 1882 | sensor = pmbus_add_sensor(data, "fan", "target", index, page, |
|---|
| 1827 | | - PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, |
|---|
| 1883 | + 0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, |
|---|
| 1828 | 1884 | false, false, true); |
|---|
| 1829 | 1885 | |
|---|
| 1830 | 1886 | if (!sensor) |
|---|
| .. | .. |
|---|
| 1835 | 1891 | return 0; |
|---|
| 1836 | 1892 | |
|---|
| 1837 | 1893 | sensor = pmbus_add_sensor(data, "pwm", NULL, index, page, |
|---|
| 1838 | | - PMBUS_VIRT_PWM_1 + id, PSC_PWM, |
|---|
| 1894 | + 0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM, |
|---|
| 1839 | 1895 | false, false, true); |
|---|
| 1840 | 1896 | |
|---|
| 1841 | 1897 | if (!sensor) |
|---|
| 1842 | 1898 | return -ENOMEM; |
|---|
| 1843 | 1899 | |
|---|
| 1844 | 1900 | sensor = pmbus_add_sensor(data, "pwm", "enable", index, page, |
|---|
| 1845 | | - PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, |
|---|
| 1901 | + 0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, |
|---|
| 1846 | 1902 | true, false, false); |
|---|
| 1847 | 1903 | |
|---|
| 1848 | 1904 | if (!sensor) |
|---|
| .. | .. |
|---|
| 1884 | 1940 | continue; |
|---|
| 1885 | 1941 | |
|---|
| 1886 | 1942 | if (pmbus_add_sensor(data, "fan", "input", index, |
|---|
| 1887 | | - page, pmbus_fan_registers[f], |
|---|
| 1943 | + page, 0xff, pmbus_fan_registers[f], |
|---|
| 1888 | 1944 | PSC_FAN, true, true, true) == NULL) |
|---|
| 1889 | 1945 | return -ENOMEM; |
|---|
| 1890 | 1946 | |
|---|
| .. | .. |
|---|
| 1904 | 1960 | if ((info->func[page] & pmbus_fan_status_flags[f]) && |
|---|
| 1905 | 1961 | pmbus_check_byte_register(client, |
|---|
| 1906 | 1962 | page, pmbus_fan_status_registers[f])) { |
|---|
| 1907 | | - int base; |
|---|
| 1963 | + int reg; |
|---|
| 1908 | 1964 | |
|---|
| 1909 | 1965 | if (f > 1) /* fan 3, 4 */ |
|---|
| 1910 | | - base = PB_STATUS_FAN34_BASE + page; |
|---|
| 1966 | + reg = PMBUS_STATUS_FAN_34; |
|---|
| 1911 | 1967 | else |
|---|
| 1912 | | - base = PB_STATUS_FAN_BASE + page; |
|---|
| 1968 | + reg = PMBUS_STATUS_FAN_12; |
|---|
| 1913 | 1969 | ret = pmbus_add_boolean(data, "fan", |
|---|
| 1914 | | - "alarm", index, NULL, NULL, base, |
|---|
| 1970 | + "alarm", index, NULL, NULL, page, reg, |
|---|
| 1915 | 1971 | PB_FAN_FAN1_WARNING >> (f & 1)); |
|---|
| 1916 | 1972 | if (ret) |
|---|
| 1917 | 1973 | return ret; |
|---|
| 1918 | 1974 | ret = pmbus_add_boolean(data, "fan", |
|---|
| 1919 | | - "fault", index, NULL, NULL, base, |
|---|
| 1975 | + "fault", index, NULL, NULL, page, reg, |
|---|
| 1920 | 1976 | PB_FAN_FAN1_FAULT >> (f & 1)); |
|---|
| 1921 | 1977 | if (ret) |
|---|
| 1922 | 1978 | return ret; |
|---|
| .. | .. |
|---|
| 1924 | 1980 | index++; |
|---|
| 1925 | 1981 | } |
|---|
| 1926 | 1982 | } |
|---|
| 1983 | + return 0; |
|---|
| 1984 | +} |
|---|
| 1985 | + |
|---|
| 1986 | +struct pmbus_samples_attr { |
|---|
| 1987 | + int reg; |
|---|
| 1988 | + char *name; |
|---|
| 1989 | +}; |
|---|
| 1990 | + |
|---|
| 1991 | +struct pmbus_samples_reg { |
|---|
| 1992 | + int page; |
|---|
| 1993 | + struct pmbus_samples_attr *attr; |
|---|
| 1994 | + struct device_attribute dev_attr; |
|---|
| 1995 | +}; |
|---|
| 1996 | + |
|---|
| 1997 | +static struct pmbus_samples_attr pmbus_samples_registers[] = { |
|---|
| 1998 | + { |
|---|
| 1999 | + .reg = PMBUS_VIRT_SAMPLES, |
|---|
| 2000 | + .name = "samples", |
|---|
| 2001 | + }, { |
|---|
| 2002 | + .reg = PMBUS_VIRT_IN_SAMPLES, |
|---|
| 2003 | + .name = "in_samples", |
|---|
| 2004 | + }, { |
|---|
| 2005 | + .reg = PMBUS_VIRT_CURR_SAMPLES, |
|---|
| 2006 | + .name = "curr_samples", |
|---|
| 2007 | + }, { |
|---|
| 2008 | + .reg = PMBUS_VIRT_POWER_SAMPLES, |
|---|
| 2009 | + .name = "power_samples", |
|---|
| 2010 | + }, { |
|---|
| 2011 | + .reg = PMBUS_VIRT_TEMP_SAMPLES, |
|---|
| 2012 | + .name = "temp_samples", |
|---|
| 2013 | + } |
|---|
| 2014 | +}; |
|---|
| 2015 | + |
|---|
| 2016 | +#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr) |
|---|
| 2017 | + |
|---|
| 2018 | +static ssize_t pmbus_show_samples(struct device *dev, |
|---|
| 2019 | + struct device_attribute *devattr, char *buf) |
|---|
| 2020 | +{ |
|---|
| 2021 | + int val; |
|---|
| 2022 | + struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 2023 | + struct pmbus_samples_reg *reg = to_samples_reg(devattr); |
|---|
| 2024 | + struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 2025 | + |
|---|
| 2026 | + mutex_lock(&data->update_lock); |
|---|
| 2027 | + val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg); |
|---|
| 2028 | + mutex_unlock(&data->update_lock); |
|---|
| 2029 | + if (val < 0) |
|---|
| 2030 | + return val; |
|---|
| 2031 | + |
|---|
| 2032 | + return snprintf(buf, PAGE_SIZE, "%d\n", val); |
|---|
| 2033 | +} |
|---|
| 2034 | + |
|---|
| 2035 | +static ssize_t pmbus_set_samples(struct device *dev, |
|---|
| 2036 | + struct device_attribute *devattr, |
|---|
| 2037 | + const char *buf, size_t count) |
|---|
| 2038 | +{ |
|---|
| 2039 | + int ret; |
|---|
| 2040 | + long val; |
|---|
| 2041 | + struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 2042 | + struct pmbus_samples_reg *reg = to_samples_reg(devattr); |
|---|
| 2043 | + struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 2044 | + |
|---|
| 2045 | + if (kstrtol(buf, 0, &val) < 0) |
|---|
| 2046 | + return -EINVAL; |
|---|
| 2047 | + |
|---|
| 2048 | + mutex_lock(&data->update_lock); |
|---|
| 2049 | + ret = _pmbus_write_word_data(client, reg->page, reg->attr->reg, val); |
|---|
| 2050 | + mutex_unlock(&data->update_lock); |
|---|
| 2051 | + |
|---|
| 2052 | + return ret ? : count; |
|---|
| 2053 | +} |
|---|
| 2054 | + |
|---|
| 2055 | +static int pmbus_add_samples_attr(struct pmbus_data *data, int page, |
|---|
| 2056 | + struct pmbus_samples_attr *attr) |
|---|
| 2057 | +{ |
|---|
| 2058 | + struct pmbus_samples_reg *reg; |
|---|
| 2059 | + |
|---|
| 2060 | + reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL); |
|---|
| 2061 | + if (!reg) |
|---|
| 2062 | + return -ENOMEM; |
|---|
| 2063 | + |
|---|
| 2064 | + reg->attr = attr; |
|---|
| 2065 | + reg->page = page; |
|---|
| 2066 | + |
|---|
| 2067 | + pmbus_dev_attr_init(®->dev_attr, attr->name, 0644, |
|---|
| 2068 | + pmbus_show_samples, pmbus_set_samples); |
|---|
| 2069 | + |
|---|
| 2070 | + return pmbus_add_attribute(data, ®->dev_attr.attr); |
|---|
| 2071 | +} |
|---|
| 2072 | + |
|---|
| 2073 | +static int pmbus_add_samples_attributes(struct i2c_client *client, |
|---|
| 2074 | + struct pmbus_data *data) |
|---|
| 2075 | +{ |
|---|
| 2076 | + const struct pmbus_driver_info *info = data->info; |
|---|
| 2077 | + int s; |
|---|
| 2078 | + |
|---|
| 2079 | + if (!(info->func[0] & PMBUS_HAVE_SAMPLES)) |
|---|
| 2080 | + return 0; |
|---|
| 2081 | + |
|---|
| 2082 | + for (s = 0; s < ARRAY_SIZE(pmbus_samples_registers); s++) { |
|---|
| 2083 | + struct pmbus_samples_attr *attr; |
|---|
| 2084 | + int ret; |
|---|
| 2085 | + |
|---|
| 2086 | + attr = &pmbus_samples_registers[s]; |
|---|
| 2087 | + if (!pmbus_check_word_register(client, 0, attr->reg)) |
|---|
| 2088 | + continue; |
|---|
| 2089 | + |
|---|
| 2090 | + ret = pmbus_add_samples_attr(data, 0, attr); |
|---|
| 2091 | + if (ret) |
|---|
| 2092 | + return ret; |
|---|
| 2093 | + } |
|---|
| 2094 | + |
|---|
| 1927 | 2095 | return 0; |
|---|
| 1928 | 2096 | } |
|---|
| 1929 | 2097 | |
|---|
| .. | .. |
|---|
| 1958 | 2126 | |
|---|
| 1959 | 2127 | /* Fans */ |
|---|
| 1960 | 2128 | ret = pmbus_add_fan_attributes(client, data); |
|---|
| 2129 | + if (ret) |
|---|
| 2130 | + return ret; |
|---|
| 2131 | + |
|---|
| 2132 | + ret = pmbus_add_samples_attributes(client, data); |
|---|
| 1961 | 2133 | return ret; |
|---|
| 1962 | 2134 | } |
|---|
| 1963 | 2135 | |
|---|
| .. | .. |
|---|
| 2009 | 2181 | |
|---|
| 2010 | 2182 | static int pmbus_read_status_word(struct i2c_client *client, int page) |
|---|
| 2011 | 2183 | { |
|---|
| 2012 | | - return _pmbus_read_word_data(client, page, PMBUS_STATUS_WORD); |
|---|
| 2184 | + return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD); |
|---|
| 2013 | 2185 | } |
|---|
| 2014 | 2186 | |
|---|
| 2015 | 2187 | static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, |
|---|
| .. | .. |
|---|
| 2040 | 2212 | ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); |
|---|
| 2041 | 2213 | if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) |
|---|
| 2042 | 2214 | client->flags |= I2C_CLIENT_PEC; |
|---|
| 2215 | + |
|---|
| 2216 | + /* |
|---|
| 2217 | + * Check if the chip is write protected. If it is, we can not clear |
|---|
| 2218 | + * faults, and we should not try it. Also, in that case, writes into |
|---|
| 2219 | + * limit registers need to be disabled. |
|---|
| 2220 | + */ |
|---|
| 2221 | + ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); |
|---|
| 2222 | + if (ret > 0 && (ret & PB_WP_ANY)) |
|---|
| 2223 | + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; |
|---|
| 2043 | 2224 | |
|---|
| 2044 | 2225 | if (data->info->pages) |
|---|
| 2045 | 2226 | pmbus_clear_faults(client); |
|---|
| .. | .. |
|---|
| 2074 | 2255 | { |
|---|
| 2075 | 2256 | struct device *dev = rdev_get_dev(rdev); |
|---|
| 2076 | 2257 | struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 2258 | + struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 2077 | 2259 | u8 page = rdev_get_id(rdev); |
|---|
| 2078 | 2260 | int ret; |
|---|
| 2079 | 2261 | |
|---|
| 2262 | + mutex_lock(&data->update_lock); |
|---|
| 2080 | 2263 | ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); |
|---|
| 2264 | + mutex_unlock(&data->update_lock); |
|---|
| 2265 | + |
|---|
| 2081 | 2266 | if (ret < 0) |
|---|
| 2082 | 2267 | return ret; |
|---|
| 2083 | 2268 | |
|---|
| .. | .. |
|---|
| 2088 | 2273 | { |
|---|
| 2089 | 2274 | struct device *dev = rdev_get_dev(rdev); |
|---|
| 2090 | 2275 | struct i2c_client *client = to_i2c_client(dev->parent); |
|---|
| 2276 | + struct pmbus_data *data = i2c_get_clientdata(client); |
|---|
| 2091 | 2277 | u8 page = rdev_get_id(rdev); |
|---|
| 2278 | + int ret; |
|---|
| 2092 | 2279 | |
|---|
| 2093 | | - return pmbus_update_byte_data(client, page, PMBUS_OPERATION, |
|---|
| 2094 | | - PB_OPERATION_CONTROL_ON, |
|---|
| 2095 | | - enable ? PB_OPERATION_CONTROL_ON : 0); |
|---|
| 2280 | + mutex_lock(&data->update_lock); |
|---|
| 2281 | + ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION, |
|---|
| 2282 | + PB_OPERATION_CONTROL_ON, |
|---|
| 2283 | + enable ? PB_OPERATION_CONTROL_ON : 0); |
|---|
| 2284 | + mutex_unlock(&data->update_lock); |
|---|
| 2285 | + |
|---|
| 2286 | + return ret; |
|---|
| 2096 | 2287 | } |
|---|
| 2097 | 2288 | |
|---|
| 2098 | 2289 | static int pmbus_regulator_enable(struct regulator_dev *rdev) |
|---|
| .. | .. |
|---|
| 2183 | 2374 | DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, |
|---|
| 2184 | 2375 | NULL, "0x%04llx\n"); |
|---|
| 2185 | 2376 | |
|---|
| 2377 | +static int pmbus_debugfs_get_pec(void *data, u64 *val) |
|---|
| 2378 | +{ |
|---|
| 2379 | + struct i2c_client *client = data; |
|---|
| 2380 | + |
|---|
| 2381 | + *val = !!(client->flags & I2C_CLIENT_PEC); |
|---|
| 2382 | + |
|---|
| 2383 | + return 0; |
|---|
| 2384 | +} |
|---|
| 2385 | + |
|---|
| 2386 | +static int pmbus_debugfs_set_pec(void *data, u64 val) |
|---|
| 2387 | +{ |
|---|
| 2388 | + int rc; |
|---|
| 2389 | + struct i2c_client *client = data; |
|---|
| 2390 | + |
|---|
| 2391 | + if (!val) { |
|---|
| 2392 | + client->flags &= ~I2C_CLIENT_PEC; |
|---|
| 2393 | + return 0; |
|---|
| 2394 | + } |
|---|
| 2395 | + |
|---|
| 2396 | + if (val != 1) |
|---|
| 2397 | + return -EINVAL; |
|---|
| 2398 | + |
|---|
| 2399 | + rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); |
|---|
| 2400 | + if (rc < 0) |
|---|
| 2401 | + return rc; |
|---|
| 2402 | + |
|---|
| 2403 | + if (!(rc & PB_CAPABILITY_ERROR_CHECK)) |
|---|
| 2404 | + return -EOPNOTSUPP; |
|---|
| 2405 | + |
|---|
| 2406 | + client->flags |= I2C_CLIENT_PEC; |
|---|
| 2407 | + |
|---|
| 2408 | + return 0; |
|---|
| 2409 | +} |
|---|
| 2410 | +DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec, |
|---|
| 2411 | + pmbus_debugfs_set_pec, "%llu\n"); |
|---|
| 2412 | + |
|---|
| 2186 | 2413 | static int pmbus_init_debugfs(struct i2c_client *client, |
|---|
| 2187 | 2414 | struct pmbus_data *data) |
|---|
| 2188 | 2415 | { |
|---|
| .. | .. |
|---|
| 2210 | 2437 | GFP_KERNEL); |
|---|
| 2211 | 2438 | if (!entries) |
|---|
| 2212 | 2439 | return -ENOMEM; |
|---|
| 2440 | + |
|---|
| 2441 | + debugfs_create_file("pec", 0664, data->debugfs, client, |
|---|
| 2442 | + &pmbus_debugfs_ops_pec); |
|---|
| 2213 | 2443 | |
|---|
| 2214 | 2444 | for (i = 0; i < data->info->pages; ++i) { |
|---|
| 2215 | 2445 | /* Check accessibility of status register if it's not page 0 */ |
|---|
| .. | .. |
|---|
| 2325 | 2555 | } |
|---|
| 2326 | 2556 | #endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ |
|---|
| 2327 | 2557 | |
|---|
| 2328 | | -int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, |
|---|
| 2329 | | - struct pmbus_driver_info *info) |
|---|
| 2558 | +int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) |
|---|
| 2330 | 2559 | { |
|---|
| 2331 | 2560 | struct device *dev = &client->dev; |
|---|
| 2332 | 2561 | const struct pmbus_platform_data *pdata = dev_get_platdata(dev); |
|---|
| 2333 | 2562 | struct pmbus_data *data; |
|---|
| 2563 | + size_t groups_num = 0; |
|---|
| 2334 | 2564 | int ret; |
|---|
| 2335 | 2565 | |
|---|
| 2336 | 2566 | if (!info) |
|---|
| .. | .. |
|---|
| 2345 | 2575 | if (!data) |
|---|
| 2346 | 2576 | return -ENOMEM; |
|---|
| 2347 | 2577 | |
|---|
| 2578 | + if (info->groups) |
|---|
| 2579 | + while (info->groups[groups_num]) |
|---|
| 2580 | + groups_num++; |
|---|
| 2581 | + |
|---|
| 2582 | + data->groups = devm_kcalloc(dev, groups_num + 2, sizeof(void *), |
|---|
| 2583 | + GFP_KERNEL); |
|---|
| 2584 | + if (!data->groups) |
|---|
| 2585 | + return -ENOMEM; |
|---|
| 2586 | + |
|---|
| 2348 | 2587 | i2c_set_clientdata(client, data); |
|---|
| 2349 | 2588 | mutex_init(&data->update_lock); |
|---|
| 2350 | 2589 | data->dev = dev; |
|---|
| .. | .. |
|---|
| 2352 | 2591 | if (pdata) |
|---|
| 2353 | 2592 | data->flags = pdata->flags; |
|---|
| 2354 | 2593 | data->info = info; |
|---|
| 2594 | + data->currpage = -1; |
|---|
| 2595 | + data->currphase = -1; |
|---|
| 2355 | 2596 | |
|---|
| 2356 | 2597 | ret = pmbus_init_common(client, data, info); |
|---|
| 2357 | 2598 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 2359 | 2600 | |
|---|
| 2360 | 2601 | ret = pmbus_find_attributes(client, data); |
|---|
| 2361 | 2602 | if (ret) |
|---|
| 2362 | | - goto out_kfree; |
|---|
| 2603 | + return ret; |
|---|
| 2363 | 2604 | |
|---|
| 2364 | 2605 | /* |
|---|
| 2365 | 2606 | * If there are no attributes, something is wrong. |
|---|
| .. | .. |
|---|
| 2367 | 2608 | */ |
|---|
| 2368 | 2609 | if (!data->num_attributes) { |
|---|
| 2369 | 2610 | dev_err(dev, "No attributes found\n"); |
|---|
| 2370 | | - ret = -ENODEV; |
|---|
| 2371 | | - goto out_kfree; |
|---|
| 2611 | + return -ENODEV; |
|---|
| 2372 | 2612 | } |
|---|
| 2373 | 2613 | |
|---|
| 2374 | 2614 | data->groups[0] = &data->group; |
|---|
| 2375 | | - data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, |
|---|
| 2376 | | - data, data->groups); |
|---|
| 2615 | + memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); |
|---|
| 2616 | + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, |
|---|
| 2617 | + client->name, data, data->groups); |
|---|
| 2377 | 2618 | if (IS_ERR(data->hwmon_dev)) { |
|---|
| 2378 | | - ret = PTR_ERR(data->hwmon_dev); |
|---|
| 2379 | 2619 | dev_err(dev, "Failed to register hwmon device\n"); |
|---|
| 2380 | | - goto out_kfree; |
|---|
| 2620 | + return PTR_ERR(data->hwmon_dev); |
|---|
| 2381 | 2621 | } |
|---|
| 2382 | 2622 | |
|---|
| 2383 | 2623 | ret = pmbus_regulator_register(data); |
|---|
| 2384 | 2624 | if (ret) |
|---|
| 2385 | | - goto out_unregister; |
|---|
| 2625 | + return ret; |
|---|
| 2386 | 2626 | |
|---|
| 2387 | 2627 | ret = pmbus_init_debugfs(client, data); |
|---|
| 2388 | 2628 | if (ret) |
|---|
| 2389 | 2629 | dev_warn(dev, "Failed to register debugfs\n"); |
|---|
| 2390 | 2630 | |
|---|
| 2391 | 2631 | return 0; |
|---|
| 2392 | | - |
|---|
| 2393 | | -out_unregister: |
|---|
| 2394 | | - hwmon_device_unregister(data->hwmon_dev); |
|---|
| 2395 | | -out_kfree: |
|---|
| 2396 | | - kfree(data->group.attrs); |
|---|
| 2397 | | - return ret; |
|---|
| 2398 | 2632 | } |
|---|
| 2399 | 2633 | EXPORT_SYMBOL_GPL(pmbus_do_probe); |
|---|
| 2400 | 2634 | |
|---|
| .. | .. |
|---|
| 2404 | 2638 | |
|---|
| 2405 | 2639 | debugfs_remove_recursive(data->debugfs); |
|---|
| 2406 | 2640 | |
|---|
| 2407 | | - hwmon_device_unregister(data->hwmon_dev); |
|---|
| 2408 | | - kfree(data->group.attrs); |
|---|
| 2409 | 2641 | return 0; |
|---|
| 2410 | 2642 | } |
|---|
| 2411 | 2643 | EXPORT_SYMBOL_GPL(pmbus_do_remove); |
|---|