参考文章
- Xilinx Wiki/Linux/Linux Drivers/SPI Zynq driver
- Xilinx Wiki/Linux/Linux Drivers/Linux SPI Driver
- SPI EEPROM
一、AT25M02介绍
AT25M02 提供 2,097,152 位串行电可擦和可编程只读存储器(EEPROM),组织为262,144字,每个8位。 该设备经过了优化,适用于许多低功率和低压操作必不可少的工业和商业应用。 该器件可用于节省空间的8引线SOIC和8球WLCSP封装。 所有的封装工作在1.7V到5.5V或2.5V到5.5V。
• SPI Serial EEPROM 2 Mbits (262,144 x 8)
• Supports SPI Modes 0 (0,0) and 3 (1,1):
– Data sheet describes mode 0 operation
• 5 MHz Clock Rate (5V)
• 256‑Byte Page Mode
• 24‑bit address
二、Vivado工程说明
AT25M02 连接到 PS 端的 SPI_0 上:
三、设备树配置
在 spi 节点中添加 eeprom 节点,根据 芯片手册 和 驱动程序 drivers/misc/eeprom/at25.c 配置设备树如下:
&spi0 {
is-decoded-cs = <0>;
num-cs = <1>;
status = "okay";
eeprom: eeprom-at25m02@0 {
compatible = "atmel,at25";
spi-max-frequency = <5000000>;//5MHz
reg = <0>;
//在 drivers/misc/eeprom/at25.c 和 include/linux/spi/eeprom.h 中定义
at25,addr-mode = <4>;//address bits: 24 bit
at25,page-size = <256>;//Page: 256 Byte
at25,byte-len = <262144>;//262144 bytes(2-Mbit)
};
};
说明:
- 使用现有驱动程序 "atmel,at25"
- SPI EEPROM 最大时钟频率 5MHz
- AT25M02寻址 24 bits ,对应驱动中 mode 为 4
- AT25M02页大小 256 Bytes
- AT25M02总大小 262144 bytes
驱动程序如下:
drivers/misc/eeprom/at25.c
中解析设备树程序如下:
static const struct of_device_id at25_of_match[] = {
{ .compatible = "atmel,at25", },
{ }
};
static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
{
u32 val;
memset(chip, 0, sizeof(*chip));
strncpy(chip->name, "at25", sizeof(chip->name));
if (device_property_read_u32(dev, "size", &val) == 0 ||
device_property_read_u32(dev, "at25,byte-len", &val) == 0) {
chip->byte_len = val;
} else {
dev_err(dev, "Error: missing \"size\" property\n");
return -ENODEV;
}
if (device_property_read_u32(dev, "pagesize", &val) == 0 ||
device_property_read_u32(dev, "at25,page-size", &val) == 0) {
chip->page_size = (u16)val;
} else {
dev_err(dev, "Error: missing \"pagesize\" property\n");
return -ENODEV;
}
if (device_property_read_u32(dev, "at25,addr-mode", &val) == 0) {
chip->flags = (u16)val;
} else {
if (device_property_read_u32(dev, "address-width", &val)) {
dev_err(dev,
"Error: missing \"address-width\" property\n");
return -ENODEV;
}
switch (val) {
case 9:
chip->flags |= EE_INSTR_BIT3_IS_ADDR;
/* fall through */
case 8:
chip->flags |= EE_ADDR1;
break;
case 16:
chip->flags |= EE_ADDR2;
break;
case 24:
chip->flags |= EE_ADDR3;
break;
default:
dev_err(dev,
"Error: bad \"address-width\" property: %u\n",
val);
return -ENODEV;
}
if (device_property_present(dev, "read-only"))
chip->flags |= EE_READONLY;
}
return 0;
}
include/linux/spi/eeprom.h
:
struct spi_eeprom {
u32 byte_len;
char name[10];
u16 page_size; /* for writes */
u16 flags;
#define EE_ADDR1 0x0001 /* 8 bit addrs */
#define EE_ADDR2 0x0002 /* 16 bit addrs */
#define EE_ADDR3 0x0004 /* 24 bit addrs */
#define EE_READONLY 0x0008 /* disallow writes */
/*
* Certain EEPROMS have a size that is larger than the number of address
* bytes would allow (e.g. like M95040 from ST that has 512 Byte size
* but uses only one address byte (A0 to A7) for addressing.) For
* the extra address bit (A8, A16 or A24) bit 3 of the instruction byte
* is used. This instruction bit is normally defined as don't care for
* other AT25 like chips.
*/
#define EE_INSTR_BIT3_IS_ADDR 0x0010
void *context;
};
四、测试程序
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SPI_EEPROM_PATH "/sys/bus/spi/devices/spi1.0/eeprom"
int eeprom_read(const int fd, const uint32_t offset, void *dev_to_buf, const uint32_t size)
{
lseek(fd, offset, SEEK_SET);
return (read(fd, dev_to_buf, size));
}
int eeprom_write(const int fd, const uint32_t offset, const void *buf_to_dev, const uint32_t size)
{
lseek(fd, offset, SEEK_SET);
return (write(fd, buf_to_dev, size));
}
int eeprom_open()
{
int fd = 0;
fd = open(SPI_EEPROM_PATH, O_RDWR);
if(fd < 0) {
printf("%s: SPI EEPROM device open failed\n", __func__);
return -1;
}
return fd;
}
void eeprom_close(const int fd)
{
close(fd);
}
int main()
{
int size;
char sbuf[] = "Hi,this is an eeprom test.";
char rbuf[64];
int len;
int fd;
len = sizeof(sbuf);
fd = eeprom_open();
size = eeprom_write(fd, 0, sbuf, len);
if(size != len) {
printf("EEPROM Write Error\n");
return -1;
} else {
printf("\tWrite EEPROM: %s\n", sbuf);
}
size = eeprom_read(fd, 0, rbuf, len);
if(size != len) {
printf("EEPROM Read Error\n");
return -1;
} else {
printf("\tRead EEPROM: %s\n", rbuf);
}
eeprom_close(fd);
return 0;
}
五、测试
- 查看spi总线下设备:
ls /sys/bus/spi/devices/spi1.0/
- 运行测试程序