mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 07:18:51 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
133
drivers/hwmon/pmbus/Kconfig
Normal file
133
drivers/hwmon/pmbus/Kconfig
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#
|
||||
# PMBus chip drivers configuration
|
||||
#
|
||||
|
||||
menuconfig PMBUS
|
||||
tristate "PMBus support"
|
||||
depends on I2C
|
||||
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 y
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for generic
|
||||
PMBus devices, including but not limited to ADP4000, BMR453, BMR454,
|
||||
MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, UDT020, and TPS40400.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called pmbus.
|
||||
|
||||
config SENSORS_ADM1275
|
||||
tristate "Analog Devices ADM1275 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Analog
|
||||
Devices ADM1075, ADM1275, and ADM1276 Hot-Swap Controller and Digital
|
||||
Power Monitors.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called adm1275.
|
||||
|
||||
config SENSORS_LM25066
|
||||
tristate "National Semiconductor LM25066 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for National
|
||||
Semiconductor LM25056, LM25066, LM5064, and LM5066.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called lm25066.
|
||||
|
||||
config SENSORS_LTC2978
|
||||
tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Linear
|
||||
Technology LTC2974, LTC2978, LTC3880, and LTC3883.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc2978.
|
||||
|
||||
config SENSORS_MAX16064
|
||||
tristate "Maxim MAX16064"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX16064.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max16064.
|
||||
|
||||
config SENSORS_MAX34440
|
||||
tristate "Maxim MAX34440 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX34440, MAX34441, MAX34446, MAX34460, and MAX34461.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max34440.
|
||||
|
||||
config SENSORS_MAX8688
|
||||
tristate "Maxim MAX8688"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX8688.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max8688.
|
||||
|
||||
config SENSORS_TPS40422
|
||||
tristate "TI TPS40422"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for TI
|
||||
TPS40422.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tps40422.
|
||||
|
||||
config SENSORS_UCD9000
|
||||
tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for TI
|
||||
UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
|
||||
Controllers.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ucd9000.
|
||||
|
||||
config SENSORS_UCD9200
|
||||
tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for TI
|
||||
UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
|
||||
Digital PWM System Controllers.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ucd9200.
|
||||
|
||||
config SENSORS_ZL6100
|
||||
tristate "Intersil ZL6100 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Intersil
|
||||
ZL2004, ZL2005, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, ZL6105,
|
||||
ZL9101M, and ZL9117M Digital DC/DC Controllers, as well as for
|
||||
Ericsson BMR450, BMR451, BMR462, BMR463, and BMR464.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called zl6100.
|
||||
|
||||
endif # PMBUS
|
||||
16
drivers/hwmon/pmbus/Makefile
Normal file
16
drivers/hwmon/pmbus/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# Makefile for PMBus chip drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PMBUS) += pmbus_core.o
|
||||
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
||||
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
|
||||
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
||||
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
||||
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
||||
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
||||
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
||||
397
drivers/hwmon/pmbus/adm1275.c
Normal file
397
drivers/hwmon/pmbus/adm1275.c
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
|
||||
* and Digital Power Monitor
|
||||
*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { adm1075, adm1275, adm1276 };
|
||||
|
||||
#define ADM1275_PEAK_IOUT 0xd0
|
||||
#define ADM1275_PEAK_VIN 0xd1
|
||||
#define ADM1275_PEAK_VOUT 0xd2
|
||||
#define ADM1275_PMON_CONFIG 0xd4
|
||||
|
||||
#define ADM1275_VIN_VOUT_SELECT (1 << 6)
|
||||
#define ADM1275_VRANGE (1 << 5)
|
||||
#define ADM1075_IRANGE_50 (1 << 4)
|
||||
#define ADM1075_IRANGE_25 (1 << 3)
|
||||
#define ADM1075_IRANGE_MASK ((1 << 3) | (1 << 4))
|
||||
|
||||
#define ADM1275_IOUT_WARN2_LIMIT 0xd7
|
||||
#define ADM1275_DEVICE_CONFIG 0xd8
|
||||
|
||||
#define ADM1275_IOUT_WARN2_SELECT (1 << 4)
|
||||
|
||||
#define ADM1276_PEAK_PIN 0xda
|
||||
|
||||
#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0)
|
||||
|
||||
#define ADM1075_READ_VAUX 0xdd
|
||||
#define ADM1075_VAUX_OV_WARN_LIMIT 0xde
|
||||
#define ADM1075_VAUX_UV_WARN_LIMIT 0xdf
|
||||
#define ADM1075_VAUX_STATUS 0xf6
|
||||
|
||||
#define ADM1075_VAUX_OV_WARN (1<<7)
|
||||
#define ADM1075_VAUX_UV_WARN (1<<6)
|
||||
|
||||
struct adm1275_data {
|
||||
int id;
|
||||
bool have_oc_fault;
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#define to_adm1275_data(x) container_of(x, struct adm1275_data, info)
|
||||
|
||||
static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct adm1275_data *data = to_adm1275_data(info);
|
||||
int ret = 0;
|
||||
|
||||
if (page)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_IOUT_UC_FAULT_LIMIT:
|
||||
if (data->have_oc_fault) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
|
||||
break;
|
||||
case PMBUS_IOUT_OC_FAULT_LIMIT:
|
||||
if (!data->have_oc_fault) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
|
||||
break;
|
||||
case PMBUS_VOUT_OV_WARN_LIMIT:
|
||||
if (data->id != adm1075) {
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
ADM1075_VAUX_OV_WARN_LIMIT);
|
||||
break;
|
||||
case PMBUS_VOUT_UV_WARN_LIMIT:
|
||||
if (data->id != adm1075) {
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
ADM1075_VAUX_UV_WARN_LIMIT);
|
||||
break;
|
||||
case PMBUS_READ_VOUT:
|
||||
if (data->id != adm1075) {
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1075_READ_VAUX);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VIN_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_PIN_MAX:
|
||||
if (data->id == adm1275) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
if (data->id == adm1275)
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
u16 word)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (page)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_IOUT_UC_FAULT_LIMIT:
|
||||
case PMBUS_IOUT_OC_FAULT_LIMIT:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT,
|
||||
word);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct adm1275_data *data = to_adm1275_data(info);
|
||||
int mfr_status, ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_STATUS_IOUT:
|
||||
ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT);
|
||||
if (ret < 0)
|
||||
break;
|
||||
mfr_status = pmbus_read_byte_data(client, page,
|
||||
PMBUS_STATUS_MFR_SPECIFIC);
|
||||
if (mfr_status < 0) {
|
||||
ret = mfr_status;
|
||||
break;
|
||||
}
|
||||
if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) {
|
||||
ret |= data->have_oc_fault ?
|
||||
PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT;
|
||||
}
|
||||
break;
|
||||
case PMBUS_STATUS_VOUT:
|
||||
if (data->id != adm1075) {
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
mfr_status = pmbus_read_byte_data(client, 0,
|
||||
ADM1075_VAUX_STATUS);
|
||||
if (mfr_status & ADM1075_VAUX_OV_WARN)
|
||||
ret |= PB_VOLTAGE_OV_WARNING;
|
||||
if (mfr_status & ADM1075_VAUX_UV_WARN)
|
||||
ret |= PB_VOLTAGE_UV_WARNING;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1275_id[] = {
|
||||
{ "adm1075", adm1075 },
|
||||
{ "adm1275", adm1275 },
|
||||
{ "adm1276", adm1276 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1275_id);
|
||||
|
||||
static int adm1275_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
int config, device_config;
|
||||
int ret;
|
||||
struct pmbus_driver_info *info;
|
||||
struct adm1275_data *data;
|
||||
const struct i2c_device_id *mid;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read Manufacturer ID\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret != 3 || strncmp(block_buffer, "ADI", 3)) {
|
||||
dev_err(&client->dev, "Unsupported Manufacturer ID\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read Manufacturer Model\n");
|
||||
return ret;
|
||||
}
|
||||
for (mid = adm1275_id; mid->name[0]; mid++) {
|
||||
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
dev_err(&client->dev, "Unsupported device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (id->driver_data != mid->driver_data)
|
||||
dev_notice(&client->dev,
|
||||
"Device mismatch: Configured %s, detected %s\n",
|
||||
id->name, mid->name);
|
||||
|
||||
config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
|
||||
if (config < 0)
|
||||
return config;
|
||||
|
||||
device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG);
|
||||
if (device_config < 0)
|
||||
return device_config;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->id = mid->driver_data;
|
||||
|
||||
info = &data->info;
|
||||
|
||||
info->pages = 1;
|
||||
info->format[PSC_VOLTAGE_IN] = direct;
|
||||
info->format[PSC_VOLTAGE_OUT] = direct;
|
||||
info->format[PSC_CURRENT_OUT] = direct;
|
||||
info->m[PSC_CURRENT_OUT] = 807;
|
||||
info->b[PSC_CURRENT_OUT] = 20475;
|
||||
info->R[PSC_CURRENT_OUT] = -1;
|
||||
info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
|
||||
|
||||
info->read_word_data = adm1275_read_word_data;
|
||||
info->read_byte_data = adm1275_read_byte_data;
|
||||
info->write_word_data = adm1275_write_word_data;
|
||||
|
||||
if (data->id == adm1075) {
|
||||
info->m[PSC_VOLTAGE_IN] = 27169;
|
||||
info->b[PSC_VOLTAGE_IN] = 0;
|
||||
info->R[PSC_VOLTAGE_IN] = -1;
|
||||
info->m[PSC_VOLTAGE_OUT] = 27169;
|
||||
info->b[PSC_VOLTAGE_OUT] = 0;
|
||||
info->R[PSC_VOLTAGE_OUT] = -1;
|
||||
} else if (config & ADM1275_VRANGE) {
|
||||
info->m[PSC_VOLTAGE_IN] = 19199;
|
||||
info->b[PSC_VOLTAGE_IN] = 0;
|
||||
info->R[PSC_VOLTAGE_IN] = -2;
|
||||
info->m[PSC_VOLTAGE_OUT] = 19199;
|
||||
info->b[PSC_VOLTAGE_OUT] = 0;
|
||||
info->R[PSC_VOLTAGE_OUT] = -2;
|
||||
} else {
|
||||
info->m[PSC_VOLTAGE_IN] = 6720;
|
||||
info->b[PSC_VOLTAGE_IN] = 0;
|
||||
info->R[PSC_VOLTAGE_IN] = -1;
|
||||
info->m[PSC_VOLTAGE_OUT] = 6720;
|
||||
info->b[PSC_VOLTAGE_OUT] = 0;
|
||||
info->R[PSC_VOLTAGE_OUT] = -1;
|
||||
}
|
||||
|
||||
if (device_config & ADM1275_IOUT_WARN2_SELECT)
|
||||
data->have_oc_fault = true;
|
||||
|
||||
switch (data->id) {
|
||||
case adm1075:
|
||||
info->format[PSC_POWER] = direct;
|
||||
info->b[PSC_POWER] = 0;
|
||||
info->R[PSC_POWER] = -1;
|
||||
switch (config & ADM1075_IRANGE_MASK) {
|
||||
case ADM1075_IRANGE_25:
|
||||
info->m[PSC_POWER] = 8549;
|
||||
info->m[PSC_CURRENT_OUT] = 806;
|
||||
break;
|
||||
case ADM1075_IRANGE_50:
|
||||
info->m[PSC_POWER] = 4279;
|
||||
info->m[PSC_CURRENT_OUT] = 404;
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "Invalid input current range");
|
||||
info->m[PSC_POWER] = 0;
|
||||
info->m[PSC_CURRENT_OUT] = 0;
|
||||
break;
|
||||
}
|
||||
info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
|
||||
| PMBUS_HAVE_STATUS_INPUT;
|
||||
if (config & ADM1275_VIN_VOUT_SELECT)
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
break;
|
||||
case adm1275:
|
||||
if (config & ADM1275_VIN_VOUT_SELECT)
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
else
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
|
||||
break;
|
||||
case adm1276:
|
||||
info->format[PSC_POWER] = direct;
|
||||
info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
|
||||
| PMBUS_HAVE_STATUS_INPUT;
|
||||
if (config & ADM1275_VIN_VOUT_SELECT)
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
if (config & ADM1275_VRANGE) {
|
||||
info->m[PSC_POWER] = 6043;
|
||||
info->b[PSC_POWER] = 0;
|
||||
info->R[PSC_POWER] = -2;
|
||||
} else {
|
||||
info->m[PSC_POWER] = 2115;
|
||||
info->b[PSC_POWER] = 0;
|
||||
info->R[PSC_POWER] = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
|
||||
static struct i2c_driver adm1275_driver = {
|
||||
.driver = {
|
||||
.name = "adm1275",
|
||||
},
|
||||
.probe = adm1275_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = adm1275_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(adm1275_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
|
||||
MODULE_LICENSE("GPL");
|
||||
530
drivers/hwmon/pmbus/lm25066.c
Normal file
530
drivers/hwmon/pmbus/lm25066.c
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* Hardware monitoring driver for LM25056 / LM25063 / LM25066 / LM5064 / LM5066
|
||||
*
|
||||
* Copyright (c) 2011 Ericsson AB.
|
||||
* Copyright (c) 2013 Guenter Roeck
|
||||
*
|
||||
* 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/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 };
|
||||
|
||||
#define LM25066_READ_VAUX 0xd0
|
||||
#define LM25066_MFR_READ_IIN 0xd1
|
||||
#define LM25066_MFR_READ_PIN 0xd2
|
||||
#define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3
|
||||
#define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4
|
||||
#define LM25066_READ_PIN_PEAK 0xd5
|
||||
#define LM25066_CLEAR_PIN_PEAK 0xd6
|
||||
#define LM25066_DEVICE_SETUP 0xd9
|
||||
#define LM25066_READ_AVG_VIN 0xdc
|
||||
#define LM25066_READ_AVG_VOUT 0xdd
|
||||
#define LM25066_READ_AVG_IIN 0xde
|
||||
#define LM25066_READ_AVG_PIN 0xdf
|
||||
|
||||
#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */
|
||||
|
||||
/* LM25056 only */
|
||||
|
||||
#define LM25056_VAUX_OV_WARN_LIMIT 0xe3
|
||||
#define LM25056_VAUX_UV_WARN_LIMIT 0xe4
|
||||
|
||||
#define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1)
|
||||
#define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0)
|
||||
|
||||
/* LM25063 only */
|
||||
|
||||
#define LM25063_READ_VOUT_MAX 0xe5
|
||||
#define LM25063_READ_VOUT_MIN 0xe6
|
||||
|
||||
struct __coeff {
|
||||
short m, b, R;
|
||||
};
|
||||
|
||||
#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES)
|
||||
#define PSC_POWER_L (PSC_NUM_CLASSES + 1)
|
||||
|
||||
static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = {
|
||||
[lm25056] = {
|
||||
[PSC_VOLTAGE_IN] = {
|
||||
.m = 16296,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN] = {
|
||||
.m = 13797,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN_L] = {
|
||||
.m = 6726,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_POWER] = {
|
||||
.m = 5501,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_POWER_L] = {
|
||||
.m = 26882,
|
||||
.R = -4,
|
||||
},
|
||||
[PSC_TEMPERATURE] = {
|
||||
.m = 1580,
|
||||
.b = -14500,
|
||||
.R = -2,
|
||||
},
|
||||
},
|
||||
[lm25066] = {
|
||||
[PSC_VOLTAGE_IN] = {
|
||||
.m = 22070,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_VOLTAGE_OUT] = {
|
||||
.m = 22070,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN] = {
|
||||
.m = 13661,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN_L] = {
|
||||
.m = 6852,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_POWER] = {
|
||||
.m = 736,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_POWER_L] = {
|
||||
.m = 369,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_TEMPERATURE] = {
|
||||
.m = 16,
|
||||
},
|
||||
},
|
||||
[lm25063] = {
|
||||
[PSC_VOLTAGE_IN] = {
|
||||
.m = 16000,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_VOLTAGE_OUT] = {
|
||||
.m = 16000,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN] = {
|
||||
.m = 10000,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN_L] = {
|
||||
.m = 10000,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_POWER] = {
|
||||
.m = 5000,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_POWER_L] = {
|
||||
.m = 5000,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_TEMPERATURE] = {
|
||||
.m = 15596,
|
||||
.R = -3,
|
||||
},
|
||||
},
|
||||
[lm5064] = {
|
||||
[PSC_VOLTAGE_IN] = {
|
||||
.m = 4611,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_VOLTAGE_OUT] = {
|
||||
.m = 4621,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN] = {
|
||||
.m = 10742,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN_L] = {
|
||||
.m = 5456,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_POWER] = {
|
||||
.m = 1204,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_POWER_L] = {
|
||||
.m = 612,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_TEMPERATURE] = {
|
||||
.m = 16,
|
||||
},
|
||||
},
|
||||
[lm5066] = {
|
||||
[PSC_VOLTAGE_IN] = {
|
||||
.m = 4587,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_VOLTAGE_OUT] = {
|
||||
.m = 4587,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN] = {
|
||||
.m = 10753,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_CURRENT_IN_L] = {
|
||||
.m = 5405,
|
||||
.R = -2,
|
||||
},
|
||||
[PSC_POWER] = {
|
||||
.m = 1204,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_POWER_L] = {
|
||||
.m = 605,
|
||||
.R = -3,
|
||||
},
|
||||
[PSC_TEMPERATURE] = {
|
||||
.m = 16,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
struct lm25066_data {
|
||||
int id;
|
||||
u16 rlimit; /* Maximum register value */
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct lm25066_data *data = to_lm25066_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VMON:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX);
|
||||
if (ret < 0)
|
||||
break;
|
||||
/* Adjust returned value to match VIN coefficients */
|
||||
switch (data->id) {
|
||||
case lm25056:
|
||||
/* VIN: 6.14 mV VAUX: 293 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
|
||||
break;
|
||||
case lm25063:
|
||||
/* VIN: 6.25 mV VAUX: 200.0 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 20, 625);
|
||||
break;
|
||||
case lm25066:
|
||||
/* VIN: 4.54 mV VAUX: 283.2 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
|
||||
break;
|
||||
case lm5064:
|
||||
/* VIN: 4.53 mV VAUX: 700 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 70, 453);
|
||||
break;
|
||||
case lm5066:
|
||||
/* VIN: 2.18 mV VAUX: 725 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PMBUS_READ_IIN:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
|
||||
break;
|
||||
case PMBUS_READ_PIN:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN);
|
||||
break;
|
||||
case PMBUS_IIN_OC_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
LM25066_MFR_IIN_OC_WARN_LIMIT);
|
||||
break;
|
||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
LM25066_MFR_PIN_OP_WARN_LIMIT);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VIN_AVG:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_AVG:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IIN_AVG:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_PIN_AVG:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_PIN_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm25063_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MAX);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MIN:
|
||||
ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MIN);
|
||||
break;
|
||||
default:
|
||||
ret = lm25066_read_word_data(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm25056_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
LM25056_VAUX_UV_WARN_LIMIT);
|
||||
if (ret < 0)
|
||||
break;
|
||||
/* Adjust returned value to match VIN coefficients */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
LM25056_VAUX_OV_WARN_LIMIT);
|
||||
if (ret < 0)
|
||||
break;
|
||||
/* Adjust returned value to match VIN coefficients */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
|
||||
break;
|
||||
default:
|
||||
ret = lm25066_read_word_data(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret, s;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_STATUS_VMON:
|
||||
ret = pmbus_read_byte_data(client, 0,
|
||||
PMBUS_STATUS_MFR_SPECIFIC);
|
||||
if (ret < 0)
|
||||
break;
|
||||
s = 0;
|
||||
if (ret & LM25056_MFR_STS_VAUX_UV_WARN)
|
||||
s |= PB_VOLTAGE_UV_WARNING;
|
||||
if (ret & LM25056_MFR_STS_VAUX_OV_WARN)
|
||||
s |= PB_VOLTAGE_OV_WARNING;
|
||||
ret = s;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
u16 word)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct lm25066_data *data = to_lm25066_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_POUT_OP_FAULT_LIMIT:
|
||||
case PMBUS_POUT_OP_WARN_LIMIT:
|
||||
case PMBUS_VOUT_UV_WARN_LIMIT:
|
||||
case PMBUS_OT_FAULT_LIMIT:
|
||||
case PMBUS_OT_WARN_LIMIT:
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT:
|
||||
case PMBUS_VIN_UV_WARN_LIMIT:
|
||||
case PMBUS_VIN_UV_FAULT_LIMIT:
|
||||
case PMBUS_VIN_OV_FAULT_LIMIT:
|
||||
case PMBUS_VIN_OV_WARN_LIMIT:
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0, reg, word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_IIN_OC_WARN_LIMIT:
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25066_MFR_IIN_OC_WARN_LIMIT,
|
||||
word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25066_MFR_PIN_OP_WARN_LIMIT,
|
||||
word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||
/* Adjust from VIN coefficients (for LM25056) */
|
||||
word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25056_VAUX_UV_WARN_LIMIT, word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||
/* Adjust from VIN coefficients (for LM25056) */
|
||||
word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25056_VAUX_OV_WARN_LIMIT, word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm25066_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int config;
|
||||
struct lm25066_data *data;
|
||||
struct pmbus_driver_info *info;
|
||||
struct __coeff *coeff;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct lm25066_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP);
|
||||
if (config < 0)
|
||||
return config;
|
||||
|
||||
data->id = id->driver_data;
|
||||
info = &data->info;
|
||||
|
||||
info->pages = 1;
|
||||
info->format[PSC_VOLTAGE_IN] = direct;
|
||||
info->format[PSC_VOLTAGE_OUT] = direct;
|
||||
info->format[PSC_CURRENT_IN] = direct;
|
||||
info->format[PSC_TEMPERATURE] = direct;
|
||||
info->format[PSC_POWER] = direct;
|
||||
|
||||
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON
|
||||
| PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||
|
||||
if (data->id == lm25056) {
|
||||
info->func[0] |= PMBUS_HAVE_STATUS_VMON;
|
||||
info->read_word_data = lm25056_read_word_data;
|
||||
info->read_byte_data = lm25056_read_byte_data;
|
||||
data->rlimit = 0x0fff;
|
||||
} else if (data->id == lm25063) {
|
||||
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_POUT;
|
||||
info->read_word_data = lm25063_read_word_data;
|
||||
data->rlimit = 0xffff;
|
||||
} else {
|
||||
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
info->read_word_data = lm25066_read_word_data;
|
||||
data->rlimit = 0x0fff;
|
||||
}
|
||||
info->write_word_data = lm25066_write_word_data;
|
||||
|
||||
coeff = &lm25066_coeff[data->id][0];
|
||||
info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m;
|
||||
info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b;
|
||||
info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R;
|
||||
info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m;
|
||||
info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b;
|
||||
info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R;
|
||||
info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
|
||||
info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
|
||||
info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R;
|
||||
info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b;
|
||||
info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R;
|
||||
info->b[PSC_POWER] = coeff[PSC_POWER].b;
|
||||
info->R[PSC_POWER] = coeff[PSC_POWER].R;
|
||||
if (config & LM25066_DEV_SETUP_CL) {
|
||||
info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m;
|
||||
info->m[PSC_POWER] = coeff[PSC_POWER_L].m;
|
||||
} else {
|
||||
info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m;
|
||||
info->m[PSC_POWER] = coeff[PSC_POWER].m;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm25066_id[] = {
|
||||
{"lm25056", lm25056},
|
||||
{"lm25063", lm25063},
|
||||
{"lm25066", lm25066},
|
||||
{"lm5064", lm5064},
|
||||
{"lm5066", lm5066},
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, lm25066_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver lm25066_driver = {
|
||||
.driver = {
|
||||
.name = "lm25066",
|
||||
},
|
||||
.probe = lm25066_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = lm25066_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(lm25066_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips");
|
||||
MODULE_LICENSE("GPL");
|
||||
507
drivers/hwmon/pmbus/ltc2978.c
Normal file
507
drivers/hwmon/pmbus/ltc2978.c
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880,
|
||||
* LTC3883, and LTM4676
|
||||
*
|
||||
* Copyright (c) 2011 Ericsson AB.
|
||||
* Copyright (c) 2013, 2014 Guenter Roeck
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 };
|
||||
|
||||
/* Common for all chips */
|
||||
#define LTC2978_MFR_VOUT_PEAK 0xdd
|
||||
#define LTC2978_MFR_VIN_PEAK 0xde
|
||||
#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf
|
||||
#define LTC2978_MFR_SPECIAL_ID 0xe7
|
||||
|
||||
/* LTC2974, LCT2977, and LTC2978 */
|
||||
#define LTC2978_MFR_VOUT_MIN 0xfb
|
||||
#define LTC2978_MFR_VIN_MIN 0xfc
|
||||
#define LTC2978_MFR_TEMPERATURE_MIN 0xfd
|
||||
|
||||
/* LTC2974 only */
|
||||
#define LTC2974_MFR_IOUT_PEAK 0xd7
|
||||
#define LTC2974_MFR_IOUT_MIN 0xd8
|
||||
|
||||
/* LTC3880, LTC3883, and LTM4676 */
|
||||
#define LTC3880_MFR_IOUT_PEAK 0xd7
|
||||
#define LTC3880_MFR_CLEAR_PEAKS 0xe3
|
||||
#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
|
||||
|
||||
/* LTC3883 only */
|
||||
#define LTC3883_MFR_IIN_PEAK 0xe1
|
||||
|
||||
#define LTC2974_ID_REV1 0x0212
|
||||
#define LTC2974_ID_REV2 0x0213
|
||||
#define LTC2977_ID 0x0130
|
||||
#define LTC2978_ID_REV1 0x0121
|
||||
#define LTC2978_ID_REV2 0x0122
|
||||
#define LTC2978A_ID 0x0124
|
||||
#define LTC3880_ID 0x4000
|
||||
#define LTC3880_ID_MASK 0xff00
|
||||
#define LTC3883_ID 0x4300
|
||||
#define LTC3883_ID_MASK 0xff00
|
||||
#define LTM4676_ID 0x4480 /* datasheet claims 0x440X */
|
||||
#define LTM4676_ID_MASK 0xfff0
|
||||
|
||||
#define LTC2974_NUM_PAGES 4
|
||||
#define LTC2978_NUM_PAGES 8
|
||||
#define LTC3880_NUM_PAGES 2
|
||||
#define LTC3883_NUM_PAGES 1
|
||||
|
||||
/*
|
||||
* LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
|
||||
* happens pretty much each time chip data is updated. Raw peak data therefore
|
||||
* does not provide much value. To be able to provide useful peak data, keep an
|
||||
* internal cache of measured peak data, which is only cleared if an explicit
|
||||
* "clear peak" command is executed for the sensor in question.
|
||||
*/
|
||||
|
||||
struct ltc2978_data {
|
||||
enum chips id;
|
||||
u16 vin_min, vin_max;
|
||||
u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES];
|
||||
u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES];
|
||||
u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES];
|
||||
u16 iin_max;
|
||||
u16 temp2_max;
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info)
|
||||
|
||||
static inline int lin11_to_val(int data)
|
||||
{
|
||||
s16 e = ((s16)data) >> 11;
|
||||
s32 m = (((s16)(data << 5)) >> 5);
|
||||
|
||||
/*
|
||||
* mantissa is 10 bit + sign, exponent adds up to 15 bit.
|
||||
* Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31).
|
||||
*/
|
||||
e += 6;
|
||||
return (e < 0 ? m >> -e : m << e);
|
||||
}
|
||||
|
||||
static int ltc2978_read_word_data_common(struct i2c_client *client, int page,
|
||||
int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VIN_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret) > lin11_to_val(data->vin_max))
|
||||
data->vin_max = ret;
|
||||
ret = data->vin_max;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK);
|
||||
if (ret >= 0) {
|
||||
/*
|
||||
* VOUT is 16 bit unsigned with fixed exponent,
|
||||
* so we can compare it directly
|
||||
*/
|
||||
if (ret > data->vout_max[page])
|
||||
data->vout_max[page] = ret;
|
||||
ret = data->vout_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
LTC2978_MFR_TEMPERATURE_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
> lin11_to_val(data->temp_max[page]))
|
||||
data->temp_max[page] = ret;
|
||||
ret = data->temp_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VIN_MIN:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret) < lin11_to_val(data->vin_min))
|
||||
data->vin_min = ret;
|
||||
ret = data->vin_min;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MIN:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN);
|
||||
if (ret >= 0) {
|
||||
/*
|
||||
* VOUT_MIN is known to not be supported on some lots
|
||||
* of LTC2978 revision 1, and will return the maximum
|
||||
* possible voltage if read. If VOUT_MAX is valid and
|
||||
* lower than the reading of VOUT_MIN, use it instead.
|
||||
*/
|
||||
if (data->vout_max[page] && ret > data->vout_max[page])
|
||||
ret = data->vout_max[page];
|
||||
if (ret < data->vout_min[page])
|
||||
data->vout_min[page] = ret;
|
||||
ret = data->vout_min[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MIN:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
LTC2978_MFR_TEMPERATURE_MIN);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
< lin11_to_val(data->temp_min[page]))
|
||||
data->temp_min[page] = ret;
|
||||
ret = data->temp_min[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_READ_TEMP2_MAX:
|
||||
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
default:
|
||||
ret = ltc2978_read_word_data_common(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
> lin11_to_val(data->iout_max[page]))
|
||||
data->iout_max[page] = ret;
|
||||
ret = data->iout_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MIN:
|
||||
ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
< lin11_to_val(data->iout_min[page]))
|
||||
data->iout_min[page] = ret;
|
||||
ret = data->iout_min[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = ltc2978_read_word_data(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
> lin11_to_val(data->iout_max[page]))
|
||||
data->iout_max[page] = ret;
|
||||
ret = data->iout_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP2_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
LTC3880_MFR_TEMPERATURE2_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret) > lin11_to_val(data->temp2_max))
|
||||
data->temp2_max = ret;
|
||||
ret = data->temp2_max;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VIN_MIN:
|
||||
case PMBUS_VIRT_READ_VOUT_MIN:
|
||||
case PMBUS_VIRT_READ_TEMP_MIN:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = ltc2978_read_word_data_common(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_IIN_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
> lin11_to_val(data->iin_max))
|
||||
data->iin_max = ret;
|
||||
ret = data->iin_max;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IIN_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = ltc3880_read_word_data(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_clear_peaks(struct i2c_client *client, int page,
|
||||
enum chips id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (id == ltc3880 || id == ltc3883)
|
||||
ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
|
||||
else
|
||||
ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_write_word_data(struct i2c_client *client, int page,
|
||||
int reg, u16 word)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_RESET_IIN_HISTORY:
|
||||
data->iin_max = 0x7c00;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
data->iout_max[page] = 0x7c00;
|
||||
data->iout_min[page] = 0xfbff;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
|
||||
data->temp2_max = 0x7c00;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
data->vout_min[page] = 0xffff;
|
||||
data->vout_max[page] = 0;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
data->vin_min = 0x7bff;
|
||||
data->vin_max = 0x7c00;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
data->temp_min[page] = 0x7bff;
|
||||
data->temp_max[page] = 0x7c00;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc2978_id[] = {
|
||||
{"ltc2974", ltc2974},
|
||||
{"ltc2977", ltc2977},
|
||||
{"ltc2978", ltc2978},
|
||||
{"ltc3880", ltc3880},
|
||||
{"ltc3883", ltc3883},
|
||||
{"ltm4676", ltm4676},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
|
||||
|
||||
static int ltc2978_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int chip_id, i;
|
||||
struct ltc2978_data *data;
|
||||
struct pmbus_driver_info *info;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct ltc2978_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID);
|
||||
if (chip_id < 0)
|
||||
return chip_id;
|
||||
|
||||
if (chip_id == LTC2974_ID_REV1 || chip_id == LTC2974_ID_REV2) {
|
||||
data->id = ltc2974;
|
||||
} else if (chip_id == LTC2977_ID) {
|
||||
data->id = ltc2977;
|
||||
} else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2 ||
|
||||
chip_id == LTC2978A_ID) {
|
||||
data->id = ltc2978;
|
||||
} else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) {
|
||||
data->id = ltc3880;
|
||||
} else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) {
|
||||
data->id = ltc3883;
|
||||
} else if ((chip_id & LTM4676_ID_MASK) == LTM4676_ID) {
|
||||
data->id = ltm4676;
|
||||
} else {
|
||||
dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (data->id != id->driver_data)
|
||||
dev_warn(&client->dev,
|
||||
"Device mismatch: Configured %s, detected %s\n",
|
||||
id->name,
|
||||
ltc2978_id[data->id].name);
|
||||
|
||||
info = &data->info;
|
||||
info->write_word_data = ltc2978_write_word_data;
|
||||
|
||||
data->vin_min = 0x7bff;
|
||||
data->vin_max = 0x7c00;
|
||||
for (i = 0; i < ARRAY_SIZE(data->vout_min); i++)
|
||||
data->vout_min[i] = 0xffff;
|
||||
for (i = 0; i < ARRAY_SIZE(data->iout_min); i++)
|
||||
data->iout_min[i] = 0xfbff;
|
||||
for (i = 0; i < ARRAY_SIZE(data->iout_max); i++)
|
||||
data->iout_max[i] = 0x7c00;
|
||||
for (i = 0; i < ARRAY_SIZE(data->temp_min); i++)
|
||||
data->temp_min[i] = 0x7bff;
|
||||
for (i = 0; i < ARRAY_SIZE(data->temp_max); i++)
|
||||
data->temp_max[i] = 0x7c00;
|
||||
data->temp2_max = 0x7c00;
|
||||
|
||||
switch (data->id) {
|
||||
case ltc2974:
|
||||
info->read_word_data = ltc2974_read_word_data;
|
||||
info->pages = LTC2974_NUM_PAGES;
|
||||
info->func[0] = 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 ltc2977:
|
||||
case ltc2978:
|
||||
info->read_word_data = ltc2978_read_word_data;
|
||||
info->pages = LTC2978_NUM_PAGES;
|
||||
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||
for (i = 1; i < LTC2978_NUM_PAGES; i++) {
|
||||
info->func[i] = PMBUS_HAVE_VOUT
|
||||
| PMBUS_HAVE_STATUS_VOUT;
|
||||
}
|
||||
break;
|
||||
case ltc3880:
|
||||
case ltm4676:
|
||||
info->read_word_data = ltc3880_read_word_data;
|
||||
info->pages = LTC3880_NUM_PAGES;
|
||||
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
|
||||
| PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
|
||||
| PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
|
||||
info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_POUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||
break;
|
||||
case ltc3883:
|
||||
info->read_word_data = ltc3883_read_word_data;
|
||||
info->pages = LTC3883_NUM_PAGES;
|
||||
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
|
||||
| PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
|
||||
| PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ltc2978_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2978",
|
||||
},
|
||||
.probe = ltc2978_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = ltc2978_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ltc2978_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, LTC3883, and LTM4676");
|
||||
MODULE_LICENSE("GPL");
|
||||
127
drivers/hwmon/pmbus/max16064.c
Normal file
127
drivers/hwmon/pmbus/max16064.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Hardware monitoring driver for Maxim MAX16064
|
||||
*
|
||||
* Copyright (c) 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/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX16064_MFR_VOUT_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX16064_MFR_TEMPERATURE_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max16064_write_word_data(struct i2c_client *client, int page,
|
||||
int reg, u16 word)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX16064_MFR_VOUT_PEAK, 0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX16064_MFR_TEMPERATURE_PEAK,
|
||||
0xffff);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info max16064_info = {
|
||||
.pages = 4,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 19995,
|
||||
.b[PSC_VOLTAGE_IN] = 0,
|
||||
.R[PSC_VOLTAGE_IN] = -1,
|
||||
.m[PSC_VOLTAGE_OUT] = 19995,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = -1,
|
||||
.m[PSC_TEMPERATURE] = -7612,
|
||||
.b[PSC_TEMPERATURE] = 335,
|
||||
.R[PSC_TEMPERATURE] = -3,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP
|
||||
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.read_word_data = max16064_read_word_data,
|
||||
.write_word_data = max16064_write_word_data,
|
||||
};
|
||||
|
||||
static int max16064_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return pmbus_do_probe(client, id, &max16064_info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max16064_id[] = {
|
||||
{"max16064", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, max16064_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver max16064_driver = {
|
||||
.driver = {
|
||||
.name = "max16064",
|
||||
},
|
||||
.probe = max16064_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = max16064_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max16064_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064");
|
||||
MODULE_LICENSE("GPL");
|
||||
435
drivers/hwmon/pmbus/max34440.c
Normal file
435
drivers/hwmon/pmbus/max34440.c
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
* Hardware monitoring driver for Maxim MAX34440/MAX34441
|
||||
*
|
||||
* Copyright (c) 2011 Ericsson AB.
|
||||
* Copyright (c) 2012 Guenter Roeck
|
||||
*
|
||||
* 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/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { max34440, max34441, max34446, max34460, max34461 };
|
||||
|
||||
#define MAX34440_MFR_VOUT_PEAK 0xd4
|
||||
#define MAX34440_MFR_IOUT_PEAK 0xd5
|
||||
#define MAX34440_MFR_TEMPERATURE_PEAK 0xd6
|
||||
#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_FAULT (1 << 1)
|
||||
#define MAX34440_STATUS_OT_FAULT (1 << 5)
|
||||
#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)
|
||||
{
|
||||
int ret;
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct max34440_data *data = to_max34440_data(info);
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VOUT_MIN:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX34440_MFR_VOUT_MIN);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX34440_MFR_VOUT_PEAK);
|
||||
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:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX34440_MFR_IOUT_PEAK);
|
||||
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 && data->id != max34460 &&
|
||||
data->id != max34461)
|
||||
return -ENXIO;
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX34446_MFR_TEMPERATURE_AVG);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
MAX34440_MFR_TEMPERATURE_PEAK);
|
||||
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_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max34440_write_word_data(struct i2c_client *client, int page,
|
||||
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;
|
||||
|
||||
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:
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34440_MFR_VOUT_MIN, 0x7fff);
|
||||
if (ret)
|
||||
break;
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34440_MFR_VOUT_PEAK, 0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34440_MFR_IOUT_PEAK, 0);
|
||||
if (!ret && data->id == max34446)
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34446_MFR_IOUT_AVG, 0);
|
||||
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34440_MFR_TEMPERATURE_PEAK,
|
||||
0x8000);
|
||||
if (!ret && data->id == max34446)
|
||||
ret = pmbus_write_word_data(client, page,
|
||||
MAX34446_MFR_TEMPERATURE_AVG, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret = 0;
|
||||
int mfg_status;
|
||||
|
||||
if (page >= 0) {
|
||||
ret = pmbus_set_page(client, page);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_STATUS_IOUT:
|
||||
mfg_status = pmbus_read_word_data(client, 0,
|
||||
PMBUS_STATUS_MFR_SPECIFIC);
|
||||
if (mfg_status < 0)
|
||||
return mfg_status;
|
||||
if (mfg_status & MAX34440_STATUS_OC_WARN)
|
||||
ret |= PB_IOUT_OC_WARNING;
|
||||
if (mfg_status & MAX34440_STATUS_OC_FAULT)
|
||||
ret |= PB_IOUT_OC_FAULT;
|
||||
break;
|
||||
case PMBUS_STATUS_TEMPERATURE:
|
||||
mfg_status = pmbus_read_word_data(client, 0,
|
||||
PMBUS_STATUS_MFR_SPECIFIC);
|
||||
if (mfg_status < 0)
|
||||
return mfg_status;
|
||||
if (mfg_status & MAX34440_STATUS_OT_WARN)
|
||||
ret |= PB_TEMP_OT_WARNING;
|
||||
if (mfg_status & MAX34440_STATUS_OT_FAULT)
|
||||
ret |= PB_TEMP_OT_FAULT;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info max34440_info[] = {
|
||||
[max34440] = {
|
||||
.pages = 14,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 1,
|
||||
.b[PSC_VOLTAGE_IN] = 0,
|
||||
.R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */
|
||||
.m[PSC_VOLTAGE_OUT] = 1,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = 3, /* R = 0 in datasheet reflects mV */
|
||||
.m[PSC_CURRENT_OUT] = 1,
|
||||
.b[PSC_CURRENT_OUT] = 0,
|
||||
.R[PSC_CURRENT_OUT] = 3, /* R = 0 in datasheet reflects mA */
|
||||
.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,
|
||||
.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,
|
||||
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[13] = 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,
|
||||
},
|
||||
[max34441] = {
|
||||
.pages = 12,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.format[PSC_FAN] = 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_TEMPERATURE] = 1,
|
||||
.b[PSC_TEMPERATURE] = 0,
|
||||
.R[PSC_TEMPERATURE] = 2,
|
||||
.m[PSC_FAN] = 1,
|
||||
.b[PSC_FAN] = 0,
|
||||
.R[PSC_FAN] = 0,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.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,
|
||||
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
|
||||
.func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[11] = 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,
|
||||
},
|
||||
[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,
|
||||
},
|
||||
[max34460] = {
|
||||
.pages = 18,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 1,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = 3,
|
||||
.m[PSC_TEMPERATURE] = 1,
|
||||
.b[PSC_TEMPERATURE] = 0,
|
||||
.R[PSC_TEMPERATURE] = 2,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[14] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[15] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[17] = 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,
|
||||
},
|
||||
[max34461] = {
|
||||
.pages = 23,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 1,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = 3,
|
||||
.m[PSC_TEMPERATURE] = 1,
|
||||
.b[PSC_TEMPERATURE] = 0,
|
||||
.R[PSC_TEMPERATURE] = 2,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[12] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[13] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[14] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
.func[15] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
|
||||
/* page 16 is reserved */
|
||||
.func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[21] = 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,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
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[] = {
|
||||
{"max34440", max34440},
|
||||
{"max34441", max34441},
|
||||
{"max34446", max34446},
|
||||
{"max34460", max34460},
|
||||
{"max34461", max34461},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max34440_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver max34440_driver = {
|
||||
.driver = {
|
||||
.name = "max34440",
|
||||
},
|
||||
.probe = max34440_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = max34440_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max34440_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441");
|
||||
MODULE_LICENSE("GPL");
|
||||
204
drivers/hwmon/pmbus/max8688.c
Normal file
204
drivers/hwmon/pmbus/max8688.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Hardware monitoring driver for Maxim MAX8688
|
||||
*
|
||||
* Copyright (c) 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/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define MAX8688_MFR_VOUT_PEAK 0xd4
|
||||
#define MAX8688_MFR_IOUT_PEAK 0xd5
|
||||
#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6
|
||||
#define MAX8688_MFG_STATUS 0xd8
|
||||
|
||||
#define MAX8688_STATUS_OC_FAULT (1 << 4)
|
||||
#define MAX8688_STATUS_OV_FAULT (1 << 5)
|
||||
#define MAX8688_STATUS_OV_WARNING (1 << 8)
|
||||
#define MAX8688_STATUS_UV_FAULT (1 << 9)
|
||||
#define MAX8688_STATUS_UV_WARNING (1 << 10)
|
||||
#define MAX8688_STATUS_UC_FAULT (1 << 11)
|
||||
#define MAX8688_STATUS_OC_WARNING (1 << 12)
|
||||
#define MAX8688_STATUS_OT_FAULT (1 << 13)
|
||||
#define MAX8688_STATUS_OT_WARNING (1 << 14)
|
||||
|
||||
static int max8688_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (page)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MAX:
|
||||
ret = pmbus_read_word_data(client, 0,
|
||||
MAX8688_MFR_TEMPERATURE_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max8688_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
u16 word)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK,
|
||||
0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK,
|
||||
0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
MAX8688_MFR_TEMPERATURE_PEAK,
|
||||
0xffff);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret = 0;
|
||||
int mfg_status;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_STATUS_VOUT:
|
||||
mfg_status = pmbus_read_word_data(client, 0,
|
||||
MAX8688_MFG_STATUS);
|
||||
if (mfg_status < 0)
|
||||
return mfg_status;
|
||||
if (mfg_status & MAX8688_STATUS_UV_WARNING)
|
||||
ret |= PB_VOLTAGE_UV_WARNING;
|
||||
if (mfg_status & MAX8688_STATUS_UV_FAULT)
|
||||
ret |= PB_VOLTAGE_UV_FAULT;
|
||||
if (mfg_status & MAX8688_STATUS_OV_WARNING)
|
||||
ret |= PB_VOLTAGE_OV_WARNING;
|
||||
if (mfg_status & MAX8688_STATUS_OV_FAULT)
|
||||
ret |= PB_VOLTAGE_OV_FAULT;
|
||||
break;
|
||||
case PMBUS_STATUS_IOUT:
|
||||
mfg_status = pmbus_read_word_data(client, 0,
|
||||
MAX8688_MFG_STATUS);
|
||||
if (mfg_status < 0)
|
||||
return mfg_status;
|
||||
if (mfg_status & MAX8688_STATUS_UC_FAULT)
|
||||
ret |= PB_IOUT_UC_FAULT;
|
||||
if (mfg_status & MAX8688_STATUS_OC_WARNING)
|
||||
ret |= PB_IOUT_OC_WARNING;
|
||||
if (mfg_status & MAX8688_STATUS_OC_FAULT)
|
||||
ret |= PB_IOUT_OC_FAULT;
|
||||
break;
|
||||
case PMBUS_STATUS_TEMPERATURE:
|
||||
mfg_status = pmbus_read_word_data(client, 0,
|
||||
MAX8688_MFG_STATUS);
|
||||
if (mfg_status < 0)
|
||||
return mfg_status;
|
||||
if (mfg_status & MAX8688_STATUS_OT_WARNING)
|
||||
ret |= PB_TEMP_OT_WARNING;
|
||||
if (mfg_status & MAX8688_STATUS_OT_FAULT)
|
||||
ret |= PB_TEMP_OT_FAULT;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info max8688_info = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 19995,
|
||||
.b[PSC_VOLTAGE_IN] = 0,
|
||||
.R[PSC_VOLTAGE_IN] = -1,
|
||||
.m[PSC_VOLTAGE_OUT] = 19995,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = -1,
|
||||
.m[PSC_CURRENT_OUT] = 23109,
|
||||
.b[PSC_CURRENT_OUT] = 0,
|
||||
.R[PSC_CURRENT_OUT] = -2,
|
||||
.m[PSC_TEMPERATURE] = -7612,
|
||||
.b[PSC_TEMPERATURE] = 335,
|
||||
.R[PSC_TEMPERATURE] = -3,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP
|
||||
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_STATUS_TEMP,
|
||||
.read_byte_data = max8688_read_byte_data,
|
||||
.read_word_data = max8688_read_word_data,
|
||||
.write_word_data = max8688_write_word_data,
|
||||
};
|
||||
|
||||
static int max8688_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return pmbus_do_probe(client, id, &max8688_info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max8688_id[] = {
|
||||
{"max8688", 0},
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, max8688_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver max8688_driver = {
|
||||
.driver = {
|
||||
.name = "max8688",
|
||||
},
|
||||
.probe = max8688_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = max8688_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max8688_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688");
|
||||
MODULE_LICENSE("GPL");
|
||||
216
drivers/hwmon/pmbus/pmbus.c
Normal file
216
drivers/hwmon/pmbus/pmbus.c
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* 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_byte_register(client, 0, PMBUS_FAN_CONFIG_12) &&
|
||||
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_byte_register(client, 0, PMBUS_FAN_CONFIG_34) &&
|
||||
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_word_register(client, 0, PMBUS_READ_TEMPERATURE_2))
|
||||
info->func[0] |= PMBUS_HAVE_TEMP2;
|
||||
if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3))
|
||||
info->func[0] |= PMBUS_HAVE_TEMP3;
|
||||
if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
|
||||
| PMBUS_HAVE_TEMP3)
|
||||
&& 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)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
|
||||
int vout_mode;
|
||||
|
||||
vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
|
||||
if (vout_mode >= 0 && vout_mode != 0xff) {
|
||||
switch (vout_mode >> 5) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
info->format[PSC_VOLTAGE_OUT] = vid;
|
||||
break;
|
||||
case 2:
|
||||
info->format[PSC_VOLTAGE_OUT] = direct;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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. Until then, abort if a chip configured for direct
|
||||
* mode was detected.
|
||||
*/
|
||||
if (info->format[PSC_VOLTAGE_OUT] == direct) {
|
||||
ret = -ENODEV;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Try to find sensor groups */
|
||||
pmbus_find_sensor_groups(client, info);
|
||||
abort:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmbus_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pmbus_driver_info *info;
|
||||
|
||||
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->pages = id->driver_data;
|
||||
info->identify = pmbus_identify;
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use driver_data to set the number of pages supported by the chip.
|
||||
*/
|
||||
static const struct i2c_device_id pmbus_id[] = {
|
||||
{"adp4000", 1},
|
||||
{"bmr453", 1},
|
||||
{"bmr454", 1},
|
||||
{"mdt040", 1},
|
||||
{"ncp4200", 1},
|
||||
{"ncp4208", 1},
|
||||
{"pdt003", 1},
|
||||
{"pdt006", 1},
|
||||
{"pdt012", 1},
|
||||
{"pmbus", 0},
|
||||
{"tps40400", 1},
|
||||
{"udt020", 1},
|
||||
{}
|
||||
};
|
||||
|
||||
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_do_remove,
|
||||
.id_table = pmbus_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(pmbus_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("Generic PMBus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
387
drivers/hwmon/pmbus/pmbus.h
Normal file
387
drivers/hwmon/pmbus/pmbus.h
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* pmbus.h - Common defines and structures for PMBus devices
|
||||
*
|
||||
* Copyright (c) 2010, 2011 Ericsson AB.
|
||||
* Copyright (c) 2012 Guenter Roeck
|
||||
*
|
||||
* 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
|
||||
|
||||
/*
|
||||
* Virtual registers.
|
||||
* Useful to support attributes which are not supported by standard PMBus
|
||||
* registers but exist as manufacturer specific registers on individual chips.
|
||||
* Must be mapped to real registers in device specific code.
|
||||
*
|
||||
* Semantics:
|
||||
* Virtual registers are all word size.
|
||||
* READ registers are read-only; writes are either ignored or return an error.
|
||||
* RESET registers are read/write. Reading reset registers returns zero
|
||||
* (used for detection), writing any value causes the associated history to be
|
||||
* reset.
|
||||
* Virtual registers have to be handled in device specific driver code. Chip
|
||||
* driver code returns non-negative register values if a virtual register is
|
||||
* supported, or a negative error code if not. The chip driver may return
|
||||
* -ENODATA or any other error code in this case, though an error code other
|
||||
* than -ENODATA is handled more efficiently and thus preferred. Either case,
|
||||
* the calling PMBus core code will abort if the chip driver returns an error
|
||||
* code when reading or writing virtual registers.
|
||||
*/
|
||||
#define PMBUS_VIRT_BASE 0x100
|
||||
#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0)
|
||||
#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1)
|
||||
#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2)
|
||||
#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3)
|
||||
#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4)
|
||||
#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5)
|
||||
#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6)
|
||||
#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7)
|
||||
#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8)
|
||||
#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9)
|
||||
#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10)
|
||||
#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11)
|
||||
#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12)
|
||||
#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13)
|
||||
#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14)
|
||||
#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15)
|
||||
#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16)
|
||||
#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17)
|
||||
#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18)
|
||||
#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19)
|
||||
#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20)
|
||||
#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21)
|
||||
#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22)
|
||||
#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23)
|
||||
#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24)
|
||||
#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25)
|
||||
#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26)
|
||||
#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27)
|
||||
#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28)
|
||||
#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29)
|
||||
|
||||
#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30)
|
||||
#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31)
|
||||
#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32)
|
||||
#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33)
|
||||
#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34)
|
||||
#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35)
|
||||
|
||||
/*
|
||||
* 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)
|
||||
#define PMBUS_HAVE_VMON (1 << 18)
|
||||
#define PMBUS_HAVE_STATUS_VMON (1 << 19)
|
||||
|
||||
enum pmbus_data_format { linear = 0, direct, vid };
|
||||
|
||||
struct pmbus_driver_info {
|
||||
int pages; /* Total number of pages */
|
||||
enum pmbus_data_format format[PSC_NUM_CLASSES];
|
||||
/*
|
||||
* 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 following functions map manufacturing specific register values
|
||||
* to PMBus standard register values. Specify only if mapping is
|
||||
* necessary.
|
||||
* Functions return the register value (read) or zero (write) if
|
||||
* successful. A return value of -ENODATA indicates that there is no
|
||||
* manufacturer specific register, but that a standard PMBus register
|
||||
* may exist. Any other negative return value indicates that the
|
||||
* register does not exist, and that no attempt should be made to read
|
||||
* 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 (*write_word_data)(struct i2c_client *client, int page, int reg,
|
||||
u16 word);
|
||||
int (*write_byte)(struct i2c_client *client, int page, u8 value);
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
void pmbus_clear_cache(struct i2c_client *client);
|
||||
int pmbus_set_page(struct i2c_client *client, u8 page);
|
||||
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
|
||||
int pmbus_write_word_data(struct i2c_client *client, u8 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);
|
||||
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 */
|
||||
1803
drivers/hwmon/pmbus/pmbus_core.c
Normal file
1803
drivers/hwmon/pmbus/pmbus_core.c
Normal file
File diff suppressed because it is too large
Load diff
64
drivers/hwmon/pmbus/tps40422.c
Normal file
64
drivers/hwmon/pmbus/tps40422.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Hardware monitoring driver for TI TPS40422
|
||||
*
|
||||
* Copyright (c) 2014 Nokia Solutions and Networks.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
static struct pmbus_driver_info tps40422_info = {
|
||||
.pages = 2,
|
||||
.format[PSC_VOLTAGE_IN] = linear,
|
||||
.format[PSC_VOLTAGE_OUT] = linear,
|
||||
.format[PSC_TEMPERATURE] = linear,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP2
|
||||
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP2
|
||||
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
};
|
||||
|
||||
static int tps40422_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return pmbus_do_probe(client, id, &tps40422_info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tps40422_id[] = {
|
||||
{"tps40422", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tps40422_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver tps40422_driver = {
|
||||
.driver = {
|
||||
.name = "tps40422",
|
||||
},
|
||||
.probe = tps40422_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = tps40422_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(tps40422_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for TI TPS40422");
|
||||
MODULE_LICENSE("GPL");
|
||||
246
drivers/hwmon/pmbus/ucd9000.c
Normal file
246
drivers/hwmon/pmbus/ucd9000.c
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Hardware monitoring driver for UCD90xxx Sequencer and System Health
|
||||
* Controller series
|
||||
*
|
||||
* Copyright (C) 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/i2c.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 };
|
||||
|
||||
#define UCD9000_MONITOR_CONFIG 0xd5
|
||||
#define UCD9000_NUM_PAGES 0xd6
|
||||
#define UCD9000_FAN_CONFIG_INDEX 0xe7
|
||||
#define UCD9000_FAN_CONFIG 0xe8
|
||||
#define UCD9000_DEVICE_ID 0xfd
|
||||
|
||||
#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07)
|
||||
#define UCD9000_MON_PAGE(x) ((x) & 0x0f)
|
||||
|
||||
#define UCD9000_MON_VOLTAGE 1
|
||||
#define UCD9000_MON_TEMPERATURE 2
|
||||
#define UCD9000_MON_CURRENT 3
|
||||
#define UCD9000_MON_VOLTAGE_HW 4
|
||||
|
||||
#define UCD9000_NUM_FAN 4
|
||||
|
||||
struct ucd9000_data {
|
||||
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
|
||||
|
||||
static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
|
||||
{
|
||||
int fan_config = 0;
|
||||
struct ucd9000_data *data
|
||||
= to_ucd9000_data(pmbus_get_driver_info(client));
|
||||
|
||||
if (data->fan_data[fan][3] & 1)
|
||||
fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */
|
||||
|
||||
/* Pulses/revolution */
|
||||
fan_config |= (data->fan_data[fan][3] & 0x06) >> 1;
|
||||
|
||||
return fan_config;
|
||||
}
|
||||
|
||||
static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret = 0;
|
||||
int fan_config;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_FAN_CONFIG_12:
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
ret = ucd9000_get_fan_config(client, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fan_config = ret << 4;
|
||||
ret = ucd9000_get_fan_config(client, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fan_config |= ret;
|
||||
ret = fan_config;
|
||||
break;
|
||||
case PMBUS_FAN_CONFIG_34:
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
ret = ucd9000_get_fan_config(client, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fan_config = ret << 4;
|
||||
ret = ucd9000_get_fan_config(client, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
fan_config |= ret;
|
||||
ret = fan_config;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ucd9000_id[] = {
|
||||
{"ucd9000", ucd9000},
|
||||
{"ucd90120", ucd90120},
|
||||
{"ucd90124", ucd90124},
|
||||
{"ucd9090", ucd9090},
|
||||
{"ucd90910", ucd90910},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ucd9000_id);
|
||||
|
||||
static int ucd9000_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
struct ucd9000_data *data;
|
||||
struct pmbus_driver_info *info;
|
||||
const struct i2c_device_id *mid;
|
||||
int i, ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID,
|
||||
block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read device ID\n");
|
||||
return ret;
|
||||
}
|
||||
block_buffer[ret] = '\0';
|
||||
dev_info(&client->dev, "Device ID %s\n", block_buffer);
|
||||
|
||||
for (mid = ucd9000_id; mid->name[0]; mid++) {
|
||||
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
dev_err(&client->dev, "Unsupported device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (id->driver_data != ucd9000 && id->driver_data != mid->driver_data)
|
||||
dev_notice(&client->dev,
|
||||
"Device mismatch: Configured %s, detected %s\n",
|
||||
id->name, mid->name);
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
info = &data->info;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to read number of active pages\n");
|
||||
return ret;
|
||||
}
|
||||
info->pages = ret;
|
||||
if (!info->pages) {
|
||||
dev_err(&client->dev, "No pages configured\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* The internal temperature sensor is always active */
|
||||
info->func[0] = PMBUS_HAVE_TEMP;
|
||||
|
||||
/* Everything else is configurable */
|
||||
ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG,
|
||||
block_buffer);
|
||||
if (ret <= 0) {
|
||||
dev_err(&client->dev, "Failed to read configuration data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
for (i = 0; i < ret; i++) {
|
||||
int page = UCD9000_MON_PAGE(block_buffer[i]);
|
||||
|
||||
if (page >= info->pages)
|
||||
continue;
|
||||
|
||||
switch (UCD9000_MON_TYPE(block_buffer[i])) {
|
||||
case UCD9000_MON_VOLTAGE:
|
||||
case UCD9000_MON_VOLTAGE_HW:
|
||||
info->func[page] |= PMBUS_HAVE_VOUT
|
||||
| PMBUS_HAVE_STATUS_VOUT;
|
||||
break;
|
||||
case UCD9000_MON_TEMPERATURE:
|
||||
info->func[page] |= PMBUS_HAVE_TEMP2
|
||||
| PMBUS_HAVE_STATUS_TEMP;
|
||||
break;
|
||||
case UCD9000_MON_CURRENT:
|
||||
info->func[page] |= PMBUS_HAVE_IOUT
|
||||
| PMBUS_HAVE_STATUS_IOUT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fan configuration */
|
||||
if (mid->driver_data == ucd90124) {
|
||||
for (i = 0; i < UCD9000_NUM_FAN; i++) {
|
||||
i2c_smbus_write_byte_data(client,
|
||||
UCD9000_FAN_CONFIG_INDEX, i);
|
||||
ret = i2c_smbus_read_block_data(client,
|
||||
UCD9000_FAN_CONFIG,
|
||||
data->fan_data[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0);
|
||||
|
||||
info->read_byte_data = ucd9000_read_byte_data;
|
||||
info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
|
||||
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, mid, info);
|
||||
}
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ucd9000_driver = {
|
||||
.driver = {
|
||||
.name = "ucd9000",
|
||||
},
|
||||
.probe = ucd9000_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = ucd9000_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ucd9000_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
|
||||
MODULE_LICENSE("GPL");
|
||||
180
drivers/hwmon/pmbus/ucd9200.c
Normal file
180
drivers/hwmon/pmbus/ucd9200.c
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Hardware monitoring driver for ucd9200 series Digital PWM System Controllers
|
||||
*
|
||||
* Copyright (C) 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/i2c.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define UCD9200_PHASE_INFO 0xd2
|
||||
#define UCD9200_DEVICE_ID 0xfd
|
||||
|
||||
enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246,
|
||||
ucd9248 };
|
||||
|
||||
static const struct i2c_device_id ucd9200_id[] = {
|
||||
{"ucd9200", ucd9200},
|
||||
{"ucd9220", ucd9220},
|
||||
{"ucd9222", ucd9222},
|
||||
{"ucd9224", ucd9224},
|
||||
{"ucd9240", ucd9240},
|
||||
{"ucd9244", ucd9244},
|
||||
{"ucd9246", ucd9246},
|
||||
{"ucd9248", ucd9248},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ucd9200_id);
|
||||
|
||||
static int ucd9200_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
struct pmbus_driver_info *info;
|
||||
const struct i2c_device_id *mid;
|
||||
int i, j, ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID,
|
||||
block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read device ID\n");
|
||||
return ret;
|
||||
}
|
||||
block_buffer[ret] = '\0';
|
||||
dev_info(&client->dev, "Device ID %s\n", block_buffer);
|
||||
|
||||
for (mid = ucd9200_id; mid->name[0]; mid++) {
|
||||
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
dev_err(&client->dev, "Unsupported device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (id->driver_data != ucd9200 && id->driver_data != mid->driver_data)
|
||||
dev_notice(&client->dev,
|
||||
"Device mismatch: Configured %s, detected %s\n",
|
||||
id->name, mid->name);
|
||||
|
||||
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO,
|
||||
block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read phase information\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate number of configured pages (rails) from PHASE_INFO
|
||||
* register.
|
||||
* Rails have to be sequential, so we can abort after finding
|
||||
* the first unconfigured rail.
|
||||
*/
|
||||
info->pages = 0;
|
||||
for (i = 0; i < ret; i++) {
|
||||
if (!block_buffer[i])
|
||||
break;
|
||||
info->pages++;
|
||||
}
|
||||
if (!info->pages) {
|
||||
dev_err(&client->dev, "No rails configured\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_info(&client->dev, "%d rails configured\n", info->pages);
|
||||
|
||||
/*
|
||||
* Set PHASE registers on all pages to 0xff to ensure that phase
|
||||
* specific commands will apply to all phases of a given page (rail).
|
||||
* This only affects the READ_IOUT and READ_TEMPERATURE2 registers.
|
||||
* READ_IOUT will return the sum of currents of all phases of a rail,
|
||||
* and READ_TEMPERATURE2 will return the maximum temperature detected
|
||||
* for the the phases of the rail.
|
||||
*/
|
||||
for (i = 0; i < info->pages; i++) {
|
||||
/*
|
||||
* Setting PAGE & PHASE fails once in a while for no obvious
|
||||
* reason, so we need to retry a couple of times.
|
||||
*/
|
||||
for (j = 0; j < 3; j++) {
|
||||
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
ret = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
|
||||
0xff);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to initialize PHASE registers\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (info->pages > 1)
|
||||
i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
|
||||
|
||||
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP |
|
||||
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
|
||||
|
||||
for (i = 1; i < info->pages; i++)
|
||||
info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_POUT |
|
||||
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
|
||||
|
||||
/* ucd9240 supports a single fan */
|
||||
if (mid->driver_data == ucd9240)
|
||||
info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12;
|
||||
|
||||
return pmbus_do_probe(client, mid, info);
|
||||
}
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ucd9200_driver = {
|
||||
.driver = {
|
||||
.name = "ucd9200",
|
||||
},
|
||||
.probe = ucd9200_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = ucd9200_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ucd9200_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x");
|
||||
MODULE_LICENSE("GPL");
|
||||
419
drivers/hwmon/pmbus/zl6100.c
Normal file
419
drivers/hwmon/pmbus/zl6100.c
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* Hardware monitoring driver for ZL6100 and compatibles
|
||||
*
|
||||
* Copyright (c) 2011 Ericsson AB.
|
||||
* Copyright (c) 2012 Guenter Roeck
|
||||
*
|
||||
* 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/i2c.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/delay.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
|
||||
zl9101, zl9117 };
|
||||
|
||||
struct zl6100_data {
|
||||
int id;
|
||||
ktime_t access; /* chip access time */
|
||||
int delay; /* Delay between chip accesses in uS */
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#define to_zl6100_data(x) container_of(x, struct zl6100_data, info)
|
||||
|
||||
#define ZL6100_MFR_CONFIG 0xd0
|
||||
#define ZL6100_DEVICE_ID 0xe4
|
||||
|
||||
#define ZL6100_MFR_XTEMP_ENABLE (1 << 7)
|
||||
|
||||
#define MFR_VMON_OV_FAULT_LIMIT 0xf5
|
||||
#define MFR_VMON_UV_FAULT_LIMIT 0xf6
|
||||
#define MFR_READ_VMON 0xf7
|
||||
|
||||
#define VMON_UV_WARNING (1 << 5)
|
||||
#define VMON_OV_WARNING (1 << 4)
|
||||
#define VMON_UV_FAULT (1 << 1)
|
||||
#define VMON_OV_FAULT (1 << 0)
|
||||
|
||||
#define ZL6100_WAIT_TIME 1000 /* uS */
|
||||
|
||||
static ushort delay = ZL6100_WAIT_TIME;
|
||||
module_param(delay, ushort, 0644);
|
||||
MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
|
||||
|
||||
/* Convert linear sensor value to milli-units */
|
||||
static long zl6100_l2d(s16 l)
|
||||
{
|
||||
s16 exponent;
|
||||
s32 mantissa;
|
||||
long val;
|
||||
|
||||
exponent = l >> 11;
|
||||
mantissa = ((s16)((l & 0x7ff) << 5)) >> 5;
|
||||
|
||||
val = mantissa;
|
||||
|
||||
/* scale result to milli-units */
|
||||
val = val * 1000L;
|
||||
|
||||
if (exponent >= 0)
|
||||
val <<= exponent;
|
||||
else
|
||||
val >>= -exponent;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define MAX_MANTISSA (1023 * 1000)
|
||||
#define MIN_MANTISSA (511 * 1000)
|
||||
|
||||
static u16 zl6100_d2l(long val)
|
||||
{
|
||||
s16 exponent = 0, mantissa;
|
||||
bool negative = false;
|
||||
|
||||
/* simple case */
|
||||
if (val == 0)
|
||||
return 0;
|
||||
|
||||
if (val < 0) {
|
||||
negative = true;
|
||||
val = -val;
|
||||
}
|
||||
|
||||
/* Reduce large mantissa until it fits into 10 bit */
|
||||
while (val >= MAX_MANTISSA && exponent < 15) {
|
||||
exponent++;
|
||||
val >>= 1;
|
||||
}
|
||||
/* Increase small mantissa to improve precision */
|
||||
while (val < MIN_MANTISSA && exponent > -15) {
|
||||
exponent--;
|
||||
val <<= 1;
|
||||
}
|
||||
|
||||
/* Convert mantissa from milli-units to units */
|
||||
mantissa = DIV_ROUND_CLOSEST(val, 1000);
|
||||
|
||||
/* Ensure that resulting number is within range */
|
||||
if (mantissa > 0x3ff)
|
||||
mantissa = 0x3ff;
|
||||
|
||||
/* restore sign */
|
||||
if (negative)
|
||||
mantissa = -mantissa;
|
||||
|
||||
/* Convert to 5 bit exponent, 11 bit mantissa */
|
||||
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
|
||||
}
|
||||
|
||||
/* Some chips need a delay between accesses */
|
||||
static inline void zl6100_wait(const struct zl6100_data *data)
|
||||
{
|
||||
if (data->delay) {
|
||||
s64 delta = ktime_us_delta(ktime_get(), data->access);
|
||||
if (delta < data->delay)
|
||||
udelay(data->delay - delta);
|
||||
}
|
||||
}
|
||||
|
||||
static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct zl6100_data *data = to_zl6100_data(info);
|
||||
int ret, vreg;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
if (data->id == zl2005) {
|
||||
/*
|
||||
* Limit register detection is not reliable on ZL2005.
|
||||
* Make sure registers are not erroneously detected.
|
||||
*/
|
||||
switch (reg) {
|
||||
case PMBUS_VOUT_OV_WARN_LIMIT:
|
||||
case PMBUS_VOUT_UV_WARN_LIMIT:
|
||||
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VMON:
|
||||
vreg = MFR_READ_VMON;
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||
case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:
|
||||
vreg = MFR_VMON_OV_FAULT_LIMIT;
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||
case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:
|
||||
vreg = MFR_VMON_UV_FAULT_LIMIT;
|
||||
break;
|
||||
default:
|
||||
if (reg >= PMBUS_VIRT_BASE)
|
||||
return -ENXIO;
|
||||
vreg = reg;
|
||||
break;
|
||||
}
|
||||
|
||||
zl6100_wait(data);
|
||||
ret = pmbus_read_word_data(client, page, vreg);
|
||||
data->access = ktime_get();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||
ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10));
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||
ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10));
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct zl6100_data *data = to_zl6100_data(info);
|
||||
int ret, status;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
zl6100_wait(data);
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_STATUS_VMON:
|
||||
ret = pmbus_read_byte_data(client, 0,
|
||||
PMBUS_STATUS_MFR_SPECIFIC);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
status = 0;
|
||||
if (ret & VMON_UV_WARNING)
|
||||
status |= PB_VOLTAGE_UV_WARNING;
|
||||
if (ret & VMON_OV_WARNING)
|
||||
status |= PB_VOLTAGE_OV_WARNING;
|
||||
if (ret & VMON_UV_FAULT)
|
||||
status |= PB_VOLTAGE_UV_FAULT;
|
||||
if (ret & VMON_OV_FAULT)
|
||||
status |= PB_VOLTAGE_OV_FAULT;
|
||||
ret = status;
|
||||
break;
|
||||
default:
|
||||
ret = pmbus_read_byte_data(client, page, reg);
|
||||
break;
|
||||
}
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
u16 word)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct zl6100_data *data = to_zl6100_data(info);
|
||||
int ret, vreg;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||
word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9));
|
||||
vreg = MFR_VMON_OV_FAULT_LIMIT;
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:
|
||||
vreg = MFR_VMON_OV_FAULT_LIMIT;
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||
word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11));
|
||||
vreg = MFR_VMON_UV_FAULT_LIMIT;
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:
|
||||
vreg = MFR_VMON_UV_FAULT_LIMIT;
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
default:
|
||||
if (reg >= PMBUS_VIRT_BASE)
|
||||
return -ENXIO;
|
||||
vreg = reg;
|
||||
}
|
||||
|
||||
zl6100_wait(data);
|
||||
ret = pmbus_write_word_data(client, page, vreg, word);
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct zl6100_data *data = to_zl6100_data(info);
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
zl6100_wait(data);
|
||||
ret = pmbus_write_byte(client, page, value);
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id zl6100_id[] = {
|
||||
{"bmr450", zl2005},
|
||||
{"bmr451", zl2005},
|
||||
{"bmr462", zl2008},
|
||||
{"bmr463", zl2008},
|
||||
{"bmr464", zl2008},
|
||||
{"zl2004", zl2004},
|
||||
{"zl2005", zl2005},
|
||||
{"zl2006", zl2006},
|
||||
{"zl2008", zl2008},
|
||||
{"zl2105", zl2105},
|
||||
{"zl2106", zl2106},
|
||||
{"zl6100", zl6100},
|
||||
{"zl6105", zl6105},
|
||||
{"zl9101", zl9101},
|
||||
{"zl9117", zl9117},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, zl6100_id);
|
||||
|
||||
static int zl6100_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct zl6100_data *data;
|
||||
struct pmbus_driver_info *info;
|
||||
u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
const struct i2c_device_id *mid;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
|
||||
device_id);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read device ID\n");
|
||||
return ret;
|
||||
}
|
||||
device_id[ret] = '\0';
|
||||
dev_info(&client->dev, "Device ID %s\n", device_id);
|
||||
|
||||
mid = NULL;
|
||||
for (mid = zl6100_id; mid->name[0]; mid++) {
|
||||
if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
dev_err(&client->dev, "Unsupported device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (id->driver_data != mid->driver_data)
|
||||
dev_notice(&client->dev,
|
||||
"Device mismatch: Configured %s, detected %s\n",
|
||||
id->name, mid->name);
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->id = mid->driver_data;
|
||||
|
||||
/*
|
||||
* According to information from the chip vendor, all currently
|
||||
* supported chips are known to require a wait time between I2C
|
||||
* accesses.
|
||||
*/
|
||||
data->delay = delay;
|
||||
|
||||
/*
|
||||
* Since there was a direct I2C device access above, wait before
|
||||
* accessing the chip again.
|
||||
*/
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
|
||||
info = &data->info;
|
||||
|
||||
info->pages = 1;
|
||||
info->func[0] = PMBUS_HAVE_VIN | 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;
|
||||
|
||||
/*
|
||||
* ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage
|
||||
* (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon.
|
||||
*/
|
||||
if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117)
|
||||
info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret & ZL6100_MFR_XTEMP_ENABLE)
|
||||
info->func[0] |= PMBUS_HAVE_TEMP2;
|
||||
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
|
||||
info->read_word_data = zl6100_read_word_data;
|
||||
info->read_byte_data = zl6100_read_byte_data;
|
||||
info->write_word_data = zl6100_write_word_data;
|
||||
info->write_byte = zl6100_write_byte;
|
||||
|
||||
return pmbus_do_probe(client, mid, info);
|
||||
}
|
||||
|
||||
static struct i2c_driver zl6100_driver = {
|
||||
.driver = {
|
||||
.name = "zl6100",
|
||||
},
|
||||
.probe = zl6100_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
.id_table = zl6100_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(zl6100_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Add table
Add a link
Reference in a new issue