/* Read the status register of the external SPI flash chip. */ static int read_status_reg(struct flash_bank *bank, uint32_t *status) { struct target *target = bank->target; struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv; uint32_t ssp_base = lpcspifi_info->ssp_base; uint32_t io_base = lpcspifi_info->io_base; uint32_t value; int retval = ERROR_OK; retval = ssp_setcs(target, io_base, 0); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, SPIFLASH_READ_STATUS); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); /* Dummy write to clock in the register */ if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_setcs(target, io_base, 1); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); if (retval == ERROR_OK) *status = value; return retval; }
/* Initialize the ssp module */ static int lpcspifi_set_sw_mode(struct flash_bank *bank) { struct target *target = bank->target; struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv; uint32_t ssp_base = lpcspifi_info->ssp_base; uint32_t io_base = lpcspifi_info->io_base; uint32_t ioconfig_base = lpcspifi_info->ioconfig_base; int retval = ERROR_OK; /* Re-initialize SPIFI. There are a couple of errata on this, so this makes sure that nothing's in an unhappy state. */ retval = lpcspifi_set_hw_mode(bank); /* If we couldn't initialize hardware mode, don't even bother continuing */ if (retval != ERROR_OK) return retval; /* Initialize the pins */ retval = ioconfig_write_reg(target, ioconfig_base, 0x194, 0x00000040); if (retval == ERROR_OK) retval = ioconfig_write_reg(target, ioconfig_base, 0x1a0, 0x00000044); if (retval == ERROR_OK) retval = ioconfig_write_reg(target, ioconfig_base, 0x190, 0x00000040); if (retval == ERROR_OK) retval = ioconfig_write_reg(target, ioconfig_base, 0x19c, 0x000000ed); if (retval == ERROR_OK) retval = ioconfig_write_reg(target, ioconfig_base, 0x198, 0x000000ed); if (retval == ERROR_OK) retval = ioconfig_write_reg(target, ioconfig_base, 0x18c, 0x000000ea); /* Set CS high & as an output */ if (retval == ERROR_OK) retval = io_write_reg(target, io_base, 0x12ac, 0xffffffff); if (retval == ERROR_OK) retval = io_write_reg(target, io_base, 0x2014, 0x00000800); /* Initialize the module */ if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_CR0, 0x00000007); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_CR1, 0x00000000); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_CPSR, 0x00000008); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_CR1, 0x00000002); /* If something didn't work out, attempt to return SPIFI to HW mode */ if (retval != ERROR_OK) lpcspifi_set_hw_mode(bank); return retval; }
static int lpcspifi_bulk_erase(struct flash_bank *bank) { struct target *target = bank->target; struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv; uint32_t ssp_base = lpcspifi_info->ssp_base; uint32_t io_base = lpcspifi_info->io_base; uint32_t value; int retval = ERROR_OK; retval = lpcspifi_set_sw_mode(bank); if (retval == ERROR_OK) retval = lpcspifi_write_enable(bank); /* send SPI command "bulk erase" */ if (retval == ERROR_OK) ssp_setcs(target, io_base, 0); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, lpcspifi_info->dev->chip_erase_cmd); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); if (retval == ERROR_OK) retval = ssp_setcs(target, io_base, 1); /* poll flash BSY for self-timed bulk erase */ if (retval == ERROR_OK) retval = wait_till_ready(bank, bank->num_sectors*SSP_MAX_TIMEOUT); return retval; }
/* Send "write enable" command to SPI flash chip. */ static int lpcspifi_write_enable(struct flash_bank *bank) { struct target *target = bank->target; struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv; uint32_t ssp_base = lpcspifi_info->ssp_base; uint32_t io_base = lpcspifi_info->io_base; uint32_t status, value; int retval = ERROR_OK; retval = ssp_setcs(target, io_base, 0); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, SPIFLASH_WRITE_ENABLE); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); if (retval == ERROR_OK) retval = ssp_setcs(target, io_base, 1); /* read flash status register */ if (retval == ERROR_OK) retval = read_status_reg(bank, &status); if (retval != ERROR_OK) return retval; /* Check write enabled */ if ((status & SPIFLASH_WE_BIT) == 0) { LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status); return ERROR_FAIL; } return retval; }
/** * ssp_set_clkdiv - set SSP clock divider * @div: serial clock rate divider */ static void ssp_set_scr(struct ssp_device *ssp, u32 div) { u32 sscr0 = ssp_read_reg(ssp, SSCR0); if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) { sscr0 &= ~0x0000ff00; sscr0 |= ((div - 2)/2) << 8; /* 2..512 */ } else { sscr0 &= ~0x000fff00; sscr0 |= (div - 1) << 8; /* 1..4096 */ } ssp_write_reg(ssp, SSCR0, sscr0); }
/* On exit, SW mode is kept */ static int lpcspifi_read_flash_id(struct flash_bank *bank, uint32_t *id) { struct target *target = bank->target; struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv; uint32_t ssp_base = lpcspifi_info->ssp_base; uint32_t io_base = lpcspifi_info->io_base; uint32_t value; uint8_t id_buf[3]; int retval; if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; } LOG_DEBUG("Getting ID"); retval = lpcspifi_set_sw_mode(bank); if (retval != ERROR_OK) return retval; /* poll WIP */ if (retval == ERROR_OK) retval = wait_till_ready(bank, SSP_PROBE_TIMEOUT); /* Send SPI command "read ID" */ if (retval == ERROR_OK) retval = ssp_setcs(target, io_base, 0); if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, SPIFLASH_READ_ID); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); /* Dummy write to clock in data */ if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); if (retval == ERROR_OK) id_buf[0] = value; /* Dummy write to clock in data */ if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); if (retval == ERROR_OK) id_buf[1] = value; /* Dummy write to clock in data */ if (retval == ERROR_OK) retval = ssp_write_reg(target, ssp_base, SSP_DATA, 0x00); if (retval == ERROR_OK) retval = poll_ssp_busy(target, ssp_base, SSP_CMD_TIMEOUT); if (retval == ERROR_OK) retval = ssp_read_reg(target, ssp_base, SSP_DATA, &value); if (retval == ERROR_OK) id_buf[2] = value; if (retval == ERROR_OK) retval = ssp_setcs(target, io_base, 1); if (retval == ERROR_OK) *id = id_buf[2] << 16 | id_buf[1] << 8 | id_buf[0]; return retval; }
/* Un-initialize the ssp module and initialize the SPIFI module */ static int lpcspifi_set_hw_mode(struct flash_bank *bank) { struct target *target = bank->target; struct lpcspifi_flash_bank *lpcspifi_info = bank->driver_priv; uint32_t ssp_base = lpcspifi_info->ssp_base; struct armv7m_algorithm armv7m_info; struct working_area *spifi_init_algorithm; struct reg_param reg_params[1]; int retval = ERROR_OK; LOG_DEBUG("Uninitializing LPC43xx SSP"); /* Turn off the SSP module */ retval = ssp_write_reg(target, ssp_base, SSP_CR1, 0x00000000); if (retval != ERROR_OK) return retval; /* see contrib/loaders/flash/lpcspifi_init.S for src */ static const uint8_t spifi_init_code[] = { 0x4f, 0xea, 0x00, 0x08, 0xa1, 0xb0, 0x00, 0xaf, 0x4f, 0xf4, 0xc0, 0x43, 0xc4, 0xf2, 0x08, 0x03, 0x4f, 0xf0, 0xf3, 0x02, 0xc3, 0xf8, 0x8c, 0x21, 0x4f, 0xf4, 0xc0, 0x43, 0xc4, 0xf2, 0x08, 0x03, 0x4f, 0xf4, 0xc0, 0x42, 0xc4, 0xf2, 0x08, 0x02, 0x4f, 0xf4, 0xc0, 0x41, 0xc4, 0xf2, 0x08, 0x01, 0x4f, 0xf4, 0xc0, 0x40, 0xc4, 0xf2, 0x08, 0x00, 0x4f, 0xf0, 0xd3, 0x04, 0xc0, 0xf8, 0x9c, 0x41, 0x20, 0x46, 0xc1, 0xf8, 0x98, 0x01, 0x01, 0x46, 0xc2, 0xf8, 0x94, 0x11, 0xc3, 0xf8, 0x90, 0x11, 0x4f, 0xf4, 0xc0, 0x43, 0xc4, 0xf2, 0x08, 0x03, 0x4f, 0xf0, 0x13, 0x02, 0xc3, 0xf8, 0xa0, 0x21, 0x40, 0xf2, 0x18, 0x13, 0xc1, 0xf2, 0x40, 0x03, 0x1b, 0x68, 0x1c, 0x68, 0x40, 0xf2, 0xb4, 0x30, 0xc1, 0xf2, 0x00, 0x00, 0x4f, 0xf0, 0x03, 0x01, 0x4f, 0xf0, 0xc0, 0x02, 0x4f, 0xea, 0x08, 0x03, 0xa0, 0x47, 0x00, 0xf0, 0x00, 0xb8, 0x00, 0xbe }; armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; armv7m_info.core_mode = ARM_MODE_THREAD; LOG_DEBUG("Allocating working area for SPIFI init algorithm"); /* Get memory for spifi initialization algorithm */ retval = target_alloc_working_area(target, sizeof(spifi_init_code), &spifi_init_algorithm); if (retval != ERROR_OK) { LOG_ERROR("Insufficient working area to initialize SPIFI "\ "module. You must allocate at least %zdB of working "\ "area in order to use this driver.", sizeof(spifi_init_code) ); return retval; } LOG_DEBUG("Writing algorithm to working area at 0x%08" PRIx32, spifi_init_algorithm->address); /* Write algorithm to working area */ retval = target_write_buffer(target, spifi_init_algorithm->address, sizeof(spifi_init_code), spifi_init_code ); if (retval != ERROR_OK) { target_free_working_area(target, spifi_init_algorithm); return retval; } init_reg_param(®_params[0], "r0", 32, PARAM_OUT); /* spifi clk speed */ /* For now, the algorithm will set up the SPIFI module * @ the IRC clock speed. In the future, it could be made * a bit smarter to use other clock sources if the user has * already configured them in order to speed up memory- * mapped reads. */ buf_set_u32(reg_params[0].value, 0, 32, 12); /* Run the algorithm */ LOG_DEBUG("Running SPIFI init algorithm"); retval = target_run_algorithm(target, 0 , NULL, 1, reg_params, spifi_init_algorithm->address, spifi_init_algorithm->address + sizeof(spifi_init_code) - 2, 1000, &armv7m_info); if (retval != ERROR_OK) LOG_ERROR("Error executing SPIFI init algorithm"); target_free_working_area(target, spifi_init_algorithm); destroy_reg_param(®_params[0]); return retval; }