Commit 87098936 authored by Pavel Boldin's avatar Pavel Boldin

RT #89962: Added support for EA LPC4357 OEM board

parent f7b4a226
......@@ -3271,6 +3271,9 @@ k70-som_config : unconfig
lpc4350-eval_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm_cortexm3 lpc4350-eval hitex lpc18xx
ea-lpc4357_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm_cortexm3 ea-lpc4357 nxp lpc18xx
lpc1850-eval_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm_cortexm3 lpc1850-eval hitex lpc18xx
......
......@@ -36,6 +36,7 @@
#include <asm/arch/lpc18xx_scu.h>
#include <asm/arch/lpc18xx_creg.h>
#include <asm/arch/lpc18xx_ccu.h>
#include <asm/arch/lpc18xx_emc.h>
/*
* IS42S16400F SDRAM: 16-bit, 4 banks, 12 row bits, 8 column bits.
......@@ -168,80 +169,6 @@
/* CAS latency */
#define LPC_EMC_DYRASCAS_CAS_BITS 8
/*
* EMC per-chip registers for DRAM.
*
* This structure must be 0x20 bytes in size
* (for `struct lpc_emc_regs` to be correct.)
*/
struct lpc_emc_dy_regs {
u32 cfg; /* Dynamic Memory Configuration register */
u32 rascas; /* Dynamic Memory RAS & CAS Delay registers */
u32 rsv0[6];
};
/*
* EMC controls for Static Memory CS. Each block occupies 0x20 bytes.
*/
struct lpc_emc_st_regs {
u32 cfg; /* Static Memory Configuration register */
u32 we; /* CS to WE delay register */
u32 oe; /* CS to OE delay register */
u32 rd; /* CS to Read delay register */
u32 page; /* async page mode access delay */
u32 wr; /* CS to Write delay register */
u32 ta; /* number of turnaround cycles */
u32 rsv0[1];
};
/*
* EMC (External Memory Controller) register map
*/
struct lpc_emc_regs {
/* 0x000 */
u32 emcctrl; /* EMC Control register */
u32 emcsts; /* EMC Status register */
u32 emccfg; /* EMC Configuration register */
u32 rsv0[5];
/* 0x020 */
u32 dy_ctrl; /* Dynamic Memory Control register */
u32 dy_rfsh; /* Dynamic Memory Refresh Timer register */
u32 dy_rdcfg; /* Dynamic Memory Read Configuration register */
u32 rsv1;
/* 0x030 */
u32 dy_trp; /* Dynamic Memory Precharge Command Period register */
u32 dy_tras; /* Dynamic Memory Active to Precharge Command
Period register */
u32 dy_srex; /* Dynamic Memory Self-refresh Exit Time register */
u32 dy_apr; /* Dynamic Memory Last Data Out to Active
Time register */
u32 dy_dal; /* Dynamic Memory Data-in to Active Command
Time register */
u32 dy_wr; /* Dynamic Memory Write Recovery Time register */
u32 dy_rc; /* Dynamic Memory Active to Active Command
Period register */
u32 dy_rfc; /* Dynamic Memory Auto-refresh Period register */
u32 dy_xsr; /* Dynamic Memory Exit Self-refresh register */
u32 dy_rrd; /* Dynamic Memory Active Bank A to
Active Bank B Time register */
u32 dy_mrd; /* Dynamic Memory Load Mode register to
Active Command Time */
/* 0x05C */
u32 rsv2[41];
/* 0x100 */
struct lpc_emc_dy_regs dy[4]; /* 4 DRAM chips are possible */
u32 rsv3[32];
/* 0x200 */
struct lpc_emc_st_regs st[4]; /* 4 static memory devices */
};
#define LPC18XX_EMC_BASE 0x40005000
#define LPC_EMC ((volatile struct lpc_emc_regs *) \
LPC18XX_EMC_BASE)
DECLARE_GLOBAL_DATA_PTR;
/*
......
#
# (C) Copyright 2011
#
# Alexander Potashev, Emcraft Systems, aspotashev@emcraft.com
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
COBJS := board.o
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
$(LIB): $(obj).depend $(OBJS)
$(AR) $(ARFLAGS) $@ $(OBJS)
clean:
rm -f $(OBJS)
distclean: clean
rm -f $(LIB) core *.bak $(obj).depend
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
This diff is collapsed.
......@@ -28,6 +28,10 @@ PLATFORM_CPPFLAGS += -DCONFIG_MEM_RAM_LEN=$(CONFIG_MEM_RAM_LEN)
PLATFORM_CPPFLAGS += -DCONFIG_MEM_RAM_BUF_LEN=$(CONFIG_MEM_RAM_BUF_LEN)
PLATFORM_CPPFLAGS += -DCONFIG_MEM_MALLOC_LEN=$(CONFIG_MEM_MALLOC_LEN)
PLATFORM_CPPFLAGS += -DCONFIG_MEM_STACK_LEN=$(CONFIG_MEM_STACK_LEN)
ifdef CONFIG_MEM_RAMCODE_BASE
PLATFORM_CPPFLAGS += -DCONFIG_MEM_RAMCODE_BASE=$(CONFIG_MEM_RAMCODE_BASE)
PLATFORM_CPPFLAGS += -DCONFIG_MEM_RAMCODE_LEN=$(CONFIG_MEM_RAMCODE_LEN)
endif
PLATFORM_CPPFLAGS += -I$(TOPDIR)/cpu/$(CPU)
......
......@@ -27,6 +27,10 @@
*/
void envm_init(void);
#if !defined(CONFIG_ARMCORTEXM3_RAMCODE) && defined(CONFIG_LPC43XX_ENVM)
# error "LPC43XX ENVM requires RAMCODE"
#endif
/*
* Write a data buffer to eNVM.
* Note that we need for this function to reside in RAM since it
......
......@@ -29,7 +29,12 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(SOC).a
COBJS := clock.o cpu.o envm.o wdt.o
COBJS := clock.o cpu.o wdt.o
ifeq ($(CONFIG_LPC43XX_ENVM),y)
COBJS += envm-lpc43xx.o
else
COBJS += envm-lpc18xx.o
endif
ifeq ($(CONFIG_SPIFI),y)
COBJS += spifi.o
endif
......
......@@ -23,6 +23,7 @@
#include <asm/errno.h>
#include <asm/arch/lpc18xx_creg.h>
#include <asm/arch/lpc18xx.h>
#include "clock.h"
/*
......@@ -193,12 +194,6 @@ struct lpc18xx_rgu_regs {
*/
static u32 clock_val[CLOCK_END];
/*
* Set LPC18XX_PLL1_CLK_OUT to the output rate of PLL1
*/
#define LPC18XX_PLL1_CLK_OUT \
(CONFIG_LPC18XX_EXTOSC_RATE * CONFIG_LPC18XX_PLL1_M)
/*
* Compile time sanity checks for defined board clock setup
*/
......@@ -211,7 +206,7 @@ static u32 clock_val[CLOCK_END];
* of 156 MHz to 320 MHz, so that the PLL1 Direct Mode is applicable.
* Our clock configuration code (`clock_setup()`) support only this mode.
*/
#if LPC18XX_PLL1_CLK_OUT < 156000000
#if LPC18XX_PLL1_CLK_OUT < 144000000
#error Requested PLL1 output frequency is too low
#endif
#if LPC18XX_PLL1_CLK_OUT > 320000000
......@@ -337,6 +332,7 @@ void eth_clock_setup(void)
int timeout;
int rv;
#ifndef CONFIG_LPC18XX_ENET_USE_PHY_RMII
/*
* This clock configuration is valid only for MII
*/
......@@ -344,13 +340,18 @@ void eth_clock_setup(void)
LPC18XX_CGU_CLKSEL_ENET_RX | LPC18XX_CGU_AUTOBLOCK_MSK;
LPC18XX_CGU->phy_tx_clk =
LPC18XX_CGU_CLKSEL_ENET_TX | LPC18XX_CGU_AUTOBLOCK_MSK;
#endif /* CONFIG_LPC18XX_ENET_USE_PHY_RMII */
/*
* Choose the MII Ethernet mode
*/
LPC18XX_CREG->creg6 =
(LPC18XX_CREG->creg6 & ~LPC18XX_CREG_CREG6_ETHMODE_MSK) |
#ifndef CONFIG_LPC18XX_ENET_USE_PHY_RMII
LPC18XX_CREG_CREG6_ETHMODE_MII;
#else
LPC18XX_CREG_CREG6_ETHMODE_RMII;
#endif
/*
* Reset the Ethernet module of the MCU
......
/*
* (C) Copyright 2011-2013
*
* Alexander Potashev, Emcraft Systems, aspotashev@emcraft.com
* Pavel Boldin, Emcraft Systems, paboldin@emcraft.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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <errno.h>
#include "envm.h"
/*
* IAP library for editing eNVM
*/
/* IAP function pointer */
#define IAP_LOCATION_PTR 0x10400100
typedef void (*lpc43xn_iap) (u32 *, u32 *);
/* IAP commands */
#define IAP_CMD_INIT 49
#define IAP_CMD_PREP_SECTORS 50
#define IAP_CMD_RAM_TO_FLASH 51
#define IAP_CMD_ERASE_SECTORS 52
#define IAP_CMD_BLANK_CHECK_SECTORS 53
#define IAP_CMD_READ_PART_ID 54
/* IAP statuses */
#define IAP_STATUS_SUCCESS 0
/*
* Block size used for the "Copy RAM to Flash" operation
*
* Can be 256, 512, 1024 or 4096
*/
#define IAP_BLOCK_SIZE 512
#define IAP_BLOCK_MASK (IAP_BLOCK_SIZE - 1)
static u32 iap_clkrate; /* CPU clock in KHz */
/*
* List of block addresses and sizes
*
* IMPORTANT: We force this data into the `.data` section, because otherwise
* it will be in eNVM and will be overwritten on self-upgrade.
*/
static lpc43xn_iap lpc43xn_iap_entry __attribute__((section(".data")));
struct lpc43xn_flash_layout {
int sectors;
int size;
};
static struct lpc43xn_flash_layout flash_layout[]
__attribute__((section(".data"))) =
{
{ 8, 8192},
{ 7, 65536}
};
static u32 iap_commands[6];
static u32 iap_results[5];
/*
* A temporary buffer in SRAM (section `.bss`) used for the "Copy RAM to Flash"
* operation. We need this buffer, because direct transfer from the external RAM
* does not work.
*
* This buffer should be aligned on a 4 byte boundary.
*/
static u8 iap_sram_buf[IAP_BLOCK_SIZE] __attribute__((aligned(4)));
/*
* Prepare sectors for erase or write
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
lpc43xn_iap_prepare_sectors(u32 start, u32 end, u32 bank)
{
iap_commands[0] = IAP_CMD_PREP_SECTORS;
iap_commands[1] = start;
iap_commands[2] = end;
iap_commands[3] = bank;
lpc43xn_iap_entry(iap_commands, iap_results);
return iap_results[0];
}
/*
* Copy RAM to FLASH
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
lpc43xn_iap_ram_to_flash(u32 dst, u32 src, u32 bytes)
{
iap_commands[0] = IAP_CMD_RAM_TO_FLASH;
iap_commands[1] = dst;
iap_commands[2] = src;
iap_commands[3] = bytes;
iap_commands[4] = iap_clkrate;
lpc43xn_iap_entry(iap_commands, iap_results);
return iap_results[0];
}
/*
* Erase sectors
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
lpc43xn_iap_erase_sectors(u32 start, u32 end, u32 bank)
{
iap_commands[0] = IAP_CMD_ERASE_SECTORS;
iap_commands[1] = start;
iap_commands[2] = end;
iap_commands[3] = iap_clkrate;
iap_commands[4] = bank;
lpc43xn_iap_entry(iap_commands, iap_results);
return iap_results[0];
}
/*
* Blank check sectors
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
lpc43xn_iap_blank_check_sectors(
u32 start, u32 end, u32 bank, u32 *bad_addr, u32 *bad_data)
{
iap_commands[0] = IAP_CMD_BLANK_CHECK_SECTORS;
iap_commands[1] = start;
iap_commands[2] = end;
iap_commands[3] = bank;
lpc43xn_iap_entry(iap_commands, iap_results);
*bad_addr = iap_results[1];
*bad_data = iap_results[2];
return iap_results[0];
}
/*
* Initialize IAP library - call this first
*
* This function should not be in .ramcode, because it will be called only once
* before self-upgrade.
*/
void lpc43xn_iap_init(void)
{
lpc43xn_iap_entry = (lpc43xn_iap) *(u32*)IAP_LOCATION_PTR;
iap_commands[0] = IAP_CMD_INIT;
lpc43xn_iap_entry(iap_commands, iap_results);
if (iap_results[0] != IAP_STATUS_SUCCESS) {
printf("%s: failed %d\n", __func__, iap_results[0]);
return;
}
}
/*
* Initialize internal Flash interface
*
* This function should not be in .ramcode, because it will be called only once
* before self-upgrade.
*/
void envm_init(void)
{
lpc43xn_iap_init();
}
/*
* `addr` is the offset from the beginning of eNVM
*/
static u32 __attribute__((section(".ramcode")))
__attribute__((long_call))
find_sector(u32 addr, u32 bank) {
u32 i, j, current = CONFIG_MEM_NVM_BASE, sector = 0;
#ifdef CONFIG_MEM_NVM2_BASE
current = bank ? CONFIG_MEM_NVM2_BASE : CONFIG_MEM_NVM_BASE;
#endif
for (i = 0; flash_layout[i].sectors; i++)
{
for (j = 0; j < flash_layout[i].sectors; j++)
{
if (addr < current + flash_layout[i].size)
{
return sector;
}
sector++;
current += flash_layout[i].size;
}
}
return -1;
}
/*
* Erase FLASH sectors
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
lpc43xn_flash_erase(u32 offset, u32 size, u32 bank)
{
int rv;
u32 badaddr, baddata;
u32 first, last;
/*
* Convert (offset, size) to (first, last)
*/
first = find_sector(offset, bank);
last = find_sector(offset + size - 1, bank);
if (first < 0 || last < first ||
last >=
#ifdef CONFIG_MEM_NVM2_BASE
bank ? CONFIG_MEM_NVM2_SECTORS :
#endif
CONFIG_MEM_NVM_SECTORS) {
rv = -EINVAL;
goto out;
}
if (lpc43xn_iap_prepare_sectors(first, last, bank)
!= IAP_STATUS_SUCCESS) {
rv = -EIO;
goto out;
}
if (lpc43xn_iap_erase_sectors(first, last, bank)
!= IAP_STATUS_SUCCESS) {
rv = -EIO;
goto out;
}
if (lpc43xn_iap_blank_check_sectors(first, last,
bank, &badaddr, &baddata) != IAP_STATUS_SUCCESS) {
rv = -EIO;
goto out;
}
rv = 0;
out:
return rv;
}
/*
* Copy memory buffer to FLASH
*/
int __attribute__((section(".ramcode")))
__attribute__((long_call))
lpc43xn_flash_program(u32 dest_addr, u8 *src, u32 size)
{
int rv;
u32 offset; /* Offset of the current block being written */
u32 i;
u32 sect, bank = 0;
#ifdef CONFIG_MEM_NVM2_BASE
bank = CONFIG_MEM_NVM2_BASE <= dest_addr;
#endif
u8 *dest = (u8 *)dest_addr;
/*
* Write size must be on a block boundary
*/
if (size & IAP_BLOCK_MASK) {
rv = -EINVAL;
goto out;
}
/*
* Write address must be on a 256 byte boundary
* (even if IAP_BLOCK_SIZE is not 256)
*/
if (dest_addr & 0xFF) {
rv = -EINVAL;
goto out;
}
/*
* Write range should not exceed the end of FLASH
*/
if (size >
#ifdef CONFIG_MEM_NVM2_LEN
bank ? CONFIG_MEM_NVM2_LEN :
#endif
CONFIG_MEM_NVM_LEN) {
rv = -EINVAL;
goto out;
}
/*
* Check that the destination area is erased
*/
for (i = 0; i < size; i ++) {
if (dest[i] != 0xFF) {
/* FLASH is not blank */
rv = -EEXIST;
goto out;
}
}
/*
* Write data in blocks of size IAP_BLOCK_SIZE
*/
for (offset = 0; offset < size; offset += IAP_BLOCK_SIZE) {
sect = find_sector(dest_addr + offset, bank);
if (lpc43xn_iap_prepare_sectors(sect, sect, bank) !=
IAP_STATUS_SUCCESS) {
rv = -EACCES;
goto out;
}
/*
* Copy block into the second SRAM region
* ("Peripheral RAM"). Cannot use `memcpy()` here, because it
* resides in eNVM, not in `.ramcode` section.
*/
for (i = 0; i < IAP_BLOCK_SIZE; i ++)
iap_sram_buf[i] = src[offset + i];
/*
* Copy block from RAM to FLASH
*/
if (lpc43xn_iap_ram_to_flash(
dest_addr + offset, (u32)iap_sram_buf, IAP_BLOCK_SIZE) !=
IAP_STATUS_SUCCESS) {
rv = -EACCES;
goto out;
}
}
/*
* Verify
*/
for (i = 0; i < size; i ++) {
if (dest[i] != src[i]) {
rv = -EAGAIN;
goto out;
}
}
rv = 0;
out:
return rv;
}
/*
* Write a data buffer to internal Flash.
* Note that we need for this function to reside in RAM since it
* will be used to self-upgrade U-boot in internal Flash.
*/
u32 __attribute__((section(".ramcode")))
__attribute__((long_call))
envm_write(u32 offset, void *buf, u32 size)
{
u32 rv = 0, bank = 0;
/*
* Clock in is CPU in KHz
*
* This initialization code cannot be put into `envm_init()`, because
* at the time when `envm_init()` is called `clock_get()` is not yet
* initialized.
*/
iap_clkrate = clock_get(CLOCK_SYSTICK) / 1000;
if ((offset < CONFIG_MEM_NVM_BASE ||
offset + size > CONFIG_MEM_NVM_BASE + CONFIG_MEM_NVM_LEN)
#ifdef CONFIG_MEM_NVM2_BASE
&& (offset < CONFIG_MEM_NVM2_BASE ||
offset + size > CONFIG_MEM_NVM2_BASE + CONFIG_MEM_NVM2_LEN)
#endif
) {