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

Commit 442aba78 authored by Guenter Roeck's avatar Guenter Roeck
Browse files

hwmon: PMBus device driver



This driver adds support for hardware monitoring features of various PMBus
devices.

Signed-off-by: default avatarGuenter Roeck <guenter.roeck@ericsson.com>
Acked-by: default avatarJonathan Cameron <jic23@cam.ac.uk>
parent 06923f84
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -756,6 +756,31 @@ config SENSORS_PCF8591
	  These devices are hard to detect and rarely found on mainstream
	  hardware.  If unsure, say N.

config PMBUS
	tristate "PMBus support"
	depends on I2C && EXPERIMENTAL
	default n
	help
	  Say yes here if you want to enable PMBus support.

	  This driver can also be built as a module. If so, the module will
	  be called pmbus_core.

if PMBUS

config SENSORS_PMBUS
	tristate "Generic PMBus devices"
	default n
	help
	  If you say yes here you get hardware monitoring support for generic
	  PMBus devices, including but not limited to BMR450, BMR451, BMR453,
	  BMR454, and LTC2978.

	  This driver can also be built as a module. If so, the module will
	  be called pmbus.

endif # PMBUS

config SENSORS_SHT15
	tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
	depends on GENERIC_GPIO
+4 −0
Original line number Diff line number Diff line
@@ -114,6 +114,10 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o

# PMBus drivers
obj-$(CONFIG_PMBUS)		+= pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS)	+= pmbus.o

ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif

drivers/hwmon/pmbus.c

0 → 100644
+203 −0
Original line number Diff line number Diff line
/*
 * Hardware monitoring driver for PMBus devices
 *
 * Copyright (c) 2010, 2011 Ericsson AB.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include "pmbus.h"

/*
 * Find sensor groups and status registers on each page.
 */
static void pmbus_find_sensor_groups(struct i2c_client *client,
				     struct pmbus_driver_info *info)
{
	int page;

	/* Sensors detected on page 0 only */
	if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN))
		info->func[0] |= PMBUS_HAVE_VIN;
	if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP))
		info->func[0] |= PMBUS_HAVE_VCAP;
	if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN))
		info->func[0] |= PMBUS_HAVE_IIN;
	if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN))
		info->func[0] |= PMBUS_HAVE_PIN;
	if (info->func[0]
	    && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT))
		info->func[0] |= PMBUS_HAVE_STATUS_INPUT;
	if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) {
		info->func[0] |= PMBUS_HAVE_FAN12;
		if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12))
			info->func[0] |= PMBUS_HAVE_STATUS_FAN12;
	}
	if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) {
		info->func[0] |= PMBUS_HAVE_FAN34;
		if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34))
			info->func[0] |= PMBUS_HAVE_STATUS_FAN34;
	}
	if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) {
		info->func[0] |= PMBUS_HAVE_TEMP;
		if (pmbus_check_byte_register(client, 0,
					      PMBUS_STATUS_TEMPERATURE))
			info->func[0] |= PMBUS_HAVE_STATUS_TEMP;
	}

	/* Sensors detected on all pages */
	for (page = 0; page < info->pages; page++) {
		if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) {
			info->func[page] |= PMBUS_HAVE_VOUT;
			if (pmbus_check_byte_register(client, page,
						      PMBUS_STATUS_VOUT))
				info->func[page] |= PMBUS_HAVE_STATUS_VOUT;
		}
		if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) {
			info->func[page] |= PMBUS_HAVE_IOUT;
			if (pmbus_check_byte_register(client, 0,
						      PMBUS_STATUS_IOUT))
				info->func[page] |= PMBUS_HAVE_STATUS_IOUT;
		}
		if (pmbus_check_word_register(client, page, PMBUS_READ_POUT))
			info->func[page] |= PMBUS_HAVE_POUT;
	}
}

/*
 * Identify chip parameters.
 */
static int pmbus_identify(struct i2c_client *client,
			  struct pmbus_driver_info *info)
{
	if (!info->pages) {
		/*
		 * Check if the PAGE command is supported. If it is,
		 * keep setting the page number until it fails or until the
		 * maximum number of pages has been reached. Assume that
		 * this is the number of pages supported by the chip.
		 */
		if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) {
			int page;

			for (page = 1; page < PMBUS_PAGES; page++) {
				if (pmbus_set_page(client, page) < 0)
					break;
			}
			pmbus_set_page(client, 0);
			info->pages = page;
		} else {
			info->pages = 1;
		}
	}

	/*
	 * We should check if the COEFFICIENTS register is supported.
	 * If it is, and the chip is configured for direct mode, we can read
	 * the coefficients from the chip, one set per group of sensor
	 * registers.
	 *
	 * To do this, we will need access to a chip which actually supports the
	 * COEFFICIENTS command, since the command is too complex to implement
	 * without testing it.
	 */

	/* Try to find sensor groups  */
	pmbus_find_sensor_groups(client, info);

	return 0;
}

static int pmbus_probe(struct i2c_client *client,
		       const struct i2c_device_id *id)
{
	struct pmbus_driver_info *info;
	int ret;

	info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	info->pages = id->driver_data;
	info->identify = pmbus_identify;

	ret = pmbus_do_probe(client, id, info);
	if (ret < 0)
		goto out;
	return 0;

out:
	kfree(info);
	return ret;
}

static int pmbus_remove(struct i2c_client *client)
{
	int ret;
	const struct pmbus_driver_info *info;

	info = pmbus_get_driver_info(client);
	ret = pmbus_do_remove(client);
	kfree(info);
	return ret;
}

/*
 * Use driver_data to set the number of pages supported by the chip.
 */
static const struct i2c_device_id pmbus_id[] = {
	{"bmr450", 1},
	{"bmr451", 1},
	{"bmr453", 1},
	{"bmr454", 1},
	{"ltc2978", 8},
	{"pmbus", 0},
	{}
};

MODULE_DEVICE_TABLE(i2c, pmbus_id);

/* This is the driver that will be inserted */
static struct i2c_driver pmbus_driver = {
	.driver = {
		   .name = "pmbus",
		   },
	.probe = pmbus_probe,
	.remove = pmbus_remove,
	.id_table = pmbus_id,
};

static int __init pmbus_init(void)
{
	return i2c_add_driver(&pmbus_driver);
}

static void __exit pmbus_exit(void)
{
	i2c_del_driver(&pmbus_driver);
}

MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("Generic PMBus driver");
MODULE_LICENSE("GPL");
module_init(pmbus_init);
module_exit(pmbus_exit);

drivers/hwmon/pmbus.h

0 → 100644
+313 −0
Original line number Diff line number Diff line
/*
 * pmbus.h - Common defines and structures for PMBus devices
 *
 * Copyright (c) 2010, 2011 Ericsson AB.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef PMBUS_H
#define PMBUS_H

/*
 * Registers
 */
#define PMBUS_PAGE			0x00
#define PMBUS_OPERATION			0x01
#define PMBUS_ON_OFF_CONFIG		0x02
#define PMBUS_CLEAR_FAULTS		0x03
#define PMBUS_PHASE			0x04

#define PMBUS_CAPABILITY		0x19
#define PMBUS_QUERY			0x1A

#define PMBUS_VOUT_MODE			0x20
#define PMBUS_VOUT_COMMAND		0x21
#define PMBUS_VOUT_TRIM			0x22
#define PMBUS_VOUT_CAL_OFFSET		0x23
#define PMBUS_VOUT_MAX			0x24
#define PMBUS_VOUT_MARGIN_HIGH		0x25
#define PMBUS_VOUT_MARGIN_LOW		0x26
#define PMBUS_VOUT_TRANSITION_RATE	0x27
#define PMBUS_VOUT_DROOP		0x28
#define PMBUS_VOUT_SCALE_LOOP		0x29
#define PMBUS_VOUT_SCALE_MONITOR	0x2A

#define PMBUS_COEFFICIENTS		0x30
#define PMBUS_POUT_MAX			0x31

#define PMBUS_FAN_CONFIG_12		0x3A
#define PMBUS_FAN_COMMAND_1		0x3B
#define PMBUS_FAN_COMMAND_2		0x3C
#define PMBUS_FAN_CONFIG_34		0x3D
#define PMBUS_FAN_COMMAND_3		0x3E
#define PMBUS_FAN_COMMAND_4		0x3F

#define PMBUS_VOUT_OV_FAULT_LIMIT	0x40
#define PMBUS_VOUT_OV_FAULT_RESPONSE	0x41
#define PMBUS_VOUT_OV_WARN_LIMIT	0x42
#define PMBUS_VOUT_UV_WARN_LIMIT	0x43
#define PMBUS_VOUT_UV_FAULT_LIMIT	0x44
#define PMBUS_VOUT_UV_FAULT_RESPONSE	0x45
#define PMBUS_IOUT_OC_FAULT_LIMIT	0x46
#define PMBUS_IOUT_OC_FAULT_RESPONSE	0x47
#define PMBUS_IOUT_OC_LV_FAULT_LIMIT	0x48
#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE	0x49
#define PMBUS_IOUT_OC_WARN_LIMIT	0x4A
#define PMBUS_IOUT_UC_FAULT_LIMIT	0x4B
#define PMBUS_IOUT_UC_FAULT_RESPONSE	0x4C

#define PMBUS_OT_FAULT_LIMIT		0x4F
#define PMBUS_OT_FAULT_RESPONSE		0x50
#define PMBUS_OT_WARN_LIMIT		0x51
#define PMBUS_UT_WARN_LIMIT		0x52
#define PMBUS_UT_FAULT_LIMIT		0x53
#define PMBUS_UT_FAULT_RESPONSE		0x54
#define PMBUS_VIN_OV_FAULT_LIMIT	0x55
#define PMBUS_VIN_OV_FAULT_RESPONSE	0x56
#define PMBUS_VIN_OV_WARN_LIMIT		0x57
#define PMBUS_VIN_UV_WARN_LIMIT		0x58
#define PMBUS_VIN_UV_FAULT_LIMIT	0x59

#define PMBUS_IIN_OC_FAULT_LIMIT	0x5B
#define PMBUS_IIN_OC_WARN_LIMIT		0x5D

#define PMBUS_POUT_OP_FAULT_LIMIT	0x68
#define PMBUS_POUT_OP_WARN_LIMIT	0x6A
#define PMBUS_PIN_OP_WARN_LIMIT		0x6B

#define PMBUS_STATUS_BYTE		0x78
#define PMBUS_STATUS_WORD		0x79
#define PMBUS_STATUS_VOUT		0x7A
#define PMBUS_STATUS_IOUT		0x7B
#define PMBUS_STATUS_INPUT		0x7C
#define PMBUS_STATUS_TEMPERATURE	0x7D
#define PMBUS_STATUS_CML		0x7E
#define PMBUS_STATUS_OTHER		0x7F
#define PMBUS_STATUS_MFR_SPECIFIC	0x80
#define PMBUS_STATUS_FAN_12		0x81
#define PMBUS_STATUS_FAN_34		0x82

#define PMBUS_READ_VIN			0x88
#define PMBUS_READ_IIN			0x89
#define PMBUS_READ_VCAP			0x8A
#define PMBUS_READ_VOUT			0x8B
#define PMBUS_READ_IOUT			0x8C
#define PMBUS_READ_TEMPERATURE_1	0x8D
#define PMBUS_READ_TEMPERATURE_2	0x8E
#define PMBUS_READ_TEMPERATURE_3	0x8F
#define PMBUS_READ_FAN_SPEED_1		0x90
#define PMBUS_READ_FAN_SPEED_2		0x91
#define PMBUS_READ_FAN_SPEED_3		0x92
#define PMBUS_READ_FAN_SPEED_4		0x93
#define PMBUS_READ_DUTY_CYCLE		0x94
#define PMBUS_READ_FREQUENCY		0x95
#define PMBUS_READ_POUT			0x96
#define PMBUS_READ_PIN			0x97

#define PMBUS_REVISION			0x98
#define PMBUS_MFR_ID			0x99
#define PMBUS_MFR_MODEL			0x9A
#define PMBUS_MFR_REVISION		0x9B
#define PMBUS_MFR_LOCATION		0x9C
#define PMBUS_MFR_DATE			0x9D
#define PMBUS_MFR_SERIAL		0x9E

/*
 * CAPABILITY
 */
#define PB_CAPABILITY_SMBALERT		(1<<4)
#define PB_CAPABILITY_ERROR_CHECK	(1<<7)

/*
 * VOUT_MODE
 */
#define PB_VOUT_MODE_MODE_MASK		0xe0
#define PB_VOUT_MODE_PARAM_MASK		0x1f

#define PB_VOUT_MODE_LINEAR		0x00
#define PB_VOUT_MODE_VID		0x20
#define PB_VOUT_MODE_DIRECT		0x40

/*
 * Fan configuration
 */
#define PB_FAN_2_PULSE_MASK		((1 << 0) | (1 << 1))
#define PB_FAN_2_RPM			(1 << 2)
#define PB_FAN_2_INSTALLED		(1 << 3)
#define PB_FAN_1_PULSE_MASK		((1 << 4) | (1 << 5))
#define PB_FAN_1_RPM			(1 << 6)
#define PB_FAN_1_INSTALLED		(1 << 7)

/*
 * STATUS_BYTE, STATUS_WORD (lower)
 */
#define PB_STATUS_NONE_ABOVE		(1<<0)
#define PB_STATUS_CML			(1<<1)
#define PB_STATUS_TEMPERATURE		(1<<2)
#define PB_STATUS_VIN_UV		(1<<3)
#define PB_STATUS_IOUT_OC		(1<<4)
#define PB_STATUS_VOUT_OV		(1<<5)
#define PB_STATUS_OFF			(1<<6)
#define PB_STATUS_BUSY			(1<<7)

/*
 * STATUS_WORD (upper)
 */
#define PB_STATUS_UNKNOWN		(1<<8)
#define PB_STATUS_OTHER			(1<<9)
#define PB_STATUS_FANS			(1<<10)
#define PB_STATUS_POWER_GOOD_N		(1<<11)
#define PB_STATUS_WORD_MFR		(1<<12)
#define PB_STATUS_INPUT			(1<<13)
#define PB_STATUS_IOUT_POUT		(1<<14)
#define PB_STATUS_VOUT			(1<<15)

/*
 * STATUS_IOUT
 */
#define PB_POUT_OP_WARNING		(1<<0)
#define PB_POUT_OP_FAULT		(1<<1)
#define PB_POWER_LIMITING		(1<<2)
#define PB_CURRENT_SHARE_FAULT		(1<<3)
#define PB_IOUT_UC_FAULT		(1<<4)
#define PB_IOUT_OC_WARNING		(1<<5)
#define PB_IOUT_OC_LV_FAULT		(1<<6)
#define PB_IOUT_OC_FAULT		(1<<7)

/*
 * STATUS_VOUT, STATUS_INPUT
 */
#define PB_VOLTAGE_UV_FAULT		(1<<4)
#define PB_VOLTAGE_UV_WARNING		(1<<5)
#define PB_VOLTAGE_OV_WARNING		(1<<6)
#define PB_VOLTAGE_OV_FAULT		(1<<7)

/*
 * STATUS_INPUT
 */
#define PB_PIN_OP_WARNING		(1<<0)
#define PB_IIN_OC_WARNING		(1<<1)
#define PB_IIN_OC_FAULT			(1<<2)

/*
 * STATUS_TEMPERATURE
 */
#define PB_TEMP_UT_FAULT		(1<<4)
#define PB_TEMP_UT_WARNING		(1<<5)
#define PB_TEMP_OT_WARNING		(1<<6)
#define PB_TEMP_OT_FAULT		(1<<7)

/*
 * STATUS_FAN
 */
#define PB_FAN_AIRFLOW_WARNING		(1<<0)
#define PB_FAN_AIRFLOW_FAULT		(1<<1)
#define PB_FAN_FAN2_SPEED_OVERRIDE	(1<<2)
#define PB_FAN_FAN1_SPEED_OVERRIDE	(1<<3)
#define PB_FAN_FAN2_WARNING		(1<<4)
#define PB_FAN_FAN1_WARNING		(1<<5)
#define PB_FAN_FAN2_FAULT		(1<<6)
#define PB_FAN_FAN1_FAULT		(1<<7)

/*
 * CML_FAULT_STATUS
 */
#define PB_CML_FAULT_OTHER_MEM_LOGIC	(1<<0)
#define PB_CML_FAULT_OTHER_COMM		(1<<1)
#define PB_CML_FAULT_PROCESSOR		(1<<3)
#define PB_CML_FAULT_MEMORY		(1<<4)
#define PB_CML_FAULT_PACKET_ERROR	(1<<5)
#define PB_CML_FAULT_INVALID_DATA	(1<<6)
#define PB_CML_FAULT_INVALID_COMMAND	(1<<7)

enum pmbus_sensor_classes {
	PSC_VOLTAGE_IN = 0,
	PSC_VOLTAGE_OUT,
	PSC_CURRENT_IN,
	PSC_CURRENT_OUT,
	PSC_POWER,
	PSC_TEMPERATURE,
	PSC_FAN,
	PSC_NUM_CLASSES		/* Number of power sensor classes */
};

#define PMBUS_PAGES	32	/* Per PMBus specification */

/* Functionality bit mask */
#define PMBUS_HAVE_VIN		(1 << 0)
#define PMBUS_HAVE_VCAP		(1 << 1)
#define PMBUS_HAVE_VOUT		(1 << 2)
#define PMBUS_HAVE_IIN		(1 << 3)
#define PMBUS_HAVE_IOUT		(1 << 4)
#define PMBUS_HAVE_PIN		(1 << 5)
#define PMBUS_HAVE_POUT		(1 << 6)
#define PMBUS_HAVE_FAN12	(1 << 7)
#define PMBUS_HAVE_FAN34	(1 << 8)
#define PMBUS_HAVE_TEMP		(1 << 9)
#define PMBUS_HAVE_TEMP2	(1 << 10)
#define PMBUS_HAVE_TEMP3	(1 << 11)
#define PMBUS_HAVE_STATUS_VOUT	(1 << 12)
#define PMBUS_HAVE_STATUS_IOUT	(1 << 13)
#define PMBUS_HAVE_STATUS_INPUT	(1 << 14)
#define PMBUS_HAVE_STATUS_TEMP	(1 << 15)
#define PMBUS_HAVE_STATUS_FAN12	(1 << 16)
#define PMBUS_HAVE_STATUS_FAN34	(1 << 17)

struct pmbus_driver_info {
	int pages;		/* Total number of pages */
	bool direct[PSC_NUM_CLASSES];
				/* true if device uses direct data format
				   for the given sensor class */
	/*
	 * Support one set of coefficients for each sensor type
	 * Used for chips providing data in direct mode.
	 */
	int m[PSC_NUM_CLASSES];	/* mantissa for direct data format */
	int b[PSC_NUM_CLASSES];	/* offset */
	int R[PSC_NUM_CLASSES];	/* exponent */

	u32 func[PMBUS_PAGES];	/* Functionality, per page */
	/*
	 * The get_status function maps manufacturing specific status values
	 * into PMBus standard status values.
	 * This function is optional and only necessary if chip specific status
	 * register values have to be mapped into standard PMBus status register
	 * values.
	 */
	int (*get_status)(struct i2c_client *client, int page, int reg);
	/*
	 * The identify function determines supported PMBus functionality.
	 * This function is only necessary if a chip driver supports multiple
	 * chips, and the chip functionality is not pre-determined.
	 */
	int (*identify)(struct i2c_client *client,
			struct pmbus_driver_info *info);
};

/* Function declarations */

int pmbus_set_page(struct i2c_client *client, u8 page);
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
		   struct pmbus_driver_info *info);
int pmbus_do_remove(struct i2c_client *client);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client
						      *client);

#endif /* PMBUS_H */
+1628 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading