Skip to content

Commit

Permalink
hwmon: PMBus device driver
Browse files Browse the repository at this point in the history
This driver adds support for hardware monitoring features of various PMBus
devices.

Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
  • Loading branch information
Guenter Roeck committed Mar 15, 2011
1 parent 06923f8 commit 442aba7
Show file tree
Hide file tree
Showing 6 changed files with 2,218 additions and 0 deletions.
25 changes: 25 additions & 0 deletions drivers/hwmon/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions drivers/hwmon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
203 changes: 203 additions & 0 deletions drivers/hwmon/pmbus.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*
* 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);
Loading

0 comments on commit 442aba7

Please sign in to comment.