Uwe Kleine-König (The Capable Hub)misc: Unify style of of_device_id arrays66586733 months agoHistory
299 lines · 7.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Dallas Semiconductor DS1682 Elapsed Time Recorder device driver * * Written by: Grant Likely <grant.likely@secretlab.ca> * * Copyright (C) 2007 Secret Lab Technologies Ltd. */ /* * The DS1682 elapsed timer recorder is a simple device that implements * one elapsed time counter, one event counter, an alarm signal and 10 * bytes of general purpose EEPROM. * * This driver provides access to the DS1682 counters and user data via * the sysfs.  The following attributes are added to the device node: *     elapsed_time (u32): Total elapsed event time in ms resolution *     alarm_time (u32): When elapsed time exceeds the value in alarm_time, *                       then the alarm pin is asserted. *     event_count (u16): number of times the event pin has gone low. *     eeprom (u8[10]): general purpose EEPROM * * Counter registers and user data are both read/write unless the device * has been write protected.  This driver does not support turning off write * protection.  Once write protection is turned on, it is impossible to * turn it off again, so I have left the feature out of this driver to avoid * accidental enabling, but it is trivial to add write protect support. * */ #include <linux/module.h>#include <linux/i2c.h>#include <linux/string.h>#include <linux/list.h>#include <linux/nvmem-provider.h>#include <linux/sysfs.h>#include <linux/ctype.h>#include <linux/hwmon-sysfs.h> /* Device registers */#define DS1682_REG_CONFIG		0x00#define DS1682_REG_ALARM		0x01#define DS1682_REG_ELAPSED		0x05#define DS1682_REG_EVT_CNTR		0x09#define DS1682_REG_EEPROM		0x0b#define DS1682_REG_RESET		0x1d#define DS1682_REG_WRITE_DISABLE	0x1e#define DS1682_REG_WRITE_MEM_DISABLE	0x1f #define DS1682_EEPROM_SIZE		10 /* * Generic counter attributes */static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr,			   char *buf){	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);	struct i2c_client *client = to_i2c_client(dev);	unsigned long long val, check;	__le32 val_le = 0;	int rc; 	dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name); 	/* Read the register */	rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr,					   (u8 *)&val_le);	if (rc < 0)		return -EIO; 	val = le32_to_cpu(val_le); 	if (sattr->index == DS1682_REG_ELAPSED) {		int retries = 5; 		/* Detect and retry when a tick occurs mid-read */		do {			rc = i2c_smbus_read_i2c_block_data(client, sattr->index,							   sattr->nr,							   (u8 *)&val_le);			if (rc < 0 || retries <= 0)				return -EIO; 			check = val;			val = le32_to_cpu(val_le);			retries--;		} while (val != check && val != (check + 1));	} 	/* Format the output string and return # of bytes	 * Special case: the 32 bit regs are time values with 1/4s	 * resolution, scale them up to milliseconds	 */	return sprintf(buf, "%llu\n", (sattr->nr == 4) ? (val * 250) : val);} static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,			    const char *buf, size_t count){	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);	struct i2c_client *client = to_i2c_client(dev);	u64 val;	__le32 val_le;	int rc; 	dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name); 	/* Decode input */	rc = kstrtoull(buf, 0, &val);	if (rc < 0) {		dev_dbg(dev, "input string not a number\n");		return -EINVAL;	} 	/* Special case: the 32 bit regs are time values with 1/4s	 * resolution, scale input down to quarter-seconds */	if (sattr->nr == 4)		do_div(val, 250); 	/* write out the value */	val_le = cpu_to_le32(val);	rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr,					    (u8 *) & val_le);	if (rc < 0) {		dev_err(dev, "register write failed; reg=0x%x, size=%i\n",			sattr->index, sattr->nr);		return -EIO;	} 	return count;} /* * Simple register attributes */static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show,			    ds1682_store, 4, DS1682_REG_ELAPSED);static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show,			    ds1682_store, 4, DS1682_REG_ALARM);static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show,			    ds1682_store, 2, DS1682_REG_EVT_CNTR); static const struct attribute_group ds1682_group = {	.attrs = (struct attribute *[]) {		&sensor_dev_attr_elapsed_time.dev_attr.attr,		&sensor_dev_attr_alarm_time.dev_attr.attr,		&sensor_dev_attr_event_count.dev_attr.attr,		NULL,	},}; /* * User data attribute */static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj,				  const struct bin_attribute *attr,				  char *buf, loff_t off, size_t count){	struct i2c_client *client = kobj_to_i2c_client(kobj);	int rc; 	dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n",		buf, off, count); 	rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off,					   count, buf);	if (rc < 0)		return -EIO; 	return count;} static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj,				   const struct bin_attribute *attr,				   char *buf, loff_t off, size_t count){	struct i2c_client *client = kobj_to_i2c_client(kobj); 	dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n",		buf, off, count); 	/* Write out to the device */	if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off,					   count, buf) < 0)		return -EIO; 	return count;} static const struct bin_attribute ds1682_eeprom_attr = {	.attr = {		.name = "eeprom",		.mode = S_IRUGO | S_IWUSR,	},	.size = DS1682_EEPROM_SIZE,	.read = ds1682_eeprom_read,	.write = ds1682_eeprom_write,}; static int ds1682_nvmem_read(void *priv, unsigned int offset, void *val,			     size_t bytes){	struct i2c_client *client = priv;	int ret; 	ret = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + offset,					    bytes, val);	return ret < 0 ? ret : 0;} static int ds1682_nvmem_write(void *priv, unsigned int offset, void *val,			      size_t bytes){	struct i2c_client *client = priv;	int ret; 	ret = i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + offset,					     bytes, val);	return ret < 0 ? ret : 0;} /* * Called when a ds1682 device is matched with this driver */static int ds1682_probe(struct i2c_client *client){	struct nvmem_config config = {		.dev = &client->dev,		.owner = THIS_MODULE,		.type = NVMEM_TYPE_EEPROM,		.reg_read = ds1682_nvmem_read,		.reg_write = ds1682_nvmem_write,		.size = DS1682_EEPROM_SIZE,		.priv = client,	};	struct nvmem_device *nvmem;	int rc; 	if (!i2c_check_functionality(client->adapter,				     I2C_FUNC_SMBUS_I2C_BLOCK)) {		dev_err(&client->dev, "i2c bus does not support the ds1682\n");		rc = -ENODEV;		goto exit;	} 	nvmem = devm_nvmem_register(&client->dev, &config);	if (IS_ENABLED(CONFIG_NVMEM) && IS_ERR(nvmem))		return PTR_ERR(nvmem); 	rc = sysfs_create_group(&client->dev.kobj, &ds1682_group);	if (rc)		goto exit; 	rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);	if (rc)		goto exit_bin_attr; 	return 0;  exit_bin_attr:	sysfs_remove_group(&client->dev.kobj, &ds1682_group); exit:	return rc;} static void ds1682_remove(struct i2c_client *client){	sysfs_remove_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);	sysfs_remove_group(&client->dev.kobj, &ds1682_group);} static const struct i2c_device_id ds1682_id[] = {	{ .name = "ds1682" },	{ }};MODULE_DEVICE_TABLE(i2c, ds1682_id); static const struct of_device_id ds1682_of_match[] = {	{ .compatible = "dallas,ds1682" },	{ }};MODULE_DEVICE_TABLE(of, ds1682_of_match); static struct i2c_driver ds1682_driver = {	.driver = {		.name = "ds1682",		.of_match_table = ds1682_of_match,	},	.probe = ds1682_probe,	.remove = ds1682_remove,	.id_table = ds1682_id,}; module_i2c_driver(ds1682_driver); MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver");MODULE_LICENSE("GPL");