/* * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019, Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include "cadence_qspi.h" #define LESS(a, b) (((a) < (b)) ? (a) : (b)) #define MORE(a, b) (((a) > (b)) ? (a) : (b)) uint32_t qspi_device_size; int cad_qspi_cs; int cad_qspi_idle(void) { return (mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG) & CAD_QSPI_CFG_IDLE) >> 31; } int cad_qspi_set_baudrate_div(uint32_t div) { if (div > 0xf) return CAD_INVALID; mmio_clrsetbits_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG, ~CAD_QSPI_CFG_BAUDDIV_MSK, CAD_QSPI_CFG_BAUDDIV(div)); return 0; } int cad_qspi_configure_dev_size(uint32_t addr_bytes, uint32_t bytes_per_dev, uint32_t bytes_per_block) { mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DEVSZ, CAD_QSPI_DEVSZ_ADDR_BYTES(addr_bytes) | CAD_QSPI_DEVSZ_BYTES_PER_PAGE(bytes_per_dev) | CAD_QSPI_DEVSZ_BYTES_PER_BLOCK(bytes_per_block)); return 0; } int cad_qspi_set_read_config(uint32_t opcode, uint32_t instr_type, uint32_t addr_type, uint32_t data_type, uint32_t mode_bit, uint32_t dummy_clk_cycle) { mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DEVRD, CAD_QSPI_DEV_OPCODE(opcode) | CAD_QSPI_DEV_INST_TYPE(instr_type) | CAD_QSPI_DEV_ADDR_TYPE(addr_type) | CAD_QSPI_DEV_DATA_TYPE(data_type) | CAD_QSPI_DEV_MODE_BIT(mode_bit) | CAD_QSPI_DEV_DUMMY_CLK_CYCLE(dummy_clk_cycle)); return 0; } int cad_qspi_set_write_config(uint32_t opcode, uint32_t addr_type, uint32_t data_type, uint32_t dummy_clk_cycle) { mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DEVWR, CAD_QSPI_DEV_OPCODE(opcode) | CAD_QSPI_DEV_ADDR_TYPE(addr_type) | CAD_QSPI_DEV_DATA_TYPE(data_type) | CAD_QSPI_DEV_DUMMY_CLK_CYCLE(dummy_clk_cycle)); return 0; } int cad_qspi_timing_config(uint32_t clkphase, uint32_t clkpol, uint32_t csda, uint32_t csdads, uint32_t cseot, uint32_t cssot, uint32_t rddatacap) { uint32_t cfg = mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG); cfg &= CAD_QSPI_CFG_SELCLKPHASE_CLR_MSK & CAD_QSPI_CFG_SELCLKPOL_CLR_MSK; cfg |= CAD_QSPI_SELCLKPHASE(clkphase) | CAD_QSPI_SELCLKPOL(clkpol); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG, cfg); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DELAY, CAD_QSPI_DELAY_CSSOT(cssot) | CAD_QSPI_DELAY_CSEOT(cseot) | CAD_QSPI_DELAY_CSDADS(csdads) | CAD_QSPI_DELAY_CSDA(csda)); return 0; } int cad_qspi_stig_cmd_helper(int cs, uint32_t cmd) { uint32_t count = 0; /* chip select */ mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG, (mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG) & CAD_QSPI_CFG_CS_MSK) | CAD_QSPI_CFG_CS(cs)); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD, cmd); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD, cmd | CAD_QSPI_FLASHCMD_EXECUTE); do { uint32_t reg = mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD); if (!(reg & CAD_QSPI_FLASHCMD_EXECUTE_STAT)) break; count++; } while (count < CAD_QSPI_COMMAND_TIMEOUT); if (count >= CAD_QSPI_COMMAND_TIMEOUT) { ERROR("Error sending QSPI command %x, timed out\n", cmd); return CAD_QSPI_ERROR; } return 0; } int cad_qspi_stig_cmd(uint32_t opcode, uint32_t dummy) { if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) { ERROR("Faulty dummy bytes\n"); return -1; } return cad_qspi_stig_cmd_helper(cad_qspi_cs, CAD_QSPI_FLASHCMD_OPCODE(opcode) | CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES(dummy)); } int cad_qspi_stig_read_cmd(uint32_t opcode, uint32_t dummy, uint32_t num_bytes, uint32_t *output) { if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) { ERROR("Faulty dummy byes\n"); return -1; } if ((num_bytes > 8) || (num_bytes == 0)) return -1; uint32_t cmd = CAD_QSPI_FLASHCMD_OPCODE(opcode) | CAD_QSPI_FLASHCMD_ENRDDATA(1) | CAD_QSPI_FLASHCMD_NUMRDDATABYTES(num_bytes - 1) | CAD_QSPI_FLASHCMD_ENCMDADDR(0) | CAD_QSPI_FLASHCMD_ENMODEBIT(0) | CAD_QSPI_FLASHCMD_NUMADDRBYTES(0) | CAD_QSPI_FLASHCMD_ENWRDATA(0) | CAD_QSPI_FLASHCMD_NUMWRDATABYTES(0) | CAD_QSPI_FLASHCMD_NUMDUMMYBYTES(dummy); if (cad_qspi_stig_cmd_helper(cad_qspi_cs, cmd)) { ERROR("failed to send stig cmd\n"); return -1; } output[0] = mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_RDDATA0); if (num_bytes > 4) { output[1] = mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_RDDATA1); } return 0; } int cad_qspi_stig_wr_cmd(uint32_t opcode, uint32_t dummy, uint32_t num_bytes, uint32_t *input) { if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) { ERROR("Faulty dummy byes\n"); return -1; } if ((num_bytes > 8) || (num_bytes == 0)) return -1; uint32_t cmd = CAD_QSPI_FLASHCMD_OPCODE(opcode) | CAD_QSPI_FLASHCMD_ENRDDATA(0) | CAD_QSPI_FLASHCMD_NUMRDDATABYTES(0) | CAD_QSPI_FLASHCMD_ENCMDADDR(0) | CAD_QSPI_FLASHCMD_ENMODEBIT(0) | CAD_QSPI_FLASHCMD_NUMADDRBYTES(0) | CAD_QSPI_FLASHCMD_ENWRDATA(1) | CAD_QSPI_FLASHCMD_NUMWRDATABYTES(num_bytes - 1) | CAD_QSPI_FLASHCMD_NUMDUMMYBYTES(dummy); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_WRDATA0, input[0]); if (num_bytes > 4) mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_WRDATA1, input[1]); return cad_qspi_stig_cmd_helper(cad_qspi_cs, cmd); } int cad_qspi_stig_addr_cmd(uint32_t opcode, uint32_t dummy, uint32_t addr) { uint32_t cmd; if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) return -1; cmd = CAD_QSPI_FLASHCMD_OPCODE(opcode) | CAD_QSPI_FLASHCMD_NUMDUMMYBYTES(dummy) | CAD_QSPI_FLASHCMD_ENCMDADDR(1) | CAD_QSPI_FLASHCMD_NUMADDRBYTES(2); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_ADDR, addr); return cad_qspi_stig_cmd_helper(cad_qspi_cs, cmd); } int cad_qspi_device_bank_select(uint32_t bank) { int status = 0; status = cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WREN, 0); if (status != 0) return status; status = cad_qspi_stig_wr_cmd(CAD_QSPI_STIG_OPCODE_WREN_EXT_REG, 0, 1, &bank); if (status != 0) return status; return cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WRDIS, 0); } int cad_qspi_device_status(uint32_t *status) { return cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDSR, 0, 1, status); } #if CAD_QSPI_MICRON_N25Q_SUPPORT int cad_qspi_n25q_enable(void) { cad_qspi_set_read_config(QSPI_FAST_READ, CAD_QSPI_INST_SINGLE, CAD_QSPI_ADDR_FASTREAD, CAT_QSPI_ADDR_SINGLE_IO, 1, 0); cad_qspi_set_write_config(QSPI_WRITE, 0, 0, 0); return 0; } int cad_qspi_n25q_wait_for_program_and_erase(int program_only) { uint32_t status, flag_sr; int count = 0; while (count < CAD_QSPI_COMMAND_TIMEOUT) { status = cad_qspi_device_status(&status); if (status != 0) { ERROR("Error getting device status\n"); return -1; } if (!CAD_QSPI_STIG_SR_BUSY(status)) break; count++; } if (count >= CAD_QSPI_COMMAND_TIMEOUT) { ERROR("Timed out waiting for idle\n"); return -1; } count = 0; while (count < CAD_QSPI_COMMAND_TIMEOUT) { status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDFLGSR, 0, 1, &flag_sr); if (status != 0) { ERROR("Error waiting program and erase.\n"); return status; } if ((program_only && CAD_QSPI_STIG_FLAGSR_PROGRAMREADY(flag_sr)) || (!program_only && CAD_QSPI_STIG_FLAGSR_ERASEREADY(flag_sr))) break; } if (count >= CAD_QSPI_COMMAND_TIMEOUT) ERROR("Timed out waiting for program and erase\n"); if ((program_only && CAD_QSPI_STIG_FLAGSR_PROGRAMERROR(flag_sr)) || (!program_only && CAD_QSPI_STIG_FLAGSR_ERASEERROR(flag_sr))) { ERROR("Error programming/erasing flash\n"); cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_CLFSR, 0); return -1; } return 0; } #endif int cad_qspi_indirect_read_start_bank(uint32_t flash_addr, uint32_t num_bytes) { mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRDSTADDR, flash_addr); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRDCNT, num_bytes); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRD, CAD_QSPI_INDRD_START | CAD_QSPI_INDRD_IND_OPS_DONE); return 0; } int cad_qspi_indirect_write_start_bank(uint32_t flash_addr, uint32_t num_bytes) { mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWRSTADDR, flash_addr); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWRCNT, num_bytes); mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWR, CAD_QSPI_INDWR_START | CAD_QSPI_INDWR_INDDONE); return 0; } int cad_qspi_indirect_write_finish(void) { #if CAD_QSPI_MICRON_N25Q_SUPPORT return cad_qspi_n25q_wait_for_program_and_erase(1); #else return 0; #endif } int cad_qspi_enable(void) { int status; mmio_setbits_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG, CAD_QSPI_CFG_ENABLE); #if CAD_QSPI_MICRON_N25Q_SUPPORT status = cad_qspi_n25q_enable(); if (status != 0) return status; #endif return 0; } int cad_qspi_enable_subsector_bank(uint32_t addr) { int status = 0; status = cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WREN, 0); if (status != 0) return status; status = cad_qspi_stig_addr_cmd(CAD_QSPI_STIG_OPCODE_SUBSEC_ERASE, 0, addr); if (status != 0) return status; #if CAD_QSPI_MICRON_N25Q_SUPPORT status = cad_qspi_n25q_wait_for_program_and_erase(0); #endif return status; } int cad_qspi_erase_subsector(uint32_t addr) { int status = 0; status = cad_qspi_device_bank_select(addr >> 24); if (status != 0) return status; return cad_qspi_enable_subsector_bank(addr); } int cad_qspi_erase_sector(uint32_t addr) { int status = 0; status = cad_qspi_device_bank_select(addr >> 24); if (status != 0) return status; status = cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WREN, 0); if (status != 0) return status; status = cad_qspi_stig_addr_cmd(CAD_QSPI_STIG_OPCODE_SEC_ERASE, 0, addr); if (status != 0) return status; #if CAD_QSPI_MICRON_N25Q_SUPPORT status = cad_qspi_n25q_wait_for_program_and_erase(0); #endif return status; } void cad_qspi_calibration(uint32_t dev_clk, uint32_t qspi_clk_mhz) { int status; uint32_t dev_sclk_mhz = 27; /*min value to get biggest 0xF div factor*/ uint32_t data_cap_delay; uint32_t sample_rdid; uint32_t rdid; uint32_t div_actual; uint32_t div_bits; int first_pass, last_pass; /*1. Set divider to bigger value (slowest SCLK) *2. RDID and save the value */ div_actual = (qspi_clk_mhz + (dev_sclk_mhz - 1)) / dev_sclk_mhz; div_bits = (((div_actual + 1) / 2) - 1); status = cad_qspi_set_baudrate_div(0xf); status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0, 3, &sample_rdid); if (status != 0) return; /*3. Set divider to the intended frequency *4. Set the read delay = 0 *5. RDID and check whether the value is same as item 2 *6. Increase read delay and compared the value against item 2 *7. Find the range of read delay that have same as * item 2 and divide it to 2 */ div_actual = (qspi_clk_mhz + (dev_clk - 1)) / dev_clk; div_bits = (((div_actual + 1) / 2) - 1); status = cad_qspi_set_baudrate_div(div_bits); if (status != 0) return; data_cap_delay = 0; first_pass = -1; last_pass = -1; do { if (status != 0) break; status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0, 3, &rdid); if (status != 0) break; if (rdid == sample_rdid) { if (first_pass == -1) first_pass = data_cap_delay; else last_pass = data_cap_delay; } data_cap_delay++; mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_RDDATACAP, CAD_QSPI_RDDATACAP_BYP(1) | CAD_QSPI_RDDATACAP_DELAY(data_cap_delay)); } while (data_cap_delay < 0x10); if (first_pass > 0) { int diff = first_pass - last_pass; data_cap_delay = first_pass + diff / 2; } mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_RDDATACAP, CAD_QSPI_RDDATACAP_BYP(1) | CAD_QSPI_RDDATACAP_DELAY(data_cap_delay)); status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0, 3, &rdid); if (status != 0) return; } int cad_qspi_int_disable(uint32_t mask) { if (cad_qspi_idle() == 0) return -1; if ((CAD_QSPI_INT_STATUS_ALL & mask) == 0) return -1; mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_IRQMSK, mask); return 0; } void cad_qspi_set_chip_select(int cs) { cad_qspi_cs = cs; } int cad_qspi_init(uint32_t desired_clk_freq, uint32_t clk_phase, uint32_t clk_pol, uint32_t csda, uint32_t csdads, uint32_t cseot, uint32_t cssot, uint32_t rddatacap) { int status = 0; uint32_t qspi_desired_clk_freq; uint32_t rdid = 0; uint32_t cap_code; INFO("Initializing Qspi\n"); if (cad_qspi_idle() == 0) { ERROR("device not idle\n"); return -1; } status = cad_qspi_timing_config(clk_phase, clk_pol, csda, csdads, cseot, cssot, rddatacap); if (status != 0) { ERROR("config set timing failure\n"); return status; } mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_REMAPADDR, CAD_QSPI_REMAPADDR_VALUE_SET(0)); status = cad_qspi_int_disable(CAD_QSPI_INT_STATUS_ALL); if (status != 0) { ERROR("failed disable\n"); return status; } cad_qspi_set_baudrate_div(0xf); status = cad_qspi_enable(); if (status != 0) { ERROR("failed enable\n"); return status; } qspi_desired_clk_freq = 100; cad_qspi_calibration(qspi_desired_clk_freq, 50000000); status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0, 3, &rdid); if (status != 0) { ERROR("Error reading RDID\n"); return status; } /* * NOTE: The Size code seems to be a form of BCD (binary coded decimal). * The first nibble is the 10's digit and the second nibble is the 1's * digit in the number of bytes. * * Capacity ID samples: * 0x15 : 16 Mb => 2 MiB => 1 << 21 ; BCD=15 * 0x16 : 32 Mb => 4 MiB => 1 << 22 ; BCD=16 * 0x17 : 64 Mb => 8 MiB => 1 << 23 ; BCD=17 * 0x18 : 128 Mb => 16 MiB => 1 << 24 ; BCD=18 * 0x19 : 256 Mb => 32 MiB => 1 << 25 ; BCD=19 * 0x1a * 0x1b * 0x1c * 0x1d * 0x1e * 0x1f * 0x20 : 512 Mb => 64 MiB => 1 << 26 ; BCD=20 * 0x21 : 1024 Mb => 128 MiB => 1 << 27 ; BCD=21 */ cap_code = CAD_QSPI_STIG_RDID_CAPACITYID(rdid); if (!(((cap_code >> 4) > 0x9) || ((cap_code & 0xf) > 0x9))) { uint32_t decoded_cap = ((cap_code >> 4) * 10) + (cap_code & 0xf); qspi_device_size = 1 << (decoded_cap + 6); INFO("QSPI Capacity: %x\n\n", qspi_device_size); } else { ERROR("Invalid CapacityID encountered: 0x%02x\n", cap_code); return -1; } cad_qspi_configure_dev_size(INTEL_QSPI_ADDR_BYTES, INTEL_QSPI_BYTES_PER_DEV, INTEL_BYTES_PER_BLOCK); INFO("Flash size: %d Bytes\n", qspi_device_size); return status; } int cad_qspi_indirect_page_bound_write(uint32_t offset, uint8_t *buffer, uint32_t len) { int status = 0, i; uint32_t write_count, write_capacity, *write_data, space, write_fill_level, sram_partition; status = cad_qspi_indirect_write_start_bank(offset, len); if (status != 0) return status; write_count = 0; sram_partition = CAD_QSPI_SRAMPART_ADDR(mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_SRAMPART)); write_capacity = (uint32_t) CAD_QSPI_SRAM_FIFO_ENTRY_COUNT - sram_partition; while (write_count < len) { write_fill_level = CAD_QSPI_SRAMFILL_INDWRPART( mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_SRAMFILL)); space = LESS(write_capacity - write_fill_level, (len - write_count) / sizeof(uint32_t)); write_data = (uint32_t *)(buffer + write_count); for (i = 0; i < space; ++i) mmio_write_32(CAD_QSPIDATA_OFST, *write_data++); write_count += space * sizeof(uint32_t); } return cad_qspi_indirect_write_finish(); } int cad_qspi_read_bank(uint8_t *buffer, uint32_t offset, uint32_t size) { int status; uint32_t read_count = 0, *read_data; int level = 1, count = 0, i; status = cad_qspi_indirect_read_start_bank(offset, size); if (status != 0) return status; while (read_count < size) { do { level = CAD_QSPI_SRAMFILL_INDRDPART( mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_SRAMFILL)); read_data = (uint32_t *)(buffer + read_count); for (i = 0; i < level; ++i) *read_data++ = mmio_read_32(CAD_QSPIDATA_OFST); read_count += level * sizeof(uint32_t); count++; } while (level > 0); } return 0; } int cad_qspi_write_bank(uint32_t offset, uint8_t *buffer, uint32_t size) { int status = 0; uint32_t page_offset = offset & (CAD_QSPI_PAGE_SIZE - 1); uint32_t write_size = LESS(size, CAD_QSPI_PAGE_SIZE - page_offset); while (size) { status = cad_qspi_indirect_page_bound_write(offset, buffer, write_size); if (status != 0) break; offset += write_size; buffer += write_size; size -= write_size; write_size = LESS(size, CAD_QSPI_PAGE_SIZE); } return status; } int cad_qspi_read(void *buffer, uint32_t offset, uint32_t size) { uint32_t bank_count, bank_addr, bank_offset, copy_len; uint8_t *read_data; int i, status; status = 0; if ((offset >= qspi_device_size) || (offset + size - 1 >= qspi_device_size) || (size == 0) || ((long) ((int *)buffer) & 0x3) || (offset & 0x3) || (size & 0x3)) { ERROR("Invalid read parameter\n"); return -1; } if (CAD_QSPI_INDRD_RD_STAT(mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRD))) { ERROR("Read in progress\n"); return -1; } /* * bank_count : Number of bank(s) affected, including partial banks. * bank_addr : Aligned address of the first bank, * including partial bank. * bank_ofst : The offset of the bank to read. * Only used when reading the first bank. */ bank_count = CAD_QSPI_BANK_ADDR(offset + size - 1) - CAD_QSPI_BANK_ADDR(offset) + 1; bank_addr = offset & CAD_QSPI_BANK_ADDR_MSK; bank_offset = offset & (CAD_QSPI_BANK_SIZE - 1); read_data = (uint8_t *)buffer; copy_len = LESS(size, CAD_QSPI_BANK_SIZE - bank_offset); for (i = 0; i < bank_count; ++i) { status = cad_qspi_device_bank_select(CAD_QSPI_BANK_ADDR( bank_addr)); if (status != 0) break; status = cad_qspi_read_bank(read_data, bank_offset, copy_len); if (status != 0) break; bank_addr += CAD_QSPI_BANK_SIZE; read_data += copy_len; size -= copy_len; bank_offset = 0; copy_len = LESS(size, CAD_QSPI_BANK_SIZE); } return status; } int cad_qspi_erase(uint32_t offset, uint32_t size) { int status = 0; uint32_t subsector_offset = offset & (CAD_QSPI_SUBSECTOR_SIZE - 1); uint32_t erase_size = LESS(size, CAD_QSPI_SUBSECTOR_SIZE - subsector_offset); while (size) { status = cad_qspi_erase_subsector(offset); if (status != 0) break; offset += erase_size; size -= erase_size; erase_size = LESS(size, CAD_QSPI_SUBSECTOR_SIZE); } return status; } int cad_qspi_write(void *buffer, uint32_t offset, uint32_t size) { int status, i; uint32_t bank_count, bank_addr, bank_offset, copy_len; uint8_t *write_data; status = 0; if ((offset >= qspi_device_size) || (offset + size - 1 >= qspi_device_size) || (size == 0) || ((long)buffer & 0x3) || (offset & 0x3) || (size & 0x3)) return -2; if (CAD_QSPI_INDWR_RDSTAT(mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWR))) { ERROR("QSPI Error: Write in progress\n"); return -1; } bank_count = CAD_QSPI_BANK_ADDR(offset + size - 1) - CAD_QSPI_BANK_ADDR(offset) + 1; bank_addr = offset & CAD_QSPI_BANK_ADDR_MSK; bank_offset = offset & (CAD_QSPI_BANK_SIZE - 1); write_data = buffer; copy_len = LESS(size, CAD_QSPI_BANK_SIZE - bank_offset); for (i = 0; i < bank_count; ++i) { status = cad_qspi_device_bank_select( CAD_QSPI_BANK_ADDR(bank_addr)); if (status != 0) break; status = cad_qspi_write_bank(bank_offset, write_data, copy_len); if (status != 0) break; bank_addr += CAD_QSPI_BANK_SIZE; write_data += copy_len; size -= copy_len; bank_offset = 0; copy_len = LESS(size, CAD_QSPI_BANK_SIZE); } return status; } int cad_qspi_update(void *Buffer, uint32_t offset, uint32_t size) { int status = 0; status = cad_qspi_erase(offset, size); if (status != 0) return status; return cad_qspi_write(Buffer, offset, size); } void cad_qspi_reset(void) { cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_RESET_EN, 0); cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_RESET_MEM, 0); }