diff options
Diffstat (limited to 'drivers/hwmon')
30 files changed, 1299 insertions, 226 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 47ac20aee06f..05a30832c6ba 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -280,6 +280,15 @@ config SENSORS_ASC7621 This driver can also be built as a module. If so, the module will be called asc7621. +config SENSORS_AXI_FAN_CONTROL + tristate "Analog Devices FAN Control HDL Core driver" + help + If you say yes here you get support for the Analog Devices + AXI HDL FAN monitoring core. + + This driver can also be built as a module. If so, the module + will be called axi-fan-control + config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" depends on X86 && PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 613f50987965..b0b9c8e57176 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o +obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 9632e2e3c4bb..319a0519ebdb 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -413,7 +413,7 @@ static int ADT7462_REG_VOLT(struct adt7462_data *data, int which) return 0x95; break; } - return -ENODEV; + return 0; } /* Provide labels for sysfs */ diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 01c2eeb02aa9..054080443b47 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -19,6 +19,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/jiffies.h> +#include <linux/of.h> #include <linux/util_macros.h> /* Indexes for the sysfs hooks */ @@ -193,6 +194,7 @@ struct adt7475_data { unsigned long measure_updated; bool valid; + u8 config2; u8 config4; u8 config5; u8 has_voltage; @@ -1458,6 +1460,85 @@ static int adt7475_update_limits(struct i2c_client *client) return 0; } +static int set_property_bit(const struct i2c_client *client, char *property, + u8 *config, u8 bit_index) +{ + u32 prop_value = 0; + int ret = of_property_read_u32(client->dev.of_node, property, + &prop_value); + + if (!ret) { + if (prop_value) + *config |= (1 << bit_index); + else + *config &= ~(1 << bit_index); + } + + return ret; +} + +static int load_attenuators(const struct i2c_client *client, int chip, + struct adt7475_data *data) +{ + int ret; + + if (chip == adt7476 || chip == adt7490) { + set_property_bit(client, "adi,bypass-attenuator-in0", + &data->config4, 4); + set_property_bit(client, "adi,bypass-attenuator-in1", + &data->config4, 5); + set_property_bit(client, "adi,bypass-attenuator-in3", + &data->config4, 6); + set_property_bit(client, "adi,bypass-attenuator-in4", + &data->config4, 7); + + ret = i2c_smbus_write_byte_data(client, REG_CONFIG4, + data->config4); + if (ret < 0) + return ret; + } else if (chip == adt7473 || chip == adt7475) { + set_property_bit(client, "adi,bypass-attenuator-in1", + &data->config2, 5); + + ret = i2c_smbus_write_byte_data(client, REG_CONFIG2, + data->config2); + if (ret < 0) + return ret; + } + + return 0; +} + +static int adt7475_set_pwm_polarity(struct i2c_client *client) +{ + u32 states[ADT7475_PWM_COUNT]; + int ret, i; + u8 val; + + ret = of_property_read_u32_array(client->dev.of_node, + "adi,pwm-active-state", states, + ARRAY_SIZE(states)); + if (ret) + return ret; + + for (i = 0; i < ADT7475_PWM_COUNT; i++) { + ret = adt7475_read(PWM_CONFIG_REG(i)); + if (ret < 0) + return ret; + val = ret; + if (states[i]) + val &= ~BIT(4); + else + val |= BIT(4); + + ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(i), val); + if (ret) + return ret; + } + + return 0; +} + static int adt7475_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1472,7 +1553,7 @@ static int adt7475_probe(struct i2c_client *client, struct adt7475_data *data; struct device *hwmon_dev; int i, ret = 0, revision, group_num = 0; - u8 config2, config3; + u8 config3; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1546,8 +1627,12 @@ static int adt7475_probe(struct i2c_client *client, } /* Voltage attenuators can be bypassed, globally or individually */ - config2 = adt7475_read(REG_CONFIG2); - if (config2 & CONFIG2_ATTN) { + data->config2 = adt7475_read(REG_CONFIG2); + ret = load_attenuators(client, chip, data); + if (ret) + dev_warn(&client->dev, "Error configuring attenuator bypass\n"); + + if (data->config2 & CONFIG2_ATTN) { data->bypass_attn = (0x3 << 3) | 0x3; } else { data->bypass_attn = ((data->config4 & CONFIG4_ATTN_IN10) >> 4) | @@ -1562,6 +1647,10 @@ static int adt7475_probe(struct i2c_client *client, for (i = 0; i < ADT7475_PWM_COUNT; i++) adt7475_read_pwm(client, i); + ret = adt7475_set_pwm_polarity(client); + if (ret && ret != -EINVAL) + dev_warn(&client->dev, "Error configuring pwm polarity\n"); + /* Start monitoring */ switch (chip) { case adt7475: diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c new file mode 100644 index 000000000000..38d9cdb3db1a --- /dev/null +++ b/drivers/hwmon/axi-fan-control.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Fan Control HDL CORE driver + * + * Copyright 2019 Analog Devices Inc. + */ +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/fpga/adi-axi-common.h> +#include <linux/hwmon.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#define ADI_AXI_PCORE_VER_MAJOR(version) (((version) >> 16) & 0xff) +#define ADI_AXI_PCORE_VER_MINOR(version) (((version) >> 8) & 0xff) +#define ADI_AXI_PCORE_VER_PATCH(version) ((version) & 0xff) + +/* register map */ +#define ADI_REG_RSTN 0x0080 +#define ADI_REG_PWM_WIDTH 0x0084 +#define ADI_REG_TACH_PERIOD 0x0088 +#define ADI_REG_TACH_TOLERANCE 0x008c +#define ADI_REG_PWM_PERIOD 0x00c0 +#define ADI_REG_TACH_MEASUR 0x00c4 +#define ADI_REG_TEMPERATURE 0x00c8 + +#define ADI_REG_IRQ_MASK 0x0040 +#define ADI_REG_IRQ_PENDING 0x0044 +#define ADI_REG_IRQ_SRC 0x0048 + +/* IRQ sources */ +#define ADI_IRQ_SRC_PWM_CHANGED BIT(0) +#define ADI_IRQ_SRC_TACH_ERR BIT(1) +#define ADI_IRQ_SRC_TEMP_INCREASE BIT(2) +#define ADI_IRQ_SRC_NEW_MEASUR BIT(3) +#define ADI_IRQ_SRC_MASK GENMASK(3, 0) +#define ADI_IRQ_MASK_OUT_ALL 0xFFFFFFFFU + +#define SYSFS_PWM_MAX 255 + +struct axi_fan_control_data { + void __iomem *base; + struct device *hdev; + unsigned long clk_rate; + int irq; + /* pulses per revolution */ + u32 ppr; + bool hw_pwm_req; + bool update_tacho_params; + u8 fan_fault; +}; + +static inline void axi_iowrite(const u32 val, const u32 reg, + const struct axi_fan_control_data *ctl) +{ + iowrite32(val, ctl->base + reg); +} + +static inline u32 axi_ioread(const u32 reg, + const struct axi_fan_control_data *ctl) +{ + return ioread32(ctl->base + reg); +} + +static long axi_fan_control_get_pwm_duty(const struct axi_fan_control_data *ctl) +{ + u32 pwm_width = axi_ioread(ADI_REG_PWM_WIDTH, ctl); + u32 pwm_period = axi_ioread(ADI_REG_PWM_PERIOD, ctl); + /* + * PWM_PERIOD is a RO register set by the core. It should never be 0. + * For now we are trusting the HW... + */ + return DIV_ROUND_CLOSEST(pwm_width * SYSFS_PWM_MAX, pwm_period); +} + +static int axi_fan_control_set_pwm_duty(const long val, + struct axi_fan_control_data *ctl) +{ + u32 pwm_period = axi_ioread(ADI_REG_PWM_PERIOD, ctl); + u32 new_width; + long __val = clamp_val(val, 0, SYSFS_PWM_MAX); + + new_width = DIV_ROUND_CLOSEST(__val * pwm_period, SYSFS_PWM_MAX); + + axi_iowrite(new_width, ADI_REG_PWM_WIDTH, ctl); + + return 0; +} + +static long axi_fan_control_get_fan_rpm(const struct axi_fan_control_data *ctl) +{ + const u32 tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl); + + if (tach == 0) + /* should we return error, EAGAIN maybe? */ + return 0; + /* + * The tacho period should be: + * TACH = 60/(ppr * rpm), where rpm is revolutions per second + * and ppr is pulses per revolution. + * Given the tacho period, we can multiply it by the input clock + * so that we know how many clocks we need to have this period. + * From this, we can derive the RPM value. + */ + return DIV_ROUND_CLOSEST(60 * ctl->clk_rate, ctl->ppr * tach); +} + +static int axi_fan_control_read_temp(struct device *dev, u32 attr, long *val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + long raw_temp; + + switch (attr) { + case hwmon_temp_input: + raw_temp = axi_ioread(ADI_REG_TEMPERATURE, ctl); + /* + * The formula for the temperature is: + * T = (ADC * 501.3743 / 2^bits) - 273.6777 + * It's multiplied by 1000 to have millidegrees as + * specified by the hwmon sysfs interface. + */ + *val = ((raw_temp * 501374) >> 16) - 273677; + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read_fan(struct device *dev, u32 attr, long *val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_fan_fault: + *val = ctl->fan_fault; + /* clear it now */ + ctl->fan_fault = 0; + return 0; + case hwmon_fan_input: + *val = axi_fan_control_get_fan_rpm(ctl); + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read_pwm(struct device *dev, u32 attr, long *val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_pwm_input: + *val = axi_fan_control_get_pwm_duty(ctl); + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_write_pwm(struct device *dev, u32 attr, long val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_pwm_input: + return axi_fan_control_set_pwm_duty(val, ctl); + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_fan: + *str = "FAN"; + return 0; + case hwmon_temp: + *str = "SYSMON4"; + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return axi_fan_control_read_fan(dev, attr, val); + case hwmon_pwm: + return axi_fan_control_read_pwm(dev, attr, val); + case hwmon_temp: + return axi_fan_control_read_temp(dev, attr, val); + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_write(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + return axi_fan_control_write_pwm(dev, attr, val); + default: + return -ENOTSUPP; + } +} + +static umode_t axi_fan_control_fan_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + case hwmon_fan_label: + return 0444; + default: + return 0; + } +} + +static umode_t axi_fan_control_pwm_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + return 0; + } +} + +static umode_t axi_fan_control_temp_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + default: + return 0; + } +} + +static umode_t axi_fan_control_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return axi_fan_control_fan_is_visible(attr); + case hwmon_pwm: + return axi_fan_control_pwm_is_visible(attr); + case hwmon_temp: + return axi_fan_control_temp_is_visible(attr); + default: + return 0; + } +} + +/* + * This core has two main ways of changing the PWM duty cycle. It is done, + * either by a request from userspace (writing on pwm1_input) or by the + * core itself. When the change is done by the core, it will use predefined + * parameters to evaluate the tach signal and, on that case we cannot set them. + * On the other hand, when the request is done by the user, with some arbitrary + * value that the core does not now about, we have to provide the tach + * parameters so that, the core can evaluate the signal. On the IRQ handler we + * distinguish this by using the ADI_IRQ_SRC_TEMP_INCREASE interrupt. This tell + * us that the CORE requested a new duty cycle. After this, there is 5s delay + * on which the core waits for the fan rotation speed to stabilize. After this + * we get ADI_IRQ_SRC_PWM_CHANGED irq where we will decide if we need to set + * the tach parameters or not on the next tach measurement cycle (corresponding + * already to the ney duty cycle) based on the %ctl->hw_pwm_req flag. + */ +static irqreturn_t axi_fan_control_irq_handler(int irq, void *data) +{ + struct axi_fan_control_data *ctl = (struct axi_fan_control_data *)data; + u32 irq_pending = axi_ioread(ADI_REG_IRQ_PENDING, ctl); + u32 clear_mask; + + if (irq_pending & ADI_IRQ_SRC_NEW_MEASUR) { + if (ctl->update_tacho_params) { + u32 new_tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl); + + /* get 25% tolerance */ + u32 tach_tol = DIV_ROUND_CLOSEST(new_tach * 25, 100); + /* set new tacho parameters */ + axi_iowrite(new_tach, ADI_REG_TACH_PERIOD, ctl); + axi_iowrite(tach_tol, ADI_REG_TACH_TOLERANCE, ctl); + ctl->update_tacho_params = false; + } + } + + if (irq_pending & ADI_IRQ_SRC_PWM_CHANGED) { + /* + * if the pwm changes on behalf of software, + * we need to provide new tacho parameters to the core. + * Wait for the next measurement for that... + */ + if (!ctl->hw_pwm_req) { + ctl->update_tacho_params = true; + } else { + ctl->hw_pwm_req = false; + sysfs_notify(&ctl->hdev->kobj, NULL, "pwm1"); + } + } + + if (irq_pending & ADI_IRQ_SRC_TEMP_INCREASE) + /* hardware requested a new pwm */ + ctl->hw_pwm_req = true; + + if (irq_pending & ADI_IRQ_SRC_TACH_ERR) + ctl->fan_fault = 1; + + /* clear all interrupts */ + clear_mask = irq_pending & ADI_IRQ_SRC_MASK; + axi_iowrite(clear_mask, ADI_REG_IRQ_PENDING, ctl); + + return IRQ_HANDLED; +} + +static int axi_fan_control_init(struct axi_fan_control_data *ctl, + const struct device_node *np) +{ + int ret; + + /* get fan pulses per revolution */ + ret = of_property_read_u32(np, "pulses-per-revolution", &ctl->ppr); + if (ret) + return ret; + + /* 1, 2 and 4 are the typical and accepted values */ + if (ctl->ppr != 1 && ctl->ppr != 2 && ctl->ppr != 4) + return -EINVAL; + /* + * Enable all IRQs + */ + axi_iowrite(ADI_IRQ_MASK_OUT_ALL & + ~(ADI_IRQ_SRC_NEW_MEASUR | ADI_IRQ_SRC_TACH_ERR | + ADI_IRQ_SRC_PWM_CHANGED | ADI_IRQ_SRC_TEMP_INCREASE), + ADI_REG_IRQ_MASK, ctl); + + /* bring the device out of reset */ + axi_iowrite(0x01, ADI_REG_RSTN, ctl); + + return ret; +} + +static const struct hwmon_channel_info *axi_fan_control_info[] = { + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), + NULL +}; + +static const struct hwmon_ops axi_fan_control_hwmon_ops = { + .is_visible = axi_fan_control_is_visible, + .read = axi_fan_control_read, + .write = axi_fan_control_write, + .read_string = axi_fan_control_read_labels, +}; + +static const struct hwmon_chip_info axi_chip_info = { + .ops = &axi_fan_control_hwmon_ops, + .info = axi_fan_control_info, +}; + +static const u32 version_1_0_0 = ADI_AXI_PCORE_VER(1, 0, 'a'); + +static const struct of_device_id axi_fan_control_of_match[] = { + { .compatible = "adi,axi-fan-control-1.00.a", + .data = (void *)&version_1_0_0}, + {}, +}; +MODULE_DEVICE_TABLE(of, axi_fan_control_of_match); + +static int axi_fan_control_probe(struct platform_device *pdev) +{ + struct axi_fan_control_data *ctl; + struct clk *clk; + const struct of_device_id *id; + const char *name = "axi_fan_control"; + u32 version; + int ret; + + id = of_match_node(axi_fan_control_of_match, pdev->dev.of_node); + if (!id) + return -EINVAL; + + ctl = devm_kzalloc(&pdev->dev, sizeof(*ctl), GFP_KERNEL); + if (!ctl) + return -ENOMEM; + + ctl->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctl->base)) + return PTR_ERR(ctl->base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "clk_get failed with %ld\n", PTR_ERR(clk)); + return PTR_ERR(clk); + } + + ctl->clk_rate = clk_get_rate(clk); + if (!ctl->clk_rate) + return -EINVAL; + + version = axi_ioread(ADI_AXI_REG_VERSION, ctl); + if (ADI_AXI_PCORE_VER_MAJOR(version) != + ADI_AXI_PCORE_VER_MAJOR((*(u32 *)id->data))) { + dev_err(&pdev->dev, "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", + ADI_AXI_PCORE_VER_MAJOR((*(u32 *)id->data)), + ADI_AXI_PCORE_VER_MINOR((*(u32 *)id->data)), + ADI_AXI_PCORE_VER_PATCH((*(u32 *)id->data)), + ADI_AXI_PCORE_VER_MAJOR(version), + ADI_AXI_PCORE_VER_MINOR(version), + ADI_AXI_PCORE_VER_PATCH(version)); + return -ENODEV; + } + + ctl->irq = platform_get_irq(pdev, 0); + if (ctl->irq < 0) + return ctl->irq; + + ret = devm_request_threaded_irq(&pdev->dev, ctl->irq, NULL, + axi_fan_control_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + pdev->driver_override, ctl); + if (ret) { + dev_err(&pdev->dev, "failed to request an irq, %d", ret); + return ret; + } + + ret = axi_fan_control_init(ctl, pdev->dev.of_node); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize device\n"); + return ret; + } + + ctl->hdev = devm_hwmon_device_register_with_info(&pdev->dev, + name, + ctl, + &axi_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(ctl->hdev); +} + +static struct platform_driver axi_fan_control_driver = { + .driver = { + .name = "axi_fan_control_driver", + .of_match_table = axi_fan_control_of_match, + }, + .probe = axi_fan_control_probe, +}; +module_platform_driver(axi_fan_control_driver); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices Fan Control HDL CORE driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index d05ab713566d..a4ec85207782 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -219,7 +219,7 @@ struct aem_read_sensor_req { struct aem_read_sensor_resp { struct aem_iana_id id; - u8 bytes[0]; + u8 bytes[]; } __packed; /* Data structures to talk to the IPMI layer */ diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 0e525cfbdfc5..a750647e66a4 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -186,7 +186,7 @@ static void make_sensor_label(struct device_node *np, u32 id; size_t n; - n = snprintf(sdata->label, sizeof(sdata->label), "%s", label); + n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label); /* * Core temp pretty print @@ -199,11 +199,11 @@ static void make_sensor_label(struct device_node *np, * The digital thermal sensors are associated * with a core. */ - n += snprintf(sdata->label + n, + n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, " %d", cpuid); else - n += snprintf(sdata->label + n, + n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, " phy%d", id); } @@ -211,7 +211,7 @@ static void make_sensor_label(struct device_node *np, * Membuffer pretty print */ if (!of_property_read_u32(np, "ibm,chip-id", &id)) - n += snprintf(sdata->label + n, sizeof(sdata->label) - n, + n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, " %d", id & 0xffff); } diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index e39354ffe973..3f37d5d81fe4 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -96,13 +96,20 @@ struct k10temp_data { void (*read_tempreg)(struct pci_dev *pdev, u32 *regval); int temp_offset; u32 temp_adjust_mask; - bool show_tdie; - u32 show_tccd; + u32 show_temp; u32 svi_addr[2]; + bool is_zen; bool show_current; int cfactor[2]; }; +#define TCTL_BIT 0 +#define TDIE_BIT 1 +#define TCCD_BIT(x) ((x) + 2) + +#define HAVE_TEMP(d, channel) ((d)->show_temp & BIT(channel)) +#define HAVE_TDIE(d) HAVE_TEMP(d, TDIE_BIT) + struct tctl_offset { u8 model; char const *id; @@ -180,8 +187,8 @@ static long get_raw_temp(struct k10temp_data *data) } const char *k10temp_temp_label[] = { - "Tdie", "Tctl", + "Tdie", "Tccd1", "Tccd2", "Tccd3", @@ -269,13 +276,13 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel, switch (attr) { case hwmon_temp_input: switch (channel) { - case 0: /* Tdie */ - *val = get_raw_temp(data) - data->temp_offset; + case 0: /* Tctl */ + *val = get_raw_temp(data); if (*val < 0) *val = 0; break; - case 1: /* Tctl */ - *val = get_raw_temp(data); + case 1: /* Tdie */ + *val = get_raw_temp(data) - data->temp_offset; if (*val < 0) *val = 0; break; @@ -333,23 +340,11 @@ static umode_t k10temp_is_visible(const void *_data, case hwmon_temp: switch (attr) { case hwmon_temp_input: - switch (channel) { - case 0: /* Tdie, or Tctl if we don't show it */ - break; - case 1: /* Tctl */ - if (!data->show_tdie) - return 0; - break; - case 2 ... 9: /* Tccd{1-8} */ - if (!(data->show_tccd & BIT(channel - 2))) - return 0; - break; - default: + if (!HAVE_TEMP(data, channel)) return 0; - } break; case hwmon_temp_max: - if (channel || data->show_tdie) + if (channel || data->is_zen) return 0; break; case hwmon_temp_crit: @@ -368,20 +363,9 @@ static umode_t k10temp_is_visible(const void *_data, return 0; break; case hwmon_temp_label: - /* No labels if we don't show the die temperature */ - if (!data->show_tdie) - return 0; - switch (channel) { - case 0: /* Tdie */ - case 1: /* Tctl */ - break; - case 2 ... 9: /* Tccd{1-8} */ - if (!(data->show_tccd & BIT(channel - 2))) - return 0; - break; - default: + /* Show temperature labels only on Zen CPUs */ + if (!data->is_zen || !HAVE_TEMP(data, channel)) return 0; - } break; default: return 0; @@ -480,7 +464,7 @@ static void k10temp_init_debugfs(struct k10temp_data *data) char name[32]; /* Only show debugfs data for Family 17h/18h CPUs */ - if (!data->show_tdie) + if (!data->is_zen) return; scnprintf(name, sizeof(name), "k10temp-%s", pci_name(data->pdev)); @@ -546,7 +530,7 @@ static void k10temp_get_ccd_support(struct pci_dev *pdev, amd_smn_read(amd_pci_dev_to_node_id(pdev), F17H_M70H_CCD_TEMP(i), ®val); if (regval & F17H_M70H_CCD_TEMP_VALID) - data->show_tccd |= BIT(i); + data->show_temp |= BIT(TCCD_BIT(i)); } } @@ -573,6 +557,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; data->pdev = pdev; + data->show_temp |= BIT(TCTL_BIT); /* Always show Tctl */ if (boot_cpu_data.x86 == 0x15 && ((boot_cpu_data.x86_model & 0xf0) == 0x60 || @@ -582,7 +567,8 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) } else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { data->temp_adjust_mask = CUR_TEMP_RANGE_SEL_MASK; data->read_tempreg = read_tempreg_nb_f17; - data->show_tdie = true; + data->show_temp |= BIT(TDIE_BIT); /* show Tdie */ + data->is_zen = true; switch (boot_cpu_data.x86_model) { case 0x1: /* Zen */ diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 1eeb9d7de2a0..733c48bf6c98 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -262,10 +262,20 @@ static int lm73_detect(struct i2c_client *new_client, return 0; } +static const struct of_device_id lm73_of_match[] = { + { + .compatible = "ti,lm73", + }, + { }, +}; + +MODULE_DEVICE_TABLE(of, lm73_of_match); + static struct i2c_driver lm73_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm73", + .of_match_table = lm73_of_match, }, .probe = lm73_probe, .id_table = lm73_ids, diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 281c81edabc6..1f5743d68984 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -7,6 +7,11 @@ * * Copyright (c) 2019 Advantech * Author: Amy.Shih <amy.shih@advantech.com.tw> + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp #dts chip ID + * nct7904d 20 12 4 5 8 0xc5 */ #include <linux/module.h> @@ -820,6 +825,10 @@ static const struct hwmon_channel_info *nct7904_info[] = { HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM), HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, @@ -853,6 +862,18 @@ static const struct hwmon_channel_info *nct7904_info[] = { HWMON_T_CRIT_HYST, HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | HWMON_T_CRIT_HYST), NULL }; diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index a9ea06204767..de12a565006d 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -92,10 +92,10 @@ config SENSORS_IRPS5401 be called irps5401. config SENSORS_ISL68137 - tristate "Intersil ISL68137" + tristate "Renesas Digital Multiphase Voltage Regulators" help - If you say yes here you get hardware monitoring support for Intersil - ISL68137. + If you say yes here you get hardware monitoring support for Renesas + digital multiphase voltage regulators. This driver can also be built as a module. If so, the module will be called isl68137. @@ -113,8 +113,8 @@ config SENSORS_LTC2978 tristate "Linear Technologies LTC2978 and compatibles" help If you say yes here you get hardware monitoring support for Linear - Technology LTC2974, LTC2975, LTC2977, LTC2978, LTC2980, LTC3880, - LTC3883, LTC3886, LTC3887, LTCM2987, LTM4675, and LTM4676. + Technology LTC2972, LTC2974, LTC2975, LTC2977, LTC2978, LTC2979, + LTC2980, and LTM2987. This driver can also be built as a module. If so, the module will be called ltc2978. @@ -123,9 +123,10 @@ config SENSORS_LTC2978_REGULATOR bool "Regulator support for LTC2978 and compatibles" depends on SENSORS_LTC2978 && REGULATOR help - If you say yes here you get regulator support for Linear - Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, LTM4676 - and LTM4686. + If you say yes here you get regulator support for Linear Technology + LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7880, + LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686, + and LTM4700. config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" @@ -209,10 +210,10 @@ config SENSORS_TPS40422 be called tps40422. config SENSORS_TPS53679 - tristate "TI TPS53679, TPS53688" + tristate "TI TPS53647, TPS53667, TPS53679, TPS53681, TPS53688" help If you say yes here you get hardware monitoring support for TI - TPS53679, TPS53688 + TPS53647, TPS53667, TPS53679, TPS53681, and TPS53688. This driver can also be built as a module. If so, the module will be called tps53679. diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 5caa37fbfc18..e25f541227da 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -226,7 +226,8 @@ static int adm1275_write_pmon_config(const struct adm1275_data *data, return ret; } -static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) +static int adm1275_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct adm1275_data *data = to_adm1275_data(info); @@ -239,58 +240,68 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) case PMBUS_IOUT_UC_FAULT_LIMIT: if (!data->have_uc_fault) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_IOUT_WARN2_LIMIT); break; case PMBUS_IOUT_OC_FAULT_LIMIT: if (!data->have_oc_fault) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_IOUT_WARN2_LIMIT); break; case PMBUS_VOUT_OV_WARN_LIMIT: if (data->have_vout) return -ENODATA; - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, ADM1075_VAUX_OV_WARN_LIMIT); break; case PMBUS_VOUT_UV_WARN_LIMIT: if (data->have_vout) return -ENODATA; - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, ADM1075_VAUX_UV_WARN_LIMIT); break; case PMBUS_READ_VOUT: if (data->have_vout) return -ENODATA; - ret = pmbus_read_word_data(client, 0, ADM1075_READ_VAUX); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1075_READ_VAUX); break; case PMBUS_VIRT_READ_IOUT_MIN: if (!data->have_iout_min) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1293_IOUT_MIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1293_IOUT_MIN); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_PEAK_IOUT); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_PEAK_VOUT); break; case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_PEAK_VIN); break; case PMBUS_VIRT_READ_PIN_MIN: if (!data->have_pin_min) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1293_PIN_MIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1293_PIN_MIN); break; case PMBUS_VIRT_READ_PIN_MAX: if (!data->have_pin_max) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1276_PEAK_PIN); break; case PMBUS_VIRT_READ_TEMP_MAX: if (!data->have_temp_max) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1278_PEAK_TEMP); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1278_PEAK_TEMP); break; case PMBUS_VIRT_RESET_IOUT_HISTORY: case PMBUS_VIRT_RESET_VOUT_HISTORY: diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 3795fe55b84f..7d300f2f338d 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -33,9 +33,12 @@ #define CFFPS_INPUT_HISTORY_CMD 0xD6 #define CFFPS_INPUT_HISTORY_SIZE 100 +#define CFFPS_CCIN_REVISION GENMASK(7, 0) +#define CFFPS_CCIN_REVISION_LEGACY 0xde #define CFFPS_CCIN_VERSION GENMASK(15, 8) #define CFFPS_CCIN_VERSION_1 0x2b #define CFFPS_CCIN_VERSION_2 0x2e +#define CFFPS_CCIN_VERSION_3 0x51 /* STATUS_MFR_SPECIFIC bits */ #define CFFPS_MFR_FAN_FAULT BIT(0) @@ -148,7 +151,7 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf, struct ibm_cffps *psu = to_psu(idxp, idx); char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); switch (idx) { case CFFPS_DEBUGFS_INPUT_HISTORY: @@ -247,7 +250,7 @@ static ssize_t ibm_cffps_debugfs_write(struct file *file, switch (idx) { case CFFPS_DEBUGFS_ON_OFF_CONFIG: - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); rc = simple_write_to_buffer(&data, 1, ppos, buf, count); if (rc <= 0) @@ -325,13 +328,13 @@ static int ibm_cffps_read_byte_data(struct i2c_client *client, int page, } static int ibm_cffps_read_word_data(struct i2c_client *client, int page, - int reg) + int phase, int reg) { int rc, mfr; switch (reg) { case PMBUS_STATUS_WORD: - rc = pmbus_read_word_data(client, page, reg); + rc = pmbus_read_word_data(client, page, phase, reg); if (rc < 0) return rc; @@ -348,7 +351,8 @@ static int ibm_cffps_read_word_data(struct i2c_client *client, int page, rc |= PB_STATUS_OFF; break; case PMBUS_VIRT_READ_VMON: - rc = pmbus_read_word_data(client, page, CFFPS_12VCS_VOUT_CMD); + rc = pmbus_read_word_data(client, page, phase, + CFFPS_12VCS_VOUT_CMD); break; default: rc = -ENODATA; @@ -379,7 +383,7 @@ static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, dev_dbg(&psu->client->dev, "LED brightness set: %d. Command: %d.\n", brightness, next_led_state); - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, next_led_state); @@ -401,7 +405,7 @@ static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev, dev_dbg(&psu->client->dev, "LED blink set.\n"); - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, CFFPS_LED_BLINK); @@ -485,11 +489,14 @@ static int ibm_cffps_probe(struct i2c_client *client, vs = (enum versions)id->driver_data; if (vs == cffps_unknown) { + u16 ccin_revision = 0; u16 ccin_version = CFFPS_CCIN_VERSION_1; int ccin = i2c_smbus_read_word_swapped(client, CFFPS_CCIN_CMD); - if (ccin > 0) + if (ccin > 0) { + ccin_revision = FIELD_GET(CFFPS_CCIN_REVISION, ccin); ccin_version = FIELD_GET(CFFPS_CCIN_VERSION, ccin); + } switch (ccin_version) { default: @@ -499,6 +506,12 @@ static int ibm_cffps_probe(struct i2c_client *client, case CFFPS_CCIN_VERSION_2: vs = cffps2; break; + case CFFPS_CCIN_VERSION_3: + if (ccin_revision == CFFPS_CCIN_REVISION_LEGACY) + vs = cffps1; + else + vs = cffps2; + break; } /* Set the client name to include the version number. */ diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 0d878bcd6d26..3eea3e006a96 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -21,37 +21,42 @@ #define IR35221_MFR_IOUT_VALLEY 0xcb #define IR35221_MFR_TEMP_VALLEY 0xcc -static int ir35221_read_word_data(struct i2c_client *client, int page, int reg) +static int ir35221_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_VIN_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_VIN_PEAK); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_VOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_IOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_TEMP_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_TEMP_PEAK); break; case PMBUS_VIRT_READ_VIN_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_VIN_VALLEY); break; case PMBUS_VIRT_READ_VOUT_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_VOUT_VALLEY); break; case PMBUS_VIRT_READ_IOUT_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_IOUT_VALLEY); break; case PMBUS_VIRT_READ_TEMP_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_TEMP_VALLEY); break; default: diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 515596c92fe1..4d2315208bb5 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Hardware monitoring driver for Intersil ISL68137 + * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators * * Copyright (c) 2017 Google Inc + * Copyright (c) 2020 Renesas Electronics America * */ @@ -14,9 +15,19 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/sysfs.h> + #include "pmbus.h" #define ISL68137_VOUT_AVS 0x30 +#define RAA_DMPVR2_READ_VMON 0xc8 + +enum versions { + isl68137, + raa_dmpvr2_1rail, + raa_dmpvr2_2rail, + raa_dmpvr2_3rail, + raa_dmpvr2_hv, +}; static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, int page, @@ -49,7 +60,8 @@ static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client, * enabling AVS control is the workaround. */ if (op_val == ISL68137_VOUT_AVS) { - rc = pmbus_read_word_data(client, page, PMBUS_VOUT_COMMAND); + rc = pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_COMMAND); if (rc < 0) return rc; @@ -98,13 +110,31 @@ static const struct attribute_group enable_group = { .attrs = enable_attrs, }; -static const struct attribute_group *attribute_groups[] = { +static const struct attribute_group *isl68137_attribute_groups[] = { &enable_group, NULL, }; -static struct pmbus_driver_info isl68137_info = { - .pages = 2, +static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, page, phase, + RAA_DMPVR2_READ_VMON); + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info raa_dmpvr_info = { + .pages = 3, .format[PSC_VOLTAGE_IN] = direct, .format[PSC_VOLTAGE_OUT] = direct, .format[PSC_CURRENT_IN] = direct, @@ -113,7 +143,7 @@ static struct pmbus_driver_info isl68137_info = { .format[PSC_TEMPERATURE] = direct, .m[PSC_VOLTAGE_IN] = 1, .b[PSC_VOLTAGE_IN] = 0, - .R[PSC_VOLTAGE_IN] = 3, + .R[PSC_VOLTAGE_IN] = 2, .m[PSC_VOLTAGE_OUT] = 1, .b[PSC_VOLTAGE_OUT] = 0, .R[PSC_VOLTAGE_OUT] = 3, @@ -133,24 +163,76 @@ static struct pmbus_driver_info isl68137_info = { | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT - | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, - .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT - | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, - .groups = attribute_groups, + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_VMON, + .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, + .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, }; static int isl68137_probe(struct i2c_client *client, const struct i2c_device_id *id) { - return pmbus_do_probe(client, id, &isl68137_info); + struct pmbus_driver_info *info; + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + memcpy(info, &raa_dmpvr_info, sizeof(*info)); + + switch (id->driver_data) { + case isl68137: + info->pages = 2; + info->R[PSC_VOLTAGE_IN] = 3; + info->func[0] &= ~PMBUS_HAVE_VMON; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT; + info->groups = isl68137_attribute_groups; + break; + case raa_dmpvr2_1rail: + info->pages = 1; + info->read_word_data = raa_dmpvr2_read_word_data; + break; + case raa_dmpvr2_2rail: + info->pages = 2; + info->read_word_data = raa_dmpvr2_read_word_data; + break; + case raa_dmpvr2_3rail: + info->read_word_data = raa_dmpvr2_read_word_data; + break; + case raa_dmpvr2_hv: + info->pages = 1; + info->R[PSC_VOLTAGE_IN] = 1; + info->m[PSC_VOLTAGE_OUT] = 2; + info->R[PSC_VOLTAGE_OUT] = 2; + info->m[PSC_CURRENT_IN] = 2; + info->m[PSC_POWER] = 2; + info->R[PSC_POWER] = -1; + info->read_word_data = raa_dmpvr2_read_word_data; + break; + default: + return -ENODEV; + } + + return pmbus_do_probe(client, id, info); } -static const struct i2c_device_id isl68137_id[] = { - {"isl68137", 0}, +static const struct i2c_device_id raa_dmpvr_id[] = { + {"isl68137", isl68137}, + {"raa_dmpvr2_1rail", raa_dmpvr2_1rail}, + {"raa_dmpvr2_2rail", raa_dmpvr2_2rail}, + {"raa_dmpvr2_3rail", raa_dmpvr2_3rail}, + {"raa_dmpvr2_hv", raa_dmpvr2_hv}, {} }; -MODULE_DEVICE_TABLE(i2c, isl68137_id); +MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); /* This is the driver that will be inserted */ static struct i2c_driver isl68137_driver = { @@ -159,11 +241,11 @@ static struct i2c_driver isl68137_driver = { }, .probe = isl68137_probe, .remove = pmbus_do_remove, - .id_table = isl68137_id, + .id_table = raa_dmpvr_id, }; module_i2c_driver(isl68137_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); -MODULE_DESCRIPTION("PMBus driver for Intersil ISL68137"); +MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 05fce86f1f81..9e4cf0800186 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -211,7 +211,8 @@ struct lm25066_data { #define to_lm25066_data(x) container_of(x, struct lm25066_data, info) -static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) +static int lm25066_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct lm25066_data *data = to_lm25066_data(info); @@ -219,7 +220,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_VMON: - ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX); + ret = pmbus_read_word_data(client, 0, 0xff, LM25066_READ_VAUX); if (ret < 0) break; /* Adjust returned value to match VIN coefficients */ @@ -244,33 +245,40 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) } break; case PMBUS_READ_IIN: - ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_MFR_READ_IIN); break; case PMBUS_READ_PIN: - ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_MFR_READ_PIN); break; case PMBUS_IIN_OC_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25066_MFR_IIN_OC_WARN_LIMIT); break; case PMBUS_PIN_OP_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25066_MFR_PIN_OP_WARN_LIMIT); break; case PMBUS_VIRT_READ_VIN_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_VIN); break; case PMBUS_VIRT_READ_VOUT_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_VOUT); break; case PMBUS_VIRT_READ_IIN_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_IIN); break; case PMBUS_VIRT_READ_PIN_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_PIN); break; case PMBUS_VIRT_READ_PIN_MAX: - ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_PIN_PEAK); break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = 0; @@ -288,13 +296,14 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) +static int lm25056_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_VMON_UV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25056_VAUX_UV_WARN_LIMIT); if (ret < 0) break; @@ -302,7 +311,7 @@ static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) ret = DIV_ROUND_CLOSEST(ret * 293, 6140); break; case PMBUS_VIRT_VMON_OV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25056_VAUX_OV_WARN_LIMIT); if (ret < 0) break; @@ -310,7 +319,7 @@ static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) ret = DIV_ROUND_CLOSEST(ret * 293, 6140); break; default: - ret = lm25066_read_word_data(client, page, reg); + ret = lm25066_read_word_data(client, page, phase, reg); break; } return ret; diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index a91ed01abb68..7b0e6b37e247 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -19,8 +19,15 @@ #include <linux/regulator/driver.h> #include "pmbus.h" -enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, - ltc3883, ltc3886, ltc3887, ltm2987, ltm4675, ltm4676, ltm4686 }; +enum chips { + /* Managers */ + ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, + /* Controllers */ + ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7880, + /* Modules */ + ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, + ltm4700, +}; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd @@ -43,9 +50,10 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 -/* LTC3883 and LTC3886 only */ +/* LTC3883, LTC3884, LTC3886, LTC3889 and LTC7880 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 + /* LTC2975 only */ #define LTC2975_MFR_IIN_PEAK 0xc4 #define LTC2975_MFR_IIN_MIN 0xc5 @@ -54,27 +62,41 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, #define LTC2978_ID_MASK 0xfff0 +#define LTC2972_ID 0x0310 #define LTC2974_ID 0x0210 #define LTC2975_ID 0x0220 #define LTC2977_ID 0x0130 #define LTC2978_ID_REV1 0x0110 /* Early revision */ #define LTC2978_ID_REV2 0x0120 +#define LTC2979_ID_A 0x8060 +#define LTC2979_ID_B 0x8070 #define LTC2980_ID_A 0x8030 /* A/B for two die IDs */ #define LTC2980_ID_B 0x8040 #define LTC3880_ID 0x4020 #define LTC3882_ID 0x4200 #define LTC3882_ID_D1 0x4240 /* Dash 1 */ #define LTC3883_ID 0x4300 +#define LTC3884_ID 0x4C00 #define LTC3886_ID 0x4600 #define LTC3887_ID 0x4700 #define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ #define LTM2987_ID_B 0x8020 +#define LTC3889_ID 0x4900 +#define LTC7880_ID 0x49E0 +#define LTM4664_ID 0x4120 #define LTM4675_ID 0x47a0 #define LTM4676_ID_REV1 0x4400 #define LTM4676_ID_REV2 0x4480 #define LTM4676A_ID 0x47e0 +#define LTM4677_ID_REV1 0x47B0 +#define LTM4677_ID_REV2 0x47D0 +#define LTM4678_ID_REV1 0x4100 +#define LTM4678_ID_REV2 0x4110 +#define LTM4680_ID 0x4140 #define LTM4686_ID 0x4770 +#define LTM4700_ID 0x4130 +#define LTC2972_NUM_PAGES 2 #define LTC2974_NUM_PAGES 4 #define LTC2978_NUM_PAGES 8 #define LTC3880_NUM_PAGES 2 @@ -151,7 +173,8 @@ static int ltc_wait_ready(struct i2c_client *client) return -ETIMEDOUT; } -static int ltc_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc_read_word_data(struct i2c_client *client, int page, int phase, + int reg) { int ret; @@ -159,7 +182,7 @@ static int ltc_read_word_data(struct i2c_client *client, int page, int reg) if (ret < 0) return ret; - return pmbus_read_word_data(client, page, reg); + return pmbus_read_word_data(client, page, 0xff, reg); } static int ltc_read_byte_data(struct i2c_client *client, int page, int reg) @@ -202,7 +225,7 @@ static int ltc_get_max(struct ltc2978_data *data, struct i2c_client *client, { int ret; - ret = ltc_read_word_data(client, page, reg); + ret = ltc_read_word_data(client, page, 0xff, reg); if (ret >= 0) { if (lin11_to_val(ret) > lin11_to_val(*pmax)) *pmax = ret; @@ -216,7 +239,7 @@ static int ltc_get_min(struct ltc2978_data *data, struct i2c_client *client, { int ret; - ret = ltc_read_word_data(client, page, reg); + ret = ltc_read_word_data(client, page, 0xff, reg); if (ret >= 0) { if (lin11_to_val(ret) < lin11_to_val(*pmin)) *pmin = ret; @@ -238,7 +261,8 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, &data->vin_max); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); + ret = ltc_read_word_data(client, page, 0xff, + LTC2978_MFR_VOUT_PEAK); if (ret >= 0) { /* * VOUT is 16 bit unsigned with fixed exponent, @@ -269,7 +293,8 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, return ret; } -static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2978_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -281,7 +306,8 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) &data->vin_min); break; case PMBUS_VIRT_READ_VOUT_MIN: - ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); + ret = ltc_read_word_data(client, page, phase, + LTC2978_MFR_VOUT_MIN); if (ret >= 0) { /* * VOUT_MIN is known to not be supported on some lots @@ -314,7 +340,8 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2974_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -333,13 +360,14 @@ static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) ret = 0; break; default: - ret = ltc2978_read_word_data(client, page, reg); + ret = ltc2978_read_word_data(client, page, phase, reg); break; } return ret; } -static int ltc2975_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2975_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -367,13 +395,14 @@ static int ltc2975_read_word_data(struct i2c_client *client, int page, int reg) ret = 0; break; default: - ret = ltc2978_read_word_data(client, page, reg); + ret = ltc2978_read_word_data(client, page, phase, reg); break; } return ret; } -static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc3880_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -405,7 +434,8 @@ static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc3883_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -420,7 +450,7 @@ static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) ret = 0; break; default: - ret = ltc3880_read_word_data(client, page, reg); + ret = ltc3880_read_word_data(client, page, phase, reg); break; } return ret; @@ -492,20 +522,30 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { + {"ltc2972", ltc2972}, {"ltc2974", ltc2974}, {"ltc2975", ltc2975}, {"ltc2977", ltc2977}, {"ltc2978", ltc2978}, + {"ltc2979", ltc2979}, {"ltc2980", ltc2980}, {"ltc3880", ltc3880}, {"ltc3882", ltc3882}, {"ltc3883", ltc3883}, + {"ltc3884", ltc3884}, {"ltc3886", ltc3886}, {"ltc3887", ltc3887}, + {"ltc3889", ltc3889}, + {"ltc7880", ltc7880}, {"ltm2987", ltm2987}, + {"ltm4664", ltm4664}, {"ltm4675", ltm4675}, {"ltm4676", ltm4676}, + {"ltm4677", ltm4677}, + {"ltm4678", ltm4678}, + {"ltm4680", ltm4680}, {"ltm4686", ltm4686}, + {"ltm4700", ltm4700}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); @@ -555,7 +595,9 @@ static int ltc2978_get_id(struct i2c_client *client) chip_id &= LTC2978_ID_MASK; - if (chip_id == LTC2974_ID) + if (chip_id == LTC2972_ID) + return ltc2972; + else if (chip_id == LTC2974_ID) return ltc2974; else if (chip_id == LTC2975_ID) return ltc2975; @@ -563,6 +605,8 @@ static int ltc2978_get_id(struct i2c_client *client) return ltc2977; else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) return ltc2978; + else if (chip_id == LTC2979_ID_A || chip_id == LTC2979_ID_B) + return ltc2979; else if (chip_id == LTC2980_ID_A || chip_id == LTC2980_ID_B) return ltc2980; else if (chip_id == LTC3880_ID) @@ -571,19 +615,35 @@ static int ltc2978_get_id(struct i2c_client *client) return ltc3882; else if (chip_id == LTC3883_ID) return ltc3883; + else if (chip_id == LTC3884_ID) + return ltc3884; else if (chip_id == LTC3886_ID) return ltc3886; else if (chip_id == LTC3887_ID) return ltc3887; + else if (chip_id == LTC3889_ID) + return ltc3889; + else if (chip_id == LTC7880_ID) + return ltc7880; else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B) return ltm2987; + else if (chip_id == LTM4664_ID) + return ltm4664; else if (chip_id == LTM4675_ID) return ltm4675; else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 || chip_id == LTM4676A_ID) return ltm4676; + else if (chip_id == LTM4677_ID_REV1 || chip_id == LTM4677_ID_REV2) + return ltm4677; + else if (chip_id == LTM4678_ID_REV1 || chip_id == LTM4678_ID_REV2) + return ltm4678; + else if (chip_id == LTM4680_ID) + return ltm4680; else if (chip_id == LTM4686_ID) return ltm4686; + else if (chip_id == LTM4700_ID) + return ltm4700; dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); return -ENODEV; @@ -637,6 +697,19 @@ static int ltc2978_probe(struct i2c_client *client, data->temp2_max = 0x7c00; switch (data->id) { + case ltc2972: + info->read_word_data = ltc2975_read_word_data; + info->pages = LTC2972_NUM_PAGES; + info->func[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + } + break; case ltc2974: info->read_word_data = ltc2974_read_word_data; info->pages = LTC2974_NUM_PAGES; @@ -662,8 +735,10 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; } break; + case ltc2977: case ltc2978: + case ltc2979: case ltc2980: case ltm2987: info->read_word_data = ltc2978_read_word_data; @@ -680,6 +755,7 @@ static int ltc2978_probe(struct i2c_client *client, case ltc3887: case ltm4675: case ltm4676: + case ltm4677: case ltm4686: data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3880_read_word_data; @@ -721,7 +797,14 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; break; + case ltc3884: case ltc3886: + case ltc3889: + case ltc7880: + case ltm4664: + case ltm4678: + case ltm4680: + case ltm4700: data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3883_read_word_data; info->pages = LTC3880_NUM_PAGES; @@ -752,22 +835,33 @@ static int ltc2978_probe(struct i2c_client *client, return pmbus_do_probe(client, id, info); } + #ifdef CONFIG_OF static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,ltc2972" }, { .compatible = "lltc,ltc2974" }, { .compatible = "lltc,ltc2975" }, { .compatible = "lltc,ltc2977" }, { .compatible = "lltc,ltc2978" }, + { .compatible = "lltc,ltc2979" }, { .compatible = "lltc,ltc2980" }, { .compatible = "lltc,ltc3880" }, { .compatible = "lltc,ltc3882" }, { .compatible = "lltc,ltc3883" }, + { .compatible = "lltc,ltc3884" }, { .compatible = "lltc,ltc3886" }, { .compatible = "lltc,ltc3887" }, + { .compatible = "lltc,ltc3889" }, + { .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltm2987" }, + { .compatible = "lltc,ltm4664" }, { .compatible = "lltc,ltm4675" }, { .compatible = "lltc,ltm4676" }, + { .compatible = "lltc,ltm4677" }, + { .compatible = "lltc,ltm4678" }, + { .compatible = "lltc,ltm4680" }, { .compatible = "lltc,ltm4686" }, + { .compatible = "lltc,ltm4700" }, { } }; MODULE_DEVICE_TABLE(of, ltc2978_of_match); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index b83a18a58364..3036263e0a66 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -55,7 +55,7 @@ static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg) * LTC3815 does not support the CLEAR_FAULTS command. * Emulate it by clearing the status register. */ - ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD); + ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_WORD); if (ret > 0) { pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD, ret); @@ -69,25 +69,31 @@ static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg) return ret; } -static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc3815_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_VIN_PEAK); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_TEMP_PEAK); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_IIN_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_IIN_PEAK); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: case PMBUS_VIRT_RESET_VIN_HISTORY: diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index b3e7b8d2e69d..288e93f74c28 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -15,17 +15,18 @@ #define MAX16064_MFR_VOUT_PEAK 0xd4 #define MAX16064_MFR_TEMPERATURE_PEAK 0xd6 -static int max16064_read_word_data(struct i2c_client *client, int page, int reg) +static int max16064_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX16064_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX16064_MFR_TEMPERATURE_PEAK); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index 294e2212f61e..c0bb05487e0e 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -85,7 +85,8 @@ static u32 max_current[][5] = { [max20743] = { 18900, 24100, 29200, 34100 }, }; -static int max20730_read_word_data(struct i2c_client *client, int page, int reg) +static int max20730_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct max20730_data *data = to_max20730_data(info); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 254b0f98c755..d9aa5c873d21 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -72,7 +72,7 @@ static int max31785_read_long_data(struct i2c_client *client, int page, cmdbuf[0] = reg; - rc = pmbus_set_page(client, page); + rc = pmbus_set_page(client, page, 0xff); if (rc < 0) return rc; @@ -110,7 +110,7 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page) if (config < 0) return config; - command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1); + command = pmbus_read_word_data(client, page, 0xff, PMBUS_FAN_COMMAND_1); if (command < 0) return command; @@ -126,7 +126,7 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page) } static int max31785_read_word_data(struct i2c_client *client, int page, - int reg) + int phase, int reg) { u32 val; int rv; diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 5c63a6600729..18b4e071067f 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -41,7 +41,8 @@ struct max34440_data { #define to_max34440_data(x) container_of(x, struct max34440_data, info) -static int max34440_read_word_data(struct i2c_client *client, int page, int reg) +static int max34440_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; const struct pmbus_driver_info *info = pmbus_get_driver_info(client); @@ -49,44 +50,44 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_VOUT_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_VOUT_MIN); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_AVG: if (data->id != max34446 && data->id != max34451) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_POUT_AVG: if (data->id != max34446) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_POUT_AVG); break; case PMBUS_VIRT_READ_POUT_MAX: if (data->id != max34446) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_POUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_AVG: if (data->id != max34446 && data->id != max34460 && data->id != max34461) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_TEMPERATURE_AVG); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_TEMPERATURE_PEAK); break; case PMBUS_VIRT_RESET_POUT_HISTORY: @@ -159,14 +160,14 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) int mfg_status; if (page >= 0) { - ret = pmbus_set_page(client, page); + ret = pmbus_set_page(client, page, 0xff); if (ret < 0) return ret; } switch (reg) { case PMBUS_STATUS_IOUT: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_MFR_SPECIFIC); if (mfg_status < 0) return mfg_status; @@ -176,7 +177,7 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) ret |= PB_IOUT_OC_FAULT; break; case PMBUS_STATUS_TEMPERATURE: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_MFR_SPECIFIC); if (mfg_status < 0) return mfg_status; diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index bc5f4cb6450e..643ccfc05106 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -28,7 +28,8 @@ #define MAX8688_STATUS_OT_FAULT BIT(13) #define MAX8688_STATUS_OT_WARNING BIT(14) -static int max8688_read_word_data(struct i2c_client *client, int page, int reg) +static int max8688_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; @@ -37,13 +38,15 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK); + ret = pmbus_read_word_data(client, 0, 0xff, + MAX8688_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK); + ret = pmbus_read_word_data(client, 0, 0xff, + MAX8688_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFR_TEMPERATURE_PEAK); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: @@ -94,7 +97,7 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_STATUS_VOUT: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFG_STATUS); if (mfg_status < 0) return mfg_status; @@ -108,7 +111,7 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) ret |= PB_VOLTAGE_OV_FAULT; break; case PMBUS_STATUS_IOUT: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFG_STATUS); if (mfg_status < 0) return mfg_status; @@ -120,7 +123,7 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) ret |= PB_IOUT_OC_FAULT; break; case PMBUS_STATUS_TEMPERATURE: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFG_STATUS); if (mfg_status < 0) return mfg_status; diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 51e8312b6c2d..6d384e8ee1db 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -102,10 +102,10 @@ static int pmbus_identify(struct i2c_client *client, int page; for (page = 1; page < PMBUS_PAGES; page++) { - if (pmbus_set_page(client, page) < 0) + if (pmbus_set_page(client, page, 0xff) < 0) break; } - pmbus_set_page(client, 0); + pmbus_set_page(client, 0, 0xff); info->pages = page; } else { info->pages = 1; diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 13b34bd67f23..18e06fc6c53f 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -119,6 +119,9 @@ enum pmbus_regs { PMBUS_MFR_DATE = 0x9D, PMBUS_MFR_SERIAL = 0x9E, + PMBUS_IC_DEVICE_ID = 0xAD, + PMBUS_IC_DEVICE_REV = 0xAE, + /* * Virtual registers. * Useful to support attributes which are not supported by standard PMBus @@ -359,6 +362,7 @@ enum pmbus_sensor_classes { }; #define PMBUS_PAGES 32 /* Per PMBus specification */ +#define PMBUS_PHASES 8 /* Maximum number of phases per page */ /* Functionality bit mask */ #define PMBUS_HAVE_VIN BIT(0) @@ -385,13 +389,15 @@ enum pmbus_sensor_classes { #define PMBUS_HAVE_PWM34 BIT(21) #define PMBUS_HAVE_SAMPLES BIT(22) -#define PMBUS_PAGE_VIRTUAL BIT(31) +#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */ +#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */ enum pmbus_data_format { linear = 0, direct, vid }; enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; struct pmbus_driver_info { int pages; /* Total number of pages */ + u8 phases[PMBUS_PAGES]; /* Number of phases per page */ enum pmbus_data_format format[PSC_NUM_CLASSES]; enum vrm_version vrm_version[PMBUS_PAGES]; /* vrm version per page */ /* @@ -403,6 +409,7 @@ struct pmbus_driver_info { int R[PSC_NUM_CLASSES]; /* exponent */ u32 func[PMBUS_PAGES]; /* Functionality, per page */ + u32 pfunc[PMBUS_PHASES];/* Functionality, per phase */ /* * The following functions map manufacturing specific register values * to PMBus standard register values. Specify only if mapping is @@ -415,7 +422,8 @@ struct pmbus_driver_info { * the standard register. */ int (*read_byte_data)(struct i2c_client *client, int page, int reg); - int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int phase, + int reg); int (*write_word_data)(struct i2c_client *client, int page, int reg, u16 word); int (*write_byte)(struct i2c_client *client, int page, u8 value); @@ -454,9 +462,11 @@ extern const struct regulator_ops pmbus_regulator_ops; /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); -int pmbus_set_page(struct i2c_client *client, int page); -int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg); -int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 word); +int pmbus_set_page(struct i2c_client *client, int page, int phase); +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, + u8 reg); +int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, + u16 word); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); int pmbus_write_byte(struct i2c_client *client, int page, u8 value); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index d9c17feb7b4a..8d321bf7d15b 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -49,6 +49,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 */ @@ -109,6 +110,7 @@ struct pmbus_data { int (*read_status)(struct i2c_client *client, int page); u8 currpage; + u8 currphase; /* current phase, 0xff for all */ }; struct pmbus_debugfs_entry { @@ -146,15 +148,16 @@ void pmbus_clear_cache(struct i2c_client *client) } EXPORT_SYMBOL_GPL(pmbus_clear_cache); -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,9 +169,17 @@ 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); @@ -177,7 +188,7 @@ 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; @@ -208,7 +219,7 @@ 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; @@ -286,11 +297,11 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, } EXPORT_SYMBOL_GPL(pmbus_update_fan); -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; @@ -320,14 +331,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,14 +347,20 @@ 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; @@ -354,7 +372,7 @@ 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; @@ -440,7 +458,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id, 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 */ @@ -530,7 +548,7 @@ EXPORT_SYMBOL_GPL(pmbus_check_byte_register); 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_GPL(pmbus_check_word_register); @@ -595,6 +613,7 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) sensor->data = _pmbus_read_word_data(client, sensor->page, + sensor->phase, sensor->reg); } pmbus_clear_faults(client); @@ -1076,7 +1095,8 @@ static int pmbus_add_boolean(struct pmbus_data *data, 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,6 +1120,7 @@ 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; @@ -1119,7 +1140,7 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, 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 +1152,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); @@ -1200,7 +1231,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) @@ -1227,7 +1258,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 +1268,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) @@ -1315,10 +1347,25 @@ static int pmbus_add_sensor_attrs(struct i2c_client *client, continue; ret = pmbus_add_sensor_attrs_one(client, data, info, name, index, page, - attrs, paged); + 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++; } @@ -1822,7 +1869,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, + PMBUS_VIRT_FAN_TARGET_1 + id, 0xff, PSC_FAN, false, false, true); if (!sensor) @@ -1833,14 +1880,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, + PMBUS_VIRT_PWM_1 + id, 0xff, 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, + PMBUS_VIRT_PWM_ENABLE_1 + id, 0xff, PSC_PWM, true, false, false); if (!sensor) @@ -1882,7 +1929,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, pmbus_fan_registers[f], 0xff, PSC_FAN, true, true, true) == NULL) return -ENOMEM; @@ -1964,7 +2011,7 @@ static ssize_t pmbus_show_samples(struct device *dev, struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_samples_reg *reg = to_samples_reg(devattr); - val = _pmbus_read_word_data(client, reg->page, reg->attr->reg); + val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg); if (val < 0) return val; @@ -2120,7 +2167,7 @@ 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); } static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, @@ -2482,6 +2529,8 @@ 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 = 0xff; + data->currphase = 0xfe; ret = pmbus_init_common(client, data, info); if (ret < 0) diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 9c22e9013dd7..157c99ffb52b 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -6,13 +6,21 @@ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> */ +#include <linux/bits.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_device.h> #include "pmbus.h" +enum chips { + tps53647, tps53667, tps53679, tps53681, tps53688 +}; + +#define TPS53647_PAGE_NUM 1 + #define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ #define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ #define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ @@ -20,13 +28,19 @@ #define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ #define TPS53679_PAGE_NUM 2 -static int tps53679_identify(struct i2c_client *client, - struct pmbus_driver_info *info) +#define TPS53681_DEVICE_ID 0x81 + +#define TPS53681_PMBUS_REVISION 0x33 + +#define TPS53681_MFR_SPECIFIC_20 0xe4 /* Number of phases, per page */ + +static int tps53679_identify_mode(struct i2c_client *client, + struct pmbus_driver_info *info) { u8 vout_params; int i, ret; - for (i = 0; i < TPS53679_PAGE_NUM; i++) { + for (i = 0; i < info->pages; i++) { /* Read the register with VOUT scaling value.*/ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); if (ret < 0) @@ -52,48 +66,180 @@ static int tps53679_identify(struct i2c_client *client, return 0; } +static int tps53679_identify_phases(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int ret; + + /* On TPS53681, only channel A provides per-phase output current */ + ret = pmbus_read_byte_data(client, 0, TPS53681_MFR_SPECIFIC_20); + if (ret < 0) + return ret; + info->phases[0] = (ret & 0x07) + 1; + + return 0; +} + +static int tps53679_identify_chip(struct i2c_client *client, + u8 revision, u16 id) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION); + if (ret < 0) + return ret; + if (ret != revision) { + dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n", ret); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return ret; + if (ret != 1 || buf[0] != id) { + dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]); + return -ENODEV; + } + return 0; +} + +/* + * Common identification function for chips with multi-phase support. + * Since those chips have special configuration registers, we want to have + * some level of reassurance that we are really talking with the chip + * being probed. Check PMBus revision and chip ID. + */ +static int tps53679_identify_multiphase(struct i2c_client *client, + struct pmbus_driver_info *info, + int pmbus_rev, int device_id) +{ + int ret; + + ret = tps53679_identify_chip(client, pmbus_rev, device_id); + if (ret < 0) + return ret; + + ret = tps53679_identify_mode(client, info); + if (ret < 0) + return ret; + + return tps53679_identify_phases(client, info); +} + +static int tps53679_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + return tps53679_identify_mode(client, info); +} + +static int tps53681_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + return tps53679_identify_multiphase(client, info, + TPS53681_PMBUS_REVISION, + TPS53681_DEVICE_ID); +} + +static int tps53681_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + /* + * For reading the total output current (READ_IOUT) for all phases, + * the chip datasheet is a bit vague. It says "PHASE must be set to + * FFh to access all phases simultaneously. PHASE may also be set to + * 80h readack (!) the total phase current". + * Experiments show that the command does _not_ report the total + * current for all phases if the phase is set to 0xff. Instead, it + * appears to report the current of one of the phases. Override phase + * parameter with 0x80 when reading the total output current on page 0. + */ + if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff) + return pmbus_read_word_data(client, page, 0x80, reg); + return -ENODATA; +} + static struct pmbus_driver_info tps53679_info = { - .pages = TPS53679_PAGE_NUM, .format[PSC_VOLTAGE_IN] = linear, .format[PSC_VOLTAGE_OUT] = vid, .format[PSC_TEMPERATURE] = linear, .format[PSC_CURRENT_OUT] = linear, .format[PSC_POWER] = linear, - .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT, - .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT, - .identify = tps53679_identify, + .pfunc[0] = PMBUS_HAVE_IOUT, + .pfunc[1] = PMBUS_HAVE_IOUT, + .pfunc[2] = PMBUS_HAVE_IOUT, + .pfunc[3] = PMBUS_HAVE_IOUT, + .pfunc[4] = PMBUS_HAVE_IOUT, + .pfunc[5] = PMBUS_HAVE_IOUT, }; static int tps53679_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct pmbus_driver_info *info; + enum chips chip_id; + + if (dev->of_node) + chip_id = (enum chips)of_device_get_match_data(dev); + else + chip_id = id->driver_data; - info = devm_kmemdup(&client->dev, &tps53679_info, sizeof(*info), - GFP_KERNEL); + info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; + switch (chip_id) { + case tps53647: + case tps53667: + info->pages = TPS53647_PAGE_NUM; + info->identify = tps53679_identify; + break; + case tps53679: + case tps53688: + info->pages = TPS53679_PAGE_NUM; + info->identify = tps53679_identify; + break; + case tps53681: + info->pages = TPS53679_PAGE_NUM; + info->phases[0] = 6; + info->identify = tps53681_identify; + info->read_word_data = tps53681_read_word_data; + break; + default: + return -ENODEV; + } + return pmbus_do_probe(client, id, info); } static const struct i2c_device_id tps53679_id[] = { - {"tps53679", 0}, - {"tps53688", 0}, + {"tps53647", tps53647}, + {"tps53667", tps53667}, + {"tps53679", tps53679}, + {"tps53681", tps53681}, + {"tps53688", tps53688}, {} }; MODULE_DEVICE_TABLE(i2c, tps53679_id); static const struct of_device_id __maybe_unused tps53679_of_match[] = { - {.compatible = "ti,tps53679"}, - {.compatible = "ti,tps53688"}, + {.compatible = "ti,tps53647", .data = (void *)tps53647}, + {.compatible = "ti,tps53667", .data = (void *)tps53667}, + {.compatible = "ti,tps53679", .data = (void *)tps53679}, + {.compatible = "ti,tps53681", .data = (void *)tps53681}, + {.compatible = "ti,tps53688", .data = (void *)tps53688}, {} }; MODULE_DEVICE_TABLE(of, tps53679_of_match); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 23ea3415f166..81f4c4f166cd 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -370,7 +370,7 @@ static void ucd9000_probe_gpio(struct i2c_client *client, #ifdef CONFIG_DEBUG_FS static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer) { - int ret = pmbus_set_page(client, 0); + int ret = pmbus_set_page(client, 0, 0xff); if (ret < 0) return ret; diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index ecd9b65627ec..d5103fc9e269 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -18,6 +18,60 @@ #define XDPE122_AMD_625MV 0x10 /* AMD mode 6.25mV */ #define XDPE122_PAGE_NUM 2 +static int xdpe122_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + long val; + s16 exponent; + s32 mantissa; + int ret; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + /* Convert register value to LINEAR11 data. */ + exponent = ((s16)ret) >> 11; + mantissa = ((s16)((ret & GENMASK(10, 0)) << 5)) >> 5; + val = mantissa * 1000L; + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + /* Convert data to VID register. */ + switch (info->vrm_version[page]) { + case vr13: + if (val >= 500) + return 1 + DIV_ROUND_CLOSEST(val - 500, 10); + return 0; + case vr12: + if (val >= 250) + return 1 + DIV_ROUND_CLOSEST(val - 250, 5); + return 0; + case imvp9: + if (val >= 200) + return 1 + DIV_ROUND_CLOSEST(val - 200, 10); + return 0; + case amd625mv: + if (val >= 200 && val <= 1550) + return DIV_ROUND_CLOSEST((1550 - val) * 100, + 625); + return 0; + default: + return -EINVAL; + } + default: + return -ENODATA; + } + + return 0; +} + static int xdpe122_identify(struct i2c_client *client, struct pmbus_driver_info *info) { @@ -70,6 +124,7 @@ static struct pmbus_driver_info xdpe122_info = { PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, .identify = xdpe122_identify, + .read_word_data = xdpe122_read_word_data, }; static int xdpe122_probe(struct i2c_client *client, diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 190b898e404a..3a827d0a881d 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -125,7 +125,8 @@ static inline void zl6100_wait(const struct zl6100_data *data) } } -static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) +static int zl6100_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct zl6100_data *data = to_zl6100_data(info); @@ -167,7 +168,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) } zl6100_wait(data); - ret = pmbus_read_word_data(client, page, vreg); + ret = pmbus_read_word_data(client, page, phase, vreg); data->access = ktime_get(); if (ret < 0) return ret; |