/*******************************************************************************
 * @file    dr_w25q32.h
 * @brief   W25Q32 SPI Flash Driver for nRF52840
 *          32Mbit (4MB) Serial NOR Flash Memory
 * @date    2026-03-12
 *
 * @details Software SPI (bit-bang) driver for W25Q32RVXHJQ.
 *          Shares SPI bus (SCK/MISO/MOSI) with ADC121S051.
 *          Only CS pin is unique (P0.24).
 *
 *          Pin connections:
 *            nRF52840      W25Q32 (U16)
 *            P0.24  -----> /CS   (pin 1)
 *            P0.15  <----- DO    (pin 2, MISO)
 *            P0.16  -----> DI    (pin 5, MOSI)
 *            P0.14  -----> CLK   (pin 6)
 *
 * @note    WP (pin 3) and HOLD (pin 7) are pulled HIGH via 10k resistors.
 ******************************************************************************/

#ifndef DR_W25Q32_H
#define DR_W25Q32_H

#include <stdint.h>
#include <stdbool.h>
#include "nrf_gpio.h"

/*==============================================================================
 * PIN CONFIGURATION
 *============================================================================*/
#define DR_FLASH_PIN_CS         NRF_GPIO_PIN_MAP(0, 24)  /* Chip Select - SPI_CS_FLASH(P0.24) */
#define DR_FLASH_PIN_SCLK       NRF_GPIO_PIN_MAP(0, 14)  /* Serial Clock(shared) - SPI_SCK(P0.14) */
#define DR_FLASH_PIN_MISO       NRF_GPIO_PIN_MAP(0, 15)  /* DI_(IO0)(shared) - SPI_MOSI(P0.15) */
#define DR_FLASH_PIN_MOSI       NRF_GPIO_PIN_MAP(0, 16)  /* DO_(IO1)(shared) - SPI_MISO(P0.16) */

/*==============================================================================
 * W25Q32 SPECIFICATIONS
 *============================================================================*/
#define DR_FLASH_PAGE_SIZE      256         /**< Page size (bytes) */
#define DR_FLASH_SECTOR_SIZE    4096        /**< Sector size (bytes, 4KB) */
#define DR_FLASH_BLOCK_32K      (32*1024)   /**< 32KB block */
#define DR_FLASH_BLOCK_64K      (64*1024)   /**< 64KB block */
#define DR_FLASH_TOTAL_SIZE     (4*1024*1024) /**< 4MB total */
#define DR_FLASH_UID_LENGTH     8           /**< Unique ID length (bytes) */

/*==============================================================================
 * W25Q32 COMMAND OPCODES
 *============================================================================*/
#define DR_FLASH_CMD_WRITE_ENABLE       0x06
#define DR_FLASH_CMD_WRITE_DISABLE      0x04
#define DR_FLASH_CMD_READ_STATUS1       0x05
#define DR_FLASH_CMD_READ_STATUS2       0x35
#define DR_FLASH_CMD_WRITE_STATUS       0x01
#define DR_FLASH_CMD_READ_DATA          0x03
#define DR_FLASH_CMD_PAGE_PROGRAM       0x02
#define DR_FLASH_CMD_SECTOR_ERASE       0x20    /**< 4KB erase */
#define DR_FLASH_CMD_BLOCK_ERASE_32K    0x52    /**< 32KB erase */
#define DR_FLASH_CMD_BLOCK_ERASE_64K    0xD8    /**< 64KB erase */
#define DR_FLASH_CMD_CHIP_ERASE         0xC7    /**< Full chip erase */
#define DR_FLASH_CMD_POWER_DOWN         0xB9    /**< Deep power-down */
#define DR_FLASH_CMD_RELEASE_PD         0xAB    /**< Release from deep power-down */
#define DR_FLASH_CMD_READ_UID           0x4B    /**< Read unique ID */
#define DR_FLASH_CMD_READ_JEDEC_ID      0x9F    /**< Read JEDEC ID */
#define DR_FLASH_CMD_READ_MFR_ID        0x90    /**< Read manufacturer/device ID */

/* Status Register 1 bits */
#define DR_FLASH_SR1_BUSY       0x01    /**< Erase/Write in progress */
#define DR_FLASH_SR1_WEL        0x02    /**< Write enable latch */

/*==============================================================================
 * ERROR CODES
 *============================================================================*/
typedef enum {
    DR_FLASH_OK = 0,
    DR_FLASH_ERR_NOT_INIT,
    DR_FLASH_ERR_INVALID_PARAM,
    DR_FLASH_ERR_TIMEOUT,
    DR_FLASH_ERR_WRITE_FAILED,
    DR_FLASH_ERR_JEDEC_MISMATCH
} dr_flash_err_t;

/*==============================================================================
 * DATA STRUCTURES
 *============================================================================*/

/** @brief JEDEC ID structure */
typedef struct {
    uint8_t manufacturer_id;    /**< 0xEF = Winbond */
    uint8_t memory_type;        /**< 0x40 = SPI */
    uint8_t capacity;           /**< 0x16 = 32Mbit */
} dr_flash_jedec_t;

/*==============================================================================
 * INITIALIZATION
 *============================================================================*/

/**
 * @brief Initialize W25Q32 Flash driver
 * @return dr_flash_err_t Error code
 * @note Sets up GPIO pins. Does NOT wake chip from deep power-down.
 */
dr_flash_err_t dr_w25q32_init(void);

/**
 * @brief Uninitialize driver, release GPIO pins
 */
void dr_w25q32_uninit(void);

/**
 * @brief Check if driver is initialized
 */
bool dr_w25q32_is_initialized(void);

/*==============================================================================
 * IDENTIFICATION
 *============================================================================*/

/**
 * @brief Read JEDEC ID (manufacturer, type, capacity)
 * @param jedec Pointer to JEDEC ID structure
 * @return dr_flash_err_t Error code
 * @note Expected: manufacturer=0xEF, type=0x40, capacity=0x16
 */
dr_flash_err_t dr_w25q32_read_jedec_id(dr_flash_jedec_t *jedec);

/**
 * @brief Read 8-byte unique ID
 * @param uid Buffer to store 8-byte UID (must be >= 8 bytes)
 * @return dr_flash_err_t Error code
 */
dr_flash_err_t dr_w25q32_read_uid(uint8_t *uid);

/*==============================================================================
 * READ
 *============================================================================*/

/**
 * @brief Read data from flash
 * @param addr 24-bit start address (0x000000 ~ 0x3FFFFF)
 * @param buf Buffer to store read data
 * @param len Number of bytes to read
 * @return dr_flash_err_t Error code
 */
dr_flash_err_t dr_w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len);

/*==============================================================================
 * WRITE
 *============================================================================*/

/**
 * @brief Write data to flash (page program, max 256 bytes)
 * @param addr 24-bit start address (must be page-aligned for best results)
 * @param data Data to write
 * @param len Number of bytes (1~256, must not cross page boundary)
 * @return dr_flash_err_t Error code
 * @note Automatically sends Write Enable before programming.
 *       Waits for completion (BUSY flag).
 */
dr_flash_err_t dr_w25q32_write(uint32_t addr, const uint8_t *data, uint32_t len);

/*==============================================================================
 * ERASE
 *============================================================================*/

/**
 * @brief Erase 4KB sector
 * @param addr Any address within the sector to erase
 * @return dr_flash_err_t Error code
 * @note Typical 45ms, max 400ms. Waits for completion.
 */
dr_flash_err_t dr_w25q32_erase_sector(uint32_t addr);

/**
 * @brief Erase 32KB block
 * @param addr Any address within the block
 * @return dr_flash_err_t Error code
 */
dr_flash_err_t dr_w25q32_erase_block_32k(uint32_t addr);

/**
 * @brief Erase 64KB block
 * @param addr Any address within the block
 * @return dr_flash_err_t Error code
 */
dr_flash_err_t dr_w25q32_erase_block_64k(uint32_t addr);

/**
 * @brief Erase entire chip
 * @return dr_flash_err_t Error code
 * @note Takes 6~50 seconds. Use with caution.
 */
dr_flash_err_t dr_w25q32_chip_erase(void);

/*==============================================================================
 * POWER MANAGEMENT
 *============================================================================*/

/**
 * @brief Enter deep power-down mode (~1uA)
 * @return dr_flash_err_t Error code
 */
dr_flash_err_t dr_w25q32_deep_powerdown(void);

/**
 * @brief Release from deep power-down
 * @return dr_flash_err_t Error code
 * @note Requires ~3us recovery time (handled internally)
 */
dr_flash_err_t dr_w25q32_wakeup(void);

/*==============================================================================
 * STATUS
 *============================================================================*/

/**
 * @brief Read Status Register 1
 * @param status Pointer to store status byte
 * @return dr_flash_err_t Error code
 */
dr_flash_err_t dr_w25q32_read_status(uint8_t *status);

/**
 * @brief Check if flash is busy (erase/write in progress)
 * @return true if busy
 */
bool dr_w25q32_is_busy(void);

/**
 * @brief Wait until flash is not busy
 * @param timeout_ms Maximum wait time in milliseconds
 * @return dr_flash_err_t DR_FLASH_OK or DR_FLASH_ERR_TIMEOUT
 */
dr_flash_err_t dr_w25q32_wait_busy(uint32_t timeout_ms);

/*==============================================================================
 * DEBUG / TEST
 *============================================================================*/

/**
 * @brief Test flash communication by reading JEDEC ID
 * @return true if JEDEC ID matches W25Q32 (0xEF, 0x40, 0x16)
 */
bool dr_w25q32_test(void);

#endif /* DR_W25Q32_H */
