| 1 | |
| 2 | |
| 3 | * Driver for most of the SPI EEPROMs, such as Atmel AT25 models
|
| 4 | * and Cypress FRAMs FM25 models.
|
| 5 | *
|
| 6 | * Copyright (C) 2006 David Brownell
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/bits.h>
|
| 10 | #include <linux/cleanup.h>
|
| 11 | #include <linux/delay.h>
|
| 12 | #include <linux/device.h>
|
| 13 | #include <linux/iopoll.h>
|
| 14 | #include <linux/kernel.h>
|
| 15 | #include <linux/module.h>
|
| 16 | #include <linux/property.h>
|
| 17 | #include <linux/sched.h>
|
| 18 | #include <linux/slab.h>
|
| 19 |
|
| 20 | #include <linux/spi/eeprom.h>
|
| 21 | #include <linux/spi/spi.h>
|
| 22 | #include <linux/spi/spi-mem.h>
|
| 23 |
|
| 24 | #include <linux/nvmem-provider.h>
|
| 25 |
|
| 26 | |
| 27 | * NOTE: this is an *EEPROM* driver. The vagaries of product naming
|
| 28 | * mean that some AT25 products are EEPROMs, and others are FLASH.
|
| 29 | * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver,
|
| 30 | * not this one!
|
| 31 | *
|
| 32 | * EEPROMs that can be used with this driver include, for example:
|
| 33 | * AT25M02, AT25128B
|
| 34 |
|
| 35 |
|
| 36 | #define FM25_SN_LEN 8
|
| 37 | #define FM25_MAX_ID_LEN 9
|
| 38 | #define EE_MAXADDRLEN 3
|
| 39 |
|
| 40 | struct at25_data {
|
| 41 | struct spi_eeprom chip;
|
| 42 | struct spi_mem *spimem;
|
| 43 | struct mutex lock;
|
| 44 | unsigned addrlen;
|
| 45 | struct nvmem_config nvmem_config;
|
| 46 | struct nvmem_device *nvmem;
|
| 47 | u8 sernum[FM25_SN_LEN];
|
| 48 | u8 id[FM25_MAX_ID_LEN];
|
| 49 | u8 id_len;
|
| 50 | };
|
| 51 |
|
| 52 | #define AT25_WREN 0x06
|
| 53 | #define AT25_WRDI 0x04
|
| 54 | #define AT25_RDSR 0x05
|
| 55 | #define AT25_WRSR 0x01
|
| 56 | #define AT25_READ 0x03
|
| 57 | #define AT25_WRITE 0x02
|
| 58 | #define FM25_SLEEP 0xb9
|
| 59 | #define FM25_RDID 0x9f
|
| 60 | #define FM25_RDSN 0xc3
|
| 61 |
|
| 62 | #define AT25_SR_nRDY 0x01
|
| 63 | #define AT25_SR_WEN 0x02
|
| 64 | #define AT25_SR_BP0 0x04
|
| 65 | #define AT25_SR_BP1 0x08
|
| 66 | #define AT25_SR_WPEN 0x80
|
| 67 |
|
| 68 | #define AT25_INSTR_BIT3 0x08
|
| 69 |
|
| 70 | |
| 71 | * Specs often allow 5ms for a page write, sometimes 20ms;
|
| 72 | * it's important to recover from write timeouts.
|
| 73 |
|
| 74 | #define EE_TIMEOUT 25
|
| 75 |
|
| 76 |
|
| 77 |
|
| 78 | #define io_limit PAGE_SIZE
|
| 79 |
|
| 80 |
|
| 81 | static u8 at25_instr(struct at25_data *at25, u8 instr, unsigned int off)
|
| 82 | {
|
| 83 | if (!(at25->chip.flags & EE_INSTR_BIT3_IS_ADDR))
|
| 84 | return instr;
|
| 85 | if (off < BIT(at25->addrlen * 8))
|
| 86 | return instr;
|
| 87 | return instr | AT25_INSTR_BIT3;
|
| 88 | }
|
| 89 |
|
| 90 | static int at25_ee_read(void *priv, unsigned int offset,
|
| 91 | void *val, size_t count)
|
| 92 | {
|
| 93 | u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL);
|
| 94 | struct at25_data *at25 = priv;
|
| 95 | char *buf = val;
|
| 96 | unsigned int msg_offset = offset;
|
| 97 | size_t bytes_left = count;
|
| 98 | size_t segment;
|
| 99 | int status;
|
| 100 |
|
| 101 | if (!bounce)
|
| 102 | return -ENOMEM;
|
| 103 |
|
| 104 | if (unlikely(offset >= at25->chip.byte_len))
|
| 105 | return -EINVAL;
|
| 106 | if ((offset + count) > at25->chip.byte_len)
|
| 107 | count = at25->chip.byte_len - offset;
|
| 108 | if (unlikely(!count))
|
| 109 | return -EINVAL;
|
| 110 |
|
| 111 | do {
|
| 112 | struct spi_mem_op op;
|
| 113 |
|
| 114 | segment = min(bytes_left, io_limit);
|
| 115 |
|
| 116 | op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_READ,
|
| 117 | msg_offset), 1),
|
| 118 | SPI_MEM_OP_ADDR(at25->addrlen, msg_offset, 1),
|
| 119 | SPI_MEM_OP_NO_DUMMY,
|
| 120 | SPI_MEM_OP_DATA_IN(segment, bounce, 1));
|
| 121 |
|
| 122 | status = spi_mem_adjust_op_size(at25->spimem, &op);
|
| 123 | if (status)
|
| 124 | return status;
|
| 125 | segment = op.data.nbytes;
|
| 126 |
|
| 127 | mutex_lock(&at25->lock);
|
| 128 | status = spi_mem_exec_op(at25->spimem, &op);
|
| 129 | mutex_unlock(&at25->lock);
|
| 130 | if (status)
|
| 131 | return status;
|
| 132 | memcpy(buf, bounce, segment);
|
| 133 |
|
| 134 | msg_offset += segment;
|
| 135 | buf += segment;
|
| 136 | bytes_left -= segment;
|
| 137 | } while (bytes_left > 0);
|
| 138 |
|
| 139 | dev_dbg(&at25->spimem->spi->dev, "read %zu bytes at %d\n",
|
| 140 | count, offset);
|
| 141 | return 0;
|
| 142 | }
|
| 143 |
|
| 144 | |
| 145 | * Read extra registers as ID or serial number
|
| 146 | *
|
| 147 | * Allow for the callers to provide @buf on stack (not necessary DMA-capable)
|
| 148 | * by allocating a bounce buffer internally.
|
| 149 |
|
| 150 | static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command,
|
| 151 | int len)
|
| 152 | {
|
| 153 | u8 *bounce __free(kfree) = kmalloc(len, GFP_KERNEL);
|
| 154 | struct spi_mem_op op;
|
| 155 | int status;
|
| 156 |
|
| 157 | if (!bounce)
|
| 158 | return -ENOMEM;
|
| 159 |
|
| 160 | op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(command, 1),
|
| 161 | SPI_MEM_OP_NO_ADDR,
|
| 162 | SPI_MEM_OP_NO_DUMMY,
|
| 163 | SPI_MEM_OP_DATA_IN(len, bounce, 1));
|
| 164 |
|
| 165 | status = spi_mem_exec_op(at25->spimem, &op);
|
| 166 | dev_dbg(&at25->spimem->spi->dev, "read %d aux bytes --> %d\n", len, status);
|
| 167 | if (status)
|
| 168 | return status;
|
| 169 |
|
| 170 | memcpy(buf, bounce, len);
|
| 171 |
|
| 172 | return 0;
|
| 173 | }
|
| 174 |
|
| 175 | static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf)
|
| 176 | {
|
| 177 | struct at25_data *at25;
|
| 178 |
|
| 179 | at25 = dev_get_drvdata(dev);
|
| 180 | return sysfs_emit(buf, "%*ph\n", (int)sizeof(at25->sernum), at25->sernum);
|
| 181 | }
|
| 182 | static DEVICE_ATTR_RO(sernum);
|
| 183 |
|
| 184 | static ssize_t jedec_id_show(struct device *dev, struct device_attribute *attr, char *buf)
|
| 185 | {
|
| 186 | struct at25_data *at25;
|
| 187 |
|
| 188 | at25 = dev_get_drvdata(dev);
|
| 189 |
|
| 190 | if (!at25->id_len)
|
| 191 | return -EOPNOTSUPP;
|
| 192 |
|
| 193 | return sysfs_emit(buf, "%*phN\n", at25->id_len, at25->id);
|
| 194 | }
|
| 195 | static DEVICE_ATTR_RO(jedec_id);
|
| 196 |
|
| 197 | static struct attribute *at25_attrs[] = {
|
| 198 | &dev_attr_sernum.attr,
|
| 199 | &dev_attr_jedec_id.attr,
|
| 200 | NULL,
|
| 201 | };
|
| 202 | ATTRIBUTE_GROUPS(at25);
|
| 203 |
|
| 204 | |
| 205 | * Poll Read Status Register with timeout
|
| 206 | *
|
| 207 | * Return:
|
| 208 | * 0, if the chip is ready
|
| 209 | * [positive] Status Register value as-is, if the chip is busy
|
| 210 | * [negative] error code in case of read failure
|
| 211 |
|
| 212 | static int at25_wait_ready(struct at25_data *at25)
|
| 213 | {
|
| 214 | u8 *bounce __free(kfree) = kmalloc(1, GFP_KERNEL);
|
| 215 | struct spi_mem_op op;
|
| 216 | int status;
|
| 217 |
|
| 218 | if (!bounce)
|
| 219 | return -ENOMEM;
|
| 220 |
|
| 221 | op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_RDSR, 1),
|
| 222 | SPI_MEM_OP_NO_ADDR,
|
| 223 | SPI_MEM_OP_NO_DUMMY,
|
| 224 | SPI_MEM_OP_DATA_IN(1, bounce, 1));
|
| 225 |
|
| 226 | read_poll_timeout(spi_mem_exec_op, status,
|
| 227 | status || !(bounce[0] & AT25_SR_nRDY), false,
|
| 228 | USEC_PER_MSEC, USEC_PER_MSEC * EE_TIMEOUT,
|
| 229 | at25->spimem, &op);
|
| 230 | if (status < 0)
|
| 231 | return status;
|
| 232 | if (!(bounce[0] & AT25_SR_nRDY))
|
| 233 | return 0;
|
| 234 |
|
| 235 | return bounce[0];
|
| 236 | }
|
| 237 |
|
| 238 | static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
|
| 239 | {
|
| 240 | u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL);
|
| 241 | struct at25_data *at25 = priv;
|
| 242 | const char *buf = val;
|
| 243 | unsigned int buf_size;
|
| 244 | int status;
|
| 245 |
|
| 246 | if (unlikely(off >= at25->chip.byte_len))
|
| 247 | return -EFBIG;
|
| 248 | if ((off + count) > at25->chip.byte_len)
|
| 249 | count = at25->chip.byte_len - off;
|
| 250 | if (unlikely(!count))
|
| 251 | return -EINVAL;
|
| 252 |
|
| 253 | buf_size = at25->chip.page_size;
|
| 254 |
|
| 255 | if (!bounce)
|
| 256 | return -ENOMEM;
|
| 257 |
|
| 258 | |
| 259 | * For write, rollover is within the page ... so we write at
|
| 260 | * most one page, then manually roll over to the next page.
|
| 261 |
|
| 262 | guard(mutex)(&at25->lock);
|
| 263 | do {
|
| 264 | struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_WREN, 1),
|
| 265 | SPI_MEM_OP_NO_ADDR,
|
| 266 | SPI_MEM_OP_NO_DUMMY,
|
| 267 | SPI_MEM_OP_NO_DATA);
|
| 268 | unsigned int segment;
|
| 269 |
|
| 270 | status = spi_mem_exec_op(at25->spimem, &op);
|
| 271 | if (status < 0) {
|
| 272 | dev_dbg(&at25->spimem->spi->dev, "WREN --> %d\n", status);
|
| 273 | return status;
|
| 274 | }
|
| 275 |
|
| 276 |
|
| 277 | segment = buf_size - (off % buf_size);
|
| 278 | if (segment > count)
|
| 279 | segment = count;
|
| 280 | if (segment > io_limit)
|
| 281 | segment = io_limit;
|
| 282 |
|
| 283 | op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_WRITE, off),
|
| 284 | 1),
|
| 285 | SPI_MEM_OP_ADDR(at25->addrlen, off, 1),
|
| 286 | SPI_MEM_OP_NO_DUMMY,
|
| 287 | SPI_MEM_OP_DATA_OUT(segment, bounce, 1));
|
| 288 |
|
| 289 | status = spi_mem_adjust_op_size(at25->spimem, &op);
|
| 290 | if (status)
|
| 291 | return status;
|
| 292 | segment = op.data.nbytes;
|
| 293 |
|
| 294 | memcpy(bounce, buf, segment);
|
| 295 |
|
| 296 | status = spi_mem_exec_op(at25->spimem, &op);
|
| 297 | dev_dbg(&at25->spimem->spi->dev, "write %u bytes at %u --> %d\n",
|
| 298 | segment, off, status);
|
| 299 | if (status)
|
| 300 | return status;
|
| 301 |
|
| 302 | |
| 303 | * REVISIT this should detect (or prevent) failed writes
|
| 304 | * to read-only sections of the EEPROM...
|
| 305 |
|
| 306 |
|
| 307 | status = at25_wait_ready(at25);
|
| 308 | if (status < 0) {
|
| 309 | dev_err_probe(&at25->spimem->spi->dev, status,
|
| 310 | "Read Status Redister command failed\n");
|
| 311 | return status;
|
| 312 | }
|
| 313 | if (status) {
|
| 314 | dev_dbg(&at25->spimem->spi->dev,
|
| 315 | "Status %02x\n", status);
|
| 316 | dev_err(&at25->spimem->spi->dev,
|
| 317 | "write %u bytes offset %u, timeout after %u msecs\n",
|
| 318 | segment, off, EE_TIMEOUT);
|
| 319 | return -ETIMEDOUT;
|
| 320 | }
|
| 321 |
|
| 322 | off += segment;
|
| 323 | buf += segment;
|
| 324 | count -= segment;
|
| 325 |
|
| 326 | } while (count > 0);
|
| 327 |
|
| 328 | return status;
|
| 329 | }
|
| 330 |
|
| 331 |
|
| 332 |
|
| 333 | static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
|
| 334 | {
|
| 335 | u32 val;
|
| 336 | int err;
|
| 337 |
|
| 338 | strscpy(chip->name, "at25", sizeof(chip->name));
|
| 339 |
|
| 340 | err = device_property_read_u32(dev, "size", &val);
|
| 341 | if (err)
|
| 342 | err = device_property_read_u32(dev, "at25,byte-len", &val);
|
| 343 | if (err) {
|
| 344 | dev_err(dev, "Error: missing \"size\" property\n");
|
| 345 | return err;
|
| 346 | }
|
| 347 | chip->byte_len = val;
|
| 348 |
|
| 349 | err = device_property_read_u32(dev, "pagesize", &val);
|
| 350 | if (err)
|
| 351 | err = device_property_read_u32(dev, "at25,page-size", &val);
|
| 352 | if (err) {
|
| 353 | dev_err(dev, "Error: missing \"pagesize\" property\n");
|
| 354 | return err;
|
| 355 | }
|
| 356 | chip->page_size = val;
|
| 357 |
|
| 358 | err = device_property_read_u32(dev, "address-width", &val);
|
| 359 | if (err) {
|
| 360 | err = device_property_read_u32(dev, "at25,addr-mode", &val);
|
| 361 | if (err) {
|
| 362 | dev_err(dev, "Error: missing \"address-width\" property\n");
|
| 363 | return err;
|
| 364 | }
|
| 365 | chip->flags = (u16)val;
|
| 366 | } else {
|
| 367 | switch (val) {
|
| 368 | case 9:
|
| 369 | chip->flags |= EE_INSTR_BIT3_IS_ADDR;
|
| 370 | fallthrough;
|
| 371 | case 8:
|
| 372 | chip->flags |= EE_ADDR1;
|
| 373 | break;
|
| 374 | case 16:
|
| 375 | chip->flags |= EE_ADDR2;
|
| 376 | break;
|
| 377 | case 24:
|
| 378 | chip->flags |= EE_ADDR3;
|
| 379 | break;
|
| 380 | default:
|
| 381 | dev_err(dev,
|
| 382 | "Error: bad \"address-width\" property: %u\n",
|
| 383 | val);
|
| 384 | return -ENODEV;
|
| 385 | }
|
| 386 | if (device_property_present(dev, "read-only"))
|
| 387 | chip->flags |= EE_READONLY;
|
| 388 | }
|
| 389 | return 0;
|
| 390 | }
|
| 391 |
|
| 392 | static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
|
| 393 | {
|
| 394 | struct at25_data *at25 = container_of(chip, struct at25_data, chip);
|
| 395 | u8 sernum[FM25_SN_LEN];
|
| 396 | u8 id[FM25_MAX_ID_LEN];
|
| 397 | u32 val;
|
| 398 | int i;
|
| 399 |
|
| 400 | strscpy(chip->name, "fm25", sizeof(chip->name));
|
| 401 |
|
| 402 | if (!device_property_read_u32(dev, "size", &val)) {
|
| 403 | chip->byte_len = val;
|
| 404 | } else {
|
| 405 |
|
| 406 | fm25_aux_read(at25, id, FM25_RDID, FM25_MAX_ID_LEN);
|
| 407 |
|
| 408 |
|
| 409 | memcpy(at25->id, id, FM25_MAX_ID_LEN);
|
| 410 | at25->id_len = FM25_MAX_ID_LEN;
|
| 411 |
|
| 412 |
|
| 413 | if (id[6] == 0x7f && id[2] == 0xc2)
|
| 414 | for (i = 0; i < ARRAY_SIZE(id) / 2; i++) {
|
| 415 | u8 tmp = id[i];
|
| 416 | int j = ARRAY_SIZE(id) - i - 1;
|
| 417 |
|
| 418 | id[i] = id[j];
|
| 419 | id[j] = tmp;
|
| 420 | }
|
| 421 |
|
| 422 | if (id[6] == 0xc2) {
|
| 423 | at25->id_len = 9;
|
| 424 | switch (id[7]) {
|
| 425 | case 0x21 ... 0x26:
|
| 426 | chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024;
|
| 427 | break;
|
| 428 | case 0x2a ... 0x30:
|
| 429 |
|
| 430 | chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13);
|
| 431 | break;
|
| 432 | default:
|
| 433 | dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]);
|
| 434 | return -ENODEV;
|
| 435 | }
|
| 436 | } else if (id[2] == 0x82 && id[3] == 0x06) {
|
| 437 | at25->id_len = 8;
|
| 438 | switch (id[1]) {
|
| 439 | case 0x51 ... 0x54:
|
| 440 |
|
| 441 | chip->byte_len = BIT(((id[0] >> 3) & 0x1F) + 9);
|
| 442 | break;
|
| 443 | default:
|
| 444 | dev_err(dev, "Error: unsupported product id %02x\n", id[1]);
|
| 445 | return -ENODEV;
|
| 446 | }
|
| 447 | } else {
|
| 448 | dev_err(dev, "Error: unrecognized JEDEC ID format: %*ph\n",
|
| 449 | FM25_MAX_ID_LEN, id);
|
| 450 | return -ENODEV;
|
| 451 | }
|
| 452 |
|
| 453 | fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN);
|
| 454 |
|
| 455 | for (i = 0; i < FM25_SN_LEN; i++)
|
| 456 | at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i];
|
| 457 | }
|
| 458 |
|
| 459 | if (chip->byte_len > 64 * 1024)
|
| 460 | chip->flags |= EE_ADDR3;
|
| 461 | else
|
| 462 | chip->flags |= EE_ADDR2;
|
| 463 |
|
| 464 | chip->page_size = PAGE_SIZE;
|
| 465 | return 0;
|
| 466 | }
|
| 467 |
|
| 468 | static const struct of_device_id at25_of_match[] = {
|
| 469 | { .compatible = "atmel,at25" },
|
| 470 | { .compatible = "cypress,fm25" },
|
| 471 | { }
|
| 472 | };
|
| 473 | MODULE_DEVICE_TABLE(of, at25_of_match);
|
| 474 |
|
| 475 | static const struct spi_device_id at25_spi_ids[] = {
|
| 476 | { .name = "at25" },
|
| 477 | { .name = "fm25" },
|
| 478 | { }
|
| 479 | };
|
| 480 | MODULE_DEVICE_TABLE(spi, at25_spi_ids);
|
| 481 |
|
| 482 | static int at25_probe(struct spi_mem *mem)
|
| 483 | {
|
| 484 | struct spi_device *spi = mem->spi;
|
| 485 | struct spi_eeprom *pdata;
|
| 486 | struct at25_data *at25;
|
| 487 | bool is_fram;
|
| 488 | int err;
|
| 489 |
|
| 490 | at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL);
|
| 491 | if (!at25)
|
| 492 | return -ENOMEM;
|
| 493 |
|
| 494 | at25->spimem = mem;
|
| 495 |
|
| 496 | |
| 497 | * Ping the chip ... the status register is pretty portable,
|
| 498 | * unlike probing manufacturer IDs.
|
| 499 |
|
| 500 | err = at25_wait_ready(at25);
|
| 501 | if (err < 0)
|
| 502 | return dev_err_probe(&spi->dev, err, "Read Status Register command failed\n");
|
| 503 | if (err) {
|
| 504 | dev_err(&spi->dev, "Not ready (%02x)\n", err);
|
| 505 | return -ENXIO;
|
| 506 | }
|
| 507 |
|
| 508 | mutex_init(&at25->lock);
|
| 509 | spi_set_drvdata(spi, at25);
|
| 510 |
|
| 511 | is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25");
|
| 512 |
|
| 513 |
|
| 514 | pdata = dev_get_platdata(&spi->dev);
|
| 515 | if (pdata) {
|
| 516 | at25->chip = *pdata;
|
| 517 | } else {
|
| 518 | if (is_fram)
|
| 519 | err = at25_fram_to_chip(&spi->dev, &at25->chip);
|
| 520 | else
|
| 521 | err = at25_fw_to_chip(&spi->dev, &at25->chip);
|
| 522 | if (err)
|
| 523 | return err;
|
| 524 | }
|
| 525 |
|
| 526 |
|
| 527 | if (at25->chip.flags & EE_ADDR1)
|
| 528 | at25->addrlen = 1;
|
| 529 | else if (at25->chip.flags & EE_ADDR2)
|
| 530 | at25->addrlen = 2;
|
| 531 | else if (at25->chip.flags & EE_ADDR3)
|
| 532 | at25->addrlen = 3;
|
| 533 | else {
|
| 534 | dev_dbg(&spi->dev, "unsupported address type\n");
|
| 535 | return -EINVAL;
|
| 536 | }
|
| 537 |
|
| 538 | at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM;
|
| 539 | at25->nvmem_config.name = dev_name(&spi->dev);
|
| 540 | at25->nvmem_config.dev = &spi->dev;
|
| 541 | at25->nvmem_config.read_only = at25->chip.flags & EE_READONLY;
|
| 542 | at25->nvmem_config.root_only = true;
|
| 543 | at25->nvmem_config.owner = THIS_MODULE;
|
| 544 | at25->nvmem_config.compat = true;
|
| 545 | at25->nvmem_config.base_dev = &spi->dev;
|
| 546 | at25->nvmem_config.reg_read = at25_ee_read;
|
| 547 | at25->nvmem_config.reg_write = at25_ee_write;
|
| 548 | at25->nvmem_config.priv = at25;
|
| 549 | at25->nvmem_config.stride = 1;
|
| 550 | at25->nvmem_config.word_size = 1;
|
| 551 | at25->nvmem_config.size = at25->chip.byte_len;
|
| 552 |
|
| 553 | at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config);
|
| 554 | if (IS_ERR(at25->nvmem))
|
| 555 | return PTR_ERR(at25->nvmem);
|
| 556 |
|
| 557 | dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n",
|
| 558 | (at25->chip.byte_len < 1024) ?
|
| 559 | at25->chip.byte_len : (at25->chip.byte_len / 1024),
|
| 560 | (at25->chip.byte_len < 1024) ? "Byte" : "KByte",
|
| 561 | at25->chip.name, is_fram ? "fram" : "eeprom",
|
| 562 | (at25->chip.flags & EE_READONLY) ? " (readonly)" : "",
|
| 563 | at25->chip.page_size);
|
| 564 | return 0;
|
| 565 | }
|
| 566 |
|
| 567 |
|
| 568 |
|
| 569 | static struct spi_mem_driver at25_driver = {
|
| 570 | .spidrv = {
|
| 571 | .driver = {
|
| 572 | .name = "at25",
|
| 573 | .of_match_table = at25_of_match,
|
| 574 | .dev_groups = at25_groups,
|
| 575 | },
|
| 576 | .id_table = at25_spi_ids,
|
| 577 | },
|
| 578 | .probe = at25_probe,
|
| 579 | };
|
| 580 |
|
| 581 | module_spi_mem_driver(at25_driver);
|
| 582 |
|
| 583 | MODULE_DESCRIPTION("Driver for most SPI EEPROMs");
|
| 584 | MODULE_AUTHOR("David Brownell");
|
| 585 | MODULE_LICENSE("GPL");
|