-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
6 changed files
with
2,218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
Oops, something went wrong.