/* SPIFI device-init for Chingis serial flashes 
   called by spifi_rom_init when JEDEC ID shows Chingis manufacturer code
   Copyright (C) NXP Seiconductors 2010 
   
   This file covers the following devices, though it may not 
     have been tested with all of them:
                Pm25LD256, Pm25LD512, Pm25LV010, Pm25LD010, Pm25LV020, Pm25LD020,
                Pm25LV040, Pm25LD040, Pm25LV080, Pm25LQ080, Pm25LV016, Pm25LQ016, 
                Pm25LQ032 */
 
#include "lpc43xx_spifi.h"
#include "spifi_sys_config.h"
#include "mfgers.h"
#include "error.h"
 
int chi (SPIFIobj *obj, unsigned options, unsigned unused) {
        uc type = obj->devType, id = obj->devID;
        unsigned sizeWidth; 
#define _DUAL 0x100
#define _QUAD 0x300
 
        if (type != 0x9D) return ERR_SPIFI_UNKNOWN_TYPE;
 
        if      (id >= 0x13 && id <= 0x14) sizeWidth = id; /* 0xF = 64KB */
        else if (id >= 0x1F && id <= 0x22) sizeWidth = id - 0x11 | _DUAL;
        else if (id == 0x2F)               sizeWidth = 0x0E      | _DUAL;
        else if (id >= 0x44 && id <= 0x46) sizeWidth = id - 0x31 | _QUAD;
        else if (id >= 0x7C && id <= 0x7E) sizeWidth = id - 0x6C;
        else return ERR_SPIFI_UNKNOWN_ID;
 
        /* erases from spifi_rom_api.c (20=4K, D8=64K) OK except <= 1Mb D8=32K
           protection STAT 1C is OK except quad >= 2Mb = 3C */
        if ((sizeWidth & 0x1F) <= 0x10) obj->erase_shifts[2] = 15;
 
        if (sizeWidth >= (0x14 | _QUAD)) {                      
                obj->write_prot = 0x3C;
                obj->set_prot   = 0xBC;
        } 
        setSize(obj, sizeWidth & 0x1F);
                
        /* change commands if device can do dual or quad */
        if (sizeWidth & _QUAD) {
                unsigned opts, read, prog;
                /* do dual if not a quad device or user is forcing dual */
                if (sizeWidth < _QUAD || options & S_DUAL) {
                        opts = OPT_DUAL | OPT_PROT_STAT;
                        read = (sizeWidth > _QUAD ? CMD_READ_DUAL_IO<<OPCODE_SHIFT | 2<<PS_SHIFT 
                                                  : CMD_READ_DUAL_O <<OPCODE_SHIFT | 1<<PS_SHIFT)
                                 | 1<<INTLEN_SHIFT | UNL_DATA;
                        /* no dual programming command */
                        prog = obj->prog_cmd;
                } else {
                        opts = OPT_05_OR40_01 | OPT_PROT_STAT;
                        read = CMD_READ_QUAD_IO<<OPCODE_SHIFT | 3<<INTLEN_SHIFT 
                             | 2<<PS_SHIFT | UNL_DATA;
                        prog = CMD_PROG_QUAD   <<OPCODE_SHIFT | 4<<FRAMEFORM_SHIFT 
                                 | 1<<PS_SHIFT | DOUT;
                }
                /* quad devices don't go busy on write status---v */
                return setDev(obj, opts | (sizeWidth > _QUAD ? OPT_01_NO_BUSY: 0),
                   /* no opcode mode on quad parts--------v */ 
                                      read | (sizeWidth > _QUAD ? 6 : 4)<<FRAMEFORM_SHIFT | UNL_DATA,
                                          prog);
        }
        return 0;
}
