Blame view

drivers/misc/ds1682.c 6.95 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
5162b75b2   Grant Likely   i2c: New DS1682 c...
2
3
4
5
6
7
  /*
   * Dallas Semiconductor DS1682 Elapsed Time Recorder device driver
   *
   * Written by: Grant Likely <grant.likely@secretlab.ca>
   *
   * Copyright (C) 2007 Secret Lab Technologies Ltd.
5162b75b2   Grant Likely   i2c: New DS1682 c...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   */
  
  /*
   * 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>
5162b75b2   Grant Likely   i2c: New DS1682 c...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  #include <linux/i2c.h>
  #include <linux/string.h>
  #include <linux/list.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);
066ed4271   Thomas VanSelus   misc: ds1682: Ign...
59
  	unsigned long long val, check;
2fa065fde   Aaron Sierra   misc: ds1682: Sho...
60
  	__le32 val_le = 0;
5162b75b2   Grant Likely   i2c: New DS1682 c...
61
62
63
64
65
66
67
  	int rc;
  
  	dev_dbg(dev, "ds1682_show() called on %s
  ", attr->attr.name);
  
  	/* Read the register */
  	rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr,
2fa065fde   Aaron Sierra   misc: ds1682: Sho...
68
  					   (u8 *)&val_le);
5162b75b2   Grant Likely   i2c: New DS1682 c...
69
70
  	if (rc < 0)
  		return -EIO;
2fa065fde   Aaron Sierra   misc: ds1682: Sho...
71
  	val = le32_to_cpu(val_le);
5162b75b2   Grant Likely   i2c: New DS1682 c...
72

066ed4271   Thomas VanSelus   misc: ds1682: Ign...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  	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));
  	}
2fa065fde   Aaron Sierra   misc: ds1682: Sho...
89
90
91
92
93
94
  	/* 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
  ", (sattr->nr == 4) ? (val * 250) : val);
5162b75b2   Grant Likely   i2c: New DS1682 c...
95
96
97
98
99
100
101
  }
  
  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);
5162b75b2   Grant Likely   i2c: New DS1682 c...
102
103
104
105
106
107
108
109
  	u64 val;
  	__le32 val_le;
  	int rc;
  
  	dev_dbg(dev, "ds1682_store() called on %s
  ", attr->attr.name);
  
  	/* Decode input */
403007457   Sebastien Bourdelin   misc: (ds1682) re...
110
111
  	rc = kstrtoull(buf, 0, &val);
  	if (rc < 0) {
5162b75b2   Grant Likely   i2c: New DS1682 c...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  		dev_dbg(dev, "input string not a number
  ");
  		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
  ",
  			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
   */
2c3c8bea6   Chris Wright   sysfs: add struct...
158
159
  static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj,
  				  struct bin_attribute *attr,
05bd711ea   Al Viro   missing argument ...
160
  				  char *buf, loff_t off, size_t count)
5162b75b2   Grant Likely   i2c: New DS1682 c...
161
162
163
164
165
166
167
  {
  	struct i2c_client *client = kobj_to_i2c_client(kobj);
  	int rc;
  
  	dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)
  ",
  		buf, off, count);
5162b75b2   Grant Likely   i2c: New DS1682 c...
168
169
170
171
172
173
174
  	rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off,
  					   count, buf);
  	if (rc < 0)
  		return -EIO;
  
  	return count;
  }
2c3c8bea6   Chris Wright   sysfs: add struct...
175
176
  static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj,
  				   struct bin_attribute *attr,
05bd711ea   Al Viro   missing argument ...
177
  				   char *buf, loff_t off, size_t count)
5162b75b2   Grant Likely   i2c: New DS1682 c...
178
179
180
181
182
183
  {
  	struct i2c_client *client = kobj_to_i2c_client(kobj);
  
  	dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)
  ",
  		buf, off, count);
5162b75b2   Grant Likely   i2c: New DS1682 c...
184
185
186
187
188
189
190
  	/* Write out to the device */
  	if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off,
  					   count, buf) < 0)
  		return -EIO;
  
  	return count;
  }
57daedf81   Bhumika Goyal   MISC: add const t...
191
  static const struct bin_attribute ds1682_eeprom_attr = {
5162b75b2   Grant Likely   i2c: New DS1682 c...
192
193
194
  	.attr = {
  		.name = "eeprom",
  		.mode = S_IRUGO | S_IWUSR,
5162b75b2   Grant Likely   i2c: New DS1682 c...
195
196
197
198
199
200
201
202
203
  	},
  	.size = DS1682_EEPROM_SIZE,
  	.read = ds1682_eeprom_read,
  	.write = ds1682_eeprom_write,
  };
  
  /*
   * Called when a ds1682 device is matched with this driver
   */
d2653e927   Jean Delvare   i2c: Add support ...
204
205
  static int ds1682_probe(struct i2c_client *client,
  			const struct i2c_device_id *id)
5162b75b2   Grant Likely   i2c: New DS1682 c...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  {
  	int rc;
  
  	if (!i2c_check_functionality(client->adapter,
  				     I2C_FUNC_SMBUS_I2C_BLOCK)) {
  		dev_err(&client->dev, "i2c bus does not support the ds1682
  ");
  		rc = -ENODEV;
  		goto exit;
  	}
  
  	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 int 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;
  }
3760f7367   Jean Delvare   i2c: Convert most...
239
240
241
242
243
  static const struct i2c_device_id ds1682_id[] = {
  	{ "ds1682", 0 },
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ds1682_id);
5ef168545   Javier Martinez Canillas   misc: ds1682: Add...
244
245
246
247
248
  static const struct of_device_id ds1682_of_match[] = {
  	{ .compatible = "dallas,ds1682", },
  	{}
  };
  MODULE_DEVICE_TABLE(of, ds1682_of_match);
5162b75b2   Grant Likely   i2c: New DS1682 c...
249
250
251
  static struct i2c_driver ds1682_driver = {
  	.driver = {
  		.name = "ds1682",
5ef168545   Javier Martinez Canillas   misc: ds1682: Add...
252
  		.of_match_table = ds1682_of_match,
5162b75b2   Grant Likely   i2c: New DS1682 c...
253
254
255
  	},
  	.probe = ds1682_probe,
  	.remove = ds1682_remove,
3760f7367   Jean Delvare   i2c: Convert most...
256
  	.id_table = ds1682_id,
5162b75b2   Grant Likely   i2c: New DS1682 c...
257
  };
a64fe2ed7   Axel Lin   MISC: convert dri...
258
  module_i2c_driver(ds1682_driver);
5162b75b2   Grant Likely   i2c: New DS1682 c...
259
260
261
262
  
  MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
  MODULE_DESCRIPTION("DS1682 Elapsed Time Indicator driver");
  MODULE_LICENSE("GPL");