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

Commit e04d1ce9 authored by Michael Jones's avatar Michael Jones Committed by Guenter Roeck
Browse files

hwmon: (ltc2978) Add polling for chips requiring it



Some of the LTC chips supported by this driver have to be polled
to ensure that they are ready to accept commands.

Signed-off-by: default avatarMichael Jones <mike@proclivis.com>
[Guenter Roeck: simplifications and formatting changes]
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent d830e27d
Loading
Loading
Loading
Loading
+101 −10
Original line number Original line Diff line number Diff line
@@ -3,6 +3,7 @@
 *
 *
 * Copyright (c) 2011 Ericsson AB.
 * Copyright (c) 2011 Ericsson AB.
 * Copyright (c) 2013, 2014, 2015 Guenter Roeck
 * Copyright (c) 2013, 2014, 2015 Guenter Roeck
 * Copyright (c) 2015 Linear Technology
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * 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
 * it under the terms of the GNU General Public License as published by
@@ -15,6 +16,8 @@
 * GNU General Public License for more details.
 * GNU General Public License for more details.
 */
 */


#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/init.h>
@@ -32,6 +35,7 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882,
#define LTC2978_MFR_VIN_PEAK		0xde
#define LTC2978_MFR_VIN_PEAK		0xde
#define LTC2978_MFR_TEMPERATURE_PEAK	0xdf
#define LTC2978_MFR_TEMPERATURE_PEAK	0xdf
#define LTC2978_MFR_SPECIAL_ID		0xe7	/* Undocumented on LTC3882 */
#define LTC2978_MFR_SPECIAL_ID		0xe7	/* Undocumented on LTC3882 */
#define LTC2978_MFR_COMMON		0xef


/* LTC2974, LTC2975, LCT2977, LTC2980, LTC2978, and LTM2987 */
/* LTC2974, LTC2975, LCT2977, LTC2980, LTC2978, and LTM2987 */
#define LTC2978_MFR_VOUT_MIN		0xfb
#define LTC2978_MFR_VOUT_MIN		0xfb
@@ -82,6 +86,11 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882,
#define LTC3880_NUM_PAGES		2
#define LTC3880_NUM_PAGES		2
#define LTC3883_NUM_PAGES		1
#define LTC3883_NUM_PAGES		1


#define LTC_POLL_TIMEOUT		100	/* in milli-seconds */

#define LTC_NOT_BUSY			BIT(5)
#define LTC_NOT_PENDING			BIT(4)

/*
/*
 * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
 * 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
 * happens pretty much each time chip data is updated. Raw peak data therefore
@@ -105,8 +114,81 @@ struct ltc2978_data {
#define to_ltc2978_data(x)  container_of(x, struct ltc2978_data, info)
#define to_ltc2978_data(x)  container_of(x, struct ltc2978_data, info)


#define FEAT_CLEAR_PEAKS	BIT(0)
#define FEAT_CLEAR_PEAKS	BIT(0)
#define FEAT_NEEDS_POLLING	BIT(1)


#define has_clear_peaks(d)	((d)->features & FEAT_CLEAR_PEAKS)
#define has_clear_peaks(d)	((d)->features & FEAT_CLEAR_PEAKS)
#define needs_polling(d)	((d)->features & FEAT_NEEDS_POLLING)

static int ltc_wait_ready(struct i2c_client *client)
{
	unsigned long timeout = jiffies + msecs_to_jiffies(LTC_POLL_TIMEOUT);
	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
	struct ltc2978_data *data = to_ltc2978_data(info);
	int status;
	u8 mask;

	if (!needs_polling(data))
		return 0;

	/*
	 * LTC3883 does not support LTC_NOT_PENDING, even though
	 * the datasheet claims that it does.
	 */
	mask = LTC_NOT_BUSY;
	if (data->id != ltc3883)
		mask |= LTC_NOT_PENDING;

	do {
		status = pmbus_read_byte_data(client, 0, LTC2978_MFR_COMMON);
		if (status == -EBADMSG || status == -ENXIO) {
			/* PEC error or NACK: chip may be busy, try again */
			usleep_range(50, 100);
			continue;
		}
		if (status < 0)
			return status;

		if ((status & mask) == mask)
			return 0;

		usleep_range(50, 100);
	} while (time_before(jiffies, timeout));

	return -ETIMEDOUT;
}

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

	ret = ltc_wait_ready(client);
	if (ret < 0)
		return ret;

	return pmbus_read_word_data(client, page, reg);
}

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

	ret = ltc_wait_ready(client);
	if (ret < 0)
		return ret;

	return pmbus_read_byte_data(client, page, reg);
}

static int ltc_write_byte(struct i2c_client *client, int page, u8 byte)
{
	int ret;

	ret = ltc_wait_ready(client);
	if (ret < 0)
		return ret;

	return pmbus_write_byte(client, page, byte);
}


static inline int lin11_to_val(int data)
static inline int lin11_to_val(int data)
{
{
@@ -126,7 +208,7 @@ static int ltc_get_max(struct ltc2978_data *data, struct i2c_client *client,
{
{
	int ret;
	int ret;


	ret = pmbus_read_word_data(client, page, reg);
	ret = ltc_read_word_data(client, page, reg);
	if (ret >= 0) {
	if (ret >= 0) {
		if (lin11_to_val(ret) > lin11_to_val(*pmax))
		if (lin11_to_val(ret) > lin11_to_val(*pmax))
			*pmax = ret;
			*pmax = ret;
@@ -140,7 +222,7 @@ static int ltc_get_min(struct ltc2978_data *data, struct i2c_client *client,
{
{
	int ret;
	int ret;


	ret = pmbus_read_word_data(client, page, reg);
	ret = ltc_read_word_data(client, page, reg);
	if (ret >= 0) {
	if (ret >= 0) {
		if (lin11_to_val(ret) < lin11_to_val(*pmin))
		if (lin11_to_val(ret) < lin11_to_val(*pmin))
			*pmin = ret;
			*pmin = ret;
@@ -162,7 +244,7 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page,
				  &data->vin_max);
				  &data->vin_max);
		break;
		break;
	case PMBUS_VIRT_READ_VOUT_MAX:
	case PMBUS_VIRT_READ_VOUT_MAX:
		ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK);
		ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK);
		if (ret >= 0) {
		if (ret >= 0) {
			/*
			/*
			 * VOUT is 16 bit unsigned with fixed exponent,
			 * VOUT is 16 bit unsigned with fixed exponent,
@@ -184,6 +266,9 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page,
		ret = 0;
		ret = 0;
		break;
		break;
	default:
	default:
		ret = ltc_wait_ready(client);
		if (ret < 0)
			return ret;
		ret = -ENODATA;
		ret = -ENODATA;
		break;
		break;
	}
	}
@@ -202,7 +287,7 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
				  &data->vin_min);
				  &data->vin_min);
		break;
		break;
	case PMBUS_VIRT_READ_VOUT_MIN:
	case PMBUS_VIRT_READ_VOUT_MIN:
		ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN);
		ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_MIN);
		if (ret >= 0) {
		if (ret >= 0) {
			/*
			/*
			 * VOUT_MIN is known to not be supported on some lots
			 * VOUT_MIN is known to not be supported on some lots
@@ -353,9 +438,9 @@ static int ltc2978_clear_peaks(struct ltc2978_data *data,
	int ret;
	int ret;


	if (has_clear_peaks(data))
	if (has_clear_peaks(data))
		ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
		ret = ltc_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
	else
	else
		ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
		ret = ltc_write_byte(client, page, PMBUS_CLEAR_FAULTS);


	return ret;
	return ret;
}
}
@@ -403,6 +488,9 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
		ret = ltc2978_clear_peaks(data, client, page);
		ret = ltc2978_clear_peaks(data, client, page);
		break;
		break;
	default:
	default:
		ret = ltc_wait_ready(client);
		if (ret < 0)
			return ret;
		ret = -ENODATA;
		ret = -ENODATA;
		break;
		break;
	}
	}
@@ -530,6 +618,9 @@ static int ltc2978_probe(struct i2c_client *client,


	info = &data->info;
	info = &data->info;
	info->write_word_data = ltc2978_write_word_data;
	info->write_word_data = ltc2978_write_word_data;
	info->write_byte = ltc_write_byte;
	info->read_word_data = ltc_read_word_data;
	info->read_byte_data = ltc_read_byte_data;


	data->vin_min = 0x7bff;
	data->vin_min = 0x7bff;
	data->vin_max = 0x7c00;
	data->vin_max = 0x7c00;
@@ -588,7 +679,7 @@ static int ltc2978_probe(struct i2c_client *client,
	case ltc3880:
	case ltc3880:
	case ltc3887:
	case ltc3887:
	case ltm4676:
	case ltm4676:
		data->features |= FEAT_CLEAR_PEAKS;
		data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
		info->read_word_data = ltc3880_read_word_data;
		info->read_word_data = ltc3880_read_word_data;
		info->pages = LTC3880_NUM_PAGES;
		info->pages = LTC3880_NUM_PAGES;
		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
@@ -603,7 +694,7 @@ static int ltc2978_probe(struct i2c_client *client,
		  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
		  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
		break;
		break;
	case ltc3882:
	case ltc3882:
		data->features |= FEAT_CLEAR_PEAKS;
		data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
		info->read_word_data = ltc3880_read_word_data;
		info->read_word_data = ltc3880_read_word_data;
		info->pages = LTC3880_NUM_PAGES;
		info->pages = LTC3880_NUM_PAGES;
		info->func[0] = PMBUS_HAVE_VIN
		info->func[0] = PMBUS_HAVE_VIN
@@ -618,7 +709,7 @@ static int ltc2978_probe(struct i2c_client *client,
		  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
		  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
		break;
		break;
	case ltc3883:
	case ltc3883:
		data->features |= FEAT_CLEAR_PEAKS;
		data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
		info->read_word_data = ltc3883_read_word_data;
		info->read_word_data = ltc3883_read_word_data;
		info->pages = LTC3883_NUM_PAGES;
		info->pages = LTC3883_NUM_PAGES;
		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
@@ -629,7 +720,7 @@ static int ltc2978_probe(struct i2c_client *client,
		  | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
		  | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
		break;
		break;
	case ltc3886:
	case ltc3886:
		data->features |= FEAT_CLEAR_PEAKS;
		data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
		info->read_word_data = ltc3883_read_word_data;
		info->read_word_data = ltc3883_read_word_data;
		info->pages = LTC3880_NUM_PAGES;
		info->pages = LTC3880_NUM_PAGES;
		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN