| /**************************************************************************** |
| * arch/arm/src/tlsr82/tlsr82_flash_mtd.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <assert.h> |
| #include <debug.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/param.h> |
| #include <sys/types.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/mm/mm.h> |
| #include <nuttx/kmalloc.h> |
| |
| #include "hardware/tlsr82_gpio.h" |
| #include "tlsr82_flash.h" |
| #include "tlsr82_flash_mtd.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Flash size deifnitions */ |
| |
| #define TLSR82_SECTOR_SHIFT 12 |
| #define TLSR82_SECTOR_SIZE (1 << TLSR82_SECTOR_SHIFT) |
| #define TLSR82_PAGE_SHIFT 8 |
| #define TLSR82_PAGE_SIZE (1 << TLSR82_PAGE_SHIFT) |
| |
| /* Flash layout definitions */ |
| |
| #define TLSR82_START_ADDR (0) |
| #define TLSR82_TOTAL_SIZE (512 * 1024) |
| |
| #define TLSR82_BANKA_ADDR TLSR82_START_ADDR |
| #define TLSR82_BANKA_SIZE (240 * 1024) |
| |
| #define TLSR82_USR_ADDR (TLSR82_BANKA_ADDR + TLSR82_BANKA_SIZE) |
| #define TLSR82_USR_SIZE (12 * 1024) |
| |
| #define TLSR82_KEY_ADDR (TLSR82_USR_ADDR + TLSR82_USR_SIZE) |
| #define TLSR82_KEY_SIZE (4 * 1024) |
| |
| #define TLSR82_BANKB_ADDR (TLSR82_KEY_ADDR + TLSR82_KEY_SIZE) |
| #define TLSR82_BANKB_SIZE (240 * 1024) |
| |
| #define TLSR82_SYS_ADDR (TLSR82_BANKB_ADDR + TLSR82_BANKB_SIZE) |
| #define TLSR82_SYS_SIZE (16 * 1024) |
| |
| /* Flash write buffer size definitions */ |
| |
| #ifdef CONFIG_TLSR82_FLASH_WRITE_BUFFER |
| # define FLASH_WRITE_BUF_SIZE CONFIG_TLSR82_FLASH_WRITE_BUFFER_SIZE |
| #endif |
| |
| /* Flash test definitions */ |
| |
| #ifdef CONFIG_TLSR82_FLASH_TEST |
| |
| /* Flash sector erase trace macro definition */ |
| |
| #define FLASH_ERASE_TRACE_START() |
| #define FLASH_ERASE_TRACE_END() |
| |
| /* Flash page read/write trace macro definition */ |
| |
| #define FLASH_READ_TRACE_START() |
| #define FLASH_READ_TRACE_END() |
| |
| #define FLASH_WRITE_TRACE_START() |
| #define FLASH_WRITE_TRACE_END() |
| |
| /* Flash protect/unprotect trace marco definition */ |
| |
| #define FLASH_PROT_TRACE_START() |
| #define FLASH_PROT_TRACE_END() |
| |
| #define FLASH_UNPROT_TRACE_START() |
| #define FLASH_UNPROT_TRACE_END() |
| |
| #define FLASH_BUF_LIST16(n) \ |
| n , n + 1, n + 2, n + 3, n + 4, n + 5, n + 6, n + 7, \ |
| n + 8, n + 9, n + 10, n + 11, n + 12, n + 13, n + 14, n + 15, |
| |
| #define FLASH_BUF_LIST128(n) \ |
| FLASH_BUF_LIST16(n) \ |
| FLASH_BUF_LIST16(n + 0x10) \ |
| FLASH_BUF_LIST16(n + 0x20) \ |
| FLASH_BUF_LIST16(n + 0x30) \ |
| FLASH_BUF_LIST16(n + 0x40) \ |
| FLASH_BUF_LIST16(n + 0x50) \ |
| FLASH_BUF_LIST16(n + 0x60) \ |
| FLASH_BUF_LIST16(n + 0x70) \ |
| |
| #endif |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* This type represents the state of the MTD device. |
| * The struct mtd_dev_s must appear at the beginning of the definition |
| * so that you can freely cast between pointers to struct mtd_dev_s and |
| * struct tlsr82_flash_dev_s. |
| */ |
| |
| struct tlsr82_flash_dev_s |
| { |
| struct mtd_dev_s mtd; /* MTD interface */ |
| uint32_t baseaddr; /* mtd flash start address */ |
| uint32_t size; /* avaliable size for MTD */ |
| uint16_t nsectors; /* Number of erase sectors */ |
| uint16_t sectorsize; /* Size of one sector */ |
| uint16_t pagesize; /* Size of one page */ |
| }; |
| |
| struct tlsr82_partition_s |
| { |
| char *path; |
| off_t firstblock; |
| off_t nblocks; |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Flash mtd partition table */ |
| |
| #ifdef CONFIG_MTD_PARTITION |
| static const struct tlsr82_partition_s tlsr82_part_table[] = |
| { |
| { |
| .path = "dev/banka", |
| .firstblock = TLSR82_BANKA_ADDR / TLSR82_PAGE_SIZE, |
| .nblocks = TLSR82_BANKA_SIZE / TLSR82_PAGE_SIZE, |
| }, |
| { |
| .path = "dev/usr", |
| .firstblock = TLSR82_USR_ADDR / TLSR82_PAGE_SIZE, |
| .nblocks = TLSR82_USR_SIZE / TLSR82_PAGE_SIZE, |
| }, |
| { |
| .path = "dev/key", |
| .firstblock = TLSR82_KEY_ADDR / TLSR82_PAGE_SIZE, |
| .nblocks = TLSR82_KEY_SIZE / TLSR82_PAGE_SIZE, |
| }, |
| { |
| .path = "dev/bankb", |
| .firstblock = TLSR82_BANKB_ADDR / TLSR82_PAGE_SIZE, |
| .nblocks = TLSR82_BANKB_SIZE / TLSR82_PAGE_SIZE, |
| }, |
| { |
| .path = "dev/sys", |
| .firstblock = TLSR82_SYS_ADDR / TLSR82_PAGE_SIZE, |
| .nblocks = TLSR82_SYS_SIZE / TLSR82_PAGE_SIZE, |
| } |
| }; |
| #endif |
| |
| /* Flash manufacture ID and unique ID */ |
| |
| static uint32_t g_flash_mid; |
| static uint8_t g_flash_uid[16]; |
| |
| /* Flash byte write buffer when enable the flash byte write buffer */ |
| |
| #ifdef CONFIG_TLSR82_FLASH_WRITE_BUFFER |
| static uint8_t flash_write_buffer[FLASH_WRITE_BUF_SIZE]; |
| #endif |
| |
| /* Flash test buffer when enable the flash test */ |
| |
| #ifdef CONFIG_TLSR82_FLASH_TEST |
| |
| static uint8_t flash_buffer[TLSR82_PAGE_SIZE] = |
| { |
| FLASH_BUF_LIST128(0) |
| FLASH_BUF_LIST128(1) |
| }; |
| |
| static uint8_t flash_read_buffer[TLSR82_PAGE_SIZE]; |
| static char print_buf[128]; |
| #endif |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Flash driver methods */ |
| |
| static int tlsr82_flash_chip_erase(struct tlsr82_flash_dev_s *priv); |
| |
| #ifdef CONFIG_TLSR82_FLASH_TEST |
| static void tlsr82_flash_print(const char *msg, const uint8_t *buf, |
| size_t len); |
| |
| static int tlsr82_flash_test(struct tlsr82_flash_dev_s *priv); |
| #endif |
| |
| /* MTD driver methods */ |
| |
| static int tlsr82_flash_erase (struct mtd_dev_s *dev, off_t startblock, |
| size_t nblocks); |
| |
| static ssize_t tlsr82_flash_bread (struct mtd_dev_s *dev, off_t startblock, |
| size_t nblocks, uint8_t *buf); |
| |
| static ssize_t tlsr82_flash_bwrite(struct mtd_dev_s *dev, off_t startblock, |
| size_t nblocks, const uint8_t *buf); |
| |
| static ssize_t tlsr82_flash_read (struct mtd_dev_s *dev, off_t offset, |
| size_t nbytes, uint8_t *buffer); |
| |
| #ifdef CONFIG_MTD_BYTE_WRITE |
| static ssize_t tlsr82_flash_write (struct mtd_dev_s *dev, off_t offset, |
| size_t nbytes, const uint8_t *buffer); |
| #endif |
| |
| static int tlsr82_flash_ioctl (struct mtd_dev_s *dev, int cmd, |
| unsigned long arg); |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_readid |
| ****************************************************************************/ |
| |
| static int tlsr82_flash_chip_erase(struct tlsr82_flash_dev_s *priv) |
| { |
| int i; |
| uint32_t addr; |
| |
| addr = priv->baseaddr; |
| for (i = 0; i < priv->nsectors; i++) |
| { |
| finfo("Erase sector: %d, address: 0x%08lx\n", i, addr); |
| tlsr82_flash_erase_sector(addr); |
| addr += priv->sectorsize; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_print |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_TLSR82_FLASH_TEST |
| static void tlsr82_flash_print(const char *msg, const uint8_t *buf, |
| size_t len) |
| { |
| int i = 0; |
| int off = 0; |
| |
| ferr("%s\n", msg); |
| while (len--) |
| { |
| if (i % 16 == 0) |
| { |
| snprintf(&print_buf[off], |
| sizeof(print_buf) - off, "0x%08x:", i); |
| off += strlen(&print_buf[off]); |
| } |
| |
| snprintf(&print_buf[off], |
| sizeof(print_buf) - off, "0x%02x ", buf[i]); |
| off += strlen(&print_buf[off]); |
| |
| if (++i % 16 == 0) |
| { |
| ferr("%s\n", print_buf); |
| off = 0; |
| } |
| } |
| |
| if (i % 16 != 0) |
| { |
| ferr("%s\n", print_buf); |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_test |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_TLSR82_FLASH_TEST |
| static int tlsr82_flash_test(struct tlsr82_flash_dev_s *priv) |
| { |
| struct mtd_geometry_s geo; |
| int ret = 0; |
| int npages = 0; |
| int i = 0; |
| int j = 0; |
| |
| ferr("======== Flash test start ========\n"); |
| |
| /* 1. print the manufacture id and unique id */ |
| |
| ferr("Flash information print:\n"); |
| ferr(" Flash MID: 0x%08lx\n", g_flash_mid); |
| snprintf(&print_buf[ret], |
| sizeof(print_buf) - ret, " Flash UID: "); |
| ret += strlen(&print_buf[ret]); |
| for (i = 1; i < 16; i++) |
| { |
| snprintf(&print_buf[ret], |
| sizeof(print_buf) - ret, "0x%x ", g_flash_uid[i]); |
| ret += strlen(&print_buf[ret]); |
| } |
| |
| ferr("%s\n", print_buf); |
| |
| ret = tlsr82_flash_ioctl(&priv->mtd, MTDIOC_GEOMETRY, (unsigned long)&geo); |
| if (ret != OK) |
| { |
| ferr(" Flash geometry get failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| npages = priv->nsectors * (TLSR82_SECTOR_SIZE / TLSR82_PAGE_SIZE); |
| |
| ferr(" Flash Start Address: 0x%08lx\n", priv->baseaddr); |
| ferr(" Flash Size : 0x%08lx\n", priv->size); |
| ferr(" Flash Sector Size : %ld\n", geo.erasesize); |
| ferr(" Flash Page Size : %ld\n", geo.blocksize); |
| ferr(" Flash Sector : %ld\n", geo.neraseblocks); |
| ferr(" Flash Page : %d\n", npages); |
| |
| /* 2. erase chip and check all the erased sector, all the bit in erased |
| * sector should be 1. |
| */ |
| |
| ferr("Flash chip erase test start:\n"); |
| |
| FLASH_ERASE_TRACE_START(); |
| ret = tlsr82_flash_ioctl(&priv->mtd, MTDIOC_BULKERASE, 0); |
| if (ret != OK) |
| { |
| ferr(" Flash erase failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| FLASH_ERASE_TRACE_END(); |
| |
| for (i = 0; i < npages; i++) |
| { |
| FLASH_READ_TRACE_START(); |
| ret = tlsr82_flash_bread(&priv->mtd, i, 1, flash_read_buffer); |
| FLASH_READ_TRACE_END(); |
| if (ret != 1) |
| { |
| ferr(" Flash block read failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| for (j = 0; j < TLSR82_PAGE_SIZE; j++) |
| { |
| if (flash_read_buffer[j] != 0xff) |
| { |
| ferr(" Flash erased data is not 0xff, page_i=%d, byte_j=%d," |
| "data=0x%x\n", i, j, flash_read_buffer[j]); |
| goto errout; |
| } |
| } |
| } |
| |
| ferr("Flach chip erase test Success.\n"); |
| |
| /* 3. Write all the flash and check result */ |
| |
| ferr("Flash page write/read test start:\n"); |
| tlsr82_flash_print("Write buffer data:", flash_buffer, |
| TLSR82_PAGE_SIZE); |
| for (i = 0; i < npages; i++) |
| { |
| FLASH_WRITE_TRACE_START(); |
| ret = tlsr82_flash_bwrite(&priv->mtd, i, 1, flash_buffer); |
| FLASH_WRITE_TRACE_END(); |
| if (ret != 1) |
| { |
| ferr(" Flash block write failed, ret=%d\n", ret); |
| goto errout; |
| } |
| } |
| |
| for (i = 0; i < npages; i++) |
| { |
| memset(flash_read_buffer, 0, TLSR82_PAGE_SIZE); |
| ret = tlsr82_flash_bread(&priv->mtd, i, 1, flash_read_buffer); |
| if (ret != 1) |
| { |
| ferr(" Flash block read failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| if (memcmp(flash_read_buffer, flash_buffer, TLSR82_PAGE_SIZE) != 0) |
| { |
| ferr(" Flash write compre is not equal, page_i=%d\n", i); |
| tlsr82_flash_print("Write buffer data:", flash_buffer, |
| TLSR82_PAGE_SIZE); |
| tlsr82_flash_print("Read buffer data:", flash_read_buffer, |
| TLSR82_PAGE_SIZE); |
| goto errout; |
| } |
| } |
| |
| ferr("Flash page write/read test Success.\n"); |
| |
| #ifdef CONFIG_MTD_BYTE_WRITE |
| |
| /* 4. Erase chip again for byte write/read test */ |
| |
| uint32_t offset = 0; |
| int k; |
| |
| ferr("Erase chip for byte write test\n"); |
| ret = tlsr82_flash_ioctl(&priv->mtd, MTDIOC_BULKERASE, 0); |
| if (ret != OK) |
| { |
| ferr(" Flash erase failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| ferr("Erase chip finished\n"); |
| |
| ferr("Flash byte read/write test start\n"); |
| do |
| { |
| /* i = the write number, j = the flash_buffer index */ |
| |
| i = 40; |
| if (i + offset > priv->size) |
| { |
| i = priv->size - offset; |
| } |
| |
| j = offset & 128; |
| |
| FLASH_WRITE_TRACE_START(); |
| ret = tlsr82_flash_write(&priv->mtd, offset, i, &flash_buffer[j]); |
| FLASH_WRITE_TRACE_END(); |
| |
| if (ret != i) |
| { |
| ferr(" Flash byte write failed, offset: 0x%08lx, ret=%d\n", |
| offset, ret); |
| goto errout; |
| } |
| |
| memset(flash_read_buffer, 0, i); |
| |
| FLASH_READ_TRACE_START(); |
| ret = tlsr82_flash_read(&priv->mtd, offset, i, flash_read_buffer); |
| FLASH_READ_TRACE_END(); |
| |
| if (ret != i) |
| { |
| ferr(" Flash byte read failed, offset: 0x%08lx, ret=%d\n", |
| offset, ret); |
| goto errout; |
| } |
| |
| for (k = 0; k < i; k++) |
| { |
| if (flash_buffer[j + k] != flash_read_buffer[k]) |
| { |
| ferr(" Flash byte write/read compare failed\n"); |
| ferr(" Flash address: 0x%08lx\n", priv->baseaddr + offset); |
| tlsr82_flash_print("Write Data:", &flash_buffer[j], i); |
| tlsr82_flash_print("Read Data:", flash_read_buffer, i); |
| goto errout; |
| } |
| } |
| |
| offset += i; |
| } |
| while (offset < priv->size); |
| |
| ferr("Flash byte read/write test Success\n"); |
| |
| #endif |
| |
| #ifdef CONFIG_TLSR82_FLASH_PROTECT |
| /* 5. Flash protect/unprotect test: |
| * 1) erase the chip; |
| * 2) write data into chip; |
| * 3) protect flash; |
| * 4) erase the chip with flash protected; |
| * 5) read the chip, the data should be same as the data written in 2); |
| */ |
| |
| uint8_t status; |
| uint32_t addr; |
| |
| /* 1) erase the chip */ |
| |
| ferr("Erase chip for protect/unprotect test\n"); |
| ret = tlsr82_flash_ioctl(&priv->mtd, MTDIOC_BULKERASE, 0); |
| if (ret != OK) |
| { |
| ferr(" Flash erase failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| ferr("Erase chip finished\n"); |
| |
| /* 2) write data into chip */ |
| |
| tlsr82_flash_print("Write buffer data:", flash_buffer, TLSR82_PAGE_SIZE); |
| for (i = 0; i < npages; i++) |
| { |
| ret = tlsr82_flash_bwrite(&priv->mtd, i, 1, flash_buffer); |
| if (ret != 1) |
| { |
| ferr(" Flash block write failed, ret=%d\n", ret); |
| goto errout; |
| } |
| } |
| |
| /* 3) protect the flash, read the flash status register */ |
| |
| FLASH_PROT_TRACE_START(); |
| ret = tlsr82_flash_ioctl(&priv->mtd, MTDIOC_PROTECT, 0); |
| if (ret != OK) |
| { |
| ferr(" Flash protect failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| FLASH_PROT_TRACE_END(); |
| |
| status = tlsr82_flash_read_status(5); |
| ferr("Current flash status: 0x%x\n", status); |
| |
| /* 4) erase the chip with flash protected */ |
| |
| addr = priv->baseaddr; |
| for (i = 0; i < priv->nsectors; i++) |
| { |
| tlsr82_flash_erase_sector(addr); |
| addr += priv->sectorsize; |
| } |
| |
| FLASH_UNPROT_TRACE_START(); |
| ret = tlsr82_flash_ioctl(&priv->mtd, MTDIOC_UNPROTECT, 0); |
| if (ret != OK) |
| { |
| ferr(" Flash unprotect failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| FLASH_UNPROT_TRACE_END(); |
| |
| /* 5) read the chip, the data should be same as the data written in 2) */ |
| |
| for (i = 0; i < npages; i++) |
| { |
| memset(flash_read_buffer, 0, TLSR82_PAGE_SIZE); |
| ret = tlsr82_flash_bread(&priv->mtd, i, 1, flash_read_buffer); |
| if (ret != 1) |
| { |
| ferr(" Flash block read failed, ret=%d\n", ret); |
| goto errout; |
| } |
| |
| if (memcmp(flash_read_buffer, flash_buffer, TLSR82_PAGE_SIZE) != 0) |
| { |
| ferr(" Flash write compre is not equal, page_i=%d\n", i); |
| tlsr82_flash_print("Write buffer data:", flash_buffer, |
| TLSR82_PAGE_SIZE); |
| tlsr82_flash_print("Read buffer data:", flash_read_buffer, |
| TLSR82_PAGE_SIZE); |
| goto errout; |
| } |
| } |
| #endif |
| |
| ferr("======== Flash test Success ========\n"); |
| |
| return OK; |
| |
| errout: |
| ferr("Flash test Failed.\n"); |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_erase |
| ****************************************************************************/ |
| |
| static int tlsr82_flash_erase(struct mtd_dev_s *dev, off_t startblock, |
| size_t nblocks) |
| { |
| struct tlsr82_flash_dev_s *priv = (struct tlsr82_flash_dev_s *)dev; |
| size_t i; |
| uint32_t addr; |
| |
| finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); |
| |
| addr = priv->baseaddr + startblock * priv->sectorsize; |
| for (i = 0; i < nblocks; i++) |
| { |
| tlsr82_flash_erase_sector(addr); |
| addr += priv->sectorsize; |
| } |
| |
| return (int)nblocks; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_bread |
| ****************************************************************************/ |
| |
| static ssize_t tlsr82_flash_bread(struct mtd_dev_s *dev, off_t startblock, |
| size_t nblocks, uint8_t *buffer) |
| { |
| struct tlsr82_flash_dev_s *priv = (struct tlsr82_flash_dev_s *)dev; |
| uint32_t addr; |
| size_t i; |
| |
| finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); |
| |
| addr = priv->baseaddr + startblock * priv->pagesize; |
| |
| for (i = 0; i < nblocks; i++) |
| { |
| tlsr82_flash_read_data(addr, buffer, priv->pagesize); |
| addr += priv->pagesize; |
| buffer += priv->pagesize; |
| } |
| |
| return nblocks; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_bwrite |
| ****************************************************************************/ |
| |
| static ssize_t tlsr82_flash_bwrite(struct mtd_dev_s *dev, off_t startblock, |
| size_t nblocks, const uint8_t *buffer) |
| { |
| struct tlsr82_flash_dev_s *priv = (struct tlsr82_flash_dev_s *)dev; |
| uint32_t addr; |
| size_t i; |
| |
| finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); |
| |
| addr = priv->baseaddr + startblock * priv->pagesize; |
| |
| for (i = 0; i < nblocks; i++) |
| { |
| tlsr82_flash_write_data(addr, buffer, priv->pagesize); |
| addr += priv->pagesize; |
| buffer += priv->pagesize; |
| } |
| |
| return nblocks; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_read |
| ****************************************************************************/ |
| |
| static ssize_t tlsr82_flash_read(struct mtd_dev_s *dev, off_t offset, |
| size_t nbytes, uint8_t *buffer) |
| { |
| struct tlsr82_flash_dev_s *priv = (struct tlsr82_flash_dev_s *)dev; |
| uint32_t addr; |
| |
| finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes); |
| |
| addr = priv->baseaddr + offset; |
| |
| tlsr82_flash_read_data(addr, buffer, nbytes); |
| |
| finfo("return nbytes: %d\n", (int)nbytes); |
| return (ssize_t)nbytes; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_write |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_MTD_BYTE_WRITE |
| static ssize_t tlsr82_flash_write (struct mtd_dev_s *dev, off_t offset, |
| size_t nbytes, const uint8_t *buffer) |
| { |
| struct tlsr82_flash_dev_s *priv = (struct tlsr82_flash_dev_s *)dev; |
| uint32_t addr; |
| size_t allow_len = 0; |
| size_t write_len = 0; |
| size_t len = nbytes; |
| |
| finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes); |
| |
| addr = priv->baseaddr + offset; |
| |
| do |
| { |
| /* The allow length is the max allow write lentgh that not cross page */ |
| |
| allow_len = TLSR82_PAGE_SIZE - (addr & (TLSR82_PAGE_SIZE - 1)); |
| write_len = MIN(nbytes, allow_len); |
| |
| #ifdef CONFIG_TLSR82_FLASH_WRITE_BUFFER |
| |
| /* The write length can not be larger than FLASH_WRITE_BUF_SIZE */ |
| |
| write_len = MIN(FLASH_WRITE_BUF_SIZE, write_len); |
| |
| /* Copy the data to the buffer and write, the temporary buffer is used |
| * to make sure the argument buffer is not in flash. Telink flash is |
| * not allowed read during flash writing. |
| */ |
| |
| memcpy(flash_write_buffer, buffer, write_len); |
| tlsr82_flash_write_data(addr, flash_write_buffer, write_len); |
| #else |
| tlsr82_flash_write_data(addr, buffer, write_len); |
| #endif |
| |
| addr += write_len; |
| buffer += write_len; |
| nbytes -= write_len; |
| } |
| while (nbytes > 0); |
| |
| return (ssize_t)len; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_ioctl |
| ****************************************************************************/ |
| |
| static int tlsr82_flash_ioctl(struct mtd_dev_s *dev, int cmd, |
| unsigned long arg) |
| { |
| struct tlsr82_flash_dev_s *priv = (struct tlsr82_flash_dev_s *)dev; |
| int ret = OK; /* Assume good command with good parameters */ |
| |
| finfo("cmd: %d\n", cmd); |
| |
| switch (cmd) |
| { |
| case MTDIOC_GEOMETRY: |
| { |
| struct mtd_geometry_s *geo = |
| (struct mtd_geometry_s *)((uintptr_t)arg); |
| |
| if (geo) |
| { |
| memset(geo, 0, sizeof(*geo)); |
| |
| /* Populate the geometry structure with information need to |
| * know the capacity and how to access the device. |
| * |
| * NOTE: |
| * that the device is treated as though it where just an array |
| * of fixed size blocks. That is most likely not true, but |
| * the client will expect the device logic to do whatever is |
| * necessary to make it appear so. |
| */ |
| |
| geo->blocksize = TLSR82_PAGE_SIZE; |
| geo->erasesize = TLSR82_SECTOR_SIZE; |
| geo->neraseblocks = priv->nsectors; |
| ret = OK; |
| |
| finfo("blocksize: %" PRId32 " erasesize: %" PRId32 |
| " neraseblocks: %" PRId32 "\n", |
| geo->blocksize, geo->erasesize, geo->neraseblocks); |
| } |
| else |
| { |
| ret = -EINVAL; |
| } |
| } |
| break; |
| |
| case MTDIOC_BULKERASE: |
| { |
| tlsr82_flash_chip_erase(priv); |
| } |
| break; |
| |
| case MTDIOC_PROTECT: |
| { |
| /* Ignore arg, direct protect full chip */ |
| |
| tlsr82_flash_protect(); |
| } |
| break; |
| |
| case MTDIOC_UNPROTECT: |
| { |
| /* Ignore arg, direct unprotect full chip */ |
| |
| tlsr82_flash_unprotect(); |
| } |
| break; |
| |
| default: |
| ret = -ENOTTY; /* Bad/unsupported command */ |
| break; |
| } |
| |
| finfo("return %d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tlsr82_flash_initialize |
| * |
| * Description: |
| * Create an initialize MTD device instance for the internal FLASH. |
| * |
| * MTD devices are not registered in the file system, but are created as |
| * instances that can be bound to other functions (such as a block or |
| * character driver front end). |
| * |
| * Parameter: |
| * offset - offset from 0 of internal flash |
| * size - avaiable size for NVM |
| * |
| ****************************************************************************/ |
| |
| struct mtd_dev_s *tlsr82_flash_initialize(uint32_t offset, uint32_t size) |
| { |
| int ret; |
| struct tlsr82_flash_dev_s *priv; |
| |
| /* If the mtd device address exceeded the flash address space, return |
| * NULL. |
| */ |
| |
| if ((offset + size) > (CONFIG_TLSR82_FLASH_SIZE_KB * 1024)) |
| { |
| goto errout; |
| } |
| |
| /* Allocate a state structure (we allocate the structure instead of using |
| * a fixed, static allocation so that we can handle multiple FLASH devices. |
| * The current implementation would handle only one FLASH part per QuadSPI |
| * device (only because of the QSPIDEV_FLASH(0) definition) and so would |
| * have to be extended to handle multiple FLASH parts on the same QuadSPI |
| * bus. |
| */ |
| |
| priv = (struct tlsr82_flash_dev_s *) |
| kmm_zalloc(sizeof(struct tlsr82_flash_dev_s)); |
| if (priv) |
| { |
| /* Initialize the allocated structure (unsupported methods were |
| * nullified by kmm_zalloc). |
| */ |
| |
| priv->mtd.erase = tlsr82_flash_erase; |
| priv->mtd.bread = tlsr82_flash_bread; |
| priv->mtd.bwrite = tlsr82_flash_bwrite; |
| priv->mtd.read = tlsr82_flash_read; |
| #ifdef CONFIG_MTD_BYTE_WRITE |
| priv->mtd.write = tlsr82_flash_write; |
| #endif |
| priv->mtd.ioctl = tlsr82_flash_ioctl; |
| priv->mtd.name = "tlsr82_flash_mtd"; |
| |
| priv->baseaddr = offset; |
| priv->size = size; |
| priv->sectorsize = TLSR82_SECTOR_SIZE; |
| priv->pagesize = TLSR82_PAGE_SIZE; |
| priv->nsectors = size >> TLSR82_SECTOR_SHIFT; |
| |
| /* Identify the FLASH chip */ |
| |
| ret = tlsr82_flash_miduid_check(&g_flash_mid, &g_flash_uid[0]); |
| if (ret < 0) |
| { |
| ferr("Flash mid or uid error\n"); |
| goto errout_with_priv; |
| } |
| } |
| else |
| { |
| ferr("Flash device malloc failed, size=%lu\n", |
| sizeof(struct tlsr82_flash_dev_s)); |
| goto errout; |
| } |
| |
| /* Calibrate the flash */ |
| |
| tlsr82_flash_calibrate(g_flash_mid); |
| |
| #ifdef CONFIG_TLSR82_FLASH_TEST |
| ret = tlsr82_flash_test(priv); |
| if (ret < 0) |
| { |
| ferr("Flash test failed, ret=%d\n", ret); |
| goto errout_with_priv; |
| } |
| #endif |
| |
| /* Return the implementation-specific state structure as the MTD device */ |
| |
| finfo("Return %p\n", priv); |
| return (struct mtd_dev_s *)priv; |
| |
| errout_with_priv: |
| kmm_free(priv); |
| return NULL; |
| |
| errout: |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: tlsr82_partition_init |
| * |
| * Description: |
| * Register partition MTD device on the parent MTD device. |
| * |
| * Parameter: |
| * path - the parent mtd device path. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_MTD_PARTITION |
| int tlsr82_partition_init(const char *path) |
| { |
| int ret = OK; |
| int num; |
| int i; |
| |
| num = sizeof(tlsr82_part_table) / sizeof(struct tlsr82_partition_s); |
| |
| for (i = 0; i < num; i++) |
| { |
| ret = register_mtdpartition(tlsr82_part_table[i].path, 0, path, |
| tlsr82_part_table[i].firstblock, |
| tlsr82_part_table[i].nblocks); |
| if (ret < 0) |
| { |
| ferr("Register mtd partition failed, path=%s, ret=%d\n", |
| tlsr82_part_table[i].path, ret); |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| #endif |