Commit 8d28cd1b authored by Guenter Roeck's avatar Guenter Roeck
Browse files

hwmon: (pmbus) Add client driver for LTC3815



LTC3815 is a Monolithic Synchronous DC/DC Step-Down Converter.

Cc: Michael Jones <mike@proclivis.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 2ff44072
Loading
Loading
Loading
Loading
+61 −0
Original line number Original line Diff line number Diff line
Kernel driver ltc3815
=====================

Supported chips:
  * Linear Technology LTC3815
    Prefix: 'ltc3815'
    Addresses scanned: -
    Datasheet: http://www.linear.com/product/ltc3815

Author: Guenter Roeck <linux@roeck-us.net>


Description
-----------

LTC3815 is a Monolithic Synchronous DC/DC Step-Down Converter.


Usage Notes
-----------

This driver does not probe for PMBus devices. You will have to instantiate
devices explicitly.

Example: the following commands will load the driver for an LTC3815
at address 0x20 on I2C bus #1:

# modprobe ltc3815
# echo ltc3815 0x20 > /sys/bus/i2c/devices/i2c-1/new_device


Sysfs attributes
----------------

in1_label		"vin"
in1_input		Measured input voltage.
in1_alarm		Input voltage alarm.
in1_highest		Highest input voltage.
in1_reset_history	Reset input voltage history.

in2_label		"vout1".
in2_input		Measured output voltage.
in2_alarm		Output voltage alarm.
in2_highest		Highest output voltage.
in2_reset_history	Reset output voltage history.

temp1_input		Measured chip temperature.
temp1_alarm		Temperature alarm.
temp1_highest		Highest measured temperature.
temp1_reset_history	Reset temperature history.

curr1_label		"iin".
curr1_input		Measured input current.
curr1_highest		Highest input current.
curr1_reset_history	Reset input current history.

curr2_label		"iout1".
curr2_input		Measured output current.
curr2_alarm		Output current alarm.
curr2_highest		Highest output current.
curr2_reset_history	Reset output current history.
+10 −0
Original line number Original line Diff line number Diff line
@@ -65,6 +65,16 @@ config SENSORS_LTC2978_REGULATOR
	  If you say yes here you get regulator support for Linear
	  If you say yes here you get regulator support for Linear
	  Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
	  Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.


config SENSORS_LTC3815
	tristate "Linear Technologies LTC3815"
	default n
	help
	  If you say yes here you get hardware monitoring support for Linear
	  Technology LTC3815.

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

config SENSORS_MAX16064
config SENSORS_MAX16064
	tristate "Maxim MAX16064"
	tristate "Maxim MAX16064"
	default n
	default n
+1 −0
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o
obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o
obj-$(CONFIG_SENSORS_LM25066)	+= lm25066.o
obj-$(CONFIG_SENSORS_LM25066)	+= lm25066.o
obj-$(CONFIG_SENSORS_LTC2978)	+= ltc2978.o
obj-$(CONFIG_SENSORS_LTC2978)	+= ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815)	+= ltc3815.o
obj-$(CONFIG_SENSORS_MAX16064)	+= max16064.o
obj-$(CONFIG_SENSORS_MAX16064)	+= max16064.o
obj-$(CONFIG_SENSORS_MAX20751)	+= max20751.o
obj-$(CONFIG_SENSORS_MAX20751)	+= max20751.o
obj-$(CONFIG_SENSORS_MAX34440)	+= max34440.o
obj-$(CONFIG_SENSORS_MAX34440)	+= max34440.o
+215 −0
Original line number Original line Diff line number Diff line
/*
 * Hardware monitoring driver for LTC3815
 *
 * Copyright (c) 2015 Linear Technology
 * Copyright (c) 2015 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/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"

#define LTC3815_MFR_IOUT_PEAK	0xd7
#define LTC3815_MFR_VOUT_PEAK	0xdd
#define LTC3815_MFR_VIN_PEAK	0xde
#define LTC3815_MFR_TEMP_PEAK	0xdf
#define LTC3815_MFR_IIN_PEAK	0xe1
#define LTC3815_MFR_SPECIAL_ID	0xe7

#define LTC3815_ID		0x8000
#define LTC3815_ID_MASK		0xff00

static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg)
{
	int ret;

	switch (reg) {
	case PMBUS_VOUT_MODE:
		/*
		 * The chip returns 0x3e, suggesting VID mode with manufacturer
		 * specific VID codes. Since the output voltage is reported
		 * with a LSB of 0.5mV, override and report direct mode with
		 * appropriate coefficients.
		 */
		ret = 0x40;
		break;
	default:
		ret = -ENODATA;
		break;
	}
	return ret;
}

static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg)
{
	int ret;

	switch (reg) {
	case PMBUS_CLEAR_FAULTS:
		/*
		 * LTC3815 does not support the CLEAR_FAULTS command.
		 * Emulate it by clearing the status register.
		 */
		ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD);
		if (ret > 0) {
			pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD,
					      ret);
			ret = 0;
		}
		break;
	default:
		ret = -ENODATA;
		break;
	}
	return ret;
}

static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg)
{
	int ret;

	switch (reg) {
	case PMBUS_VIRT_READ_VIN_MAX:
		ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK);
		break;
	case PMBUS_VIRT_READ_VOUT_MAX:
		ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK);
		break;
	case PMBUS_VIRT_READ_TEMP_MAX:
		ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK);
		break;
	case PMBUS_VIRT_READ_IOUT_MAX:
		ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK);
		break;
	case PMBUS_VIRT_READ_IIN_MAX:
		ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK);
		break;
	case PMBUS_VIRT_RESET_VOUT_HISTORY:
	case PMBUS_VIRT_RESET_VIN_HISTORY:
	case PMBUS_VIRT_RESET_TEMP_HISTORY:
	case PMBUS_VIRT_RESET_IOUT_HISTORY:
	case PMBUS_VIRT_RESET_IIN_HISTORY:
		ret = 0;
		break;
	default:
		ret = -ENODATA;
		break;
	}
	return ret;
}

static int ltc3815_write_word_data(struct i2c_client *client, int page,
				   int reg, u16 word)
{
	int ret;

	switch (reg) {
	case PMBUS_VIRT_RESET_IIN_HISTORY:
		ret = pmbus_write_word_data(client, page,
					    LTC3815_MFR_IIN_PEAK, 0);
		break;
	case PMBUS_VIRT_RESET_IOUT_HISTORY:
		ret = pmbus_write_word_data(client, page,
					    LTC3815_MFR_IOUT_PEAK, 0);
		break;
	case PMBUS_VIRT_RESET_VOUT_HISTORY:
		ret = pmbus_write_word_data(client, page,
					    LTC3815_MFR_VOUT_PEAK, 0);
		break;
	case PMBUS_VIRT_RESET_VIN_HISTORY:
		ret = pmbus_write_word_data(client, page,
					    LTC3815_MFR_VIN_PEAK, 0);
		break;
	case PMBUS_VIRT_RESET_TEMP_HISTORY:
		ret = pmbus_write_word_data(client, page,
					    LTC3815_MFR_TEMP_PEAK, 0);
		break;
	default:
		ret = -ENODATA;
		break;
	}
	return ret;
}

static const struct i2c_device_id ltc3815_id[] = {
	{"ltc3815", 0},
	{ }
};
MODULE_DEVICE_TABLE(i2c, ltc3815_id);

static struct pmbus_driver_info ltc3815_info = {
	.pages = 1,
	.format[PSC_VOLTAGE_IN] = direct,
	.format[PSC_VOLTAGE_OUT] = direct,
	.format[PSC_CURRENT_IN] = direct,
	.format[PSC_CURRENT_OUT] = direct,
	.format[PSC_TEMPERATURE] = direct,
	.m[PSC_VOLTAGE_IN] = 250,
	.b[PSC_VOLTAGE_IN] = 0,
	.R[PSC_VOLTAGE_IN] = 0,
	.m[PSC_VOLTAGE_OUT] = 2,
	.b[PSC_VOLTAGE_OUT] = 0,
	.R[PSC_VOLTAGE_OUT] = 3,
	.m[PSC_CURRENT_IN] = 1,
	.b[PSC_CURRENT_IN] = 0,
	.R[PSC_CURRENT_IN] = 2,
	.m[PSC_CURRENT_OUT] = 1,
	.b[PSC_CURRENT_OUT] = 0,
	.R[PSC_CURRENT_OUT] = 2,
	.m[PSC_TEMPERATURE] = 1,
	.b[PSC_TEMPERATURE] = 0,
	.R[PSC_TEMPERATURE] = 0,
	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT |
		PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
	.read_byte_data = ltc3815_read_byte_data,
	.read_word_data = ltc3815_read_word_data,
	.write_byte = ltc3815_write_byte,
	.write_word_data = ltc3815_write_word_data,
};

static int ltc3815_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	int chip_id;

	if (!i2c_check_functionality(client->adapter,
				     I2C_FUNC_SMBUS_READ_WORD_DATA))
		return -ENODEV;

	chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
	if (chip_id < 0)
		return chip_id;
	if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
		return -ENODEV;

	return pmbus_do_probe(client, id, &ltc3815_info);
}

static struct i2c_driver ltc3815_driver = {
	.driver = {
		   .name = "ltc3815",
		   },
	.probe = ltc3815_probe,
	.remove = pmbus_do_remove,
	.id_table = ltc3815_id,
};

module_i2c_driver(ltc3815_driver);

MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for LTC3815");
MODULE_LICENSE("GPL");