File Index
Files and directories
Sign in
FILE

m24lr.c

File preview with syntax highlighting.

linux-6.19.14/drivers/misc/eeprom/m24lr.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * m24lr.c - Sysfs control interface for ST M24LR series RFID/NFC chips
  4 *
  5 * Copyright (c) 2025 Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
  6 *
  7 * This driver implements both the sysfs-based control interface and EEPROM
  8 * access for STMicroelectronics M24LR series chips (e.g., M24LR04E-R).
  9 * It provides access to control registers for features such as password
 10 * authentication, memory protection, and device configuration. In addition,
 11 * it manages read and write operations to the EEPROM region of the chip.
 12 */
 13
 14#include <linux/device.h>
 15#include <linux/i2c.h>
 16#include <linux/module.h>
 17#include <linux/nvmem-provider.h>
 18#include <linux/of.h>
 19#include <linux/of_device.h>
 20#include <linux/regmap.h>
 21
 22#define M24LR_WRITE_TIMEOUT	  25u
 23#define M24LR_READ_TIMEOUT	  (M24LR_WRITE_TIMEOUT)
 24
 25/**
 26 * struct m24lr_chip - describes chip-specific sysfs layout
 27 * @sss_len:       the length of the sss region
 28 * @page_size:	   chip-specific limit on the maximum number of bytes allowed
 29 *		   in a single write operation.
 30 * @eeprom_size:   size of the EEPROM in byte
 31 *
 32 * Supports multiple M24LR chip variants (e.g., M24LRxx) by allowing each
 33 * to define its own set of sysfs attributes, depending on its available
 34 * registers and features.
 35 */
 36struct m24lr_chip {
 37	unsigned int sss_len;
 38	unsigned int page_size;
 39	unsigned int eeprom_size;
 40};
 41
 42/**
 43 * struct m24lr - core driver data for M24LR chip control
 44 * @uid:           64 bits unique identifier stored in the device
 45 * @sss_len:       the length of the sss region
 46 * @page_size:	   chip-specific limit on the maximum number of bytes allowed
 47 *		   in a single write operation.
 48 * @eeprom_size:   size of the EEPROM in byte
 49 * @ctl_regmap:	   regmap interface for accessing the system parameter sector
 50 * @eeprom_regmap: regmap interface for accessing the EEPROM
 51 * @lock:	   mutex to synchronize operations to the device
 52 *
 53 * Central data structure holding the state and resources used by the
 54 * M24LR device driver.
 55 */
 56struct m24lr {
 57	u64 uid;
 58	unsigned int sss_len;
 59	unsigned int page_size;
 60	unsigned int eeprom_size;
 61	struct regmap *ctl_regmap;
 62	struct regmap *eeprom_regmap;
 63	struct mutex lock;	 /* synchronize operations to the device */
 64};
 65
 66static const struct regmap_range m24lr_ctl_vo_ranges[] = {
 67	regmap_reg_range(0, 63),
 68};
 69
 70static const struct regmap_access_table m24lr_ctl_vo_table = {
 71	.yes_ranges = m24lr_ctl_vo_ranges,
 72	.n_yes_ranges = ARRAY_SIZE(m24lr_ctl_vo_ranges),
 73};
 74
 75static const struct regmap_config m24lr_ctl_regmap_conf = {
 76	.name = "m24lr_ctl",
 77	.reg_stride = 1,
 78	.reg_bits = 16,
 79	.val_bits = 8,
 80	.disable_locking = false,
 81	.cache_type = REGCACHE_RBTREE,/* Flat can't be used, there's huge gap */
 82	.volatile_table = &m24lr_ctl_vo_table,
 83};
 84
 85/* Chip descriptor for M24LR04E-R variant */
 86static const struct m24lr_chip m24lr04e_r_chip = {
 87	.page_size = 4,
 88	.eeprom_size = 512,
 89	.sss_len = 4,
 90};
 91
 92/* Chip descriptor for M24LR16E-R variant */
 93static const struct m24lr_chip m24lr16e_r_chip = {
 94	.page_size = 4,
 95	.eeprom_size = 2048,
 96	.sss_len = 16,
 97};
 98
 99/* Chip descriptor for M24LR64E-R variant */
100static const struct m24lr_chip m24lr64e_r_chip = {
101	.page_size = 4,
102	.eeprom_size = 8192,
103	.sss_len = 64,
104};
105
106static const struct i2c_device_id m24lr_ids[] = {
107	{ "m24lr04e-r", (kernel_ulong_t)&m24lr04e_r_chip},
108	{ "m24lr16e-r", (kernel_ulong_t)&m24lr16e_r_chip},
109	{ "m24lr64e-r", (kernel_ulong_t)&m24lr64e_r_chip},
110	{ }
111};
112MODULE_DEVICE_TABLE(i2c, m24lr_ids);
113
114static const struct of_device_id m24lr_of_match[] = {
115	{ .compatible = "st,m24lr04e-r", .data = &m24lr04e_r_chip},
116	{ .compatible = "st,m24lr16e-r", .data = &m24lr16e_r_chip},
117	{ .compatible = "st,m24lr64e-r", .data = &m24lr64e_r_chip},
118	{ }
119};
120MODULE_DEVICE_TABLE(of, m24lr_of_match);
121
122/**
123 * m24lr_regmap_read - read data using regmap with retry on failure
124 * @regmap:  regmap instance for the device
125 * @buf:     buffer to store the read data
126 * @size:    number of bytes to read
127 * @offset:  starting register address
128 *
129 * Attempts to read a block of data from the device with retries and timeout.
130 * Some M24LR chips may transiently NACK reads (e.g., during internal write
131 * cycles), so this function retries with a short sleep until the timeout
132 * expires.
133 *
134 * Returns:
135 *	 Number of bytes read on success,
136 *	 -ETIMEDOUT if the read fails within the timeout window.
137 */
138static ssize_t m24lr_regmap_read(struct regmap *regmap, u8 *buf,
139				 size_t size, unsigned int offset)
140{
141	int err;
142	unsigned long timeout, read_time;
143	ssize_t ret = -ETIMEDOUT;
144
145	timeout = jiffies + msecs_to_jiffies(M24LR_READ_TIMEOUT);
146	do {
147		read_time = jiffies;
148
149		err = regmap_bulk_read(regmap, offset, buf, size);
150		if (!err) {
151			ret = size;
152			break;
153		}
154
155		usleep_range(1000, 2000);
156	} while (time_before(read_time, timeout));
157
158	return ret;
159}
160
161/**
162 * m24lr_regmap_write - write data using regmap with retry on failure
163 * @regmap: regmap instance for the device
164 * @buf:    buffer containing the data to write
165 * @size:   number of bytes to write
166 * @offset: starting register address
167 *
168 * Attempts to write a block of data to the device with retries and a timeout.
169 * Some M24LR devices may NACK I2C writes while an internal write operation
170 * is in progress. This function retries the write operation with a short delay
171 * until it succeeds or the timeout is reached.
172 *
173 * Returns:
174 *	 Number of bytes written on success,
175 *	 -ETIMEDOUT if the write fails within the timeout window.
176 */
177static ssize_t m24lr_regmap_write(struct regmap *regmap, const u8 *buf,
178				  size_t size, unsigned int offset)
179{
180	int err;
181	unsigned long timeout, write_time;
182	ssize_t ret = -ETIMEDOUT;
183
184	timeout = jiffies + msecs_to_jiffies(M24LR_WRITE_TIMEOUT);
185
186	do {
187		write_time = jiffies;
188
189		err = regmap_bulk_write(regmap, offset, buf, size);
190		if (!err) {
191			ret = size;
192			break;
193		}
194
195		usleep_range(1000, 2000);
196	} while (time_before(write_time, timeout));
197
198	return ret;
199}
200
201static ssize_t m24lr_read(struct m24lr *m24lr, u8 *buf, size_t size,
202			  unsigned int offset, bool is_eeprom)
203{
204	struct regmap *regmap;
205	ssize_t ret;
206
207	if (is_eeprom)
208		regmap = m24lr->eeprom_regmap;
209	else
210		regmap = m24lr->ctl_regmap;
211
212	mutex_lock(&m24lr->lock);
213	ret = m24lr_regmap_read(regmap, buf, size, offset);
214	mutex_unlock(&m24lr->lock);
215
216	return ret;
217}
218
219/**
220 * m24lr_write - write buffer to M24LR device with page alignment handling
221 * @m24lr:     pointer to driver context
222 * @buf:       data buffer to write
223 * @size:      number of bytes to write
224 * @offset:    target register address in the device
225 * @is_eeprom: true if the write should target the EEPROM,
226 *             false if it should target the system parameters sector.
227 *
228 * Writes data to the M24LR device using regmap, split into chunks no larger
229 * than page_size to respect device-specific write limitations (e.g., page
230 * size or I2C hold-time concerns). Each chunk is aligned to the page boundary
231 * defined by page_size.
232 *
233 * Returns:
234 *	 Total number of bytes written on success,
235 *	 A negative error code if any write fails.
236 */
237static ssize_t m24lr_write(struct m24lr *m24lr, const u8 *buf, size_t size,
238			   unsigned int offset, bool is_eeprom)
239{
240	unsigned int n, next_sector;
241	struct regmap *regmap;
242	ssize_t ret = 0;
243	ssize_t err;
244
245	if (is_eeprom)
246		regmap = m24lr->eeprom_regmap;
247	else
248		regmap = m24lr->ctl_regmap;
249
250	n = min_t(unsigned int, size, m24lr->page_size);
251	next_sector = roundup(offset + 1, m24lr->page_size);
252	if (offset + n > next_sector)
253		n = next_sector - offset;
254
255	mutex_lock(&m24lr->lock);
256	while (n) {
257		err = m24lr_regmap_write(regmap, buf + offset, n, offset);
258		if (IS_ERR_VALUE(err)) {
259			if (!ret)
260				ret = err;
261
262			break;
263		}
264
265		offset += n;
266		size -= n;
267		ret += n;
268		n = min_t(unsigned int, size, m24lr->page_size);
269	}
270	mutex_unlock(&m24lr->lock);
271
272	return ret;
273}
274
275/**
276 * m24lr_write_pass - Write password to M24LR043-R using secure format
277 * @m24lr: Pointer to device control structure
278 * @buf:   Input buffer containing hex-encoded password
279 * @count: Number of bytes in @buf
280 * @code:  Operation code to embed between password copies
281 *
282 * This function parses a 4-byte password, encodes it in  big-endian format,
283 * and constructs a 9-byte sequence of the form:
284 *
285 *	  [BE(password), code, BE(password)]
286 *
287 * The result is written to register 0x0900 (2304), which is the password
288 * register in M24LR04E-R chip.
289 *
290 * Return: Number of bytes written on success, or negative error code on failure
291 */
292static ssize_t m24lr_write_pass(struct m24lr *m24lr, const char *buf,
293				size_t count, u8 code)
294{
295	__be32 be_pass;
296	u8 output[9];
297	ssize_t ret;
298	u32 pass;
299	int err;
300
301	if (!count)
302		return -EINVAL;
303
304	if (count > 8)
305		return -EINVAL;
306
307	err = kstrtou32(buf, 16, &pass);
308	if (err)
309		return err;
310
311	be_pass = cpu_to_be32(pass);
312
313	memcpy(output, &be_pass, sizeof(be_pass));
314	output[4] = code;
315	memcpy(output + 5, &be_pass, sizeof(be_pass));
316
317	mutex_lock(&m24lr->lock);
318	ret = m24lr_regmap_write(m24lr->ctl_regmap, output, 9, 2304);
319	mutex_unlock(&m24lr->lock);
320
321	return ret;
322}
323
324static ssize_t m24lr_read_reg_le(struct m24lr *m24lr, u64 *val,
325				 unsigned int reg_addr,
326				 unsigned int reg_size)
327{
328	ssize_t ret;
329	__le64 input = 0;
330
331	ret = m24lr_read(m24lr, (u8 *)&input, reg_size, reg_addr, false);
332	if (IS_ERR_VALUE(ret))
333		return ret;
334
335	if (ret != reg_size)
336		return -EINVAL;
337
338	switch (reg_size) {
339	case 1:
340		*val = *(u8 *)&input;
341		break;
342	case 2:
343		*val = le16_to_cpu((__le16)input);
344		break;
345	case 4:
346		*val = le32_to_cpu((__le32)input);
347		break;
348	case 8:
349		*val = le64_to_cpu((__le64)input);
350		break;
351	default:
352		return -EINVAL;
353	}
354
355	return 0;
356}
357
358static int m24lr_nvmem_read(void *priv, unsigned int offset, void *val,
359			    size_t bytes)
360{
361	ssize_t err;
362	struct m24lr *m24lr = priv;
363
364	if (!bytes)
365		return bytes;
366
367	if (offset + bytes > m24lr->eeprom_size)
368		return -EINVAL;
369
370	err = m24lr_read(m24lr, val, bytes, offset, true);
371	if (IS_ERR_VALUE(err))
372		return err;
373
374	return 0;
375}
376
377static int m24lr_nvmem_write(void *priv, unsigned int offset, void *val,
378			     size_t bytes)
379{
380	ssize_t err;
381	struct m24lr *m24lr = priv;
382
383	if (!bytes)
384		return -EINVAL;
385
386	if (offset + bytes > m24lr->eeprom_size)
387		return -EINVAL;
388
389	err = m24lr_write(m24lr, val, bytes, offset, true);
390	if (IS_ERR_VALUE(err))
391		return err;
392
393	return 0;
394}
395
396static ssize_t m24lr_ctl_sss_read(struct file *filep, struct kobject *kobj,
397				  const struct bin_attribute *attr, char *buf,
398				  loff_t offset, size_t count)
399{
400	struct m24lr *m24lr = attr->private;
401
402	if (!count)
403		return count;
404
405	if (size_add(offset, count) > m24lr->sss_len)
406		return -EINVAL;
407
408	return m24lr_read(m24lr, buf, count, offset, false);
409}
410
411static ssize_t m24lr_ctl_sss_write(struct file *filep, struct kobject *kobj,
412				   const struct bin_attribute *attr, char *buf,
413				   loff_t offset, size_t count)
414{
415	struct m24lr *m24lr = attr->private;
416
417	if (!count)
418		return -EINVAL;
419
420	if (size_add(offset, count) > m24lr->sss_len)
421		return -EINVAL;
422
423	return m24lr_write(m24lr, buf, count, offset, false);
424}
425static BIN_ATTR(sss, 0600, m24lr_ctl_sss_read, m24lr_ctl_sss_write, 0);
426
427static ssize_t new_pass_store(struct device *dev, struct device_attribute *attr,
428			      const char *buf, size_t count)
429{
430	struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
431
432	return m24lr_write_pass(m24lr, buf, count, 7);
433}
434static DEVICE_ATTR_WO(new_pass);
435
436static ssize_t unlock_store(struct device *dev, struct device_attribute *attr,
437			    const char *buf, size_t count)
438{
439	struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
440
441	return m24lr_write_pass(m24lr, buf, count, 9);
442}
443static DEVICE_ATTR_WO(unlock);
444
445static ssize_t uid_show(struct device *dev, struct device_attribute *attr,
446			char *buf)
447{
448	struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
449
450	return sysfs_emit(buf, "%llx\n", m24lr->uid);
451}
452static DEVICE_ATTR_RO(uid);
453
454static ssize_t total_sectors_show(struct device *dev,
455				  struct device_attribute *attr, char *buf)
456{
457	struct m24lr *m24lr = i2c_get_clientdata(to_i2c_client(dev));
458
459	return sysfs_emit(buf, "%x\n", m24lr->sss_len);
460}
461static DEVICE_ATTR_RO(total_sectors);
462
463static struct attribute *m24lr_ctl_dev_attrs[] = {
464	&dev_attr_unlock.attr,
465	&dev_attr_new_pass.attr,
466	&dev_attr_uid.attr,
467	&dev_attr_total_sectors.attr,
468	NULL,
469};
470
471static const struct m24lr_chip *m24lr_get_chip(struct device *dev)
472{
473	const struct m24lr_chip *ret;
474	const struct i2c_device_id *id;
475
476	id = i2c_match_id(m24lr_ids, to_i2c_client(dev));
477
478	if (dev->of_node && of_match_device(m24lr_of_match, dev))
479		ret = of_device_get_match_data(dev);
480	else if (id)
481		ret = (void *)id->driver_data;
482	else
483		ret = acpi_device_get_match_data(dev);
484
485	return ret;
486}
487
488static int m24lr_probe(struct i2c_client *client)
489{
490	struct regmap_config eeprom_regmap_conf = {0};
491	struct nvmem_config nvmem_conf = {0};
492	struct device *dev = &client->dev;
493	struct i2c_client *eeprom_client;
494	const struct m24lr_chip *chip;
495	struct regmap *eeprom_regmap;
496	struct nvmem_device *nvmem;
497	struct regmap *ctl_regmap;
498	struct m24lr *m24lr;
499	u32 regs[2];
500	long err;
501
502	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
503		return -EOPNOTSUPP;
504
505	chip = m24lr_get_chip(dev);
506	if (!chip)
507		return -ENODEV;
508
509	m24lr = devm_kzalloc(dev, sizeof(struct m24lr), GFP_KERNEL);
510	if (!m24lr)
511		return -ENOMEM;
512
513	err = device_property_read_u32_array(dev, "reg", regs, ARRAY_SIZE(regs));
514	if (err)
515		return dev_err_probe(dev, err, "Failed to read 'reg' property\n");
516
517	/* Create a second I2C client for the eeprom interface */
518	eeprom_client = devm_i2c_new_dummy_device(dev, client->adapter, regs[1]);
519	if (IS_ERR(eeprom_client))
520		return dev_err_probe(dev, PTR_ERR(eeprom_client),
521				     "Failed to create dummy I2C client for the EEPROM\n");
522
523	ctl_regmap = devm_regmap_init_i2c(client, &m24lr_ctl_regmap_conf);
524	if (IS_ERR(ctl_regmap))
525		return dev_err_probe(dev, PTR_ERR(ctl_regmap),
526				      "Failed to init regmap\n");
527
528	eeprom_regmap_conf.name = "m24lr_eeprom";
529	eeprom_regmap_conf.reg_bits = 16;
530	eeprom_regmap_conf.val_bits = 8;
531	eeprom_regmap_conf.disable_locking = true;
532	eeprom_regmap_conf.max_register = chip->eeprom_size - 1;
533
534	eeprom_regmap = devm_regmap_init_i2c(eeprom_client,
535					     &eeprom_regmap_conf);
536	if (IS_ERR(eeprom_regmap))
537		return dev_err_probe(dev, PTR_ERR(eeprom_regmap),
538				     "Failed to init regmap\n");
539
540	mutex_init(&m24lr->lock);
541	m24lr->sss_len = chip->sss_len;
542	m24lr->page_size = chip->page_size;
543	m24lr->eeprom_size = chip->eeprom_size;
544	m24lr->eeprom_regmap = eeprom_regmap;
545	m24lr->ctl_regmap = ctl_regmap;
546
547	nvmem_conf.dev = &eeprom_client->dev;
548	nvmem_conf.owner = THIS_MODULE;
549	nvmem_conf.type = NVMEM_TYPE_EEPROM;
550	nvmem_conf.reg_read = m24lr_nvmem_read;
551	nvmem_conf.reg_write = m24lr_nvmem_write;
552	nvmem_conf.size = chip->eeprom_size;
553	nvmem_conf.word_size = 1;
554	nvmem_conf.stride = 1;
555	nvmem_conf.priv = m24lr;
556
557	nvmem = devm_nvmem_register(dev, &nvmem_conf);
558	if (IS_ERR(nvmem))
559		return dev_err_probe(dev, PTR_ERR(nvmem),
560				     "Failed to register nvmem\n");
561
562	i2c_set_clientdata(client, m24lr);
563	i2c_set_clientdata(eeprom_client, m24lr);
564
565	bin_attr_sss.size = chip->sss_len;
566	bin_attr_sss.private = m24lr;
567	err = sysfs_create_bin_file(&dev->kobj, &bin_attr_sss);
568	if (err)
569		return dev_err_probe(dev, err,
570				     "Failed to create sss bin file\n");
571
572	/* test by reading the uid, if success store it */
573	err = m24lr_read_reg_le(m24lr, &m24lr->uid, 2324, sizeof(m24lr->uid));
574	if (IS_ERR_VALUE(err))
575		goto remove_bin_file;
576
577	return 0;
578
579remove_bin_file:
580	sysfs_remove_bin_file(&dev->kobj, &bin_attr_sss);
581
582	return err;
583}
584
585static void m24lr_remove(struct i2c_client *client)
586{
587	sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_sss);
588}
589
590ATTRIBUTE_GROUPS(m24lr_ctl_dev);
591
592static struct i2c_driver m24lr_driver = {
593	.driver = {
594		.name = "m24lr",
595		.of_match_table = m24lr_of_match,
596		.dev_groups = m24lr_ctl_dev_groups,
597	},
598	.probe	  = m24lr_probe,
599	.remove = m24lr_remove,
600	.id_table = m24lr_ids,
601};
602module_i2c_driver(m24lr_driver);
603
604MODULE_AUTHOR("Abd-Alrhman Masalkhi");
605MODULE_DESCRIPTION("st m24lr control driver");
606MODULE_LICENSE("GPL");
~/ Read-only file preview.