// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for most of the SPI EEPROMs, such as Atmel AT25 models * and Cypress FRAMs FM25 models. * * Copyright (C) 2006 David Brownell
*/
/* * NOTE: this is an *EEPROM* driver. The vagaries of product naming * mean that some AT25 products are EEPROMs, and others are FLASH. * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver, * not this one! * * EEPROMs that can be used with this driver include, for example: * AT25M02, AT25128B
*/
#define FM25_SN_LEN 8 /* serial number length */ #define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
/* * Read extra registers as ID or serial number * * Allow for the callers to provide @buf on stack (not necessary DMA-capable) * by allocating a bounce buffer internally.
*/ staticint fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, int len)
{
u8 *bounce __free(kfree) = kmalloc(len, GFP_KERNEL); struct spi_mem_op op; int status;
if (!bounce) return -ENOMEM;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(command, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(len, bounce, 1));
status = spi_mem_exec_op(at25->spimem, &op);
dev_dbg(&at25->spimem->spi->dev, "read %d aux bytes --> %d\n", len, status); if (status) return status;
/* * Poll Read Status Register with timeout * * Return: * 0, if the chip is ready * [positive] Status Register value as-is, if the chip is busy * [negative] error code in case of read failure
*/ staticint at25_wait_ready(struct at25_data *at25)
{
u8 *bounce __free(kfree) = kmalloc(1, GFP_KERNEL); struct spi_mem_op op; int status;
if (!bounce) return -ENOMEM;
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_RDSR, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, bounce, 1));
read_poll_timeout(spi_mem_exec_op, status,
status || !(bounce[0] & AT25_SR_nRDY), false,
USEC_PER_MSEC, USEC_PER_MSEC * EE_TIMEOUT,
at25->spimem, &op); if (status < 0) return status; if (!(bounce[0] & AT25_SR_nRDY)) return 0;
if (unlikely(off >= at25->chip.byte_len)) return -EFBIG; if ((off + count) > at25->chip.byte_len)
count = at25->chip.byte_len - off; if (unlikely(!count)) return -EINVAL;
buf_size = at25->chip.page_size;
if (!bounce) return -ENOMEM;
/* * For write, rollover is within the page ... so we write at * most one page, then manually roll over to the next page.
*/
guard(mutex)(&at25->lock); do { struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_WREN, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA); unsignedint segment;
status = spi_mem_exec_op(at25->spimem, &op); if (status < 0) {
dev_dbg(&at25->spimem->spi->dev, "WREN --> %d\n", status); return status;
}
/* Write as much of a page as we can */
segment = buf_size - (off % buf_size); if (segment > count)
segment = count; if (segment > io_limit)
segment = io_limit;
if (!device_property_read_u32(dev, "size", &val)) {
chip->byte_len = val;
} else { /* Get ID of chip */
fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); /* There are inside-out FRAM variations, detect them and reverse the ID bytes */ if (id[6] == 0x7f && id[2] == 0xc2) for (i = 0; i < ARRAY_SIZE(id) / 2; i++) {
u8 tmp = id[i]; int j = ARRAY_SIZE(id) - i - 1;
id[i] = id[j];
id[j] = tmp;
} if (id[6] != 0xc2) {
dev_err(dev, "Error: no Cypress FRAM (id %02x)\n", id[6]); return -ENODEV;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.