diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2010-06-23 08:27:57 +0000 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2010-06-23 08:27:57 +0000 |
commit | 3103d4cbad582cc9e62aaf15fd5af308949a83f0 (patch) | |
tree | b1ff4f68908b3177c614349a2f0252e4b64e9b18 /c/src/lib/libbsp/arm/lpc32xx/misc | |
parent | 2010-06-23 Sebastian Huber <sebastian.huber@embedded-brains.de> (diff) | |
download | rtems-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.c | 54 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/emc.c | 124 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c | 259 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c | 132 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c | 166 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c | 322 | ||||
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/restart.c | 56 |
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) + ); +} |