drivers/misc/eeprom/at25.c

Source file repositories/reference/linux-study-clean/drivers/misc/eeprom/at25.c

File Facts

System
Linux kernel
Corpus path
drivers/misc/eeprom/at25.c
Extension
.c
Size
15000 bytes
Lines
586
Domain
Driver Families
Bucket
drivers/misc
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct at25_data {
	struct spi_eeprom	chip;
	struct spi_mem		*spimem;
	struct mutex		lock;
	unsigned		addrlen;
	struct nvmem_config	nvmem_config;
	struct nvmem_device	*nvmem;
	u8 sernum[FM25_SN_LEN];
	u8 id[FM25_MAX_ID_LEN];
	u8 id_len;
};

#define	AT25_WREN	0x06		/* latch the write enable */
#define	AT25_WRDI	0x04		/* reset the write enable */
#define	AT25_RDSR	0x05		/* read status register */
#define	AT25_WRSR	0x01		/* write status register */
#define	AT25_READ	0x03		/* read byte(s) */
#define	AT25_WRITE	0x02		/* write byte(s)/sector */
#define	FM25_SLEEP	0xb9		/* enter sleep mode */
#define	FM25_RDID	0x9f		/* read device ID */
#define	FM25_RDSN	0xc3		/* read S/N */

#define	AT25_SR_nRDY	0x01		/* nRDY = write-in-progress */
#define	AT25_SR_WEN	0x02		/* write enable (latched) */
#define	AT25_SR_BP0	0x04		/* BP for software writeprotect */
#define	AT25_SR_BP1	0x08
#define	AT25_SR_WPEN	0x80		/* writeprotect enable */

#define	AT25_INSTR_BIT3	0x08		/* additional address bit in instr */

/*
 * Specs often allow 5ms for a page write, sometimes 20ms;
 * it's important to recover from write timeouts.
 */
#define	EE_TIMEOUT	25

/*-------------------------------------------------------------------------*/

#define	io_limit	PAGE_SIZE	/* bytes */

/* Handle the address MSB as part of instruction byte */
static u8 at25_instr(struct at25_data *at25, u8 instr, unsigned int off)
{
	if (!(at25->chip.flags & EE_INSTR_BIT3_IS_ADDR))
		return instr;
	if (off < BIT(at25->addrlen * 8))
		return instr;
	return instr | AT25_INSTR_BIT3;
}

static int at25_ee_read(void *priv, unsigned int offset,
			void *val, size_t count)
{
	u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL);
	struct at25_data *at25 = priv;
	char *buf = val;
	unsigned int msg_offset = offset;
	size_t bytes_left = count;
	size_t segment;
	int status;

	if (!bounce)
		return -ENOMEM;

	if (unlikely(offset >= at25->chip.byte_len))
		return -EINVAL;
	if ((offset + count) > at25->chip.byte_len)
		count = at25->chip.byte_len - offset;
	if (unlikely(!count))
		return -EINVAL;

	do {
		struct spi_mem_op op;

		segment = min(bytes_left, io_limit);

		op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_READ,
									     msg_offset), 1),
						   SPI_MEM_OP_ADDR(at25->addrlen, msg_offset, 1),
						   SPI_MEM_OP_NO_DUMMY,
						   SPI_MEM_OP_DATA_IN(segment, bounce, 1));

		status = spi_mem_adjust_op_size(at25->spimem, &op);
		if (status)
			return status;
		segment = op.data.nbytes;

		mutex_lock(&at25->lock);
		status = spi_mem_exec_op(at25->spimem, &op);
		mutex_unlock(&at25->lock);

Annotation

Implementation Notes