flash
-----

The flash driver subsystem is a work in progress which aims at
supporting common external SPI/I2C flash/eeprom memory chips. This is
equivalent to what Linux calls ``MTD`` for
``Memory Technology Devices``.

At the moment the only ``flash`` device that is already supported is the
AT45DBxxx SPI flash family with the ``at45db`` driver.

The flash driver aims for full compatibility with the ``hal_flash`` API,
which means initialization and usage can be performed by any ``fs`` that
supports the ``hal_flash`` interface.

Initialization
^^^^^^^^^^^^^^

To be compatible with the standard ``hal_flash`` interface, the
``at45db`` driver embeds a ``struct hal_flash`` to its own
``struct at45db_dev``. The whole ``at45db_dev`` struct is shown below.

.. code:: c

    struct at45db_dev {
        struct hal_flash hal;
        struct hal_spi_settings *settings;
        int spi_num;
        void *spi_cfg;                  /** Low-level MCU SPI config */
        int ss_pin;
        uint32_t baudrate;
        uint16_t page_size;             /** Page size to be used, valid: 512 and 528 */
        uint8_t disable_auto_erase;     /** Reads and writes auto-erase by default */
    };

To ease with initialization a helper function ``at45db_default_config``
was added. It returns an already initialized ``struct at45db_dev``
leaving the user with just having to provide the SPI related config.

To initialize the device, pass the ``at45db_dev`` struct to
``at45db_init``.

.. code:: c

    int at45db_init(const struct hal_flash *dev);

For low-level access to the device the following functions are provided:

.. code:: c

    int at45db_read(const struct hal_flash *dev, uint32_t addr, void *buf,
                    uint32_t len);
    int at45db_write(const struct hal_flash *dev, uint32_t addr, const void *buf,
                     uint32_t len);
    int at45db_erase_sector(const struct hal_flash *dev, uint32_t sector_address);
    int at45db_sector_info(const struct hal_flash *dev, int idx, uint32_t *address,
                           uint32_t *sz);

Also, ``nffs`` is able to run on the device due to the fact that
standard ``hal_flash`` interface compatibility is provided. Due to
current limitations of ``nffs``, it can only run on ``at45db`` if the
internal flash of the MCU is not being used.

Dependencies
^^^^^^^^^^^^

To include the ``at45db`` driver on a project, just include it as a
dependency in your pkg.yml:

::

    pkg.deps:
        - "@apache-mynewt-core/hw/drivers/flash/at45db"

Header file
^^^^^^^^^^^

The ``at45db`` SPI flash follows the standard ``hal_flash`` interface
but requires that a special struct

.. code:: c

    #include <at45db/at45db.h>

Example
^^^^^^^

This following examples assume that the ``at45db`` is being used on a
STM32F4 MCU.

.. code:: c

    static const int SPI_SS_PIN   = MCU_GPIO_PORTA(4);
    static const int SPI_SCK_PIN  = MCU_GPIO_PORTA(5);
    static const int SPI_MISO_PIN = MCU_GPIO_PORTA(6);
    static const int SPI_MOSI_PIN = MCU_GPIO_PORTA(7);

    struct stm32f4_hal_spi_cfg spi_cfg = {
        .ss_pin   = SPI_SS_PIN,
        .sck_pin  = SPI_SCK_PIN,
        .miso_pin = SPI_MISO_PIN,
        .mosi_pin = SPI_MOSI_PIN,
        .irq_prio = 2
    };

    struct at45db_dev *my_at45db_dev = NULL;

    my_at45db_dev = at45db_default_config();
    my_at45db_dev->spi_num = 0;
    my_at45db_dev->spi_cfg = &spi_cfg;
    my_at45db_dev->ss_pin = spi_cfg.ss_pin;

    rc = at45db_init((struct hal_flash *) my_at45db_dev);
    if (rc) {
        /* XXX: error handling */
    }

The enable ``nffs`` to run on the ``at45db``, the ``flash_id`` 0 needs
to map to provide a mapping from 0 to this struct.

.. code:: c

    const struct hal_flash *
    hal_bsp_flash_dev(uint8_t id)
    {
        if (id != 0) {
            return NULL;
        }
        return &my_at45db_dev;
    }
