12e192b24SSimon Glass /* 22e192b24SSimon Glass * (C) Copyright 2000, 2001 32e192b24SSimon Glass * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 42e192b24SSimon Glass * 52e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 62e192b24SSimon Glass */ 72e192b24SSimon Glass 82e192b24SSimon Glass /* 92e192b24SSimon Glass * Support for read and write access to EEPROM like memory devices. This 102e192b24SSimon Glass * includes regular EEPROM as well as FRAM (ferroelectic nonvolaile RAM). 112e192b24SSimon Glass * FRAM devices read and write data at bus speed. In particular, there is no 122e192b24SSimon Glass * write delay. Also, there is no limit imposed on the number of bytes that can 132e192b24SSimon Glass * be transferred with a single read or write. 142e192b24SSimon Glass * 152e192b24SSimon Glass * Use the following configuration options to ensure no unneeded performance 162e192b24SSimon Glass * degradation (typical for EEPROM) is incured for FRAM memory: 172e192b24SSimon Glass * 182e192b24SSimon Glass * #define CONFIG_SYS_I2C_FRAM 192e192b24SSimon Glass * #undef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 202e192b24SSimon Glass * 212e192b24SSimon Glass */ 222e192b24SSimon Glass 232e192b24SSimon Glass #include <common.h> 242e192b24SSimon Glass #include <config.h> 252e192b24SSimon Glass #include <command.h> 262e192b24SSimon Glass #include <i2c.h> 272e192b24SSimon Glass 282e192b24SSimon Glass #ifndef CONFIG_SYS_I2C_SPEED 292e192b24SSimon Glass #define CONFIG_SYS_I2C_SPEED 50000 302e192b24SSimon Glass #endif 312e192b24SSimon Glass 322e192b24SSimon Glass #ifndef CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 332e192b24SSimon Glass #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 0 342e192b24SSimon Glass #endif 352e192b24SSimon Glass 362e192b24SSimon Glass #ifndef CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 372e192b24SSimon Glass #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 8 382e192b24SSimon Glass #endif 392e192b24SSimon Glass 40a6e7b774SMario Six #ifndef I2C_RXTX_LEN 41a6e7b774SMario Six #define I2C_RXTX_LEN 128 42a6e7b774SMario Six #endif 43a6e7b774SMario Six 442e192b24SSimon Glass #define EEPROM_PAGE_SIZE (1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS) 452e192b24SSimon Glass #define EEPROM_PAGE_OFFSET(x) ((x) & (EEPROM_PAGE_SIZE - 1)) 462e192b24SSimon Glass 472e192b24SSimon Glass /* 482e192b24SSimon Glass * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 2 (16-bit EEPROM address) offset is 492e192b24SSimon Glass * 0x000nxxxx for EEPROM address selectors at n, offset xxxx in EEPROM. 502e192b24SSimon Glass * 512e192b24SSimon Glass * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 (8-bit EEPROM page address) offset is 522e192b24SSimon Glass * 0x00000nxx for EEPROM address selectors and page number at n. 532e192b24SSimon Glass */ 542e192b24SSimon Glass #if !defined(CONFIG_SPI) || defined(CONFIG_ENV_EEPROM_IS_ON_I2C) 552e192b24SSimon Glass #if !defined(CONFIG_SYS_I2C_EEPROM_ADDR_LEN) || \ 562e192b24SSimon Glass (CONFIG_SYS_I2C_EEPROM_ADDR_LEN < 1) || \ 572e192b24SSimon Glass (CONFIG_SYS_I2C_EEPROM_ADDR_LEN > 2) 582e192b24SSimon Glass #error CONFIG_SYS_I2C_EEPROM_ADDR_LEN must be 1 or 2 592e192b24SSimon Glass #endif 602e192b24SSimon Glass #endif 612e192b24SSimon Glass 622e192b24SSimon Glass __weak int eeprom_write_enable(unsigned dev_addr, int state) 632e192b24SSimon Glass { 642e192b24SSimon Glass return 0; 652e192b24SSimon Glass } 662e192b24SSimon Glass 672e192b24SSimon Glass void eeprom_init(int bus) 682e192b24SSimon Glass { 692e192b24SSimon Glass /* SPI EEPROM */ 702e192b24SSimon Glass #if defined(CONFIG_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C) 712e192b24SSimon Glass spi_init_f(); 722e192b24SSimon Glass #endif 732e192b24SSimon Glass 742e192b24SSimon Glass /* I2C EEPROM */ 75*2636ac65SNikita Kiryanov #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C) 762e192b24SSimon Glass #if defined(CONFIG_SYS_I2C) 772e192b24SSimon Glass if (bus >= 0) 782e192b24SSimon Glass i2c_set_bus_num(bus); 792e192b24SSimon Glass #endif 802e192b24SSimon Glass i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); 812e192b24SSimon Glass #endif 822e192b24SSimon Glass } 832e192b24SSimon Glass 842e192b24SSimon Glass static int eeprom_addr(unsigned dev_addr, unsigned offset, uchar *addr) 852e192b24SSimon Glass { 862e192b24SSimon Glass unsigned blk_off; 872e192b24SSimon Glass int alen; 882e192b24SSimon Glass 892e192b24SSimon Glass blk_off = offset & 0xff; /* block offset */ 902e192b24SSimon Glass #if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 912e192b24SSimon Glass addr[0] = offset >> 8; /* block number */ 922e192b24SSimon Glass addr[1] = blk_off; /* block offset */ 932e192b24SSimon Glass alen = 2; 942e192b24SSimon Glass #else 952e192b24SSimon Glass addr[0] = offset >> 16; /* block number */ 962e192b24SSimon Glass addr[1] = offset >> 8; /* upper address octet */ 972e192b24SSimon Glass addr[2] = blk_off; /* lower address octet */ 982e192b24SSimon Glass alen = 3; 992e192b24SSimon Glass #endif /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN */ 1002e192b24SSimon Glass 1012e192b24SSimon Glass addr[0] |= dev_addr; /* insert device address */ 1022e192b24SSimon Glass 1032e192b24SSimon Glass return alen; 1042e192b24SSimon Glass } 1052e192b24SSimon Glass 1062e192b24SSimon Glass static int eeprom_len(unsigned offset, unsigned end) 1072e192b24SSimon Glass { 1082e192b24SSimon Glass unsigned len = end - offset; 1092e192b24SSimon Glass 1102e192b24SSimon Glass /* 1112e192b24SSimon Glass * For a FRAM device there is no limit on the number of the 1122e192b24SSimon Glass * bytes that can be ccessed with the single read or write 1132e192b24SSimon Glass * operation. 1142e192b24SSimon Glass */ 1152e192b24SSimon Glass #if !defined(CONFIG_SYS_I2C_FRAM) 1162e192b24SSimon Glass unsigned blk_off = offset & 0xff; 1172e192b24SSimon Glass unsigned maxlen = EEPROM_PAGE_SIZE - EEPROM_PAGE_OFFSET(blk_off); 1182e192b24SSimon Glass 1192e192b24SSimon Glass if (maxlen > I2C_RXTX_LEN) 1202e192b24SSimon Glass maxlen = I2C_RXTX_LEN; 1212e192b24SSimon Glass 1222e192b24SSimon Glass if (len > maxlen) 1232e192b24SSimon Glass len = maxlen; 1242e192b24SSimon Glass #endif 1252e192b24SSimon Glass 1262e192b24SSimon Glass return len; 1272e192b24SSimon Glass } 1282e192b24SSimon Glass 1292e192b24SSimon Glass static int eeprom_rw_block(unsigned offset, uchar *addr, unsigned alen, 1302e192b24SSimon Glass uchar *buffer, unsigned len, bool read) 1312e192b24SSimon Glass { 1322e192b24SSimon Glass int ret = 0; 1332e192b24SSimon Glass 1342e192b24SSimon Glass /* SPI */ 1352e192b24SSimon Glass #if defined(CONFIG_SPI) && !defined(CONFIG_ENV_EEPROM_IS_ON_I2C) 1362e192b24SSimon Glass if (read) 1372e192b24SSimon Glass spi_read(addr, alen, buffer, len); 1382e192b24SSimon Glass else 1392e192b24SSimon Glass spi_write(addr, alen, buffer, len); 1402e192b24SSimon Glass #else /* I2C */ 1412e192b24SSimon Glass 1422e192b24SSimon Glass #if defined(CONFIG_SYS_I2C_EEPROM_BUS) 1432e192b24SSimon Glass i2c_set_bus_num(CONFIG_SYS_I2C_EEPROM_BUS); 1442e192b24SSimon Glass #endif 1452e192b24SSimon Glass 1462e192b24SSimon Glass if (read) 1472e192b24SSimon Glass ret = i2c_read(addr[0], offset, alen - 1, buffer, len); 1482e192b24SSimon Glass else 1492e192b24SSimon Glass ret = i2c_write(addr[0], offset, alen - 1, buffer, len); 1502e192b24SSimon Glass 1512e192b24SSimon Glass if (ret) 1522e192b24SSimon Glass ret = 1; 1532e192b24SSimon Glass #endif 1542e192b24SSimon Glass return ret; 1552e192b24SSimon Glass } 1562e192b24SSimon Glass 1572e192b24SSimon Glass static int eeprom_rw(unsigned dev_addr, unsigned offset, uchar *buffer, 1582e192b24SSimon Glass unsigned cnt, bool read) 1592e192b24SSimon Glass { 1602e192b24SSimon Glass unsigned end = offset + cnt; 1612e192b24SSimon Glass unsigned alen, len; 1622e192b24SSimon Glass int rcode = 0; 1632e192b24SSimon Glass uchar addr[3]; 1642e192b24SSimon Glass 1652e192b24SSimon Glass while (offset < end) { 1662e192b24SSimon Glass alen = eeprom_addr(dev_addr, offset, addr); 1672e192b24SSimon Glass 1682e192b24SSimon Glass len = eeprom_len(offset, end); 1692e192b24SSimon Glass 1702e192b24SSimon Glass rcode = eeprom_rw_block(offset, addr, alen, buffer, len, read); 1712e192b24SSimon Glass 1722e192b24SSimon Glass buffer += len; 1732e192b24SSimon Glass offset += len; 1742e192b24SSimon Glass 1752e192b24SSimon Glass if (!read) 1762e192b24SSimon Glass udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000); 1772e192b24SSimon Glass } 1782e192b24SSimon Glass 1792e192b24SSimon Glass return rcode; 1802e192b24SSimon Glass } 1812e192b24SSimon Glass 1822e192b24SSimon Glass int eeprom_read(unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt) 1832e192b24SSimon Glass { 1842e192b24SSimon Glass /* 1852e192b24SSimon Glass * Read data until done or would cross a page boundary. 1862e192b24SSimon Glass * We must write the address again when changing pages 1872e192b24SSimon Glass * because the next page may be in a different device. 1882e192b24SSimon Glass */ 1892e192b24SSimon Glass return eeprom_rw(dev_addr, offset, buffer, cnt, 1); 1902e192b24SSimon Glass } 1912e192b24SSimon Glass 1922e192b24SSimon Glass int eeprom_write(unsigned dev_addr, unsigned offset, 1932e192b24SSimon Glass uchar *buffer, unsigned cnt) 1942e192b24SSimon Glass { 1952e192b24SSimon Glass int ret; 1962e192b24SSimon Glass 1972e192b24SSimon Glass eeprom_write_enable(dev_addr, 1); 1982e192b24SSimon Glass 1992e192b24SSimon Glass /* 2002e192b24SSimon Glass * Write data until done or would cross a write page boundary. 2012e192b24SSimon Glass * We must write the address again when changing pages 2022e192b24SSimon Glass * because the address counter only increments within a page. 2032e192b24SSimon Glass */ 2042e192b24SSimon Glass ret = eeprom_rw(dev_addr, offset, buffer, cnt, 0); 2052e192b24SSimon Glass 2062e192b24SSimon Glass eeprom_write_enable(dev_addr, 0); 2072e192b24SSimon Glass return ret; 2082e192b24SSimon Glass } 2092e192b24SSimon Glass 2102e192b24SSimon Glass static int do_eeprom(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 2112e192b24SSimon Glass { 2122e192b24SSimon Glass const char *const fmt = 2132e192b24SSimon Glass "\nEEPROM @0x%lX %s: addr %08lx off %04lx count %ld ... "; 2142e192b24SSimon Glass char * const *args = &argv[2]; 2152e192b24SSimon Glass int rcode; 2162e192b24SSimon Glass ulong dev_addr, addr, off, cnt; 2172e192b24SSimon Glass int bus_addr; 2182e192b24SSimon Glass 2192e192b24SSimon Glass switch (argc) { 2202e192b24SSimon Glass #ifdef CONFIG_SYS_DEF_EEPROM_ADDR 2212e192b24SSimon Glass case 5: 2222e192b24SSimon Glass bus_addr = -1; 2232e192b24SSimon Glass dev_addr = CONFIG_SYS_DEF_EEPROM_ADDR; 2242e192b24SSimon Glass break; 2252e192b24SSimon Glass #endif 2262e192b24SSimon Glass case 6: 2272e192b24SSimon Glass bus_addr = -1; 2282e192b24SSimon Glass dev_addr = simple_strtoul(*args++, NULL, 16); 2292e192b24SSimon Glass break; 2302e192b24SSimon Glass case 7: 2312e192b24SSimon Glass bus_addr = simple_strtoul(*args++, NULL, 16); 2322e192b24SSimon Glass dev_addr = simple_strtoul(*args++, NULL, 16); 2332e192b24SSimon Glass break; 2342e192b24SSimon Glass default: 2352e192b24SSimon Glass return CMD_RET_USAGE; 2362e192b24SSimon Glass } 2372e192b24SSimon Glass 2382e192b24SSimon Glass addr = simple_strtoul(*args++, NULL, 16); 2392e192b24SSimon Glass off = simple_strtoul(*args++, NULL, 16); 2402e192b24SSimon Glass cnt = simple_strtoul(*args++, NULL, 16); 2412e192b24SSimon Glass 2422e192b24SSimon Glass eeprom_init(bus_addr); 2432e192b24SSimon Glass 2442e192b24SSimon Glass if (strcmp(argv[1], "read") == 0) { 2452e192b24SSimon Glass printf(fmt, dev_addr, argv[1], addr, off, cnt); 2462e192b24SSimon Glass 2472e192b24SSimon Glass rcode = eeprom_read(dev_addr, off, (uchar *)addr, cnt); 2482e192b24SSimon Glass 2492e192b24SSimon Glass puts("done\n"); 2502e192b24SSimon Glass return rcode; 2512e192b24SSimon Glass } else if (strcmp(argv[1], "write") == 0) { 2522e192b24SSimon Glass printf(fmt, dev_addr, argv[1], addr, off, cnt); 2532e192b24SSimon Glass 2542e192b24SSimon Glass rcode = eeprom_write(dev_addr, off, (uchar *)addr, cnt); 2552e192b24SSimon Glass 2562e192b24SSimon Glass puts("done\n"); 2572e192b24SSimon Glass return rcode; 2582e192b24SSimon Glass } 2592e192b24SSimon Glass 2602e192b24SSimon Glass return CMD_RET_USAGE; 2612e192b24SSimon Glass } 2622e192b24SSimon Glass 2632e192b24SSimon Glass U_BOOT_CMD( 2642e192b24SSimon Glass eeprom, 7, 1, do_eeprom, 2652e192b24SSimon Glass "EEPROM sub-system", 2662e192b24SSimon Glass "read <bus> <devaddr> addr off cnt\n" 2672e192b24SSimon Glass "eeprom write <bus> <devaddr> addr off cnt\n" 2682e192b24SSimon Glass " - read/write `cnt' bytes from `devaddr` EEPROM at offset `off'" 2692e192b24SSimon Glass ) 270