<commit_before>/*
 * Copyright (C) 2016 Eistec AB
 *               2017 OTA keys S.A.
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 *
 */

/**
 * @ingroup     drivers_mtd_spi_nor
 * @{
 *
 * @file
 * @brief       Driver for serial flash memory attached to SPI
 *
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 *
 * @}
 */

#include <stdint.h>
#include <string.h>
#include <errno.h>

#include "mtd.h"
#if MODULE_XTIMER
#include "xtimer.h"
#include "timex.h"
#else
#include "thread.h"
#endif
#include "byteorder.h"
#include "mtd_spi_nor.h"

#define ENABLE_DEBUG    0
#include "debug.h"

#define ENABLE_TRACE    0
#define TRACE(...)      DEBUG(__VA_ARGS__)

/* after power up, on an invalid JEDEC ID, wait and read N times */
#ifndef MTD_POWER_UP_WAIT_FOR_ID
#define MTD_POWER_UP_WAIT_FOR_ID    (0x0F)
#endif

#define SFLASH_CMD_4_BYTE_ADDR (0xB7)   /**< enable 32 bit addressing */
#define SFLASH_CMD_3_BYTE_ADDR (0xE9)   /**< enable 24 bit addressing */

#define MTD_64K             (65536ul)
#define MTD_64K_ADDR_MASK   (0xFFFF)
#define MTD_32K             (32768ul)
#define MTD_32K_ADDR_MASK   (0x7FFF)
#define MTD_4K              (4096ul)
#define MTD_4K_ADDR_MASK    (0xFFF)

#define MBIT_AS_BYTES       ((1024 * 1024) / 8)

#define MIN(a, b) ((a) > (b) ? (b) : (a))

/**
 * @brief   JEDEC memory manufacturer ID codes.
 *
 *          see http://www.softnology.biz/pdf/JEP106AV.pdf
 * @{
 */
#define JEDEC_BANK(n)   ((n) << 8)

typedef enum {
    SPI_NOR_JEDEC_ATMEL = 0x1F | JEDEC_BANK(1),
} jedec_manuf_t;
/** @} */

static inline spi_t _get_spi(const mtd_spi_nor_t *dev)
{
    return dev->params->spi;
}

static void mtd_spi_acquire(const mtd_spi_nor_t *dev)
{
    spi_acquire(_get_spi(dev), dev->params->cs,
                dev->params->mode, dev->params->clk);
}

static void mtd_spi_release(const mtd_spi_nor_t *dev)
{
    spi_release(_get_spi(dev));
}

static inline uint8_t* _be_addr(const mtd_spi_nor_t *dev, uint32_t *addr)
{
    *addr = htonl(*addr);
    return &((uint8_t*)addr)[4 - dev->params->addr_width];
}

/**
 * @internal
 * @brief Send command opcode followed by address, followed by a read to buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[in]  addr   address (big endian)
 * @param[out] dest   read buffer
 * @param[in]  count  number of bytes to read after the address has been sent
 */
static void mtd_spi_cmd_addr_read(const mtd_spi_nor_t *dev, uint8_t opcode,
                                  uint32_t addr, void *dest, uint32_t count)
{
    TRACE("mtd_spi_cmd_addr_read: %p, %02x, (%06"PRIx32"), %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, addr, dest, count);

    uint8_t *addr_buf = _be_addr(dev, &addr);

    if (ENABLE_TRACE) {
        TRACE("mtd_spi_cmd_addr_read: addr:");
        for (unsigned int i = 0; i < dev->params->addr_width; ++i) {
            TRACE(" %02x", addr_buf[i]);
        }
        TRACE("\n");
    }

    /* Send opcode followed by address */
    spi_transfer_byte(_get_spi(dev), dev->params->cs, true, opcode);
    spi_transfer_bytes(_get_spi(dev), dev->params->cs, true,
                       (char *)addr_buf, NULL, dev->params->addr_width);

    /* Read data */
    spi_transfer_bytes(_get_spi(dev), dev->params->cs, false,
                       NULL, dest, count);
}

/**
 * @internal
 * @brief Send command opcode followed by address, followed by a write from buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[in]  addr   address (big endian)
 * @param[out] src    write buffer
 * @param[in]  count  number of bytes to write after the opcode has been sent
 */
static void mtd_spi_cmd_addr_write(const mtd_spi_nor_t *dev, uint8_t opcode,
                                   uint32_t addr, const void *src, uint32_t count)
{
    TRACE("mtd_spi_cmd_addr_write: %p, %02x, (%06"PRIx32"), %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, addr, src, count);

    uint8_t *addr_buf = _be_addr(dev, &addr);

    if (ENABLE_TRACE) {
        TRACE("mtd_spi_cmd_addr_write: addr:");
        for (unsigned int i = 0; i < dev->params->addr_width; ++i) {
            TRACE(" %02x", addr_buf[i]);
        }
        TRACE("\n");
    }

    /* Send opcode followed by address */
    spi_transfer_byte(_get_spi(dev), dev->params->cs, true, opcode);

    /* only keep CS asserted when there is data that follows */
    bool cont = (count > 0);
    spi_transfer_bytes(_get_spi(dev), dev->params->cs, cont,
                       (char *)addr_buf, NULL, dev->params->addr_width);

    /* Write data */
    if (cont) {
        spi_transfer_bytes(_get_spi(dev), dev->params->cs,
                           false, (void *)src, NULL, count);
    }
}

/**
 * @internal
 * @brief Send command opcode followed by a read to buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[out] dest   read buffer
 * @param[in]  count  number of bytes to write after the opcode has been sent
 */
static void mtd_spi_cmd_read(const mtd_spi_nor_t *dev, uint8_t opcode, void *dest, uint32_t count)
{
    TRACE("mtd_spi_cmd_read: %p, %02x, %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, dest, count);

    spi_transfer_regs(_get_spi(dev), dev->params->cs, opcode, NULL, dest, count);
}

/**
 * @internal
 * @brief Send command opcode followed by a write from buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[out] src    write buffer
 * @param[in]  count  number of bytes to write after the opcode has been sent
 */
static void __attribute__((unused)) mtd_spi_cmd_write(const mtd_spi_nor_t *dev, uint8_t opcode, const void *src, uint32_t count)
{
    TRACE("mtd_spi_cmd_write: %p, %02x, %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, src, count);

    spi_transfer_regs(_get_spi(dev), dev->params->cs, opcode,
                      (void *)src, NULL, count);
}

/**
 * @internal
 * @brief Send command opcode
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 */
static void mtd_spi_cmd(const mtd_spi_nor_t *dev, uint8_t opcode)
{
    TRACE("mtd_spi_cmd: %p, %02x\n",
          (void *)dev, (unsigned int)opcode);

    spi_transfer_byte(_get_spi(dev), dev->params->cs, false, opcode);
}

static bool mtd_spi_manuf_match(const mtd_jedec_id_t *id, jedec_manuf_t manuf)
{
    return manuf == ((id->bank << 8) | id->manuf);
}

/**
 * @internal
 * @brief Compute 8 bit parity
 */
static inline uint8_t parity8(uint8_t x)
{
    /* Taken from http://stackoverflow.com/a/21618038/1805713 */
    x ^= x >> 4;
    x ^= x >> 2;
    x ^= x >> 1;
    return (x & 1);
}

/**
 * @internal
 * @brief Read JEDEC ID
 */
static int mtd_spi_read_jedec_id(const mtd_spi_nor_t *dev, mtd_jedec_id_t *out)
{
    uint8_t buffer[JEDEC_BANK_MAX + sizeof(mtd_jedec_id_t) - 1];

    DEBUG("mtd_spi_read_jedec_id: rdid=0x%02x\n",
          (unsigned int)dev->params->opcode->rdid);

    /* Send opcode */
    mtd_spi_cmd_read(dev, dev->params->opcode->rdid, buffer, sizeof(buffer));

    /* Manufacturer IDs are organized in 'banks'.
     * If we read the 'next bank' instead of manufacturer ID, skip
     * the byte and increment the bank counter.
     */
    uint8_t bank = 0;
    while (buffer[bank] == JEDEC_NEXT_BANK) {
        if (++bank == JEDEC_BANK_MAX) {
            DEBUG_PUTS("mtd_spi_read_jedec_id: bank out of bounds\n")
            return -1;
        }
    }

    if (parity8(buffer[bank]) == 0) {
        /* saw even parity, we expected odd parity => parity error */
        DEBUG("mtd_spi_read_jedec_id: Parity error (0x%02x)\n", buffer[bank]);
        return -2;
    }

    if (buffer[bank] == 0xFF || buffer[bank] == 0x00) {
        DEBUG_PUTS("mtd_spi_read_jedec_id: failed to read manufacturer ID");
        return -3;
    }

    /* Copy manufacturer ID */
    out->bank = bank + 1;
    memcpy((uint8_t*)out + 1, &buffer[bank], 3);

    DEBUG("mtd_spi_read_jedec_id: bank=%u manuf=0x%02x\n", (unsigned int)out->bank,
          (unsigned int)out->manuf);

    DEBUG("mtd_spi_read_jedec_id: device=0x%02x, 0x%02x\n",
          (unsigned int)out->device[0], (unsigned int)out->device[1]);

    return 0;
}

/**
 * @internal
 * @brief Get Flash capacity based on JEDEC ID
 *
 * @note The way the capacity is encoded differs between vendors.
 *       This formula has been tested with flash chips from Adesto,
 *       ISSI, Micron and Spansion, but it might not cover all cases.
 *       Please extend the function if necessary.
 */
static uint32_t mtd_spi_nor_get_size(const mtd_jedec_id_t *id)
{
    /* old Atmel (now Adesto) parts use 5 lower bits of device ID 1 for density */
    if (mtd_spi_manuf_match(id, SPI_NOR_JEDEC_ATMEL) &&
        /* ID 2 is used to encode the product version, usually 1 or 2 */
        (id->device[1] & ~0x3) == 0) {
        return (0x1F & id->device[0]) * MBIT_AS_BYTES;
    }

    /* everyone else seems to use device ID 2 for density */
    return 1 << id->device[1];
}

static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us)
{
    unsigned i = 0, j = 0;
    uint32_t div = 2;
#if ENABLE_DEBUG && defined(MODULE_XTIMER)
    uint32_t diff = xtimer_now_usec();
#endif
    do {
        uint8_t status;
        mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status));

        TRACE("mtd_spi_nor: wait device status = 0x%02x\n", (unsigned int)status);
        if ((status & 1) == 0) { /* TODO magic number */
            break;
        }
        i++;
#if MODULE_XTIMER
        if (us) {
            xtimer_usleep(us);
            /* reduce the waiting time quickly if the estimate was too short,
             * but still avoid busy (yield) waiting */
            if (us > 2 * XTIMER_BACKOFF) {
                us -= (us / div);
                div++;
            }
            else {
                us = 2 * XTIMER_BACKOFF;
            }
        }
        else {
            j++;
            thread_yield();
        }
#else
        (void)div;
        (void) us;
        thread_yield();
#endif
    } while (1);
    DEBUG("wait loop %u times, yield %u times", i, j);
#if ENABLE_DEBUG && defined(MODULE_XTIMER)
    diff = xtimer_now_usec() - diff;
    DEBUG(", total wait %"PRIu32"us", diff);
#endif
    DEBUG("\n");
}

static void _init_pins(mtd_spi_nor_t *dev)
{
    DEBUG("mtd_spi_nor_init: init pins\n");

    /* CS */
    spi_init_cs(_get_spi(dev), dev->params->cs);

    /* Write Protect - not used by the driver */
    if (gpio_is_valid(dev->params->wp)) {
        gpio_init(dev->params->wp, GPIO_OUT);
        gpio_set(dev->params->wp);
    }

    /* Hold - not used by the driver */
    if (gpio_is_valid(dev->params->hold)) {
        gpio_init(dev->params->hold, GPIO_OUT);
        gpio_set(dev->params->hold);
    }
}

static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power)
{
    mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;

    mtd_spi_acquire(dev);
    switch (power) {
        case MTD_POWER_UP:
            mtd_spi_cmd(dev, dev->params->opcode->wake);
#if defined(MODULE_XTIMER)
            /* No sense in trying multiple times if no xtimer to wait between
               reads */
            uint8_t retries = 0;
            int res = 0;
            do {
                xtimer_usleep(dev->params->wait_chip_wake_up);
                res = mtd_spi_read_jedec_id(dev, &dev->jedec_id);
                retries++;
            } while (res < 0 || retries < MTD_POWER_UP_WAIT_FOR_ID);
            if (res < 0) {
                return -EIO;
            }
#endif
            /* enable 32 bit address mode */
            if (dev->params->addr_width == 4) {
                mtd_spi_cmd(dev, SFLASH_CMD_4_BYTE_ADDR);
            }

            break;
        case MTD_POWER_DOWN:
            mtd_spi_cmd(dev, dev->params->opcode->sleep);
            break;
    }
    mtd_spi_release(dev);

    return 0;
}

static int mtd_spi_nor_init(mtd_dev_t *mtd)
{
    DEBUG("mtd_spi_nor_init: %p\n", (void *)mtd);
    mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;

    DEBUG("mtd_spi_nor_init: -> spi: %lx, cs: %lx, opcodes: %p\n",
          (unsigned long)_get_spi(dev), (unsigned long)dev->params->cs, (void *)dev->params->opcode);

    if (dev->params->addr_width == 0) {
        return -EINVAL;
    }

    /* CS, WP, Hold */
    _init_pins(dev);

    /* power up the MTD device*/
    DEBUG("mtd_spi_nor_init: power up MTD device");
    if (mtd_spi_nor_power(mtd, MTD_POWER_UP)) {
        DEBUG("mtd_spi_nor_init: failed to power up MTD device");
        return -EIO;
    }

    mtd_spi_acquire(dev);
    int res = mtd_spi_read_jedec_id(dev, &dev->jedec_id);
    if (res < 0) {
        mtd_spi_release(dev);
        return -EIO;
    }
    DEBUG("mtd_spi_nor_init: Found chip with ID: (%d, 0x%02x, 0x%02x, 0x%02x)\n",
          dev->jedec_id.bank, dev->jedec_id.manuf, dev->jedec_id.device[0], dev->jedec_id.device[1]);

    /* derive density from JEDEC ID  */<commit_msg>drivers/mtd_spi_nor: check addr_width with assert()<commit_after>/*
 * Copyright (C) 2016 Eistec AB
 *               2017 OTA keys S.A.
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 *
 */

/**
 * @ingroup     drivers_mtd_spi_nor
 * @{
 *
 * @file
 * @brief       Driver for serial flash memory attached to SPI
 *
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 *
 * @}
 */

#include <stdint.h>
#include <string.h>
#include <errno.h>

#include "mtd.h"
#if MODULE_XTIMER
#include "xtimer.h"
#include "timex.h"
#else
#include "thread.h"
#endif
#include "byteorder.h"
#include "mtd_spi_nor.h"

#define ENABLE_DEBUG    0
#include "debug.h"

#define ENABLE_TRACE    0
#define TRACE(...)      DEBUG(__VA_ARGS__)

/* after power up, on an invalid JEDEC ID, wait and read N times */
#ifndef MTD_POWER_UP_WAIT_FOR_ID
#define MTD_POWER_UP_WAIT_FOR_ID    (0x0F)
#endif

#define SFLASH_CMD_4_BYTE_ADDR (0xB7)   /**< enable 32 bit addressing */
#define SFLASH_CMD_3_BYTE_ADDR (0xE9)   /**< enable 24 bit addressing */

#define MTD_64K             (65536ul)
#define MTD_64K_ADDR_MASK   (0xFFFF)
#define MTD_32K             (32768ul)
#define MTD_32K_ADDR_MASK   (0x7FFF)
#define MTD_4K              (4096ul)
#define MTD_4K_ADDR_MASK    (0xFFF)

#define MBIT_AS_BYTES       ((1024 * 1024) / 8)

#define MIN(a, b) ((a) > (b) ? (b) : (a))

/**
 * @brief   JEDEC memory manufacturer ID codes.
 *
 *          see http://www.softnology.biz/pdf/JEP106AV.pdf
 * @{
 */
#define JEDEC_BANK(n)   ((n) << 8)

typedef enum {
    SPI_NOR_JEDEC_ATMEL = 0x1F | JEDEC_BANK(1),
} jedec_manuf_t;
/** @} */

static inline spi_t _get_spi(const mtd_spi_nor_t *dev)
{
    return dev->params->spi;
}

static void mtd_spi_acquire(const mtd_spi_nor_t *dev)
{
    spi_acquire(_get_spi(dev), dev->params->cs,
                dev->params->mode, dev->params->clk);
}

static void mtd_spi_release(const mtd_spi_nor_t *dev)
{
    spi_release(_get_spi(dev));
}

static inline uint8_t* _be_addr(const mtd_spi_nor_t *dev, uint32_t *addr)
{
    *addr = htonl(*addr);
    return &((uint8_t*)addr)[4 - dev->params->addr_width];
}

/**
 * @internal
 * @brief Send command opcode followed by address, followed by a read to buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[in]  addr   address (big endian)
 * @param[out] dest   read buffer
 * @param[in]  count  number of bytes to read after the address has been sent
 */
static void mtd_spi_cmd_addr_read(const mtd_spi_nor_t *dev, uint8_t opcode,
                                  uint32_t addr, void *dest, uint32_t count)
{
    TRACE("mtd_spi_cmd_addr_read: %p, %02x, (%06"PRIx32"), %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, addr, dest, count);

    uint8_t *addr_buf = _be_addr(dev, &addr);

    if (ENABLE_TRACE) {
        TRACE("mtd_spi_cmd_addr_read: addr:");
        for (unsigned int i = 0; i < dev->params->addr_width; ++i) {
            TRACE(" %02x", addr_buf[i]);
        }
        TRACE("\n");
    }

    /* Send opcode followed by address */
    spi_transfer_byte(_get_spi(dev), dev->params->cs, true, opcode);
    spi_transfer_bytes(_get_spi(dev), dev->params->cs, true,
                       (char *)addr_buf, NULL, dev->params->addr_width);

    /* Read data */
    spi_transfer_bytes(_get_spi(dev), dev->params->cs, false,
                       NULL, dest, count);
}

/**
 * @internal
 * @brief Send command opcode followed by address, followed by a write from buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[in]  addr   address (big endian)
 * @param[out] src    write buffer
 * @param[in]  count  number of bytes to write after the opcode has been sent
 */
static void mtd_spi_cmd_addr_write(const mtd_spi_nor_t *dev, uint8_t opcode,
                                   uint32_t addr, const void *src, uint32_t count)
{
    TRACE("mtd_spi_cmd_addr_write: %p, %02x, (%06"PRIx32"), %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, addr, src, count);

    uint8_t *addr_buf = _be_addr(dev, &addr);

    if (ENABLE_TRACE) {
        TRACE("mtd_spi_cmd_addr_write: addr:");
        for (unsigned int i = 0; i < dev->params->addr_width; ++i) {
            TRACE(" %02x", addr_buf[i]);
        }
        TRACE("\n");
    }

    /* Send opcode followed by address */
    spi_transfer_byte(_get_spi(dev), dev->params->cs, true, opcode);

    /* only keep CS asserted when there is data that follows */
    bool cont = (count > 0);
    spi_transfer_bytes(_get_spi(dev), dev->params->cs, cont,
                       (char *)addr_buf, NULL, dev->params->addr_width);

    /* Write data */
    if (cont) {
        spi_transfer_bytes(_get_spi(dev), dev->params->cs,
                           false, (void *)src, NULL, count);
    }
}

/**
 * @internal
 * @brief Send command opcode followed by a read to buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[out] dest   read buffer
 * @param[in]  count  number of bytes to write after the opcode has been sent
 */
static void mtd_spi_cmd_read(const mtd_spi_nor_t *dev, uint8_t opcode, void *dest, uint32_t count)
{
    TRACE("mtd_spi_cmd_read: %p, %02x, %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, dest, count);

    spi_transfer_regs(_get_spi(dev), dev->params->cs, opcode, NULL, dest, count);
}

/**
 * @internal
 * @brief Send command opcode followed by a write from buffer
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 * @param[out] src    write buffer
 * @param[in]  count  number of bytes to write after the opcode has been sent
 */
static void __attribute__((unused)) mtd_spi_cmd_write(const mtd_spi_nor_t *dev, uint8_t opcode, const void *src, uint32_t count)
{
    TRACE("mtd_spi_cmd_write: %p, %02x, %p, %" PRIu32 "\n",
          (void *)dev, (unsigned int)opcode, src, count);

    spi_transfer_regs(_get_spi(dev), dev->params->cs, opcode,
                      (void *)src, NULL, count);
}

/**
 * @internal
 * @brief Send command opcode
 *
 * @param[in]  dev    pointer to device descriptor
 * @param[in]  opcode command opcode
 */
static void mtd_spi_cmd(const mtd_spi_nor_t *dev, uint8_t opcode)
{
    TRACE("mtd_spi_cmd: %p, %02x\n",
          (void *)dev, (unsigned int)opcode);

    spi_transfer_byte(_get_spi(dev), dev->params->cs, false, opcode);
}

static bool mtd_spi_manuf_match(const mtd_jedec_id_t *id, jedec_manuf_t manuf)
{
    return manuf == ((id->bank << 8) | id->manuf);
}

/**
 * @internal
 * @brief Compute 8 bit parity
 */
static inline uint8_t parity8(uint8_t x)
{
    /* Taken from http://stackoverflow.com/a/21618038/1805713 */
    x ^= x >> 4;
    x ^= x >> 2;
    x ^= x >> 1;
    return (x & 1);
}

/**
 * @internal
 * @brief Read JEDEC ID
 */
static int mtd_spi_read_jedec_id(const mtd_spi_nor_t *dev, mtd_jedec_id_t *out)
{
    uint8_t buffer[JEDEC_BANK_MAX + sizeof(mtd_jedec_id_t) - 1];

    DEBUG("mtd_spi_read_jedec_id: rdid=0x%02x\n",
          (unsigned int)dev->params->opcode->rdid);

    /* Send opcode */
    mtd_spi_cmd_read(dev, dev->params->opcode->rdid, buffer, sizeof(buffer));

    /* Manufacturer IDs are organized in 'banks'.
     * If we read the 'next bank' instead of manufacturer ID, skip
     * the byte and increment the bank counter.
     */
    uint8_t bank = 0;
    while (buffer[bank] == JEDEC_NEXT_BANK) {
        if (++bank == JEDEC_BANK_MAX) {
            DEBUG_PUTS("mtd_spi_read_jedec_id: bank out of bounds\n")
            return -1;
        }
    }

    if (parity8(buffer[bank]) == 0) {
        /* saw even parity, we expected odd parity => parity error */
        DEBUG("mtd_spi_read_jedec_id: Parity error (0x%02x)\n", buffer[bank]);
        return -2;
    }

    if (buffer[bank] == 0xFF || buffer[bank] == 0x00) {
        DEBUG_PUTS("mtd_spi_read_jedec_id: failed to read manufacturer ID");
        return -3;
    }

    /* Copy manufacturer ID */
    out->bank = bank + 1;
    memcpy((uint8_t*)out + 1, &buffer[bank], 3);

    DEBUG("mtd_spi_read_jedec_id: bank=%u manuf=0x%02x\n", (unsigned int)out->bank,
          (unsigned int)out->manuf);

    DEBUG("mtd_spi_read_jedec_id: device=0x%02x, 0x%02x\n",
          (unsigned int)out->device[0], (unsigned int)out->device[1]);

    return 0;
}

/**
 * @internal
 * @brief Get Flash capacity based on JEDEC ID
 *
 * @note The way the capacity is encoded differs between vendors.
 *       This formula has been tested with flash chips from Adesto,
 *       ISSI, Micron and Spansion, but it might not cover all cases.
 *       Please extend the function if necessary.
 */
static uint32_t mtd_spi_nor_get_size(const mtd_jedec_id_t *id)
{
    /* old Atmel (now Adesto) parts use 5 lower bits of device ID 1 for density */
    if (mtd_spi_manuf_match(id, SPI_NOR_JEDEC_ATMEL) &&
        /* ID 2 is used to encode the product version, usually 1 or 2 */
        (id->device[1] & ~0x3) == 0) {
        return (0x1F & id->device[0]) * MBIT_AS_BYTES;
    }

    /* everyone else seems to use device ID 2 for density */
    return 1 << id->device[1];
}

static inline void wait_for_write_complete(const mtd_spi_nor_t *dev, uint32_t us)
{
    unsigned i = 0, j = 0;
    uint32_t div = 2;
#if ENABLE_DEBUG && defined(MODULE_XTIMER)
    uint32_t diff = xtimer_now_usec();
#endif
    do {
        uint8_t status;
        mtd_spi_cmd_read(dev, dev->params->opcode->rdsr, &status, sizeof(status));

        TRACE("mtd_spi_nor: wait device status = 0x%02x\n", (unsigned int)status);
        if ((status & 1) == 0) { /* TODO magic number */
            break;
        }
        i++;
#if MODULE_XTIMER
        if (us) {
            xtimer_usleep(us);
            /* reduce the waiting time quickly if the estimate was too short,
             * but still avoid busy (yield) waiting */
            if (us > 2 * XTIMER_BACKOFF) {
                us -= (us / div);
                div++;
            }
            else {
                us = 2 * XTIMER_BACKOFF;
            }
        }
        else {
            j++;
            thread_yield();
        }
#else
        (void)div;
        (void) us;
        thread_yield();
#endif
    } while (1);
    DEBUG("wait loop %u times, yield %u times", i, j);
#if ENABLE_DEBUG && defined(MODULE_XTIMER)
    diff = xtimer_now_usec() - diff;
    DEBUG(", total wait %"PRIu32"us", diff);
#endif
    DEBUG("\n");
}

static void _init_pins(mtd_spi_nor_t *dev)
{
    DEBUG("mtd_spi_nor_init: init pins\n");

    /* CS */
    spi_init_cs(_get_spi(dev), dev->params->cs);

    /* Write Protect - not used by the driver */
    if (gpio_is_valid(dev->params->wp)) {
        gpio_init(dev->params->wp, GPIO_OUT);
        gpio_set(dev->params->wp);
    }

    /* Hold - not used by the driver */
    if (gpio_is_valid(dev->params->hold)) {
        gpio_init(dev->params->hold, GPIO_OUT);
        gpio_set(dev->params->hold);
    }
}

static int mtd_spi_nor_power(mtd_dev_t *mtd, enum mtd_power_state power)
{
    mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;

    mtd_spi_acquire(dev);
    switch (power) {
        case MTD_POWER_UP:
            mtd_spi_cmd(dev, dev->params->opcode->wake);
#if defined(MODULE_XTIMER)
            /* No sense in trying multiple times if no xtimer to wait between
               reads */
            uint8_t retries = 0;
            int res = 0;
            do {
                xtimer_usleep(dev->params->wait_chip_wake_up);
                res = mtd_spi_read_jedec_id(dev, &dev->jedec_id);
                retries++;
            } while (res < 0 || retries < MTD_POWER_UP_WAIT_FOR_ID);
            if (res < 0) {
                return -EIO;
            }
#endif
            /* enable 32 bit address mode */
            if (dev->params->addr_width == 4) {
                mtd_spi_cmd(dev, SFLASH_CMD_4_BYTE_ADDR);
            }

            break;
        case MTD_POWER_DOWN:
            mtd_spi_cmd(dev, dev->params->opcode->sleep);
            break;
    }
    mtd_spi_release(dev);

    return 0;
}

static int mtd_spi_nor_init(mtd_dev_t *mtd)
{
    DEBUG("mtd_spi_nor_init: %p\n", (void *)mtd);
    mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd;

    DEBUG("mtd_spi_nor_init: -> spi: %lx, cs: %lx, opcodes: %p\n",
          (unsigned long)_get_spi(dev), (unsigned long)dev->params->cs, (void *)dev->params->opcode);

    /* verify configuration */
    assert(dev->params->addr_width > 0);
    assert(dev->params->addr_width <= 4);

    /* CS, WP, Hold */
    _init_pins(dev);

    /* power up the MTD device*/
    DEBUG("mtd_spi_nor_init: power up MTD device");
    if (mtd_spi_nor_power(mtd, MTD_POWER_UP)) {
        DEBUG("mtd_spi_nor_init: failed to power up MTD device");
        return -EIO;
    }

    mtd_spi_acquire(dev);
    int res = mtd_spi_read_jedec_id(dev, &dev->jedec_id);
    if (res < 0) {
        mtd_spi_release(dev);
        return -EIO;
    }
    DEBUG("mtd_spi_nor_init: Found chip with ID: (%d, 0x%02x, 0x%02x, 0x%02x)\n",
          dev->jedec_id.bank, dev->jedec_id.manuf, dev->jedec_id.device[0], dev->jedec_id.device[1]);

    /* derive density from JEDEC ID  */
{'fraction_non_alphanumeric': 0.13415927862392968, 'fraction_numerical': 0.01852693794044101, 'mean_word_length': 2.6261849155103723, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 228, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1343997', 'n_tokens_mistral': 9949, 'n_tokens_neox': 9578, 'n_words': 2403}