I am working on an custom embedded device based on iMX8MP MPU
. I need to read the first 16 bits of the EEPROM connected to address 0x50 on the i2c-0 bus in Linux from user space.
In the first place, I wrote on my eeprom thanks to u-boot
as follow:
u-boot=> i2c mw 0x50 0x00.2 57
u-boot=> i2c mw 0x50 0x01.2 69
u-boot=> i2c mw 0x50 0x02.2 74
u-boot=> i2c mw 0x50 0x03.2 65
u-boot=> i2c mw 0x50 0x04.2 6B
u-boot=> i2c mw 0x50 0x05.2 69
u-boot=> i2c mw 0x50 0x06.2 6F
Then I checked that the value are correctly written in eeprom after a reboot as follow:
u-boot=> i2c md 0x50 0x0.2 B
0000: 57 69 74 65 6b 69 6f 20 53 41 53
I wrote a code that uses ioctls
with I2C_SLAVE_FORCE
and I2C_SMBUS
request to communicate with EEPROM. However, values displayed are not correct and I can not figure out why.
#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#define I2C_ADDRESS 0x50
#define I2C_BUS "/dev/i2c-0"
int main(void)
{
int file;
char filename[20];
int res;
unsigned char data[16];
snprintf(filename, 19, "%s", I2C_BUS);
file = open(filename, O_RDWR);
if (file < 0) {
perror("open");
exit(1);
}
res = ioctl(file, I2C_SLAVE_FORCE, I2C_ADDRESS);
if (res < 0) {
perror("ioctl");
exit(1);
}
struct i2c_smbus_ioctl_data ioctl_data = {
.read_write = I2C_SMBUS_READ,
.command = 0x00, /* read start address */
.size = I2C_SMBUS_WORD_DATA,
.data = data,
};
res = ioctl(file, I2C_SMBUS, &ioctl_data);
if (res < 0) {
perror("ioctl");
exit(1);
}
printf("Data read: ");
for (int i = 0; i < 16; i++) {
printf("%02x ", data[i]);
}
printf("\n");
close(file);
return 0;
}
The output is:
data read : ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
At this point I have no clue why it is not working. Any hint would be appreciated
There is a much easier way to read / write on eeprom. Indeed, If there is a file named "eeprom" for instance under /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0050
it means that there is a kernel driver for this eeprom.
A simple cat to /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0050/uevent
indicate the corresponding driver:
DRIVER=at24
OF_NAME=eeprom
OF_FULLNAME=/soc@0/bus@30800000/i2c@30a20000/eeprom@50
OF_COMPATIBLE_0=atmel,24c32
OF_COMPATIBLE_N=1
MODALIAS=of:NeepromT(null)Catmel,24c32
at24.c is the kernel driver for my eeprom. I can operate read and write operation simply by accessing the eeprom file /sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0050/eeprom
.
You can find below a dummy C program that allow me to read write on eeprom.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define EEPROM_ARGUMENT 2
#define RDWR_ARGUMENT 3
#define MINIMAL_ARGUMENT 4
#define OFFSET_ARGUMENT 4
#define EEPROM_SOM "/sys/devices/platform/soc@0/30800000.bus/30a20000.i2c/i2c-0/0-0050/eeprom"
void read_eeprom(int fd, uint16_t offset, unsigned long read_count) {
char *buff = (char*) malloc(read_count * sizeof(char));
if (pread(fd, buff, read_count, offset) != read_count) {
printf("Error: could not read data from device\n");
close(fd);
exit(-1);
}
printf("Data read: ");
for (int i = 0; i < read_count; i++) {
printf("%02x ", buff[i]);
}
printf("\n");
close(fd);
free(buff);
exit(0);
}
void write_eeprom(int fd, uint16_t offset, const char* write_word) {
if (pwrite(fd, write_word, strlen(write_word),offset) != strlen(write_word)) {
printf("Error: could not write data to device\n");
exit(-1);
}
close(fd);
exit(0);
}
int main(int argc, char* argv[]) {
unsigned long read_count;
const char *mode = argv[1];
const char *write_word = argv[RDWR_ARGUMENT];
const char *eeprom;
uint16_t offset = 0x00;
int fd;
if (argc < MINIMAL_ARGUMENT) goto usage;
if (argv[OFFSET_ARGUMENT])
offset = strtoul(argv[OFFSET_ARGUMENT], NULL, 0);
if (!strcmp(argv[EEPROM_ARGUMENT], "EEPROM_SOM"))
eeprom = EEPROM_SOM;
else goto usage;
if (!strcmp(mode, "read")) {
read_count = strtoul(argv[RDWR_ARGUMENT], NULL, 0);
fd = open(eeprom, O_RDONLY);
if (fd < 0)
goto open_error;
read_eeprom(fd, offset, read_count);
}
else if (!strcmp(mode, "write")) {
fd = open(eeprom, O_WRONLY);
if (fd < 0) goto open_error;
write_eeprom(fd, offset, write_word);
}
else goto usage;
return 0;
usage:
errx(-1, "[read/write] [eeprom] [bytes to read/word to write] optional:[offset]\nSupported eeprom: EEPROM_SOM");
open_error:
printf("Error: could not open device file %s\n", eeprom);
return -1;
}