| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2023-02-25 GuEe-GUI the first version
- */
- #include <rtthread.h>
- #include <rtdevice.h>
- #include <drivers/byteorder.h>
- #define DBG_TAG "mtd.cfi"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include "mtd-cfi.h"
- struct cfi_flash_device
- {
- struct rt_mtd_nor_device parent;
- struct rt_mutex rw_lock;
- rt_ubase_t sect[CFI_FLASH_SECT_MAX];
- rt_ubase_t protect[CFI_FLASH_SECT_MAX];
- rt_size_t sect_count;
- rt_uint8_t portwidth;
- rt_uint8_t chipwidth;
- rt_uint8_t chip_lsb;
- rt_uint8_t cmd_reset;
- rt_uint8_t cmd_erase_sector;
- rt_uint8_t sr_supported;
- rt_uint16_t ext_addr;
- rt_uint16_t version;
- rt_uint16_t offset;
- rt_uint16_t vendor;
- rt_uint16_t device_id;
- rt_uint16_t device_ext_id;
- rt_uint16_t manufacturer_id;
- rt_uint16_t interface;
- rt_ubase_t addr_unlock1;
- rt_ubase_t addr_unlock2;
- rt_ubase_t write_tout;
- rt_ubase_t erase_blk_tout;
- rt_size_t size;
- };
- #define raw_to_cfi_flash_device(raw) rt_container_of(raw, struct cfi_flash_device, parent)
- struct cfi_flash
- {
- int count;
- struct cfi_flash_device dev[];
- };
- #define __get_unaligned_t(type, ptr) \
- ({ \
- const rt_packed(struct { type x; }) *_ptr = (typeof(_ptr))(ptr); \
- _ptr->x; \
- })
- #define __put_unaligned_t(type, val, ptr) \
- do { \
- rt_packed(struct { type x; }) *_ptr = (typeof(_ptr))(ptr); \
- _ptr->x = (val); \
- } while (0)
- #define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))
- #define put_unaligned(val, ptr) __put_unaligned_t(typeof(*(ptr)), (val), (ptr))
- static rt_uint32_t cfi_flash_offset[2] = { FLASH_OFFSET_CFI, FLASH_OFFSET_CFI_ALT };
- rt_inline void *cfi_flash_map(struct cfi_flash_device *fdev, rt_off_t sect, rt_off_t offset)
- {
- rt_off_t byte_offset = offset * fdev->portwidth;
- return (void *)(fdev->sect[sect] + (byte_offset << fdev->chip_lsb));
- }
- rt_inline void cfi_flash_write8(void *addr, rt_uint8_t value)
- {
- HWREG8(addr) = value;
- }
- rt_inline void cfi_flash_write16(void *addr, rt_uint16_t value)
- {
- HWREG16(addr) = value;
- }
- rt_inline void cfi_flash_write32(void *addr, rt_uint32_t value)
- {
- HWREG32(addr) = value;
- }
- rt_inline void cfi_flash_write64(void *addr, rt_uint64_t value)
- {
- HWREG64(addr) = value;
- }
- rt_inline rt_uint8_t cfi_flash_read8(void *addr)
- {
- return HWREG8(addr);
- }
- rt_inline rt_uint16_t cfi_flash_read16(void *addr)
- {
- return HWREG16(addr);
- }
- rt_inline rt_uint32_t cfi_flash_read32(void *addr)
- {
- return HWREG32(addr);
- }
- rt_inline rt_uint64_t cfi_flash_read64(void *addr)
- {
- return HWREG64(addr);
- }
- rt_inline rt_uint8_t cfi_flash_read_byte(struct cfi_flash_device *fdev, rt_off_t offset)
- {
- unsigned char *cp, value;
- cp = cfi_flash_map(fdev, 0, offset);
- #ifndef ARCH_CPU_BIG_ENDIAN
- value = cfi_flash_read8(cp);
- #else
- value = cfi_flash_read8(cp + fdev->portwidth - 1);
- #endif
- return value;
- }
- rt_inline rt_uint16_t cfi_flash_read_word(struct cfi_flash_device *fdev, rt_off_t offset)
- {
- rt_uint16_t *addr, value;
- addr = cfi_flash_map(fdev, 0, offset);
- value = cfi_flash_read16(addr);
- return value;
- }
- static void cfi_flash_make_cmd(struct cfi_flash_device *fdev, rt_uint32_t cmd, void *cmdbuf)
- {
- int cword_offset, cp_offset;
- rt_uint8_t val, *cp = (rt_uint8_t *)cmdbuf;
- #ifndef ARCH_CPU_BIG_ENDIAN
- cmd = rt_cpu_to_le32(cmd);
- #endif
- for (int i = fdev->portwidth; i > 0; --i)
- {
- cword_offset = (fdev->portwidth - i) % fdev->chipwidth;
- #ifndef ARCH_CPU_BIG_ENDIAN
- cp_offset = fdev->portwidth - i;
- val = *((rt_uint8_t *)&cmd + cword_offset);
- #else
- cp_offset = i - 1;
- val = *((rt_uint8_t *)&cmd + sizeof(rt_uint32_t) - cword_offset - 1);
- #endif
- cp[cp_offset] = (cword_offset >= sizeof(rt_uint32_t)) ? 0x00 : val;
- }
- }
- static void cfi_flash_write_cmd(struct cfi_flash_device *fdev,
- rt_off_t sect, rt_off_t offset, rt_uint32_t cmd)
- {
- void *addr;
- union cfi_word word;
- addr = cfi_flash_map(fdev, sect, offset);
- cfi_flash_make_cmd(fdev, cmd, &word);
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- cfi_flash_write8(addr, word.w8);
- break;
- case FLASH_CFI_16BIT:
- cfi_flash_write16(addr, word.w16);
- break;
- case FLASH_CFI_32BIT:
- cfi_flash_write32(addr, word.w32);
- break;
- case FLASH_CFI_64BIT:
- cfi_flash_write64(addr, word.w64);
- break;
- }
- rt_hw_isb();
- }
- static void cfi_flash_unlock_seq(struct cfi_flash_device *fdev, rt_off_t sect)
- {
- cfi_flash_write_cmd(fdev, sect, fdev->addr_unlock1, AMD_CMD_UNLOCK_START);
- cfi_flash_write_cmd(fdev, sect, fdev->addr_unlock2, AMD_CMD_UNLOCK_ACK);
- }
- static rt_off_t cfi_flash_find(struct cfi_flash_device *fdev, rt_ubase_t addr)
- {
- rt_off_t sect = 0;
- while (sect < fdev->sect_count - 1 && fdev->sect[sect] < addr)
- {
- sect++;
- }
- while (fdev->sect[sect] > addr && sect > 0)
- {
- sect--;
- }
- return sect;
- }
- static rt_bool_t cfi_flash_isequal(struct cfi_flash_device *fdev,
- rt_off_t sect, rt_off_t offset, rt_uint32_t cmd)
- {
- void *addr;
- union cfi_word word;
- addr = cfi_flash_map(fdev, sect, offset);
- cfi_flash_make_cmd(fdev, cmd, &word);
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- return cfi_flash_read8(addr) == word.w8;
- case FLASH_CFI_16BIT:
- return cfi_flash_read16(addr) == word.w16;
- case FLASH_CFI_32BIT:
- return cfi_flash_read32(addr) == word.w32;
- case FLASH_CFI_64BIT:
- return cfi_flash_read64(addr) == word.w64;
- default:
- break;
- }
- return RT_FALSE;
- }
- static void cfi_flash_add_byte(struct cfi_flash_device *fdev,
- union cfi_word *word, rt_uint8_t c)
- {
- #ifndef ARCH_CPU_BIG_ENDIAN
- rt_uint16_t w;
- rt_uint32_t l;
- rt_uint64_t ll;
- #endif
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- word->w8 = c;
- break;
- case FLASH_CFI_16BIT:
- #ifndef ARCH_CPU_BIG_ENDIAN
- w = c;
- w <<= 8;
- word->w16 = (word->w16 >> 8) | w;
- #else
- word->w16 = (word->w16 << 8) | c;
- #endif
- break;
- case FLASH_CFI_32BIT:
- #ifndef ARCH_CPU_BIG_ENDIAN
- l = c;
- l <<= 24;
- word->w32 = (word->w32 >> 8) | l;
- #else
- word->w32 = (word->w32 << 8) | c;
- #endif
- break;
- case FLASH_CFI_64BIT:
- #ifndef ARCH_CPU_BIG_ENDIAN
- ll = c;
- ll <<= 56;
- word->w64 = (word->w64 >> 8) | ll;
- #else
- word->w64 = (word->w64 << 8) | c;
- #endif
- break;
- }
- }
- static rt_bool_t cfi_flash_isset(struct cfi_flash_device *fdev,
- rt_off_t sect, rt_off_t offset, rt_uint8_t cmd)
- {
- rt_bool_t res;
- rt_uint8_t *addr;
- union cfi_word word;
- addr = cfi_flash_map(fdev, sect, offset);
- cfi_flash_make_cmd(fdev, cmd, &word);
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- res = (cfi_flash_read8(addr) & word.w8) == word.w8;
- break;
- case FLASH_CFI_16BIT:
- res = (cfi_flash_read16(addr) & word.w16) == word.w16;
- break;
- case FLASH_CFI_32BIT:
- res = (cfi_flash_read32(addr) & word.w32) == word.w32;
- break;
- case FLASH_CFI_64BIT:
- res = (cfi_flash_read64(addr) & word.w64) == word.w64;
- break;
- default:
- res = RT_FALSE;
- break;
- }
- return res;
- }
- static rt_bool_t cfi_flash_toggle(struct cfi_flash_device *fdev,
- rt_off_t sect, rt_off_t offset, rt_uint8_t cmd)
- {
- rt_bool_t res;
- rt_uint8_t *addr;
- union cfi_word word;
- addr = cfi_flash_map(fdev, sect, offset);
- cfi_flash_make_cmd(fdev, cmd, &word);
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- res = cfi_flash_read8(addr) != cfi_flash_read8(addr);
- break;
- case FLASH_CFI_16BIT:
- res = cfi_flash_read16(addr) != cfi_flash_read16(addr);
- break;
- case FLASH_CFI_32BIT:
- res = cfi_flash_read32(addr) != cfi_flash_read32(addr);
- break;
- case FLASH_CFI_64BIT:
- res = cfi_flash_read32(addr) != cfi_flash_read32(addr) ||
- cfi_flash_read32(addr + 4) != cfi_flash_read32(addr + 4);
- break;
- default:
- res = RT_FALSE;
- break;
- }
- return res;
- }
- static rt_bool_t cfi_flash_is_busy(struct cfi_flash_device *fdev, rt_off_t sect)
- {
- switch (fdev->vendor)
- {
- case CFI_CMDSET_INTEL_PROG_REGIONS:
- case CFI_CMDSET_INTEL_STANDARD:
- case CFI_CMDSET_INTEL_EXTENDED:
- return !cfi_flash_isset(fdev, sect, 0, FLASH_STATUS_DONE);
- case CFI_CMDSET_AMD_STANDARD:
- case CFI_CMDSET_AMD_EXTENDED:
- if (fdev->sr_supported)
- {
- cfi_flash_write_cmd(fdev, sect, fdev->addr_unlock1, FLASH_CMD_READ_STATUS);
- return !cfi_flash_isset(fdev, sect, 0, FLASH_STATUS_DONE);
- }
- else
- {
- return cfi_flash_toggle(fdev, sect, 0, AMD_STATUS_TOGGLE);
- }
- default:
- break;
- }
- return RT_FALSE;
- }
- static rt_err_t cfi_flash_status_check(struct cfi_flash_device *fdev,
- rt_off_t sect, rt_ubase_t tout)
- {
- rt_tick_t tick;
- tick = rt_tick_from_millisecond(rt_max_t(rt_ubase_t, tout / 1000, 1));
- tick += rt_tick_get();
- while (cfi_flash_is_busy(fdev, sect))
- {
- if (rt_tick_get() > tick)
- {
- cfi_flash_write_cmd(fdev, sect, 0, fdev->cmd_reset);
- rt_hw_us_delay(1);
- return -RT_ETIMEOUT;
- }
- rt_hw_us_delay(1);
- }
- return RT_EOK;
- }
- static rt_err_t cfi_flash_full_status_check(struct cfi_flash_device *fdev,
- rt_off_t sect, rt_ubase_t tout)
- {
- rt_err_t err;
- err = cfi_flash_status_check(fdev, sect, tout);
- switch (fdev->vendor)
- {
- case CFI_CMDSET_INTEL_PROG_REGIONS:
- case CFI_CMDSET_INTEL_EXTENDED:
- case CFI_CMDSET_INTEL_STANDARD:
- cfi_flash_write_cmd(fdev, sect, 0, fdev->cmd_reset);
- rt_hw_us_delay(1);
- break;
- default:
- break;
- }
- return err;
- }
- static rt_err_t cfi_flash_write_word(struct cfi_flash_device *fdev,
- rt_ubase_t dest, union cfi_word word)
- {
- int flag;
- void *dstaddr;
- rt_off_t sect = 0;
- rt_bool_t sect_found = RT_FALSE;
- dstaddr = (void *)dest;
- /* Check if Flash is (sufficiently) erased */
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- flag = (cfi_flash_read8(dstaddr) & word.w8) == word.w8;
- break;
- case FLASH_CFI_16BIT:
- flag = (cfi_flash_read16(dstaddr) & word.w16) == word.w16;
- break;
- case FLASH_CFI_32BIT:
- flag = (cfi_flash_read32(dstaddr) & word.w32) == word.w32;
- break;
- case FLASH_CFI_64BIT:
- flag = (cfi_flash_read64(dstaddr) & word.w64) == word.w64;
- break;
- default:
- flag = 0;
- break;
- }
- if (!flag)
- {
- return -RT_EIO;
- }
- switch (fdev->vendor)
- {
- case CFI_CMDSET_INTEL_PROG_REGIONS:
- case CFI_CMDSET_INTEL_EXTENDED:
- case CFI_CMDSET_INTEL_STANDARD:
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_CLEAR_STATUS);
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_WRITE);
- break;
- case CFI_CMDSET_AMD_EXTENDED:
- case CFI_CMDSET_AMD_STANDARD:
- sect = cfi_flash_find(fdev, dest);
- cfi_flash_unlock_seq(fdev, sect);
- cfi_flash_write_cmd(fdev, sect, fdev->addr_unlock1, AMD_CMD_WRITE);
- sect_found = RT_TRUE;
- break;
- }
- switch (fdev->portwidth)
- {
- case FLASH_CFI_8BIT:
- cfi_flash_write8(dstaddr, word.w8);
- break;
- case FLASH_CFI_16BIT:
- cfi_flash_write16(dstaddr, word.w16);
- break;
- case FLASH_CFI_32BIT:
- cfi_flash_write32(dstaddr, word.w32);
- break;
- case FLASH_CFI_64BIT:
- cfi_flash_write64(dstaddr, word.w64);
- break;
- }
- if (!sect_found)
- {
- sect = cfi_flash_find(fdev, dest);
- }
- return cfi_flash_full_status_check(fdev, sect, fdev->write_tout);
- }
- static rt_err_t cfi_flash_read_id(struct rt_mtd_nor_device *dev)
- {
- struct cfi_flash_device *fdev = raw_to_cfi_flash_device(dev);
- return fdev->device_id;
- }
- static rt_ssize_t cfi_flash_read(struct rt_mtd_nor_device *dev, rt_off_t offset, rt_uint8_t *data, rt_size_t length)
- {
- struct cfi_flash_device *fdev = raw_to_cfi_flash_device(dev);
- rt_mutex_take(&fdev->rw_lock, RT_WAITING_FOREVER);
- rt_memcpy(data, (void *)fdev->sect[0] + offset, length);
- rt_mutex_release(&fdev->rw_lock);
- return length;
- }
- static rt_ssize_t cfi_flash_write(struct rt_mtd_nor_device *dev, rt_off_t offset,
- const rt_uint8_t *data, rt_size_t length)
- {
- int i;
- rt_ubase_t wp;
- rt_uint8_t *ptr;
- rt_ssize_t res;
- rt_size_t size, written = 0, tail_written = 0;
- union cfi_word word;
- struct cfi_flash_device *fdev = raw_to_cfi_flash_device(dev);
- rt_mutex_take(&fdev->rw_lock, RT_WAITING_FOREVER);
- offset += fdev->sect[0];
- wp = offset & ~(fdev->portwidth - 1);
- size = offset - wp;
- if (size)
- {
- rt_size_t head_written = 0;
- word.w32 = 0;
- ptr = (rt_uint8_t *)wp;
- for (i = 0; i < size; ++i)
- {
- cfi_flash_add_byte(fdev, &word, cfi_flash_read8(ptr + i));
- }
- for (; i < fdev->portwidth && length > 0; ++i)
- {
- cfi_flash_add_byte(fdev, &word, *data++);
- --length;
- ++head_written;
- }
- for (; !length && i < fdev->portwidth; ++i)
- {
- cfi_flash_add_byte(fdev, &word, cfi_flash_read8(ptr + i));
- }
- if ((res = cfi_flash_write_word(fdev, wp, word)))
- {
- goto _out_lock;
- }
- written += head_written;
- wp += i;
- }
- /* Handle the aligned part */
- while (length >= fdev->portwidth)
- {
- word.w32 = 0;
- for (i = 0; i < fdev->portwidth; ++i)
- {
- cfi_flash_add_byte(fdev, &word, *data++);
- }
- if ((res = cfi_flash_write_word(fdev, wp, word)))
- {
- goto _out_lock;
- }
- wp += fdev->portwidth;
- length -= fdev->portwidth;
- written += fdev->portwidth; /* new: accumulate bytes */
- }
- if (!length)
- {
- res = written;
- goto _out_lock;
- }
- /* Handle unaligned tail bytes */
- word.w32 = 0;
- ptr = (rt_uint8_t *)wp;
- for (i = 0; i < fdev->portwidth && length > 0; ++i)
- {
- cfi_flash_add_byte(fdev, &word, *data++);
- --length;
- ++tail_written;
- }
- for (; i < fdev->portwidth; ++i)
- {
- cfi_flash_add_byte(fdev, &word, cfi_flash_read8(ptr + i));
- }
- if ((res = cfi_flash_write_word(fdev, wp, word)))
- {
- goto _out_lock;
- }
- written += tail_written;
- res = written;
- _out_lock:
- rt_mutex_release(&fdev->rw_lock);
- return res;
- }
- static rt_err_t cfi_flash_erase_block(struct rt_mtd_nor_device *dev,
- rt_off_t offset, rt_size_t length)
- {
- rt_err_t err = RT_EOK;
- rt_ubase_t sect, sect_end;
- struct cfi_flash_device *fdev = raw_to_cfi_flash_device(dev);
- rt_mutex_take(&fdev->rw_lock, RT_WAITING_FOREVER);
- sect = cfi_flash_find(fdev, fdev->sect[0] + offset);
- sect_end = cfi_flash_find(fdev, fdev->sect[0] + offset + length);
- for (; sect <= sect_end; ++sect)
- {
- if (fdev->protect[sect])
- {
- continue;
- }
- switch (fdev->vendor)
- {
- case CFI_CMDSET_INTEL_PROG_REGIONS:
- case CFI_CMDSET_INTEL_STANDARD:
- case CFI_CMDSET_INTEL_EXTENDED:
- cfi_flash_write_cmd(fdev, sect, 0, FLASH_CMD_CLEAR_STATUS);
- cfi_flash_write_cmd(fdev, sect, 0, FLASH_CMD_BLOCK_ERASE);
- cfi_flash_write_cmd(fdev, sect, 0, FLASH_CMD_ERASE_CONFIRM);
- break;
- case CFI_CMDSET_AMD_STANDARD:
- case CFI_CMDSET_AMD_EXTENDED:
- cfi_flash_unlock_seq(fdev, sect);
- cfi_flash_write_cmd(fdev, sect, fdev->addr_unlock1, AMD_CMD_ERASE_START);
- cfi_flash_unlock_seq(fdev, sect);
- cfi_flash_write_cmd(fdev, sect, 0, fdev->cmd_erase_sector);
- break;
- default:
- break;
- }
- err = cfi_flash_full_status_check(fdev, sect, fdev->erase_blk_tout);
- }
- rt_mutex_release(&fdev->rw_lock);
- return err;
- }
- const static struct rt_mtd_nor_driver_ops cfi_flash_ops =
- {
- .read_id = cfi_flash_read_id,
- .read = cfi_flash_read,
- .write = cfi_flash_write,
- .erase_block = cfi_flash_erase_block,
- };
- static rt_bool_t cfi_flash_detect_info(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- rt_uint8_t *buffer;
- /* Reset, unknown reset's cmd now, so try Intel and AMD both. */
- cfi_flash_write_cmd(fdev, 0, 0, AMD_CMD_RESET);
- rt_hw_us_delay(1);
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_RESET);
- for (int i = 0; i < RT_ARRAY_SIZE(cfi_flash_offset); ++i)
- {
- cfi_flash_write_cmd(fdev, 0, cfi_flash_offset[i], FLASH_CMD_CFI);
- if (cfi_flash_isequal(fdev, 0, FLASH_OFFSET_CFI_RESP, 'Q') &&
- cfi_flash_isequal(fdev, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') &&
- cfi_flash_isequal(fdev, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y'))
- {
- buffer = (void *)query;
- for (int byte = 0; byte < sizeof(*query); ++byte)
- {
- buffer[byte] = cfi_flash_read_byte(fdev, FLASH_OFFSET_CFI_RESP + byte);
- }
- fdev->interface = rt_le16_to_cpu(query->interface_desc);
- /*
- * Some flash chips can support multiple bus widths.
- * In this case, override the interface width and
- * limit it to the port width.
- */
- if (fdev->interface == FLASH_CFI_X8X16 &&
- fdev->portwidth == FLASH_CFI_8BIT)
- {
- fdev->interface = FLASH_CFI_X8;
- }
- else if (fdev->interface == FLASH_CFI_X16X32 &&
- fdev->portwidth == FLASH_CFI_16BIT)
- {
- fdev->interface = FLASH_CFI_X16;
- }
- fdev->offset = cfi_flash_offset[i];
- fdev->addr_unlock1 = 0x555;
- fdev->addr_unlock2 = 0x2aa;
- /* modify the unlock address if we are in compatibility mode */
- if ((fdev->chipwidth == FLASH_CFI_BY8 && /* x8/x16 in x8 mode */
- fdev->interface == FLASH_CFI_X8X16) ||
- (fdev->chipwidth == FLASH_CFI_BY16 && /* x16/x32 in x16 mode */
- fdev->interface == FLASH_CFI_X16X32))
- {
- fdev->addr_unlock1 = 0xaaa;
- fdev->addr_unlock2 = 0x555;
- }
- return RT_TRUE;
- }
- }
- return RT_FALSE;
- }
- static rt_bool_t cfi_flash_detect(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- for (fdev->portwidth = FLASH_CFI_8BIT;
- fdev->portwidth <= FLASH_CFI_64BIT;
- fdev->portwidth <<= 1)
- {
- for (fdev->chipwidth = FLASH_CFI_BY8;
- fdev->chipwidth <= fdev->portwidth;
- fdev->chipwidth <<= 1)
- {
- /*
- * First, try detection without shifting the addresses
- * for 8bit devices (16bit wide connection)
- */
- fdev->chip_lsb = 0;
- if (cfi_flash_detect_info(fdev, query))
- {
- return RT_TRUE;
- }
- /* Not detected, so let's try with shifting for 8bit devices */
- fdev->chip_lsb = 1;
- if (cfi_flash_detect_info(fdev, query))
- {
- return RT_TRUE;
- }
- }
- }
- return RT_FALSE;
- }
- static void cfi_cmdset_intel_init(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- fdev->cmd_reset = FLASH_CMD_RESET;
- /* Intel read jedec IDs */
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_RESET);
- rt_hw_us_delay(1);
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_READ_ID);
- rt_hw_us_delay(1000);
- fdev->manufacturer_id = cfi_flash_read_byte(fdev, FLASH_OFFSET_MANUFACTURER_ID);
- fdev->device_id = (fdev->chipwidth == FLASH_CFI_16BIT) ?
- cfi_flash_read_word(fdev, FLASH_OFFSET_DEVICE_ID) :
- cfi_flash_read_byte(fdev, FLASH_OFFSET_DEVICE_ID);
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_RESET);
- /* Read end */
- cfi_flash_write_cmd(fdev, 0, fdev->offset, FLASH_CMD_CFI);
- }
- static void cfi_cmdset_amd_init(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- rt_uint16_t bank_id = 0;
- rt_uint8_t manu_id, feature;
- fdev->cmd_reset = AMD_CMD_RESET;
- fdev->cmd_erase_sector = AMD_CMD_ERASE_SECTOR;
- /* AMD read jedec IDs */
- cfi_flash_write_cmd(fdev, 0, 0, AMD_CMD_RESET);
- cfi_flash_unlock_seq(fdev, 0);
- cfi_flash_write_cmd(fdev, 0, fdev->addr_unlock1, FLASH_CMD_READ_ID);
- rt_hw_us_delay(1000);
- manu_id = cfi_flash_read_byte(fdev, FLASH_OFFSET_MANUFACTURER_ID);
- /* JEDEC JEP106Z specifies ID codes up to bank 7 */
- while (manu_id == FLASH_CONTINUATION_CODE && bank_id < 0x800)
- {
- bank_id += 0x100;
- manu_id = cfi_flash_read_byte(fdev, bank_id | FLASH_OFFSET_MANUFACTURER_ID);
- }
- fdev->manufacturer_id = manu_id;
- if (fdev->ext_addr && fdev->version >= 0x3134)
- {
- /* Read software feature (at 0x53) */
- feature = cfi_flash_read_byte(fdev, fdev->ext_addr + 0x13);
- fdev->sr_supported = feature & 0x1;
- }
- switch (fdev->chipwidth)
- {
- case FLASH_CFI_8BIT:
- fdev->device_id = cfi_flash_read_byte(fdev, FLASH_OFFSET_DEVICE_ID);
- if (fdev->device_id == 0x7e)
- {
- /* AMD 3-byte (expanded) device ids */
- fdev->device_ext_id = cfi_flash_read_byte(fdev, FLASH_OFFSET_DEVICE_ID2);
- fdev->device_ext_id <<= 8;
- fdev->device_ext_id |= cfi_flash_read_byte(fdev, FLASH_OFFSET_DEVICE_ID3);
- }
- break;
- case FLASH_CFI_16BIT:
- fdev->device_id = cfi_flash_read_word(fdev, FLASH_OFFSET_DEVICE_ID);
- if ((fdev->device_id & 0xff) == 0x7e)
- {
- /* AMD 3-byte (expanded) device ids */
- fdev->device_ext_id = cfi_flash_read_byte(fdev, FLASH_OFFSET_DEVICE_ID2);
- fdev->device_ext_id <<= 8;
- fdev->device_ext_id |= cfi_flash_read_byte(fdev, FLASH_OFFSET_DEVICE_ID3);
- }
- break;
- default:
- break;
- }
- cfi_flash_write_cmd(fdev, 0, 0, AMD_CMD_RESET);
- rt_hw_us_delay(1);
- /* Read end */
- cfi_flash_write_cmd(fdev, 0, fdev->offset, FLASH_CMD_CFI);
- }
- static void cfi_reverse_geometry(struct cfi_query *query)
- {
- rt_uint32_t info;
- for (int i = 0, j = query->num_erase_regions - 1; i < j; ++i, --j)
- {
- info = get_unaligned(&query->erase_region_info[i]);
- put_unaligned(get_unaligned(&query->erase_region_info[j]),
- &query->erase_region_info[i]);
- put_unaligned(info, &query->erase_region_info[j]);
- }
- }
- static void cfi_flash_fixup_amd(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- /* check if flash geometry needs reversal */
- if (query->num_erase_regions > 1)
- {
- /* reverse geometry if top boot part */
- if (fdev->version < 0x3131)
- {
- /* CFI < 1.1, try to guess from device id */
- if ((fdev->device_id & 0x80) != 0)
- {
- cfi_reverse_geometry(query);
- }
- }
- else if (cfi_flash_read_byte(fdev, fdev->ext_addr + 0xf) == 3)
- {
- /*
- * CFI >= 1.1, deduct from top/bottom flag,
- * ext_addr is valid since cfi version > 0.
- */
- cfi_reverse_geometry(query);
- }
- }
- }
- static void cfi_flash_fixup_atmel(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- int reverse_geometry = 0;
- /* Check the "top boot" bit in the PRI */
- if (fdev->ext_addr && !(cfi_flash_read_byte(fdev, fdev->ext_addr + 6) & 1))
- {
- reverse_geometry = 1;
- }
- /*
- * AT49BV6416(T) list the erase regions in the wrong order.
- * However, the device ID is identical with the non-broken
- * AT49BV642D they differ in the high byte.
- */
- if (fdev->device_id == 0xd6 || fdev->device_id == 0xd2)
- {
- reverse_geometry = !reverse_geometry;
- }
- if (reverse_geometry)
- {
- cfi_reverse_geometry(query);
- }
- }
- static void cfi_flash_fixup_stm(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- /* Check if flash geometry needs reversal */
- if (query->num_erase_regions > 1)
- {
- /* Reverse geometry if top boot part */
- if (fdev->version < 0x3131)
- {
- /*
- * CFI < 1.1, guess by device id:
- * M29W320DT, M29W320ET, M29W800DT
- */
- if (fdev->device_id == 0x22ca ||
- fdev->device_id == 0x2256 ||
- fdev->device_id == 0x22d7)
- {
- cfi_reverse_geometry(query);
- }
- }
- else if (cfi_flash_read_byte(fdev, fdev->ext_addr + 0xf) == 3)
- {
- /*
- * CFI >= 1.1, deduct from top/bottom flag,
- * ext_addr is valid since cfi version > 0.
- */
- cfi_reverse_geometry(query);
- }
- }
- }
- static void cfi_flash_fixup_sst(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- /*
- * SST, for many recent nor parallel flashes, says they are
- * CFI-conformant. This is not true, since qry struct.
- * reports a std. AMD command set (0x0002), while SST allows to
- * erase two different sector sizes for the same memory.
- * 64KB sector (SST call it block) needs 0x30 to be erased.
- * 4KB sector (SST call it sector) needs 0x50 to be erased.
- * Since CFI query detect the 4KB number of sectors, users expects
- * a sector granularity of 4KB, and it is here set.
- */
- /* SST39VF3201B, SST39VF3202B */
- if (fdev->device_id == 0x5d23 || fdev->device_id == 0x5c23)
- {
- /* Set sector granularity to 4KB */
- fdev->cmd_erase_sector = 0x50;
- }
- }
- static void cfi_flash_fixup_num(struct cfi_flash_device *fdev, struct cfi_query *query)
- {
- /*
- * The M29EW devices seem to report the CFI information wrong
- * when it's in 8 bit mode.
- * There's an app note from Numonyx on this issue.
- * So adjust the buffer size for M29EW while operating in 8-bit mode
- */
- if (query->max_buf_write_size > 0x8 && fdev->device_id == 0x7e &&
- (fdev->device_ext_id == 0x2201 || fdev->device_ext_id == 0x2301 ||
- fdev->device_ext_id == 0x2801 || fdev->device_ext_id == 0x4801))
- {
- query->max_buf_write_size = 0x8;
- }
- }
- static rt_err_t cfi_flash_dev_probe(struct rt_device *dev, struct cfi_flash_device *fdev, int index)
- {
- rt_err_t err;
- int size_ratio;
- const char *name;
- rt_uint64_t addr, size;
- rt_ubase_t sect, value;
- rt_size_t max_size, sect_count;
- int num_erase_regions, erase_region_count, erase_region_size;
- struct cfi_query *query = rt_malloc(sizeof(*query));
- if (!query)
- {
- return -RT_ENOMEM;
- }
- if (rt_dm_dev_get_address(dev, index, &addr, &size) < 0)
- {
- err = -RT_EIO;
- goto _fail;
- }
- fdev->sect[0] = (rt_ubase_t)rt_ioremap((void *)addr, size);
- if (!cfi_flash_detect(fdev, query))
- {
- err = -RT_EEMPTY;
- goto _fail;
- }
- fdev->vendor = rt_le16_to_cpu(get_unaligned(&query->primary_id));
- fdev->ext_addr = rt_le16_to_cpu(get_unaligned(&query->primary_address));
- num_erase_regions = query->num_erase_regions;
- if (fdev->ext_addr)
- {
- fdev->version = cfi_flash_read_byte(fdev, fdev->ext_addr + 3) << 8;
- fdev->version |= cfi_flash_read_byte(fdev, fdev->ext_addr + 4);
- }
- switch (fdev->vendor)
- {
- case CFI_CMDSET_INTEL_PROG_REGIONS:
- case CFI_CMDSET_INTEL_STANDARD:
- case CFI_CMDSET_INTEL_EXTENDED:
- cfi_cmdset_intel_init(fdev, query);
- break;
- case CFI_CMDSET_AMD_STANDARD:
- case CFI_CMDSET_AMD_EXTENDED:
- cfi_cmdset_amd_init(fdev, query);
- break;
- default:
- /* Try an Intel-style reset*/
- cfi_flash_write_cmd(fdev, 0, 0, FLASH_CMD_RESET);
- break;
- }
- switch (fdev->manufacturer_id)
- {
- case 0x0001: /* AMD */
- case 0x0037: /* AMIC */
- cfi_flash_fixup_amd(fdev, query);
- break;
- case 0x001f:
- cfi_flash_fixup_atmel(fdev, query);
- break;
- case 0x0020:
- cfi_flash_fixup_stm(fdev, query);
- break;
- case 0x00bf: /* SST */
- cfi_flash_fixup_sst(fdev, query);
- break;
- case 0x0089: /* Numonyx */
- cfi_flash_fixup_num(fdev, query);
- break;
- }
- size_ratio = fdev->portwidth / fdev->chipwidth;
- if (fdev->interface == FLASH_CFI_X8X16 && fdev->chipwidth == FLASH_CFI_BY8)
- {
- size_ratio >>= 1;
- }
- fdev->size = 1 << query->dev_size;
- /* Multiply the size by the number of chips */
- fdev->size *= size_ratio;
- max_size = size;
- if (max_size && fdev->size > max_size)
- {
- fdev->size = max_size;
- }
- sect_count = 0;
- sect = fdev->sect[0];
- for (int i = 0; i < num_erase_regions; ++i)
- {
- if (i > RT_ARRAY_SIZE(query->erase_region_info))
- {
- LOG_E("Too many %d (> %d) erase regions found",
- num_erase_regions, RT_ARRAY_SIZE(query->erase_region_info));
- break;
- }
- value = rt_le32_to_cpu(get_unaligned(&query->erase_region_info[i]));
- erase_region_count = (value & 0xffff) + 1;
- value >>= 16;
- erase_region_size = (value & 0xffff) ? ((value & 0xffff) * 256) : 128;
- for (int j = 0; j < erase_region_count; ++j)
- {
- if (sect - fdev->sect[0] >= fdev->size)
- {
- break;
- }
- if (sect_count > RT_ARRAY_SIZE(fdev->sect))
- {
- LOG_E("Too many %d (> %d) sector found",
- sect_count, RT_ARRAY_SIZE(fdev->sect));
- break;
- }
- fdev->sect[sect_count] = sect;
- sect += (erase_region_size * size_ratio);
- switch (fdev->vendor)
- {
- case CFI_CMDSET_INTEL_PROG_REGIONS:
- case CFI_CMDSET_INTEL_EXTENDED:
- case CFI_CMDSET_INTEL_STANDARD:
- /*
- * Set flash to read-id mode. Otherwise
- * reading protected status is not guaranteed.
- */
- cfi_flash_write_cmd(fdev, sect_count, 0, FLASH_CMD_READ_ID);
- fdev->protect[sect_count] = cfi_flash_isset(fdev,
- sect_count, FLASH_OFFSET_PROTECT, FLASH_STATUS_PROTECT);
- cfi_flash_write_cmd(fdev, sect_count, 0, FLASH_CMD_RESET);
- break;
- case CFI_CMDSET_AMD_EXTENDED:
- case CFI_CMDSET_AMD_STANDARD:
- default:
- /* Default: not protected */
- fdev->protect[sect_count] = RT_NULL;
- }
- ++sect_count;
- }
- fdev->sect_count = sect_count;
- }
- if (fdev->interface == FLASH_CFI_X8X16 && fdev->chipwidth == FLASH_CFI_BY8)
- {
- /* Need to test on x8/x16 in parallel. */
- fdev->portwidth >>= 1;
- }
- value = 1 << query->block_erase_timeout_type;
- fdev->erase_blk_tout = value * (1 << query->block_erase_timeout_max);
- value = (1 << query->word_write_timeout_type) * (1 << query->word_write_timeout_max);
- /* Round up when converting to ms */
- fdev->write_tout = (value + 999) / 1000;
- cfi_flash_write_cmd(fdev, 0, 0, fdev->cmd_reset);
- fdev->parent.ops = &cfi_flash_ops;
- fdev->parent.block_start = 0;
- fdev->parent.block_end = fdev->sect_count;
- fdev->parent.block_size = size / fdev->sect_count;
- if ((err = rt_dm_dev_set_name_auto(&fdev->parent.parent, "nor")) < 0)
- {
- goto _fail;
- }
- name = rt_dm_dev_get_name(&fdev->parent.parent);
- if ((err = rt_mutex_init(&fdev->rw_lock, name, RT_IPC_FLAG_PRIO)))
- {
- goto _fail;
- }
- if ((err = rt_mtd_nor_register_device(name, &fdev->parent)))
- {
- goto _fail;
- }
- rt_free(query);
- return RT_EOK;
- _fail:
- if (fdev->sect[0])
- {
- rt_iounmap((void *)fdev->sect[0]);
- }
- rt_free(query);
- return err;
- }
- static void cfi_flash_dev_remove(struct rt_device *dev, struct cfi_flash_device *fdev)
- {
- if (fdev->rw_lock.parent.parent.name[0])
- {
- rt_mutex_detach(&fdev->rw_lock);
- }
- if (fdev->sect[0])
- {
- rt_iounmap((void *)fdev->sect[0]);
- }
- }
- static rt_err_t cfi_flash_probe(struct rt_platform_device *pdev)
- {
- rt_err_t err;
- rt_size_t count;
- struct cfi_flash *cfi;
- struct rt_device *dev = &pdev->parent;
- if ((count = rt_dm_dev_get_address_count(dev)) <= 0)
- {
- return -RT_EEMPTY;
- }
- cfi = rt_calloc(1, sizeof(*cfi) + count * sizeof(cfi->dev[0]));
- if (!cfi)
- {
- return -RT_ENOMEM;
- }
- cfi->count = count;
- for (int i = 0; i < cfi->count; ++i)
- {
- if ((err = cfi_flash_dev_probe(dev, &cfi->dev[i], i)))
- {
- goto _fail;
- }
- }
- dev->user_data = cfi;
- return RT_EOK;
- _fail:
- for (int i = 0; i < cfi->count; ++i)
- {
- cfi_flash_dev_remove(dev, &cfi->dev[i]);
- }
- rt_free(cfi);
- return err;
- }
- static rt_err_t cfi_flash_remove(struct rt_platform_device *pdev)
- {
- struct rt_device *dev = &pdev->parent;
- struct cfi_flash *cfi = pdev->parent.user_data;
- for (int i = 0; i < cfi->count; ++i)
- {
- cfi_flash_dev_remove(dev, &cfi->dev[i]);
- }
- rt_free(cfi);
- return RT_EOK;
- }
- static const struct rt_ofw_node_id cfi_flash_ofw_ids[] =
- {
- { .compatible = "cfi-flash" },
- { .compatible = "jedec-flash" },
- { /* sentinel */ }
- };
- static struct rt_platform_driver cfi_flash_driver =
- {
- .name = "cfi-flash",
- .ids = cfi_flash_ofw_ids,
- .probe = cfi_flash_probe,
- .remove = cfi_flash_remove,
- };
- RT_PLATFORM_DRIVER_EXPORT(cfi_flash_driver);
|