From 43250167c69b00d3bb188b1e637816e946a3b143 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 25 Apr 2018 10:36:52 +0200 Subject: bsp/lpc32xx: Move source files to bsps This patch is a part of the BSP source reorganization. Update #3285. --- bsps/arm/lpc32xx/nand/nand-mlc-erase-block-safe.c | 122 +++++++ bsps/arm/lpc32xx/nand/nand-mlc-read-blocks.c | 134 ++++++++ bsps/arm/lpc32xx/nand/nand-mlc-write-blocks.c | 111 ++++++ bsps/arm/lpc32xx/nand/nand-mlc.c | 397 ++++++++++++++++++++++ bsps/arm/lpc32xx/nand/nand-select.c | 44 +++ bsps/arm/lpc32xx/start/boot.c | 61 ++++ bsps/arm/lpc32xx/start/bspidle.c | 34 ++ bsps/arm/lpc32xx/start/emc.c | 111 ++++++ bsps/arm/lpc32xx/start/restart.c | 32 ++ bsps/arm/lpc32xx/start/system-clocks.c | 140 ++++++++ bsps/arm/lpc32xx/start/timer.c | 42 +++ 11 files changed, 1228 insertions(+) create mode 100644 bsps/arm/lpc32xx/nand/nand-mlc-erase-block-safe.c create mode 100644 bsps/arm/lpc32xx/nand/nand-mlc-read-blocks.c create mode 100644 bsps/arm/lpc32xx/nand/nand-mlc-write-blocks.c create mode 100644 bsps/arm/lpc32xx/nand/nand-mlc.c create mode 100644 bsps/arm/lpc32xx/nand/nand-select.c create mode 100644 bsps/arm/lpc32xx/start/boot.c create mode 100644 bsps/arm/lpc32xx/start/bspidle.c create mode 100644 bsps/arm/lpc32xx/start/emc.c create mode 100644 bsps/arm/lpc32xx/start/restart.c create mode 100644 bsps/arm/lpc32xx/start/system-clocks.c create mode 100644 bsps/arm/lpc32xx/start/timer.c (limited to 'bsps') diff --git a/bsps/arm/lpc32xx/nand/nand-mlc-erase-block-safe.c b/bsps/arm/lpc32xx/nand/nand-mlc-erase-block-safe.c new file mode 100644 index 0000000000..b7e570e46d --- /dev/null +++ b/bsps/arm/lpc32xx/nand/nand-mlc-erase-block-safe.c @@ -0,0 +1,122 @@ +/** + * @file + * + * @ingroup lpc32xx_nand_mlc + * + * @brief lpc32xx_mlc_erase_block_safe(), lpc32xx_mlc_erase_block_safe_3(), and + * lpc32xx_mlc_zero_block() implementation. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +#include + +void lpc32xx_mlc_zero_pages(uint32_t page_begin, uint32_t page_end) +{ + uint32_t page = 0; + + for (page = page_begin; page < page_end; ++page) { + lpc32xx_mlc_write_page_with_ecc( + page, + lpc32xx_magic_zero_begin, + lpc32xx_magic_zero_begin + ); + } +} + +static rtems_status_code is_valid_page( + uint32_t page_begin, + uint32_t page_offset +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint32_t spare [MLC_LARGE_SPARE_WORD_COUNT]; + + memset(spare, 0, MLC_LARGE_SPARE_SIZE); + + sc = lpc32xx_mlc_read_page( + page_begin + page_offset, + lpc32xx_magic_zero_begin, + spare, + NULL + ); + if (sc == RTEMS_SUCCESSFUL) { + if (lpc32xx_mlc_is_bad_page(spare)) { + sc = RTEMS_INCORRECT_STATE; + } + } + + return sc; +} + +static rtems_status_code is_valid_block(uint32_t page_begin) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + if (lpc32xx_mlc_page_size() == 512 && lpc32xx_mlc_io_width() == 8) { + sc = is_valid_page(page_begin, 0); + if (sc == RTEMS_SUCCESSFUL) { + sc = is_valid_page(page_begin, 1); + } + } else { + sc = RTEMS_NOT_IMPLEMENTED; + } + + return sc; +} + +rtems_status_code lpc32xx_mlc_is_valid_block(uint32_t block_index) +{ + uint32_t pages_per_block = lpc32xx_mlc_pages_per_block(); + uint32_t page_begin = block_index * pages_per_block; + + return is_valid_block(page_begin); +} + +rtems_status_code lpc32xx_mlc_erase_block_safe_3( + uint32_t block_index, + uint32_t page_begin, + uint32_t page_end +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + sc = is_valid_block(page_begin); + if (sc == RTEMS_SUCCESSFUL) { + sc = lpc32xx_mlc_erase_block(block_index); + if (sc == RTEMS_UNSATISFIED) { + lpc32xx_mlc_zero_pages(page_begin, page_end); + } + } + + return sc; +} + +rtems_status_code lpc32xx_mlc_erase_block_safe(uint32_t block_index) +{ + uint32_t pages_per_block = lpc32xx_mlc_pages_per_block(); + uint32_t page_begin = block_index * pages_per_block; + uint32_t page_end = page_begin + pages_per_block; + + return lpc32xx_mlc_erase_block_safe_3( + block_index, + page_begin, + page_end + ); +} diff --git a/bsps/arm/lpc32xx/nand/nand-mlc-read-blocks.c b/bsps/arm/lpc32xx/nand/nand-mlc-read-blocks.c new file mode 100644 index 0000000000..bc01a984ea --- /dev/null +++ b/bsps/arm/lpc32xx/nand/nand-mlc-read-blocks.c @@ -0,0 +1,134 @@ +/** + * @file + * + * @ingroup lpc32xx_nand_mlc + * + * @brief lpc32xx_mlc_read_blocks() implementation. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +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, NULL); + 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/bsps/arm/lpc32xx/nand/nand-mlc-write-blocks.c b/bsps/arm/lpc32xx/nand/nand-mlc-write-blocks.c new file mode 100644 index 0000000000..cfcb9cd27e --- /dev/null +++ b/bsps/arm/lpc32xx/nand/nand-mlc-write-blocks.c @@ -0,0 +1,111 @@ +/** + * @file + * + * @ingroup lpc32xx_nand_mlc + * + * @brief lpc32xx_mlc_write_blocks() implementation. + */ + +/* + * Copyright (c) 2010-2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +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 +}; + +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 page_begin = block * pages_per_block; + uint32_t page_end = page_begin + pages_per_block; + uint32_t page = 0; + + sc = lpc32xx_mlc_erase_block_safe_3(block, page_begin, page_end); + if (sc != RTEMS_SUCCESSFUL) { + continue; + } + + for (page = page_begin; page < page_end; ++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( + page, + page_data_buffer, + ones_spare + ); + if (sc != RTEMS_SUCCESSFUL) { + lpc32xx_mlc_erase_block(block); + lpc32xx_mlc_zero_pages(page_begin, page_end); + current = last; + continue; + } + + current += delta; + } else { + goto done; + } + } + + last = current; + } + +done: + + if (current != end) { + return RTEMS_IO_ERROR; + } + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/arm/lpc32xx/nand/nand-mlc.c b/bsps/arm/lpc32xx/nand/nand-mlc.c new file mode 100644 index 0000000000..f87c59b133 --- /dev/null +++ b/bsps/arm/lpc32xx/nand/nand-mlc.c @@ -0,0 +1,397 @@ +/** + * @file + * + * @ingroup lpc32xx_nand_mlc + * + * @brief NAND MLC controller implementation. + */ + +/* + * Copyright (c) 2010-2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include + +static volatile lpc32xx_nand_mlc *const mlc = &lpc32xx.nand_mlc; + +static uint32_t mlc_flags; + +static uint32_t mlc_block_count; + +static uint32_t mlc_page_count; + +static bool mlc_small_pages(void) +{ + return (mlc_flags & MLC_SMALL_PAGES) != 0; +} + +static bool mlc_many_address_cycles(void) +{ + return (mlc_flags & MLC_MANY_ADDRESS_CYCLES) != 0; +} + +static bool mlc_normal_blocks(void) +{ + return (mlc_flags & MLC_NORMAL_BLOCKS) != 0; +} + +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; +} + +uint32_t lpc32xx_mlc_io_width(void) +{ + if ((mlc_flags & MLC_IO_WIDTH_16_BIT) == 0) { + return 8; + } else { + return 16; + } +} + +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_flags = cfg->flags; + 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; +} + +static bool is_word_aligned(const void *data, const void *spare) +{ + return (((uintptr_t) data) | ((uintptr_t) spare)) % 4 == 0; +} + +rtems_status_code lpc32xx_mlc_read_page( + uint32_t page_index, + void *data, + void *spare, + uint32_t *symbol_error_count_ptr +) +{ + 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; + uint32_t symbol_error_count = 0xffffffff; + bool aligned = is_word_aligned(data, spare); + uint8_t *current_data = data; + uint8_t *current_spare = spare; + + 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) { + uint32_t *aligned_data = (uint32_t *) current_data; + uint32_t *aligned_spare = (uint32_t *) current_spare; + + mlc->ecc_dec = 0; + + if (aligned) { + for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) { + aligned_data [i] = mlc->data.w32; + } + for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) { + aligned_spare [i] = mlc->data.w32; + } + } else { + for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) { + current_data [i] = mlc->data.w8; + } + for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) { + current_spare [i] = mlc->data.w8; + } + } + + mlc_wait(MLC_ISR_ECC_READY); + + isr = mlc->isr; + if ((isr & MLC_ISR_ERRORS_DETECTED) == 0) { + symbol_error_count = 0; + } else { + if ((isr & MLC_ISR_DECODER_FAILURE) == 0) { + symbol_error_count = MLC_ISR_SYMBOL_ERRORS(isr); + if (aligned) { + mlc->rubp = 0; + for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) { + aligned_data [i] = mlc->buff.w32; + } + mlc->robp = 0; + for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) { + aligned_spare [i] = mlc->buff.w32; + } + } else { + mlc->rubp = 0; + for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) { + current_data [i] = mlc->buff.w8; + } + mlc->robp = 0; + for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) { + current_spare [i] = mlc->buff.w8; + } + } + } else { + sc = RTEMS_IO_ERROR; + } + } + + current_data += MLC_SMALL_DATA_SIZE; + current_spare += MLC_SMALL_SPARE_SIZE; + } + + if (symbol_error_count_ptr != NULL) { + *symbol_error_count_ptr = symbol_error_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_UNSATISFIED; + + 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 void *data, + const void *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; + bool aligned = is_word_aligned(data, spare); + const uint8_t *current_data = data; + const uint8_t *current_spare = spare; + + 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; + + if (aligned) { + const uint32_t *aligned_data = (const uint32_t *) current_data; + const uint32_t *aligned_spare = (const uint32_t *) current_spare; + + for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) { + mlc->data.w32 = aligned_data [i]; + } + mlc->data.w32 = aligned_spare [0]; + mlc->data.w16 = (uint16_t) aligned_spare [1]; + } else { + for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) { + mlc->data.w8 = current_data [i]; + } + for (i = 0; i < MLC_SMALL_USER_SPARE_SIZE; ++i) { + mlc->data.w8 = current_spare [i]; + } + } + mlc->wpr = 0; + + mlc_wait(MLC_ISR_CONTROLLER_READY); + + current_data += MLC_SMALL_DATA_SIZE; + current_spare += MLC_SMALL_SPARE_SIZE; + } + + mlc->cmd = 0x10; + + if (mlc_was_operation_successful()) { + sc = RTEMS_SUCCESSFUL; + } + + return sc; +} diff --git a/bsps/arm/lpc32xx/nand/nand-select.c b/bsps/arm/lpc32xx/nand/nand-select.c new file mode 100644 index 0000000000..9c03b12b85 --- /dev/null +++ b/bsps/arm/lpc32xx/nand/nand-select.c @@ -0,0 +1,44 @@ +/** + * @file + * + * @ingroup arm_lpc32xx + * + * @brief NAND controller selection. + */ + +/* + * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include +#include + +void lpc32xx_select_nand_controller(lpc32xx_nand_controller nand_controller) +{ + uint32_t flashclk_ctrl = LPC32XX_FLASHCLK_CTRL & ~(FLASHCLK_IRQ_MLC + | FLASHCLK_SELECT_SLC | FLASHCLK_MLC_CLK_ENABLE | FLASHCLK_SLC_CLK_ENABLE); + + switch (nand_controller) { + case LPC32XX_NAND_CONTROLLER_MLC: + flashclk_ctrl |= FLASHCLK_IRQ_MLC | FLASHCLK_MLC_CLK_ENABLE; + break; + case LPC32XX_NAND_CONTROLLER_SLC: + flashclk_ctrl |= FLASHCLK_SELECT_SLC | FLASHCLK_SLC_CLK_ENABLE; + break; + default: + break; + } + + LPC32XX_FLASHCLK_CTRL = flashclk_ctrl; +} diff --git a/bsps/arm/lpc32xx/start/boot.c b/bsps/arm/lpc32xx/start/boot.c new file mode 100644 index 0000000000..431273a8c6 --- /dev/null +++ b/bsps/arm/lpc32xx/start/boot.c @@ -0,0 +1,61 @@ +/** + * @file + * + * @ingroup lpc32xx_boot + * + * @brief Boot support implementation. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +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; +} + +void lpc32xx_set_boot_block_bad( + lpc32xx_boot_block *boot_block +) +{ + boot_block->field.d12 = 0; +} diff --git a/bsps/arm/lpc32xx/start/bspidle.c b/bsps/arm/lpc32xx/start/bspidle.c new file mode 100644 index 0000000000..0623c6646d --- /dev/null +++ b/bsps/arm/lpc32xx/start/bspidle.c @@ -0,0 +1,34 @@ +/** + * @file + * + * @ingroup arm_lpc32xx + * + * @brief bsp_idle_thread() implementation. + */ + +/* + * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +void *bsp_idle_thread(uintptr_t arg) +{ + while (true) { + arm_cp15_wait_for_interrupt(); + } + + return NULL; +} diff --git a/bsps/arm/lpc32xx/start/emc.c b/bsps/arm/lpc32xx/start/emc.c new file mode 100644 index 0000000000..d8b82e7051 --- /dev/null +++ b/bsps/arm/lpc32xx/start/emc.c @@ -0,0 +1,111 @@ +/** + * @file + * + * @ingroup lpc32xx_emc + * + * @brief EMC support implementation. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include +#include + +static volatile lpc_emc *const emc = &lpc32xx.emc; + +static volatile lpc32xx_emc_ahb *const emc_ahb = &lpc32xx.emc_ahb [0]; + +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) { + lpc32xx_set_translation_table_entries( + (void *) chip_begin, + (void *) (chip_begin + cfg->chip [i].size), + LPC32XX_MMU_READ_WRITE + ); + emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_MODE; + *(volatile uint32_t *)(chip_begin + cfg->chip [i].mode); + emc->dynamiccontrol = dynamiccontrol | EMC_DYN_CTRL_I_MODE; + *(volatile uint32_t *)(chip_begin + cfg->chip [i].extmode); + } + chip_begin += 0x20000000; + } + + emc->dynamiccontrol = cfg->control; +} + +void lpc32xx_emc_init(const lpc32xx_emc_dynamic_config *dyn_cfg) +{ + /* 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_E, + emc->config = 0; + + dynamic_init(dyn_cfg); +} diff --git a/bsps/arm/lpc32xx/start/restart.c b/bsps/arm/lpc32xx/start/restart.c new file mode 100644 index 0000000000..6f6a8a5f9a --- /dev/null +++ b/bsps/arm/lpc32xx/start/restart.c @@ -0,0 +1,32 @@ +/** + * @file + * + * @ingroup arm_lpc32xx + * + * @brief Restart implementation. + */ + +/* + * Copyright (c) 2010, 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +#include + +#include + +void bsp_restart(void *addr) +{ + LPC32XX_DO_RESTART(addr); +} diff --git a/bsps/arm/lpc32xx/start/system-clocks.c b/bsps/arm/lpc32xx/start/system-clocks.c new file mode 100644 index 0000000000..e5e3f00dd4 --- /dev/null +++ b/bsps/arm/lpc32xx/start/system-clocks.c @@ -0,0 +1,140 @@ +/** + * @file + * + * @ingroup arm_lpc32xx + * + * @brief System clocks. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include + +uint32_t lpc32xx_sysclk(void) +{ + uint32_t sysclk_ctrl = LPC32XX_SYSCLK_CTRL; + + return (sysclk_ctrl & 0x1) == 0 ? + LPC32XX_OSCILLATOR_MAIN + : (397 * LPC32XX_OSCILLATOR_RTC); +} + +uint32_t lpc32xx_hclkpll_clk(void) +{ + uint32_t sysclk = lpc32xx_sysclk(); + uint32_t hclkpll_ctrl = LPC32XX_HCLKPLL_CTRL; + uint32_t m = HCLK_PLL_M_GET(hclkpll_ctrl) + 1; + uint32_t n = HCLK_PLL_N_GET(hclkpll_ctrl) + 1; + uint32_t p = 1U << HCLK_PLL_P_GET(hclkpll_ctrl); + uint32_t hclkpll_clk = 0; + + if ((hclkpll_ctrl & HCLK_PLL_BYPASS) != 0) { + if ((hclkpll_ctrl & HCLK_PLL_DIRECT) != 0) { + hclkpll_clk = sysclk; + } else { + hclkpll_clk = sysclk / (2 * p); + } + } else { + if ((hclkpll_ctrl & HCLK_PLL_DIRECT) != 0) { + hclkpll_clk = (m * sysclk) / n; + } else { + if ((hclkpll_ctrl & HCLK_PLL_FBD_FCLKOUT) != 0) { + hclkpll_clk = m * (sysclk / n); + } else { + hclkpll_clk = (m / (2 * p)) * (sysclk / n); + } + } + } + + return hclkpll_clk; +} + +uint32_t lpc32xx_periph_clk(void) +{ + uint32_t pwr_ctrl = LPC32XX_PWR_CTRL; + uint32_t periph_clk = 0; + + if ((pwr_ctrl & PWR_NORMAL_RUN_MODE) != 0) { + uint32_t hclkdiv_ctrl = LPC32XX_HCLKDIV_CTRL; + uint32_t div = HCLK_DIV_PERIPH_CLK_GET(hclkdiv_ctrl) + 1; + + periph_clk = lpc32xx_hclkpll_clk() / div; + } else { + periph_clk = lpc32xx_sysclk(); + } + + return periph_clk; +} + +uint32_t lpc32xx_hclk(void) +{ + uint32_t pwr_ctrl = LPC32XX_PWR_CTRL; + uint32_t hclk = 0; + + if ((pwr_ctrl & PWR_HCLK_USES_PERIPH_CLK) != 0) { + hclk = lpc32xx_periph_clk(); + } else { + if ((pwr_ctrl & PWR_NORMAL_RUN_MODE) != 0) { + uint32_t hclkdiv_ctrl = LPC32XX_HCLKDIV_CTRL; + uint32_t div = 1U << HCLK_DIV_HCLK_GET(hclkdiv_ctrl); + + hclk = lpc32xx_hclkpll_clk() / div; + } else { + hclk = lpc32xx_sysclk(); + } + } + + return hclk; +} + +uint32_t lpc32xx_arm_clk(void) +{ + uint32_t pwr_ctrl = LPC32XX_PWR_CTRL; + uint32_t arm_clk = 0; + + if ((pwr_ctrl & PWR_HCLK_USES_PERIPH_CLK) != 0) { + arm_clk = lpc32xx_periph_clk(); + } else { + if ((pwr_ctrl & PWR_NORMAL_RUN_MODE) != 0) { + arm_clk = lpc32xx_hclkpll_clk(); + } else { + arm_clk = lpc32xx_sysclk(); + } + } + + return arm_clk; +} + +uint32_t lpc32xx_ddram_clk(void) +{ + uint32_t hclkdiv_ctrl = LPC32XX_HCLKDIV_CTRL; + uint32_t div = HCLK_DIV_DDRAM_CLK_GET(hclkdiv_ctrl); + uint32_t ddram_clk = 0; + + if (div != 0) { + uint32_t pwr_ctrl = LPC32XX_PWR_CTRL; + + if ((pwr_ctrl & PWR_NORMAL_RUN_MODE) != 0) { + ddram_clk = lpc32xx_hclkpll_clk(); + } else { + ddram_clk = lpc32xx_sysclk(); + } + + ddram_clk /= div; + } + + return ddram_clk; +} diff --git a/bsps/arm/lpc32xx/start/timer.c b/bsps/arm/lpc32xx/start/timer.c new file mode 100644 index 0000000000..5079452d8c --- /dev/null +++ b/bsps/arm/lpc32xx/start/timer.c @@ -0,0 +1,42 @@ +/** + * @file + * + * @ingroup arm_lpc32xx + * + * @brief Benchmark timer support. + */ + +/* + * Copyright (c) 2008, 2009 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include +#include + +#include + +static uint32_t benchmark_timer_base; + +void benchmark_timer_initialize(void) +{ + benchmark_timer_base = lpc32xx_timer(); +} + +benchmark_timer_t benchmark_timer_read(void) +{ + return lpc32xx_timer() - benchmark_timer_base; +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_average_overhead) +{ + /* VOID */ +} -- cgit v1.2.3