blob: acef29507ffc3cc79b7f0bca5d3251be56e4593c [file] [log] [blame]
/**
* \addtogroup BSP
* \{
* \addtogroup DEVICES
* \{
* \addtogroup QSPI
* \{
*/
/**
****************************************************************************************
*
* @file hw_qspi.c
*
* @brief Implementation of the QSPI Low Level Driver.
*
* Copyright (c) 2016, Dialog Semiconductor
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
****************************************************************************************
*/
#if dg_configUSE_HW_QSPI
#include <stdint.h>
#include "hw_qspi.h"
__RETAINED_RW uint8_t dummy_num[] = { 0, 1, 2, 0, 3 };
#if (dg_configFLASH_POWER_DOWN == 1)
__attribute__((section("text_retained")))
#endif
void hw_qspi_set_bus_mode(HW_QSPI_BUS_MODE mode)
{
switch (mode)
{
case HW_QSPI_BUS_MODE_SINGLE:
QSPIC->QSPIC_CTRLBUS_REG = REG_MSK(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_SINGLE);
break;
case HW_QSPI_BUS_MODE_DUAL:
QSPIC->QSPIC_CTRLBUS_REG = REG_MSK(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_DUAL);
break;
case HW_QSPI_BUS_MODE_QUAD:
QSPIC->QSPIC_CTRLBUS_REG = REG_MSK(QSPIC, QSPIC_CTRLBUS_REG, QSPIC_SET_QUAD);
hw_qspi_set_io2_output(false);
hw_qspi_set_io3_output(false);
break;
}
}
__attribute__((section("text_retained"))) void hw_qspi_set_automode(bool automode)
{
if (automode)
{
const uint32_t burst_cmd_a = QSPIC->QSPIC_BURSTCMDA_REG;
const uint32_t burst_cmd_b = QSPIC->QSPIC_BURSTCMDB_REG;
const uint32_t status_cmd = QSPIC->QSPIC_STATUSCMD_REG;
const uint32_t erase_cmd_b = QSPIC->QSPIC_ERASECMDB_REG;
const uint32_t burstbrk = QSPIC->QSPIC_BURSTBRK_REG;
if (GETBITS32(QSPIC, QSPIC_BURSTCMDA_REG, burst_cmd_a, QSPIC_INST_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_BURSTCMDA_REG, burst_cmd_a, QSPIC_ADR_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_BURSTCMDA_REG, burst_cmd_a, QSPIC_DMY_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_BURSTCMDA_REG, burst_cmd_a, QSPIC_EXT_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_BURSTCMDB_REG, burst_cmd_b, QSPIC_DAT_RX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_BURSTCMDB_REG, burst_cmd_b, QSPIC_DAT_RX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_STATUSCMD_REG, status_cmd, QSPIC_RSTAT_RX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_STATUSCMD_REG, status_cmd, QSPIC_RSTAT_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_ERASECMDB_REG, erase_cmd_b, QSPIC_ERS_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_ERASECMDB_REG, erase_cmd_b, QSPIC_WEN_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_ERASECMDB_REG, erase_cmd_b, QSPIC_SUS_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_ERASECMDB_REG, erase_cmd_b, QSPIC_RES_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_ERASECMDB_REG, erase_cmd_b, QSPIC_EAD_TX_MD) == HW_QSPI_BUS_MODE_QUAD ||
GETBITS32(QSPIC, QSPIC_BURSTBRK_REG, burstbrk, QSPIC_BRK_TX_MD) == HW_QSPI_BUS_MODE_QUAD)
{
hw_qspi_set_io2_output(false);
hw_qspi_set_io3_output(false);
}
}
HW_QSPIC_REG_SETF(CTRLMODE, AUTO_MD, automode);
}
void hw_qspi_set_wrapping_burst_instruction(uint8_t inst, HW_QSPI_WRAP_LEN len,
HW_QSPI_WRAP_SIZE size)
{
HW_QSPIC_REG_SETF(BURSTCMDA, INST_WB, inst);
QSPIC->QSPIC_BURSTCMDB_REG =
(QSPIC->QSPIC_BURSTCMDB_REG &
~(REG_MSK(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_SIZE) |
REG_MSK(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_LEN))) |
BITS32(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_SIZE, size) |
BITS32(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_LEN, len) |
BITS32(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_WRAP_MD, 1);
}
void hw_qspi_set_dummy_bytes_count(uint8_t count)
{
if (count == 3)
{
HW_QSPIC_REG_SETF(BURSTCMDB, DMY_FORCE, 1);
}
else
{
QSPIC->QSPIC_BURSTCMDB_REG =
(QSPIC->QSPIC_BURSTCMDB_REG &
~(REG_MSK(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_FORCE) |
REG_MSK(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_NUM))) |
BITS32(QSPIC, QSPIC_BURSTCMDB_REG, QSPIC_DMY_NUM,
dummy_num[count]);
}
}
void hw_qspi_erase_block(uint32_t addr)
{
if (!hw_qspi_get_automode())
{
hw_qspi_set_automode(true);
}
// Wait for previous erase to end
while (hw_qspi_get_erase_status() != 0)
{
}
if (hw_qspi_get_address_size() == HW_QSPI_ADDR_SIZE_32)
{
addr >>= 12;
}
else
{
addr >>= 4;
}
// Setup erase block page
HW_QSPIC_REG_SETF(ERASECTRL, ERS_ADDR, addr);
// Fire erase
HW_QSPIC_REG_SETF(ERASECTRL, ERASE_EN, 1);
}
void hw_qspi_set_pads(HW_QSPI_SLEW_RATE rate, HW_QSPI_DRIVE_CURRENT current)
{
QSPIC->QSPIC_GP_REG =
BITS16(QSPIC, QSPIC_GP_REG, QSPIC_PADS_SLEW, rate) |
BITS16(QSPIC, QSPIC_GP_REG, QSPIC_PADS_DRV, current);
}
void hw_qspi_init(const qspi_config *cfg)
{
hw_qspi_enable_clock();
hw_qspi_set_automode(false);
hw_qspi_set_bus_mode(HW_QSPI_BUS_MODE_SINGLE);
hw_qspi_set_io2_output(true);
hw_qspi_set_io2(1);
hw_qspi_set_io3_output(true);
hw_qspi_set_io3(1);
if (cfg)
{
hw_qspi_set_address_size(cfg->address_size);
hw_qspi_set_clock_mode(cfg->idle_clock);
hw_qspi_set_read_sampling_edge(cfg->sampling_edge);
}
}
#endif /* dg_configUSE_HW_QSPI */
/**
* \}
* \}
* \}
*/