/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 <assert.h>

#include "os/mynewt.h"
#include <bsp/bsp.h>
#include <hal/hal_spi.h>
#include <hal/hal_gpio.h>
#include <hal/hal_flash.h>
#include <hal/hal_flash_int.h>
#include <spiflash/spiflash.h>
#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
#include <bus/drivers/spi_common.h>
#endif

#if MYNEWT_VAL(SPIFLASH)

#if MYNEWT_VAL(SPIFLASH_STAT)
STATS_NAME_START(spiflash_stats_section)
    STATS_NAME(spiflash_stats_section, read_count)
    STATS_NAME(spiflash_stats_section, write_count)
    STATS_NAME(spiflash_stats_section, erase_count)
    STATS_NAME(spiflash_stats_section, error_count)
    STATS_NAME(spiflash_stats_section, read_bytes)
    STATS_NAME(spiflash_stats_section, written_bytes)
STATS_NAME_END(spiflash_stats_section)
#endif

#if MYNEWT_VAL(SPIFLASH_SPI_CS_PIN) < 0
#error SPIFLASH_SPI_CS_PIN must be set to the correct value in bsp syscfg.yml
#endif

#if MYNEWT_VAL(SPIFLASH_SECTOR_COUNT) == 0
#error SPIFLASH_SECTOR_COUNT must be set to the correct value in bsp syscfg.yml
#endif

#if MYNEWT_VAL(SPIFLASH_SECTOR_SIZE) == 0
#error SPIFLASH_SECTOR_SIZE must be set to the correct value in bsp syscfg.yml
#endif

#if MYNEWT_VAL(SPIFLASH_PAGE_SIZE) == 0
#error SPIFLASH_PAGE_SIZE must be set to the correct value in bsp syscfg.yml
#endif

#if MYNEWT_VAL(SPIFLASH_BAUDRATE) == 0
#error SPIFLASH_BAUDRATE must be set to the correct value in bsp syscfg.yml
#endif

static void spiflash_release_power_down_macronix(struct spiflash_dev *dev) __attribute__((unused));
static void spiflash_release_power_down_generic(struct spiflash_dev *dev) __attribute__((unused));

#define STD_FLASH_CHIP(name, mfid, typ, cap, release_power_down) \
    { \
        .fc_jedec_id = { \
            .ji_manufacturer = mfid, \
            .ji_type = typ, \
            .ji_capacity = cap, \
        }, \
        .fc_release_power_down = release_power_down, \
    }


#define ISSI_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_ISSI, typ, cap, spiflash_release_power_down_generic)
#define WINBOND_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_WINBOND, typ, cap, spiflash_release_power_down_generic)
#define MACRONIX_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_MACRONIX, typ, cap, spiflash_release_power_down_generic)
/* Macro for chips with no RPD command */
#define MACRONIX_CHIP1(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_MACRONIX, typ, cap, spiflash_release_power_down_macronix)
#define GIGADEVICE_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_GIGADEVICE, typ, cap, spiflash_release_power_down_generic)
#define MICRON_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_MICRON, typ, cap, spiflash_release_power_down_generic)
#define ADESTO_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_ADESTO, typ, cap, spiflash_release_power_down_generic)
#define EON_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_EON, typ, cap, spiflash_release_power_down_generic)
#define XTX_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_XTX, typ, cap, spiflash_release_power_down_generic)
#define PUYA_CHIP(name, typ, cap) \
    STD_FLASH_CHIP(name, JEDEC_MFC_PUYA, typ, cap, spiflash_release_power_down_generic)

static struct spiflash_chip supported_chips[] = {
#if MYNEWT_VAL(SPIFLASH_MANUFACTURER) && MYNEWT_VAL(SPIFLASH_MEMORY_TYPE) && MYNEWT_VAL(SPIFLASH_MEMORY_CAPACITY)
    STD_FLASH_CHIP("", MYNEWT_VAL(SPIFLASH_MANUFACTURER),
        MYNEWT_VAL(SPIFLASH_MEMORY_TYPE), MYNEWT_VAL(SPIFLASH_MEMORY_CAPACITY),
        spiflash_release_power_down_generic),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25D05C)
    GIGADEVICE_CHIP(GD25D05C, 0x40, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LD05C)
    GIGADEVICE_CHIP(GD25LD05C, 0x60, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LE05C)
    GIGADEVICE_CHIP(GD25LE05C, 0x60, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH05C)
    GIGADEVICE_CHIP(GD25LH05C, 0x60, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25VD05B)
    GIGADEVICE_CHIP(GD25VD05B, 0x40, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25WD05C)
    GIGADEVICE_CHIP(GD25WD05C, 0x64, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25D10C)
    GIGADEVICE_CHIP(GD25D10C, 0x40, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LD10C)
    GIGADEVICE_CHIP(GD25LD10C, 0x60, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LE10C)
    GIGADEVICE_CHIP(GD25LE10C, 0x60, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH10C)
    GIGADEVICE_CHIP(GD25LH10C, 0x60, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25VD10B)
    GIGADEVICE_CHIP(GD25VD10B, 0x40, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25WD10C)
    GIGADEVICE_CHIP(GD25WD10C, 0x64, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LE20C)
    GIGADEVICE_CHIP(GD25LE20C, 0x60, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH20C)
    GIGADEVICE_CHIP(GD25LH20C, 0x60, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25D20C)
    GIGADEVICE_CHIP(GD25D20C, 0x40, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LD20C)
    GIGADEVICE_CHIP(GD25LD20C, 0x60, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25VE20C)
    GIGADEVICE_CHIP(GD25VE20C, 0x42, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25WD20C)
    GIGADEVICE_CHIP(GD25WD20C, 0x64, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LE40C)
    GIGADEVICE_CHIP(GD25LE40C, 0x60, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH40C)
    GIGADEVICE_CHIP(GD25LH40C, 0x60, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25D40C)
    GIGADEVICE_CHIP(GD25D40C, 0x40, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LD40C)
    GIGADEVICE_CHIP(GD25LD40C, 0x60, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25WD40C)
    GIGADEVICE_CHIP(GD25WD40C, 0x64, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25VE40C)
    GIGADEVICE_CHIP(GD25VE40C, 0x42, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25VE40B)
    GIGADEVICE_CHIP(GD25VE40B, 0x60, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25D80C)
    GIGADEVICE_CHIP(GD25D80C, 0x40, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LD80C)
    GIGADEVICE_CHIP(GD25LD80C, 0x60, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LE80C)
    GIGADEVICE_CHIP(GD25LE80C, 0x60, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH80B)
    GIGADEVICE_CHIP(GD25LH80B, 0x60, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH80C)
    GIGADEVICE_CHIP(GD25LH80C, 0x60, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25WD80C)
    GIGADEVICE_CHIP(GD25WD80C, 0x64, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25Q80C)
    GIGADEVICE_CHIP(GD25Q80C, 0x40, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25B16C)
    GIGADEVICE_CHIP(GD25B16C, 0x40, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LE16C)
    GIGADEVICE_CHIP(GD25LE16C, 0x60, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25LH16C)
    GIGADEVICE_CHIP(GD25LH16C, 0x60, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25Q16C)
    GIGADEVICE_CHIP(GD25Q16C, 0x40, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_GD25VE16C)
    GIGADEVICE_CHIP(GD25VE16C, 0x42, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L512E)
    MACRONIX_CHIP(MX25L512E, 0x20, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L5121E)
    MACRONIX_CHIP(MX25L5121E, 0x22, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1021E)
    MACRONIX_CHIP(MX25L1021E, 0x22, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R512F)
    MACRONIX_CHIP1(MX25R512F, 0x28, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U5121E)
    MACRONIX_CHIP(MX25U5121E, 0x25, 0x20 | FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U1001E)
    MACRONIX_CHIP(MX25U1001E, 0x25, 0x20 | FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V512E)
    MACRONIX_CHIP(MX25V512E, 0x20, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V512F)
    MACRONIX_CHIP1(MX25V512F, 0x23, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1006E)
    MACRONIX_CHIP(MX25L1006E, 0x20, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1026E)
    MACRONIX_CHIP(MX25L1026E, 0x20, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R1035F)
    MACRONIX_CHIP1(MX25R1035F, 0x28, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V1006E)
    MACRONIX_CHIP(MX25V1006E, 0x20, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V1006F)
    MACRONIX_CHIP(MX25V1006F, 0x20, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V1035F)
    MACRONIX_CHIP1(MX25V1035F, 0x23, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L2006E)
    MACRONIX_CHIP(MX25L2006E, 0x20, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L2026E)
    MACRONIX_CHIP(MX25L2026E, 0x20, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R2035F)
    MACRONIX_CHIP1(MX25R2035F, 0x28, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U2032E)
    MACRONIX_CHIP(MX25U2032E, 0x25, 0x20 | FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U2033E)
    MACRONIX_CHIP(MX25U2033E, 0x25, 0x20 | FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U2035F)
    MACRONIX_CHIP1(MX25U2035F, 0x25, 0x20 | FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V2006E)
    MACRONIX_CHIP(MX25V2006E, 0x20, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V2033F)
    MACRONIX_CHIP(MX25V2033F, 0x20, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V2035F)
    MACRONIX_CHIP1(MX25V2035F, 0x23, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L4006E)
    MACRONIX_CHIP(MX25L4006E, 0x20, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L4026E)
    MACRONIX_CHIP(MX25L4026E, 0x20, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R4035F)
    MACRONIX_CHIP1(MX25R4035F, 0x28, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U4032E)
    MACRONIX_CHIP(MX25U4032E, 0x25, 0x20 | FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U4033E)
    MACRONIX_CHIP(MX25U4033E, 0x25, 0x20 | FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U4035)
    MACRONIX_CHIP(MX25U4035, 0x25, 0x20 | FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U4035F)
    MACRONIX_CHIP1(MX25U4035F, 0x25, 0x20 | FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V4006E)
    MACRONIX_CHIP(MX25V4006E, 0x20, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V4035F)
    MACRONIX_CHIP1(MX25V4035F, 0x23, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U8035)
    MACRONIX_CHIP(MX25U8035, 0x25, 0x20 | FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L8006E)
    MACRONIX_CHIP(MX25L8006E, 0x20, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L8008E)
    MACRONIX_CHIP(MX25L8008E, 0x20, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L8035E)
    MACRONIX_CHIP(MX25L8035E, 0x20, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L8036E)
    MACRONIX_CHIP(MX25L8036E, 0x20, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L8073E)
    MACRONIX_CHIP(MX25L8073E, 0x20, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R8035F)
    MACRONIX_CHIP1(MX25R8035F, 0x28, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U8032E)
    MACRONIX_CHIP(MX25U8032E, 0x25, 0x20 | FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U8033E)
    MACRONIX_CHIP(MX25U8033E, 0x25, 0x20 | FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U8035E)
    MACRONIX_CHIP(MX25U8035E, 0x25, 0x20 | FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U8035F)
    MACRONIX_CHIP1(MX25U8035F, 0x25, 0x20 | FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V8006E)
    MACRONIX_CHIP(MX25V8006E, 0x20, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V8033F)
    MACRONIX_CHIP1(MX25V8033F, 0x23, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V8035F)
    MACRONIX_CHIP1(MX25V8035F, 0x23, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1606E)
    MACRONIX_CHIP(MX25L1606E, 0x20, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1608E)
    MACRONIX_CHIP(MX25L1608E, 0x20, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1633E)
    MACRONIX_CHIP(MX25L1633E, 0x24, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1635E)
    MACRONIX_CHIP(MX25L1635E, 0x25, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1636E)
    MACRONIX_CHIP(MX25L1636E, 0x25, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L1673E)
    MACRONIX_CHIP(MX25L1673E, 0x24, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R1635F)
    MACRONIX_CHIP1(MX25R1635F, 0x28, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U1633F)
    MACRONIX_CHIP1(MX25U1633F, 0x25, 0x20 | FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U1635E)
    MACRONIX_CHIP(MX25U1635E, 0x25, 0x20 | FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U1635F)
    MACRONIX_CHIP(MX25U1635F, 0x25, 0x20 | FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25V1635F)
    MACRONIX_CHIP1(MX25V1635F, 0x23, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3206E)
    MACRONIX_CHIP(MX25L3206E, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3208E)
    MACRONIX_CHIP(MX25L3208E, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3233F)
    MACRONIX_CHIP(MX25L3233F, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3235E)
    MACRONIX_CHIP(MX25L3235E, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3236F)
    MACRONIX_CHIP(MX25L3236F, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3239E)
    MACRONIX_CHIP(MX25L3239E, 0x25, 0x20 | FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3273E)
    MACRONIX_CHIP(MX25L3273E, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25L3273F)
    MACRONIX_CHIP(MX25L3273F, 0x20, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25R3235F)
    MACRONIX_CHIP1(MX25R3235F, 0x28, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U3235E)
    MACRONIX_CHIP(MX25U3235E, 0x25, 0x20 | FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U3235F)
    MACRONIX_CHIP(MX25U3235F, 0x25, 0x20 | FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_MX25U3273F)
    MACRONIX_CHIP1(MX25U3273F, 0x25, 0x20 | FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LP080D)
    ISSI_CHIP(IS25LP080D, 0x60, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WP080D)
    ISSI_CHIP(IS25WP080D, 0x70, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WP040D)
    ISSI_CHIP(IS25WP040D, 0x70, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WP020D)
    ISSI_CHIP(IS25WP020D, 0x70, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LQ040B)
    ISSI_CHIP(IS25LQ040B, 0x40, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LQ020B)
    ISSI_CHIP(IS25LQ020B, 0x40, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LQ010B)
    ISSI_CHIP(IS25LQ010B, 0x40, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LQ512B)
    ISSI_CHIP(IS25LQ512B, 0x40, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WQ040)
    ISSI_CHIP(IS25WQ040, 0x12, 0x4 | FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WQ020)
    ISSI_CHIP(IS25WQ020, 0x11, 0x4 | FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LQ025B)
    ISSI_CHIP(IS25LQ025B, 0x40, FLASH_CAPACITY_256KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LP016D)
    ISSI_CHIP(IS25LP016D, 0x60, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WP016D)
    ISSI_CHIP(IS25WP016D, 0x70, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LP032D)
    ISSI_CHIP(IS25LP032D, 0x60, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25WP032D)
    ISSI_CHIP(IS25WP032D, 0x70, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_IS25LP064A)
    ISSI_CHIP(IS25LP064A, 0x60, FLASH_CAPACITY_64MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25X05CL)
    WINBOND_CHIP(W25X05CL, 0x30, FLASH_CAPACITY_512KBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q10EW)
    WINBOND_CHIP(W25Q10EW, 0x60, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25X10CL)
    WINBOND_CHIP(W25X10CL, 0x30, FLASH_CAPACITY_1MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q20CL)
    WINBOND_CHIP(W25Q20CL, 0x40, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q20EW)
    WINBOND_CHIP(W25Q20EW, 0x60, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25X20CL)
    WINBOND_CHIP(W25X20CL, 0x30, FLASH_CAPACITY_2MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q40CL)
    WINBOND_CHIP(W25Q40CL, 0x40, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q40EW)
    WINBOND_CHIP(W25Q40EW, 0x60, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25X40CL)
    WINBOND_CHIP(W25X40CL, 0x30, FLASH_CAPACITY_4MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q80DV)
    WINBOND_CHIP(W25Q80DV, 0x40, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q80DL)
    WINBOND_CHIP(W25Q80DL, 0x40, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q80EW)
    WINBOND_CHIP(W25Q80EW, 0x60, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16DV)
    WINBOND_CHIP(W25Q16DV, 0x40, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16DW)
    WINBOND_CHIP(W25Q16DW, 0x60, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16FW)
    WINBOND_CHIP(W25Q16FW, 0x60, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16JL)
    WINBOND_CHIP(W25Q16JL, 0x40, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16JV_DTR)
    WINBOND_CHIP(W25Q16JV_DTR, 0x70, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16JV_IQ)
    WINBOND_CHIP(W25Q16JV_IQ, 0x40, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q16JV_IM)
    WINBOND_CHIP(W25Q16JV_IM, 0x70, FLASH_CAPACITY_16MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32BV)
    WINBOND_CHIP(W25Q32BV, 0x40, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32FV)
    WINBOND_CHIP(W25Q32FV, 0x40, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32FW)
    WINBOND_CHIP(W25Q32FW, 0x60, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32JV)
    WINBOND_CHIP(W25Q32JV, 0x70, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32JV_IQ)
    WINBOND_CHIP(W25Q32JV_IQ, 0x40, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32JW)
    WINBOND_CHIP(W25Q32JW, 0x80, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q32JW_IQ)
    WINBOND_CHIP(W25Q32JW_IQ, 0x60, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q64JV)
    WINBOND_CHIP(W25Q64JV, 0x40, FLASH_CAPACITY_64MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_W25Q128JV)
    WINBOND_CHIP(W25Q128JV, 0x40, FLASH_CAPACITY_128MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25SF041)
    ADESTO_CHIP(AT25SF041, 0x84, 1),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25SF081)
    ADESTO_CHIP(AT25SF081, 0x85, 1),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25DF081A)
    ADESTO_CHIP(AT25DF081A, 0x45, 1),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25DL081)
    ADESTO_CHIP(AT25DL081, 0x45, 2),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25SF161)
    ADESTO_CHIP(AT25SF161, 0x86, 1),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25DL161)
    ADESTO_CHIP(AT25DL161, 0x46, 3),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25SL321)
    ADESTO_CHIP(AT25SL321, 0x42, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25SF321)
    ADESTO_CHIP(AT25SF321, 0x87, 1),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25DF321A)
    ADESTO_CHIP(AT25DF321A, 0x47, 1),
#endif
#if MYNEWT_VAL(SPIFLASH_AT25QL321)
    ADESTO_CHIP(AT25QL321, 0x42, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_EON2580B)
    EON_CHIP(EN80B, 0x30, FLASH_CAPACITY_8MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_XT25F32B)
    XTX_CHIP(XT25F32B, 0x40, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_XM25QH32B)
    MICRON_CHIP(XM25QH32B, 0x40, FLASH_CAPACITY_32MBIT),
#endif
#if MYNEWT_VAL(SPIFLASH_P25Q32H)
    PUYA_CHIP(P25Q32H, 0x60, FLASH_CAPACITY_32MBIT),
#endif

    { {0} },
};

static int hal_spiflash_read(const struct hal_flash *hal_flash_dev, uint32_t addr,
        void *buf, uint32_t len);
static int hal_spiflash_write(const struct hal_flash *hal_flash_dev, uint32_t addr,
        const void *buf, uint32_t len);
static int hal_spiflash_erase_sector(const struct hal_flash *hal_flash_dev,
        uint32_t sector_address);
static int hal_spiflash_sector_info(const struct hal_flash *hal_flash_dev, int idx,
        uint32_t *address, uint32_t *sz);
static int hal_spiflash_init(const struct hal_flash *dev);
static int hal_spiflash_erase(const struct hal_flash *hal_flash_dev,
        uint32_t address, uint32_t sz);

static const struct hal_flash_funcs spiflash_flash_funcs = {
    .hff_read         = hal_spiflash_read,
    .hff_write        = hal_spiflash_write,
    .hff_erase_sector = hal_spiflash_erase_sector,
    .hff_sector_info  = hal_spiflash_sector_info,
    .hff_init         = hal_spiflash_init,
    .hff_erase        = hal_spiflash_erase,
};

static const struct spiflash_characteristics spiflash_characteristics = {
    .tse = {
        .typical = MYNEWT_VAL(SPIFLASH_TSE_TYPICAL),
        .maximum = MYNEWT_VAL(SPIFLASH_TSE_MAXIMUM)
    },
    .tbe1 = {
        .typical = MYNEWT_VAL(SPIFLASH_TBE1_TYPICAL),
        .maximum = MYNEWT_VAL(SPIFLASH_TBE1_MAXIMUM)
    },
    .tbe2 = {
        .typical = MYNEWT_VAL(SPIFLASH_TBE2_TYPICAL),
        .maximum = MYNEWT_VAL(SPIFLASH_TBE2_MAXIMUM)
    },
    .tce = {
        .typical = MYNEWT_VAL(SPIFLASH_TCE_TYPICAL),
        .maximum = MYNEWT_VAL(SPIFLASH_TCE_MAXIMUM)
    },
    .tpp = {
        .typical = MYNEWT_VAL(SPIFLASH_TPP_TYPICAL),
        .maximum = MYNEWT_VAL(SPIFLASH_TPP_MAXIMUM)
    },
    .tbp1 = {
        .typical = MYNEWT_VAL(SPIFLASH_TBP1_TYPICAL),
        .maximum = MYNEWT_VAL(SPIFLASH_TBP1_MAXIMUM)
    },
};

struct spiflash_dev spiflash_dev = {
    /* struct hal_flash for compatibility */
    .hal = {
        .hf_itf        = &spiflash_flash_funcs,
        .hf_base_addr  = 0,
        .hf_size       = MYNEWT_VAL(SPIFLASH_SECTOR_COUNT) *
                         MYNEWT_VAL(SPIFLASH_SECTOR_SIZE),
        .hf_sector_cnt = MYNEWT_VAL(SPIFLASH_SECTOR_COUNT),
        .hf_align      = 1,
        .hf_erased_val = 0xff,
    },

#if !MYNEWT_VAL(BUS_DRIVER_PRESENT)
    /* SPI settings */
    .spi_settings = {
        .data_order = HAL_SPI_MSB_FIRST,
        .data_mode  = MYNEWT_VAL(SPIFLASH_SPI_MODE),
        .baudrate   = MYNEWT_VAL(SPIFLASH_BAUDRATE),
        .word_size  = HAL_SPI_WORD_SIZE_8BIT,
    },
    .spi_num            = MYNEWT_VAL(SPIFLASH_SPI_NUM),
    .spi_cfg            = NULL,
    .ss_pin             = MYNEWT_VAL(SPIFLASH_SPI_CS_PIN),
#endif
    .sector_size        = MYNEWT_VAL(SPIFLASH_SECTOR_SIZE),
    .page_size          = MYNEWT_VAL(SPIFLASH_PAGE_SIZE),

    .supported_chips = supported_chips,
    .characteristics = &spiflash_characteristics,
    .flash_chip = NULL,
};

static inline void spiflash_lock_no_apd(struct spiflash_dev *dev)
{
#if MYNEWT_VAL(OS_SCHEDULING)
    os_mutex_pend(&dev->lock, OS_TIMEOUT_NEVER);
#endif
}

static inline void spiflash_unlock_no_apd(struct spiflash_dev *dev)
{
#if MYNEWT_VAL(OS_SCHEDULING)
    os_mutex_release(&dev->lock);
#endif
}

static inline void spiflash_lock(struct spiflash_dev *dev)
{
#if MYNEWT_VAL(OS_SCHEDULING)
    os_mutex_pend(&dev->lock, OS_TIMEOUT_NEVER);
#endif

#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN)
    if (dev->pd_active) {
        spiflash_release_power_down(dev);
    } else {
#if MYNEWT_VAL(OS_SCHEDULING)
        if (os_mutex_get_level(&dev->lock) == 1) {
            os_callout_stop(&dev->apd_tmo_co);
        }
#endif
    }
#endif
}

static inline void spiflash_unlock(struct spiflash_dev *dev)
{
#if MYNEWT_VAL(OS_SCHEDULING)
#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN)
    if (dev->apd_tmo && !dev->pd_active &&
        (os_mutex_get_level(&dev->lock) == 1)) {
        os_callout_reset(&dev->apd_tmo_co, dev->apd_tmo);
    }
#endif

    os_mutex_release(&dev->lock);
#endif
}

static inline void
spiflash_cs_activate(struct spiflash_dev *dev)
{
#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    struct bus_spi_node *node = (struct bus_spi_node *)&dev->dev;
    hal_gpio_write(node->pin_cs, 0);
#else
    hal_gpio_write(dev->ss_pin, 0);
#endif
}

static inline void
spiflash_cs_deactivate(struct spiflash_dev *dev)
{
#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    struct bus_spi_node *node = &dev->dev;
    hal_gpio_write(node->pin_cs, 1);
#else
    hal_gpio_write(dev->ss_pin, 1);
#endif
}

void
spiflash_power_down(struct spiflash_dev *dev)
{
    uint8_t cmd[1] = { SPIFLASH_DEEP_POWER_DOWN };

    spiflash_lock_no_apd(dev);

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    bus_node_simple_write((struct os_dev *)&dev->dev, cmd, sizeof(cmd));
#else
    spiflash_cs_activate(dev);

    hal_spi_txrx(dev->spi_num, cmd, cmd, sizeof cmd);

    spiflash_cs_deactivate(dev);
#endif

#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN)
    dev->pd_active = true;
#endif
    dev->ready = false;

    spiflash_unlock_no_apd(dev);
}

/*
 * Some Macronix chips don't have standard release power down command 0xAB.
 * Instead they use CS pin alone to wake up from sleep.
 */
static void
spiflash_release_power_down_macronix(struct spiflash_dev *dev)
{
    spiflash_cs_activate(dev);

    os_cputime_delay_usecs(20);

    spiflash_cs_deactivate(dev);
}

static void
spiflash_release_power_down_generic(struct spiflash_dev *dev)
{
    uint8_t cmd[1] = { SPIFLASH_RELEASE_POWER_DOWN };

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    bus_node_simple_write((struct os_dev *)&dev->dev, cmd, sizeof(cmd));
#else
    spiflash_cs_activate(dev);

    hal_spi_txrx(dev->spi_num, cmd, cmd, sizeof cmd);

    spiflash_cs_deactivate(dev);
#endif
}

void
spiflash_release_power_down(struct spiflash_dev *dev)
{
    spiflash_lock_no_apd(dev);

    if (dev->flash_chip->fc_release_power_down) {
        dev->flash_chip->fc_release_power_down(dev);
    }

#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN)
    dev->pd_active = false;
#endif

    spiflash_unlock_no_apd(dev);
}

int
spiflash_auto_power_down_set(struct spiflash_dev *dev, uint32_t timeout_ms)
{
#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN) && MYNEWT_VAL(OS_SCHEDULING)
    /*
     * Lock with no auto power down since we do not want to wake up flash here,
     * it will be done if APD is disabled and power down is active. Then unlock
     * with APD to make sure it's applied if needed.
     */

    spiflash_lock_no_apd(dev);

    dev->apd_tmo = os_time_ms_to_ticks32(timeout_ms);

    if (!dev->apd_tmo && dev->pd_active) {
        spiflash_release_power_down_generic(dev);
    }

    spiflash_unlock(dev);

    return 0;
#else
    /* Not supported */
    return -1;
#endif
}

#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN)
static void
spiflash_apd_tmo_func(struct os_event *ev)
{
    struct spiflash_dev *dev = ev->ev_arg;

    spiflash_lock_no_apd(dev);

    if (dev->apd_tmo && !dev->pd_active) {
        spiflash_power_down(dev);
    }

    spiflash_unlock_no_apd(dev);
}
#endif

uint8_t
spiflash_read_jedec_id(struct spiflash_dev *dev,
        uint8_t *manufacturer, uint8_t *memory_type, uint8_t *capacity)
{
    uint8_t cmd[4] = { SPIFLASH_READ_JEDEC_ID, 0, 0, 0 };

    spiflash_lock(dev);

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    bus_node_simple_write_read_transact((struct os_dev *)&dev->dev,
        cmd, 1, cmd + 1, 3);
#else
    spiflash_cs_activate(dev);

    hal_spi_txrx(dev->spi_num, cmd, cmd, sizeof cmd);

    spiflash_cs_deactivate(dev);
#endif

    if (manufacturer) {
        *manufacturer = cmd[1];
    }

    if (memory_type) {
        *memory_type = cmd[2];
    }

    if (capacity) {
        *capacity = cmd[3];
    }

    spiflash_unlock(dev);

    return 0;
}

uint8_t
spiflash_read_status(struct spiflash_dev *dev)
{
    uint8_t val;
    const uint8_t cmd = SPIFLASH_READ_STATUS_REGISTER;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    bus_node_simple_write_read_transact((struct os_dev *)&dev->dev,
        &cmd, 1, &val, 1);
#else
    spiflash_lock(dev);

    spiflash_cs_activate(dev);

    hal_spi_tx_val(dev->spi_num, cmd);
    val = hal_spi_tx_val(dev->spi_num, 0xFF);

    spiflash_cs_deactivate(dev);

    spiflash_unlock(dev);
#endif

    return val;
}

static void
spiflash_delay_us(uint32_t usecs)
{
#if MYNEWT_VAL(OS_SCHEDULING)
    uint32_t ticks = os_time_ms_to_ticks32(usecs / 1000);
    if (ticks > 1) {
        os_time_delay(ticks);
    } else {
        os_cputime_delay_usecs(usecs);
    }
#else
    os_cputime_delay_usecs(usecs);
#endif
}

bool
spiflash_device_ready(struct spiflash_dev *dev)
{
    dev->ready = !(spiflash_read_status(dev) & SPIFLASH_STATUS_BUSY);

    return dev->ready;
}

static int
spiflash_wait_ready_till(struct spiflash_dev *dev, uint32_t timeout_us,
    uint32_t step_us)
{
    int rc = -1;
    uint32_t limit;

    if (dev->ready) {
        return 0;
    }

    if (step_us < MYNEWT_VAL(SPIFLASH_READ_STATUS_INTERVAL)) {
        step_us = MYNEWT_VAL(SPIFLASH_READ_STATUS_INTERVAL);
    } else if (step_us > 1000000) {
        /* Read status once per second max */
        step_us = 1000000;
    }

    spiflash_lock(dev);

    limit = os_cputime_get32() + os_cputime_usecs_to_ticks(timeout_us);
    do {
        if (spiflash_device_ready(dev)) {
            rc = 0;
            break;
        }
        spiflash_delay_us(step_us);
    } while (CPUTIME_LT(os_cputime_get32(), limit));

    spiflash_unlock(dev);

    return rc;
}

int
spiflash_wait_ready(struct spiflash_dev *dev, uint32_t timeout_ms)
{
    /*
     * Timeout is in ms, check status register 100 times in this time.
     * If it would be shorter time than SPIFLASH_READ_STATUS_INTERVAL
     * number of timer status register is checked will be smaler.
     */
    return spiflash_wait_ready_till(dev, timeout_ms * 1000, timeout_ms * 10);
}

int
spiflash_write_enable(struct spiflash_dev *dev)
{
    uint8_t cmd = SPIFLASH_WRITE_ENABLE;
    spiflash_lock(dev);

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    bus_node_simple_write((struct os_dev *)&dev->dev, &cmd, 1);
#else
    spiflash_cs_activate(dev);

    hal_spi_tx_val(dev->spi_num, cmd);

    spiflash_cs_deactivate(dev);
#endif

    spiflash_unlock(dev);

    return 0;
}

static int
hal_spiflash_read(const struct hal_flash *hal_flash_dev, uint32_t addr, void *buf,
                  uint32_t len)
{
    int err = 0;
#if MYNEWT_VAL(SPIFLASH_CACHE_SIZE)
    uint32_t cached_size;
    uint8_t *user_buf;
    uint32_t left;
#endif
    uint8_t cmd[] = { SPIFLASH_READ,
        (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)(addr) };
    struct spiflash_dev *dev;

    dev = (struct spiflash_dev *)hal_flash_dev;

    spiflash_lock(dev);

    SPIFLASH_STATS_INC(dev->stats, read_count);

    err = spiflash_wait_ready(dev, 100);
    if (!err) {
#if MYNEWT_VAL(SPIFLASH_CACHE_SIZE)
        if ((dev->cached_addr <= addr) &&
            (addr < dev->cached_addr + MYNEWT_VAL(SPIFLASH_CACHE_SIZE))) {
            /* Check if everything is cached */
            if (len < MYNEWT_VAL(SPIFLASH_CACHE_SIZE) -
                      (addr - dev->cached_addr)) {
                /* Everything  was cached */
                memcpy(buf, dev->cache + addr - dev->cached_addr, len);
                len = 0;
            } else {
                /* Some data was cached */
                cached_size = MYNEWT_VAL(SPIFLASH_CACHE_SIZE) -
                    (addr - dev->cached_addr);
                memcpy(buf, dev->cache + addr - dev->cached_addr, cached_size);
                len -= cached_size;
                buf = (uint8_t *)buf + cached_size;
                addr += cached_size;
                cmd[1] = (uint8_t)(addr >> 16);
                cmd[2] = (uint8_t)(addr >> 8);
                cmd[3] = (uint8_t)(addr);
            }
        }
        left = len;
        user_buf = buf;
        /*
         * In case small amount of data was requested use cache buffer for
         * reading, instead of caller buffer that would be to small.
         */
        if (len > 0 && len < MYNEWT_VAL(SPIFLASH_CACHE_SIZE)) {
            len = MYNEWT_VAL(SPIFLASH_CACHE_SIZE);
            buf = dev->cache;
        }
#endif
        if (len > 0) {
#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
            bus_node_simple_write_read_transact((struct os_dev *)&dev->dev,
                &cmd, 4, buf, len);
#else
            spiflash_cs_activate(dev);

            /* Send command + address */
            hal_spi_txrx(dev->spi_num, cmd, NULL, sizeof cmd);
            /* For security mostly, do not output random data, fill it with FF */
            memset(buf, 0xFF, len);
            /* Tx buf does not matter, for simplicity pass read buffer */
            hal_spi_txrx(dev->spi_num, buf, buf, len);

            spiflash_cs_deactivate(dev);
#endif
#if MYNEWT_VAL(SPIFLASH_CACHE_SIZE)
            /* If buffer was too small, cache was used for reading */
            if (left < MYNEWT_VAL(SPIFLASH_CACHE_SIZE)) {
                memcpy(user_buf, dev->cache, left);
                dev->cached_addr = addr;
            } else {
                /* Read was done directly to user buffer, copy end to cache */
                dev->cached_addr = addr + left - MYNEWT_VAL(SPIFLASH_CACHE_SIZE);
                memcpy(dev->cache, (uint8_t *)buf + left -
                       MYNEWT_VAL(SPIFLASH_CACHE_SIZE),
                       MYNEWT_VAL(SPIFLASH_CACHE_SIZE));
            }
#endif
            SPIFLASH_STATS_INCN(dev->stats, read_bytes, len);
        }
    }

    spiflash_unlock(dev);

    return 0;
}

static int
hal_spiflash_write(const struct hal_flash *hal_flash_dev, uint32_t addr,
        const void *buf, uint32_t len)
{
    uint8_t cmd[4] = { SPIFLASH_PAGE_PROGRAM };
    const uint8_t *u8buf = buf;
    struct spiflash_dev *dev = (struct spiflash_dev *)hal_flash_dev;
    uint32_t page_limit;
    uint32_t to_write;
    uint32_t pp_time_typical;
    uint32_t pp_time_maximum;
    int rc = 0;

    u8buf = (uint8_t *)buf;

    spiflash_lock(dev);

    if (spiflash_wait_ready(dev, 100) != 0) {
        rc = -1;
        goto err;
    }

#if MYNEWT_VAL(SPIFLASH_CACHE_SIZE)
    dev->cached_addr = 0xFFFFFFFF;
#endif

    pp_time_typical = dev->characteristics->tbp1.typical;
    pp_time_maximum = dev->characteristics->tpp.maximum;
    if (pp_time_maximum < pp_time_typical) {
        pp_time_maximum = pp_time_typical;
    }

    SPIFLASH_STATS_INC(dev->stats, write_count);

    while (len) {
        spiflash_write_enable(dev);

        cmd[1] = (uint8_t)(addr >> 16);
        cmd[2] = (uint8_t)(addr >> 8);
        cmd[3] = (uint8_t)(addr);

        page_limit = (addr & ~(dev->page_size - 1)) + dev->page_size;
        to_write = page_limit - addr > len ? len :  page_limit - addr;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
        bus_node_lock((struct os_dev *)&dev->dev,
            BUS_NODE_LOCK_DEFAULT_TIMEOUT);
        bus_node_write((struct os_dev *)&dev->dev,
            cmd, 4, BUS_NODE_LOCK_DEFAULT_TIMEOUT, BUS_F_NOSTOP);
        bus_node_simple_write((struct os_dev *)&dev->dev, u8buf, to_write);
        bus_node_unlock((struct os_dev *)&dev->dev);
#else
        spiflash_cs_activate(dev);
        hal_spi_txrx(dev->spi_num, cmd, NULL, sizeof cmd);
        hal_spi_txrx(dev->spi_num, (void *)u8buf, NULL, to_write);
        spiflash_cs_deactivate(dev);
#endif
        /* Now we know that device is not ready */
        dev->ready = false;
        spiflash_delay_us(pp_time_typical);
        rc = spiflash_wait_ready_till(dev, pp_time_maximum - pp_time_typical,
            (pp_time_maximum - pp_time_typical) / 10);
        if (rc) {
            break;
        }
        addr += to_write;
        u8buf += to_write;
        len -= to_write;
        SPIFLASH_STATS_INCN(dev->stats, written_bytes, to_write);
    }
err:
    if (rc) {
        SPIFLASH_STATS_INC(dev->stats, error_count);
    }
    spiflash_unlock(dev);

    return rc;
}

static int
hal_spiflash_erase_sector(const struct hal_flash *hal_flash_dev, uint32_t addr)
{
    struct spiflash_dev *dev = (struct spiflash_dev *)hal_flash_dev;

    return spiflash_sector_erase(dev, addr);
}

static int
hal_spiflash_erase(const struct hal_flash *hal_flash_dev,
    uint32_t address, uint32_t size)
{
    struct spiflash_dev *dev = (struct spiflash_dev *)hal_flash_dev;

    return spiflash_erase(dev, address, size);
}

static int
spiflash_execute_erase(struct spiflash_dev *dev, const uint8_t *buf,
                       uint32_t size,
                       const struct spiflash_time_spec *delay_spec)
{
    int rc = 0;
    uint32_t wait_time_us;
    uint32_t start_time;

    spiflash_lock(dev);

#if MYNEWT_VAL(SPIFLASH_CACHE_SIZE)
    dev->cached_addr = 0xFFFFFFFF;
#endif

    if (spiflash_wait_ready(dev, 100) != 0) {
        rc = -1;
        goto err;
    }

    spiflash_write_enable(dev);

    spiflash_read_status(dev);

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    bus_node_simple_write((struct os_dev *)&dev->dev, buf, (uint16_t)size);
#else
    spiflash_cs_activate(dev);

    hal_spi_txrx(dev->spi_num, (void *)buf, NULL, size);

    spiflash_cs_deactivate(dev);
#endif
    /* Now we know that device is not ready */
    dev->ready = false;

    start_time = os_cputime_get32();
    /* Wait typical erase time before starting polling for ready */
    spiflash_delay_us(delay_spec->typical);

    wait_time_us = os_cputime_ticks_to_usecs(os_cputime_get32() - start_time);
    if (wait_time_us > delay_spec->maximum) {
        wait_time_us = 0;
    } else {
        wait_time_us = delay_spec->maximum - wait_time_us;
    }

    /* Poll status ready for remaining time */
    rc = spiflash_wait_ready_till(dev, wait_time_us, wait_time_us / 50);
err:
    spiflash_unlock(dev);

    return rc;
}

static int
hal_spiflash_sector_info(const struct hal_flash *hal_flash_dev, int idx,
        uint32_t *address, uint32_t *sz)
{
    const struct spiflash_dev *dev = (const struct spiflash_dev *)hal_flash_dev;

    *address = idx * dev->sector_size;
    *sz = dev->sector_size;
    return 0;
}

static int
spiflash_erase_cmd(struct spiflash_dev *dev, uint8_t cmd, uint32_t addr,
                   const struct spiflash_time_spec *time_spec)
{
    uint8_t buf[4] = { cmd, (uint8_t)(addr >> 16U), (uint8_t)(addr >> 8U),
                       (uint8_t)addr };
    return spiflash_execute_erase(dev, buf, sizeof(buf), time_spec);

}

int
spiflash_sector_erase(struct spiflash_dev *dev, uint32_t addr)
{
    return spiflash_erase_cmd(dev, SPIFLASH_SECTOR_ERASE, addr,
                              &dev->characteristics->tse);
}

#if MYNEWT_VAL(SPIFLASH_BLOCK_ERASE_32BK)
int
spiflash_block_32k_erase(struct spiflash_dev *dev, uint32_t addr)
{
    return spiflash_erase_cmd(dev, SPIFLASH_BLOCK_ERASE_32KB, addr,
                              &dev->characteristics->tbe1);
}
#endif

#if MYNEWT_VAL(SPIFLASH_BLOCK_ERASE_64BK)
int
spiflash_block_64k_erase(struct spiflash_dev *dev, uint32_t addr)
{
    return spiflash_erase_cmd(dev, SPIFLASH_BLOCK_ERASE_64KB, addr,
                              &dev->characteristics->tbe2);
}
#endif

int
spiflash_chip_erase(struct spiflash_dev *dev)
{
    uint8_t buf[1] = { SPIFLASH_CHIP_ERASE };

    return spiflash_execute_erase(dev, buf, sizeof(buf),
                                  &dev->characteristics->tce);
}

int
spiflash_erase(struct spiflash_dev *dev, uint32_t address, uint32_t size)
{
    int rc = 0;

    if (address == 0 && size == dev->hal.hf_size) {
        SPIFLASH_STATS_INC(dev->stats, erase_count);
        return spiflash_chip_erase(dev);
    }
    address &= ~0xFFFU;
    while (size) {
        SPIFLASH_STATS_INC(dev->stats, erase_count);
#if MYNEWT_VAL(SPIFLASH_BLOCK_ERASE_64BK)
        if ((address & 0xFFFFU) == 0 && (size >= 0x10000)) {
            /* 64 KB erase if possible */
            rc = spiflash_block_64k_erase(dev, address);
            if (rc) {
                goto err;
            }
            address += 0x10000;
            size -= 0x10000;
            continue;
        }
#endif
#if MYNEWT_VAL(SPIFLASH_BLOCK_ERASE_32BK)
        if ((address & 0x7FFFU) == 0 && (size >= 0x8000)) {
            /* 32 KB erase if possible */
            rc = spiflash_block_32k_erase(dev, address);
            if (rc) {
                goto err;
            }
            address += 0x8000;
            size -= 0x8000;
            continue;
        }
#endif
        rc = spiflash_sector_erase(dev, address);
        if (rc) {
            goto err;
        }
        address += MYNEWT_VAL(SPIFLASH_SECTOR_SIZE);
        if (size > MYNEWT_VAL(SPIFLASH_SECTOR_SIZE)) {
            size -= MYNEWT_VAL(SPIFLASH_SECTOR_SIZE);
        } else {
            size = 0;
        }
    }
err:
    return rc;
}

int
spiflash_identify(struct spiflash_dev *dev)
{
    int i;
    int j;
    uint8_t manufacturer = 0;
    uint8_t memory_type = 0;
    uint8_t capacity = 0;
    int release_power_down_count = 0;
    /* Table with unique release power down functions */
    void (*rpd[3])(struct spiflash_dev *);
    int rc = 0;

    /* List of supported spi flash chips can be found in:
     * hw/drivers/flash/spiflash/chips/sysconfig.yml
     */
    static_assert((sizeof(supported_chips) / sizeof(supported_chips[0])) > 1,
        "At lease one spiflash chip must be specified in sysconfig with SPIFLASH_<chipid>:1");

    spiflash_lock(dev);

    /* Only one chip specified, no need for search*/
    if ((sizeof(supported_chips) / sizeof(supported_chips[0])) == 2) {
        supported_chips[0].fc_release_power_down(dev);
        spiflash_read_jedec_id(dev, &manufacturer, &memory_type, &capacity);
        /* If BSP defined SpiFlash manufacturer or memory type does not
         * match SpiFlash is most likely not connected, connected to
         * different pins, or of different type.
         * It is unlikely that flash depended packaged will work correctly.
         */
        assert(MYNEWT_VAL(SPIFLASH_IGNORE_MANUFACTURER) ||
            manufacturer == supported_chips[0].fc_jedec_id.ji_manufacturer);
        if (!(MYNEWT_VAL(SPIFLASH_IGNORE_MANUFACTURER) ||
              manufacturer == supported_chips[0].fc_jedec_id.ji_manufacturer)) {
            rc = -1;
            goto err;
        }
        assert(MYNEWT_VAL(SPIFLASH_IGNORE_MEMORY_TYPE) ||
            memory_type == supported_chips[0].fc_jedec_id.ji_type);
        if (!(MYNEWT_VAL(SPIFLASH_IGNORE_MEMORY_TYPE) ||
              memory_type == supported_chips[0].fc_jedec_id.ji_type)) {
            rc = -1;
            goto err;
        }
        assert(MYNEWT_VAL(SPIFLASH_IGNORE_MEMORY_CAPACITY) ||
            capacity == supported_chips[0].fc_jedec_id.ji_capacity);
        if (!(MYNEWT_VAL(SPIFLASH_IGNORE_MEMORY_CAPACITY) ||
              capacity == supported_chips[0].fc_jedec_id.ji_capacity)) {
            rc = -1;
            goto err;
        }
        dev->flash_chip = &supported_chips[0];
    } else {
        for (i = 0; supported_chips[i].fc_jedec_id.ji_manufacturer != 0; ++i) {
            for (j = 0; j < release_power_down_count; ++j) {
                if (rpd[j] == supported_chips[i].fc_release_power_down) {
                    /* release power down function same as already execute one
                     * no need check more.
                     */
                    break;
                }
            }
            /* New function found, add to function table and call */
            if (j == release_power_down_count) {
                rpd[j] = supported_chips[i].fc_release_power_down;
                rpd[j](dev);
                spiflash_read_jedec_id(dev, &manufacturer, &memory_type, &capacity);
                if ((manufacturer == 0xFF && memory_type == 0xFF && capacity == 0xFF) ||
                    (manufacturer == 0 && memory_type == 0 && capacity == 0)) {
                    /* Most likely release did not work or device is not
                     * correctly configured (wrong pins).
                     * Try another release power down method if found.
                     */
                    continue;
                }
                /* Something was read from flash, do not try another power up
                 * just check if chip should be supported */
                break;
            }
        }
        for (i = 0; supported_chips[i].fc_jedec_id.ji_manufacturer != 0; ++i) {
            if ((MYNEWT_VAL(SPIFLASH_IGNORE_MANUFACTURER) ||
                 manufacturer == supported_chips[i].fc_jedec_id.ji_manufacturer) &&
                (MYNEWT_VAL(SPIFLASH_IGNORE_MEMORY_TYPE) ||
                 memory_type == supported_chips[i].fc_jedec_id.ji_type) &&
                (MYNEWT_VAL(SPIFLASH_IGNORE_MEMORY_CAPACITY) ||
                 capacity == supported_chips[i].fc_jedec_id.ji_capacity)) {
                /* Device is supported */
                dev->flash_chip = &supported_chips[i];
                break;
            }
        }
        if (dev->flash_chip == NULL) {
            /* Not supported chip */
            assert(0);
            rc = -1;
        }
    }
err:
    spiflash_unlock(dev);

    return rc;
}

static int
hal_spiflash_init(const struct hal_flash *hal_flash_dev)
{
    int rc;
    struct spiflash_dev *dev;

    dev = (struct spiflash_dev *)hal_flash_dev;

#if MYNEWT_VAL(SPIFLASH_STAT)
    rc = stats_init_and_reg(STATS_HDR(dev->stats),
                            STATS_SIZE_INIT_PARMS(dev->stats, STATS_SIZE_32),
                            STATS_NAME_INIT_PARMS(spiflash_stats_section),
                            "spiflash");
    assert(rc == 0);
#endif

#if MYNEWT_VAL(SPIFLASH_AUTO_POWER_DOWN)
    os_mutex_init(&dev->lock);
    os_callout_init(&dev->apd_tmo_co, os_eventq_dflt_get(),
                    spiflash_apd_tmo_func, dev);
#endif

#if !MYNEWT_VAL(BUS_DRIVER_PRESENT)
    hal_gpio_init_out(dev->ss_pin, 1);

    (void)hal_spi_disable(dev->spi_num);

    rc = hal_spi_config(dev->spi_num, &dev->spi_settings);
    if (rc) {
        return (rc);
    }

    hal_spi_set_txrx_cb(dev->spi_num, NULL, NULL);
    rc = hal_spi_enable(dev->spi_num);
    if (rc) {
        return (rc);
    }
#endif
    rc = spiflash_identify(dev);

    return rc;
}

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
static void
init_node_cb(struct bus_node *bnode, void *arg)
{
    /* TODO: Check if something else should be done here */
}

int
spiflash_create_spi_dev(struct bus_spi_node *node, const char *name,
                        const struct bus_spi_node_cfg *spi_cfg)
{
    struct bus_node_callbacks cbs = {
        .init = init_node_cb,
    };
    int rc;

    bus_node_set_callbacks((struct os_dev *)node, &cbs);

    rc = bus_spi_node_create(name, node, spi_cfg, NULL);

    return rc;
}
#endif

#endif

