/*
 * eeprom_m24m01.c
 *
 *  Created on: 15/10/2021
 *      Author: alexrayne <alexraynepe196@gmail.com>
 * ----------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
 * ------------------------------------------------------------------
     STM M24M01 1Mbit I2C eeprom on i2c_masterx SSP
 */

#ifndef BSP_DEV_FLASH_EEPROM_M24M01_C_
#define BSP_DEV_FLASH_EEPROM_M24M01_C_

#include <memory.h>
#include "eeprom_m24m01.h"



/////////////////////////////////////////////////////////////////////////////////
#if !defined(RELEASE)
#include <assert.h>
#define ASSERT(...) assert(__VA_ARGS__)
#else
#define ASSERT(...)
#endif



/////////////////////////////////////////////////////////////////////////////
#include <trace_probes.h>

#ifndef trace_flash_rd
trace_need(flash_wr)
trace_need(flash_wrwe)
trace_need(flash_rd)
#endif



/////////////////////////////////////////////////////////////////////////////

//< размер данных,для которого предпочитается ожидать блокирующего завершения SSP транзакции,
//  для больших буферов - возвращается BUSY/ptWAITING, и ведется ожидание ниткой приложения
#ifndef SSP_TRANSACTION_WAIT_TH
#define SSP_TRANSACTION_WAIT_TH     8
#endif



const struct FlashChipDescribe   info_m24m01 = {
    struct_field(nb_sectors)          M24M01_SECTORS ,
    struct_field(nb_pages_in_sector)  M24M01_SEC_PAGES,
    struct_field(page_size)           M24M01_PAGE_SIZE ,
    struct_field(burn_page_ticks)     (FLASH_toYIELD*2) +CLOCKMS_MORE(5) ,
    struct_field(erase_ticks)         CLOCKMS_MORE(5) ,
    struct_field(erase_all_ticks)     CLOCKMS_MORE(5) ,
};

#define m24m01_state( ... ) flashi2c_state(__VA_ARGS__)

static
void m24m01_select_sla(FLASH_Device* self, uint8_t sla){
    I2CFlash_GenDevice* this = (I2CFlash_GenDevice*)self;
    sspio_set_cs(&this->ssp.io, sla);
}

static
bool m24m01_select_node(FLASH_Device* self, flash_page_t page){
    I2CFLASH_M24M01* this = (I2CFLASH_M24M01*)self;

    unsigned ni = page >> 16;
    uint8_t sla = this->nodes[ni];

    if (sla != M24M01_NOTHING){
        sspio_set_cs(&this->dev.ssp.io, sla);
        return true;
    }
    else
        return false;
}

DevResult m24m01_select_addr(FLASH_Device* self, flash_page_t page){
    if ( m24m01_select_node(self, page) )
        return DEV_OK;
    else
        return DEV_OUT_OF_SPACE_ERR;
}

FlashState m24m01_chip_state(FLASH_Device* self, uint8_t sla){
    m24m01_select_sla(self, sla);
    return m24m01_state(self);
}


DevResult m24m01_bind(FLASH_Device* this){
    I2CFLASH_M24M01* self = (I2CFLASH_M24M01*)this;

    if (self->dev.ssp.io.port == NULL)
        return DEV_NO_MEM_ERR;

    uint8_t*    node = self->nodes;
    for (unsigned bi = 0; bi < ARRAY_COUNT(self->nodes); ++bi, ++node){
        if (*node == M24M01_NOTHING) break;

        FlashState st = m24m01_chip_state(this, *node);
        if ( !flash_is_status(st) )
            return st;
        if ( !flash_is_ready(st) || flash_is_error(st) ) {
            return DEV_NO_SUPPORT_ID_ERR;
        }
    }
    unsigned    nodes = node - self->nodes;

    if(nodes == 0){
        // нет рабочих чипов
        return DEV_NO_MEM_ERR;
    }

    m24m01_select_node(this, 0);

    return DEV_OK;
}


DevResult m24m01_write_enable(FLASH_Device* self, bool onoff){
    self->status_cache= FLASH_sWELOCK | FLASH_sWE ;
    return DEV_OK;
}


PTResult  m24m01_read_banks(FLASH_Device* self, flash_addr_t addr, void* dst, unsigned len);


/// @arg banks - перечень чипов участвующих в банке
DevResult m24m01_init(I2CFLASH_M24M01* this, M24M01BankSet banks){

    flashi2c_init(&this->dev);
    this->dev.flash.status_cache= FLASH_sWELOCK | FLASH_sWE ;
    this->dev.flash.bind        = m24m01_bind;
    this->dev.flash.read        = m24m01_read_banks;
    this->dev.flash.write_enable= m24m01_write_enable;
    this->dev.flash.select_addr = m24m01_select_addr;

    SSP_IODevice* ssp = m24m01_ssp(this);
    sspio_set_mode(&ssp->io, sspio_mode_default_i2c | SSPI2C_TOUCH_WR);
    sspdev_style(ssp, SSPDEV_addr2BYTE | SSPDEV_addrLE);

    memset( this->nodes, M24M01_NOTHING, sizeof(this->nodes) );
    memset( &this->bank_info, 0, sizeof(this->bank_info) );

    // every chip(aka bank) have 2 nodes.
    uint8_t*    node = this->nodes;
    for (unsigned bi = 0; bi < 4;  ++bi, banks = banks>>1)
    if (banks & 1){
        uint8_t sla = M24M01_SLA | (bi<<1);
        *node++ = sla;
        *node++ = sla+1;
    }

    unsigned    nodes = node - this->nodes;
    if (nodes <= 0)
        // нет рабочих чипов
        return DEV_NO_MEM_ERR;

    struct FlashChipDescribe*    info = &this->bank_info;
    memcpy(info ,&info_m24m01, sizeof(info_m24m01) );
    info->nb_sectors = nodes << 8;
    flash_assign_info(&this->dev.flash, info);

    return DEV_OK;
}

void  m24m01_connect(I2CFLASH_M24M01* this, SSP_IOPort*    _port){
    sspio_connect(&(m24m01_ssp(this)->io), _port, sspio_mode_default_i2c | SSPI2C_TOUCH_WR, SSP_speedKEEP);
}


/// @return true - if addr ... + len places in one m24 chip bank
bool m24m01_in_single_bank(const FLASH_Device* this, flash_addr_t addr, unsigned len){
    //!!!WARN: этот код работает только со страницами размера 2^n
    ASSERT(this->info != NULL);
    unsigned mask = ~( (1u<<16) -1);
    return ( (addr&mask) == ((addr+len-1)&mask) );
}

PTResult  m24m01_read_banks(FLASH_Device* self, flash_addr_t addr, void* dst, unsigned len){
    if ( m24m01_in_single_bank(self, addr, len) ){
        return flashi2c_read_one(self, addr, dst, len);
    }

    return flashi2c_read_pages(self, addr, dst, len);

}



#endif /* BSP_DEV_FLASH_EEPROM_M24M01_C_ */
