| .. | .. |
|---|
| 1 | | -// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 2 | 2 | /* |
|---|
| 3 | 3 | * at24.c - handle most I2C EEPROMs |
|---|
| 4 | 4 | * |
|---|
| .. | .. |
|---|
| 6 | 6 | * Copyright (C) 2008 Wolfram Sang, Pengutronix |
|---|
| 7 | 7 | */ |
|---|
| 8 | 8 | |
|---|
| 9 | | -#include <linux/kernel.h> |
|---|
| 10 | | -#include <linux/init.h> |
|---|
| 11 | | -#include <linux/module.h> |
|---|
| 12 | | -#include <linux/of_device.h> |
|---|
| 13 | | -#include <linux/slab.h> |
|---|
| 14 | | -#include <linux/delay.h> |
|---|
| 15 | | -#include <linux/mutex.h> |
|---|
| 16 | | -#include <linux/mod_devicetable.h> |
|---|
| 17 | | -#include <linux/log2.h> |
|---|
| 18 | | -#include <linux/bitops.h> |
|---|
| 19 | | -#include <linux/jiffies.h> |
|---|
| 20 | | -#include <linux/property.h> |
|---|
| 21 | 9 | #include <linux/acpi.h> |
|---|
| 10 | +#include <linux/bitops.h> |
|---|
| 11 | +#include <linux/capability.h> |
|---|
| 12 | +#include <linux/delay.h> |
|---|
| 22 | 13 | #include <linux/i2c.h> |
|---|
| 14 | +#include <linux/init.h> |
|---|
| 15 | +#include <linux/jiffies.h> |
|---|
| 16 | +#include <linux/kernel.h> |
|---|
| 17 | +#include <linux/mod_devicetable.h> |
|---|
| 18 | +#include <linux/module.h> |
|---|
| 19 | +#include <linux/mutex.h> |
|---|
| 23 | 20 | #include <linux/nvmem-provider.h> |
|---|
| 24 | | -#include <linux/regmap.h> |
|---|
| 25 | | -#include <linux/platform_data/at24.h> |
|---|
| 21 | +#include <linux/of_device.h> |
|---|
| 26 | 22 | #include <linux/pm_runtime.h> |
|---|
| 27 | | -#include <linux/gpio/consumer.h> |
|---|
| 23 | +#include <linux/property.h> |
|---|
| 24 | +#include <linux/regmap.h> |
|---|
| 25 | +#include <linux/regulator/consumer.h> |
|---|
| 26 | +#include <linux/slab.h> |
|---|
| 27 | + |
|---|
| 28 | +/* Address pointer is 16 bit. */ |
|---|
| 29 | +#define AT24_FLAG_ADDR16 BIT(7) |
|---|
| 30 | +/* sysfs-entry will be read-only. */ |
|---|
| 31 | +#define AT24_FLAG_READONLY BIT(6) |
|---|
| 32 | +/* sysfs-entry will be world-readable. */ |
|---|
| 33 | +#define AT24_FLAG_IRUGO BIT(5) |
|---|
| 34 | +/* Take always 8 addresses (24c00). */ |
|---|
| 35 | +#define AT24_FLAG_TAKE8ADDR BIT(4) |
|---|
| 36 | +/* Factory-programmed serial number. */ |
|---|
| 37 | +#define AT24_FLAG_SERIAL BIT(3) |
|---|
| 38 | +/* Factory-programmed mac address. */ |
|---|
| 39 | +#define AT24_FLAG_MAC BIT(2) |
|---|
| 40 | +/* Does not auto-rollover reads to the next slave address. */ |
|---|
| 41 | +#define AT24_FLAG_NO_RDROL BIT(1) |
|---|
| 28 | 42 | |
|---|
| 29 | 43 | /* |
|---|
| 30 | 44 | * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. |
|---|
| .. | .. |
|---|
| 75 | 89 | u8 flags; |
|---|
| 76 | 90 | |
|---|
| 77 | 91 | struct nvmem_device *nvmem; |
|---|
| 78 | | - |
|---|
| 79 | | - struct gpio_desc *wp_gpio; |
|---|
| 92 | + struct regulator *vcc_reg; |
|---|
| 93 | + void (*read_post)(unsigned int off, char *buf, size_t count); |
|---|
| 80 | 94 | |
|---|
| 81 | 95 | /* |
|---|
| 82 | 96 | * Some chips tie up multiple I2C addresses; dummy devices reserve |
|---|
| .. | .. |
|---|
| 106 | 120 | module_param_named(write_timeout, at24_write_timeout, uint, 0); |
|---|
| 107 | 121 | MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); |
|---|
| 108 | 122 | |
|---|
| 123 | +struct at24_data *at24_private=NULL; |
|---|
| 124 | + |
|---|
| 109 | 125 | struct at24_chip_data { |
|---|
| 110 | | - /* |
|---|
| 111 | | - * these fields mirror their equivalents in |
|---|
| 112 | | - * struct at24_platform_data |
|---|
| 113 | | - */ |
|---|
| 114 | 126 | u32 byte_len; |
|---|
| 115 | 127 | u8 flags; |
|---|
| 128 | + void (*read_post)(unsigned int off, char *buf, size_t count); |
|---|
| 116 | 129 | }; |
|---|
| 117 | 130 | |
|---|
| 118 | 131 | #define AT24_CHIP_DATA(_name, _len, _flags) \ |
|---|
| 119 | 132 | static const struct at24_chip_data _name = { \ |
|---|
| 120 | 133 | .byte_len = _len, .flags = _flags, \ |
|---|
| 121 | 134 | } |
|---|
| 135 | + |
|---|
| 136 | +#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \ |
|---|
| 137 | + static const struct at24_chip_data _name = { \ |
|---|
| 138 | + .byte_len = _len, .flags = _flags, \ |
|---|
| 139 | + .read_post = _read_post, \ |
|---|
| 140 | + } |
|---|
| 141 | + |
|---|
| 142 | +static void at24_read_post_vaio(unsigned int off, char *buf, size_t count) |
|---|
| 143 | +{ |
|---|
| 144 | + int i; |
|---|
| 145 | + |
|---|
| 146 | + if (capable(CAP_SYS_ADMIN)) |
|---|
| 147 | + return; |
|---|
| 148 | + |
|---|
| 149 | + /* |
|---|
| 150 | + * Hide VAIO private settings to regular users: |
|---|
| 151 | + * - BIOS passwords: bytes 0x00 to 0x0f |
|---|
| 152 | + * - UUID: bytes 0x10 to 0x1f |
|---|
| 153 | + * - Serial number: 0xc0 to 0xdf |
|---|
| 154 | + */ |
|---|
| 155 | + for (i = 0; i < count; i++) { |
|---|
| 156 | + if ((off + i <= 0x1f) || |
|---|
| 157 | + (off + i >= 0xc0 && off + i <= 0xdf)) |
|---|
| 158 | + buf[i] = 0; |
|---|
| 159 | + } |
|---|
| 160 | +} |
|---|
| 122 | 161 | |
|---|
| 123 | 162 | /* needs 8 addresses as A0-A2 are ignored */ |
|---|
| 124 | 163 | AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); |
|---|
| .. | .. |
|---|
| 136 | 175 | /* spd is a 24c02 in memory DIMMs */ |
|---|
| 137 | 176 | AT24_CHIP_DATA(at24_data_spd, 2048 / 8, |
|---|
| 138 | 177 | AT24_FLAG_READONLY | AT24_FLAG_IRUGO); |
|---|
| 178 | +/* 24c02_vaio is a 24c02 on some Sony laptops */ |
|---|
| 179 | +AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8, |
|---|
| 180 | + AT24_FLAG_READONLY | AT24_FLAG_IRUGO, |
|---|
| 181 | + at24_read_post_vaio); |
|---|
| 139 | 182 | AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); |
|---|
| 140 | 183 | AT24_CHIP_DATA(at24_data_24cs04, 16, |
|---|
| 141 | 184 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); |
|---|
| .. | .. |
|---|
| 169 | 212 | { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, |
|---|
| 170 | 213 | { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, |
|---|
| 171 | 214 | { "spd", (kernel_ulong_t)&at24_data_spd }, |
|---|
| 215 | + { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, |
|---|
| 172 | 216 | { "24c04", (kernel_ulong_t)&at24_data_24c04 }, |
|---|
| 173 | 217 | { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, |
|---|
| 174 | 218 | { "24c08", (kernel_ulong_t)&at24_data_24c08 }, |
|---|
| .. | .. |
|---|
| 217 | 261 | }; |
|---|
| 218 | 262 | MODULE_DEVICE_TABLE(of, at24_of_match); |
|---|
| 219 | 263 | |
|---|
| 220 | | -static const struct acpi_device_id at24_acpi_ids[] = { |
|---|
| 264 | +static const struct acpi_device_id __maybe_unused at24_acpi_ids[] = { |
|---|
| 221 | 265 | { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, |
|---|
| 266 | + { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 }, |
|---|
| 222 | 267 | { /* END OF LIST */ } |
|---|
| 223 | 268 | }; |
|---|
| 224 | 269 | MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); |
|---|
| .. | .. |
|---|
| 379 | 424 | struct at24_data *at24; |
|---|
| 380 | 425 | struct device *dev; |
|---|
| 381 | 426 | char *buf = val; |
|---|
| 382 | | - int ret; |
|---|
| 427 | + int i, ret; |
|---|
| 383 | 428 | |
|---|
| 384 | 429 | at24 = priv; |
|---|
| 385 | 430 | dev = at24_base_client_dev(at24); |
|---|
| .. | .. |
|---|
| 402 | 447 | */ |
|---|
| 403 | 448 | mutex_lock(&at24->lock); |
|---|
| 404 | 449 | |
|---|
| 405 | | - while (count) { |
|---|
| 406 | | - ret = at24_regmap_read(at24, buf, off, count); |
|---|
| 450 | + for (i = 0; count; i += ret, count -= ret) { |
|---|
| 451 | + ret = at24_regmap_read(at24, buf + i, off + i, count); |
|---|
| 407 | 452 | if (ret < 0) { |
|---|
| 408 | 453 | mutex_unlock(&at24->lock); |
|---|
| 409 | 454 | pm_runtime_put(dev); |
|---|
| 410 | 455 | return ret; |
|---|
| 411 | 456 | } |
|---|
| 412 | | - buf += ret; |
|---|
| 413 | | - off += ret; |
|---|
| 414 | | - count -= ret; |
|---|
| 415 | 457 | } |
|---|
| 416 | 458 | |
|---|
| 417 | 459 | mutex_unlock(&at24->lock); |
|---|
| 418 | 460 | |
|---|
| 419 | 461 | pm_runtime_put(dev); |
|---|
| 420 | 462 | |
|---|
| 463 | + if (unlikely(at24->read_post)) |
|---|
| 464 | + at24->read_post(off, buf, i); |
|---|
| 465 | + |
|---|
| 421 | 466 | return 0; |
|---|
| 422 | 467 | } |
|---|
| 468 | + |
|---|
| 469 | + |
|---|
| 470 | + |
|---|
| 471 | +static ssize_t at24_read_private(struct at24_data *at24, |
|---|
| 472 | + char *buf, loff_t off, size_t count) |
|---|
| 473 | +{ |
|---|
| 474 | + ssize_t retval = 0; |
|---|
| 475 | + |
|---|
| 476 | + if (unlikely(!count)) |
|---|
| 477 | + return count; |
|---|
| 478 | + |
|---|
| 479 | + if (off + count > at24->byte_len) |
|---|
| 480 | + return -EINVAL; |
|---|
| 481 | + |
|---|
| 482 | + /* |
|---|
| 483 | + * Read data from chip, protecting against concurrent updates |
|---|
| 484 | + * from this host, but not from other I2C masters. |
|---|
| 485 | + */ |
|---|
| 486 | + mutex_lock(&at24->lock); |
|---|
| 487 | + |
|---|
| 488 | + while (count) { |
|---|
| 489 | + ssize_t status; |
|---|
| 490 | + |
|---|
| 491 | + //status = at24_eeprom_read_i2c(at24, buf, off, count); |
|---|
| 492 | + status = at24_regmap_read(at24, buf, off, count); |
|---|
| 493 | + if (status <= 0) { |
|---|
| 494 | + if (retval == 0) |
|---|
| 495 | + retval = status; |
|---|
| 496 | + break; |
|---|
| 497 | + } |
|---|
| 498 | + buf += status; |
|---|
| 499 | + off += status; |
|---|
| 500 | + count -= status; |
|---|
| 501 | + retval += status; |
|---|
| 502 | + } |
|---|
| 503 | + |
|---|
| 504 | + mutex_unlock(&at24->lock); |
|---|
| 505 | + |
|---|
| 506 | + return retval; |
|---|
| 507 | +} |
|---|
| 508 | + |
|---|
| 509 | +#if 0 |
|---|
| 510 | +static unsigned char AscToHex(unsigned char aChar) |
|---|
| 511 | +{ |
|---|
| 512 | + if((aChar>=0x30)&&(aChar<=0x39)) |
|---|
| 513 | + aChar -= 0x30; |
|---|
| 514 | + else if((aChar>=0x41)&&(aChar<=0x46)) |
|---|
| 515 | + aChar -= 0x37; |
|---|
| 516 | + else if((aChar>=0x61)&&(aChar<=0x66)) |
|---|
| 517 | + aChar -= 0x57; |
|---|
| 518 | + else aChar = 0xff; |
|---|
| 519 | + |
|---|
| 520 | + return aChar; |
|---|
| 521 | +} |
|---|
| 522 | +#endif |
|---|
| 523 | + |
|---|
| 524 | +#if 0 |
|---|
| 525 | +ssize_t at24_mac_read(unsigned char* addr) |
|---|
| 526 | +{ |
|---|
| 527 | + char buf[20]; |
|---|
| 528 | + char buf_tmp[12]; |
|---|
| 529 | + int i; |
|---|
| 530 | + ssize_t ret; |
|---|
| 531 | + if (at24_private == NULL) |
|---|
| 532 | + { |
|---|
| 533 | + printk("ben %s: at24_private==null error\n", __func__); |
|---|
| 534 | + return 0; |
|---|
| 535 | + } |
|---|
| 536 | + memset(buf, 0x00, 20); |
|---|
| 537 | + memset(buf_tmp, 0x00, 12); |
|---|
| 538 | + ret = at24_read(at24_private, 0, buf, 12); |
|---|
| 539 | + if (ret > 0) |
|---|
| 540 | + { |
|---|
| 541 | + for(i=0; i<12; i++) |
|---|
| 542 | + { |
|---|
| 543 | + buf_tmp[i] = AscToHex(buf[i]); |
|---|
| 544 | + } |
|---|
| 545 | + addr[0] = (buf_tmp[0] << 4) | buf_tmp[1]; |
|---|
| 546 | + addr[1] = (buf_tmp[2] << 4) | buf_tmp[3]; |
|---|
| 547 | + addr[2] = (buf_tmp[4] << 4) | buf_tmp[5]; |
|---|
| 548 | + addr[3] = (buf_tmp[6] << 4) | buf_tmp[7]; |
|---|
| 549 | + addr[4] = (buf_tmp[8] << 4) | buf_tmp[9]; |
|---|
| 550 | + addr[5] = (buf_tmp[10] << 4) | buf_tmp[11]; |
|---|
| 551 | + } |
|---|
| 552 | + return ret; |
|---|
| 553 | +} |
|---|
| 554 | +#endif |
|---|
| 555 | + |
|---|
| 556 | +ssize_t at24_mac_read(unsigned char* addr) |
|---|
| 557 | +{ |
|---|
| 558 | + char buf[20]; |
|---|
| 559 | + char buf_tmp[12]; |
|---|
| 560 | + ssize_t ret; |
|---|
| 561 | + if (at24_private == NULL) |
|---|
| 562 | + { |
|---|
| 563 | + printk("ben: at24_mac_read at24_private==null error"); |
|---|
| 564 | + return 0; |
|---|
| 565 | + } |
|---|
| 566 | + memset(buf, 0x00, 20); |
|---|
| 567 | + memset(buf_tmp, 0x00, 12); |
|---|
| 568 | + ret = at24_read_private(at24_private, buf, 0, 6); |
|---|
| 569 | + if (ret > 0) |
|---|
| 570 | + { |
|---|
| 571 | + addr[0] = buf[0]; |
|---|
| 572 | + addr[1] = buf[1]; |
|---|
| 573 | + addr[2] = buf[2]; |
|---|
| 574 | + addr[3] = buf[3]; |
|---|
| 575 | + addr[4] = buf[4]; |
|---|
| 576 | + addr[5] = buf[5]; |
|---|
| 577 | + } |
|---|
| 578 | + printk("at24_mac_read ...............\n"); |
|---|
| 579 | + return ret; |
|---|
| 580 | +} |
|---|
| 581 | +EXPORT_SYMBOL(at24_mac_read); |
|---|
| 582 | + |
|---|
| 583 | +ssize_t at24_mac1_read(unsigned char* mac) |
|---|
| 584 | +{ |
|---|
| 585 | + char buf[20]; |
|---|
| 586 | + char buf_tmp[12]; |
|---|
| 587 | + ssize_t ret; |
|---|
| 588 | + if (at24_private == NULL) |
|---|
| 589 | + { |
|---|
| 590 | + printk("zcl: at24_mac_read at24_private==null error"); |
|---|
| 591 | + return 0; |
|---|
| 592 | + } |
|---|
| 593 | + memset(buf, 0x00, 20); |
|---|
| 594 | + memset(buf_tmp, 0x00, 12); |
|---|
| 595 | + ret = at24_read_private(at24_private, buf, 0x10, 6); |
|---|
| 596 | + if (ret > 0) |
|---|
| 597 | + { |
|---|
| 598 | + *mac = buf[0]; |
|---|
| 599 | + *(mac + 1) = buf[1]; |
|---|
| 600 | + *(mac + 2) = buf[2]; |
|---|
| 601 | + *(mac + 3) = buf[3]; |
|---|
| 602 | + *(mac + 4) = buf[4]; |
|---|
| 603 | + *(mac + 5) = buf[5]; |
|---|
| 604 | + } |
|---|
| 605 | + printk("at24_mac1_read ...............\n"); |
|---|
| 606 | + return ret; |
|---|
| 607 | +} |
|---|
| 608 | +EXPORT_SYMBOL(at24_mac1_read); |
|---|
| 423 | 609 | |
|---|
| 424 | 610 | static int at24_write(void *priv, unsigned int off, void *val, size_t count) |
|---|
| 425 | 611 | { |
|---|
| .. | .. |
|---|
| 448 | 634 | * from this host, but not from other I2C masters. |
|---|
| 449 | 635 | */ |
|---|
| 450 | 636 | mutex_lock(&at24->lock); |
|---|
| 451 | | - gpiod_set_value_cansleep(at24->wp_gpio, 0); |
|---|
| 452 | 637 | |
|---|
| 453 | 638 | while (count) { |
|---|
| 454 | 639 | ret = at24_regmap_write(at24, buf, off, count); |
|---|
| 455 | 640 | if (ret < 0) { |
|---|
| 456 | | - gpiod_set_value_cansleep(at24->wp_gpio, 1); |
|---|
| 457 | 641 | mutex_unlock(&at24->lock); |
|---|
| 458 | 642 | pm_runtime_put(dev); |
|---|
| 459 | 643 | return ret; |
|---|
| .. | .. |
|---|
| 463 | 647 | count -= ret; |
|---|
| 464 | 648 | } |
|---|
| 465 | 649 | |
|---|
| 466 | | - gpiod_set_value_cansleep(at24->wp_gpio, 1); |
|---|
| 467 | 650 | mutex_unlock(&at24->lock); |
|---|
| 468 | 651 | |
|---|
| 469 | 652 | pm_runtime_put(dev); |
|---|
| .. | .. |
|---|
| 471 | 654 | return 0; |
|---|
| 472 | 655 | } |
|---|
| 473 | 656 | |
|---|
| 474 | | -static void at24_properties_to_pdata(struct device *dev, |
|---|
| 475 | | - struct at24_platform_data *chip) |
|---|
| 476 | | -{ |
|---|
| 477 | | - int err; |
|---|
| 478 | | - u32 val; |
|---|
| 479 | | - |
|---|
| 480 | | - if (device_property_present(dev, "read-only")) |
|---|
| 481 | | - chip->flags |= AT24_FLAG_READONLY; |
|---|
| 482 | | - if (device_property_present(dev, "no-read-rollover")) |
|---|
| 483 | | - chip->flags |= AT24_FLAG_NO_RDROL; |
|---|
| 484 | | - |
|---|
| 485 | | - err = device_property_read_u32(dev, "address-width", &val); |
|---|
| 486 | | - if (!err) { |
|---|
| 487 | | - switch (val) { |
|---|
| 488 | | - case 8: |
|---|
| 489 | | - if (chip->flags & AT24_FLAG_ADDR16) |
|---|
| 490 | | - dev_warn(dev, "Override address width to be 8, while default is 16\n"); |
|---|
| 491 | | - chip->flags &= ~AT24_FLAG_ADDR16; |
|---|
| 492 | | - break; |
|---|
| 493 | | - case 16: |
|---|
| 494 | | - chip->flags |= AT24_FLAG_ADDR16; |
|---|
| 495 | | - break; |
|---|
| 496 | | - default: |
|---|
| 497 | | - dev_warn(dev, "Bad \"address-width\" property: %u\n", |
|---|
| 498 | | - val); |
|---|
| 499 | | - } |
|---|
| 500 | | - } |
|---|
| 501 | | - |
|---|
| 502 | | - err = device_property_read_u32(dev, "size", &val); |
|---|
| 503 | | - if (!err) |
|---|
| 504 | | - chip->byte_len = val; |
|---|
| 505 | | - |
|---|
| 506 | | - err = device_property_read_u32(dev, "pagesize", &val); |
|---|
| 507 | | - if (!err) { |
|---|
| 508 | | - chip->page_size = val; |
|---|
| 509 | | - } else { |
|---|
| 510 | | - /* |
|---|
| 511 | | - * This is slow, but we can't know all eeproms, so we better |
|---|
| 512 | | - * play safe. Specifying custom eeprom-types via platform_data |
|---|
| 513 | | - * is recommended anyhow. |
|---|
| 514 | | - */ |
|---|
| 515 | | - chip->page_size = 1; |
|---|
| 516 | | - } |
|---|
| 517 | | -} |
|---|
| 518 | | - |
|---|
| 519 | | -static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) |
|---|
| 657 | +static const struct at24_chip_data *at24_get_chip_data(struct device *dev) |
|---|
| 520 | 658 | { |
|---|
| 521 | 659 | struct device_node *of_node = dev->of_node; |
|---|
| 522 | 660 | const struct at24_chip_data *cdata; |
|---|
| 523 | 661 | const struct i2c_device_id *id; |
|---|
| 524 | | - struct at24_platform_data *pd; |
|---|
| 525 | | - |
|---|
| 526 | | - pd = dev_get_platdata(dev); |
|---|
| 527 | | - if (pd) { |
|---|
| 528 | | - memcpy(pdata, pd, sizeof(*pdata)); |
|---|
| 529 | | - return 0; |
|---|
| 530 | | - } |
|---|
| 531 | 662 | |
|---|
| 532 | 663 | id = i2c_match_id(at24_ids, to_i2c_client(dev)); |
|---|
| 533 | 664 | |
|---|
| .. | .. |
|---|
| 544 | 675 | cdata = acpi_device_get_match_data(dev); |
|---|
| 545 | 676 | |
|---|
| 546 | 677 | if (!cdata) |
|---|
| 547 | | - return -ENODEV; |
|---|
| 678 | + return ERR_PTR(-ENODEV); |
|---|
| 548 | 679 | |
|---|
| 549 | | - pdata->byte_len = cdata->byte_len; |
|---|
| 550 | | - pdata->flags = cdata->flags; |
|---|
| 551 | | - at24_properties_to_pdata(dev, pdata); |
|---|
| 552 | | - |
|---|
| 553 | | - return 0; |
|---|
| 554 | | -} |
|---|
| 555 | | - |
|---|
| 556 | | -static void at24_remove_dummy_clients(struct at24_data *at24) |
|---|
| 557 | | -{ |
|---|
| 558 | | - int i; |
|---|
| 559 | | - |
|---|
| 560 | | - for (i = 1; i < at24->num_addresses; i++) |
|---|
| 561 | | - i2c_unregister_device(at24->client[i].client); |
|---|
| 680 | + return cdata; |
|---|
| 562 | 681 | } |
|---|
| 563 | 682 | |
|---|
| 564 | 683 | static int at24_make_dummy_client(struct at24_data *at24, unsigned int index, |
|---|
| 565 | 684 | struct regmap_config *regmap_config) |
|---|
| 566 | 685 | { |
|---|
| 567 | 686 | struct i2c_client *base_client, *dummy_client; |
|---|
| 568 | | - unsigned short int addr; |
|---|
| 569 | 687 | struct regmap *regmap; |
|---|
| 570 | 688 | struct device *dev; |
|---|
| 571 | 689 | |
|---|
| 572 | 690 | base_client = at24->client[0].client; |
|---|
| 573 | 691 | dev = &base_client->dev; |
|---|
| 574 | | - addr = base_client->addr + index; |
|---|
| 575 | 692 | |
|---|
| 576 | | - dummy_client = i2c_new_dummy(base_client->adapter, |
|---|
| 577 | | - base_client->addr + index); |
|---|
| 578 | | - if (!dummy_client) { |
|---|
| 579 | | - dev_err(dev, "address 0x%02x unavailable\n", addr); |
|---|
| 580 | | - return -EADDRINUSE; |
|---|
| 581 | | - } |
|---|
| 693 | + dummy_client = devm_i2c_new_dummy_device(dev, base_client->adapter, |
|---|
| 694 | + base_client->addr + index); |
|---|
| 695 | + if (IS_ERR(dummy_client)) |
|---|
| 696 | + return PTR_ERR(dummy_client); |
|---|
| 582 | 697 | |
|---|
| 583 | 698 | regmap = devm_regmap_init_i2c(dummy_client, regmap_config); |
|---|
| 584 | | - if (IS_ERR(regmap)) { |
|---|
| 585 | | - i2c_unregister_device(dummy_client); |
|---|
| 699 | + if (IS_ERR(regmap)) |
|---|
| 586 | 700 | return PTR_ERR(regmap); |
|---|
| 587 | | - } |
|---|
| 588 | 701 | |
|---|
| 589 | 702 | at24->client[index].client = dummy_client; |
|---|
| 590 | 703 | at24->client[index].regmap = regmap; |
|---|
| .. | .. |
|---|
| 619 | 732 | { |
|---|
| 620 | 733 | struct regmap_config regmap_config = { }; |
|---|
| 621 | 734 | struct nvmem_config nvmem_config = { }; |
|---|
| 622 | | - struct at24_platform_data pdata = { }; |
|---|
| 735 | + u32 byte_len, page_size, flags, addrw; |
|---|
| 736 | + const struct at24_chip_data *cdata; |
|---|
| 623 | 737 | struct device *dev = &client->dev; |
|---|
| 624 | 738 | bool i2c_fn_i2c, i2c_fn_block; |
|---|
| 625 | 739 | unsigned int i, num_addresses; |
|---|
| 626 | 740 | struct at24_data *at24; |
|---|
| 627 | 741 | struct regmap *regmap; |
|---|
| 628 | | - size_t at24_size; |
|---|
| 629 | 742 | bool writable; |
|---|
| 630 | 743 | u8 test_byte; |
|---|
| 631 | 744 | int err; |
|---|
| .. | .. |
|---|
| 634 | 747 | i2c_fn_block = i2c_check_functionality(client->adapter, |
|---|
| 635 | 748 | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); |
|---|
| 636 | 749 | |
|---|
| 637 | | - err = at24_get_pdata(dev, &pdata); |
|---|
| 750 | + cdata = at24_get_chip_data(dev); |
|---|
| 751 | + if (IS_ERR(cdata)) |
|---|
| 752 | + return PTR_ERR(cdata); |
|---|
| 753 | + |
|---|
| 754 | + err = device_property_read_u32(dev, "pagesize", &page_size); |
|---|
| 638 | 755 | if (err) |
|---|
| 639 | | - return err; |
|---|
| 756 | + /* |
|---|
| 757 | + * This is slow, but we can't know all eeproms, so we better |
|---|
| 758 | + * play safe. Specifying custom eeprom-types via device tree |
|---|
| 759 | + * or properties is recommended anyhow. |
|---|
| 760 | + */ |
|---|
| 761 | + page_size = 1; |
|---|
| 762 | + |
|---|
| 763 | + flags = cdata->flags; |
|---|
| 764 | + if (device_property_present(dev, "read-only")) |
|---|
| 765 | + flags |= AT24_FLAG_READONLY; |
|---|
| 766 | + if (device_property_present(dev, "no-read-rollover")) |
|---|
| 767 | + flags |= AT24_FLAG_NO_RDROL; |
|---|
| 768 | + |
|---|
| 769 | + err = device_property_read_u32(dev, "address-width", &addrw); |
|---|
| 770 | + if (!err) { |
|---|
| 771 | + switch (addrw) { |
|---|
| 772 | + case 8: |
|---|
| 773 | + if (flags & AT24_FLAG_ADDR16) |
|---|
| 774 | + dev_warn(dev, |
|---|
| 775 | + "Override address width to be 8, while default is 16\n"); |
|---|
| 776 | + flags &= ~AT24_FLAG_ADDR16; |
|---|
| 777 | + break; |
|---|
| 778 | + case 16: |
|---|
| 779 | + flags |= AT24_FLAG_ADDR16; |
|---|
| 780 | + break; |
|---|
| 781 | + default: |
|---|
| 782 | + dev_warn(dev, "Bad \"address-width\" property: %u\n", |
|---|
| 783 | + addrw); |
|---|
| 784 | + } |
|---|
| 785 | + } |
|---|
| 786 | + |
|---|
| 787 | + err = device_property_read_u32(dev, "size", &byte_len); |
|---|
| 788 | + if (err) |
|---|
| 789 | + byte_len = cdata->byte_len; |
|---|
| 640 | 790 | |
|---|
| 641 | 791 | if (!i2c_fn_i2c && !i2c_fn_block) |
|---|
| 642 | | - pdata.page_size = 1; |
|---|
| 792 | + page_size = 1; |
|---|
| 643 | 793 | |
|---|
| 644 | | - if (!pdata.page_size) { |
|---|
| 794 | + if (!page_size) { |
|---|
| 645 | 795 | dev_err(dev, "page_size must not be 0!\n"); |
|---|
| 646 | 796 | return -EINVAL; |
|---|
| 647 | 797 | } |
|---|
| 648 | 798 | |
|---|
| 649 | | - if (!is_power_of_2(pdata.page_size)) |
|---|
| 799 | + if (!is_power_of_2(page_size)) |
|---|
| 650 | 800 | dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); |
|---|
| 651 | 801 | |
|---|
| 652 | | - if (pdata.flags & AT24_FLAG_TAKE8ADDR) |
|---|
| 653 | | - num_addresses = 8; |
|---|
| 654 | | - else |
|---|
| 655 | | - num_addresses = DIV_ROUND_UP(pdata.byte_len, |
|---|
| 656 | | - (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256); |
|---|
| 802 | + err = device_property_read_u32(dev, "num-addresses", &num_addresses); |
|---|
| 803 | + if (err) { |
|---|
| 804 | + if (flags & AT24_FLAG_TAKE8ADDR) |
|---|
| 805 | + num_addresses = 8; |
|---|
| 806 | + else |
|---|
| 807 | + num_addresses = DIV_ROUND_UP(byte_len, |
|---|
| 808 | + (flags & AT24_FLAG_ADDR16) ? 65536 : 256); |
|---|
| 809 | + } |
|---|
| 657 | 810 | |
|---|
| 658 | | - if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) { |
|---|
| 811 | + if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) { |
|---|
| 659 | 812 | dev_err(dev, |
|---|
| 660 | 813 | "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); |
|---|
| 661 | 814 | return -EINVAL; |
|---|
| 662 | 815 | } |
|---|
| 663 | 816 | |
|---|
| 664 | 817 | regmap_config.val_bits = 8; |
|---|
| 665 | | - regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8; |
|---|
| 818 | + regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8; |
|---|
| 666 | 819 | regmap_config.disable_locking = true; |
|---|
| 667 | 820 | |
|---|
| 668 | 821 | regmap = devm_regmap_init_i2c(client, ®map_config); |
|---|
| 669 | 822 | if (IS_ERR(regmap)) |
|---|
| 670 | 823 | return PTR_ERR(regmap); |
|---|
| 671 | 824 | |
|---|
| 672 | | - at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client); |
|---|
| 673 | | - at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL); |
|---|
| 825 | + at24 = devm_kzalloc(dev, struct_size(at24, client, num_addresses), |
|---|
| 826 | + GFP_KERNEL); |
|---|
| 674 | 827 | if (!at24) |
|---|
| 675 | 828 | return -ENOMEM; |
|---|
| 676 | 829 | |
|---|
| 830 | + at24_private = at24; |
|---|
| 677 | 831 | mutex_init(&at24->lock); |
|---|
| 678 | | - at24->byte_len = pdata.byte_len; |
|---|
| 679 | | - at24->page_size = pdata.page_size; |
|---|
| 680 | | - at24->flags = pdata.flags; |
|---|
| 832 | + at24->byte_len = byte_len; |
|---|
| 833 | + at24->page_size = page_size; |
|---|
| 834 | + at24->flags = flags; |
|---|
| 835 | + at24->read_post = cdata->read_post; |
|---|
| 681 | 836 | at24->num_addresses = num_addresses; |
|---|
| 682 | | - at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len); |
|---|
| 837 | + at24->offset_adj = at24_get_offset_adj(flags, byte_len); |
|---|
| 683 | 838 | at24->client[0].client = client; |
|---|
| 684 | 839 | at24->client[0].regmap = regmap; |
|---|
| 685 | 840 | |
|---|
| 686 | | - at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH); |
|---|
| 687 | | - if (IS_ERR(at24->wp_gpio)) |
|---|
| 688 | | - return PTR_ERR(at24->wp_gpio); |
|---|
| 841 | + at24->vcc_reg = devm_regulator_get(dev, "vcc"); |
|---|
| 842 | + if (IS_ERR(at24->vcc_reg)) |
|---|
| 843 | + return PTR_ERR(at24->vcc_reg); |
|---|
| 689 | 844 | |
|---|
| 690 | | - writable = !(pdata.flags & AT24_FLAG_READONLY); |
|---|
| 845 | + writable = !(flags & AT24_FLAG_READONLY); |
|---|
| 691 | 846 | if (writable) { |
|---|
| 692 | 847 | at24->write_max = min_t(unsigned int, |
|---|
| 693 | | - pdata.page_size, at24_io_limit); |
|---|
| 848 | + page_size, at24_io_limit); |
|---|
| 694 | 849 | if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) |
|---|
| 695 | 850 | at24->write_max = I2C_SMBUS_BLOCK_MAX; |
|---|
| 696 | 851 | } |
|---|
| .. | .. |
|---|
| 698 | 853 | /* use dummy devices for multiple-address chips */ |
|---|
| 699 | 854 | for (i = 1; i < num_addresses; i++) { |
|---|
| 700 | 855 | err = at24_make_dummy_client(at24, i, ®map_config); |
|---|
| 701 | | - if (err) { |
|---|
| 702 | | - at24_remove_dummy_clients(at24); |
|---|
| 856 | + if (err) |
|---|
| 703 | 857 | return err; |
|---|
| 704 | | - } |
|---|
| 705 | 858 | } |
|---|
| 706 | | - |
|---|
| 707 | | - i2c_set_clientdata(client, at24); |
|---|
| 708 | | - |
|---|
| 709 | | - /* enable runtime pm */ |
|---|
| 710 | | - pm_runtime_set_active(dev); |
|---|
| 711 | | - pm_runtime_enable(dev); |
|---|
| 712 | 859 | |
|---|
| 713 | 860 | /* |
|---|
| 714 | | - * Perform a one-byte test read to verify that the |
|---|
| 715 | | - * chip is functional. |
|---|
| 861 | + * We initialize nvmem_config.id to NVMEM_DEVID_AUTO even if the |
|---|
| 862 | + * label property is set as some platform can have multiple eeproms |
|---|
| 863 | + * with same label and we can not register each of those with same |
|---|
| 864 | + * label. Failing to register those eeproms trigger cascade failure |
|---|
| 865 | + * on such platform. |
|---|
| 716 | 866 | */ |
|---|
| 717 | | - err = at24_read(at24, 0, &test_byte, 1); |
|---|
| 718 | | - pm_runtime_idle(dev); |
|---|
| 719 | | - if (err) { |
|---|
| 720 | | - err = -ENODEV; |
|---|
| 721 | | - goto err_clients; |
|---|
| 867 | + nvmem_config.id = NVMEM_DEVID_AUTO; |
|---|
| 868 | + |
|---|
| 869 | + if (device_property_present(dev, "label")) { |
|---|
| 870 | + err = device_property_read_string(dev, "label", |
|---|
| 871 | + &nvmem_config.name); |
|---|
| 872 | + if (err) |
|---|
| 873 | + return err; |
|---|
| 874 | + } else { |
|---|
| 875 | + nvmem_config.name = dev_name(dev); |
|---|
| 722 | 876 | } |
|---|
| 723 | 877 | |
|---|
| 724 | | - nvmem_config.name = dev_name(dev); |
|---|
| 878 | + nvmem_config.type = NVMEM_TYPE_EEPROM; |
|---|
| 725 | 879 | nvmem_config.dev = dev; |
|---|
| 726 | 880 | nvmem_config.read_only = !writable; |
|---|
| 727 | | - nvmem_config.root_only = !(pdata.flags & AT24_FLAG_IRUGO); |
|---|
| 881 | + nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO); |
|---|
| 728 | 882 | nvmem_config.owner = THIS_MODULE; |
|---|
| 729 | 883 | nvmem_config.compat = true; |
|---|
| 730 | 884 | nvmem_config.base_dev = dev; |
|---|
| .. | .. |
|---|
| 733 | 887 | nvmem_config.priv = at24; |
|---|
| 734 | 888 | nvmem_config.stride = 1; |
|---|
| 735 | 889 | nvmem_config.word_size = 1; |
|---|
| 736 | | - nvmem_config.size = pdata.byte_len; |
|---|
| 890 | + nvmem_config.size = byte_len; |
|---|
| 891 | + |
|---|
| 892 | + i2c_set_clientdata(client, at24); |
|---|
| 893 | + |
|---|
| 894 | + err = regulator_enable(at24->vcc_reg); |
|---|
| 895 | + if (err) { |
|---|
| 896 | + dev_err(dev, "Failed to enable vcc regulator\n"); |
|---|
| 897 | + return err; |
|---|
| 898 | + } |
|---|
| 899 | + |
|---|
| 900 | + /* enable runtime pm */ |
|---|
| 901 | + pm_runtime_set_active(dev); |
|---|
| 902 | + pm_runtime_enable(dev); |
|---|
| 737 | 903 | |
|---|
| 738 | 904 | at24->nvmem = devm_nvmem_register(dev, &nvmem_config); |
|---|
| 739 | 905 | if (IS_ERR(at24->nvmem)) { |
|---|
| 740 | | - err = PTR_ERR(at24->nvmem); |
|---|
| 741 | | - goto err_clients; |
|---|
| 906 | + pm_runtime_disable(dev); |
|---|
| 907 | + if (!pm_runtime_status_suspended(dev)) |
|---|
| 908 | + regulator_disable(at24->vcc_reg); |
|---|
| 909 | + return PTR_ERR(at24->nvmem); |
|---|
| 742 | 910 | } |
|---|
| 743 | 911 | |
|---|
| 744 | | - dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", |
|---|
| 745 | | - pdata.byte_len, client->name, |
|---|
| 746 | | - writable ? "writable" : "read-only", at24->write_max); |
|---|
| 912 | + /* |
|---|
| 913 | + * Perform a one-byte test read to verify that the |
|---|
| 914 | + * chip is functional. |
|---|
| 915 | + */ |
|---|
| 916 | + err = at24_read(at24, 0, &test_byte, 1); |
|---|
| 917 | + if (err) { |
|---|
| 918 | + pm_runtime_disable(dev); |
|---|
| 919 | + if (!pm_runtime_status_suspended(dev)) |
|---|
| 920 | + regulator_disable(at24->vcc_reg); |
|---|
| 921 | + return -ENODEV; |
|---|
| 922 | + } |
|---|
| 747 | 923 | |
|---|
| 748 | | - /* export data to kernel code */ |
|---|
| 749 | | - if (pdata.setup) |
|---|
| 750 | | - pdata.setup(at24->nvmem, pdata.context); |
|---|
| 924 | + pm_runtime_idle(dev); |
|---|
| 925 | + |
|---|
| 926 | + if (writable) |
|---|
| 927 | + dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n", |
|---|
| 928 | + byte_len, client->name, at24->write_max); |
|---|
| 929 | + else |
|---|
| 930 | + dev_info(dev, "%u byte %s EEPROM, read-only\n", |
|---|
| 931 | + byte_len, client->name); |
|---|
| 751 | 932 | |
|---|
| 752 | 933 | return 0; |
|---|
| 753 | | - |
|---|
| 754 | | -err_clients: |
|---|
| 755 | | - at24_remove_dummy_clients(at24); |
|---|
| 756 | | - pm_runtime_disable(dev); |
|---|
| 757 | | - |
|---|
| 758 | | - return err; |
|---|
| 759 | 934 | } |
|---|
| 760 | 935 | |
|---|
| 761 | 936 | static int at24_remove(struct i2c_client *client) |
|---|
| 762 | 937 | { |
|---|
| 763 | | - struct at24_data *at24; |
|---|
| 938 | + struct at24_data *at24 = i2c_get_clientdata(client); |
|---|
| 764 | 939 | |
|---|
| 765 | | - at24 = i2c_get_clientdata(client); |
|---|
| 766 | | - |
|---|
| 767 | | - at24_remove_dummy_clients(at24); |
|---|
| 768 | 940 | pm_runtime_disable(&client->dev); |
|---|
| 941 | + if (!pm_runtime_status_suspended(&client->dev)) |
|---|
| 942 | + regulator_disable(at24->vcc_reg); |
|---|
| 769 | 943 | pm_runtime_set_suspended(&client->dev); |
|---|
| 770 | 944 | |
|---|
| 771 | 945 | return 0; |
|---|
| 772 | 946 | } |
|---|
| 773 | 947 | |
|---|
| 948 | +static int __maybe_unused at24_suspend(struct device *dev) |
|---|
| 949 | +{ |
|---|
| 950 | + struct i2c_client *client = to_i2c_client(dev); |
|---|
| 951 | + struct at24_data *at24 = i2c_get_clientdata(client); |
|---|
| 952 | + |
|---|
| 953 | + return regulator_disable(at24->vcc_reg); |
|---|
| 954 | +} |
|---|
| 955 | + |
|---|
| 956 | +static int __maybe_unused at24_resume(struct device *dev) |
|---|
| 957 | +{ |
|---|
| 958 | + struct i2c_client *client = to_i2c_client(dev); |
|---|
| 959 | + struct at24_data *at24 = i2c_get_clientdata(client); |
|---|
| 960 | + |
|---|
| 961 | + return regulator_enable(at24->vcc_reg); |
|---|
| 962 | +} |
|---|
| 963 | + |
|---|
| 964 | +static const struct dev_pm_ops at24_pm_ops = { |
|---|
| 965 | + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
|---|
| 966 | + pm_runtime_force_resume) |
|---|
| 967 | + SET_RUNTIME_PM_OPS(at24_suspend, at24_resume, NULL) |
|---|
| 968 | +}; |
|---|
| 969 | + |
|---|
| 774 | 970 | static struct i2c_driver at24_driver = { |
|---|
| 775 | 971 | .driver = { |
|---|
| 776 | 972 | .name = "at24", |
|---|
| 973 | + .pm = &at24_pm_ops, |
|---|
| 777 | 974 | .of_match_table = at24_of_match, |
|---|
| 778 | 975 | .acpi_match_table = ACPI_PTR(at24_acpi_ids), |
|---|
| 779 | 976 | }, |
|---|
| .. | .. |
|---|
| 792 | 989 | at24_io_limit = rounddown_pow_of_two(at24_io_limit); |
|---|
| 793 | 990 | return i2c_add_driver(&at24_driver); |
|---|
| 794 | 991 | } |
|---|
| 795 | | -module_init(at24_init); |
|---|
| 992 | +//module_init(at24_init); |
|---|
| 993 | +postcore_initcall_sync(at24_init); |
|---|
| 796 | 994 | |
|---|
| 797 | 995 | static void __exit at24_exit(void) |
|---|
| 798 | 996 | { |
|---|