#include "flash_dev_sst25.h"

static t_flash_errs f_flash_init(t_flash_iface *iface) ;

typedef enum {
    SST25_CMD_READ              = 0x03,
    SST25_CMD_FAST_READ         = 0x0B,
    SST25_CMD_ERASE_4K          = 0x20,
    SST25_CMD_ERASE_32K         = 0x52,
    SST25_CMD_ERASE_64K         = 0xD8,
    SST25_CMD_ERASE_CHIP        = 0x60,
    SST25_CMD_BYTE_PROGR        = 0x02,
    SST25_CMD_AAI_WORD_PROGR    = 0xAD,
    SST25_CMD_RDSR              = 0x05,
    SST25_CMD_EWSR              = 0x50,
    SST25_CMD_WRSR              = 0x01,
    SST25_CMD_WREN              = 0x06,
    SST25_CMD_WRDI              = 0x04,
    SST25_CMD_RDID              = 0x90,
    SST25_CMD_JEDEC_ID          = 0x9F,
    SST25_CMD_EBSY              = 0x70,
    SST25_CMD_DBSY              = 0x80
} t_sst25_cmds;



static const t_flash_erase_cmd f_erase_cmds[] = {
    {FLASH_CMD_CODE_ERASE_CHIP,    SST25_FLASH_SIZE,  50, FLASH_ERASE_CMD_FLAG_NOADDR},
    {SST25_CMD_ERASE_64K,                   64*1024,  25, 0},
    {SST25_CMD_ERASE_32K,                   32*1024,  25, 0},
    {SST25_CMD_ERASE_4K,                       4096,  25, 0},
    {0,0,0,0}
};


const t_flash_info flash_info_sst25 =  {
    {SST25_ID_MANUFACTURER, SST25_ID_DEVICE, SST25_ID_CAPACITY, FLASH_ID_EDI_LEN_NOT_SUPPORTED},
    SST25_FLASH_SIZE,
    {SST25_CMD_RDSR, SST25_STATUS_BUSY, 0x0,0x0},
    {SST25_CMD_FAST_READ, 1},
    {f_erase_cmds, SST25_CMD_WREN},
    {1, SST25_CMD_BYTE_PROGR, 10},
    {0},
    {0,0,0},
    {   f_flash_init,
        flash_sst25_get_status,
        flash_sst25_read,
        flash_sst25_erase,
        flash_sst25_get_erase_cmd,
        flash_sst25_write,
        flash_sst25_write_enable,        
        NULL,
        NULL,
        NULL,
        NULL,

        NULL,
        NULL,
        NULL,

        NULL,
        NULL,
        NULL
    },
    NULL
};


static t_flash_errs f_write_byte(t_flash_iface *iface, unsigned addr, unsigned char byte) {
    t_flash_errs err = 0;
    unsigned char cmd;


    cmd = SST25_CMD_WREN;
    err = flash_exec_cmd_only(iface, &cmd, 1, 0);

    if (!err) {
        err = flash_exec_cmd_addr(iface, SST25_CMD_BYTE_PROGR, addr,
                                  0, &byte, NULL, 1, 0);
    }

    if (!err) {
        err = flash_wait_ready_mks(iface, iface->flash_info->progr.prog_tout_mks, NULL);
    }


    return err;
}

static t_flash_errs f_flash_init(t_flash_iface *iface) {
    /* на случай, если остались в режиме AAI, нужно его остановить через WRDI*/
    unsigned char cmd = SST25_CMD_WRDI;
    return flash_exec_cmd_only(iface, &cmd, 1, FLASH_FLAGS_FLUSH);
}

t_flash_errs flash_sst25_set(t_flash_iface *iface) {
    return flash_set(iface, &flash_info_sst25, NULL);
}


t_flash_errs flash_sst25_set_status(t_flash_iface *iface, unsigned char status) {
    int err;
    unsigned char cmd;

    cmd = SST25_CMD_EWSR;
    err = flash_exec_cmd(iface, &cmd, 1, NULL, 0, NULL, 0, 0);
    if (!err) {
        cmd = SST25_CMD_WRSR;
        err = flash_exec_cmd(iface, &cmd, 1, &status, 1, NULL, 0, FLASH_FLAGS_FLUSH);
    }
    return err;
}



t_flash_errs flash_sst25_write(t_flash_iface *iface, unsigned addr, const unsigned char *data,
                               size_t len, unsigned flags) {
    int err=0;

    /* Для скорости запись сделана с помощью режима AAI по 2 байта. В случае
     * невыровненого адреса или нечетного числа байт, первый и/или последний
     * байт записываются отдельно */

    if (addr & 1) {
        err = f_write_byte(iface, addr, *data);
        if (!err) {
            addr++;
            data++;
            len--;
        }
    }

    if (!err && (len>1)) {
        size_t wr_size = 2;
        unsigned char cmd;
        char first = 1;
        t_flash_errs dis_err;

        cmd = SST25_CMD_WREN;
        err = flash_exec_cmd_only(iface, &cmd, 1, 0);

        while (!err && (len>1)) {

            if (first) {
                err = flash_exec_cmd_addr(iface, SST25_CMD_AAI_WORD_PROGR, addr,
                                          0, data, NULL, wr_size, 0);
                first = 0;
            } else {
                cmd = SST25_CMD_AAI_WORD_PROGR;
                err = flash_exec_cmd(iface, &cmd, 1, data, wr_size, NULL, 0, 0);
            }

            if (!err) {
                err = flash_wait_ready_mks(iface, iface->flash_info->progr.prog_tout_mks, NULL);
            }
            if (!err) {
                data+= wr_size;
                len-=wr_size;
                addr+=(unsigned)wr_size;
            }
        }

        cmd = SST25_CMD_WRDI;
        dis_err = flash_exec_cmd_only(iface, &cmd, 1, 0);
        if (!err)
            err = dis_err;
    }


    if (!err && (len==1)) {
        err = f_write_byte(iface, addr, *data);
    }


    if (iface->flush) {
        t_flash_errs flush_err = iface->flush(iface);
        if (!err)
            err = flush_err;
    }


    return err;
}



