Commit 45d457a4 authored by Akira Shimahara's avatar Akira Shimahara Committed by Greg Kroah-Hartman
Browse files

w1_therm: adding eeprom sysfs entry



The driver implement 2 hardware functions to access device RAM:
 * copy_scratchpad
 * recall_scratchpad
They act according to device specifications.

As EEPROM operations are not device dependent (all w1_therm can perform
EEPROM read/write operation following the same protocol), it is removed
from device families structures.

Updating Documentation/ABI/testing/sysfs-driver-w1_therm accordingly.

Signed-off-by: default avatarAkira Shimahara <akira215corp@gmail.com>
Link: https://lore.kernel.org/r/20200511203725.410844-1-akira215corp@gmail.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 308bdb94
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
What:		/sys/bus/w1/devices/.../eeprom
Date:		May 2020
Contact:	Akira Shimahara <akira215corp@gmail.com>
Description:
		(WO) writing that file will either trigger a save of the
		device data to its embedded EEPROM, either restore data
		embedded in device EEPROM. Be aware that devices support
		limited EEPROM writing cycles (typical 50k)
			* 'save': save device RAM to EEPROM
			* 'restore': restore EEPROM data in device RAM
Users:		any user space application which wants to communicate with
		w1_term device


What:		/sys/bus/w1/devices/.../ext_power
Date:		May 2020
Contact:	Akira Shimahara <akira215corp@gmail.com>
+118 −57
Original line number Diff line number Diff line
@@ -43,12 +43,21 @@
static int w1_strong_pullup = 1;
module_param_named(strong_pullup, w1_strong_pullup, int, 0);

/* This command should be in public header w1.h but is not */
#define W1_RECALL_EEPROM	0xB8

/* Nb of try for an operation */
#define W1_THERM_MAX_TRY		5

/* ms delay to retry bus mutex */
#define W1_THERM_RETRY_DELAY		20

/* delay in ms to write in EEPROM */
#define W1_THERM_EEPROM_WRITE_DELAY	10

#define EEPROM_CMD_WRITE    "save"	/* cmd for write eeprom sysfs */
#define EEPROM_CMD_READ     "restore"	/* cmd for read eeprom sysfs */

/* Helpers Macros */

/*
@@ -86,7 +95,6 @@ module_param_named(strong_pullup, w1_strong_pullup, int, 0);
 * @convert: pointer to the device conversion function
 * @set_resolution: pointer to the device set_resolution function
 * @get_resolution: pointer to the device get_resolution function
 * @eeprom: pointer to eeprom function
 */
struct w1_therm_family_converter {
	u8		broken;
@@ -95,7 +103,6 @@ struct w1_therm_family_converter {
	int		(*convert)(u8 rom[9]);
	int		(*set_resolution)(struct w1_slave *sl, int val);
	int		(*get_resolution)(struct w1_slave *sl);
	int		(*eeprom)(struct device *device);
};

/**
@@ -165,6 +172,22 @@ static int read_scratchpad(struct w1_slave *sl, struct therm_info *info);
 */
static int write_scratchpad(struct w1_slave *sl, const u8 *data, u8 nb_bytes);

/**
 * copy_scratchpad() - Copy the content of scratchpad in device EEPROM
 * @sl: slave involved
 *
 * Return: 0 if success, -kernel error code otherwise
 */
static int copy_scratchpad(struct w1_slave *sl);

/**
 * recall_eeprom() - Restore EEPROM data to device RAM
 * @sl: slave involved
 *
 * Return: 0 if success, -kernel error code otherwise
 */
static int recall_eeprom(struct w1_slave *sl);

/**
 * read_powermode() - Query the power mode of the slave
 * @sl: slave to retrieve the power mode
@@ -199,12 +222,16 @@ static ssize_t resolution_show(struct device *device,
static ssize_t resolution_store(struct device *device,
	struct device_attribute *attr, const char *buf, size_t size);

static ssize_t eeprom_store(struct device *device,
	struct device_attribute *attr, const char *buf, size_t size);

/* Attributes declarations */

static DEVICE_ATTR_RW(w1_slave);
static DEVICE_ATTR_RO(w1_seq);
static DEVICE_ATTR_RO(ext_power);
static DEVICE_ATTR_RW(resolution);
static DEVICE_ATTR_WO(eeprom);

/* Interface Functions declaration */

@@ -234,12 +261,14 @@ static struct attribute *w1_therm_attrs[] = {
	&dev_attr_w1_slave.attr,
	&dev_attr_ext_power.attr,
	&dev_attr_resolution.attr,
	&dev_attr_eeprom.attr,
	NULL,
};

static struct attribute *w1_ds18s20_attrs[] = {
	&dev_attr_w1_slave.attr,
	&dev_attr_ext_power.attr,
	&dev_attr_eeprom.attr,
	NULL,
};

@@ -248,6 +277,7 @@ static struct attribute *w1_ds28ea00_attrs[] = {
	&dev_attr_w1_seq.attr,
	&dev_attr_ext_power.attr,
	&dev_attr_resolution.attr,
	&dev_attr_eeprom.attr,
	NULL,
};

@@ -359,9 +389,6 @@ static struct w1_family w1_therm_family_DS1825 = {

/* Device dependent func */

/* write configuration to eeprom */
static inline int w1_therm_eeprom(struct device *device);

static inline int w1_DS18B20_write_data(struct w1_slave *sl,
				const u8 *data)
{
@@ -477,35 +504,30 @@ static struct w1_therm_family_converter w1_therm_families[] = {
		.convert	= w1_DS18S20_convert_temp,
		.set_resolution	= NULL,	/* no config register */
		.get_resolution	= NULL,	/* no config register */
		.eeprom		= w1_therm_eeprom
	},
	{
		.f		= &w1_therm_family_DS1822,
		.convert	= w1_DS18B20_convert_temp,
		.set_resolution	= w1_DS18B20_set_resolution,
		.get_resolution	= w1_DS18B20_get_resolution,
		.eeprom		= w1_therm_eeprom
	},
	{
		.f		= &w1_therm_family_DS18B20,
		.convert	= w1_DS18B20_convert_temp,
		.set_resolution	= w1_DS18B20_set_resolution,
		.get_resolution	= w1_DS18B20_get_resolution,
		.eeprom		= w1_therm_eeprom
	},
	{
		.f		= &w1_therm_family_DS28EA00,
		.convert	= w1_DS18B20_convert_temp,
		.set_resolution	= w1_DS18B20_set_resolution,
		.get_resolution	= w1_DS18B20_get_resolution,
		.eeprom		= w1_therm_eeprom
	},
	{
		.f		= &w1_therm_family_DS1825,
		.convert	= w1_DS18B20_convert_temp,
		.set_resolution	= w1_DS18B20_set_resolution,
		.get_resolution	= w1_DS18B20_get_resolution,
		.eeprom		= w1_therm_eeprom
	}
};

@@ -838,75 +860,94 @@ error:
	return ret;
}

static inline int w1_therm_eeprom(struct device *device)
static int copy_scratchpad(struct w1_slave *sl)
{
	struct w1_slave *sl = dev_to_w1_slave(device);
	struct w1_master *dev = sl->master;
	u8 rom[9], external_power;
	int ret, max_trying = 10;
	u8 *family_data = sl->family_data;
	struct w1_master *dev_master = sl->master;
	int max_trying = W1_THERM_MAX_TRY;
	int t_write, ret = -ENODEV;
	bool strong_pullup;

	if (!sl->family_data) {
		ret = -ENODEV;
	if (!sl->family_data)
		goto error;
	}

	t_write = W1_THERM_EEPROM_WRITE_DELAY;
	strong_pullup = (w1_strong_pullup == 2 ||
					(!SLAVE_POWERMODE(sl) &&
					w1_strong_pullup));

	/* prevent the slave from going away in sleep */
	atomic_inc(THERM_REFCNT(family_data));
	atomic_inc(THERM_REFCNT(sl->family_data));

	ret = mutex_lock_interruptible(&dev->bus_mutex);
	if (ret != 0)
	if (!bus_mutex_lock(&dev_master->bus_mutex)) {
		ret = -EAGAIN;	/* Didn't acquire the mutex */
		goto dec_refcnt;
	}

	memset(rom, 0, sizeof(rom));

	while (max_trying--) {
	while (max_trying-- && ret) { /* ret should be 0 */
		/* safe version to select slave */
		if (!reset_select_slave(sl)) {
			unsigned int tm = 10;
			unsigned long sleep_rem;

			/* check if in parasite mode */
			w1_write_8(dev, W1_READ_PSUPPLY);
			external_power = w1_read_8(dev);

			if (reset_select_slave(sl))
				continue;

			/* 10ms strong pullup/delay after the copy command */
			if (w1_strong_pullup == 2 ||
			    (!external_power && w1_strong_pullup))
				w1_next_pullup(dev, tm);

			w1_write_8(dev, W1_COPY_SCRATCHPAD);
			/* 10ms strong pullup (or delay) after the convert */
			if (strong_pullup)
				w1_next_pullup(dev_master, t_write);

			if (external_power) {
				mutex_unlock(&dev->bus_mutex);
			w1_write_8(dev_master, W1_COPY_SCRATCHPAD);

				sleep_rem = msleep_interruptible(tm);
			if (strong_pullup) {
				sleep_rem = msleep_interruptible(t_write);
				if (sleep_rem != 0) {
					ret = -EINTR;
					goto dec_refcnt;
					goto mt_unlock;
				}
			}
			ret = 0;
		}

				ret = mutex_lock_interruptible(&dev->bus_mutex);
				if (ret != 0)
					goto dec_refcnt;
			} else if (!w1_strong_pullup) {
				sleep_rem = msleep_interruptible(tm);
				if (sleep_rem != 0) {
					ret = -EINTR;
					goto mt_unlock;
	}

mt_unlock:
	mutex_unlock(&dev_master->bus_mutex);
dec_refcnt:
	atomic_dec(THERM_REFCNT(sl->family_data));
error:
	return ret;
}

			break;
static int recall_eeprom(struct w1_slave *sl)
{
	struct w1_master *dev_master = sl->master;
	int max_trying = W1_THERM_MAX_TRY;
	int ret = -ENODEV;

	if (!sl->family_data)
		goto error;

	/* prevent the slave from going away in sleep */
	atomic_inc(THERM_REFCNT(sl->family_data));

	if (!bus_mutex_lock(&dev_master->bus_mutex)) {
		ret = -EAGAIN;	/* Didn't acquire the mutex */
		goto dec_refcnt;
	}

	while (max_trying-- && ret) { /* ret should be 0 */
		/* safe version to select slave */
		if (!reset_select_slave(sl)) {

			w1_write_8(dev_master, W1_RECALL_EEPROM);

			ret = 1; /* Slave will pull line to 0 */
			while (ret)
				ret = 1 - w1_touch_bit(dev_master, 1);
		}

	}

mt_unlock:
	mutex_unlock(&dev->bus_mutex);
	mutex_unlock(&dev_master->bus_mutex);

dec_refcnt:
	atomic_dec(THERM_REFCNT(family_data));
	atomic_dec(THERM_REFCNT(sl->family_data));
error:
	return ret;
}
@@ -1006,7 +1047,7 @@ static ssize_t w1_slave_store(struct device *device,
	}

	if (val == 0)	/* val=0 : trigger a EEPROM save */
		ret = SLAVE_SPECIFIC_FUNC(sl)->eeprom(device);
		ret = copy_scratchpad(sl);
	else {
		if (SLAVE_SPECIFIC_FUNC(sl)->set_resolution)
			ret = SLAVE_SPECIFIC_FUNC(sl)->set_resolution(sl, val);
@@ -1105,6 +1146,26 @@ static ssize_t resolution_store(struct device *device,
	return size;
}

static ssize_t eeprom_store(struct device *device,
	struct device_attribute *attr, const char *buf, size_t size)
{
	struct w1_slave *sl = dev_to_w1_slave(device);
	int ret = -EINVAL; /* Invalid argument */

	if (size == sizeof(EEPROM_CMD_WRITE)) {
		if (!strncmp(buf, EEPROM_CMD_WRITE, sizeof(EEPROM_CMD_WRITE)-1))
			ret = copy_scratchpad(sl);
	} else if (size == sizeof(EEPROM_CMD_READ)) {
		if (!strncmp(buf, EEPROM_CMD_READ, sizeof(EEPROM_CMD_READ)-1))
			ret = recall_eeprom(sl);
	}

	if (ret)
		dev_info(device, "%s: error in process %d\n", __func__, ret);

	return size;
}

#if IS_REACHABLE(CONFIG_HWMON)
static int w1_read_temp(struct device *device, u32 attr, int channel,
			long *val)