mbed-os/targets/TARGET_Samsung/TARGET_SIDK_S5JS100/device/sflash_api.cpp

471 lines
12 KiB
C++
Executable File

/****************************************************************************
*
* Copyright 2020 Samsung Electronics All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed 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.
*
****************************************************************************/
#include <string.h>
#include "mbed.h"
#include "stdlib.h"
#include "cmsis.h"
#include "mbed_wait_api.h"
#include "flash_api.h"
/****************************************************************************
* Private Data
****************************************************************************/
int flash_no_erase;
int flash_idx = 0;
#define S5JS100_FLASH_DELAYTIME 1
#define S5JS100_FLASH_WRITEUNIT 32
#define CACHE_LINE_MASK 0xffffffe0
#define CACHE_LINE_SIZE 32
Semaphore *g_sem = new Semaphore(1);
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
static void hw_delay_us(unsigned int Value)
{
volatile unsigned i, j;
for (i = 0; i < (Value * 2); i++)
for (j = 0; j < 100; j++);
}
unsigned int up_progmem_blocksize(void)
{
return S5JS100_FLASH_BLOCK_SIZE;
}
/*
static void sflash_set_page_size(eQSPI_PAGE_SIZE size)
{
//Bytes per Page
modifyreg32(S5JS100_SFLASH_SFCON, 0xF << 8, size << 8);
}
*/
static unsigned int up_progmem_npages(void)
{
return S5JS100_FLASH_SIZE / S5JS100_FLASH_PAGE_SIZE;
}
static int up_progmem_getaddress(int page)
{
return S5JS100_FLASH_PADDR + S5JS100_FLASH_PAGE_SIZE * page;
}
int up_progmem_getpage(int addr)
{
return (addr - S5JS100_FLASH_PADDR) / S5JS100_FLASH_PAGE_SIZE;
}
static void s5js100_sflash_disable_wp(void)
{
/* someone has been disabled wp, we should wait until it's released */
while (getreg32(S5JS100_SFLASH_SFCON) & SFLASH_SFCON_WP_DISABLE) ;
modifyreg32(S5JS100_SFLASH_SFCON, SFLASH_SFCON_WP_MASK, SFLASH_SFCON_WP_DISABLE);
}
static void s5js100_sflash_enable_wp(void)
{
modifyreg32(S5JS100_SFLASH_SFCON, SFLASH_SFCON_WP_MASK, SFLASH_SFCON_WP_ENABLE);
}
static uint8_t s5js100_sflash_read_status(void)
{
return getreg8(S5JS100_SFLASH_RDSR);
}
/* return FLASH capacity in BYTE */
uint32_t s5js100_sflash_read_capacity(void)
{
uint32_t capacity_type;
capacity_type = (getreg32(S5JS100_SFLASH_RDID) & (0xFF << 16)) >> 16;
capacity_type = (0x1 << ((capacity_type & 0xF) - 1)) * 1024 * 1024;
return capacity_type / 8;
}
int s5js100_sflash_write_protect(eQSPI_PROTECTION_AREA area)
{
int status_register;
s5js100_sflash_disable_wp();
status_register = getreg8(S5JS100_SFLASH_RDSR);
//lldbg("status_register : 0x%x, area : 0x%x\n", status_register, area);
/* send Write Enable */
putreg8(1, S5JS100_SFLASH_WREN);
while (!(getreg8(S5JS100_SFLASH_RDSR) & 0x2));
/* write status register */
status_register = (status_register & 0x83) | (area << 2);
//lldbg("status_register write : 0x%x ==> ", status_register);
putreg32(status_register | 0x0200, S5JS100_SFLASH_WRSR);
while (getreg8(S5JS100_SFLASH_RDSR) & 0x1);
status_register = getreg8(S5JS100_SFLASH_RDSR);
//lldbg("0x%x\n", status_register);
/* send Write Disable */
putreg8(1, S5JS100_SFLASH_WRDI);
while (getreg8(S5JS100_SFLASH_RDSR) & 0x2);
s5js100_sflash_enable_wp();
return 0;
}
/* SFlash_DriverInitialize: SFlash Driver Initialize function */
void SFlash_DriverInitialize()
{
putreg32(0x8660060A, S5JS100_SFLASH_SFCON); /*disable write protect for FLASH stage changing*/
modifyreg32(0x85020074, 0x1, 1); /*Set FAST READ */
/* Enable Quad Read */
putreg32(0x4, S5JS100_SFLASH_IO_MODE);
putreg32(0x8, S5JS100_SFLASH_PERF_MODE);
// command 3. RDSR2 read winbond Status Register 2
modifyreg32(0x85020024, 0xFF, 0x35); //Set QE to Status2
while (!(getreg8(S5JS100_SFLASH_BASE + 0xDC) & (0x1 << 1))) {
}; /* Check FLASH has Quad Enabled */
cal_clk_setrate(d1_qspi, 100000000);
putreg32(0x0660061A, S5JS100_SFLASH_SFCON); //enable write protect + winbond + byte program
/* change drive strength */
modifyreg32(0x82021070, 0x3 << 8, 0x0 << 8); //Drive strength CS to (0x0)2mA
modifyreg32(0x82021074, 0x3 << 8, 0x0 << 8); //Drive strength SCK to (x0)2mA
modifyreg32(0x82021078, 0x3 << 8, 0x0 << 8); //Drive strength SI to (0x0)2mA
modifyreg32(0x8202107C, 0x3 << 8, 0x0 << 8); //Drive strength SO to (0x0)2mA
modifyreg32(0x82021080, 0x3 << 8, 0x0 << 8); //Drive strength WP to (0x0)2mA
modifyreg32(0x82021084, 0x3 << 8, 0x0 << 8); //Drive strength HLD to (0x0)2mA
s5js100_sflash_write_protect(SFLASH_PROTECTION_NONE);
}
extern "C" {
void s5js100_sflash_reinit(void)
{
cal_clk_setrate(d1_qspi, 100000000);
}
}
static void local_memcpy(void *dst, void *src, size_t n)
{
unsigned char *pout = (unsigned char *)dst;
unsigned char *pin = (unsigned char *)src;
while (n-- > 0) {
*pout++ = *pin++;
}
}
static int up_progmem_write_disabledcache(unsigned int addr, const void *buf, int count)
{
int remain = count;
char *pos = (char *)buf;
g_sem->try_acquire();
while (remain) {
int tmp = remain;
if (tmp > S5JS100_FLASH_WRITEUNIT) {
tmp = S5JS100_FLASH_WRITEUNIT;
}
s5js100_sflash_disable_wp();
/* Temporary code for testing */
//memcpy((void *)(addr + S5JS100_SFLASH_WOFFSET), (void *)(pos), tmp);
local_memcpy((void *)(addr + S5JS100_SFLASH_WOFFSET), (void *)(pos), tmp);
/* Flash Mirror Address is device attribute, need to POC with Flash Read Address */
/* invalidate cache for read address, not read and write in POC state */
/* CM7 cache line 32bytes, align 32bytes */
invalidate_dcache_by_addr((uint32_t *)(addr & CACHE_LINE_MASK), tmp + CACHE_LINE_SIZE);
s5js100_sflash_enable_wp();
pos += tmp;
addr += tmp;
remain -= tmp;
hw_delay_us(1000 * S5JS100_FLASH_DELAYTIME);
}
g_sem->release();
return count;
}
int sflash_write(unsigned int addr, unsigned char *buf, int count)
{
unsigned int uf_start = (unsigned int)S5JS100_FLASH_FS_PADDR;
unsigned int uf_end = S5JS100_FLASH_PADDR + S5JS100_FLASH_SIZE;
/*
* Check the request address is in a specific userflash area
* We assumed that the userflash area's address is started from the end of
* OS partition to the end of flash.
*/
if (addr >= uf_start && addr < uf_end) {
up_progmem_write_disabledcache(addr, buf, count);
return 0;
}
int pagesize;
int remain = count;
pagesize = S5JS100_FLASH_PAGE_SIZE;
g_sem->try_acquire();
while (remain) {
int tmp = remain;
if (tmp > pagesize) {
tmp = pagesize;
}
s5js100_sflash_disable_wp();
/* Load and write data */
local_memcpy((void *)addr, buf, tmp);
/* Flush cache */
clean_invalidate_dcache_by_addr((uint32_t *)(addr & CACHE_LINE_MASK), tmp + CACHE_LINE_SIZE);
s5js100_sflash_enable_wp();
buf += tmp;
addr += tmp;
remain -= tmp;
}
g_sem->release();
return 0;
}
static int _is_erased(unsigned int address, int size)
{
unsigned int *p = (unsigned int *)address;
while (size > 0) {
if (*p != 0xFFFFFFFF) {
return 0;
} else {
p++;
size -= 4;
}
}
return 1;
}
int up_progmem_erasepage(unsigned int page)
{
int addr;
if (page >= up_progmem_npages()) {
return -1;
}
addr = up_progmem_getaddress(page);
/* skip erased block */
if (_is_erased(addr, up_progmem_blocksize())) {
return 0;
}
g_sem->try_acquire();
//s5js100_flash_take_sem();
s5js100_sflash_disable_wp();
/* Set sector address and then send erase command */
putreg32(addr - S5JS100_FLASH_PADDR, S5JS100_SFLASH_ERASE_ADDRESS);
putreg8(0xff, S5JS100_SFLASH_SE);
/* Wait for the completion */
while (s5js100_sflash_read_status() & 0x1) {
};
/* Invalidate cache */
invalidate_dcache_by_addr((uint32_t *)(addr & CACHE_LINE_MASK)/* + S5JS100_FLASH_PADDR*/, up_progmem_blocksize());
s5js100_sflash_enable_wp();
g_sem->release();
return 0;
}
unsigned int up_progmem_write(unsigned int addr, const void *buf, unsigned int count)
{
return sflash_write(addr, (unsigned char *)buf, count);
}
int sflash_erase(unsigned int address)
{
int page;
page = up_progmem_getpage(address);
return up_progmem_erasepage(page);
}
#define ENV_MAX 20
struct _env_list {
char env_name[11];
char env_val[41];
} env_list[ENV_MAX];
#define BOOTARG_VAL_OFFSET 0x40
#define BOOTARG_VAL_MAX_SIZE 0x40
void sflash_os_env_parser(void)
{
char *env_addr, *ptr;
int argc = 0;
char buf[4096];
uint32_t env_offset;
if (s5js100_sflash_read_capacity() == 8 * 1024 * 1024) {
env_offset = S5JS100_OS_ENV_OFFSET_8MB;
}
if (s5js100_sflash_read_capacity() == 16 * 1024 * 1024) {
env_offset = S5JS100_OS_ENV_OFFSET_16MB;
}
env_addr = (char *)(S5JS100_FLASH_PADDR + env_offset + BOOTARG_VAL_OFFSET);
memcpy(buf, env_addr, 4 * 1024);
ptr = buf;
while (argc < ENV_MAX) {
char *arg = ptr;
int i = 0, j = 1; //j from '='
if (*ptr == 0xFF || *ptr == 0) {
ptr += BOOTARG_VAL_OFFSET;//strtok(NULL, "\n\r\t ,");
argc++;
continue;
}
while (*(arg + i) != '=') {
if (*(arg + i) == 0xFF || i >= 10) {
return;
}
i++;
}
*(arg + i) = '\0';
strcpy(env_list[argc].env_name, arg);
while (j < BOOTARG_VAL_MAX_SIZE - i) {
if (*(arg + i + j) == 0xFF || *(arg + i + j) == 0) {
break;
}
j++;
}
*(arg + i + j) = '\0';
strcpy(env_list[argc++].env_val, arg + i + 1);
ptr += BOOTARG_VAL_OFFSET;//strtok(NULL, "\n\r\t ,");
}
}
char *get_env(const char *name)
{
int i;
for (i = 0; i < ENV_MAX; i++) {
if (!strcmp(name, env_list[i].env_name)) {
return env_list[i].env_val;
}
}
return 0;
}
#ifdef DEVICE_FLASH
/*************** flash_hal API ********************/
/* hal/flash_api.h */
int32_t flash_init(flash_t *obj)
{
SFlash_DriverInitialize();
return 0;
}
int32_t flash_free(flash_t *obj)
{
return 0;
}
uint32_t flash_get_page_size(const flash_t *info)
{
return 4; /*S5JS100_FLASH_PAGE_SIZE*/
}
uint32_t flash_get_sector_size(const flash_t *info, uint32_t addr)
{
return up_progmem_blocksize();
}
uint32_t flash_get_start_address(const flash_t *info)
{
return S5JS100_FLASH_FS_PADDR;
}
uint32_t flash_get_size(const flash_t *info)
{
return S5JS100_FLASH_FS_SIZE;
}
int32_t flash_program_page(flash_t *obj, uint32_t addr, const uint8_t *data, uint32_t size)
{
if (addr > S5JS100_FLASH_PADDR) {
return sflash_write(addr, (unsigned char *)data, size);
} else {
local_memcpy((void *)addr, (void *)data, size);
return 0;
}
}
uint8_t flash_get_erase_value(const flash_t *obj)
{
(void)obj;
return 0xFF;
}
int32_t flash_erase_sector(flash_t *obj, uint32_t addr)
{
if (addr > S5JS100_FLASH_PADDR) {
return sflash_erase(addr);
} else {
memset((void *)addr, 0xFFFFFFFF, 4096);
return 0;
}
}
#endif