summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/lpc32xx/misc
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2010-06-23 08:27:57 +0000
committerSebastian Huber <sebastian.huber@embedded-brains.de>2010-06-23 08:27:57 +0000
commit3103d4cbad582cc9e62aaf15fd5af308949a83f0 (patch)
treeb1ff4f68908b3177c614349a2f0252e4b64e9b18 /c/src/lib/libbsp/arm/lpc32xx/misc
parent2010-06-23 Sebastian Huber <sebastian.huber@embedded-brains.de> (diff)
downloadrtems-3103d4cbad582cc9e62aaf15fd5af308949a83f0.tar.bz2
2010-06-23 Sebastian Huber <sebastian.huber@embedded-brains.de>
* make/custom/lpc32xx_mzx_boot_int.cfg, startup/linkcmds.lpc32xx_mzx_boot_int: Removed files. * include/boot.h, include/emc.h, include/i2c.h, include/nand-mlc.h, make/custom/lpc32xx_mzx.cfg, make/custom/lpc32xx_mzx_stage_1.cfg, make/custom/lpc32xx_mzx_stage_2.cfg, misc/boot.c, misc/emc.c, misc/i2c.c, misc/nand-mlc.c, misc/nand-mlc-read-blocks.c, misc/nand-mlc-write-blocks.c, misc/restart.c, startup/linkcmds.lpc32xx, startup/linkcmds.lpc32xx_mzx, startup/linkcmds.lpc32xx_mzx_stage_1, startup/linkcmds.lpc32xx_mzx_stage_2: New files. * configure.ac, Makefile.am, preinstall.am: Reflect changes above. * include/bsp.h, include/lpc32xx.h, irq/irq.c, rtc/rtc-config.c, startup/bspstart.c, startup/bspstarthooks.c, startup/linkcmds.lpc32xx_phycore: Changes throughout.
Diffstat (limited to 'c/src/lib/libbsp/arm/lpc32xx/misc')
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/boot.c54
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/emc.c124
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c259
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c132
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c166
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c322
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/misc/restart.c56
7 files changed, 1113 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/boot.c b/c/src/lib/libbsp/arm/lpc32xx/misc/boot.c
new file mode 100644
index 0000000000..0f624114f3
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/boot.c
@@ -0,0 +1,54 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx_boot
+ *
+ * @brief Boot support implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <string.h>
+
+#include <bsp/boot.h>
+
+void lpc32xx_setup_boot_block(
+ lpc32xx_boot_block *boot_block,
+ uint8_t icr,
+ uint8_t page_count
+)
+{
+ memset(boot_block, 0, sizeof(*boot_block));
+
+ ++page_count;
+
+ boot_block->field.d0 = icr;
+ boot_block->field.d2 = icr;
+ boot_block->field.d4 = page_count;
+ boot_block->field.d6 = page_count;
+ boot_block->field.d8 = page_count;
+ boot_block->field.d10 = page_count;
+
+ icr = (uint8_t) ~((unsigned) icr);
+ page_count = (uint8_t) ~((unsigned) page_count);
+
+ boot_block->field.d1 = icr;
+ boot_block->field.d3 = icr;
+ boot_block->field.d5 = page_count;
+ boot_block->field.d7 = page_count;
+ boot_block->field.d9 = page_count;
+ boot_block->field.d11 = page_count;
+
+ boot_block->field.d12 = 0xaa;
+}
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/emc.c b/c/src/lib/libbsp/arm/lpc32xx/misc/emc.c
new file mode 100644
index 0000000000..0a6d75c5bc
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/emc.c
@@ -0,0 +1,124 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx_emc
+ *
+ * @brief EMC support implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <bsp/emc.h>
+
+#include <bsp.h>
+#include <bsp/mmu.h>
+
+static volatile lpc32xx_emc *const emc = &lpc32xx.emc;
+
+static void set_translation_table_entries(
+ uint32_t begin,
+ uint32_t size
+)
+{
+ uint32_t end = begin + size;
+ uint32_t *ttb = arm_cp15_get_translation_table_base();
+ uint32_t i = ARM_MMU_SECT_GET_INDEX(begin);
+ uint32_t iend = ARM_MMU_SECT_GET_INDEX(ARM_MMU_SECT_MVA_ALIGN_UP(end));
+
+ while (i < iend) {
+ ttb [i] = (i << ARM_MMU_SECT_BASE_SHIFT) | LPC32XX_MMU_READ_WRITE;
+ ++i;
+ }
+}
+
+static void dynamic_init(const lpc32xx_emc_dynamic_config *cfg)
+{
+ uint32_t chip_begin = LPC32XX_BASE_EMC_DYCS_0;
+ uint32_t dynamiccontrol = (cfg->control | EMC_DYN_CTRL_CE | EMC_DYN_CTRL_CS)
+ & ~EMC_DYN_CTRL_I_MASK;
+ size_t i = 0;
+
+ LPC32XX_SDRAMCLK_CTRL = cfg->sdramclk_ctrl;
+
+ emc->dynamicreadconfig = cfg->readconfig;
+
+ /* Timings */
+ emc->dynamictrp = cfg->trp;
+ emc->dynamictras = cfg->tras;
+ emc->dynamictsrex = cfg->tsrex;
+ emc->dynamictwr = cfg->twr;
+ emc->dynamictrc = cfg->trc;
+ emc->dynamictrfc = cfg->trfc;
+ emc->dynamictxsr = cfg->txsr;
+ emc->dynamictrrd = cfg->trrd;
+ emc->dynamictmrd = cfg->tmrd;
+ emc->dynamictcdlr = cfg->tcdlr;
+ for (i = 0; i < EMC_DYN_CHIP_COUNT; ++i) {
+ if (cfg->chip [i].size != 0) {
+ emc->dynamic [i].config = cfg->chip [i].config;
+ emc->dynamic [i].rascas = cfg->chip [i].rascas;
+ }
+ }
+
+ /* NOP period */
+ emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_NOP;
+ lpc32xx_micro_seconds_delay(cfg->nop_time_in_us);
+
+ /* Precharge */
+ emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_PALL;
+ emc->dynamicrefresh = 1;
+ /* FIXME: Why a delay, why this value? */
+ lpc32xx_micro_seconds_delay(10);
+
+ /* Refresh timing */
+ emc->dynamicrefresh = cfg->refresh;
+ /* FIXME: Why a delay, why this value? */
+ lpc32xx_micro_seconds_delay(16);
+
+ /* Set modes */
+ for (i = 0; i < EMC_DYN_CHIP_COUNT; ++i) {
+ if (cfg->chip [i].size != 0) {
+ set_translation_table_entries(chip_begin, cfg->chip [i].size);
+ emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_MODE;
+ *(volatile uint32_t *)(LPC32XX_BASE_EMC_DYCS_0 + cfg->chip [i].mode);
+ emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_MODE;
+ *(volatile uint32_t *)(LPC32XX_BASE_EMC_DYCS_0 + cfg->chip [i].extmode);
+ }
+ chip_begin += 0x20000000;
+ }
+
+ emc->dynamiccontrol = cfg->control;
+}
+
+void lpc32xx_emc_init(const lpc32xx_emc_dynamic_config *dyn_cfg)
+{
+ /* Enable clock */
+ LPC32XX_HCLKDIV_CTRL |= HCLK_DIV_DDRAM_CLK(1);
+
+ /* Enable buffers in AHB ports */
+ emc->ahb [0].control = EMC_AHB_PORT_BUFF_EN;
+ emc->ahb [3].control = EMC_AHB_PORT_BUFF_EN;
+ emc->ahb [4].control = EMC_AHB_PORT_BUFF_EN;
+
+ /* Set AHB port timeouts */
+ emc->ahb [0].timeout = EMC_AHB_TIMEOUT(32);
+ emc->ahb [3].timeout = EMC_AHB_TIMEOUT(32);
+ emc->ahb [4].timeout = EMC_AHB_TIMEOUT(32);
+
+ /* Enable EMC */
+ emc->control = EMC_CTRL_EN,
+ emc->config = 0;
+
+ dynamic_init(dyn_cfg);
+}
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c b/c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c
new file mode 100644
index 0000000000..e1ade9d4ac
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c
@@ -0,0 +1,259 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx_i2c
+ *
+ * @brief I2C support implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <rtems.h>
+
+#include <bsp.h>
+#include <bsp/i2c.h>
+
+void lpc32xx_i2c_reset(volatile lpc32xx_i2c *i2c)
+{
+ i2c->ctrl = I2C_CTRL_RESET;
+}
+
+rtems_status_code lpc32xx_i2c_init(
+ volatile lpc32xx_i2c *i2c,
+ unsigned clock_in_hz
+)
+{
+ uint32_t i2cclk = 0;
+
+ if (i2c == &lpc32xx.i2c_1) {
+ i2cclk |= I2CCLK_1_EN | I2CCLK_1_HIGH_DRIVE;
+ } else if (i2c == &lpc32xx.i2c_2) {
+ i2cclk |= I2CCLK_2_EN | I2CCLK_2_HIGH_DRIVE;
+ } else {
+ return RTEMS_INVALID_ID;
+ }
+
+ LPC32XX_I2CCLK_CTRL |= i2cclk;
+
+ lpc32xx_i2c_reset(i2c);
+
+ return lpc32xx_i2c_clock(i2c, clock_in_hz);
+}
+
+#if LPC32XX_HCLK != 104000000U
+ #error "unexpected HCLK"
+#endif
+
+rtems_status_code lpc32xx_i2c_clock(
+ volatile lpc32xx_i2c *i2c,
+ unsigned clock_in_hz
+)
+{
+ uint32_t clk_lo = 0;
+ uint32_t clk_hi = 0;
+
+ switch (clock_in_hz) {
+ case 100000:
+ clk_lo = 520;
+ clk_hi = 520;
+ break;
+ case 400000:
+ clk_lo = 166;
+ clk_hi = 94;
+ break;
+ default:
+ return RTEMS_INVALID_CLOCK;
+ }
+
+ i2c->clk_lo = clk_lo;
+ i2c->clk_hi = clk_hi;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code wait_for_transaction_done(volatile lpc32xx_i2c *i2c)
+{
+ uint32_t stat = 0;
+
+ do {
+ stat = i2c->stat;
+ } while ((stat & I2C_STAT_TDI) == 0);
+
+ if ((stat & I2C_STAT_TFE) != 0) {
+ i2c->stat = I2C_STAT_TDI;
+
+ return RTEMS_SUCCESSFUL;
+ } else {
+ lpc32xx_i2c_reset(i2c);
+
+ return RTEMS_IO_ERROR;
+ }
+}
+
+static rtems_status_code tx(volatile lpc32xx_i2c *i2c, uint32_t data)
+{
+ uint32_t stat = 0;
+
+ do {
+ stat = i2c->stat;
+ } while ((stat & (I2C_STAT_TFE | I2C_STAT_TDI)) == 0);
+
+ if ((stat & I2C_STAT_TDI) == 0) {
+ i2c->rx_or_tx = data;
+
+ return RTEMS_SUCCESSFUL;
+ } else {
+ lpc32xx_i2c_reset(i2c);
+
+ return RTEMS_IO_ERROR;
+ }
+}
+
+rtems_status_code lpc32xx_i2c_write_start(
+ volatile lpc32xx_i2c *i2c,
+ unsigned addr
+)
+{
+ return tx(i2c, I2C_TX_ADDR(addr) | I2C_TX_START);
+}
+
+rtems_status_code lpc32xx_i2c_read_start(
+ volatile lpc32xx_i2c *i2c,
+ unsigned addr
+)
+{
+ return tx(i2c, I2C_TX_ADDR(addr) | I2C_TX_START | I2C_TX_READ);
+}
+
+rtems_status_code lpc32xx_i2c_write_with_optional_stop(
+ volatile lpc32xx_i2c *i2c,
+ const uint8_t *out,
+ size_t n,
+ bool stop
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ size_t i = 0;
+
+ for (i = 0; i < n - 1 && sc == RTEMS_SUCCESSFUL; ++i) {
+ sc = tx(i2c, out [i]);
+ }
+
+ if (sc == RTEMS_SUCCESSFUL) {
+ uint32_t stop_flag = stop ? I2C_TX_STOP : 0;
+
+ sc = tx(i2c, out [n - 1] | stop_flag);
+ }
+
+ if (stop && sc == RTEMS_SUCCESSFUL) {
+ sc = wait_for_transaction_done(i2c);
+ }
+
+ return sc;
+}
+
+static bool can_tx_for_rx(volatile lpc32xx_i2c *i2c)
+{
+ return (i2c->stat & (I2C_STAT_TFF | I2C_STAT_RFF)) == 0;
+}
+
+static bool can_rx(volatile lpc32xx_i2c *i2c)
+{
+ return (i2c->stat & I2C_STAT_RFE) == 0;
+}
+
+rtems_status_code lpc32xx_i2c_read_with_optional_stop(
+ volatile lpc32xx_i2c *i2c,
+ uint8_t *in,
+ size_t n,
+ bool stop
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ size_t last = n - 1;
+ size_t rx = 0;
+ size_t tx = 0;
+
+ if (!stop) {
+ return RTEMS_NOT_IMPLEMENTED;
+ }
+
+ while (rx <= last) {
+ if ((i2c->stat & I2C_STAT_TDI) != 0) {
+ stop = true;
+
+ break;
+ }
+
+ while (tx < last && can_tx_for_rx(i2c)) {
+ i2c->rx_or_tx = 0;
+ ++tx;
+ }
+
+ if (tx == last && can_tx_for_rx(i2c)) {
+ uint32_t stop_flag = stop ? I2C_TX_STOP : 0;
+
+ i2c->rx_or_tx = stop_flag;
+ ++tx;
+ }
+
+ while (rx <= last && can_rx(i2c)) {
+ in [rx] = (uint8_t) i2c->rx_or_tx;
+ ++rx;
+ }
+ }
+
+ if (stop) {
+ sc = wait_for_transaction_done(i2c);
+ }
+
+ return sc;
+}
+
+rtems_status_code lpc32xx_i2c_write_and_read(
+ volatile lpc32xx_i2c *i2c,
+ unsigned addr,
+ const uint8_t *out,
+ size_t out_size,
+ uint8_t *in,
+ size_t in_size
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (out_size > 0) {
+ bool stop = in_size == 0;
+
+ sc = lpc32xx_i2c_write_start(i2c, addr);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ sc = lpc32xx_i2c_write_with_optional_stop(i2c, out, out_size, stop);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+ }
+
+ if (in_size > 0) {
+ sc = lpc32xx_i2c_read_start(i2c, addr);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ lpc32xx_i2c_read_with_optional_stop(i2c, in, in_size, true);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c b/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c
new file mode 100644
index 0000000000..7d00d0e52a
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c
@@ -0,0 +1,132 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx_nand_mlc
+ *
+ * @brief lpc32xx_mlc_read_blocks() implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <bsp/nand-mlc.h>
+
+static rtems_status_code read_page(
+ uint32_t first_page_of_block,
+ uint32_t page,
+ uint32_t page_data [MLC_LARGE_DATA_WORD_COUNT],
+ uint32_t page_spare [MLC_LARGE_SPARE_WORD_COUNT]
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ uint32_t page_index = first_page_of_block + page;
+ bool possible_bad_page = page == 0 || page == 1;
+
+ if (possible_bad_page) {
+ memset(page_spare, 0, MLC_LARGE_SPARE_SIZE);
+ }
+
+ sc = lpc32xx_mlc_read_page(page_index, page_data, page_spare);
+ if (possible_bad_page && lpc32xx_mlc_is_bad_page(page_spare)) {
+ return RTEMS_UNSATISFIED;
+ } else if (sc == RTEMS_SUCCESSFUL) {
+ return RTEMS_SUCCESSFUL;
+ } else {
+ return sc;
+ }
+}
+
+rtems_status_code lpc32xx_mlc_read_blocks(
+ uint32_t block_begin,
+ uint32_t block_end,
+ lpc32xx_mlc_read_process process,
+ void *process_arg,
+ uint32_t page_buffer_0 [MLC_LARGE_DATA_WORD_COUNT],
+ uint32_t page_buffer_1 [MLC_LARGE_DATA_WORD_COUNT]
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ uint32_t page_spare_0 [MLC_LARGE_SPARE_WORD_COUNT];
+ uint32_t page_spare_1 [MLC_LARGE_SPARE_WORD_COUNT];
+ uint32_t pages_per_block = lpc32xx_mlc_pages_per_block();
+ uint32_t page_size = lpc32xx_mlc_page_size();
+ uint32_t block = 0;
+ uint32_t first_page_of_block = block_begin * pages_per_block;
+
+ for (
+ block = block_begin;
+ block != block_end;
+ ++block, first_page_of_block += pages_per_block
+ ) {
+ uint32_t page = 0;
+ bool done = false;
+
+ sc = read_page(first_page_of_block, 0, page_buffer_0, page_spare_0);
+ if (sc == RTEMS_UNSATISFIED) {
+ continue;
+ } else if (sc != RTEMS_SUCCESSFUL) {
+ goto done;
+ }
+
+ sc = read_page(first_page_of_block, 1, page_buffer_1, page_spare_1);
+ if (sc == RTEMS_UNSATISFIED) {
+ continue;
+ } else if (sc != RTEMS_SUCCESSFUL) {
+ goto done;
+ }
+
+ done = (*process)(
+ process_arg,
+ first_page_of_block + 0,
+ page_size,
+ page_buffer_0,
+ page_spare_0
+ );
+ if (done) {
+ goto done;
+ }
+
+ done = (*process)(
+ process_arg,
+ first_page_of_block + 1,
+ page_size,
+ page_buffer_1,
+ page_spare_1
+ );
+ if (done) {
+ goto done;
+ }
+
+ for (page = 2; page < pages_per_block; ++page) {
+ sc = read_page(first_page_of_block, page, page_buffer_1, page_spare_1);
+ if (sc != RTEMS_SUCCESSFUL) {
+ goto done;
+ }
+
+ done = (*process)(
+ process_arg,
+ first_page_of_block + page,
+ page_size,
+ page_buffer_1,
+ page_spare_1
+ );
+ if (done) {
+ goto done;
+ }
+ }
+ }
+
+done:
+
+ return sc;
+}
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c b/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c
new file mode 100644
index 0000000000..8b9c438602
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c
@@ -0,0 +1,166 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx_nand_mlc
+ *
+ * @brief lpc32xx_mlc_write_blocks() implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <bsp/nand-mlc.h>
+
+#include <string.h>
+
+#include <bsp.h>
+
+static const uint32_t ones_spare [MLC_LARGE_SPARE_WORD_COUNT] = {
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff,
+ 0xffffffff
+};
+
+static void zero_block(uint32_t first_page_of_block, uint32_t pages_per_block)
+{
+ uint32_t page = 0;
+
+ for (page = 0; page < pages_per_block; ++page) {
+ lpc32xx_mlc_write_page_with_ecc(
+ first_page_of_block + page,
+ lpc32xx_magic_zero_begin,
+ lpc32xx_magic_zero_begin
+ );
+ }
+}
+
+static bool is_bad_page(
+ uint32_t first_page_of_block,
+ uint32_t page
+)
+{
+ uint32_t spare [MLC_LARGE_SPARE_WORD_COUNT];
+
+ memset(spare, 0, MLC_LARGE_SPARE_SIZE);
+ lpc32xx_mlc_read_page(
+ first_page_of_block + page,
+ lpc32xx_magic_zero_begin,
+ spare
+ );
+ return lpc32xx_mlc_is_bad_page(spare);
+}
+
+static rtems_status_code erase_block(
+ uint32_t block,
+ uint32_t first_page_of_block,
+ uint32_t pages_per_block
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (is_bad_page(first_page_of_block, 0)) {
+ return RTEMS_IO_ERROR;
+ }
+
+ if (is_bad_page(first_page_of_block, 1)) {
+ return RTEMS_IO_ERROR;
+ }
+
+ sc = lpc32xx_mlc_erase_block(block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ zero_block(first_page_of_block, pages_per_block);
+
+ return RTEMS_IO_ERROR;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code lpc32xx_mlc_write_blocks(
+ uint32_t block_begin,
+ uint32_t block_end,
+ const void *src,
+ size_t src_size,
+ uint32_t *page_data_buffer
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ uint32_t pages_per_block = lpc32xx_mlc_pages_per_block();
+ uint32_t block_count = lpc32xx_mlc_block_count();
+ uint32_t page_size = lpc32xx_mlc_page_size();
+ uint32_t block = 0;
+ const uint8_t *current = src;
+ const uint8_t *last = current;
+ const uint8_t *end = current + src_size;
+
+ if (block_begin > block_end || block_end > block_count) {
+ return RTEMS_INVALID_ID;
+ }
+
+ for (block = block_begin; block != block_end; ++block) {
+ uint32_t first_page_of_block = block * pages_per_block;
+ uint32_t page = 0;
+
+ sc = erase_block(block, first_page_of_block, pages_per_block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ continue;
+ }
+
+ for (page = 0; page < pages_per_block; ++page) {
+ uintptr_t remainder = (uintptr_t) end - (uintptr_t) current;
+ size_t delta = remainder < page_size ? remainder : page_size;
+
+ if (remainder > 0) {
+ memcpy(page_data_buffer, current, delta);
+ sc = lpc32xx_mlc_write_page_with_ecc(
+ first_page_of_block + page,
+ page_data_buffer,
+ ones_spare
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ erase_block(block, first_page_of_block, pages_per_block);
+ zero_block(first_page_of_block, pages_per_block);
+ current = last;
+ continue;
+ }
+
+ current += delta;
+ } else {
+ goto done;
+ }
+ }
+
+ last = current;
+ }
+
+done:
+
+ if (current != end) {
+ return RTEMS_IO_ERROR;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c b/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c
new file mode 100644
index 0000000000..16be2c98c2
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c
@@ -0,0 +1,322 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx_nand_mlc
+ *
+ * @brief NAND MLC controller implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <bsp/lpc32xx.h>
+#include <bsp/nand-mlc.h>
+
+static volatile lpc32xx_nand_mlc *const mlc = &lpc32xx.nand_mlc;
+
+static bool mlc_small_pages;
+
+static bool mlc_many_address_cycles;
+
+static bool mlc_normal_blocks;
+
+static uint32_t mlc_block_count;
+
+static uint32_t mlc_page_count;
+
+uint32_t lpc32xx_mlc_page_size(void)
+{
+ if (mlc_small_pages) {
+ return 512;
+ } else {
+ return 2048;
+ }
+}
+
+uint32_t lpc32xx_mlc_pages_per_block(void)
+{
+ if (mlc_small_pages) {
+ return 32;
+ } else {
+ if (mlc_normal_blocks) {
+ return 64;
+ } else {
+ return 128;
+ }
+ }
+}
+
+uint32_t lpc32xx_mlc_block_count(void)
+{
+ return mlc_block_count;
+}
+
+static void mlc_unlock(void)
+{
+ mlc->lock_pr = MLC_UNLOCK_PROT;
+}
+
+static void mlc_wait(uint32_t flags)
+{
+ while ((mlc->isr & flags) != flags) {
+ /* Wait */
+ }
+}
+
+static void mlc_wait_until_ready(void)
+{
+ mlc_wait(MLC_ISR_CONTROLLER_READY | MLC_ISR_NAND_READY);
+}
+
+static void mlc_reset(void)
+{
+ mlc->cmd = 0xff;
+}
+
+static uint32_t mlc_status(void)
+{
+ mlc_wait_until_ready();
+ mlc->cmd = 0x70;
+
+ return mlc->data.w8;
+}
+
+static bool mlc_was_operation_successful(void)
+{
+ return (mlc_status() & (NAND_STATUS_READY | NAND_STATUS_ERROR))
+ == NAND_STATUS_READY;
+}
+
+static void mlc_set_block_address(uint32_t block_index)
+{
+ if (mlc_small_pages) {
+ mlc->addr = (uint8_t) (block_index << 5);
+ mlc->addr = (uint8_t) (block_index >> 3);
+ if (mlc_many_address_cycles) {
+ mlc->addr = (uint8_t) (block_index >> 11);
+ }
+ } else {
+ if (mlc_normal_blocks) {
+ mlc->addr = (uint8_t) (block_index << 6);
+ mlc->addr = (uint8_t) (block_index >> 2);
+ if (mlc_many_address_cycles) {
+ mlc->addr = (uint8_t) (block_index >> 10);
+ }
+ } else {
+ mlc->addr = (uint8_t) (block_index << 7);
+ mlc->addr = (uint8_t) (block_index >> 1);
+ if (mlc_many_address_cycles) {
+ mlc->addr = (uint8_t) (block_index >> 9);
+ }
+ }
+ }
+}
+
+static void mlc_set_page_address(uint32_t page_index)
+{
+ mlc->addr = 0;
+ if (mlc_small_pages) {
+ mlc->addr = (uint8_t) page_index;
+ mlc->addr = (uint8_t) (page_index >> 8);
+ if (mlc_many_address_cycles) {
+ mlc->addr = (uint8_t) (page_index >> 16);
+ }
+ } else {
+ mlc->addr = 0;
+ mlc->addr = (uint8_t) page_index;
+ mlc->addr = (uint8_t) (page_index >> 8);
+ if (mlc_many_address_cycles) {
+ mlc->addr = (uint8_t) (page_index >> 16);
+ }
+ }
+}
+
+void lpc32xx_mlc_init(const lpc32xx_mlc_config *cfg)
+{
+ uint32_t icr = 0;
+
+ mlc_small_pages = cfg->small_pages;
+ mlc_many_address_cycles = cfg->many_address_cycles;
+ mlc_normal_blocks = cfg->normal_blocks;
+ mlc_block_count = cfg->block_count;
+ mlc_page_count = cfg->block_count * lpc32xx_mlc_pages_per_block();
+
+ /* Clock */
+ LPC32XX_FLASHCLK_CTRL = FLASHCLK_IRQ_MLC | FLASHCLK_MLC_CLK_ENABLE;
+
+ /* Timing settings */
+ mlc_unlock();
+ mlc->time = cfg->time;
+
+ /* Configuration */
+ if (!mlc_small_pages) {
+ icr |= MLC_ICR_LARGE_PAGES;
+ }
+ if (mlc_many_address_cycles) {
+ icr |= MLC_ICR_ADDR_WORD_COUNT_4_5;
+ }
+ mlc_unlock();
+ mlc->icr = icr;
+
+ mlc_reset();
+}
+
+void lpc32xx_mlc_write_protection(
+ uint32_t page_index_low,
+ uint32_t page_index_high
+)
+{
+ mlc_unlock();
+ mlc->sw_wp_add_low = page_index_low;
+ mlc_unlock();
+ mlc->sw_wp_add_hig = page_index_high;
+ mlc_unlock();
+ mlc->icr |= MLC_ICR_SOFT_WRITE_PROT;
+}
+
+rtems_status_code lpc32xx_mlc_read_page(
+ uint32_t page_index,
+ uint32_t *data,
+ uint32_t *spare
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ size_t small_pages_count = mlc_small_pages ? 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
+ size_t sp = 0;
+ size_t i = 0;
+ uint32_t isr = 0;
+
+ if (page_index >= mlc_page_count) {
+ return RTEMS_INVALID_ID;
+ }
+
+ mlc_wait_until_ready();
+ mlc->cmd = 0x00;
+ if (!mlc_small_pages) {
+ mlc->cmd = 0x30;
+ }
+ mlc_set_page_address(page_index);
+ mlc_wait(MLC_ISR_NAND_READY);
+
+ for (sp = 0; sc == RTEMS_SUCCESSFUL && sp < small_pages_count; ++sp) {
+ mlc->ecc_dec = 0;
+
+ for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
+ data [i] = mlc->data.w32;
+ }
+ for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
+ spare [i] = mlc->data.w32;
+ }
+
+ mlc_wait(MLC_ISR_ECC_READY);
+
+ isr = mlc->isr;
+ if ((isr & MLC_ISR_ERRORS_DETECTED) != 0) {
+ if ((isr & MLC_ISR_DECODER_FAILURE) == 0) {
+ mlc->rubp = 0;
+ for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
+ data [i] = mlc->buff.w32;
+ }
+ mlc->robp = 0;
+ for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
+ spare [i] = mlc->buff.w32;
+ }
+ } else {
+ sc = RTEMS_IO_ERROR;
+ }
+ }
+
+ data += MLC_SMALL_DATA_WORD_COUNT;
+ spare += MLC_SMALL_SPARE_WORD_COUNT;
+ }
+
+ return sc;
+}
+
+void lpc32xx_mlc_read_id(uint8_t *id, size_t n)
+{
+ size_t i = 0;
+
+ mlc_wait_until_ready();
+ mlc->cmd = 0x90;
+ mlc->addr = 0;
+ mlc_wait(MLC_ISR_NAND_READY);
+
+ for (i = 0; i < n; ++i) {
+ id [i] = mlc->data.w8;
+ }
+}
+
+rtems_status_code lpc32xx_mlc_erase_block(uint32_t block_index)
+{
+ rtems_status_code sc = RTEMS_IO_ERROR;
+
+ if (block_index >= mlc_block_count) {
+ return RTEMS_INVALID_ID;
+ }
+
+ mlc_wait_until_ready();
+ mlc->cmd = 0x60;
+ mlc_set_block_address(block_index);
+ mlc->cmd = 0xd0;
+
+ if (mlc_was_operation_successful()) {
+ sc = RTEMS_SUCCESSFUL;
+ }
+
+ return sc;
+}
+
+rtems_status_code lpc32xx_mlc_write_page_with_ecc(
+ uint32_t page_index,
+ const uint32_t *data,
+ const uint32_t *spare
+)
+{
+ rtems_status_code sc = RTEMS_IO_ERROR;
+ size_t small_pages_count = mlc_small_pages ? 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
+ size_t sp = 0;
+ size_t i = 0;
+
+ if (page_index >= mlc_page_count) {
+ return RTEMS_INVALID_ID;
+ }
+
+ mlc_wait_until_ready();
+ mlc->cmd = 0x80;
+ mlc_set_page_address(page_index);
+
+ for (sp = 0; sp < small_pages_count; ++sp) {
+ mlc->ecc_enc = 0;
+
+ for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
+ mlc->data.w32 = data [i];
+ }
+ mlc->data.w32 = spare [0];
+ mlc->data.w16 = (uint16_t) spare [1];
+ mlc->wpr = 0;
+
+ mlc_wait(MLC_ISR_CONTROLLER_READY);
+
+ data += MLC_SMALL_DATA_WORD_COUNT;
+ spare += MLC_SMALL_SPARE_WORD_COUNT;
+ }
+
+ mlc->cmd = 0x10;
+
+ if (mlc_was_operation_successful()) {
+ sc = RTEMS_SUCCESSFUL;
+ }
+
+ return sc;
+}
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/restart.c b/c/src/lib/libbsp/arm/lpc32xx/misc/restart.c
new file mode 100644
index 0000000000..b286390237
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/misc/restart.c
@@ -0,0 +1,56 @@
+/**
+ * @file
+ *
+ * @ingroup lpc32xx
+ *
+ * @brief Restart implementation.
+ */
+
+/*
+ * Copyright (c) 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <rtems.h>
+
+#include <libcpu/arm-cp15.h>
+
+#include <bsp.h>
+
+void lpc32xx_restart(void *addr)
+{
+ ARM_SWITCH_REGISTERS;
+ rtems_interrupt_level level;
+ uint32_t ctrl = 0;
+
+ /* FIXME: DMA shutdown */
+
+ /* FIXME: USB shutdown */
+
+ /* FIXME: Ethernet interface reset */
+
+ rtems_interrupt_disable(level);
+
+ arm_cp15_data_cache_test_and_clean();
+ arm_cp15_instruction_cache_invalidate();
+
+ ctrl = arm_cp15_get_control();
+ ctrl &= ~(ARM_CP15_CTRL_I | ARM_CP15_CTRL_C | ARM_CP15_CTRL_M);
+ arm_cp15_set_control(ctrl);
+
+ asm volatile (
+ ARM_SWITCH_TO_ARM
+ "mov pc, %[addr]\n"
+ ARM_SWITCH_BACK
+ : ARM_SWITCH_OUTPUT
+ : [addr] "r" (addr)
+ );
+}