Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 590defe5 authored by Guenter Roeck's avatar Guenter Roeck Committed by Guenter Roeck
Browse files

hwmon: (max34440) Add support for MAX34446

parent 60b873e3
Loading
Loading
Loading
Loading
+27 −2
Original line number Original line Diff line number Diff line
@@ -11,6 +11,11 @@ Supported chips:
    Prefixes: 'max34441'
    Prefixes: 'max34441'
    Addresses scanned: -
    Addresses scanned: -
    Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34441.pdf
    Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34441.pdf
  * Maxim MAX34446
    PMBus Power-Supply Data Logger
    Prefixes: 'max34446'
    Addresses scanned: -
    Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34446.pdf


Author: Guenter Roeck <guenter.roeck@ericsson.com>
Author: Guenter Roeck <guenter.roeck@ericsson.com>


@@ -19,8 +24,8 @@ Description
-----------
-----------


This driver supports hardware montoring for Maxim MAX34440 PMBus 6-Channel
This driver supports hardware montoring for Maxim MAX34440 PMBus 6-Channel
Power-Supply Manager and MAX34441 PMBus 5-Channel Power-Supply Manager
Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager
and Intelligent Fan Controller.
and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger.


The driver is a client driver to the core PMBus driver. Please see
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus for details on PMBus client drivers.
Documentation/hwmon/pmbus for details on PMBus client drivers.
@@ -33,6 +38,13 @@ This driver does not auto-detect devices. You will have to instantiate the
devices explicitly. Please see Documentation/i2c/instantiating-devices for
devices explicitly. Please see Documentation/i2c/instantiating-devices for
details.
details.


For MAX34446, the value of the currX_crit attribute determines if current or
voltage measurement is enabled for a given channel. Voltage measurement is
enabled if currX_crit is set to 0; current measurement is enabled if the
attribute is set to a positive value. Power measurement is only enabled if
channel 1 (3) is configured for voltage measurement, and channel 2 (4) is
configured for current measurement.



Platform data support
Platform data support
---------------------
---------------------
@@ -60,16 +72,27 @@ in[1-6]_lowest Historical minimum voltage.
in[1-6]_highest		Historical maximum voltage.
in[1-6]_highest		Historical maximum voltage.
in[1-6]_reset_history	Write any value to reset history.
in[1-6]_reset_history	Write any value to reset history.


			MAX34446 only supports in[1-4].

curr[1-6]_label		"iout[1-6]".
curr[1-6]_label		"iout[1-6]".
curr[1-6]_input		Measured current. From READ_IOUT register.
curr[1-6]_input		Measured current. From READ_IOUT register.
curr[1-6]_max		Maximum current. From IOUT_OC_WARN_LIMIT register.
curr[1-6]_max		Maximum current. From IOUT_OC_WARN_LIMIT register.
curr[1-6]_crit		Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
curr[1-6]_crit		Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
curr[1-6]_max_alarm	Current high alarm. From IOUT_OC_WARNING status.
curr[1-6]_max_alarm	Current high alarm. From IOUT_OC_WARNING status.
curr[1-6]_crit_alarm	Current critical high alarm. From IOUT_OC_FAULT status.
curr[1-6]_crit_alarm	Current critical high alarm. From IOUT_OC_FAULT status.
curr[1-4]_average	Historical average current (MAX34446 only).
curr[1-6]_highest	Historical maximum current.
curr[1-6]_highest	Historical maximum current.
curr[1-6]_reset_history	Write any value to reset history.
curr[1-6]_reset_history	Write any value to reset history.


			in6 and curr6 attributes only exist for MAX34440.
			in6 and curr6 attributes only exist for MAX34440.
			MAX34446 only supports curr[1-4].

power[1,3]_label	"pout[1,3]"
power[1,3]_input	Measured power.
power[1,3]_average	Historical average power.
power[1,3]_highest	Historical maximum power.

			Power attributes only exist for MAX34446.


temp[1-8]_input		Measured temperatures. From READ_TEMPERATURE_1 register.
temp[1-8]_input		Measured temperatures. From READ_TEMPERATURE_1 register.
			temp1 is the chip's internal temperature. temp2..temp5
			temp1 is the chip's internal temperature. temp2..temp5
@@ -80,7 +103,9 @@ temp[1-8]_max Maximum temperature. From OT_WARN_LIMIT register.
temp[1-8]_crit		Critical high temperature. From OT_FAULT_LIMIT register.
temp[1-8]_crit		Critical high temperature. From OT_FAULT_LIMIT register.
temp[1-8]_max_alarm	Temperature high alarm.
temp[1-8]_max_alarm	Temperature high alarm.
temp[1-8]_crit_alarm	Temperature critical high alarm.
temp[1-8]_crit_alarm	Temperature critical high alarm.
temp[1-8]_average	Historical average temperature (MAX34446 only).
temp[1-8]_highest	Historical maximum temperature.
temp[1-8]_highest	Historical maximum temperature.
temp[1-8]_reset_history	Write any value to reset history.
temp[1-8]_reset_history	Write any value to reset history.


			temp7 and temp8 attributes only exist for MAX34440.
			temp7 and temp8 attributes only exist for MAX34440.
			MAX34446 only supports temp[1-3].
+2 −2
Original line number Original line Diff line number Diff line
@@ -68,11 +68,11 @@ config SENSORS_MAX16064
	  be called max16064.
	  be called max16064.


config SENSORS_MAX34440
config SENSORS_MAX34440
	tristate "Maxim MAX34440/MAX34441"
	tristate "Maxim MAX34440 and compatibles"
	default n
	default n
	help
	help
	  If you say yes here you get hardware monitoring support for Maxim
	  If you say yes here you get hardware monitoring support for Maxim
	  MAX34440 and MAX34441.
	  MAX34440, MAX34441, and MAX34446.


	  This driver can also be built as a module. If so, the module will
	  This driver can also be built as a module. If so, the module will
	  be called max34440.
	  be called max34440.
+109 −3
Original line number Original line Diff line number Diff line
@@ -25,21 +25,35 @@
#include <linux/i2c.h>
#include <linux/i2c.h>
#include "pmbus.h"
#include "pmbus.h"


enum chips { max34440, max34441 };
enum chips { max34440, max34441, max34446 };


#define MAX34440_MFR_VOUT_PEAK		0xd4
#define MAX34440_MFR_VOUT_PEAK		0xd4
#define MAX34440_MFR_IOUT_PEAK		0xd5
#define MAX34440_MFR_IOUT_PEAK		0xd5
#define MAX34440_MFR_TEMPERATURE_PEAK	0xd6
#define MAX34440_MFR_TEMPERATURE_PEAK	0xd6
#define MAX34440_MFR_VOUT_MIN		0xd7
#define MAX34440_MFR_VOUT_MIN		0xd7


#define MAX34446_MFR_POUT_PEAK		0xe0
#define MAX34446_MFR_POUT_AVG		0xe1
#define MAX34446_MFR_IOUT_AVG		0xe2
#define MAX34446_MFR_TEMPERATURE_AVG	0xe3

#define MAX34440_STATUS_OC_WARN		(1 << 0)
#define MAX34440_STATUS_OC_WARN		(1 << 0)
#define MAX34440_STATUS_OC_FAULT	(1 << 1)
#define MAX34440_STATUS_OC_FAULT	(1 << 1)
#define MAX34440_STATUS_OT_FAULT	(1 << 5)
#define MAX34440_STATUS_OT_FAULT	(1 << 5)
#define MAX34440_STATUS_OT_WARN		(1 << 6)
#define MAX34440_STATUS_OT_WARN		(1 << 6)


struct max34440_data {
	int id;
	struct pmbus_driver_info info;
};

#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 reg)
{
{
	int ret;
	int ret;
	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
	const struct max34440_data *data = to_max34440_data(info);


	switch (reg) {
	switch (reg) {
	case PMBUS_VIRT_READ_VOUT_MIN:
	case PMBUS_VIRT_READ_VOUT_MIN:
@@ -50,14 +64,43 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg)
		ret = pmbus_read_word_data(client, page,
		ret = pmbus_read_word_data(client, page,
					   MAX34440_MFR_VOUT_PEAK);
					   MAX34440_MFR_VOUT_PEAK);
		break;
		break;
	case PMBUS_VIRT_READ_IOUT_AVG:
		if (data->id != max34446)
			return -ENXIO;
		ret = pmbus_read_word_data(client, page,
					   MAX34446_MFR_IOUT_AVG);
		break;
	case PMBUS_VIRT_READ_IOUT_MAX:
	case PMBUS_VIRT_READ_IOUT_MAX:
		ret = pmbus_read_word_data(client, page,
		ret = pmbus_read_word_data(client, page,
					   MAX34440_MFR_IOUT_PEAK);
					   MAX34440_MFR_IOUT_PEAK);
		break;
		break;
	case PMBUS_VIRT_READ_POUT_AVG:
		if (data->id != max34446)
			return -ENXIO;
		ret = pmbus_read_word_data(client, page,
					   MAX34446_MFR_POUT_AVG);
		break;
	case PMBUS_VIRT_READ_POUT_MAX:
		if (data->id != max34446)
			return -ENXIO;
		ret = pmbus_read_word_data(client, page,
					   MAX34446_MFR_POUT_PEAK);
		break;
	case PMBUS_VIRT_READ_TEMP_AVG:
		if (data->id != max34446)
			return -ENXIO;
		ret = pmbus_read_word_data(client, page,
					   MAX34446_MFR_TEMPERATURE_AVG);
		break;
	case PMBUS_VIRT_READ_TEMP_MAX:
	case PMBUS_VIRT_READ_TEMP_MAX:
		ret = pmbus_read_word_data(client, page,
		ret = pmbus_read_word_data(client, page,
					   MAX34440_MFR_TEMPERATURE_PEAK);
					   MAX34440_MFR_TEMPERATURE_PEAK);
		break;
		break;
	case PMBUS_VIRT_RESET_POUT_HISTORY:
		if (data->id != max34446)
			return -ENXIO;
		ret = 0;
		break;
	case PMBUS_VIRT_RESET_VOUT_HISTORY:
	case PMBUS_VIRT_RESET_VOUT_HISTORY:
	case PMBUS_VIRT_RESET_IOUT_HISTORY:
	case PMBUS_VIRT_RESET_IOUT_HISTORY:
	case PMBUS_VIRT_RESET_TEMP_HISTORY:
	case PMBUS_VIRT_RESET_TEMP_HISTORY:
@@ -73,9 +116,19 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg)
static int max34440_write_word_data(struct i2c_client *client, int page,
static int max34440_write_word_data(struct i2c_client *client, int page,
				    int reg, u16 word)
				    int reg, u16 word)
{
{
	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
	const struct max34440_data *data = to_max34440_data(info);
	int ret;
	int ret;


	switch (reg) {
	switch (reg) {
	case PMBUS_VIRT_RESET_POUT_HISTORY:
		ret = pmbus_write_word_data(client, page,
					    MAX34446_MFR_POUT_PEAK, 0);
		if (ret)
			break;
		ret = pmbus_write_word_data(client, page,
					    MAX34446_MFR_POUT_AVG, 0);
		break;
	case PMBUS_VIRT_RESET_VOUT_HISTORY:
	case PMBUS_VIRT_RESET_VOUT_HISTORY:
		ret = pmbus_write_word_data(client, page,
		ret = pmbus_write_word_data(client, page,
					    MAX34440_MFR_VOUT_MIN, 0x7fff);
					    MAX34440_MFR_VOUT_MIN, 0x7fff);
@@ -87,11 +140,18 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
	case PMBUS_VIRT_RESET_IOUT_HISTORY:
	case PMBUS_VIRT_RESET_IOUT_HISTORY:
		ret = pmbus_write_word_data(client, page,
		ret = pmbus_write_word_data(client, page,
					    MAX34440_MFR_IOUT_PEAK, 0);
					    MAX34440_MFR_IOUT_PEAK, 0);
		if (!ret && data->id == max34446)
			ret = pmbus_write_word_data(client, page,
					MAX34446_MFR_IOUT_AVG, 0);

		break;
		break;
	case PMBUS_VIRT_RESET_TEMP_HISTORY:
	case PMBUS_VIRT_RESET_TEMP_HISTORY:
		ret = pmbus_write_word_data(client, page,
		ret = pmbus_write_word_data(client, page,
					    MAX34440_MFR_TEMPERATURE_PEAK,
					    MAX34440_MFR_TEMPERATURE_PEAK,
					    0x8000);
					    0x8000);
		if (!ret && data->id == max34446)
			ret = pmbus_write_word_data(client, page,
					MAX34446_MFR_TEMPERATURE_AVG, 0);
		break;
		break;
	default:
	default:
		ret = -ENODATA;
		ret = -ENODATA;
@@ -225,20 +285,66 @@ static struct pmbus_driver_info max34440_info[] = {
		.read_word_data = max34440_read_word_data,
		.read_word_data = max34440_read_word_data,
		.write_word_data = max34440_write_word_data,
		.write_word_data = max34440_write_word_data,
	},
	},
	[max34446] = {
		.pages = 7,
		.format[PSC_VOLTAGE_IN] = direct,
		.format[PSC_VOLTAGE_OUT] = direct,
		.format[PSC_TEMPERATURE] = direct,
		.format[PSC_CURRENT_OUT] = direct,
		.format[PSC_POWER] = direct,
		.m[PSC_VOLTAGE_IN] = 1,
		.b[PSC_VOLTAGE_IN] = 0,
		.R[PSC_VOLTAGE_IN] = 3,
		.m[PSC_VOLTAGE_OUT] = 1,
		.b[PSC_VOLTAGE_OUT] = 0,
		.R[PSC_VOLTAGE_OUT] = 3,
		.m[PSC_CURRENT_OUT] = 1,
		.b[PSC_CURRENT_OUT] = 0,
		.R[PSC_CURRENT_OUT] = 3,
		.m[PSC_POWER] = 1,
		.b[PSC_POWER] = 0,
		.R[PSC_POWER] = 3,
		.m[PSC_TEMPERATURE] = 1,
		.b[PSC_TEMPERATURE] = 0,
		.R[PSC_TEMPERATURE] = 2,
		.func[0] = 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,
		.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
		  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
		.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
		  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
		.func[4] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
		.func[5] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
		.func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
		.read_byte_data = max34440_read_byte_data,
		.read_word_data = max34440_read_word_data,
		.write_word_data = max34440_write_word_data,
	},
};
};


static int max34440_probe(struct i2c_client *client,
static int max34440_probe(struct i2c_client *client,
			  const struct i2c_device_id *id)
			  const struct i2c_device_id *id)
{
{
	return pmbus_do_probe(client, id, &max34440_info[id->driver_data]);
	struct max34440_data *data;

	data = devm_kzalloc(&client->dev, sizeof(struct max34440_data),
			    GFP_KERNEL);
	if (!data)
		return -ENOMEM;
	data->id = id->driver_data;
	data->info = max34440_info[id->driver_data];

	return pmbus_do_probe(client, id, &data->info);
}
}


static const struct i2c_device_id max34440_id[] = {
static const struct i2c_device_id max34440_id[] = {
	{"max34440", max34440},
	{"max34440", max34440},
	{"max34441", max34441},
	{"max34441", max34441},
	{"max34446", max34446},
	{}
	{}
};
};

MODULE_DEVICE_TABLE(i2c, max34440_id);
MODULE_DEVICE_TABLE(i2c, max34440_id);


/* This is the driver that will be inserted */
/* This is the driver that will be inserted */