| /** |
| * \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 */ |
| /** |
| * \} |
| * \} |
| * \} |
| */ |