diff options
Diffstat (limited to 'drivers/misc/ds1682.c')
| -rw-r--r-- | drivers/misc/ds1682.c | 91 |
1 files changed, 70 insertions, 21 deletions
diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 28bb495f0cf1..cb09e056531a 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -1,13 +1,10 @@ +// 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. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /* @@ -35,6 +32,7 @@ #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> @@ -59,25 +57,42 @@ static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); struct i2c_client *client = to_i2c_client(dev); - __le32 val = 0; + 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); + (u8 *)&val_le); if (rc < 0) return -EIO; - /* Special case: the 32 bit regs are time values with 1/4s - * resolution, scale them up to milliseconds */ - if (sattr->nr == 4) - return sprintf(buf, "%llu\n", - ((unsigned long long)le32_to_cpu(val)) * 250); + 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; - /* Format the output string and return # of bytes */ - return sprintf(buf, "%li\n", (long)le32_to_cpu(val)); + 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, @@ -139,7 +154,7 @@ static const struct attribute_group ds1682_group = { * User data attribute */ static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); @@ -157,7 +172,7 @@ static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj, } static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); @@ -173,7 +188,7 @@ static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute ds1682_eeprom_attr = { +static const struct bin_attribute ds1682_eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, @@ -183,12 +198,43 @@ static struct bin_attribute ds1682_eeprom_attr = { .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, - const struct i2c_device_id *id) +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, @@ -198,6 +244,10 @@ static int ds1682_probe(struct i2c_client *client, 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; @@ -214,15 +264,14 @@ static int ds1682_probe(struct i2c_client *client, return rc; } -static int ds1682_remove(struct i2c_client *client) +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); - return 0; } static const struct i2c_device_id ds1682_id[] = { - { "ds1682", 0 }, + { "ds1682" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1682_id); |
