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