/***************************************************************************
* Copyright (C) 2012 by George Harris *
* george@luminairecoffee.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include "spi.h"
#include <jtag/jtag.h>
#include <helper/time_support.h>
#include <target/algorithm.h>
#include <target/armv7m.h>
/* Offsets from ssp_base into config & data registers */
#define SSP_CR0 (0x00) /* Control register 0 */
#define SSP_CR1 (0x04) /* Control register 1 */
#define SSP_DATA (0x08) /* Data register (TX and RX) */
#define SSP_SR (0x0C) /* Status register */
#define SSP_CPSR (0x10) /* Clock prescale register */
/* Status register fields */
#define SSP_BSY (0x00000010)
/* Timeout in ms */
#define SSP_CMD_TIMEOUT (100)
#define SSP_PROBE_TIMEOUT (100)
#define SSP_MAX_TIMEOUT (3000)
struct lpcspifi_flash_bank {
int probed;
uint32_t ssp_base;
uint32_t io_base;
uint32_t ioconfig_base;
uint32_t bank_num;
uint32_t max_spi_clock_mhz;
struct flash_device *dev;
};
struct lpcspifi_target {
char *name;
uint32_t tap_idcode;
uint32_t spifi_base;
uint32_t ssp_base;
uint32_t io_base;
uint32_t ioconfig_base; /* base address for the port word pin registers */
};
static struct lpcspifi_target target_devices[] = {
/* name, tap_idcode, spifi_base, ssp_base, io_base, ioconfig_base */
{ "LPC43xx/18xx", 0x4ba00477, 0x14000000, 0x40083000, 0x400F4000, 0x40086000 },
{ NULL, 0, 0, 0, 0, 0 }
};
/* flash_bank lpcspifi <base> <size> <chip_width> <bus_width> <target>
*/
FLASH_BANK_COMMAND_HANDLER(lpcspifi_flash_bank_command)
{
struct lpcspifi_flash_bank *lpcspifi_info;
if (CMD_ARGC < 6)
return ERROR_COMMAND_SYNTAX_ERROR;
lpcspifi_info = malloc(sizeof(struct lpcspifi_flash_bank));
if (lpcspifi_info == NULL) {
LOG_ERROR("not enough memory");
return ERROR_FAIL;
}
bank->driver_priv = lpcspifi_info;
lpcspifi_info->probed = 0;
return ERROR_OK;
}
static inline int ioconfig_write_reg(struct target *target, uint32_t ioconfig_base, uint32_t offset, uint32_t value)
{
return target_write_u32(target, ioconfig_base + offset, value);
}
static inline int ssp_write_reg(struct target *target, uint32_t ssp_base, uint32_t offset, uint32_t value)
{
return target_write_u32(target, ssp_base + offset, value);
}
static inline int io_write_reg(struct target *target, uint32_t io_base, uint32_t offset, uint32_t value)
{
return target_write_u32(target, io_base + offset, value);
}
static inline int ssp_read_reg(struct target *target, uint32_t ssp_base, uint32_t offset, uint32_t *value)
{
return target_read_u32(target, ssp_base + offset, value);
}
static int ssp_setcs(struct target *target, uint32_t io_base, unsigned int value)
{
return io_write_reg(target, io_base, 0x12ac, value ? 0xffffffff : 0x00000000);
}
/* Poll the SSP busy flag. When this comes back as 0, the transfer is complete
* and the controller is idle. */
static int poll_ssp_busy(struct target *target, uint32_t ssp_base, int timeout)
{
long long endtime;
uint32_t value;
int retval;
retval = ssp_read_reg(target, ssp_base, SSP_SR, &value);
if ((retval == ERROR_OK) && (value & SSP_BSY) == 0)
return ERROR_OK;
else if (retval != ERROR_OK)
return retval;
endtime = timeval_ms() + timeout;
do {
alive_sleep(1);
retval = ssp_read_reg(target, ssp_base, SSP_SR, &value);
if ((retval == ERROR_OK) && (value & SSP_BSY) == 0)
return ERROR_OK;
else if (retval != ERROR_OK)
return retval;
} while (timeval_ms() < endtime);
LOG_ERROR("Timeout while polling BSY");
return ERROR_FLASH_OPERATION_FAILED;
}
/* 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_algorith