Commit 73e5b6b5 authored by William A. Kennington III's avatar William A. Kennington III Committed by Guenter Roeck
Browse files

hwmon: (pmbus) Introduce page_change_delay



We have some buggy pmbus devices that require a delay after performing a
page change operation before trying to issue more commands to the
device.

This allows for a configurable delay after page changes, but not
affecting other read or write operations.

This makes a slight behavioral tweak to the existing delay logic, where
it considers the longest of delays between operations, instead of always
chosing the write delay over the access delay.

Signed-off-by: default avatarWilliam A. Kennington III <william@wkennington.com>
Link: https://lore.kernel.org/r/20250407201002.1198092-1-william@wkennington.com


Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 2c183963
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -482,6 +482,7 @@ struct pmbus_driver_info {
	 */
	int access_delay;		/* in microseconds */
	int write_delay;		/* in microseconds */
	int page_change_delay;		/* in microseconds */
};

/* Regulator ops */
+35 −34
Original line number Diff line number Diff line
@@ -32,6 +32,13 @@
#define PMBUS_ATTR_ALLOC_SIZE	32
#define PMBUS_NAME_SIZE		24

/*
 * The type of operation used for picking the delay between
 * successive pmbus operations.
 */
#define PMBUS_OP_WRITE		BIT(0)
#define PMBUS_OP_PAGE_CHANGE	BIT(1)

static int wp = -1;
module_param(wp, int, 0444);

@@ -113,8 +120,8 @@ struct pmbus_data {

	int vout_low[PMBUS_PAGES];	/* voltage low margin */
	int vout_high[PMBUS_PAGES];	/* voltage high margin */
	ktime_t write_time;		/* Last SMBUS write timestamp */
	ktime_t access_time;		/* Last SMBUS access timestamp */

	ktime_t next_access_backoff;	/* Wait until at least this time */
};

struct pmbus_debugfs_entry {
@@ -169,32 +176,26 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS");
static void pmbus_wait(struct i2c_client *client)
{
	struct pmbus_data *data = i2c_get_clientdata(client);
	const struct pmbus_driver_info *info = data->info;
	s64 delta;
	s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get());

	if (info->access_delay) {
		delta = ktime_us_delta(ktime_get(), data->access_time);

		if (delta < info->access_delay)
			fsleep(info->access_delay - delta);
	} else if (info->write_delay) {
		delta = ktime_us_delta(ktime_get(), data->write_time);

		if (delta < info->write_delay)
			fsleep(info->write_delay - delta);
	}
	if (delay > 0)
		fsleep(delay);
}

/* Sets the last accessed timestamp for pmbus_wait */
static void pmbus_update_ts(struct i2c_client *client, bool write_op)
/* Sets the last operation timestamp for pmbus_wait */
static void pmbus_update_ts(struct i2c_client *client, int op)
{
	struct pmbus_data *data = i2c_get_clientdata(client);
	const struct pmbus_driver_info *info = data->info;
	int delay = info->access_delay;

	if (op & PMBUS_OP_WRITE)
		delay = max(delay, info->write_delay);
	if (op & PMBUS_OP_PAGE_CHANGE)
		delay = max(delay, info->page_change_delay);

	if (info->access_delay)
		data->access_time = ktime_get();
	else if (info->write_delay && write_op)
		data->write_time = ktime_get();
	if (delay > 0)
		data->next_access_backoff = ktime_add_us(ktime_get(), delay);
}

int pmbus_set_page(struct i2c_client *client, int page, int phase)
@@ -209,13 +210,13 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
	    data->info->pages > 1 && page != data->currpage) {
		pmbus_wait(client);
		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
		pmbus_update_ts(client, true);
		pmbus_update_ts(client, PMBUS_OP_WRITE | PMBUS_OP_PAGE_CHANGE);
		if (rv < 0)
			return rv;

		pmbus_wait(client);
		rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
		pmbus_update_ts(client, false);
		pmbus_update_ts(client, 0);
		if (rv < 0)
			return rv;

@@ -229,7 +230,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
		pmbus_wait(client);
		rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
					       phase);
		pmbus_update_ts(client, true);
		pmbus_update_ts(client, PMBUS_OP_WRITE);
		if (rv)
			return rv;
	}
@@ -249,7 +250,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)

	pmbus_wait(client);
	rv = i2c_smbus_write_byte(client, value);
	pmbus_update_ts(client, true);
	pmbus_update_ts(client, PMBUS_OP_WRITE);

	return rv;
}
@@ -284,7 +285,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,

	pmbus_wait(client);
	rv = i2c_smbus_write_word_data(client, reg, word);
	pmbus_update_ts(client, true);
	pmbus_update_ts(client, PMBUS_OP_WRITE);

	return rv;
}
@@ -405,7 +406,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)

	pmbus_wait(client);
	rv = i2c_smbus_read_word_data(client, reg);
	pmbus_update_ts(client, false);
	pmbus_update_ts(client, 0);

	return rv;
}
@@ -468,7 +469,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)

	pmbus_wait(client);
	rv = i2c_smbus_read_byte_data(client, reg);
	pmbus_update_ts(client, false);
	pmbus_update_ts(client, 0);

	return rv;
}
@@ -484,7 +485,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)

	pmbus_wait(client);
	rv = i2c_smbus_write_byte_data(client, reg, value);
	pmbus_update_ts(client, true);
	pmbus_update_ts(client, PMBUS_OP_WRITE);

	return rv;
}
@@ -520,7 +521,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,

	pmbus_wait(client);
	rv = i2c_smbus_read_block_data(client, reg, data_buf);
	pmbus_update_ts(client, false);
	pmbus_update_ts(client, 0);

	return rv;
}
@@ -2524,7 +2525,7 @@ static int pmbus_read_coefficients(struct i2c_client *client,
	rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
			    I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS,
			    I2C_SMBUS_BLOCK_PROC_CALL, &data);
	pmbus_update_ts(client, true);
	pmbus_update_ts(client, PMBUS_OP_WRITE);

	if (rv < 0)
		return rv;
@@ -2728,7 +2729,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
	if (!(data->flags & PMBUS_NO_CAPABILITY)) {
		pmbus_wait(client);
		ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
		pmbus_update_ts(client, false);
		pmbus_update_ts(client, 0);

		if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
			if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
@@ -2744,13 +2745,13 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
	data->read_status = pmbus_read_status_word;
	pmbus_wait(client);
	ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
	pmbus_update_ts(client, false);
	pmbus_update_ts(client, 0);

	if (ret < 0 || ret == 0xffff) {
		data->read_status = pmbus_read_status_byte;
		pmbus_wait(client);
		ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
		pmbus_update_ts(client, false);
		pmbus_update_ts(client, 0);

		if (ret < 0 || ret == 0xff) {
			dev_err(dev, "PMBus status register not found\n");