Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

eeprom: add driver for ST M24LR series RFID/NFC EEPROM chips

adds support for STMicroelectronics M24LRxx devices, which expose
two separate I2C addresses: one for system control and one for EEPROM
access. The driver implements both a sysfs-based interface for control
registers (e.g. UID, password authentication) and an nvmem provider
for EEPROM access.

Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
Link: https://lore.kernel.org/r/20250717063934.5083-3-abd.masalkhi@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Abd-Alrhman Masalkhi and committed by
Greg Kroah-Hartman
cd5c5e02 46b4ddd2

+625
+18
drivers/misc/eeprom/Kconfig
··· 120 120 This driver can also be built as a module. If so, the module 121 121 will be called ee1004. 122 122 123 + config EEPROM_M24LR 124 + tristate "STMicroelectronics M24LR RFID/NFC EEPROM support" 125 + depends on I2C && SYSFS 126 + select REGMAP_I2C 127 + select NVMEM 128 + select NVMEM_SYSFS 129 + help 130 + This enables support for STMicroelectronics M24LR RFID/NFC EEPROM 131 + chips. These dual-interface devices expose two I2C addresses: 132 + one for EEPROM memory access and another for control and system 133 + configuration (e.g. UID, password handling). 134 + 135 + This driver provides a sysfs interface for control functions and 136 + integrates with the nvmem subsystem for EEPROM access. 137 + 138 + To compile this driver as a module, choose M here: the 139 + module will be called m24lr. 140 + 123 141 endmenu
+1
drivers/misc/eeprom/Makefile
··· 7 7 obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o 8 8 obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o 9 9 obj-$(CONFIG_EEPROM_EE1004) += ee1004.o 10 + obj-$(CONFIG_EEPROM_M24LR) += m24lr.o
+606
drivers/misc/eeprom/m24lr.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 + */ 36 + struct 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 + */ 56 + struct 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 + 66 + static const struct regmap_range m24lr_ctl_vo_ranges[] = { 67 + regmap_reg_range(0, 63), 68 + }; 69 + 70 + static 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 + 75 + static 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 */ 86 + static 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 */ 93 + static 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 */ 100 + static const struct m24lr_chip m24lr64e_r_chip = { 101 + .page_size = 4, 102 + .eeprom_size = 8192, 103 + .sss_len = 64, 104 + }; 105 + 106 + static 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 + }; 112 + MODULE_DEVICE_TABLE(i2c, m24lr_ids); 113 + 114 + static 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 + }; 120 + MODULE_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 + */ 138 + static 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 + */ 177 + static 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 + 201 + static 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 + */ 237 + static 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 + */ 292 + static 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 + 324 + static 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 + 358 + static 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 + 377 + static 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 + 396 + static 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 + 411 + static 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 + } 425 + static BIN_ATTR(sss, 0600, m24lr_ctl_sss_read, m24lr_ctl_sss_write, 0); 426 + 427 + static 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 + } 434 + static DEVICE_ATTR_WO(new_pass); 435 + 436 + static 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 + } 443 + static DEVICE_ATTR_WO(unlock); 444 + 445 + static 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 + } 452 + static DEVICE_ATTR_RO(uid); 453 + 454 + static 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 + } 461 + static DEVICE_ATTR_RO(total_sectors); 462 + 463 + static 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 + 471 + static 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 + 488 + static 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 + 579 + remove_bin_file: 580 + sysfs_remove_bin_file(&dev->kobj, &bin_attr_sss); 581 + 582 + return err; 583 + } 584 + 585 + static void m24lr_remove(struct i2c_client *client) 586 + { 587 + sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_sss); 588 + } 589 + 590 + ATTRIBUTE_GROUPS(m24lr_ctl_dev); 591 + 592 + static 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 + }; 602 + module_i2c_driver(m24lr_driver); 603 + 604 + MODULE_AUTHOR("Abd-Alrhman Masalkhi"); 605 + MODULE_DESCRIPTION("st m24lr control driver"); 606 + MODULE_LICENSE("GPL");