Newer
Older
mbed-os / targets / TARGET_Samsung / TARGET_SIDK_S5JS100 / device / sflash_api.cpp
/****************************************************************************
 *
 * 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