diff options
Diffstat (limited to 'drivers/hwmon/pmbus/pmbus_core.c')
-rw-r--r-- | drivers/hwmon/pmbus/pmbus_core.c | 1472 |
1 files changed, 1166 insertions, 306 deletions
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index d9c17feb7b4a..7ec04934747e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -16,10 +16,11 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#include <linux/jiffies.h> #include <linux/pmbus.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/of.h> +#include <linux/thermal.h> #include "pmbus.h" /* @@ -27,21 +28,6 @@ * with each call to krealloc */ #define PMBUS_ATTR_ALLOC_SIZE 32 - -/* - * Index into status register array, per status register group - */ -#define PB_STATUS_BASE 0 -#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) -#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) -#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) -#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) -#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) -#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) -#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) - -#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) - #define PMBUS_NAME_SIZE 24 struct pmbus_sensor { @@ -49,6 +35,7 @@ struct pmbus_sensor { char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ struct device_attribute attribute; u8 page; /* page number */ + u8 phase; /* phase number, 0xff for all phases */ u16 reg; /* register */ enum pmbus_sensor_classes class; /* sensor class */ bool update; /* runtime sensor update needed */ @@ -76,6 +63,21 @@ struct pmbus_label { #define to_pmbus_label(_attr) \ container_of(_attr, struct pmbus_label, attribute) +/* Macros for converting between sensor index and register/page/status mask */ + +#define PB_STATUS_MASK 0xffff +#define PB_REG_SHIFT 16 +#define PB_REG_MASK 0x3ff +#define PB_PAGE_SHIFT 26 +#define PB_PAGE_MASK 0x3f + +#define pb_reg_to_index(page, reg, mask) (((page) << PB_PAGE_SHIFT) | \ + ((reg) << PB_REG_SHIFT) | (mask)) + +#define pb_index_to_page(index) (((index) >> PB_PAGE_SHIFT) & PB_PAGE_MASK) +#define pb_index_to_reg(index) (((index) >> PB_REG_SHIFT) & PB_REG_MASK) +#define pb_index_to_mask(index) ((index) & PB_STATUS_MASK) + struct pmbus_data { struct device *dev; struct device *hwmon_dev; @@ -96,19 +98,15 @@ struct pmbus_data { struct pmbus_sensor *sensors; struct mutex update_lock; - bool valid; - unsigned long last_updated; /* in jiffies */ - - /* - * A single status register covers multiple attributes, - * so we keep them all together. - */ - u16 status[PB_NUM_STATUS_REG]; bool has_status_word; /* device uses STATUS_WORD register */ int (*read_status)(struct i2c_client *client, int page); - u8 currpage; + s16 currpage; /* current page, -1 for unknown/unset */ + s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */ + + int vout_low[PMBUS_PAGES]; /* voltage low margin */ + int vout_high[PMBUS_PAGES]; /* voltage high margin */ }; struct pmbus_debugfs_entry { @@ -141,20 +139,34 @@ static const int pmbus_fan_command_registers[] = { void pmbus_clear_cache(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor; + + for (sensor = data->sensors; sensor; sensor = sensor->next) + sensor->data = -ENODATA; +} +EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS); + +void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor; - data->valid = false; + for (sensor = data->sensors; sensor; sensor = sensor->next) + if (sensor->reg == reg) + sensor->update = update; } -EXPORT_SYMBOL_GPL(pmbus_clear_cache); +EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); -int pmbus_set_page(struct i2c_client *client, int page) +int pmbus_set_page(struct i2c_client *client, int page, int phase) { struct pmbus_data *data = i2c_get_clientdata(client); int rv; - if (page < 0 || page == data->currpage) + if (page < 0) return 0; - if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL)) { + if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && + data->info->pages > 1 && page != data->currpage) { rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); if (rv < 0) return rv; @@ -166,24 +178,32 @@ int pmbus_set_page(struct i2c_client *client, int page) if (rv != page) return -EIO; } - data->currpage = page; + if (data->info->phases[page] && data->currphase != phase && + !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, + phase); + if (rv) + return rv; + } + data->currphase = phase; + return 0; } -EXPORT_SYMBOL_GPL(pmbus_set_page); +EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS); int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_write_byte(client, value); } -EXPORT_SYMBOL_GPL(pmbus_write_byte); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); /* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if @@ -208,13 +228,13 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_write_word_data(client, reg, word); } -EXPORT_SYMBOL_GPL(pmbus_write_word_data); +EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, @@ -261,6 +281,42 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, return pmbus_write_word_data(client, page, reg, word); } +/* + * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte_data) { + status = info->write_byte_data(client, page, reg, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte_data(client, page, reg, value); +} + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); + if (status != -ENODATA) + return status; + } + return pmbus_read_byte_data(client, page, reg); +} + int pmbus_update_fan(struct i2c_client *client, int page, int id, u8 config, u8 mask, u16 command) { @@ -268,14 +324,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, int rv; u8 to; - from = pmbus_read_byte_data(client, page, + from = _pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]); if (from < 0) return from; to = (from & ~mask) | (config & mask); if (to != from) { - rv = pmbus_write_byte_data(client, page, + rv = _pmbus_write_byte_data(client, page, pmbus_fan_config_registers[id], to); if (rv < 0) return rv; @@ -284,19 +340,19 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, return _pmbus_write_word_data(client, page, pmbus_fan_command_registers[id], command); } -EXPORT_SYMBOL_GPL(pmbus_update_fan); +EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS); -int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg) +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, phase); if (rv < 0) return rv; return i2c_smbus_read_word_data(client, reg); } -EXPORT_SYMBOL_GPL(pmbus_read_word_data); +EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) { @@ -320,14 +376,15 @@ static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if * a device specific mapping function exists and calls it if necessary. */ -static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +static int _pmbus_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; int status; if (info->read_word_data) { - status = info->read_word_data(client, page, reg); + status = info->read_word_data(client, page, phase, reg); if (status != -ENODATA) return status; } @@ -335,32 +392,38 @@ static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) if (reg >= PMBUS_VIRT_BASE) return pmbus_read_virt_reg(client, page, reg); - return pmbus_read_word_data(client, page, reg); + return pmbus_read_word_data(client, page, phase, reg); +} + +/* Same as above, but without phase parameter, for use in check functions */ +static int __pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + return _pmbus_read_word_data(client, page, 0xff, reg); } int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_read_byte_data(client, reg); } -EXPORT_SYMBOL_GPL(pmbus_read_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_write_byte_data(client, reg, value); } -EXPORT_SYMBOL_GPL(pmbus_write_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, u8 mask, u8 value) @@ -368,35 +431,29 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, unsigned int tmp; int rv; - rv = pmbus_read_byte_data(client, page, reg); + rv = _pmbus_read_byte_data(client, page, reg); if (rv < 0) return rv; tmp = (rv & ~mask) | (value & mask); if (tmp != rv) - rv = pmbus_write_byte_data(client, page, reg, tmp); + rv = _pmbus_write_byte_data(client, page, reg, tmp); return rv; } -EXPORT_SYMBOL_GPL(pmbus_update_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); -/* - * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if - * a device specific mapping function exists and calls it if necessary. - */ -static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + char *data_buf) { - struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - int status; + int rv; - if (info->read_byte_data) { - status = info->read_byte_data(client, page, reg); - if (status != -ENODATA) - return status; - } - return pmbus_read_byte_data(client, page, reg); + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_read_block_data(client, reg, data_buf); } static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, @@ -433,14 +490,14 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id, return s->data; } - config = pmbus_read_byte_data(client, page, + config = _pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]); if (config < 0) return config; have_rpm = !!(config & pmbus_fan_rpm_mask[id]); if (want_rpm == have_rpm) - return pmbus_read_word_data(client, page, + return pmbus_read_word_data(client, page, 0xff, pmbus_fan_command_registers[id]); /* Can't sensibly map between RPM and PWM, just return zero */ @@ -452,14 +509,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, { return pmbus_get_fan_rate(client, page, id, mode, false); } -EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS); int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, enum pmbus_fan_mode mode) { return pmbus_get_fan_rate(client, page, id, mode, true); } -EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS); static void pmbus_clear_fault_page(struct i2c_client *client, int page) { @@ -474,7 +531,7 @@ void pmbus_clear_faults(struct i2c_client *client) for (i = 0; i < data->info->pages; i++) pmbus_clear_fault_page(client, i); } -EXPORT_SYMBOL_GPL(pmbus_clear_faults); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS); static int pmbus_check_status_cml(struct i2c_client *client) { @@ -501,6 +558,8 @@ static bool pmbus_check_register(struct i2c_client *client, rv = func(client, page, reg); if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) rv = pmbus_check_status_cml(client); + if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK)) + data->read_status(client, -1); pmbus_clear_fault_page(client, -1); return rv >= 0; } @@ -526,13 +585,29 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); } -EXPORT_SYMBOL_GPL(pmbus_check_byte_register); +EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) { - return pmbus_check_register(client, _pmbus_read_word_data, page, reg); + return pmbus_check_register(client, __pmbus_read_word_data, page, reg); +} +EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); + +static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client, + int page, int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + char data_buf[I2C_SMBUS_BLOCK_MAX + 2]; + + rv = pmbus_read_block_data(client, page, reg, data_buf); + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) + rv = pmbus_check_status_cml(client); + if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK)) + data->read_status(client, -1); + pmbus_clear_fault_page(client, -1); + return rv >= 0; } -EXPORT_SYMBOL_GPL(pmbus_check_word_register); const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) { @@ -540,81 +615,103 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) return data->info; } -EXPORT_SYMBOL_GPL(pmbus_get_driver_info); - -static struct _pmbus_status { - u32 func; - u16 base; - u16 reg; -} pmbus_status[] = { - { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, - { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, - { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, - PMBUS_STATUS_TEMPERATURE }, - { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, - { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, -}; +EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS); -static struct pmbus_data *pmbus_update_device(struct device *dev) +static int pmbus_get_status(struct i2c_client *client, int page, int reg) { - struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - struct pmbus_sensor *sensor; - - mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - int i, j; - - for (i = 0; i < info->pages; i++) { - data->status[PB_STATUS_BASE + i] - = data->read_status(client, i); - for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { - struct _pmbus_status *s = &pmbus_status[j]; - - if (!(info->func[i] & s->func)) - continue; - data->status[s->base + i] - = _pmbus_read_byte_data(client, i, - s->reg); - } - } + int status; - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) - data->status[PB_STATUS_INPUT_BASE] - = _pmbus_read_byte_data(client, 0, - PMBUS_STATUS_INPUT); - - if (info->func[0] & PMBUS_HAVE_STATUS_VMON) - data->status[PB_STATUS_VMON_BASE] - = _pmbus_read_byte_data(client, 0, - PMBUS_VIRT_STATUS_VMON); - - for (sensor = data->sensors; sensor; sensor = sensor->next) { - if (!data->valid || sensor->update) - sensor->data - = _pmbus_read_word_data(client, - sensor->page, - sensor->reg); - } + switch (reg) { + case PMBUS_STATUS_WORD: + status = data->read_status(client, page); + break; + default: + status = _pmbus_read_byte_data(client, page, reg); + break; + } + if (status < 0) pmbus_clear_faults(client); - data->last_updated = jiffies; - data->valid = 1; + return status; +} + +static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sensor *sensor) +{ + if (sensor->data < 0 || sensor->update) + sensor->data = _pmbus_read_word_data(client, sensor->page, + sensor->phase, sensor->reg); +} + +/* + * Convert ieee754 sensor values to milli- or micro-units + * depending on sensor type. + * + * ieee754 data format: + * bit 15: sign + * bit 10..14: exponent + * bit 0..9: mantissa + * exponent=0: + * v=(−1)^signbit * 2^(−14) * 0.significantbits + * exponent=1..30: + * v=(−1)^signbit * 2^(exponent - 15) * 1.significantbits + * exponent=31: + * v=NaN + * + * Add the number mantissa bits into the calculations for simplicity. + * To do that, add '10' to the exponent. By doing that, we can just add + * 0x400 to normal values and get the expected result. + */ +static long pmbus_reg2data_ieee754(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + int exponent; + bool sign; + long val; + + /* only support half precision for now */ + sign = sensor->data & 0x8000; + exponent = (sensor->data >> 10) & 0x1f; + val = sensor->data & 0x3ff; + + if (exponent == 0) { /* subnormal */ + exponent = -(14 + 10); + } else if (exponent == 0x1f) { /* NaN, convert to min/max */ + exponent = 0; + val = 65504; + } else { + exponent -= (15 + 10); /* normal */ + val |= 0x400; } - mutex_unlock(&data->update_lock); - return data; + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000L; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + if (sign) + val = -val; + + return val; } /* * Convert linear sensor values to milli- or micro-units * depending on sensor type. */ -static long pmbus_reg2data_linear(struct pmbus_data *data, - struct pmbus_sensor *sensor) +static s64 pmbus_reg2data_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor) { s16 exponent; s32 mantissa; - long val; + s64 val; if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ exponent = data->exponent[sensor->page]; @@ -628,11 +725,11 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, /* scale result to milli-units for all sensors except fans */ if (sensor->class != PSC_FAN) - val = val * 1000L; + val = val * 1000LL; /* scale result to micro-units for power sensors */ if (sensor->class == PSC_POWER) - val = val * 1000L; + val = val * 1000LL; if (exponent >= 0) val <<= exponent; @@ -646,8 +743,8 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, * Convert direct sensor values to milli- or micro-units * depending on sensor type. */ -static long pmbus_reg2data_direct(struct pmbus_data *data, - struct pmbus_sensor *sensor) +static s64 pmbus_reg2data_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor) { s64 b, val = (s16)sensor->data; s32 m, R; @@ -683,15 +780,15 @@ static long pmbus_reg2data_direct(struct pmbus_data *data, } val = div_s64(val - b, m); - return clamp_val(val, LONG_MIN, LONG_MAX); + return val; } /* * Convert VID sensor values to milli- or micro-units * depending on sensor type. */ -static long pmbus_reg2data_vid(struct pmbus_data *data, - struct pmbus_sensor *sensor) +static s64 pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) { long val = sensor->data; long rv = 0; @@ -721,9 +818,9 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, return rv; } -static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) { - long val; + s64 val; if (!sensor->convert) return sensor->data; @@ -735,6 +832,9 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) case vid: val = pmbus_reg2data_vid(data, sensor); break; + case ieee754: + val = pmbus_reg2data_ieee754(data, sensor); + break; case linear: default: val = pmbus_reg2data_linear(data, sensor); @@ -743,11 +843,75 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) return val; } -#define MAX_MANTISSA (1023 * 1000) -#define MIN_MANTISSA (511 * 1000) +#define MAX_IEEE_MANTISSA (0x7ff * 1000) +#define MIN_IEEE_MANTISSA (0x400 * 1000) + +static u16 pmbus_data2reg_ieee754(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + u16 exponent = (15 + 10); + long mantissa; + u16 sign = 0; + + /* simple case */ + if (val == 0) + return 0; + + if (val < 0) { + sign = 0x8000; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST(val, 1000L); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val > MAX_IEEE_MANTISSA && exponent < 30) { + exponent++; + val >>= 1; + } + /* + * Increase small mantissa to generate valid 'normal' + * number + */ + while (val < MIN_IEEE_MANTISSA && exponent > 1) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* + * Ensure that the resulting number is within range. + * Valid range is 0x400..0x7ff, where bit 10 reflects + * the implied high bit in normalized ieee754 numbers. + * Set the range to 0x400..0x7ff to reflect this. + * The upper bit is then removed by the mask against + * 0x3ff in the final assignment. + */ + if (mantissa > 0x7ff) + mantissa = 0x7ff; + else if (mantissa < 0x400) + mantissa = 0x400; + + /* Convert to sign, 5 bit exponent, 10 bit mantissa */ + return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00); +} + +#define MAX_LIN_MANTISSA (1023 * 1000) +#define MIN_LIN_MANTISSA (511 * 1000) static u16 pmbus_data2reg_linear(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { s16 exponent = 0, mantissa; bool negative = false; @@ -769,8 +933,8 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, val <<= -data->exponent[sensor->page]; else val >>= data->exponent[sensor->page]; - val = DIV_ROUND_CLOSEST(val, 1000); - return val & 0xffff; + val = DIV_ROUND_CLOSEST_ULL(val, 1000); + return clamp_val(val, 0, 0xffff); } if (val < 0) { @@ -780,32 +944,28 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, /* Power is in uW. Convert to mW before converting. */ if (sensor->class == PSC_POWER) - val = DIV_ROUND_CLOSEST(val, 1000L); + val = DIV_ROUND_CLOSEST_ULL(val, 1000); /* * For simplicity, convert fan data to milli-units * before calculating the exponent. */ if (sensor->class == PSC_FAN) - val = val * 1000; + val = val * 1000LL; /* Reduce large mantissa until it fits into 10 bit */ - while (val >= MAX_MANTISSA && exponent < 15) { + while (val >= MAX_LIN_MANTISSA && exponent < 15) { exponent++; val >>= 1; } /* Increase small mantissa to improve precision */ - while (val < MIN_MANTISSA && exponent > -15) { + while (val < MIN_LIN_MANTISSA && exponent > -15) { exponent--; val <<= 1; } /* Convert mantissa from milli-units to units */ - mantissa = DIV_ROUND_CLOSEST(val, 1000); - - /* Ensure that resulting number is within range */ - if (mantissa > 0x3ff) - mantissa = 0x3ff; + mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); /* restore sign */ if (negative) @@ -816,9 +976,9 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, } static u16 pmbus_data2reg_direct(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { - s64 b, val64 = val; + s64 b; s32 m, R; m = data->info->m[sensor->class]; @@ -836,30 +996,30 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, R -= 3; /* Adjust R and b for data in milli-units */ b *= 1000; } - val64 = val64 * m + b; + val = val * m + b; while (R > 0) { - val64 *= 10; + val *= 10; R--; } while (R < 0) { - val64 = div_s64(val64 + 5LL, 10L); /* round closest */ + val = div_s64(val + 5LL, 10L); /* round closest */ R++; } - return (u16)clamp_val(val64, S16_MIN, S16_MAX); + return (u16)clamp_val(val, S16_MIN, S16_MAX); } static u16 pmbus_data2reg_vid(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { val = clamp_val(val, 500, 1600); - return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); + return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); } static u16 pmbus_data2reg(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { u16 regval; @@ -873,6 +1033,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data, case vid: regval = pmbus_data2reg_vid(data, sensor, val); break; + case ieee754: + regval = pmbus_data2reg_ieee754(data, sensor, val); + break; case linear: default: regval = pmbus_data2reg_linear(data, sensor, val); @@ -904,38 +1067,56 @@ static u16 pmbus_data2reg(struct pmbus_data *data, * If a negative value is stored in any of the referenced registers, this value * reflects an error code which will be returned. */ -static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, +static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, int index) { + struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_sensor *s1 = b->s1; struct pmbus_sensor *s2 = b->s2; - u16 reg = (index >> 16) & 0xffff; - u16 mask = index & 0xffff; + u16 mask = pb_index_to_mask(index); + u8 page = pb_index_to_page(index); + u16 reg = pb_index_to_reg(index); int ret, status; u16 regval; - status = data->status[reg]; - if (status < 0) - return status; + mutex_lock(&data->update_lock); + status = pmbus_get_status(client, page, reg); + if (status < 0) { + ret = status; + goto unlock; + } + + if (s1) + pmbus_update_sensor_data(client, s1); + if (s2) + pmbus_update_sensor_data(client, s2); regval = status & mask; - if (!s1 && !s2) { - ret = !!regval; - } else if (!s1 || !s2) { - WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); - return 0; - } else { - long v1, v2; + if (regval) { + ret = _pmbus_write_byte_data(client, page, reg, regval); + if (ret) + goto unlock; + } + if (s1 && s2) { + s64 v1, v2; - if (s1->data < 0) - return s1->data; - if (s2->data < 0) - return s2->data; + if (s1->data < 0) { + ret = s1->data; + goto unlock; + } + if (s2->data < 0) { + ret = s2->data; + goto unlock; + } v1 = pmbus_reg2data(data, s1); v2 = pmbus_reg2data(data, s2); ret = !!(regval && v1 >= v2); + } else { + ret = !!regval; } +unlock: + mutex_unlock(&data->update_lock); return ret; } @@ -944,25 +1125,31 @@ static ssize_t pmbus_show_boolean(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct pmbus_boolean *boolean = to_pmbus_boolean(attr); - struct pmbus_data *data = pmbus_update_device(dev); + struct i2c_client *client = to_i2c_client(dev->parent); int val; - val = pmbus_get_boolean(data, boolean, attr->index); + val = pmbus_get_boolean(client, boolean, attr->index); if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t pmbus_show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { - struct pmbus_data *data = pmbus_update_device(dev); + struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); + ssize_t ret; + mutex_lock(&data->update_lock); + pmbus_update_sensor_data(client, sensor); if (sensor->data < 0) - return sensor->data; - - return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor)); + ret = sensor->data; + else + ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor)); + mutex_unlock(&data->update_lock); + return ret; } static ssize_t pmbus_set_sensor(struct device *dev, @@ -973,11 +1160,11 @@ static ssize_t pmbus_set_sensor(struct device *dev, struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); ssize_t rv = count; - long val = 0; + s64 val; int ret; u16 regval; - if (kstrtol(buf, 10, &val) < 0) + if (kstrtos64(buf, 10, &val) < 0) return -EINVAL; mutex_lock(&data->update_lock); @@ -986,7 +1173,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, if (ret < 0) rv = ret; else - sensor->data = regval; + sensor->data = -ENODATA; mutex_unlock(&data->update_lock); return rv; } @@ -996,16 +1183,16 @@ static ssize_t pmbus_show_label(struct device *dev, { struct pmbus_label *label = to_pmbus_label(da); - return snprintf(buf, PAGE_SIZE, "%s\n", label->label); + return sysfs_emit(buf, "%s\n", label->label); } static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) { if (data->num_attributes >= data->max_attributes - 1) { int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; - void *new_attrs = krealloc(data->group.attrs, - new_max_attrs * sizeof(void *), - GFP_KERNEL); + void *new_attrs = devm_krealloc(data->dev, data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); if (!new_attrs) return -ENOMEM; data->group.attrs = new_attrs; @@ -1053,11 +1240,14 @@ static int pmbus_add_boolean(struct pmbus_data *data, const char *name, const char *type, int seq, struct pmbus_sensor *s1, struct pmbus_sensor *s2, - u16 reg, u16 mask) + u8 page, u16 reg, u16 mask) { struct pmbus_boolean *boolean; struct sensor_device_attribute *a; + if (WARN((s1 && !s2) || (!s1 && s2), "Bad s1/s2 parameters\n")) + return -EINVAL; + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); if (!boolean) return -ENOMEM; @@ -1069,14 +1259,77 @@ static int pmbus_add_boolean(struct pmbus_data *data, boolean->s1 = s1; boolean->s2 = s2; pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL, - (reg << 16) | mask); + pb_reg_to_index(page, reg, mask)); return pmbus_add_attribute(data, &a->dev_attr.attr); } +/* of thermal for pmbus temperature sensors */ +struct pmbus_thermal_data { + struct pmbus_data *pmbus_data; + struct pmbus_sensor *sensor; +}; + +static int pmbus_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct pmbus_thermal_data *tdata = tz->devdata; + struct pmbus_sensor *sensor = tdata->sensor; + struct pmbus_data *pmbus_data = tdata->pmbus_data; + struct i2c_client *client = to_i2c_client(pmbus_data->dev); + struct device *dev = pmbus_data->hwmon_dev; + int ret = 0; + + if (!dev) { + /* May not even get to hwmon yet */ + *temp = 0; + return 0; + } + + mutex_lock(&pmbus_data->update_lock); + pmbus_update_sensor_data(client, sensor); + if (sensor->data < 0) + ret = sensor->data; + else + *temp = (int)pmbus_reg2data(pmbus_data, sensor); + mutex_unlock(&pmbus_data->update_lock); + + return ret; +} + +static const struct thermal_zone_device_ops pmbus_thermal_ops = { + .get_temp = pmbus_thermal_get_temp, +}; + +static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data, + struct pmbus_sensor *sensor, int index) +{ + struct device *dev = pmbus_data->dev; + struct pmbus_thermal_data *tdata; + struct thermal_zone_device *tzd; + + tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL); + if (!tdata) + return -ENOMEM; + + tdata->sensor = sensor; + tdata->pmbus_data = pmbus_data; + + tzd = devm_thermal_of_zone_register(dev, index, tdata, + &pmbus_thermal_ops); + /* + * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV, + * so ignore that error but forward any other error. + */ + if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) + return PTR_ERR(tzd); + + return 0; +} + static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, const char *name, const char *type, - int seq, int page, int reg, + int seq, int page, int phase, + int reg, enum pmbus_sensor_classes class, bool update, bool readonly, bool convert) @@ -1100,10 +1353,12 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, readonly = true; sensor->page = page; + sensor->phase = phase; sensor->reg = reg; sensor->class = class; sensor->update = update; sensor->convert = convert; + sensor->data = -ENODATA; pmbus_dev_attr_init(a, sensor->name, readonly ? 0444 : 0644, pmbus_show_sensor, pmbus_set_sensor); @@ -1114,12 +1369,16 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, sensor->next = data->sensors; data->sensors = sensor; + /* temperature sensors with _input values are registered with thermal */ + if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0) + pmbus_thermal_add_sensor(data, sensor, seq); + return sensor; } static int pmbus_add_label(struct pmbus_data *data, const char *name, int seq, - const char *lstring, int index) + const char *lstring, int index, int phase) { struct pmbus_label *label; struct device_attribute *a; @@ -1131,11 +1390,21 @@ static int pmbus_add_label(struct pmbus_data *data, a = &label->attribute; snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); - if (!index) - strncpy(label->label, lstring, sizeof(label->label) - 1); - else - snprintf(label->label, sizeof(label->label), "%s%d", lstring, - index); + if (!index) { + if (phase == 0xff) + strncpy(label->label, lstring, + sizeof(label->label) - 1); + else + snprintf(label->label, sizeof(label->label), "%s.%d", + lstring, phase); + } else { + if (phase == 0xff) + snprintf(label->label, sizeof(label->label), "%s%d", + lstring, index); + else + snprintf(label->label, sizeof(label->label), "%s%d.%d", + lstring, index, phase); + } pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL); return pmbus_add_attribute(data, &a->attr); @@ -1174,7 +1443,7 @@ struct pmbus_sensor_attr { bool compare; /* true if compare function needed */ u32 func; /* sensor mask */ u32 sfunc; /* sensor status mask */ - int sbase; /* status base register */ + int sreg; /* status register */ const struct pmbus_limit_attr *limit;/* limit registers */ }; @@ -1200,7 +1469,7 @@ static int pmbus_add_limit_attrs(struct i2c_client *client, for (i = 0; i < nlimit; i++) { if (pmbus_check_word_register(client, page, l->reg)) { curr = pmbus_add_sensor(data, name, l->attr, index, - page, l->reg, attr->class, + page, 0xff, l->reg, attr->class, attr->update || l->update, false, true); if (!curr) @@ -1212,7 +1481,7 @@ static int pmbus_add_limit_attrs(struct i2c_client *client, : NULL, attr->compare ? l->low ? base : curr : NULL, - attr->sbase + page, l->sbit); + page, attr->sreg, l->sbit); if (ret) return ret; have_alarm = 1; @@ -1227,7 +1496,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, struct pmbus_data *data, const struct pmbus_driver_info *info, const char *name, - int index, int page, + int index, int page, int phase, const struct pmbus_sensor_attr *attr, bool paged) { @@ -1237,15 +1506,16 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, if (attr->label) { ret = pmbus_add_label(data, name, index, attr->label, - paged ? page + 1 : 0); + paged ? page + 1 : 0, phase); if (ret) return ret; } - base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, - attr->class, true, true, true); + base = pmbus_add_sensor(data, name, "input", index, page, phase, + attr->reg, attr->class, true, true, true); if (!base) return -ENOMEM; - if (attr->sfunc) { + /* No limit and alarm attributes for phase specific sensors */ + if (attr->sfunc && phase == 0xff) { ret = pmbus_add_limit_attrs(client, data, info, name, index, page, base, attr); if (ret < 0) @@ -1257,11 +1527,11 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, * which global bit is set) for this page is accessible. */ if (!ret && attr->gbit && - (!upper || (upper && data->has_status_word)) && + (!upper || data->has_status_word) && pmbus_check_status_register(client, page)) { ret = pmbus_add_boolean(data, name, "alarm", index, NULL, NULL, - PB_STATUS_BASE + page, + page, PMBUS_STATUS_WORD, attr->gbit); if (ret) return ret; @@ -1311,14 +1581,29 @@ static int pmbus_add_sensor_attrs(struct i2c_client *client, pages = paged ? info->pages : 1; for (page = 0; page < pages; page++) { - if (!(info->func[page] & attrs->func)) - continue; - ret = pmbus_add_sensor_attrs_one(client, data, info, - name, index, page, - attrs, paged); - if (ret) - return ret; - index++; + if (info->func[page] & attrs->func) { + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + 0xff, attrs, paged); + if (ret) + return ret; + index++; + } + if (info->phases[page]) { + int phase; + + for (phase = 0; phase < info->phases[page]; + phase++) { + if (!(info->pfunc[phase] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, + data, info, name, index, page, + phase, attrs, paged); + if (ret) + return ret; + index++; + } + } } attrs++; } @@ -1335,7 +1620,7 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = { .reg = PMBUS_VIN_UV_FAULT_LIMIT, .attr = "lcrit", .alarm = "lcrit_alarm", - .sbit = PB_VOLTAGE_UV_FAULT, + .sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF, }, { .reg = PMBUS_VIN_OV_WARN_LIMIT, .attr = "max", @@ -1361,6 +1646,12 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_VIN_HISTORY, .attr = "reset_history", + }, { + .reg = PMBUS_MFR_VIN_MIN, + .attr = "rated_min", + }, { + .reg = PMBUS_MFR_VIN_MAX, + .attr = "rated_max", }, }; @@ -1424,7 +1715,13 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_VOUT_MIN, + .attr = "rated_min", + }, { + .reg = PMBUS_MFR_VOUT_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr voltage_attributes[] = { @@ -1434,7 +1731,7 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .label = "vin", .func = PMBUS_HAVE_VIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, - .sbase = PB_STATUS_INPUT_BASE, + .sreg = PMBUS_STATUS_INPUT, .gbit = PB_STATUS_VIN_UV, .limit = vin_limit_attrs, .nlimit = ARRAY_SIZE(vin_limit_attrs), @@ -1444,7 +1741,7 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .label = "vmon", .func = PMBUS_HAVE_VMON, .sfunc = PMBUS_HAVE_STATUS_VMON, - .sbase = PB_STATUS_VMON_BASE, + .sreg = PMBUS_VIRT_STATUS_VMON, .limit = vmon_limit_attrs, .nlimit = ARRAY_SIZE(vmon_limit_attrs), }, { @@ -1459,7 +1756,7 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .paged = true, .func = PMBUS_HAVE_VOUT, .sfunc = PMBUS_HAVE_STATUS_VOUT, - .sbase = PB_STATUS_VOUT_BASE, + .sreg = PMBUS_STATUS_VOUT, .gbit = PB_STATUS_VOUT_OV, .limit = vout_limit_attrs, .nlimit = ARRAY_SIZE(vout_limit_attrs), @@ -1494,7 +1791,10 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_IIN_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_IIN_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr iout_limit_attrs[] = { @@ -1528,7 +1828,10 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_IOUT_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr current_attributes[] = { @@ -1538,7 +1841,7 @@ static const struct pmbus_sensor_attr current_attributes[] = { .label = "iin", .func = PMBUS_HAVE_IIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, - .sbase = PB_STATUS_INPUT_BASE, + .sreg = PMBUS_STATUS_INPUT, .gbit = PB_STATUS_INPUT, .limit = iin_limit_attrs, .nlimit = ARRAY_SIZE(iin_limit_attrs), @@ -1549,7 +1852,7 @@ static const struct pmbus_sensor_attr current_attributes[] = { .paged = true, .func = PMBUS_HAVE_IOUT, .sfunc = PMBUS_HAVE_STATUS_IOUT, - .sbase = PB_STATUS_IOUT_BASE, + .sreg = PMBUS_STATUS_IOUT, .gbit = PB_STATUS_IOUT_OC, .limit = iout_limit_attrs, .nlimit = ARRAY_SIZE(iout_limit_attrs), @@ -1579,7 +1882,10 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_PIN_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_PIN_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr pout_limit_attrs[] = { @@ -1613,7 +1919,10 @@ static const struct pmbus_limit_attr pout_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_POUT_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_POUT_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr power_attributes[] = { @@ -1623,7 +1932,7 @@ static const struct pmbus_sensor_attr power_attributes[] = { .label = "pin", .func = PMBUS_HAVE_PIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, - .sbase = PB_STATUS_INPUT_BASE, + .sreg = PMBUS_STATUS_INPUT, .gbit = PB_STATUS_INPUT, .limit = pin_limit_attrs, .nlimit = ARRAY_SIZE(pin_limit_attrs), @@ -1634,7 +1943,7 @@ static const struct pmbus_sensor_attr power_attributes[] = { .paged = true, .func = PMBUS_HAVE_POUT, .sfunc = PMBUS_HAVE_STATUS_IOUT, - .sbase = PB_STATUS_IOUT_BASE, + .sreg = PMBUS_STATUS_IOUT, .limit = pout_limit_attrs, .nlimit = ARRAY_SIZE(pout_limit_attrs), } @@ -1677,7 +1986,10 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_MAX_TEMP_1, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr temp_limit_attrs2[] = { @@ -1715,7 +2027,10 @@ static const struct pmbus_limit_attr temp_limit_attrs2[] = { }, { .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_MAX_TEMP_2, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr temp_limit_attrs3[] = { @@ -1741,7 +2056,10 @@ static const struct pmbus_limit_attr temp_limit_attrs3[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_TEMP_OT_FAULT, - } + }, { + .reg = PMBUS_MFR_MAX_TEMP_3, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr temp_attributes[] = { @@ -1753,7 +2071,7 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .compare = true, .func = PMBUS_HAVE_TEMP, .sfunc = PMBUS_HAVE_STATUS_TEMP, - .sbase = PB_STATUS_TEMP_BASE, + .sreg = PMBUS_STATUS_TEMPERATURE, .gbit = PB_STATUS_TEMPERATURE, .limit = temp_limit_attrs, .nlimit = ARRAY_SIZE(temp_limit_attrs), @@ -1765,7 +2083,7 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .compare = true, .func = PMBUS_HAVE_TEMP2, .sfunc = PMBUS_HAVE_STATUS_TEMP, - .sbase = PB_STATUS_TEMP_BASE, + .sreg = PMBUS_STATUS_TEMPERATURE, .gbit = PB_STATUS_TEMPERATURE, .limit = temp_limit_attrs2, .nlimit = ARRAY_SIZE(temp_limit_attrs2), @@ -1777,7 +2095,7 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .compare = true, .func = PMBUS_HAVE_TEMP3, .sfunc = PMBUS_HAVE_STATUS_TEMP, - .sbase = PB_STATUS_TEMP_BASE, + .sreg = PMBUS_STATUS_TEMPERATURE, .gbit = PB_STATUS_TEMPERATURE, .limit = temp_limit_attrs3, .nlimit = ARRAY_SIZE(temp_limit_attrs3), @@ -1822,7 +2140,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client, struct pmbus_sensor *sensor; sensor = pmbus_add_sensor(data, "fan", "target", index, page, - PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, + 0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, false, false, true); if (!sensor) @@ -1833,14 +2151,14 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client, return 0; sensor = pmbus_add_sensor(data, "pwm", NULL, index, page, - PMBUS_VIRT_PWM_1 + id, PSC_PWM, + 0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM, false, false, true); if (!sensor) return -ENOMEM; sensor = pmbus_add_sensor(data, "pwm", "enable", index, page, - PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, + 0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, true, false, false); if (!sensor) @@ -1882,7 +2200,7 @@ static int pmbus_add_fan_attributes(struct i2c_client *client, continue; if (pmbus_add_sensor(data, "fan", "input", index, - page, pmbus_fan_registers[f], + page, 0xff, pmbus_fan_registers[f], PSC_FAN, true, true, true) == NULL) return -ENOMEM; @@ -1902,19 +2220,19 @@ static int pmbus_add_fan_attributes(struct i2c_client *client, if ((info->func[page] & pmbus_fan_status_flags[f]) && pmbus_check_byte_register(client, page, pmbus_fan_status_registers[f])) { - int base; + int reg; if (f > 1) /* fan 3, 4 */ - base = PB_STATUS_FAN34_BASE + page; + reg = PMBUS_STATUS_FAN_34; else - base = PB_STATUS_FAN_BASE + page; + reg = PMBUS_STATUS_FAN_12; ret = pmbus_add_boolean(data, "fan", - "alarm", index, NULL, NULL, base, + "alarm", index, NULL, NULL, page, reg, PB_FAN_FAN1_WARNING >> (f & 1)); if (ret) return ret; ret = pmbus_add_boolean(data, "fan", - "fault", index, NULL, NULL, base, + "fault", index, NULL, NULL, page, reg, PB_FAN_FAN1_FAULT >> (f & 1)); if (ret) return ret; @@ -1963,12 +2281,15 @@ static ssize_t pmbus_show_samples(struct device *dev, int val; struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_samples_reg *reg = to_samples_reg(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); - val = _pmbus_read_word_data(client, reg->page, reg->attr->reg); + mutex_lock(&data->update_lock); + val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg); + mutex_unlock(&data->update_lock); if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t pmbus_set_samples(struct device *dev, @@ -2073,6 +2394,111 @@ static int pmbus_find_attributes(struct i2c_client *client, } /* + * The pmbus_class_attr_map structure maps one sensor class to + * it's corresponding sensor attributes array. + */ +struct pmbus_class_attr_map { + enum pmbus_sensor_classes class; + int nattr; + const struct pmbus_sensor_attr *attr; +}; + +static const struct pmbus_class_attr_map class_attr_map[] = { + { + .class = PSC_VOLTAGE_IN, + .attr = voltage_attributes, + .nattr = ARRAY_SIZE(voltage_attributes), + }, { + .class = PSC_VOLTAGE_OUT, + .attr = voltage_attributes, + .nattr = ARRAY_SIZE(voltage_attributes), + }, { + .class = PSC_CURRENT_IN, + .attr = current_attributes, + .nattr = ARRAY_SIZE(current_attributes), + }, { + .class = PSC_CURRENT_OUT, + .attr = current_attributes, + .nattr = ARRAY_SIZE(current_attributes), + }, { + .class = PSC_POWER, + .attr = power_attributes, + .nattr = ARRAY_SIZE(power_attributes), + }, { + .class = PSC_TEMPERATURE, + .attr = temp_attributes, + .nattr = ARRAY_SIZE(temp_attributes), + } +}; + +/* + * Read the coefficients for direct mode. + */ +static int pmbus_read_coefficients(struct i2c_client *client, + struct pmbus_driver_info *info, + const struct pmbus_sensor_attr *attr) +{ + int rv; + union i2c_smbus_data data; + enum pmbus_sensor_classes class = attr->class; + s8 R; + s16 m, b; + + data.block[0] = 2; + data.block[1] = attr->reg; + data.block[2] = 0x01; + + rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, + I2C_SMBUS_BLOCK_PROC_CALL, &data); + + if (rv < 0) + return rv; + + if (data.block[0] != 5) + return -EIO; + + m = data.block[1] | (data.block[2] << 8); + b = data.block[3] | (data.block[4] << 8); + R = data.block[5]; + info->m[class] = m; + info->b[class] = b; + info->R[class] = R; + + return rv; +} + +static int pmbus_init_coefficients(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int i, n, ret = -EINVAL; + const struct pmbus_class_attr_map *map; + const struct pmbus_sensor_attr *attr; + + for (i = 0; i < ARRAY_SIZE(class_attr_map); i++) { + map = &class_attr_map[i]; + if (info->format[map->class] != direct) + continue; + for (n = 0; n < map->nattr; n++) { + attr = &map->attr[n]; + if (map->class != attr->class) + continue; + ret = pmbus_read_coefficients(client, info, attr); + if (ret >= 0) + break; + } + if (ret < 0) { + dev_err(&client->dev, + "No coefficients found for sensor class %d\n", + map->class); + return -EINVAL; + } + } + + return 0; +} + +/* * Identify chip parameters. * This function is called for all chips. */ @@ -2104,6 +2530,10 @@ static int pmbus_identify_common(struct i2c_client *client, if (data->info->format[PSC_VOLTAGE_OUT] != direct) return -ENODEV; break; + case 3: /* ieee 754 half precision */ + if (data->info->format[PSC_VOLTAGE_OUT] != ieee754) + return -ENODEV; + break; default: return -ENODEV; } @@ -2120,7 +2550,43 @@ static int pmbus_read_status_byte(struct i2c_client *client, int page) static int pmbus_read_status_word(struct i2c_client *client, int page) { - return _pmbus_read_word_data(client, page, PMBUS_STATUS_WORD); + return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD); +} + +/* PEC attribute support */ + +static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); +} + +static ssize_t pec_store(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + bool enable; + int err; + + err = kstrtobool(buf, &enable); + if (err < 0) + return err; + + if (enable) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + + return count; +} + +static DEVICE_ATTR_RW(pec); + +static void pmbus_remove_pec(void *dev) +{ + device_remove_file(dev, &dev_attr_pec); } static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, @@ -2130,6 +2596,21 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, int page, ret; /* + * Figure out if PEC is enabled before accessing any other register. + * Make sure PEC is disabled, will be enabled later if needed. + */ + client->flags &= ~I2C_CLIENT_PEC; + + /* Enable PEC if the controller and bus supports it */ + if (!(data->flags & PMBUS_NO_CAPABILITY)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) + client->flags |= I2C_CLIENT_PEC; + } + } + + /* * Some PMBus chips don't support PMBUS_STATUS_WORD, so try * to use PMBUS_STATUS_BYTE instead if that is the case. * Bail out if both registers are not supported. @@ -2147,19 +2628,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, data->has_status_word = true; } - /* Enable PEC if the controller supports it */ - ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) - client->flags |= I2C_CLIENT_PEC; - /* * Check if the chip is write protected. If it is, we can not clear * faults, and we should not try it. Also, in that case, writes into * limit registers need to be disabled. */ - ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); - if (ret > 0 && (ret & PB_WP_ANY)) - data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); + if (ret > 0 && (ret & PB_WP_ANY)) + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + } if (data->info->pages) pmbus_clear_faults(client); @@ -2186,6 +2664,31 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return ret; } } + + if (data->flags & PMBUS_USE_COEFFICIENTS_CMD) { + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL)) + return -ENODEV; + + ret = pmbus_init_coefficients(client, info); + if (ret < 0) + return ret; + } + + if (client->flags & I2C_CLIENT_PEC) { + /* + * If I2C_CLIENT_PEC is set here, both the I2C adapter and the + * chip support PEC. Add 'pec' attribute to client device to let + * the user control it. + */ + ret = device_create_file(dev, &dev_attr_pec); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev); + if (ret) + return ret; + } + return 0; } @@ -2194,10 +2697,14 @@ static int pmbus_regulator_is_enabled(struct regulator_dev *rdev) { struct device *dev = rdev_get_dev(rdev); struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); u8 page = rdev_get_id(rdev); int ret; - ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + mutex_lock(&data->update_lock); + ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION); + mutex_unlock(&data->update_lock); + if (ret < 0) return ret; @@ -2208,11 +2715,17 @@ static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable) { struct device *dev = rdev_get_dev(rdev); struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); u8 page = rdev_get_id(rdev); + int ret; - return pmbus_update_byte_data(client, page, PMBUS_OPERATION, - PB_OPERATION_CONTROL_ON, - enable ? PB_OPERATION_CONTROL_ON : 0); + mutex_lock(&data->update_lock); + ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION, + PB_OPERATION_CONTROL_ON, + enable ? PB_OPERATION_CONTROL_ON : 0); + mutex_unlock(&data->update_lock); + + return ret; } static int pmbus_regulator_enable(struct regulator_dev *rdev) @@ -2225,12 +2738,264 @@ static int pmbus_regulator_disable(struct regulator_dev *rdev) return _pmbus_regulator_on_off(rdev, 0); } +/* A PMBus status flag and the corresponding REGULATOR_ERROR_* flag */ +struct pmbus_regulator_status_assoc { + int pflag, rflag; +}; + +/* PMBus->regulator bit mappings for a PMBus status register */ +struct pmbus_regulator_status_category { + int func; + int reg; + const struct pmbus_regulator_status_assoc *bits; /* zero-terminated */ +}; + +static const struct pmbus_regulator_status_category pmbus_regulator_flag_map[] = { + { + .func = PMBUS_HAVE_STATUS_VOUT, + .reg = PMBUS_STATUS_VOUT, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN }, + { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE }, + { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN }, + { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT }, + { }, + }, + }, { + .func = PMBUS_HAVE_STATUS_IOUT, + .reg = PMBUS_STATUS_IOUT, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN }, + { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT }, + { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT }, + { }, + }, + }, { + .func = PMBUS_HAVE_STATUS_TEMP, + .reg = PMBUS_STATUS_TEMPERATURE, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN }, + { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP }, + { }, + }, + }, +}; + +static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) +{ + int i, status; + const struct pmbus_regulator_status_category *cat; + const struct pmbus_regulator_status_assoc *bit; + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + u8 page = rdev_get_id(rdev); + int func = data->info->func[page]; + + *flags = 0; + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(pmbus_regulator_flag_map); i++) { + cat = &pmbus_regulator_flag_map[i]; + if (!(func & cat->func)) + continue; + + status = _pmbus_read_byte_data(client, page, cat->reg); + if (status < 0) { + mutex_unlock(&data->update_lock); + return status; + } + + for (bit = cat->bits; bit->pflag; bit++) { + if (status & bit->pflag) + *flags |= bit->rflag; + } + } + + /* + * Map what bits of STATUS_{WORD,BYTE} we can to REGULATOR_ERROR_* + * bits. Some of the other bits are tempting (especially for cases + * where we don't have the relevant PMBUS_HAVE_STATUS_* + * functionality), but there's an unfortunate ambiguity in that + * they're defined as indicating a fault *or* a warning, so we can't + * easily determine whether to report REGULATOR_ERROR_<foo> or + * REGULATOR_ERROR_<foo>_WARN. + */ + status = pmbus_get_status(client, page, PMBUS_STATUS_WORD); + mutex_unlock(&data->update_lock); + if (status < 0) + return status; + + if (pmbus_regulator_is_enabled(rdev) && (status & PB_STATUS_OFF)) + *flags |= REGULATOR_ERROR_FAIL; + + /* + * Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are + * defined strictly as fault indicators (not warnings). + */ + if (status & PB_STATUS_IOUT_OC) + *flags |= REGULATOR_ERROR_OVER_CURRENT; + if (status & PB_STATUS_VOUT_OV) + *flags |= REGULATOR_ERROR_REGULATION_OUT; + + /* + * If we haven't discovered any thermal faults or warnings via + * PMBUS_STATUS_TEMPERATURE, map PB_STATUS_TEMPERATURE to a warning as + * a (conservative) best-effort interpretation. + */ + if (!(*flags & (REGULATOR_ERROR_OVER_TEMP | REGULATOR_ERROR_OVER_TEMP_WARN)) && + (status & PB_STATUS_TEMPERATURE)) + *flags |= REGULATOR_ERROR_OVER_TEMP_WARN; + + return 0; +} + +static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = page, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + + if (data->vout_low[page] < 0) { + if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MIN)) + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_MFR_VOUT_MIN); + if (s.data < 0) { + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_MARGIN_LOW); + if (s.data < 0) + return s.data; + } + data->vout_low[page] = pmbus_reg2data(data, &s); + } + + return data->vout_low[page]; +} + +static int pmbus_regulator_get_high_margin(struct i2c_client *client, int page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = page, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + + if (data->vout_high[page] < 0) { + if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MAX)) + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_MFR_VOUT_MAX); + if (s.data < 0) { + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_MARGIN_HIGH); + if (s.data < 0) + return s.data; + } + data->vout_high[page] = pmbus_reg2data(data, &s); + } + + return data->vout_high[page]; +} + +static int pmbus_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = rdev_get_id(rdev), + .class = PSC_VOLTAGE_OUT, + .convert = true, + }; + + s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT); + if (s.data < 0) + return s.data; + + return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */ +} + +static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, + int max_uv, unsigned int *selector) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = rdev_get_id(rdev), + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */ + int low, high; + + *selector = 0; + + low = pmbus_regulator_get_low_margin(client, s.page); + if (low < 0) + return low; + + high = pmbus_regulator_get_high_margin(client, s.page); + if (high < 0) + return high; + + /* Make sure we are within margins */ + if (low > val) + val = low; + if (high < val) + val = high; + + val = pmbus_data2reg(data, &s, val); + + return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val); +} + +static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + int val, low, high; + + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) + return -EINVAL; + + selector -= rdev->desc->linear_min_sel; + val = DIV_ROUND_CLOSEST(rdev->desc->min_uV + + (rdev->desc->uV_step * selector), 1000); /* convert to mV */ + + low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev)); + if (low < 0) + return low; + + high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev)); + if (high < 0) + return high; + + if (val >= low && val <= high) + return val * 1000; /* unit is uV */ + + return 0; +} + const struct regulator_ops pmbus_regulator_ops = { .enable = pmbus_regulator_enable, .disable = pmbus_regulator_disable, .is_enabled = pmbus_regulator_is_enabled, + .get_error_flags = pmbus_regulator_get_error_flags, + .get_voltage = pmbus_regulator_get_voltage, + .set_voltage = pmbus_regulator_set_voltage, + .list_voltage = pmbus_regulator_list_voltage, }; -EXPORT_SYMBOL_GPL(pmbus_regulator_ops); +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); static int pmbus_regulator_register(struct pmbus_data *data) { @@ -2251,11 +3016,10 @@ static int pmbus_regulator_register(struct pmbus_data *data) rdev = devm_regulator_register(dev, &info->reg_desc[i], &config); - if (IS_ERR(rdev)) { - dev_err(dev, "Failed to register %s regulator\n", - info->reg_desc[i].name); - return PTR_ERR(rdev); - } + if (IS_ERR(rdev)) + return dev_err_probe(dev, PTR_ERR(rdev), + "Failed to register %s regulator\n", + info->reg_desc[i].name); } return 0; @@ -2303,6 +3067,41 @@ static int pmbus_debugfs_get_status(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); +static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + struct pmbus_debugfs_entry *entry = file->private_data; + char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + + rc = pmbus_read_block_data(entry->client, entry->page, entry->reg, + data); + if (rc < 0) + return rc; + + /* Add newline at the end of a read data */ + data[rc] = '\n'; + + /* Include newline into the length */ + rc += 1; + + return simple_read_from_buffer(buf, count, ppos, data, rc); +} + +static const struct file_operations pmbus_debugfs_ops_mfr = { + .llseek = noop_llseek, + .read = pmbus_debugfs_mfr_read, + .write = NULL, + .open = simple_open, +}; + +static void pmbus_remove_debugfs(void *data) +{ + struct dentry *entry = data; + + debugfs_remove_recursive(entry); +} + static int pmbus_init_debugfs(struct i2c_client *client, struct pmbus_data *data) { @@ -2324,13 +3123,80 @@ static int pmbus_init_debugfs(struct i2c_client *client, return -ENODEV; } - /* Allocate the max possible entries we need. */ + /* + * Allocate the max possible entries we need. + * 6 entries device-specific + * 10 entries page-specific + */ entries = devm_kcalloc(data->dev, - data->info->pages * 10, sizeof(*entries), + 6 + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; + /* + * Add device-specific entries. + * Please note that the PMBUS standard allows all registers to be + * page-specific. + * To reduce the number of debugfs entries for devices with many pages + * assume that values of the following registers are the same for all + * pages and report values only for page 0. + */ + if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_ID; + debugfs_create_file("mfr_id", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_MODEL; + debugfs_create_file("mfr_model", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_REVISION; + debugfs_create_file("mfr_revision", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_LOCATION; + debugfs_create_file("mfr_location", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_DATE; + debugfs_create_file("mfr_date", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_SERIAL; + debugfs_create_file("mfr_serial", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + /* Add page specific entries */ for (i = 0; i < data->info->pages; ++i) { /* Check accessibility of status register if it's not page 0 */ if (!i || pmbus_check_status_register(client, i)) { @@ -2435,7 +3301,8 @@ static int pmbus_init_debugfs(struct i2c_client *client, } } - return 0; + return devm_add_action_or_reset(data->dev, + pmbus_remove_debugfs, data->debugfs); } #else static int pmbus_init_debugfs(struct i2c_client *client, @@ -2445,14 +3312,15 @@ static int pmbus_init_debugfs(struct i2c_client *client, } #endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ -int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, - struct pmbus_driver_info *info) +int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) { struct device *dev = &client->dev; const struct pmbus_platform_data *pdata = dev_get_platdata(dev); struct pmbus_data *data; size_t groups_num = 0; int ret; + int i; + char *name; if (!info) return -ENODEV; @@ -2482,6 +3350,13 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, if (pdata) data->flags = pdata->flags; data->info = info; + data->currpage = -1; + data->currphase = -1; + + for (i = 0; i < ARRAY_SIZE(data->vout_low); i++) { + data->vout_low[i] = -1; + data->vout_high[i] = -1; + } ret = pmbus_init_common(client, data, info); if (ret < 0) @@ -2489,7 +3364,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, ret = pmbus_find_attributes(client, data); if (ret) - goto out_kfree; + return ret; /* * If there are no attributes, something is wrong. @@ -2497,49 +3372,34 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, */ if (!data->num_attributes) { dev_err(dev, "No attributes found\n"); - ret = -ENODEV; - goto out_kfree; + return -ENODEV; } + name = devm_kstrdup(dev, client->name, GFP_KERNEL); + if (!name) + return -ENOMEM; + strreplace(name, '-', '_'); + data->groups[0] = &data->group; memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); - data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, + name, data, data->groups); if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); dev_err(dev, "Failed to register hwmon device\n"); - goto out_kfree; + return PTR_ERR(data->hwmon_dev); } ret = pmbus_regulator_register(data); if (ret) - goto out_unregister; + return ret; ret = pmbus_init_debugfs(client, data); if (ret) dev_warn(dev, "Failed to register debugfs\n"); return 0; - -out_unregister: - hwmon_device_unregister(data->hwmon_dev); -out_kfree: - kfree(data->group.attrs); - return ret; -} -EXPORT_SYMBOL_GPL(pmbus_do_probe); - -int pmbus_do_remove(struct i2c_client *client) -{ - struct pmbus_data *data = i2c_get_clientdata(client); - - debugfs_remove_recursive(data->debugfs); - - hwmon_device_unregister(data->hwmon_dev); - kfree(data->group.attrs); - return 0; } -EXPORT_SYMBOL_GPL(pmbus_do_remove); +EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { @@ -2547,7 +3407,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) return data->debugfs; } -EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir); +EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS); static int __init pmbus_core_init(void) { |