diff options
156 files changed, 6385 insertions, 2467 deletions
diff --git a/bsps/arm/atsam/console/console.c b/bsps/arm/atsam/console/console.c index d51d2ace7d..0802ca2155 100644 --- a/bsps/arm/atsam/console/console.c +++ b/bsps/arm/atsam/console/console.c @@ -16,6 +16,7 @@ #include <bsp/irq.h> #include <bsp/fatal.h> #include <rtems/console.h> +#include <rtems/seterr.h> #include <rtems/termiostypes.h> @@ -23,274 +24,92 @@ #include <unistd.h> +#define UART_RX_DMA_BUF_SIZE 32l + +typedef struct { + char buf[UART_RX_DMA_BUF_SIZE]; + LinkedListDescriporView3 desc; +} atsam_uart_rx_dma; + typedef struct { rtems_termios_device_context base; - Usart *regs; + Uart *regs; rtems_vector_number irq; uint32_t id; bool console; + bool is_usart; #ifdef ATSAM_CONSOLE_USE_INTERRUPTS bool transmitting; + bool rx_dma_enabled; + uint32_t rx_dma_channel; + atsam_uart_rx_dma *rx_dma; + char *volatile*rx_dma_da; + char *rx_next_read_pos; #endif -} atsam_usart_context; +} atsam_uart_context; -static atsam_usart_context atsam_usart_instances[] = { +static atsam_uart_context atsam_usart_instances[] = { { - .regs = USART0, + .regs = (Uart *)USART0, .irq = USART0_IRQn, - .id = ID_USART0 + .id = ID_USART0, + .is_usart = true, } #ifdef USART1 , { - .regs = USART1, + .regs = (Uart *)USART1, .irq = USART1_IRQn, - .id = ID_USART1 + .id = ID_USART1, + .is_usart = true, } #endif #ifdef USART2 , { - .regs = USART2, + .regs = (Uart *)USART2, .irq = USART2_IRQn, - .id = ID_USART2 - } -#endif -}; - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS -static void atsam_usart_interrupt(void *arg) -{ - rtems_termios_tty *tty = arg; - atsam_usart_context *ctx = rtems_termios_get_device_context(tty); - Usart *regs = ctx->regs; - uint32_t csr = regs->US_CSR; - - while ((csr & US_CSR_RXRDY) != 0) { - char c = (char) regs->US_RHR; - - rtems_termios_enqueue_raw_characters(tty, &c, 1); - - csr = regs->US_CSR; - } - - if (ctx->transmitting && (csr & US_CSR_TXEMPTY) != 0) { - rtems_termios_dequeue_characters(tty, 1); - } -} -#endif - -static bool atsam_usart_set_attributes( - rtems_termios_device_context *base, - const struct termios *term -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; - rtems_termios_baud_t baud; - uint32_t mr; - - baud = rtems_termios_baud_to_number(term->c_ospeed); - regs->US_BRGR = (BOARD_MCK / baud) / 16; - - if ((term->c_cflag & CREAD) != 0) { - regs->US_CR = US_CR_RXEN | US_CR_TXEN; - } else { - regs->US_CR = US_CR_TXEN; - } - - mr = US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK; - - switch (term->c_cflag & CSIZE) { - case CS5: - mr |= US_MR_CHRL_5_BIT; - break; - case CS6: - mr |= US_MR_CHRL_6_BIT; - break; - case CS7: - mr |= US_MR_CHRL_7_BIT; - break; - default: - mr |= US_MR_CHRL_8_BIT; - break; - } - - if ((term->c_cflag & PARENB) != 0) { - if ((term->c_cflag & PARODD) != 0) { - mr |= US_MR_PAR_ODD; - } else { - mr |= US_MR_PAR_EVEN; - } - } else { - mr |= US_MR_PAR_NO; - } - - if ((term->c_cflag & CSTOPB) != 0) { - mr |= US_MR_NBSTOP_2_BIT; - } else { - mr |= US_MR_NBSTOP_1_BIT; - } - - regs->US_MR = mr; - - return true; -} - -static bool atsam_usart_first_open( - rtems_termios_tty *tty, - rtems_termios_device_context *base, - struct termios *term, - rtems_libio_open_close_args_t *args -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - rtems_status_code sc; -#endif - - regs->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RSTSTA; - regs->US_IDR = 0xffffffff; - - PMC_EnablePeripheral(ctx->id); - - rtems_termios_set_initial_baud(tty, ATSAM_CONSOLE_BAUD); - atsam_usart_set_attributes(base, term); - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - regs->US_IER = US_IDR_RXRDY; - sc = rtems_interrupt_handler_install( - ctx->irq, - "USART", - RTEMS_INTERRUPT_SHARED, - atsam_usart_interrupt, - tty - ); - if (sc != RTEMS_SUCCESSFUL) { - return false; - } -#endif - - return true; -} - -static void atsam_usart_last_close( - rtems_termios_tty *tty, - rtems_termios_device_context *base, - rtems_libio_open_close_args_t *args -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - rtems_interrupt_handler_remove(ctx->irq, atsam_usart_interrupt, tty); -#endif - - if (!ctx->console) { - PMC_DisablePeripheral(ctx->id); - } -} - -static void atsam_usart_write( - rtems_termios_device_context *base, - const char *buf, - size_t len -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - if (len > 0) { - ctx->transmitting = true; - regs->US_THR = buf[0]; - regs->US_IER = US_IDR_TXEMPTY; - } else { - ctx->transmitting = false; - regs->US_IDR = US_IDR_TXEMPTY; - } -#else - size_t i; - - for (i = 0; i < len; ++i) { - while ((regs->US_CSR & US_CSR_TXEMPTY) == 0) { - /* Wait */ - } - - regs->US_THR = buf[i]; + .id = ID_USART2, + .is_usart = true, } #endif -} - -#ifndef ATSAM_CONSOLE_USE_INTERRUPTS -static int atsam_usart_read(rtems_termios_device_context *base) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; - - if ((regs->US_CSR & US_CSR_RXRDY) != 0) { - return (char) regs->US_RHR; - } else { - return -1; - } -} -#endif - -static const rtems_termios_device_handler atsam_usart_handler = { - .first_open = atsam_usart_first_open, - .last_close = atsam_usart_last_close, - .write = atsam_usart_write, - .set_attributes = atsam_usart_set_attributes, -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - .mode = TERMIOS_IRQ_DRIVEN -#else - .poll_read = atsam_usart_read, - .mode = TERMIOS_POLLED -#endif }; -typedef struct { - rtems_termios_device_context base; - Uart *regs; - rtems_vector_number irq; - uint32_t id; - bool console; -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - bool transmitting; -#endif -} atsam_uart_context; - static atsam_uart_context atsam_uart_instances[] = { { .regs = UART0, .irq = UART0_IRQn, - .id = ID_UART0 + .id = ID_UART0, + .is_usart = false, } #ifdef UART1 , { .regs = UART1, .irq = UART1_IRQn, - .id = ID_UART1 + .id = ID_UART1, + .is_usart = false, } #endif #ifdef UART2 , { .regs = UART2, .irq = UART2_IRQn, - .id = ID_UART2 + .id = ID_UART2, + .is_usart = false, } #endif #ifdef UART3 , { .regs = UART3, .irq = UART3_IRQn, - .id = ID_UART3 + .id = ID_UART3, + .is_usart = false, } #endif #ifdef UART4 , { .regs = UART4, .irq = UART4_IRQn, - .id = ID_UART4 + .id = ID_UART4, + .is_usart = false, } #endif }; @@ -303,16 +122,32 @@ static void atsam_uart_interrupt(void *arg) Uart *regs = ctx->regs; uint32_t sr = regs->UART_SR; - while ((sr & UART_SR_RXRDY) != 0) { - char c = (char) regs->UART_RHR; + if (!ctx->rx_dma_enabled) { + while ((sr & UART_SR_RXRDY) != 0) { + char c = (char) regs->UART_RHR; - rtems_termios_enqueue_raw_characters(tty, &c, 1); + rtems_termios_enqueue_raw_characters(tty, &c, 1); - sr = regs->UART_SR; + sr = regs->UART_SR; + } + } else { + while (*ctx->rx_dma_da != ctx->rx_next_read_pos) { + char c; + + c = *ctx->rx_next_read_pos; + + ++ctx->rx_next_read_pos; + if (ctx->rx_next_read_pos >= &ctx->rx_dma->buf[UART_RX_DMA_BUF_SIZE]) { + ctx->rx_next_read_pos = &ctx->rx_dma->buf[0]; + } + + rtems_termios_enqueue_raw_characters(tty, &c, 1); + } } - if (ctx->transmitting && (sr & UART_SR_TXEMPTY) != 0) { + while (ctx->transmitting && (sr & UART_SR_TXRDY) != 0) { rtems_termios_dequeue_characters(tty, 1); + sr = regs->UART_SR; } } #endif @@ -336,10 +171,31 @@ static bool atsam_uart_set_attributes( regs->UART_CR = UART_CR_TXEN; } - mr = UART_MR_FILTER_DISABLED | UART_MR_BRSRCCK_PERIPH_CLK; - - if ((term->c_cflag & CSIZE) != CS8) { - return false; + if (ctx->is_usart) { + mr = US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK; + } else { + mr = UART_MR_FILTER_DISABLED | UART_MR_BRSRCCK_PERIPH_CLK; + } + + if (ctx->is_usart) { + switch (term->c_cflag & CSIZE) { + case CS5: + mr |= US_MR_CHRL_5_BIT; + break; + case CS6: + mr |= US_MR_CHRL_6_BIT; + break; + case CS7: + mr |= US_MR_CHRL_7_BIT; + break; + default: + mr |= US_MR_CHRL_8_BIT; + break; + } + } else { + if ((term->c_cflag & CSIZE) != CS8) { + return false; + } } if ((term->c_cflag & PARENB) != 0) { @@ -352,8 +208,16 @@ static bool atsam_uart_set_attributes( mr |= UART_MR_PAR_NO; } - if ((term->c_cflag & CSTOPB) != 0) { - return false; + if (ctx->is_usart) { + if ((term->c_cflag & CSTOPB) != 0) { + mr |= US_MR_NBSTOP_2_BIT; + } else { + mr |= US_MR_NBSTOP_1_BIT; + } + } else { + if ((term->c_cflag & CSTOPB) != 0) { + return false; + } } regs->UART_MR = mr; @@ -361,6 +225,118 @@ static bool atsam_uart_set_attributes( return true; } +static void atsam_uart_disable_rx_dma(atsam_uart_context *ctx) +{ + if (ctx->rx_dma) { + rtems_cache_coherent_free(ctx->rx_dma); + ctx->rx_dma = NULL; + } + + if (ctx->rx_dma_channel != XDMAD_ALLOC_FAILED) { + XDMAD_FreeChannel(&XDMAD_Instance, ctx->rx_dma_channel); + } + + ctx->rx_dma_enabled = false; +} + +static rtems_status_code atsam_uart_enable_rx_dma(atsam_uart_context *ctx) +{ + eXdmadRC rc; + int channel_id; + + if (ctx->rx_dma_enabled) { + return RTEMS_SUCCESSFUL; + } + + /* + * Make sure everything is in a clean default state so that the cleanup works + * in an error case. + */ + ctx->rx_dma = NULL; + ctx->rx_dma_channel = XDMAD_ALLOC_FAILED; + + ctx->rx_dma = rtems_cache_coherent_allocate(sizeof(*ctx->rx_dma), 0, 0); + if (ctx->rx_dma == NULL) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_NO_MEMORY; + } + + ctx->rx_next_read_pos = &ctx->rx_dma->buf[0]; + + ctx->rx_dma_channel = XDMAD_AllocateChannel( + &XDMAD_Instance, + XDMAD_TRANSFER_MEMORY, + ctx->id + ); + + if (ctx->rx_dma_channel == XDMAD_ALLOC_FAILED) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + rc = XDMAD_PrepareChannel(&XDMAD_Instance, ctx->rx_dma_channel); + if (rc != XDMAD_OK) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + channel_id = ctx->rx_dma_channel & 0xff; + ctx->rx_dma_da = + (char *volatile*) &XDMAD_Instance.pXdmacs->XDMAC_CHID[channel_id].XDMAC_CDA; + + ctx->rx_dma->desc.mbr_nda = (uint32_t)&ctx->rx_dma->desc; + ctx->rx_dma->desc.mbr_ubc = + 1 | + XDMA_UBC_NVIEW_NDV3 | + XDMA_UBC_NDE_FETCH_EN | + XDMA_UBC_NDEN_UPDATED | + XDMA_UBC_NSEN_UPDATED; + ctx->rx_dma->desc.mbr_sa = (uint32_t) &ctx->regs->UART_RHR; + ctx->rx_dma->desc.mbr_da = (uint32_t) &ctx->rx_dma->buf[0]; + ctx->rx_dma->desc.mbr_cfg = + XDMAC_CC_TYPE_PER_TRAN | + XDMAC_CC_MBSIZE_SINGLE | + XDMAC_CC_DSYNC_PER2MEM | + XDMAC_CC_SWREQ_HWR_CONNECTED | + XDMAC_CC_MEMSET_NORMAL_MODE | + XDMAC_CC_CSIZE_CHK_1 | + XDMAC_CC_DWIDTH_BYTE | + XDMAC_CC_SIF_AHB_IF1 | + XDMAC_CC_DIF_AHB_IF1 | + XDMAC_CC_SAM_FIXED_AM | + XDMAC_CC_DAM_UBS_AM | + XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(ctx->id, XDMAD_TRANSFER_RX)); + ctx->rx_dma->desc.mbr_bc = UART_RX_DMA_BUF_SIZE - 1; + ctx->rx_dma->desc.mbr_ds = 0; + ctx->rx_dma->desc.mbr_sus = 0; + ctx->rx_dma->desc.mbr_dus = 0; + + rc = XDMAD_ConfigureTransfer( + &XDMAD_Instance, + ctx->rx_dma_channel, + NULL, + XDMAC_CNDC_NDE_DSCR_FETCH_EN | + XDMAC_CNDC_NDVIEW_NDV3 | + XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED | + XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED, + (uint32_t)&ctx->rx_dma->desc, + 0); + if (rc != XDMAD_OK) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + rc = XDMAD_StartTransfer(&XDMAD_Instance, ctx->rx_dma_channel); + if (rc != XDMAD_OK) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + ctx->rx_dma_enabled = true; + + return RTEMS_SUCCESSFUL; +} + static bool atsam_uart_first_open( rtems_termios_tty *tty, rtems_termios_device_context *base, @@ -386,7 +362,7 @@ static bool atsam_uart_first_open( regs->UART_IER = UART_IDR_RXRDY; sc = rtems_interrupt_handler_install( ctx->irq, - "UART", + ctx->is_usart ? "USART" : "UART", RTEMS_INTERRUPT_SHARED, atsam_uart_interrupt, tty @@ -411,6 +387,10 @@ static void atsam_uart_last_close( rtems_interrupt_handler_remove(ctx->irq, atsam_uart_interrupt, tty); #endif + if (ctx->rx_dma_enabled) { + atsam_uart_disable_rx_dma(ctx); + } + if (!ctx->console) { PMC_DisablePeripheral(ctx->id); } @@ -429,16 +409,16 @@ static void atsam_uart_write( if (len > 0) { ctx->transmitting = true; regs->UART_THR = buf[0]; - regs->UART_IER = UART_IDR_TXEMPTY; + regs->UART_IER = UART_IDR_TXRDY; } else { ctx->transmitting = false; - regs->UART_IDR = UART_IDR_TXEMPTY; + regs->UART_IDR = UART_IDR_TXRDY; } #else size_t i; for (i = 0; i < len; ++i) { - while ((regs->UART_SR & UART_SR_TXEMPTY) == 0) { + while ((regs->UART_SR & UART_SR_TXRDY) == 0) { /* Wait */ } @@ -461,13 +441,41 @@ static int atsam_uart_read(rtems_termios_device_context *base) } #endif +#ifdef ATSAM_CONSOLE_USE_INTERRUPTS +static int atsam_uart_ioctl( + rtems_termios_device_context *base, + ioctl_command_t request, + void *buffer +) +{ + atsam_uart_context *ctx = (atsam_uart_context *) base; + rtems_status_code sc; + + switch (request) { + case ATSAM_UART_ENABLE_RX_DMA: + sc = atsam_uart_enable_rx_dma(ctx); + if (sc != RTEMS_SUCCESSFUL) { + rtems_set_errno_and_return_minus_one(EIO); + } else { + ctx->rx_dma_enabled = true; + } + break; + default: + rtems_set_errno_and_return_minus_one(EINVAL); + } + + return 0; +} +#endif + static const rtems_termios_device_handler atsam_uart_handler = { .first_open = atsam_uart_first_open, .last_close = atsam_uart_last_close, .write = atsam_uart_write, .set_attributes = atsam_uart_set_attributes, #ifdef ATSAM_CONSOLE_USE_INTERRUPTS - .mode = TERMIOS_IRQ_DRIVEN + .mode = TERMIOS_IRQ_DRIVEN, + .ioctl = atsam_uart_ioctl, #else .poll_read = atsam_uart_read, .mode = TERMIOS_POLLED @@ -490,7 +498,7 @@ rtems_status_code console_initialize( usart[sizeof(usart) - 2] = (char) ('0' + i); rtems_termios_device_install( &usart[0], - &atsam_usart_handler, + &atsam_uart_handler, NULL, &atsam_usart_instances[i].base ); diff --git a/bsps/arm/atsam/i2c/atsam_i2c_bus.c b/bsps/arm/atsam/i2c/atsam_i2c_bus.c index 4425975de8..3451d15bed 100644 --- a/bsps/arm/atsam/i2c/atsam_i2c_bus.c +++ b/bsps/arm/atsam/i2c/atsam_i2c_bus.c @@ -33,72 +33,65 @@ static void atsam_i2c_disable_interrupts(Twihs *regs) { - regs->TWIHS_IDR = 0xFFFFFFFF; + TWI_DisableIt(regs, 0xFFFFFFFF); } -static void -atsam_i2c_set_transfer_status(transfer_desc *trans_desc, transfer_state state) +/* + * Return true if the message is done right after this. That is the case if all + * bytes are received but no stop is requested. + */ +static bool +atsam_i2c_continue_read(Twihs *regs, atsam_i2c_bus *bus) { - trans_desc->trans_state = state; -} + bool done = false; -static void -atsam_i2c_continue_read(Twihs *regs, transfer_desc *trans_desc) -{ - trans_desc->data[trans_desc->already_transferred] = TWI_ReadByte(regs); - trans_desc->already_transferred++; + *bus->current_msg_byte = TWI_ReadByte(regs); + ++bus->current_msg_byte; + --bus->current_msg_todo; /* check for transfer finish */ - if (trans_desc->already_transferred == trans_desc->data_size) { - if (trans_desc->stop_request){ + if (bus->current_msg_todo == 0) { + if (bus->stop_request){ TWI_DisableIt(regs, TWIHS_IDR_RXRDY); TWI_EnableIt(regs, TWIHS_IER_TXCOMP); - atsam_i2c_set_transfer_status(trans_desc, TX_RX_STOP_SENT); } else { - atsam_i2c_set_transfer_status(trans_desc, RX_CONT_MESSAGE_NEEDED); + done = true; } } /* Last byte? */ - else if ((trans_desc->already_transferred == (trans_desc->data_size - 1)) - && (trans_desc->stop_request)){ + else if (bus->current_msg_todo == 1 && bus->stop_request) { TWI_Stop(regs); } + + return done; } +/* + * Return true if the message is done right after this. That is the case if all + * bytes are sent but no stop is requested. + */ static bool -atsam_i2c_is_state(transfer_desc *trans_desc, transfer_state state) +atsam_i2c_continue_write(Twihs *regs, atsam_i2c_bus *bus) { - return (trans_desc->trans_state == state); -} + bool done = false; -static void -atsam_i2c_continue_write(Twihs *regs, transfer_desc *trans_desc) -{ /* Transfer finished ? */ - if (trans_desc->already_transferred == trans_desc->data_size) { + if (bus->current_msg_todo == 0) { TWI_DisableIt(regs, TWIHS_IDR_TXRDY); - if (trans_desc->stop_request){ + if (bus->stop_request){ TWI_EnableIt(regs, TWIHS_IER_TXCOMP); TWI_SendSTOPCondition(regs); - atsam_i2c_set_transfer_status(trans_desc, TX_RX_STOP_SENT); } else { - atsam_i2c_set_transfer_status(trans_desc, TX_CONT_MESSAGE_NEEDED); + done = true; } } /* Bytes remaining */ else { - TWI_WriteByte(regs, - trans_desc->data[trans_desc->already_transferred]); - trans_desc->already_transferred++; + TWI_WriteByte(regs, *bus->current_msg_byte); + ++bus->current_msg_byte; + --bus->current_msg_todo; } -} - -static void -atsam_i2c_finish_write_transfer(Twihs *regs, transfer_desc *trans_desc) -{ - TWI_ReadByte(regs); - TWI_DisableIt(regs, TWIHS_IDR_TXCOMP); - trans_desc->status = 0; + return done; } static void @@ -115,19 +108,6 @@ atsam_i2c_next_packet(atsam_i2c_bus *bus) bus->current_msg_byte = msg->buf; } -static void -atsam_i2c_set_td(atsam_i2c_bus *bus, uint32_t already_transferred, - bool stop_needed) -{ - transfer_desc *trans_desc = &bus->trans_desc; - - trans_desc->status = ASYNC_STATUS_PENDING; - trans_desc->data = bus->current_msg_byte; - trans_desc->data_size = bus->current_msg_todo; - trans_desc->already_transferred = already_transferred; - trans_desc->stop_request = stop_needed; -} - static bool atsam_i2c_set_address_size(const i2c_msg *msg) { @@ -186,6 +166,8 @@ atsam_i2c_setup_write_transfer(atsam_i2c_bus *bus, Twihs *regs, bool ctrl, { atsam_i2c_set_address_regs(regs, slave_addr, ctrl, false); TWI_WriteByte(regs, *bus->current_msg_byte); + ++bus->current_msg_byte; + --bus->current_msg_todo; TWI_EnableIt(regs, TWIHS_IER_TXRDY); } @@ -197,8 +179,8 @@ atsam_i2c_setup_transfer(atsam_i2c_bus *bus, Twihs *regs) uint32_t msg_todo = bus->msg_todo; uint16_t slave_addr; bool ten_bit_addr = false; - uint32_t already_transferred; bool stop_needed = true; + bool read; ten_bit_addr = atsam_i2c_set_address_size(msgs); @@ -206,22 +188,17 @@ atsam_i2c_setup_transfer(atsam_i2c_bus *bus, Twihs *regs) stop_needed = false; } - bus->read = (msgs->flags & I2C_M_RD) != 0; + read = (msgs->flags & I2C_M_RD) != 0; slave_addr = msgs->addr; - already_transferred = (bus->read == true) ? 0 : 1; - atsam_i2c_set_td(bus, already_transferred, stop_needed); - - transfer_desc *trans_desc = &bus->trans_desc; + bus->stop_request = stop_needed; - if (bus->read){ + if (read){ if (bus->current_msg_todo == 1){ send_stop = true; } - atsam_i2c_set_transfer_status(trans_desc, RX_SEND_DATA); atsam_i2c_setup_read_transfer(regs, ten_bit_addr, slave_addr, send_stop); } else { - atsam_i2c_set_transfer_status(trans_desc, TX_SEND_DATA); atsam_i2c_setup_write_transfer(bus, regs, ten_bit_addr, slave_addr); } @@ -233,40 +210,28 @@ atsam_i2c_interrupt(void *arg) atsam_i2c_bus *bus = arg; uint32_t irqstatus; bool done = false; - transfer_desc *trans_desc; Twihs *regs = bus->regs; /* read interrupts */ - irqstatus = regs->TWIHS_SR; + irqstatus = TWI_GetMaskedStatus(regs); - if((irqstatus & (TWIHS_SR_ARBLST | TWIHS_SR_NACK)) != 0) { + if((irqstatus & ATSAMV_I2C_IRQ_ERROR) != 0) { done = true; - } - - trans_desc = &bus->trans_desc; - - if (((irqstatus & TWIHS_SR_RXRDY) != 0) && - (atsam_i2c_is_state(trans_desc, RX_SEND_DATA))){ - /* carry on read transfer */ - atsam_i2c_continue_read(regs, trans_desc); - } else if (((irqstatus & TWIHS_SR_TXCOMP) != 0) && - (atsam_i2c_is_state(trans_desc, TX_RX_STOP_SENT))){ - atsam_i2c_finish_write_transfer(regs, trans_desc); + } else if ((irqstatus & TWIHS_SR_RXRDY) != 0) { + done = atsam_i2c_continue_read(regs, bus); + } else if ((irqstatus & TWIHS_SR_TXCOMP) != 0) { + TWI_DisableIt(regs, TWIHS_IDR_TXCOMP); done = true; - } else if (((irqstatus & TWIHS_SR_TXRDY) != 0) && - (atsam_i2c_is_state(trans_desc, TX_SEND_DATA))){ - atsam_i2c_continue_write(regs, trans_desc); - if (trans_desc->trans_state == TX_CONT_MESSAGE_NEEDED){ - done = true; - } + } else if ((irqstatus & TWIHS_SR_TXRDY) != 0) { + done = atsam_i2c_continue_write(regs, bus); } if(done){ - uint32_t err = irqstatus & ATSAMV_I2C_IRQ_ERROR; + bus->err = irqstatus & ATSAMV_I2C_IRQ_ERROR; atsam_i2c_next_packet(bus); - if (bus->msg_todo == 0 || err != 0) { + if (bus->msg_todo == 0 || bus->err != 0) { atsam_i2c_disable_interrupts(regs); rtems_binary_semaphore_post(&bus->sem); } else { @@ -291,27 +256,38 @@ atsam_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count) if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) { return -EINVAL; } + if (msgs[i].len == 0) { + /* Hardware doesn't support zero length messages */ + return -EINVAL; + } } bus->msgs = &msgs[0]; bus->msg_todo = msg_count; bus->current_msg_todo = msgs[0].len; bus->current_msg_byte = msgs[0].buf; + bus->err = 0; regs = bus->regs; - atsam_i2c_setup_transfer(bus, regs); + /* Start with a clean start. Enable error interrupts. */ + TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK); + TWI_EnableIt(regs, ATSAMV_I2C_IRQ_ERROR); - regs->TWIHS_IER = ATSAMV_I2C_IRQ_ERROR; + atsam_i2c_setup_transfer(bus, regs); eno = rtems_binary_semaphore_wait_timed_ticks( &bus->sem, bus->base.timeout ); - if (eno != 0) { + if (eno != 0 || bus->err != 0) { TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK); rtems_binary_semaphore_try_wait(&bus->sem); - return -ETIMEDOUT; + if (bus->err != 0) { + return -EIO; + } else { + return -ETIMEDOUT; + } } return 0; diff --git a/bsps/arm/atsam/include/bsp.h b/bsps/arm/atsam/include/bsp.h index 2556f6046d..d1b021255c 100644 --- a/bsps/arm/atsam/include/bsp.h +++ b/bsps/arm/atsam/include/bsp.h @@ -33,6 +33,7 @@ #include <bspopts.h> #include <bsp/default-initial-extension.h> +#include <sys/ioccom.h> #include <rtems.h> @@ -109,6 +110,18 @@ void atsam_rtc_get_time(rtems_time_of_day *tod); void bsp_restart( const void *const addr ); +/* + * This ioctl enables the receive DMA for an UART. The DMA can be usefull if you + * loose characters in high interrupt load situations. + * + * Disabling the DMA again is only possible by closing all file descriptors of + * that UART. + * + * Note that every UART needs one DMA channel and the system has only a limited + * amount of DMAs. So only use it if you need it. + */ +#define ATSAM_UART_ENABLE_RX_DMA _IO('d', 0) + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bsps/arm/atsam/include/bsp/atsam-i2c.h b/bsps/arm/atsam/include/bsp/atsam-i2c.h index ae0fe97584..a6a9c27d48 100644 --- a/bsps/arm/atsam/include/bsp/atsam-i2c.h +++ b/bsps/arm/atsam/include/bsp/atsam-i2c.h @@ -28,35 +28,23 @@ extern "C" { #define TWI_AMOUNT_PINS 2 -typedef enum { - TX_SEND_DATA, - TX_SEND_STOP, - TX_CONT_MESSAGE_NEEDED, - RX_SEND_DATA, - RX_SEND_STOP, - RX_CONT_MESSAGE_NEEDED, - TX_RX_STOP_SENT -}transfer_state; - -typedef struct { - uint8_t status; - uint8_t *data; - bool stop_request; - uint32_t data_size; - uint32_t already_transferred; - transfer_state trans_state; -} transfer_desc; - typedef struct { i2c_bus base; - i2c_msg *msgs; Twihs *regs; - transfer_desc trans_desc; + + /* First message and number of messages that have to be processed. */ + i2c_msg *msgs; uint32_t msg_todo; + + /* Information about the current transfer. */ + bool stop_request; uint32_t current_msg_todo; uint8_t *current_msg_byte; + + /* Error information that can be returned to the task */ + uint32_t err; + uint32_t output_clock; - bool read; rtems_binary_semaphore sem; rtems_vector_number irq; } atsam_i2c_bus; diff --git a/bsps/arm/headers.am b/bsps/arm/headers.am index 3d2b09effa..69a154f6b2 100644 --- a/bsps/arm/headers.am +++ b/bsps/arm/headers.am @@ -39,6 +39,10 @@ include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/lpc-timer.h include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/start.h include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/zynq-uart-regs.h include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/zynq-uart.h +include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/cadence-spi-regs.h +include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/cadence-spi.h +include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/xilinx-axi-spi-regs.h +include_bsp_HEADERS += ../../../../../bsps/arm/include/bsp/xilinx-axi-spi.h include_libcpudir = $(includedir)/libcpu include_libcpu_HEADERS = diff --git a/bsps/arm/imx/start/bspstart.c b/bsps/arm/imx/start/bspstart.c index 36f62993a6..07cf33a5ed 100644 --- a/bsps/arm/imx/start/bspstart.c +++ b/bsps/arm/imx/start/bspstart.c @@ -18,6 +18,7 @@ #include <bsp/fdt.h> #include <bsp/irq-generic.h> #include <bsp/linker-symbols.h> +#include <libcpu/arm-cp15.h> #include <libfdt.h> @@ -58,6 +59,60 @@ uint32_t bsp_fdt_map_intr(const uint32_t *intr, size_t icells) return intr[1] + MAGIC_IRQ_OFFSET; } +static bool imx_is_imx6(const void *fdt) +{ + /* + * At the moment: Check for some compatible strings that should be there + * somewhere in every fdt. + * + * FIXME: It would be nice if some CPU-ID could be used instead. But I didn't + * find one. + */ + int node; + + node = fdt_node_offset_by_compatible(fdt, -1, "fsl,imx6ul"); + if (node >= 0) { + return true; + } + + node = fdt_node_offset_by_compatible(fdt, -1, "fsl,imx6ull"); + if (node >= 0) { + return true; + } + + return false; +} + +#define SYSCNT_CNTCR (0x0) +#define SYSCNT_CNTCR_ENABLE (1 << 0) +#define SYSCNT_CNTCR_HDBG (1 << 1) +#define SYSCNT_CNTCR_FCREQ(n) (1 << (8 + (n))) +#define SYSCNT_CNTFID(n) (0x20 + 4 * (n)) + +static uint32_t imx_syscnt_enable_and_return_frequency(const void *fdt) +{ + uint32_t freq; + volatile void *syscnt_base; + + /* That's not in the usual FDTs. Sorry for falling back to a magic value. */ + if (imx_is_imx6(fdt)) { + syscnt_base = (void *)0x021dc000; + } else { + syscnt_base = (void *)0x306c0000; + } + + freq = *(uint32_t *)(syscnt_base + SYSCNT_CNTFID(0)); + + arm_cp15_set_counter_frequency(freq); + + *(uint32_t *)(syscnt_base + SYSCNT_CNTCR) = + SYSCNT_CNTCR_ENABLE | + SYSCNT_CNTCR_HDBG | + SYSCNT_CNTCR_FCREQ(0); + + return freq; +} + void arm_generic_timer_get_config( uint32_t *frequency, uint32_t *irq @@ -75,7 +130,11 @@ void arm_generic_timer_get_config( if (val != NULL && len >= 4) { *frequency = fdt32_to_cpu(val[0]); } else { - bsp_fatal(IMX_FATAL_GENERIC_TIMER_FREQUENCY); + /* + * Normally clock-frequency would be provided by the boot loader. If it + * didn't add one, we have to initialize the system counter ourself. + */ + *frequency = imx_syscnt_enable_and_return_frequency(fdt); } /* FIXME: Figure out how Linux gets a proper IRQ number */ diff --git a/bsps/arm/include/bsp/arm-cp15-start.h b/bsps/arm/include/bsp/arm-cp15-start.h index 86c4f8afcb..75cbdac848 100644 --- a/bsps/arm/include/bsp/arm-cp15-start.h +++ b/bsps/arm/include/bsp/arm-cp15-start.h @@ -74,6 +74,10 @@ typedef struct { .end = (uint32_t) bsp_section_bss_end, \ .flags = ARMV7_MMU_DATA_READ_WRITE_CACHED \ }, { \ + .begin = (uint32_t) bsp_section_rtemsstack_begin, \ + .end = (uint32_t) bsp_section_rtemsstack_end, \ + .flags = ARMV7_MMU_DATA_READ_WRITE_CACHED \ + }, { \ .begin = (uint32_t) bsp_section_work_begin, \ .end = (uint32_t) bsp_section_work_end, \ .flags = ARMV7_MMU_DATA_READ_WRITE_CACHED \ @@ -95,7 +99,7 @@ typedef struct { .flags = ARMV7_MMU_DATA_READ_WRITE_CACHED \ } -#define ARMV7_CP15_START_WORKSPACE_ENTRY_INDEX 8 +#define ARMV7_CP15_START_WORKSPACE_ENTRY_INDEX 9 BSP_START_DATA_SECTION extern const arm_cp15_start_section_config arm_cp15_start_mmu_config_table[]; diff --git a/bsps/arm/include/bsp/cadence-spi-regs.h b/bsps/arm/include/bsp/cadence-spi-regs.h new file mode 100644 index 0000000000..b4b2366b3d --- /dev/null +++ b/bsps/arm/include/bsp/cadence-spi-regs.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_ARM_XILINX_ZYNQ_CADENCE_SPI_REGS_H +#define LIBBSP_ARM_XILINX_ZYNQ_CADENCE_SPI_REGS_H + +#include <bsp/utility.h> + +typedef struct { + uint32_t config; +#define CADENCE_SPI_CONFIG_MODEFAIL_EN BSP_BIT32(17) +#define CADENCE_SPI_CONFIG_MANSTRT BSP_BIT32(16) +#define CADENCE_SPI_CONFIG_MANSTRT_EN BSP_BIT32(15) +#define CADENCE_SPI_CONFIG_MANUAL_CS BSP_BIT32(14) +#define CADENCE_SPI_CONFIG_CS(val) BSP_FLD32(val, 10, 13) +#define CADENCE_SPI_CONFIG_CS_GET(reg) BSP_FLD32GET(reg, 10, 13) +#define CADENCE_SPI_CONFIG_CS_SET(reg, val) BSP_FLD32SET(reg, val, 10, 13) +#define CADENCE_SPI_CONFIG_PERI_SEL BSP_BIT32(9) +#define CADENCE_SPI_CONFIG_REF_CLK BSP_BIT32(8) +#define CADENCE_SPI_CONFIG_BAUD_DIV(val) BSP_FLD32(val, 3, 5) +#define CADENCE_SPI_CONFIG_BAUD_DIV_GET(reg) BSP_FLD32GET(reg, 3, 5) +#define CADENCE_SPI_CONFIG_BAUD_DIV_SET(reg, val) BSP_FLD32SET(reg, val, 3, 5) +#define CADENCE_SPI_CONFIG_CLK_PH BSP_BIT32(2) +#define CADENCE_SPI_CONFIG_CLK_POL BSP_BIT32(1) +#define CADENCE_SPI_CONFIG_MSTREN BSP_BIT32(0) + uint32_t irqstatus; + uint32_t irqenable; + uint32_t irqdisable; + uint32_t irqmask; +#define CADENCE_SPI_IXR_TXUF BSP_BIT32(6) +#define CADENCE_SPI_IXR_RXFULL BSP_BIT32(5) +#define CADENCE_SPI_IXR_RXNEMPTY BSP_BIT32(4) +#define CADENCE_SPI_IXR_TXFULL BSP_BIT32(3) +#define CADENCE_SPI_IXR_TXOW BSP_BIT32(2) +#define CADENCE_SPI_IXR_MODF BSP_BIT32(1) +#define CADENCE_SPI_IXR_RXOVR BSP_BIT32(0) + uint32_t spienable; +#define CADENCE_SPI_EN BSP_BIT32(0) + uint32_t delay; +#define CADENCE_SPI_DELAY_DNSS(val) BSP_FLD32(val, 24, 31) +#define CADENCE_SPI_DELAY_DNSS_GET(reg) BSP_FLD32GET(reg, 24, 31) +#define CADENCE_SPI_DELAY_DNSS_SET(reg, val) BSP_FLD32SET(reg, val, 24, 31) +#define CADENCE_SPI_DELAY_DBTWN(val) BSP_FLD32(val, 16, 23) +#define CADENCE_SPI_DELAY_DBTWN_GET(reg) BSP_FLD32GET(reg, 16, 23) +#define CADENCE_SPI_DELAY_DBTWN_SET(reg, val) BSP_FLD32SET(reg, val, 16, 23) +#define CADENCE_SPI_DELAY_DAFTER(val) BSP_FLD32(val, 8, 15) +#define CADENCE_SPI_DELAY_DAFTER_GET(reg) BSP_FLD32GET(reg, 8, 15) +#define CADENCE_SPI_DELAY_DAFTER_SET(reg, val) BSP_FLD32SET(reg, val, 8, 15) +#define CADENCE_SPI_DELAY_DINT(val) BSP_FLD32(val, 0, 7) +#define CADENCE_SPI_DELAY_DINT_GET(reg) BSP_FLD32GET(reg, 0, 7) +#define CADENCE_SPI_DELAY_DINT_SET(reg, val) BSP_FLD32SET(reg, val, 0, 7) + uint32_t txdata; + uint32_t rxdata; + uint32_t slave_idle_count; + uint32_t txthreshold; + uint32_t rxthreshold; + uint32_t moduleid; +} cadence_spi; + +#endif /* LIBBSP_ARM_XILINX_ZYNQ_CADENCE_SPI_REGS_H */ diff --git a/bsps/arm/include/bsp/cadence-spi.h b/bsps/arm/include/bsp/cadence-spi.h new file mode 100644 index 0000000000..d97ede53c8 --- /dev/null +++ b/bsps/arm/include/bsp/cadence-spi.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_ARM_XILINX_ZYNQ_CADENCE_SPI_H +#define LIBBSP_ARM_XILINX_ZYNQ_CADENCE_SPI_H + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Register the cadence-spi device with rtems + * + * @param bus_path path of the new device (e.g. /dev/spi0) + * @register_base Address of the first (i.e. config) register + * @input_clock Configured frequency of the input clock + * + * @return RTEMS_SUCCESSFUL on success, negative number on failure + * + * Note: The spi frequencies the cadence spi device can achieve + * are the @p input_clock divided by a power of 2 between + * 4 and 256. + * The driver tries to find a divider which yields a spi + * frequency equal to or lower than the desired bus frequency. + */ +int spi_bus_register_cadence( + const char *bus_path, + uintptr_t register_base, + uint32_t input_clock, + rtems_vector_number irq +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_ARM_XILINX_ZYNQ_CADENCE_SPI_H */ diff --git a/bsps/arm/include/bsp/linker-symbols.h b/bsps/arm/include/bsp/linker-symbols.h index d743f67f50..43651ff7f7 100644 --- a/bsps/arm/include/bsp/linker-symbols.h +++ b/bsps/arm/include/bsp/linker-symbols.h @@ -90,6 +90,10 @@ LINKER_SYMBOL(bsp_section_bss_begin) LINKER_SYMBOL(bsp_section_bss_end) LINKER_SYMBOL(bsp_section_bss_size) +LINKER_SYMBOL(bsp_section_rtemsstack_begin) +LINKER_SYMBOL(bsp_section_rtemsstack_end) +LINKER_SYMBOL(bsp_section_rtemsstack_size) + LINKER_SYMBOL(bsp_section_work_begin) LINKER_SYMBOL(bsp_section_work_end) LINKER_SYMBOL(bsp_section_work_size) diff --git a/bsps/arm/include/bsp/xilinx-axi-spi-regs.h b/bsps/arm/include/bsp/xilinx-axi-spi-regs.h new file mode 100644 index 0000000000..6211c5b97f --- /dev/null +++ b/bsps/arm/include/bsp/xilinx-axi-spi-regs.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_ARM_XILINX_AXI_SPI_REGS_H +#define LIBBSP_ARM_XILINX_AXI_SPI_REGS_H + +#include <bsp/utility.h> + +typedef struct { + uint32_t reserved1[7]; + uint32_t globalirq; +#define XILINX_AXI_SPI_GLOBAL_IRQ_ENABLE BSP_BIT32(31) + uint32_t irqstatus; + uint32_t reserved2; + uint32_t irqenable; +#define XILINX_AXI_SPI_IRQ_CMD_ERR BSP_BIT32(13) +#define XILINX_AXI_SPI_IRQ_LOOP_ERR BSP_BIT32(12) +#define XILINX_AXI_SPI_IRQ_MSB_ERR BSP_BIT32(11) +#define XILINX_AXI_SPI_IRQ_SLV_ERR BSP_BIT32(10) +#define XILINX_AXI_SPI_IRQ_CPOL_CPHA_ERR BSP_BIT32(9) +#define XILINX_AXI_SPI_IRQ_RXNEMPTY BSP_BIT32(8) +#define XILINX_AXI_SPI_IRQ_CS_MODE BSP_BIT32(7) +#define XILINX_AXI_SPI_IRQ_TXHALF BSP_BIT32(6) +#define XILINX_AXI_SPI_IRQ_RXOVR BSP_BIT32(5) +#define XILINX_AXI_SPI_IRQ_RXFULL BSP_BIT32(4) +#define XILINX_AXI_SPI_IRQ_TXUF BSP_BIT32(3) +#define XILINX_AXI_SPI_IRQ_TXEMPTY BSP_BIT32(2) +#define XILINX_AXI_SPI_IRQ_SLV_MODF BSP_BIT32(1) +#define XILINX_AXI_SPI_IRQ_MODF BSP_BIT32(0) + uint32_t reserved3[5]; + uint32_t reset; +#define XILINX_AXI_SPI_RESET 0x0000000a + uint32_t reserved4[7]; + uint32_t control; +#define XILINX_AXI_SPI_CONTROL_LSBFIRST BSP_BIT32(9) +#define XILINX_AXI_SPI_CONTROL_MST_TRANS_INHIBIT BSP_BIT32(8) +#define XILINX_AXI_SPI_CONTROL_MANUAL_CS BSP_BIT32(7) +#define XILINX_AXI_SPI_CONTROL_RX_FIFO_RESET BSP_BIT32(6) +#define XILINX_AXI_SPI_CONTROL_TX_FIFO_RESET BSP_BIT32(5) +#define XILINX_AXI_SPI_CONTROL_CPHA BSP_BIT32(4) +#define XILINX_AXI_SPI_CONTROL_CPOL BSP_BIT32(3) +#define XILINX_AXI_SPI_CONTROL_MSTREN BSP_BIT32(2) +#define XILINX_AXI_SPI_CONTROL_SPIEN BSP_BIT32(1) +#define XILINX_AXI_SPI_CONTROL_LOOP BSP_BIT32(0) + uint32_t status; +#define XILINX_AXI_SPI_STATUS_CMD_ERR BSP_BIT32(10) +#define XILINX_AXI_SPI_STATUS_LOOP_ERR BSP_BIT32(9) +#define XILINX_AXI_SPI_STATUS_MSB_ERR BSP_BIT32(8) +#define XILINX_AXI_SPI_STATUS_SLV_ERR BSP_BIT32(7) +#define XILINX_AXI_SPI_STATUS_CPOL_CPHA_ERR BSP_BIT32(6) +#define XILINX_AXI_SPI_STATUS_SLV_MODE BSP_BIT32(5) +#define XILINX_AXI_SPI_STATUS_MODF BSP_BIT32(4) +#define XILINX_AXI_SPI_STATUS_TXFULL BSP_BIT32(3) +#define XILINX_AXI_SPI_STATUS_TXEMPTY BSP_BIT32(2) +#define XILINX_AXI_SPI_STATUS_RXFULL BSP_BIT32(1) +#define XILINX_AXI_SPI_STATUS_RXEMPTY BSP_BIT32(0) + uint32_t txdata; + uint32_t rxdata; + uint32_t cs; + uint32_t tx_fifo_len; + uint32_t rx_fifo_len; +} xilinx_axi_spi; + +#endif /* LIBBSP_ARM_XILINX_AXI_SPI_REGS_H */ diff --git a/bsps/arm/include/bsp/xilinx-axi-spi.h b/bsps/arm/include/bsp/xilinx-axi-spi.h new file mode 100644 index 0000000000..1f5e2a9989 --- /dev/null +++ b/bsps/arm/include/bsp/xilinx-axi-spi.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_ARM_XILINX_AXI_SPI_H +#define LIBBSP_ARM_XILINX_AXI_SPI_H + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Register Xilinx AXI SPI device + * + * Note: + * The Xilinx Quad SPI device is very versatile and + * supports many options. This driver assumes the + * following setup: + * - Standard SPI mode and AXI Lite interface + * - FIFO available (driver might also work without FIFOs) + * + * @param bus_path path for the new device node (e.g. "/dev/spi0") + * @param register_base base address of the device + * @param fifo_size Configured fifo size. Either 0, 16 or 256 + * @param num_cs Number of configured CS lines (0-32) + * + * @return 0 on success. Negative number otherwise. + * + */ +int spi_bus_register_xilinx_axi( + const char *bus_path, + uintptr_t register_base, + uint32_t fifo_size, + uint32_t num_cs, + rtems_vector_number irq +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_ARM_XILINX_AXI_SPI_H */ diff --git a/bsps/arm/include/bsp/zynq-uart.h b/bsps/arm/include/bsp/zynq-uart.h index 20c3c9b653..5a6c926bec 100644 --- a/bsps/arm/include/bsp/zynq-uart.h +++ b/bsps/arm/include/bsp/zynq-uart.h @@ -71,6 +71,13 @@ void zynq_uart_write_polled( */ void zynq_uart_reset_tx_flush(zynq_uart_context *ctx); +int zynq_cal_baud_rate( + uint32_t baudrate, + uint32_t* brgr, + uint32_t* bauddiv, + uint32_t modereg +); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bsps/arm/shared/serial/zynq-uart-polled.c b/bsps/arm/shared/serial/zynq-uart-polled.c index e6f478ee07..7fc9590832 100644 --- a/bsps/arm/shared/serial/zynq-uart-polled.c +++ b/bsps/arm/shared/serial/zynq-uart-polled.c @@ -40,7 +40,7 @@ uint32_t zynq_uart_input_clock(void) return ZYNQ_CLOCK_UART; } -static int zynq_cal_baud_rate(uint32_t baudrate, +int zynq_cal_baud_rate(uint32_t baudrate, uint32_t* brgr, uint32_t* bauddiv, uint32_t modereg) diff --git a/bsps/arm/shared/serial/zynq-uart.c b/bsps/arm/shared/serial/zynq-uart.c index fc670441b8..f298719fde 100644 --- a/bsps/arm/shared/serial/zynq-uart.c +++ b/bsps/arm/shared/serial/zynq-uart.c @@ -142,25 +142,82 @@ static bool zynq_uart_set_attributes( const struct termios *term ) { -#if 0 - volatile zynq_uart *regs = zynq_uart_get_regs(minor); + zynq_uart_context *ctx = (zynq_uart_context *) context; + volatile zynq_uart *regs = ctx->regs; + int32_t baud; uint32_t brgr = 0; uint32_t bauddiv = 0; + uint32_t mode = 0; int rc; - rc = zynq_cal_baud_rate(115200, &brgr, &bauddiv, regs->mode); - if (rc != 0) - return rc; + /* + * Determine the baud rate + */ + baud = rtems_termios_baud_to_number(term->c_ospeed); + + if (baud > 0) { + rc = zynq_cal_baud_rate(baud, &brgr, &bauddiv, regs->mode); + if (rc != 0) + return rc; + } + + /* + * Configure the mode register + */ + mode |= ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL); + + /* + * Parity + */ + mode |= ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_NONE); + if (term->c_cflag & PARENB) { + if (!(term->c_cflag & PARODD)) { + mode |= ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_ODD); + } else { + mode |= ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_EVEN); + } + } + + /* + * Character Size + */ + switch (term->c_cflag & CSIZE) + { + case CS5: + return false; + case CS6: + mode |= ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_6); + break; + case CS7: + mode |= ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_7); + break; + case CS8: + default: + mode |= ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_8); + break; + } + + /* + * Stop Bits + */ + if (term->c_cflag & CSTOPB) { + /* 2 stop bits */ + mode |= ZYNQ_UART_MODE_NBSTOP(ZYNQ_UART_MODE_NBSTOP_STOP_2); + } else { + /* 1 stop bit */ + mode |= ZYNQ_UART_MODE_NBSTOP(ZYNQ_UART_MODE_NBSTOP_STOP_1); + } regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN); - regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr); - regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv); + regs->mode = mode; + /* Ignore baud rate of B0. There are no modem control lines to de-assert */ + if (baud > 0) { + regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr); + regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv); + } regs->control |= ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN; return true; -#else - return false; -#endif } const rtems_termios_device_handler zynq_uart_handler = { diff --git a/bsps/arm/shared/spi/cadence-spi.c b/bsps/arm/shared/spi/cadence-spi.c new file mode 100644 index 0000000000..8e168d2e72 --- /dev/null +++ b/bsps/arm/shared/spi/cadence-spi.c @@ -0,0 +1,444 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <bsp.h> + +#include <rtems/irq-extension.h> +#include <sys/param.h> /* MAX() */ + +#include <dev/spi/spi.h> +#include <bsp/cadence-spi.h> +#include <bsp/cadence-spi-regs.h> + +#define CADENCE_SPI_FIFO_SIZE 128 +#define CADENCE_SPI_MAX_CHIPSELECTS 3 +#define CADENCE_SPI_CS_NONE 0xF + +typedef struct cadence_spi_bus cadence_spi_bus; + +struct cadence_spi_bus { + spi_bus base; + volatile cadence_spi *regs; + uint32_t clk_in; + uint16_t clk_per_usecs; + uint32_t msg_todo; + const spi_ioc_transfer *msg; + uint32_t todo; + uint32_t in_transfer; + uint8_t *rx_buf; + const uint8_t *tx_buf; + rtems_id task_id; + rtems_vector_number irq; +}; + + +static void cadence_spi_disable_interrupts(volatile cadence_spi *regs) +{ + regs->irqdisable = 0xffff; +} + +static void cadence_spi_clear_irq_status(volatile cadence_spi *regs) +{ + regs->irqstatus = regs->irqstatus; +} + +static bool cadence_spi_rx_fifo_not_empty(volatile cadence_spi *regs) +{ + return (regs->irqstatus & CADENCE_SPI_IXR_RXNEMPTY) != 0; +} + +static void cadence_spi_reset(cadence_spi_bus *bus) +{ + volatile cadence_spi *regs = bus->regs; + uint32_t val; + + cadence_spi_disable_interrupts(regs); + + regs->spienable = 0; + + val = regs->config; + val &= ~(CADENCE_SPI_CONFIG_MODEFAIL_EN + | CADENCE_SPI_CONFIG_MANSTRT_EN + | CADENCE_SPI_CONFIG_MANUAL_CS + | CADENCE_SPI_CONFIG_PERI_SEL + | CADENCE_SPI_CONFIG_REF_CLK); + + val |= CADENCE_SPI_CONFIG_CS(CADENCE_SPI_CS_NONE) + | CADENCE_SPI_CONFIG_MSTREN + | CADENCE_SPI_CONFIG_MANSTRT; + regs->config = val; + + /* Initialize here, will be set for every transfer */ + regs->txthreshold = 1; + /* Set to 1, so we can check when the rx buffer is empty */ + regs->rxthreshold = 1; + + while (cadence_spi_rx_fifo_not_empty(regs)) { + regs->rxdata; + } + cadence_spi_clear_irq_status(regs); +} + +static void cadence_spi_done(cadence_spi_bus *bus) +{ + volatile cadence_spi *regs = bus->regs; + regs->spienable = 0; + cadence_spi_disable_interrupts(regs); + cadence_spi_clear_irq_status(regs); + rtems_event_transient_send(bus->task_id); +} + +static void cadence_spi_push(cadence_spi_bus *bus, volatile cadence_spi *regs) +{ + while (bus->todo > 0 && bus->in_transfer < CADENCE_SPI_FIFO_SIZE) { + uint8_t val = 0; + if (bus->tx_buf != NULL) { + val = *bus->tx_buf; + ++bus->tx_buf; + } + + --bus->todo; + regs->txdata = val; + ++bus->in_transfer; + } +} + +static void +cadence_spi_set_chipsel(cadence_spi_bus *bus, uint32_t cs) +{ + + volatile cadence_spi *regs = bus->regs; + uint32_t cs_bit = CADENCE_SPI_CS_NONE; + uint32_t config = regs->config; + + if (cs != SPI_NO_CS && cs < CADENCE_SPI_MAX_CHIPSELECTS) { + cs_bit >>= (CADENCE_SPI_MAX_CHIPSELECTS - cs + 1); + } + config = CADENCE_SPI_CONFIG_CS_SET(config, cs_bit); + bus->base.cs = cs; + + regs->config = config; +} + +static uint32_t +cadence_spi_baud_divider(cadence_spi_bus *bus, + uint32_t speed_hz) +{ + uint32_t div; + uint32_t clk_in; + + clk_in = bus->clk_in; + + div = clk_in / speed_hz; + div = fls((int) div); + + if (clk_in > (speed_hz << div)) { + ++div; + } + + /* The baud divider needs to be at least 4, i.e. 2^2 */ + div = MAX(2, div); + + /* 0b111 is the maximum possible divider value */ + div = MIN(7, div-1); + return div; +} + +static void cadence_spi_config( + cadence_spi_bus *bus, + volatile cadence_spi *regs, + uint32_t speed_hz, + uint32_t mode, + uint16_t delay_usecs, + uint8_t cs +) +{ + spi_bus *base = &bus->base; + uint32_t config = regs->config; + uint32_t delay; + + regs->spienable = 0; + + if ((mode & SPI_CPHA) != 0) { + config |= CADENCE_SPI_CONFIG_CLK_PH; + } else { + config &= ~CADENCE_SPI_CONFIG_CLK_PH; + } + + if ((mode & SPI_CPOL) != 0) { + config |= CADENCE_SPI_CONFIG_CLK_POL; + } else { + config &= ~CADENCE_SPI_CONFIG_CLK_POL; + } + + config = CADENCE_SPI_CONFIG_BAUD_DIV_SET(config, + cadence_spi_baud_divider(bus, speed_hz)); + + regs->config = config; + cadence_spi_set_chipsel(bus, cs); + + delay = regs->delay; + delay = CADENCE_SPI_DELAY_DBTWN_SET(delay, delay_usecs * bus->clk_per_usecs); + regs->delay = delay; + + base->speed_hz = speed_hz; + base->mode = mode; + base->cs = cs; + +} + +static void +cadence_spi_next_msg(cadence_spi_bus *bus, volatile cadence_spi *regs) +{ + if (bus->msg_todo > 0) { + const spi_ioc_transfer *msg; + spi_bus *base = &bus->base; + + msg = bus->msg; + + if ( + msg->speed_hz != base->speed_hz + || msg->mode != base->mode + || msg->cs != base->cs + ) { + cadence_spi_config( + bus, + regs, + msg->speed_hz, + msg->mode, + msg->delay_usecs, + msg->cs + ); + } + + if ((msg->mode & SPI_NO_CS) != 0) { + cadence_spi_set_chipsel(bus, CADENCE_SPI_CS_NONE); + } + + bus->todo = msg->len; + bus->rx_buf = msg->rx_buf; + bus->tx_buf = msg->tx_buf; + cadence_spi_push(bus, regs); + + cadence_spi_disable_interrupts(regs); + if (bus->todo < CADENCE_SPI_FIFO_SIZE) { + /* if the msg fits into the FIFO for empty TX buffer */ + regs->txthreshold = 1; + + } else { + /* if the msg does not fit refill tx_buf when the threshold is hit */ + regs->txthreshold = CADENCE_SPI_FIFO_SIZE / 2; + } + regs->irqenable = CADENCE_SPI_IXR_TXOW; + regs->spienable = 1; + } else { + cadence_spi_done(bus); + } +} + +static void cadence_spi_interrupt(void *arg) +{ + cadence_spi_bus *bus; + volatile cadence_spi *regs; + + bus = arg; + regs = bus->regs; + + /* The RXNEMPTY flag is sometimes not cleared fast + * enough between 2 reads which could lead to + * reading an extra byte erroneously. Therefore, + * also check the in_transfer counter + */ + while (cadence_spi_rx_fifo_not_empty(regs) + && bus->in_transfer > 0) { + uint32_t val = regs->rxdata; + if (bus->rx_buf != NULL) { + *bus->rx_buf = (uint8_t)val; + ++bus->rx_buf; + } + --bus->in_transfer; + } + + if (bus->todo > 0) { + cadence_spi_push(bus, regs); + } else if (bus->in_transfer > 0) { + /* Wait until all bytes have been transfered */ + regs->txthreshold = 1; + } else { + --bus->msg_todo; + ++bus->msg; + cadence_spi_next_msg(bus, regs); + } +} + +static int cadence_spi_check_messages( + cadence_spi_bus *bus, + const spi_ioc_transfer *msg, + uint32_t size) +{ + while(size > 0) { + if (msg->bits_per_word != 8) { + return -EINVAL; + } + if ((msg->mode & + ~(SPI_CPHA | SPI_CPOL | SPI_NO_CS)) != 0) { + return -EINVAL; + } + if ((msg->mode & SPI_NO_CS) == 0 && + (msg->cs > CADENCE_SPI_MAX_CHIPSELECTS)) { + return -EINVAL; + } + + ++msg; + --size; + } + + return 0; +} + +static int cadence_spi_transfer( + spi_bus *base, + const spi_ioc_transfer *msgs, + uint32_t n +) +{ + cadence_spi_bus *bus; + int rv; + + bus = (cadence_spi_bus *) base; + + rv = cadence_spi_check_messages(bus, msgs, n); + + if (rv == 0) { + bus->msg_todo = n; + bus->msg = &msgs[0]; + bus->task_id = rtems_task_self(); + + cadence_spi_next_msg(bus, bus->regs); + rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + cadence_spi_set_chipsel(bus, CADENCE_SPI_CS_NONE); + } + return rv; +} + +static void cadence_spi_destroy(spi_bus *base) +{ + cadence_spi_bus *bus; + + bus = (cadence_spi_bus *) base; + rtems_interrupt_handler_remove(bus->irq, cadence_spi_interrupt, bus); + spi_bus_destroy_and_free(&bus->base); +} + +static int cadence_spi_setup(spi_bus *base) +{ + cadence_spi_bus *bus; + uint32_t mode = base->mode; + + bus = (cadence_spi_bus *) base; + + /* Baud rate divider is at least 4 and at most 256 */ + if ( + base->speed_hz > base->max_speed_hz + || base->speed_hz < (bus->clk_in / 256) + || bus->base.bits_per_word > 8 + ) { + return -EINVAL; + } + + /* SPI_CS_HIGH and SPI_LOOP not supported */ + if ((mode & SPI_CS_HIGH) || (mode & SPI_LOOP)) { + return -EINVAL; + } + + cadence_spi_config( + bus, + bus->regs, + base->speed_hz, + base->mode, + base->delay_usecs, + base->cs + ); + return 0; +} + +int spi_bus_register_cadence(const char *bus_path, + uintptr_t register_base, + uint32_t input_clock, + rtems_vector_number irq) +{ + cadence_spi_bus *bus; + spi_bus *base; + int sc; + + bus = (cadence_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus)); + if (bus == NULL){ + return -1; + } + + base = &bus->base; + bus->regs = (volatile cadence_spi *) register_base; + bus->clk_in = input_clock; + bus->clk_per_usecs = input_clock / 1000000; + bus->irq = irq; + + /* The minimum clock divider is 4 */ + base->max_speed_hz = (input_clock + 3) / 4; + base->delay_usecs = 0; + base->speed_hz = base->max_speed_hz; + base->cs = SPI_NO_CS; + + cadence_spi_reset(bus); + cadence_spi_config( + bus, + bus->regs, + base->speed_hz, + base->mode, + base->delay_usecs, + base->cs + ); + + + sc = rtems_interrupt_handler_install( + bus->irq, + "CASPI", + RTEMS_INTERRUPT_UNIQUE, + cadence_spi_interrupt, + bus + ); + if (sc != RTEMS_SUCCESSFUL) { + (*bus->base.destroy)(&bus->base); + rtems_set_errno_and_return_minus_one(EAGAIN); + } + + bus->base.transfer = cadence_spi_transfer; + bus->base.destroy = cadence_spi_destroy; + bus->base.setup = cadence_spi_setup; + + return spi_bus_register(&bus->base, bus_path); +} diff --git a/bsps/arm/shared/spi/xilinx-axi-spi.c b/bsps/arm/shared/spi/xilinx-axi-spi.c new file mode 100644 index 0000000000..7c4c9de8db --- /dev/null +++ b/bsps/arm/shared/spi/xilinx-axi-spi.c @@ -0,0 +1,402 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2021 Jan Sommer, German Aerospace Center (DLR) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <bsp.h> + +#include <rtems/irq-extension.h> +#include <sys/param.h> /* MAX() */ + +#include <dev/spi/spi.h> +#include <bsp/xilinx-axi-spi.h> +#include <bsp/xilinx-axi-spi-regs.h> + +#define XILINX_AXI_SPI_CS_NONE 0xFF + +typedef struct xilinx_axi_spi_bus xilinx_axi_spi_bus; + +struct xilinx_axi_spi_bus { + spi_bus base; + volatile xilinx_axi_spi *regs; + uint32_t fifo_size; + uint32_t num_cs; + uint32_t msg_todo; + const spi_ioc_transfer *msg; + uint32_t todo; + uint32_t in_transfer; + uint8_t *rx_buf; + const uint8_t *tx_buf; + rtems_id task_id; + rtems_vector_number irq; +}; + + +static void xilinx_axi_spi_disable_interrupts(volatile xilinx_axi_spi *regs) +{ + regs->globalirq = 0; + regs->irqenable = 0; +} + +static bool xilinx_axi_spi_rx_fifo_not_empty(volatile xilinx_axi_spi *regs) +{ + return (regs->status & XILINX_AXI_SPI_STATUS_RXEMPTY) == 0; +} + +static void xilinx_axi_spi_reset(xilinx_axi_spi_bus *bus) +{ + volatile xilinx_axi_spi *regs = bus->regs; + uint32_t control; + + /* Initiate soft reset for initial state */ + regs->reset = XILINX_AXI_SPI_RESET; + + /* Configure as master */ + control = regs->control; + control |= XILINX_AXI_SPI_CONTROL_MSTREN; + regs->control = control; +} + +static void xilinx_axi_spi_done(xilinx_axi_spi_bus *bus) +{ + volatile xilinx_axi_spi *regs = bus->regs; + uint32_t control = regs->control; + control &= ~XILINX_AXI_SPI_CONTROL_SPIEN; + regs->control = control; + + xilinx_axi_spi_disable_interrupts(regs); + rtems_event_transient_send(bus->task_id); +} + +static void xilinx_axi_spi_push(xilinx_axi_spi_bus *bus, volatile xilinx_axi_spi *regs) +{ + while (bus->todo > 0 && bus->in_transfer < bus->fifo_size) { + uint8_t val = 0; + if (bus->tx_buf != NULL) { + val = *bus->tx_buf; + ++bus->tx_buf; + } + + --bus->todo; + regs->txdata = val; + ++bus->in_transfer; + } +} + +static void +xilinx_axi_spi_set_chipsel(xilinx_axi_spi_bus *bus, uint32_t cs) +{ + + volatile xilinx_axi_spi *regs = bus->regs; + uint32_t cs_bit = XILINX_AXI_SPI_CS_NONE; + + if (cs != SPI_NO_CS && cs < bus->num_cs) { + cs_bit &= ~(1<<cs); + } + bus->base.cs = cs; + + regs->cs = cs_bit; +} + +static void xilinx_axi_spi_config( + xilinx_axi_spi_bus *bus, + volatile xilinx_axi_spi *regs, + uint32_t mode, + uint8_t cs +) +{ + spi_bus *base = &bus->base; + uint32_t control = regs->control; + + control &= ~XILINX_AXI_SPI_CONTROL_SPIEN; + regs->control = control; + + if ((mode & SPI_CPHA) != 0) { + control |= XILINX_AXI_SPI_CONTROL_CPHA; + } else { + control &= ~XILINX_AXI_SPI_CONTROL_CPHA; + } + + if ((mode & SPI_CPOL) != 0) { + control |= XILINX_AXI_SPI_CONTROL_CPOL; + } else { + control &= ~XILINX_AXI_SPI_CONTROL_CPOL; + } + + if ((mode & SPI_LOOP) != 0) { + control |= XILINX_AXI_SPI_CONTROL_LOOP; + } else { + control &= ~XILINX_AXI_SPI_CONTROL_LOOP; + } + + regs->control = control; + xilinx_axi_spi_set_chipsel(bus, cs); + + base->mode = mode; + base->cs = cs; + +} + +static void +xilinx_axi_spi_next_msg(xilinx_axi_spi_bus *bus, volatile xilinx_axi_spi *regs) +{ + uint32_t control = regs->control; + control |= XILINX_AXI_SPI_CONTROL_MST_TRANS_INHIBIT + | XILINX_AXI_SPI_CONTROL_RX_FIFO_RESET + | XILINX_AXI_SPI_CONTROL_TX_FIFO_RESET; + regs->control = control; + + if (bus->msg_todo > 0) { + const spi_ioc_transfer *msg; + spi_bus *base = &bus->base; + + msg = bus->msg; + + if ( + msg->mode != base->mode + || msg->cs != base->cs + ) { + xilinx_axi_spi_config( + bus, + regs, + msg->mode, + msg->cs + ); + } + + if ((msg->mode & SPI_NO_CS) != 0) { + xilinx_axi_spi_set_chipsel(bus, XILINX_AXI_SPI_CS_NONE); + } + + bus->todo = msg->len; + bus->rx_buf = msg->rx_buf; + bus->tx_buf = msg->tx_buf; + xilinx_axi_spi_push(bus, regs); + + xilinx_axi_spi_disable_interrupts(regs); + if ( + bus->todo < bus->fifo_size + || bus->fifo_size == 1) { + /* if the msg fits into the FIFO, wait for empty TX buffer */ + regs->irqenable = XILINX_AXI_SPI_IRQ_TXEMPTY; + + } else { + /* if the msg does not fit, refill tx_buf when the tx FIFO is half empty */ + regs->irqenable = XILINX_AXI_SPI_IRQ_TXHALF; + } + regs->globalirq = XILINX_AXI_SPI_GLOBAL_IRQ_ENABLE; + control = regs->control; + control |= XILINX_AXI_SPI_CONTROL_SPIEN; + control &= ~XILINX_AXI_SPI_CONTROL_MST_TRANS_INHIBIT; + regs->control = control; + } else { + xilinx_axi_spi_done(bus); + } +} + +static void xilinx_axi_spi_interrupt(void *arg) +{ + xilinx_axi_spi_bus *bus; + volatile xilinx_axi_spi *regs; + + bus = arg; + regs = bus->regs; + + /* Clear interrupt flag. It's safe, since only one IRQ active at a time */ + regs->irqstatus = regs->irqenable; + + while (xilinx_axi_spi_rx_fifo_not_empty(regs)) { + uint32_t val = regs->rxdata; + if (bus->rx_buf != NULL) { + *bus->rx_buf = (uint8_t)val; + ++bus->rx_buf; + } + --bus->in_transfer; + } + + if (bus->todo > 0) { + xilinx_axi_spi_push(bus, regs); + } else if (bus->in_transfer > 0) { + /* Wait until all bytes have been transfered */ + regs->irqenable = XILINX_AXI_SPI_IRQ_TXEMPTY; + } else { + --bus->msg_todo; + ++bus->msg; + xilinx_axi_spi_next_msg(bus, regs); + } +} + +static int xilinx_axi_spi_check_messages( + xilinx_axi_spi_bus *bus, + const spi_ioc_transfer *msg, + uint32_t size) +{ + while(size > 0) { + if (msg->bits_per_word != 8) { + return -EINVAL; + } + if ((msg->mode & + ~(SPI_CPHA | SPI_CPOL | SPI_NO_CS)) != 0) { + return -EINVAL; + } + if ((msg->mode & SPI_NO_CS) == 0 && + (msg->cs > bus->num_cs)) { + return -EINVAL; + } + + ++msg; + --size; + } + + return 0; +} + +static int xilinx_axi_spi_transfer( + spi_bus *base, + const spi_ioc_transfer *msgs, + uint32_t n +) +{ + xilinx_axi_spi_bus *bus; + int rv; + + bus = (xilinx_axi_spi_bus *) base; + + rv = xilinx_axi_spi_check_messages(bus, msgs, n); + + if (rv == 0) { + bus->msg_todo = n; + bus->msg = &msgs[0]; + bus->task_id = rtems_task_self(); + + xilinx_axi_spi_next_msg(bus, bus->regs); + rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + xilinx_axi_spi_set_chipsel(bus, XILINX_AXI_SPI_CS_NONE); + } + return rv; +} + +static void xilinx_axi_spi_destroy(spi_bus *base) +{ + xilinx_axi_spi_bus *bus; + + bus = (xilinx_axi_spi_bus *) base; + rtems_interrupt_handler_remove(bus->irq, xilinx_axi_spi_interrupt, bus); + spi_bus_destroy_and_free(&bus->base); +} + +static int xilinx_axi_spi_setup(spi_bus *base) +{ + xilinx_axi_spi_bus *bus; + uint32_t mode = base->mode; + + bus = (xilinx_axi_spi_bus *) base; + + if (bus->base.bits_per_word > 8) { + return -EINVAL; + } + + /* SPI_CS_HIGH not supported */ + if (mode & SPI_CS_HIGH) { + return -EINVAL; + } + + xilinx_axi_spi_config( + bus, + bus->regs, + base->mode, + base->cs + ); + return 0; +} + +int spi_bus_register_xilinx_axi( + const char *bus_path, + uintptr_t register_base, + uint32_t fifo_size, + uint32_t num_cs, + rtems_vector_number irq) +{ + xilinx_axi_spi_bus *bus; + spi_bus *base; + int sc; + + if (fifo_size != 0 && fifo_size != 16 && fifo_size != 256) { + return -1; + } + + if (num_cs > 32) { + return -1; + } + + bus = (xilinx_axi_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus)); + if (bus == NULL){ + return -1; + } + + base = &bus->base; + bus->regs = (volatile xilinx_axi_spi *) register_base; + bus->irq = irq; + bus->num_cs = num_cs; + + /* For operation without FIFO set fifo_size to 1 + * so that comparison operators work + */ + if (fifo_size == 0) { + bus->fifo_size = 1; + } else { + bus->fifo_size = fifo_size; + } + + base->cs = SPI_NO_CS; + + xilinx_axi_spi_reset(bus); + xilinx_axi_spi_config( + bus, + bus->regs, + base->mode, + base->cs + ); + + + sc = rtems_interrupt_handler_install( + bus->irq, + "XSPI", + RTEMS_INTERRUPT_UNIQUE, + xilinx_axi_spi_interrupt, + bus + ); + if (sc != RTEMS_SUCCESSFUL) { + (*bus->base.destroy)(&bus->base); + rtems_set_errno_and_return_minus_one(EAGAIN); + } + + bus->base.transfer = xilinx_axi_spi_transfer; + bus->base.destroy = xilinx_axi_spi_destroy; + bus->base.setup = xilinx_axi_spi_setup; + + return spi_bus_register(&bus->base, bus_path); +} diff --git a/bsps/headers.am b/bsps/headers.am index 5af7e43b4a..2c9b31df2b 100644 --- a/bsps/headers.am +++ b/bsps/headers.am @@ -34,6 +34,7 @@ include_grlib_HEADERS += ../../bsps/include/grlib/apbuart_termios.h include_grlib_HEADERS += ../../bsps/include/grlib/b1553brm.h include_grlib_HEADERS += ../../bsps/include/grlib/b1553rt.h include_grlib_HEADERS += ../../bsps/include/grlib/bspcommon.h +include_grlib_HEADERS += ../../bsps/include/grlib/canbtrs.h include_grlib_HEADERS += ../../bsps/include/grlib/canmux.h include_grlib_HEADERS += ../../bsps/include/grlib/cons.h include_grlib_HEADERS += ../../bsps/include/grlib/debug_defs.h diff --git a/bsps/i386/pc386/clock/ckinit.c b/bsps/i386/pc386/clock/ckinit.c index 09afe73cde..2df1818dd3 100644 --- a/bsps/i386/pc386/clock/ckinit.c +++ b/bsps/i386/pc386/clock/ckinit.c @@ -104,48 +104,60 @@ static uint32_t pc386_get_timecount_i8254(struct timecounter *tc) /* * Calibrate CPU cycles per tick. Interrupts should be disabled. + * Will also set the PIT, so call this before registering the + * periodic timer for rtems tick generation */ static void calibrate_tsc(void) { uint64_t begin_time; - uint8_t then_lsb, then_msb, now_lsb, now_msb; - uint32_t i; - - /* - * We just reset the timer, so we know we're at the beginning of a tick. - */ - - /* - * Count cycles. Watching the timer introduces a several microsecond - * uncertaintity, so let it cook for a while and divide by the number of - * ticks actually executed. - */ + uint8_t lsb, msb; + uint32_t max_timer_value; + uint32_t last_tick, cur_tick; + int32_t diff, remaining; + + /* Set the timer to free running mode */ + outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_INTTC); + /* Reset the 16 timer reload value, first LSB, then MSB */ + outport_byte(TIMER_CNTR0, 0); + outport_byte(TIMER_CNTR0, 0); + /* We use the full 16 bit */ + max_timer_value = 0xffff; + /* Calibrate for 1s, i.e. TIMER_TICK PIT ticks */ + remaining = TIMER_TICK; begin_time = rdtsc(); - - for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick; - i != 0; --i ) { - /* We know we've just completed a tick when timer goes from low to high */ - then_lsb = then_msb = 0xff; - do { - READ_8254(now_lsb, now_msb); - if ((then_msb < now_msb) || - ((then_msb == now_msb) && (then_lsb < now_lsb))) - break; - then_lsb = now_lsb; - then_msb = now_msb; - } while (1); + READ_8254(lsb, msb); + last_tick = (msb << 8) | lsb; + while(remaining > 0) { + READ_8254(lsb, msb); + cur_tick = (msb << 8) | lsb; + /* PIT counts down, so subtract cur from last */ + diff = last_tick - cur_tick; + last_tick = cur_tick; + if (diff < 0) { + diff += max_timer_value; + } + remaining -= diff; } pc586_tsc_frequency = rdtsc() - begin_time; #if 0 - printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_frequency / 1000000)); + printk( "CPU clock at %u Hz\n", (uint32_t)(pc586_tsc_frequency )); #endif } static void clockOn(void) { + + /* + * First calibrate the TSC. Do this every time we + * turn the clock on in case the CPU clock speed has changed. + */ + if ( x86_has_tsc() ) { + calibrate_tsc(); + } + rtems_interrupt_lock_context lock_context; pc386_isrs_per_tick = 1; pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick(); @@ -171,13 +183,6 @@ static void clockOn(void) rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER ); - - /* - * Now calibrate cycles per tick. Do this every time we - * turn the clock on in case the CPU clock speed has changed. - */ - if ( x86_has_tsc() ) - calibrate_tsc(); } bool Clock_isr_enabled = false; diff --git a/bsps/include/bsp/irq-default.h b/bsps/include/bsp/irq-default.h index a94e045e0d..4d7beb189b 100644 --- a/bsps/include/bsp/irq-default.h +++ b/bsps/include/bsp/irq-default.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * @@ -5,9 +7,7 @@ */ /* - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (C) 2019 embedded brains GmbH + * Copyright (C) 2019 embedded brains GmbH (http://www.embedded-brains.de) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/bsps/include/bsp/irq-generic.h b/bsps/include/bsp/irq-generic.h index 31835d07ba..4135aa518c 100644 --- a/bsps/include/bsp/irq-generic.h +++ b/bsps/include/bsp/irq-generic.h @@ -1,27 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt support API. + * @brief This header file provides interfaces of the generic interrupt + * controller support. */ /* - * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette. - * - * Copyright (c) 2008, 2017 embedded brains GmbH. + * Copyright (C) 2016 Chris Johns <chrisj@rtems.org> * - * embedded brains GmbH - * Dornierstr. 4 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Copyright (C) 2008, 2017 embedded brains GmbH (http://www.embedded-brains.de) * - * Copyright (c) 2016 Chris Johns <chrisj@rtems.org> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The API is based on concepts of Pavel Pisa, Till Straumann and Eric Valette. */ #ifndef LIBBSP_SHARED_IRQ_GENERIC_H diff --git a/bsps/include/bsp/irq-info.h b/bsps/include/bsp/irq-info.h index ea6d629e76..25f05a9f69 100644 --- a/bsps/include/bsp/irq-info.h +++ b/bsps/include/bsp/irq-info.h @@ -1,22 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt information API. + * @brief This header file provides interfaces of the generic interrupt + * controller support for the RTEMS Shell. */ /* - * Copyright (c) 2008, 2009 - * embedded brains GmbH - * Obere Lagerstr. 30 - * D-82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Copyright (C) 2008, 2009 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBBSP_SHARED_IRQ_INFO_H diff --git a/bsps/include/grlib/ahbstat.h b/bsps/include/grlib/ahbstat.h index 71e2330f69..0baaad0732 100644 --- a/bsps/include/grlib/ahbstat.h +++ b/bsps/include/grlib/ahbstat.h @@ -21,6 +21,8 @@ extern "C" { struct ahbstat_regs { volatile uint32_t status; volatile uint32_t failing; + volatile uint32_t status2; + volatile uint32_t failing2; }; /* AHB fail interrupt callback to user. This function is declared weak so that diff --git a/bsps/include/grlib/ambapp_ids.h b/bsps/include/grlib/ambapp_ids.h index c0c3547e94..629b4fb37a 100644 --- a/bsps/include/grlib/ambapp_ids.h +++ b/bsps/include/grlib/ambapp_ids.h @@ -226,6 +226,25 @@ #define GAISLER_SPIMASTER 0x0a6 #define GAISLER_SPISLAVE 0x0a7 #define GAISLER_GRSRIO 0x0a8 +#define GAISLER_AHBLM2AHB 0x0a9 +#define GAISLER_AHBS2NOC 0x0aa +#define GAISLER_TCAU 0x0ab +#define GAISLER_GRTMDYNVCID 0x0ac +#define GAISLER_RNOCIRQPROP 0x0ad +#define GAISLER_FTADDR 0x0ae +#define GAISLER_ATG 0x0b0 +#define GAISLER_DFITRACE 0x0b1 +#define GAISLER_SELFTEST 0x0b2 +#define GAISLER_DFIERRINJ 0x0b3 +#define GAISLER_DFICHECK 0x0b4 +#define GAISLER_GRCANFD 0x0b5 +#define GAISLER_NIM 0x0b6 +#define GAISLER_BANDGAP 0x1f0 +#define GAISLER_MPROT 0x1f1 +#define GAISLER_ADC 0x1f2 +#define GAISLER_BO 0x1f3 +#define GAISLER_DAC 0x1f4 +#define GAISLER_PLL 0x1f6 #define GAISLER_PIPEWRAPPER 0xffa #define GAISLER_L2TIME 0xffd /* internal device: leon2 timer */ diff --git a/bsps/include/grlib/canbtrs.h b/bsps/include/grlib/canbtrs.h new file mode 100644 index 0000000000..7369136931 --- /dev/null +++ b/bsps/include/grlib/canbtrs.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * @ingroup can + * @brief Common CAN baud-rate routines for OCCAN/GRCAN/GRCANFD controllers + */ + +/* + * Copyright (C) 2019, 2020 Cobham Gaisler AB + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __GRLIB_CANBTRS_H__ +#define __GRLIB_CANBTRS_H__ + +/** + * @defgroup can CAN + * + * @ingroup RTEMSBSPsSharedGRLIB + * + * @brief CAN routines shared between OCCAN, GRCAN and GRCANFD controllers + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* CAN Baud-rate parameters, range of valid parameter values */ +struct grlib_canbtrs_ranges { + unsigned int max_scaler; + char has_bpr; + unsigned char divfactor; + unsigned char min_tseg1; + unsigned char max_tseg1; + unsigned char min_tseg2; + unsigned char max_tseg2; +}; + +struct grlib_canbtrs_timing { + unsigned char scaler; + unsigned char ps1; + unsigned char ps2; + unsigned char rsj; + unsigned char bpr; +}; + +/* @brief Calculate CAN baud-rate generation parameters from requested baud-rate + * + * @param baud The CAN baud rate requested + * @param core_hz Input clock [Hz] to CAN core + * @param sampl_pt CAN sample point in %, 80 means 80% + * @param br CAN Baud-rate parameters limitations + * @param[out] timing result is placed here + * + * @retval 0 Baud-rate parameters sucessfully calculated + * @retval negative Failure to generate parameters with less than 5% error + * margin from requested baud-rate + */ +int grlib_canbtrs_calc_timing( + unsigned int baud, + unsigned int core_hz, + unsigned int sampl_pt, + struct grlib_canbtrs_ranges *br, + struct grlib_canbtrs_timing *timing + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsps/include/grlib/grcan.h b/bsps/include/grlib/grcan.h index b1bc2ba758..a956bef124 100644 --- a/bsps/include/grlib/grcan.h +++ b/bsps/include/grlib/grcan.h @@ -1,6 +1,7 @@ /** * @file * @ingroup can + * @brief Driver API for the GRLIB GRCAN and GRCANFD controllers */ /* @@ -25,6 +26,8 @@ * @{ */ +#include <stdint.h> + #ifdef __cplusplus extern "C" { #endif @@ -37,7 +40,13 @@ struct grcan_regs { volatile unsigned int smask; /* 0x18 */ volatile unsigned int scode; /* 0x1C */ - volatile unsigned int dummy1[56]; /* 0x20-0xFC */ + volatile unsigned int dummy1[8]; /* 0x20-0x3C */ + + volatile unsigned int nbtr; /* 0x40 */ + volatile unsigned int fdbtr; /* 0x44 */ + volatile unsigned int tdelay; /* 0x48 */ + + volatile unsigned int dummy1b[45]; /* 0x4C-0xFC */ volatile unsigned int pimsr; /* 0x100 */ volatile unsigned int pimr; /* 0x104 */ @@ -82,10 +91,18 @@ struct grcan_timing { unsigned char scaler; unsigned char ps1; unsigned char ps2; - unsigned int rsj; + unsigned char rsj; unsigned char bpr; }; +struct grcanfd_timing { + unsigned char scaler; + unsigned char ps1; + unsigned char ps2; + unsigned char sjw; + unsigned char resv_zero; +}; + struct grcan_selection { int selection; int enable0; @@ -97,16 +114,34 @@ struct grcan_filter { unsigned long long code; }; +#define GRCAN_FDOPT_NOM 0 +#define GRCAN_FDOPT_FDBTR 0x01 +#define GRCAN_FDOPT_FDFRM 0x02 +#define GRCAN_FDMASK (GRCAN_FDOPT_FDBTR | GRCAN_FDOPT_FDFRM) + /* CAN MESSAGE */ typedef struct { char extended; /* 1= Extended Frame (29-bit id), 0= STD Frame (11-bit id) */ char rtr; /* RTR - Remote Transmission Request */ - char unused; /* unused */ + char unused; /* Must be 0 to select classic CAN frame */ unsigned char len; unsigned char data[8]; unsigned int id; } CANMsg; +/* CAN-FD MESSAGE */ +typedef struct { + uint8_t extended; /* 1= Extended Frame (29-bit id), 0= STD Frame (11-bit id) */ + uint8_t rtr; /* RTR - Remote Transmission Request */ + uint8_t fdopts; /* Bit1: 1=Switch bit rate. bit2: 1=FD frame. */ + uint8_t len; /* 0-8, 12, 16, 20, 24, 32, 48 or 64 bytes */ + uint32_t id; + union { + uint64_t dwords[8]; /* up to 64 bytes if FD=1 and len>8 */ + uint8_t bytes[64]; /* up to 64 bytes if FD=1 and len>8 */ + } data; +} CANFDMsg; + enum { GRCAN_RET_OK = 0, GRCAN_RET_INVARG = -1, @@ -166,6 +201,26 @@ enum grcan_state { #define GRCAN_RXCTRL_ENABLE 1 #define GRCAN_RXCTRL_ONGOING 1 +#define GRCANFD_NBTR_SCALER 0x00ff0000 +#define GRCANFD_NBTR_PS1 0x0000fc00 +#define GRCANFD_NBTR_PS2 0x000003e0 +#define GRCANFD_NBTR_SJW 0x0000001f + +#define GRCANFD_NBTR_SCALER_BIT 16 +#define GRCANFD_NBTR_PS1_BIT 10 +#define GRCANFD_NBTR_PS2_BIT 5 +#define GRCANFD_NBTR_SJW_BIT 0 + +#define GRCANFD_FDBTR_SCALER 0x00ff0000 +#define GRCANFD_FDBTR_PS1 0x00003c00 +#define GRCANFD_FDBTR_PS2 0x000001e0 +#define GRCANFD_FDBTR_SJW 0x0000000f + +#define GRCANFD_FDBTR_SCALER_BIT 16 +#define GRCANFD_FDBTR_PS1_BIT 10 +#define GRCANFD_FDBTR_PS2_BIT 5 +#define GRCANFD_FDBTR_SJW_BIT 0 + /* Relative offset of IRQ sources to AMBA Plug&Play */ #define GRCAN_IRQ_IRQ 0 #define GRCAN_IRQ_TXSYNC 1 @@ -230,6 +285,15 @@ extern void *grcan_open_by_name(char *name, int *dev_no); extern int grcan_close(void *d); /* + * Returns if CAN hardware device is CANFD capable. + * + * dev_no: Device handle + * return: 0=Not FD capable, 1=FD capable. + * function returns NULL if device can not be opened. + */ +extern int grcan_canfd_capable(void *d); + +/* * Receive CAN messages * * Multiple CAN messages can be received in one call. @@ -255,6 +319,31 @@ extern int grcan_read( ); /* + * Receive CAN messages (only GRCANFD) + * + * Multiple CAN messages can be received in one call. + * + * d: Device handle + * msg: Pointer to receive messages + * count: Number of CAN messages to receive + * + * return: + * >=0: Number of CAN messages received. This can be + * less than the count parameter. + * GRCAN_RET_INVARG: count parameter less than one or NULL msg. + * GRCAN_RET_NOTSTARTED: Device not in started mode + * GRCAN_RET_TIMEOUT: Timeout in non-blocking mode + * GRCAN_RET_BUSOFF: A read was interrupted by a bus-off error. + * Device has left started mode. + * GRCAN_RET_AHBERR: Similar to BUSOFF, but was caused by AHB Error. + */ +extern int grcanfd_read( + void *d, + CANFDMsg *msg, + size_t count +); + +/* * Transmit CAN messages * * Multiple CAN messages can be transmit in one call. @@ -280,6 +369,31 @@ extern int grcan_write( ); /* + * Transmit CAN-FD complient messages (only GRCANFD) + * + * Multiple CAN messages can be transmit in one call. + * + * d: Device handle + * msg: Pointer to messages to transmit + * count: Number of CAN messages to transmit + * + * return: + * >=0: Number of CAN messages transmitted. This can be + * less than the count parameter. + * GRCAN_RET_INVARG: count parameter less than one. + * GRCAN_RET_NOTSTARTED: Device not in started mode + * GRCAN_RET_TIMEOUT: Timeout in non-blocking mode + * GRCAN_RET_BUSOFF: A write was interrupted by a Bus-off error. + * Device has left started mode + * GRCAN_RET_AHBERR: Similar to BUSOFF, but was caused by AHB Error. + */ +extern int grcanfd_write( + void *d, + CANFDMsg *msg, + size_t ucount +); + +/* * Returns current GRCAN software state * * If STATE_BUSOFF or STATE_AHBERR is returned then the function grcan_stop() @@ -320,6 +434,16 @@ extern int grcan_set_speed(void *d, unsigned int hz); /* Set baudrate by specifying the timing registers manually */ extern int grcan_set_btrs(void *d, const struct grcan_timing *timing); +/* Set the Nominal and FD baudrate by using driver's baud rate timing + * calculation routines + */ +extern int grcanfd_set_speed(void *d, unsigned int nomhz, unsigned int fdhz); +/* Set Nominal and FD baudrate by specifying the timing registers manually*/ +extern int grcanfd_set_btrs( + void *d, + const struct grcanfd_timing *nominal, + const struct grcanfd_timing *fd); + /* Functions can be called whenever */ /* Enable/disable Blocking on reception (until at least one message has been received) */ int grcan_set_rxblock(void* d, int block); diff --git a/bsps/include/grlib/greth.h b/bsps/include/grlib/greth.h index 1c42c99ddc..e7970a79f7 100644 --- a/bsps/include/grlib/greth.h +++ b/bsps/include/grlib/greth.h @@ -100,6 +100,7 @@ typedef struct _greth_regs { #define GRETH_STATUS_TXIRQ 0x00000008 /* Transmit Error IRQ */ #define GRETH_STATUS_RXAHBERR 0x00000010 /* Receiver AHB Error */ #define GRETH_STATUS_TXAHBERR 0x00000020 /* Transmitter AHB Error */ +#define GRETH_STATUS_NRD 0x0f000000 /* Number of descriptors */ /* MDIO Control */ #define GRETH_MDIO_WRITE 0x00000001 /* MDIO Write */ diff --git a/bsps/include/grlib/grlib_impl.h b/bsps/include/grlib/grlib_impl.h index e795e7f844..3bff2af0da 100644 --- a/bsps/include/grlib/grlib_impl.h +++ b/bsps/include/grlib/grlib_impl.h @@ -122,6 +122,16 @@ RTEMS_INLINE_ROUTINE unsigned int grlib_read_uncached32(unsigned int address) return tmp; } +RTEMS_INLINE_ROUTINE uint64_t grlib_read_uncached64(uint64_t *address) +{ + uint64_t tmp; + __asm__ (" ldda [%1]1, %0 " + : "=r"(tmp) + : "r"(address) + ); + return tmp; +} + #define GRLIB_DMA_IS_CACHE_COHERENT CPU_SPARC_HAS_SNOOPING #else diff --git a/bsps/include/grlib/grspw_pkt.h b/bsps/include/grlib/grspw_pkt.h index 595625b838..ede60b72a8 100644 --- a/bsps/include/grlib/grspw_pkt.h +++ b/bsps/include/grlib/grspw_pkt.h @@ -150,8 +150,8 @@ typedef enum { SPW_LS_ERRRST = 0, SPW_LS_ERRWAIT = 1, SPW_LS_READY = 2, - SPW_LS_CONNECTING = 3, - SPW_LS_STARTED = 4, + SPW_LS_STARTED = 3, + SPW_LS_CONNECTING = 4, SPW_LS_RUN = 5 } spw_link_state_t; diff --git a/bsps/include/grlib/grspw_router.h b/bsps/include/grlib/grspw_router.h index 2fab8d5f6c..8547f19de3 100644 --- a/bsps/include/grlib/grspw_router.h +++ b/bsps/include/grlib/grspw_router.h @@ -343,6 +343,7 @@ extern int router_port_enable(void *d, int port); extern int router_port_disable(void *d, int port); extern int router_port_link_stop(void *d, int port); extern int router_port_link_start(void *d, int port); +extern int router_port_link_div(void *d, int port, int rundiv); extern int router_port_link_receive_spill(void *d, int port); extern int router_port_link_transmit_reset(void *d, int port); diff --git a/bsps/include/grlib/occan.h b/bsps/include/grlib/occan.h index 0bf34dee48..1112a3e8dc 100644 --- a/bsps/include/grlib/occan.h +++ b/bsps/include/grlib/occan.h @@ -1,7 +1,7 @@ /** * @file * @ingroup can - * @brief Gaisler wrapper to OpenCores CAN - driver interface + * @brief Driver API for GRLIB wrapper to OpenCores CAN */ /* diff --git a/bsps/m68k/uC5282/start/linkcmds b/bsps/m68k/uC5282/start/linkcmds index da97472f1b..af9a69b802 100644 --- a/bsps/m68k/uC5282/start/linkcmds +++ b/bsps/m68k/uC5282/start/linkcmds @@ -91,13 +91,13 @@ SECTIONS * crtn.o are in. */ PROVIDE (_init = .); - *crti.o(.init) - *(.init) - *crtn.o(.init) + KEEP (*crti.o(.init)) + KEEP (*(.init)) + KEEP (*crtn.o(.init)) PROVIDE (_fini = .); - *crti.o(.fini) - *(.fini) - *crtn.o(.fini) + KEEP (*crti.o(.fini)) + KEEP (*(.fini)) + KEEP (*crtn.o(.fini)) /* * Special FreeBSD sysctl sections. @@ -122,18 +122,22 @@ SECTIONS * crtend.o. The same comments apply to it. */ . = ALIGN (16); - *crtbegin.o(.ctors) - *(.ctors) - *crtend.o(.ctors) - *crtbegin.o(.dtors) - *(.dtors) - *crtend.o(.dtors) + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) /* * Exception frame info */ . = ALIGN (16); - *(.eh_frame) + KEEP (*(.eh_frame)) /* * Read-only data @@ -141,7 +145,7 @@ SECTIONS . = ALIGN (16); _rodata_start = . ; *(.rodata*) - KEEP (*(SORT(.rtemsroset.*))) + KEEP (*(SORT(.rtemsroset.*))) *(.gnu.linkonce.r*) . = ALIGN (16); @@ -179,7 +183,7 @@ SECTIONS KEEP (*(SORT(.rtemsrwset.*))) *(.gnu.linkonce.d*) *(.gcc_except_table*) - *(.jcr) + KEEP (*(.jcr)) . = ALIGN (16); PROVIDE (_edata = .); PROVIDE (_copy_end = .); diff --git a/bsps/powerpc/beatnik/net/if_mve/mve_smallbuf_tst.c b/bsps/powerpc/beatnik/net/if_mve/mve_smallbuf_tst.c deleted file mode 100644 index 721ade30d1..0000000000 --- a/bsps/powerpc/beatnik/net/if_mve/mve_smallbuf_tst.c +++ /dev/null @@ -1,145 +0,0 @@ -#include <rtems.h> -#include <bsp.h> -#include <bsp/if_mve_pub.h> -#include <stdlib.h> -#include <stdio.h> - -/* Demo for the mv64360 ethernet quirk: - * - * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ - * $$ buffer segments < 8 bytes must be aligned $$ - * $$ to 8 bytes but larger segments are not $$ - * $$ sensitive to alignment. $$ - * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ - * - * How to use: - * - * Init MVE driver on (unused) unit 2: - * - * mve = mvtst_init(2) - * - * data = { 1,2,3,4,5,6,7,8,9,0xa,0xb, ... } - * - * Alloc 2-element mbuf chain (1st holds an - * ethernet header which is > 8bytes so we can't - * test this with only 1 mbuf. The 2nd mbuf holds - * a small fragment of data). - * - * mb = mvtst_getbuf(mve) - * - * Copy data into aligned area inside 2nd mbuf, - * (so that you can see if the chip starts using - * the aligned area rather than the unaligned - * buffer pointer). Point mbuf's data pointer - * at 'off'set from the aligned area: - * - * mvtst_putbuf(mb, data, len, offset) - * - * Send chain off: - * - * BSP_mve_send_buf(mve, mb, 0, 0) - * - * Watch raw data: - * - * tcpdump -XX -vv -s0 ether host <my-ether-addr> - * - * E.g, if offset = 1, len = 2 then we would like - * to see - * - * GOOD: - * < 14 header bytes > 0x02, 0x03 - - * but if the chip starts DMA at aligned address - * we see instead - * BAD: - * < 14 header bytes > 0x01, 0x02 - */ - -static inline void *rmalloc(size_t l) { return malloc(l); } -static inline void rfree(void *p) { return free(p); } - -#define _KERNEL -#include <sys/param.h> -#include <sys/mbuf.h> - -static void -cleanup_buf(void *u_b, void *closure, int error) -{ -rtems_bsdnet_semaphore_obtain(); - m_freem((struct mbuf*)u_b); -rtems_bsdnet_semaphore_release(); -} - -struct mbuf *mvtst_getbuf(struct mveth_private *mp) -{ -struct mbuf *m,*n; - - if ( !mp ) { - printf("need driver ptr arg\n"); - return 0; - } -rtems_bsdnet_semaphore_obtain(); - MGETHDR(m, M_DONTWAIT, MT_DATA); - MGET(n, M_DONTWAIT, MT_DATA); - m->m_next = n; -rtems_bsdnet_semaphore_release(); - /* Ethernet header */ - memset( mtod(m, unsigned char*), 0xff, 6); - BSP_mve_read_eaddr(mp, mtod(m, unsigned char*) + 6); - /* Arbitrary; setting to IP but we don't bother - * to setup a real IP header. We just watch the - * raw packet contents... - */ - mtod(m, unsigned char*)[12] = 0x08; - mtod(m, unsigned char*)[13] = 0x00; - m->m_pkthdr.len = m->m_len = 14; - n->m_len = 0; - return m; -} - -int -mvtst_putbuf(struct mbuf *m, void *data, int len, int off) -{ -int i; - if ( m ) { - m->m_pkthdr.len += len; - if ( ( m= m->m_next ) ) { - m->m_len = len; - memcpy(mtod(m, void*), data, 32); - m->m_data += off; - printf("m.dat: 0x%08x, m.data: 0x%08x\n", m->m_dat, m->m_data); - for ( i=0; i< 16; i++ ) { - printf(" %02x,",mtod(m, unsigned char*)[i]); - } - printf("\n"); - } - } - - return 0; -} - -static void *alloc_rxbuf(int *p_size, unsigned long *paddr) -{ - return *(void**)paddr = rmalloc((*p_size = 1800)); -} - -static void consume_buf(void *buf, void *closure, int len) -{ - rfree(buf); -} - -void * -mvtst_init(int unit) -{ -struct mveth_private *mp; - mp = BSP_mve_setup( - unit, 0, - cleanup_buf, 0, - alloc_rxbuf, - consume_buf, 0, - 10, 10, - 0); - if ( mp ) - BSP_mve_init_hw(mp, 0, 0); - return mp; -} diff --git a/bsps/powerpc/beatnik/net/if_mve/testing.c b/bsps/powerpc/beatnik/net/if_mve/testing.c deleted file mode 100644 index a1233bdb0b..0000000000 --- a/bsps/powerpc/beatnik/net/if_mve/testing.c +++ /dev/null @@ -1,324 +0,0 @@ -#ifndef KERNEL -#define KERNEL -#endif - -#include <rtems.h> -#include <rtems/rtems_bsdnet_internal.h> -#include <bsp.h> -#include <sys/param.h> -#include <sys/mbuf.h> - -#include "mv64340_eth_ll.h" - -#include <string.h> -#include <assert.h> - -#include <netinet/in.h> -#include <stdio.h> - -#define RX_SPACING 1 -#define TX_SPACING 1 - -#define RX_RING_SIZE (MV64340_RX_QUEUE_SIZE*RX_SPACING) -#define TX_RING_SIZE (MV64340_TX_QUEUE_SIZE*TX_SPACING) - - -struct eth_rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(32))); -struct eth_rx_desc rx_ring[RX_RING_SIZE] = {{0},}; - -struct eth_tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(32))); -struct eth_tx_desc tx_ring[TX_RING_SIZE] = {{0},}; - -/* packet buffers */ -char rx_buf[MV64340_RX_QUEUE_SIZE][2048] __attribute__((aligned(8))); -char rx_buf[MV64340_RX_QUEUE_SIZE][2048]; - -char tx_buf[MV64340_RX_QUEUE_SIZE][2048] __attribute__((aligned(8))); -char tx_buf[MV64340_RX_QUEUE_SIZE][2048]; - -char BcHeader[22] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* dst */ - 0x00, 0x01, 0xaf, 0x13, 0xb5, 0x3e, /* src */ - 00, 00, /* len */ - 0xAA, /* dsap */ - 0xAA, /* ssap */ - 0x03, /* ctrl */ - 0x08, 0x00, 0x56, /* snap_org [stanford] */ - 0x80, 0x5b, /* snap_type (stanford kernel) */ -}; - -struct mv64340_private mveth = { - port_num: 0, - port_mac_addr: {0x00,0x01,0xAF,0x13,0xB5,0x3C}, - /* port_config .. tx_resource_err are set by port_init */ - 0 -}; - -struct pkt_info p0,p1; - -static inline void rx_stopq(int port) -{ - MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port), 0x0000ff00); -} - -static inline void tx_stopq(int port) -{ - MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port), 0x0000ff00); -} - -#define MV64360_ENET2MEM_SNOOP_NONE 0x0000 -#define MV64360_ENET2MEM_SNOOP_WT 0x1000 -#define MV64360_ENET2MEM_SNOOP_WB 0x2000 - -#if 0 -int -mveth_init(struct mv64340_private *mp) -{ -int i; - mp->p_rx_desc_area = rx_ring; - mp->p_tx_desc_area = tx_ring; - - rx_stopq(mp->port_num); - tx_stopq(mp->port_num); - - /* MotLoad has cache snooping disabled on the ENET2MEM windows. - * Some comments in (linux) indicate that there are errata - * which cause problems which is a real bummer. - * We try it anyways... - */ - { - unsigned long disbl, bar; - disbl = MV_READ(MV64340_ETH_BASE_ADDR_ENABLE_REG); - /* disable all 6 windows */ - MV_WRITE(MV64340_ETH_BASE_ADDR_ENABLE_REG, 0x3f); - /* set WB snooping */ - for ( i=0; i<6*8; i+=8 ) { - if ( (bar = MV_READ(MV64340_ETH_BAR_0 + i)) && MV_READ(MV64340_ETH_SIZE_REG_0 + i) ) { - MV_WRITE(MV64340_ETH_BAR_0 + i, bar | MV64360_ENET2MEM_SNOOP_WB); - /* read back to flush fifo [linux comment] */ - (void)MV_READ(MV64340_ETH_BAR_0 + i); - } - } - /* restore/re-enable */ - MV_WRITE(MV64340_ETH_BASE_ADDR_ENABLE_REG, disbl); - } - - eth_port_init(mp); - - sleep(1); - - mveth_init_tx_desc_ring(mp); - mveth_init_rx_desc_ring(mp); -#if 0 - for ( i = 0; i<MV64340_RX_QUEUE_SIZE; i++ ) { - p0.byte_cnt = sizeof(rx_buf[0]); - p0.buf_ptr = (dma_addr_t)rx_buf[i]; - p0.return_info = (void*)i; - /* other fields are not used by ll driver */ - assert ( ETH_OK == eth_rx_return_buff(mp,&p0) ); - } - memset(&p0, 0, sizeof(p0)); -#endif - - return eth_port_start(mp); -} -#endif - -void -mveth_stop(struct mv64340_private *mp) -{ -extern void mveth_stop_hw(); - rtems_bsdnet_semaphore_obtain(); - mveth_stop_hw(mp); - rtems_bsdnet_semaphore_release(); -} - -extern int mveth_send_mbuf(); -extern int mveth_swipe_tx(); - -int -mveth_tx(struct mv64340_private *mp, char *data, int len, int nbufs) -{ -int rval = -1,l; -char *p; -struct mbuf *m; -char *emsg = 0; - - rtems_bsdnet_semaphore_obtain(); - MGETHDR(m, M_WAIT, MT_DATA); - if ( !m ) { - emsg="Unable to allocate header\n"; - goto bail; - } - MCLGET(m, M_WAIT); - if ( !(m->m_flags & M_EXT) ) { - m_freem(m); - emsg="Unable to allocate cluster\n"; - goto bail; - } - p = mtod(m, char *); - l = 0; - switch (nbufs) { - case 3: - default: - emsg="nbufs arg must be 1..3\n"; - goto bail; - - case 1: - l += sizeof(BcHeader); - memcpy(p, &BcHeader, sizeof(BcHeader)); - p += sizeof(BcHeader); - - case 2: - memcpy(p,data,len); - l += len; - m->m_len = m->m_pkthdr.len = l; - if ( 2 == nbufs ) { - M_PREPEND(m, sizeof (BcHeader), M_WAIT); - if (!m) { - emsg = "Unable to prepend\n"; - goto bail; - } - p = mtod(m, char*); - memcpy(p,&BcHeader,sizeof(BcHeader)); - l += sizeof(BcHeader); - } - break; - } - *(short*)(mtod(m, char*) + 12) = htons(l-14); - rval = mveth_send_mbuf(mp,m); - -bail: - rtems_bsdnet_semaphore_release(); - if (emsg) - printf(emsg); - -#if 0 - /* - * Add local net header. If no space in first mbuf, - * allocate another. - */ - M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT); - if (m == 0) - senderr(ENOBUFS); - eh = mtod(m, struct ether_header *); - (void)memcpy(&eh->ether_type, &type, - sizeof(eh->ether_type)); - (void)memcpy(eh->ether_dhost, edst, sizeof (edst)); - (void)memcpy(eh->ether_shost, ac->ac_enaddr, - sizeof(eh->ether_shost)); -#endif - return rval; -} - -int -mveth_protected(int (*p)(struct mv64340_private*), struct mv64340_private *mp) -{ -int rval; - rtems_bsdnet_semaphore_obtain(); - rval = p(mp); - rtems_bsdnet_semaphore_release(); - return rval; -} - -int -mveth_rx(struct mv64340_private *mp) -{ -extern int mveth_swipe_rx(); - return mveth_protected(mveth_swipe_rx,mp); -} - -int -mveth_reclaim(struct mv64340_private *mp) -{ -extern int mveth_swipe_tx(); - return mveth_protected(mveth_swipe_tx,mp); -} - - -int preth(FILE *f, char *p) -{ -int i; - for (i=0; i<4; i++) - fprintf(f,"%02X:",p[i]); - fprintf(f,"%02X",p[i]); - return 6; -} - -char *errcode2str(st) -{ -char *rval; - switch(st) { - case ETH_OK: - rval = "OK"; - break; - case ETH_ERROR: - rval = "Fundamental error."; - break; - case ETH_RETRY: - rval = "Could not process request. Try later."; - break; - case ETH_END_OF_JOB: - rval = "Ring has nothing to process."; - break; - case ETH_QUEUE_FULL: - rval = "Ring resource error."; - break; - case ETH_QUEUE_LAST_RESOURCE: - rval = "Ring resources about to exhaust."; - break; - default: - rval = "UNKNOWN"; break; - } - return rval; -} - - -#if 0 -int -mveth_rx(struct mv64340_private *mp) -{ -int st; -struct pkt_info p; - if ( ETH_OK != (st=eth_port_receive(mp, &p)) ) { - fprintf(stderr,"receive: %s\n", errcode2str(st)); - return -1; - } - printf("%i bytes received from ", p.byte_cnt); - preth(stdout,(char*)p.buf_ptr+6); - printf(" (desc. stat: 0x%08x)\n", p.cmd_sts); - - p.byte_cnt = sizeof(rx_buf[0]); - p.buf_ptr -= RX_BUF_OFFSET; - if ( ETH_OK != (st=eth_rx_return_buff(mp,&p) ) ) { - fprintf(stderr,"returning buffer: %s\n", errcode2str(st)); - return -1; - } - return 0; -} -#endif - -int -dring() -{ -int i; -if (1) { -struct eth_rx_desc *pr; -printf("RX:\n"); - for (i=0, pr=rx_ring; i<RX_RING_SIZE; i+=RX_SPACING, pr+=RX_SPACING) { - dcbi(pr); - printf("cnt: 0x%04x, size: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x\n", - pr->byte_cnt, pr->buf_size, pr->cmd_sts, pr->next_desc_ptr, pr->buf_ptr); - } -} -if (1) { -struct eth_tx_desc *pt; -printf("TX:\n"); - for (i=0, pt=tx_ring; i<TX_RING_SIZE; i+=TX_SPACING, pt+=TX_SPACING) { - dcbi(pt); - printf("cnt: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x\n", - pt->byte_cnt, pt->cmd_sts, pt->next_desc_ptr, pt->buf_ptr); - } -} - return 0; -} diff --git a/bsps/powerpc/beatnik/net/porting/if_xxx.modini.c b/bsps/powerpc/beatnik/net/porting/if_xxx.modini.c deleted file mode 100644 index 1abad7dc22..0000000000 --- a/bsps/powerpc/beatnik/net/porting/if_xxx.modini.c +++ /dev/null @@ -1,34 +0,0 @@ -#include <rtems.h> -#include <porting/rtemscompat.h> - -/* CEXP module initialization/finalization */ - -/* Copyright: Till Straumann <strauman@slac.stanford.edu>, 2005; - * License: see LICENSE file. - */ - -void -_cexpModuleInitialize(void *unused) -{ -extern void NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringup)(char *); - METHODSPTR = &METHODS; -/* -#ifdef DEBUG - NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringup)("192.168.2.13/255.255.255.0"); -#endif -*/ -} - -int -_cexpModuleFinalize(void *unused) -{ -#ifdef DEBUG -extern int NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringdown)(); - if (NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringdown)()) - return -1; - METHODSPTR = 0; - return 0; -#else - return -1; -#endif -} diff --git a/bsps/powerpc/include/bsp/irq_supp.h b/bsps/powerpc/include/bsp/irq_supp.h index 65af48c87f..fbb16d6211 100644 --- a/bsps/powerpc/include/bsp/irq_supp.h +++ b/bsps/powerpc/include/bsp/irq_supp.h @@ -50,6 +50,11 @@ extern int BSP_disable_irq_at_pic(const rtems_irq_number irqLine); */ extern int BSP_setup_the_pic(rtems_irq_global_settings* config); +/* + * Set up for the irq-generic.h interface. + */ +int BSP_rtems_irq_generic_set(rtems_irq_global_settings* config); + /* IRQ dispatcher to be defined by the PIC driver; note that it MUST * implement shared interrupts. * Note also that the exception frame passed to this handler is not very diff --git a/bsps/powerpc/include/libcpu/io.h b/bsps/powerpc/include/libcpu/io.h index 521c97801d..c4e529f4d5 100644 --- a/bsps/powerpc/include/libcpu/io.h +++ b/bsps/powerpc/include/libcpu/io.h @@ -107,6 +107,7 @@ static inline void out_be16(volatile uint16_t *addr, uint16_t val) __asm__ __volatile__("sth%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val)); } +#ifndef in_le32 static inline uint32_t in_le32(const volatile uint32_t *addr) { uint32_t ret; @@ -115,7 +116,9 @@ static inline uint32_t in_le32(const volatile uint32_t *addr) "r" (addr), "m" (*addr)); return ret; } +#endif +#ifndef in_be32 static inline uint32_t in_be32(const volatile uint32_t *addr) { uint32_t ret; @@ -123,17 +126,22 @@ static inline uint32_t in_be32(const volatile uint32_t *addr) __asm__ __volatile__("lwz%U1%X1 %0,%1; eieio" : "=r" (ret) : "m" (*addr)); return ret; } +#endif +#ifndef out_le32 static inline void out_le32(volatile uint32_t *addr, uint32_t val) { __asm__ __volatile__("stwbrx %1,0,%2; eieio" : "=m" (*addr) : "r" (val), "r" (addr)); } +#endif +#ifndef out_be32 static inline void out_be32(volatile uint32_t *addr, uint32_t val) { __asm__ __volatile__("stw%U0%X0 %1,%0; eieio" : "=m" (*addr) : "r" (val)); } +#endif #endif /* ASM */ #endif /* _LIBCPU_IO_H */ diff --git a/bsps/powerpc/motorola_powerpc/bootloader/misc.c b/bsps/powerpc/motorola_powerpc/bootloader/misc.c index 587bcffcff..ff2e3ff590 100644 --- a/bsps/powerpc/motorola_powerpc/bootloader/misc.c +++ b/bsps/powerpc/motorola_powerpc/bootloader/misc.c @@ -23,6 +23,8 @@ #include <rtems/bspIo.h> #include <bsp.h> +#include <rtems.h> + /* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) (((addr) + PAGE_MASK) & ~PAGE_MASK) @@ -401,7 +403,7 @@ setup_hw(void) } #endif - printk("\nRTEMS 4.x/PPC load: "); + printk("\nRTEMS " RTEMS_VERSION "/PPC load: "); timer = 0; cp = bd->cmd_line+strlen(bd->cmd_line); while (timer++ < 5*1000) { diff --git a/bsps/powerpc/motorola_powerpc/bootloader/ppcboot.lds b/bsps/powerpc/motorola_powerpc/bootloader/ppcboot.lds index 0ee7447546..501acc40dc 100644 --- a/bsps/powerpc/motorola_powerpc/bootloader/ppcboot.lds +++ b/bsps/powerpc/motorola_powerpc/bootloader/ppcboot.lds @@ -38,7 +38,9 @@ SECTIONS BYTE(0x75); BYTE(0x78); /* Partition name */ . = 0x400; *(.text) + *(.text*) *(.sdata2) + *(.sdata2*) *(.rodata) *(.rodata*) } @@ -71,13 +73,17 @@ SECTIONS *(.data) *(.data*) *(.sdata) + *(.sdata*) . = ALIGN(4); _data_end = .; } .bss : { *(.sbss) + *(.sbss*) *(.bss) + *(.bss*) + *(COMMON) . = ALIGN(4); _bss_end = .; } @@ -95,7 +101,7 @@ SECTIONS /DISCARD/ : { - *(.comment) + *(.comment*) + *(.debug*) } } - diff --git a/bsps/powerpc/motorola_powerpc/include/bsp.h b/bsps/powerpc/motorola_powerpc/include/bsp.h index 62e740272a..7d362bf406 100644 --- a/bsps/powerpc/motorola_powerpc/include/bsp.h +++ b/bsps/powerpc/motorola_powerpc/include/bsp.h @@ -31,11 +31,11 @@ #include <rtems.h> #include <libcpu/io.h> #include <bsp/vectors.h> - + #ifdef qemu #include <rtems/bspcmdline.h> #endif - + #ifdef __cplusplus extern "C" { #endif @@ -115,6 +115,36 @@ extern "C" { #endif #endif +/* + * The BSP has PCI devices. Enable support in LibBSD. + */ +#define BSP_HAS_PC_PCI + +/* + * Remap the PCI address space for LibBSD + */ +#define RTEMS_BSP_PCI_IO_REGION_BASE 0 +#define RTEMS_BSP_PCI_MEM_REGION_BASE PCI_DRAM_OFFSET + +/* + * Remap the PCI address space for LibBSD + */ +#define RTEMS_BSP_ADDR_PTR(_type) uint ## _type ## _t __volatile* +#define RTEMS_BSP_ADDR_CPTR(_type) const RTEMS_BSP_ADDR_PTR(_type) +#define RTEMS_BSP_ADDRESS_READ(_addr, _type) \ + *((RTEMS_BSP_ADDR_CPTR(_type)) (((RTEMS_BSP_ADDR_CPTR(8)) _addr) + PCI_DRAM_OFFSET)) +#define RTEMS_BSP_ADDRESS_WRITE(_addr, _val, _type) \ + *((RTEMS_BSP_ADDR_PTR(_type)) (((RTEMS_BSP_ADDR_PTR(8)) _addr) + PCI_DRAM_OFFSET)) = (_val) + +#define RTEMS_BSP_READ_1(_addr) RTEMS_BSP_ADDRESS_READ(_addr, 8) +#define RTEMS_BSP_READ_2(_addr) RTEMS_BSP_ADDRESS_READ(_addr, 16) +#define RTEMS_BSP_READ_4(_addr) RTEMS_BSP_ADDRESS_READ(_addr, 32) +#define RTEMS_BSP_READ_8(_addr) RTEMS_BSP_ADDRESS_READ(_addr, 64) + +#define RTEMS_BSP_WRITE_1(_addr, _val) RTEMS_BSP_ADDRESS_WRITE(_addr, _val, 8) +#define RTEMS_BSP_WRITE_2(_addr, _val) RTEMS_BSP_ADDRESS_WRITE(_addr, _val, 16) +#define RTEMS_BSP_WRITE_4(_addr, _val) RTEMS_BSP_ADDRESS_WRITE(_addr, _val, 32) +#define RTEMS_BSP_WRITE_8(_addr, _val) RTEMS_BSP_ADDRESS_WRITE(_addr, _val, 64) /* * Base address definitions for several devices diff --git a/bsps/powerpc/motorola_powerpc/include/bsp/irq.h b/bsps/powerpc/motorola_powerpc/include/bsp/irq.h index 3690dbbff7..cbb6ff69cf 100644 --- a/bsps/powerpc/motorola_powerpc/include/bsp/irq.h +++ b/bsps/powerpc/motorola_powerpc/include/bsp/irq.h @@ -19,9 +19,17 @@ #ifndef BSP_POWERPC_IRQ_H #define BSP_POWERPC_IRQ_H +#ifndef BSP_SHARED_HANDLER_SUPPORT #define BSP_SHARED_HANDLER_SUPPORT 1 +#endif + #include <rtems/irq.h> -#include <bsp/irq-default.h> + +/* + * Switch to using the generic support. Remove this when all BSPs have + * been converted. + */ +#define BSP_POWERPC_IRQ_GENERIC_SUPPORT 1 /* * 8259 edge/level control definitions at VIA @@ -107,6 +115,8 @@ extern "C" { #define BSP_IRQ_NUMBER (BSP_MISC_IRQ_MAX_OFFSET + 1) #define BSP_LOWEST_OFFSET (BSP_ISA_IRQ_LOWEST_OFFSET) #define BSP_MAX_OFFSET (BSP_MISC_IRQ_MAX_OFFSET) +#define BSP_INTERRUPT_VECTOR_MIN (BSP_LOWEST_OFFSET) +#define BSP_INTERRUPT_VECTOR_MAX (BSP_MAX_OFFSET) /* * Some ISA IRQ symbolic name definition */ @@ -191,6 +201,9 @@ int BSP_irq_ack_at_i8259s (const rtems_irq_number irqLine); */ int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine); +unsigned short BSP_irq_suspend_i8259s(unsigned short mask); +void BSP_irq_resume_i8259s(unsigned short in_progress_save); + extern void BSP_rtems_irq_mng_init(unsigned cpuId); extern void BSP_i8259s_init(void); diff --git a/bsps/powerpc/motorola_powerpc/start/bspstart.c b/bsps/powerpc/motorola_powerpc/start/bspstart.c index e74b02c446..ef8418e2c6 100644 --- a/bsps/powerpc/motorola_powerpc/start/bspstart.c +++ b/bsps/powerpc/motorola_powerpc/start/bspstart.c @@ -27,6 +27,7 @@ #include <bsp/pci.h> #include <bsp/openpic.h> #include <bsp/irq.h> +#include <bsp/irq-generic.h> #include <libcpu/bat.h> #include <libcpu/pte121.h> #include <libcpu/cpuIdent.h> @@ -334,10 +335,8 @@ static void bsp_early( void ) */ bsp_clicks_per_usec = BSP_bus_frequency/(BSP_time_base_divisor * 1000); - /* - * Initalize RTEMS IRQ system - */ - BSP_rtems_irq_mng_init(0); + /* Initialize interrupt support */ + bsp_interrupt_initialize(); /* Activate the page table mappings only after * initializing interrupts because the irq_mng_init() diff --git a/bsps/powerpc/shared/irq/i8259.c b/bsps/powerpc/shared/irq/i8259.c index 7363e87ba0..6a80e24946 100644 --- a/bsps/powerpc/shared/irq/i8259.c +++ b/bsps/powerpc/shared/irq/i8259.c @@ -12,6 +12,19 @@ #include <bsp.h> #include <bsp/irq.h> +#define PIC_EOSI 0x60 ///< End of Specific Interrupt (EOSI) +#define PIC_EOI 0x20 ///< Generic End of Interrupt (EOI) + +/* Operation control word type 3. Bit 3 (0x08) must be set. Even address. */ +#define PIC_OCW3_RIS 0x01 /* 1 = read IS, 0 = read IR */ +#define PIC_OCW3_RR 0x02 /* register read */ +#define PIC_OCW3_P 0x04 /* poll mode command */ +/* 0x08 must be 1 to select OCW3 vs OCW2 */ +#define PIC_OCW3_SEL 0x08 /* must be 1 */ +/* 0x10 must be 0 to select OCW3 vs ICW1 */ +#define PIC_OCW3_SMM 0x20 /* special mode mask */ +#define PIC_OCW3_ESMM 0x40 /* enable SMM */ + /*-------------------------------------------------------------------------+ | Cache for 1st and 2nd PIC IRQ line's status (enabled or disabled) register. +--------------------------------------------------------------------------*/ @@ -19,91 +32,137 @@ * lower byte is interrupt mask on the master PIC. * while upper bits are interrupt on the slave PIC. */ -volatile rtems_i8259_masks i8259s_cache = 0xfffb; +static rtems_i8259_masks i8259s_imr_cache = 0xFFFB; +static rtems_i8259_masks i8259s_in_progress = 0; + +static inline +void BSP_i8259s_irq_update_master_imr( void ) +{ + rtems_i8259_masks mask = i8259s_in_progress | i8259s_imr_cache; + outport_byte( PIC_MASTER_IMR_IO_PORT, mask & 0xff ); +} + +static inline +void BSP_i8259s_irq_update_slave_imr( void ) +{ + rtems_i8259_masks mask = i8259s_in_progress | i8259s_imr_cache; + outport_byte( PIC_SLAVE_IMR_IO_PORT, ( mask >> 8 ) & 0xff ); +} + +/* + * Is the IRQ valid? + */ +static inline bool BSP_i8259s_irq_valid(const rtems_irq_number irqLine) +{ + return ((int)irqLine >= BSP_ISA_IRQ_LOWEST_OFFSET) && + ((int)irqLine <= BSP_ISA_IRQ_MAX_OFFSET); +} + +/* + * Read the IRR register. The default. + */ +static inline uint8_t BSP_i8259s_irq_int_request_reg(uint32_t ioport) +{ + uint8_t isr; + inport_byte(ioport, isr); + return isr; +} + +/* + * Read the ISR register. Keep the default of the IRR. + */ +static inline uint8_t BSP_i8259s_irq_in_service_reg(uint32_t ioport) +{ + uint8_t isr; + outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR | PIC_OCW3_RIS); + inport_byte(ioport, isr); + outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR); + return isr; +} /*-------------------------------------------------------------------------+ | Function: BSP_irq_disable_at_i8259s | Description: Mask IRQ line in appropriate PIC chip. -| Global Variables: i8259s_cache +| Global Variables: i8259s_imr_cache, i8259s_in_progress | Arguments: vector_offset - number of IRQ line to mask. -| Returns: original state or -1 on error. +| Returns: 0 is OK. +--------------------------------------------------------------------------*/ -int BSP_irq_disable_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_disable_at_i8259s(const rtems_irq_number irqLine) { unsigned short mask; rtems_interrupt_level level; - int rval; - if ( ((int)irqLine < BSP_ISA_IRQ_LOWEST_OFFSET) || - ((int)irqLine > BSP_ISA_IRQ_MAX_OFFSET) - ) + if (!BSP_i8259s_irq_valid(irqLine)) return -1; rtems_interrupt_disable(level); mask = 1 << irqLine; - rval = i8259s_cache & mask ? 0 : 1; - i8259s_cache |= mask; + i8259s_imr_cache |= mask; if (irqLine < 8) { - outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); + BSP_i8259s_irq_update_master_imr(); } else { - outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8)); + BSP_i8259s_irq_update_slave_imr(); } + rtems_interrupt_enable(level); - return rval; + return 0; } /*-------------------------------------------------------------------------+ | Function: BSP_irq_enable_at_i8259s | Description: Unmask IRQ line in appropriate PIC chip. -| Global Variables: i8259s_cache +| Global Variables: i8259s_imr_cache, i8259s_in_progress | Arguments: irqLine - number of IRQ line to mask. | Returns: Nothing. +--------------------------------------------------------------------------*/ -int BSP_irq_enable_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_enable_at_i8259s(const rtems_irq_number irqLine) { - unsigned short mask; rtems_interrupt_level level; + unsigned short mask; + uint8_t isr; + uint8_t irr; - if ( ((int)irqLine < BSP_ISA_IRQ_LOWEST_OFFSET) || - ((int)irqLine > BSP_ISA_IRQ_MAX_OFFSET ) - ) + if (!BSP_i8259s_irq_valid(irqLine)) return 1; rtems_interrupt_disable(level); - mask = ~(1 << irqLine); - i8259s_cache &= mask; + mask = 1 << irqLine; + i8259s_imr_cache &= ~mask; if (irqLine < 8) { - outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); + isr = BSP_i8259s_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT); + irr = BSP_i8259s_irq_int_request_reg(PIC_MASTER_COMMAND_IO_PORT); + BSP_i8259s_irq_update_master_imr(); } else { - outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8)); + isr = BSP_i8259s_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT); + irr = BSP_i8259s_irq_int_request_reg(PIC_SLAVE_COMMAND_IO_PORT); + BSP_i8259s_irq_update_slave_imr(); } + rtems_interrupt_enable(level); return 0; } /* mask_irq */ -int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_enabled_at_i8259s(const rtems_irq_number irqLine) { unsigned short mask; - if ( ((int)irqLine < BSP_ISA_IRQ_LOWEST_OFFSET) || - ((int)irqLine > BSP_ISA_IRQ_MAX_OFFSET) - ) + if (!BSP_i8259s_irq_valid(irqLine)) return 1; mask = (1 << irqLine); - return (~(i8259s_cache & mask)); + return (~(i8259s_imr_cache & mask)); } /*-------------------------------------------------------------------------+ @@ -113,24 +172,47 @@ int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine) | Arguments: irqLine - number of IRQ line to acknowledge. | Returns: Nothing. +--------------------------------------------------------------------------*/ -int BSP_irq_ack_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_ack_at_i8259s(const rtems_irq_number irqLine) { + uint8_t slave_isr = 0; + if (irqLine >= 8) { - outport_byte(PIC_MASTER_COMMAND_IO_PORT, SLAVE_PIC_EOSI); - outport_byte(PIC_SLAVE_COMMAND_IO_PORT, (PIC_EOSI | (irqLine - 8))); - } - else { - outport_byte(PIC_MASTER_COMMAND_IO_PORT, (PIC_EOSI | irqLine)); + outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI); + slave_isr = BSP_i8259s_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT); } + /* + * Only issue the EOI to the master if there are no more interrupts in + * service for the slave. i8259a data sheet page 18, The Special Fully Nested + * Mode, b. + */ + if (slave_isr == 0) + outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI); + return 0; } /* ackIRQ */ +unsigned short BSP_irq_suspend_i8259s(unsigned short mask) +{ + unsigned short in_progress_save = i8259s_in_progress; + i8259s_in_progress |= mask; + BSP_i8259s_irq_update_master_imr(); + BSP_i8259s_irq_update_slave_imr(); + return in_progress_save; +} + +void BSP_irq_resume_i8259s(unsigned short in_progress_save) +{ + i8259s_in_progress = in_progress_save; + BSP_i8259s_irq_update_master_imr(); + BSP_i8259s_irq_update_slave_imr(); +} + void BSP_i8259s_init(void) { /* - * init master 8259 interrupt controller + * Always mask at least current interrupt to prevent re-entrance */ outport_byte(PIC_MASTER_COMMAND_IO_PORT, 0x11); /* Start init sequence */ outport_byte(PIC_MASTER_IMR_IO_PORT, 0x00);/* Vector base = 0 */ diff --git a/bsps/powerpc/shared/irq/irq_init.c b/bsps/powerpc/shared/irq/irq_init.c index 1a44992a5b..233c659b85 100644 --- a/bsps/powerpc/shared/irq/irq_init.c +++ b/bsps/powerpc/shared/irq/irq_init.c @@ -280,6 +280,7 @@ void BSP_rtems_irq_mng_init(unsigned cpuId) int known_cpi_isa_bridge = 0; #endif int i; + int r; /* * First initialize the Interrupt management hardware @@ -310,7 +311,7 @@ void BSP_rtems_irq_mng_init(unsigned cpuId) #endif known_cpi_isa_bridge = 1; } - if ( currentBoard == MVME_2300 ) { + if ( currentBoard == MVME_2300 || currentBoard == MVME_2600_2700_W_MVME761 ) { /* nothing to do for W83C553 bridge */ known_cpi_isa_bridge = 1; } @@ -351,7 +352,19 @@ void BSP_rtems_irq_mng_init(unsigned cpuId) initial_config.irqBase = BSP_LOWEST_OFFSET; initial_config.irqPrioTbl = irqPrioTable; - if (!BSP_rtems_irq_mngt_set(&initial_config)) { +#ifdef BSP_POWERPC_IRQ_GENERIC_SUPPORT +#ifdef TRACE_IRQ_INIT + printk("RTEMS IRQ management: irq-generic\n"); +#endif + r = BSP_rtems_irq_generic_set(&initial_config); +#else +#ifdef TRACE_IRQ_INIT + printk("RTEMS IRQ management: legacy\n"); +#endif + r = BSP_rtems_irq_mngt_set(&initial_config); +#endif + + if (!r) { /* * put something here that will show the failure... */ diff --git a/bsps/powerpc/shared/irq/openpic_i8259_irq.c b/bsps/powerpc/shared/irq/openpic_i8259_irq.c index 4a9c393f7f..2617555658 100644 --- a/bsps/powerpc/shared/irq/openpic_i8259_irq.c +++ b/bsps/powerpc/shared/irq/openpic_i8259_irq.c @@ -15,6 +15,7 @@ #include <bsp.h> #include <bsp/irq.h> #include <bsp/irq_supp.h> +#include <bsp/irq-generic.h> #ifndef BSP_HAS_NO_VME #include <bsp/VMEConfig.h> #endif @@ -214,7 +215,7 @@ int BSP_setup_the_pic(rtems_irq_global_settings* config) /* * Must enable PCI/ISA bridge IRQ */ - openpic_enable_irq (BSP_PCI_ISA_BRIDGE_IRQ); + openpic_enable_irq (BSP_PCI_ISA_BRIDGE_IRQ - BSP_PCI_IRQ_LOWEST_OFFSET); #endif #endif @@ -234,15 +235,15 @@ int C_dispatch_irq_handler (BSP_Exception_frame *frame, unsigned int excNum) #if BSP_ISA_IRQ_NUMBER > 0 register unsigned isaIntr; /* boolean */ register unsigned oldMask = 0; /* old isa pic masks */ - register unsigned newMask; /* new isa pic masks */ #endif if (excNum == ASM_DEC_VECTOR) { - - bsp_irq_dispatch_list(rtems_hdl_tbl, BSP_DECREMENTER, default_rtems_entry.hdl); - +#ifdef BSP_POWERPC_IRQ_GENERIC_SUPPORT + bsp_interrupt_handler_dispatch(BSP_DECREMENTER); +#else + bsp_irq_dispatch_list(rtems_hdl_tbl, BSP_DECREMENTER, default_rtems_entry.hdl); +#endif return 0; - } #if BSP_PCI_IRQ_NUMBER > 0 @@ -274,7 +275,7 @@ int C_dispatch_irq_handler (BSP_Exception_frame *frame, unsigned int excNum) #if BSP_ISA_IRQ_NUMBER > 0 #ifdef BSP_PCI_ISA_BRIDGE_IRQ -#if 0 == BSP_PCI_IRQ_NUMBER +#if 0 == BSP_PCI_IRQ_NUMBER #error "Configuration Error -- BSP w/o PCI IRQs MUST NOT define BSP_PCI_ISA_BRIDGE_IRQ" #endif isaIntr = (irq == BSP_PCI_ISA_BRIDGE_IRQ); @@ -289,11 +290,7 @@ int C_dispatch_irq_handler (BSP_Exception_frame *frame, unsigned int excNum) /* * store current PIC mask */ - oldMask = i8259s_cache; - newMask = oldMask | irq_mask_or_tbl [irq]; - i8259s_cache = newMask; - outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); - outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8)); + oldMask = BSP_irq_suspend_i8259s(irq_mask_or_tbl [irq]); BSP_irq_ack_at_i8259s (irq); #if BSP_PCI_IRQ_NUMBER > 0 if ( OpenPIC ) @@ -303,13 +300,15 @@ int C_dispatch_irq_handler (BSP_Exception_frame *frame, unsigned int excNum) #endif /* dispatch handlers */ +#ifdef BSP_POWERPC_IRQ_GENERIC_SUPPORT + bsp_interrupt_handler_dispatch(irq); +#else bsp_irq_dispatch_list(rtems_hdl_tbl, irq, default_rtems_entry.hdl); +#endif #if BSP_ISA_IRQ_NUMBER > 0 if (isaIntr) { - i8259s_cache = oldMask; - outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); - outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8)); + BSP_irq_resume_i8259s(oldMask); } else #endif diff --git a/bsps/powerpc/shared/irq/ppc-irq-generic.c b/bsps/powerpc/shared/irq/ppc-irq-generic.c new file mode 100644 index 0000000000..8a70109e52 --- /dev/null +++ b/bsps/powerpc/shared/irq/ppc-irq-generic.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsPowerPC + * + * @brief Generic Interrupt suppoer + */ + +/* + * Copyright (C) 2021 Chris Johns. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> + +#include <rtems.h> +#include <stdlib.h> +#include <rtems/bspIo.h> /* for printk */ +#include <libcpu/spr.h> +#include <bsp/irq_supp.h> +#include <bsp/irq-generic.h> +#include <bsp/vectors.h> + +SPR_RW(BOOKE_TSR) +SPR_RW(PPC405_TSR) + +/* legacy mode for bookE DEC exception; + * to avoid the double layer of function calls + * (dec_handler_bookE -> C_dispatch_irq_handler -> user handler) + * it is preferrable for the user to hook the DEC + * exception directly. + * However, the legacy mode works with less modifications + * of user code. + */ +static int C_dispatch_dec_handler_bookE (BSP_Exception_frame *frame, unsigned int excNum) +{ + /* clear interrupt; we must do this + * before C_dispatch_irq_handler() + * re-enables MSR_EE. + * Note that PPC405 uses a different SPR# for TSR + */ + if (ppc_cpu_is_bookE()==PPC_BOOKE_405) + _write_PPC405_TSR( BOOKE_TSR_DIS ); + else + _write_BOOKE_TSR( BOOKE_TSR_DIS ); + return C_dispatch_irq_handler(frame, ASM_DEC_VECTOR); +} + +/* + * RTEMS Global Interrupt Handler Management Routines + */ +int BSP_rtems_irq_generic_set(rtems_irq_global_settings* config) +{ + int r; + + r = BSP_setup_the_pic(config); + if (!r) + return r; + + ppc_exc_set_handler(ASM_EXT_VECTOR, C_dispatch_irq_handler); + if ( ppc_cpu_is_bookE() ) { + /* bookE decrementer interrupt needs to be cleared BEFORE + * dispatching the user ISR (because the user ISR is called + * with EE enabled) + * We do this so that existing DEC handlers can be used + * with minor modifications. + */ + ppc_exc_set_handler(ASM_BOOKE_DEC_VECTOR, C_dispatch_dec_handler_bookE); + } else { + ppc_exc_set_handler(ASM_DEC_VECTOR, C_dispatch_irq_handler); + } + + return 1; +} + +void bsp_interrupt_vector_enable(rtems_vector_number vector) +{ + bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); + BSP_enable_irq_at_pic(vector); +} + +void bsp_interrupt_vector_disable(rtems_vector_number vector) +{ + bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector)); + BSP_disable_irq_at_pic(vector); +} + +rtems_status_code bsp_interrupt_facility_initialize(void) +{ + /* + * Initialize RTEMS IRQ system + */ + BSP_rtems_irq_mng_init(0); + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/shared/grlib-sources.am b/bsps/shared/grlib-sources.am index 512a48c7f7..3dd3bdd89e 100644 --- a/bsps/shared/grlib-sources.am +++ b/bsps/shared/grlib-sources.am @@ -20,8 +20,11 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/ascs/grascs.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/btimer/gptimer.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/btimer/tlib.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/btimer/tlib_ckinit.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/canbtrs.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/canmux.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/grcan.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/grcanfd.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/grcanstd.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/occan.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/satcan.c librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/drvmgr/ambapp_bus.c diff --git a/bsps/shared/grlib/1553/gr1553b.c b/bsps/shared/grlib/1553/gr1553b.c index 777b6dc743..179f5e3786 100644 --- a/bsps/shared/grlib/1553/gr1553b.c +++ b/bsps/shared/grlib/1553/gr1553b.c @@ -206,6 +206,10 @@ static int gr1553_init2(struct drvmgr_dev *dev) GR1553B_WRITE_REG(®s->rt_cfg, 0x15530000); /* Stop BM logging (just in case) */ GR1553B_WRITE_REG(®s->bm_ctrl, 0); + /* Set codec version. This is only supported by some devices, i.e. GR740. + * It will not have any effect on devices that does not support this bit. + */ + GR1553B_WRITE_REG(®s->hwcfg, 1<<12); return DRVMGR_OK; } diff --git a/bsps/shared/grlib/1553/gr1553bc.c b/bsps/shared/grlib/1553/gr1553bc.c index a22e2d8007..f37364e0ea 100644 --- a/bsps/shared/grlib/1553/gr1553bc.c +++ b/bsps/shared/grlib/1553/gr1553bc.c @@ -63,7 +63,7 @@ struct gr1553bc_priv { */ #define NEXT_MINOR_MARKER 0x01 -/* To separate ASYNC list from SYNC list we mark them differently, but with +/* To separate ASYNC list from SYNC list we mark them differently, but with * LSB always set. This can be used to get the list the descriptor is a part * of. */ @@ -71,7 +71,7 @@ struct gr1553bc_priv { struct gr1553bc_list_cfg gr1553bc_def_cfg = { - .rt_timeout = + .rt_timeout = { 20, 20, 20, 20, 20, 20, 20, 20, @@ -80,7 +80,7 @@ struct gr1553bc_list_cfg gr1553bc_def_cfg = 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20 + 20, 20, 20 }, .bc_timeout = 30, .tropt_irq_on_err = 0, @@ -132,7 +132,7 @@ int gr1553bc_list_config /* RT Time Tolerances */ for (i=0; i<31; i++) { - /* 0=14us, 1=18us ... 0xf=74us + /* 0=14us, 1=18us ... 0xf=74us * round upwards: 15us will be 18us */ timeout = ((cfg->rt_timeout[i] + 1) - 14) / 4; @@ -167,7 +167,7 @@ void gr1553bc_list_link_major( if ( major ) { major->next = next; if ( next ) { - major->minors[major->cfg->minor_cnt-1]->next = + major->minors[major->cfg->minor_cnt-1]->next = next->minors[0]; } else { major->minors[major->cfg->minor_cnt-1]->next = NULL; @@ -195,7 +195,7 @@ int gr1553bc_list_set_major( prev = list->majors[list->major_cnt-1]; } - /* Link to next Major if not the last one and if there is + /* Link to next Major if not the last one and if there is * a next major */ if ( no == list->major_cnt-1 ) { @@ -262,7 +262,7 @@ int gr1553bc_list_table_size(struct gr1553bc_list *list) minor_cnt = major->cfg->minor_cnt; for (j=0; j<minor_cnt; j++) { /* 128-bit Alignment required by HW */ - size += (GR1553BC_BD_ALIGN - + size += (GR1553BC_BD_ALIGN - (size & (GR1553BC_BD_ALIGN-1))) & ~(GR1553BC_BD_ALIGN-1); @@ -284,6 +284,7 @@ int gr1553bc_list_table_alloc int i, j, minor_cnt, size; unsigned int table; struct gr1553bc_priv *bcpriv = list->bc; + int retval = 0; /* Free previous allocated descriptor table */ gr1553bc_list_table_free(list); @@ -298,8 +299,8 @@ int gr1553bc_list_table_alloc /* Address given in Hardware accessible address, we * convert it into CPU-accessible address. */ - list->table_hw = (unsigned int)bdtab_custom & ~0x1; - list->_table = bdtab_custom; + list->_table = (void*)((unsigned int)bdtab_custom & ~0x1); + list->table_hw = (unsigned int)list->_table; drvmgr_translate_check( *bcpriv->pdev, DMAMEM_TO_CPU, @@ -310,16 +311,19 @@ int gr1553bc_list_table_alloc if (bdtab_custom == NULL) { /* Allocate descriptors */ list->_table = grlib_malloc(size + (GR1553BC_BD_ALIGN-1)); - if ( list->_table == NULL ) - return -1; + if ( list->_table == NULL ) { + retval = -1; + goto err; + } + /* 128-bit Alignment required by HW */ + list->table_cpu = + (((unsigned int)list->_table + (GR1553BC_BD_ALIGN-1)) & + ~(GR1553BC_BD_ALIGN-1)); } else { /* Custom address, given in CPU-accessible address */ list->_table = bdtab_custom; + list->table_cpu = (unsigned int)list->_table; } - /* 128-bit Alignment required by HW */ - list->table_cpu = - (((unsigned int)list->_table + (GR1553BC_BD_ALIGN-1)) & - ~(GR1553BC_BD_ALIGN-1)); /* We got CPU accessible descriptor table address, now we * translate that into an address that the Hardware can @@ -338,6 +342,12 @@ int gr1553bc_list_table_alloc } } + /* Verify alignment */ + if (list->table_hw & (GR1553BC_BD_ALIGN-1)) { + retval = -2; + goto err; + } + /* Write End-Of-List all over the descriptor table here, * For debugging/safety? */ @@ -359,8 +369,16 @@ int gr1553bc_list_table_alloc table += gr1553bc_minor_table_size(major->minors[j]); } } - - return 0; +err: + if (retval) { + if (list->_table_custom == NULL && list->_table) { + free(list->_table); + } + list->table_hw = 0; + list->table_cpu = 0; + list->_table = NULL; + } + return retval; } void gr1553bc_list_table_free(struct gr1553bc_list *list) @@ -399,7 +417,7 @@ int gr1553bc_list_table_build(struct gr1553bc_list *list) bds = minor->bds; /* BD[0..SLOTCNT-1] = message slots - * BD[SLOTCNT+0] = END + * BD[SLOTCNT+0] = END * BD[SLOTCNT+1] = JUMP * * or if no optional time slot handling: @@ -485,7 +503,7 @@ void gr1553bc_bd_init( ((word0 & GR1553BC_BD_TYPE) == 0) ) { /* Don't touch timeslot previously allocated */ word0 &= ~GR1553BC_TR_TIME; - word0 |= GR1553BC_READ_MEM(&raw->words[0]) & + word0 |= GR1553BC_READ_MEM(&raw->words[0]) & GR1553BC_TR_TIME; } GR1553BC_WRITE_MEM(&raw->words[0], word0); @@ -523,7 +541,7 @@ int gr1553bc_major_alloc_skel maj->cfg = cfg; maj->next = NULL; - /* Create links between minor frames, and from minor frames + /* Create links between minor frames, and from minor frames * to configuration structure. */ minor = (struct gr1553bc_minor *)&maj->minors[cfg->minor_cnt]; @@ -697,7 +715,7 @@ int gr1553bc_slot_alloc2( set0 = (set0 & ~GR1553BC_TR_TIME) | timefree; GR1553BC_WRITE_MEM(&trbd->settings[0], set0); /* Note: at the moment the minor frame can be executed faster - * than expected, we hurry up writing requested + * than expected, we hurry up writing requested * descriptor. */ } @@ -886,7 +904,7 @@ int gr1553bc_slot_irq_prepare union gr1553bc_bd *bd; int slot_no, to_mid; - /* Build unconditional IRQ descriptor. The padding is used + /* Build unconditional IRQ descriptor. The padding is used * for identifying the MINOR frame and function and custom data. * * The IRQ is disabled at first, a unconditional jump to next @@ -1115,7 +1133,7 @@ int gr1553bc_slot_update *stat = GR1553BC_READ_MEM(&bd->tr.status); if ( status ) { /* Clear status fields user selects, then - * or bit31 if user wants that. The bit31 + * or bit31 if user wants that. The bit31 * may be used to indicate if the BC has * performed the access. */ @@ -1192,7 +1210,7 @@ int gr1553bc_mid_from_bd( found_mid: /* Get MID of JUMP descriptor */ bdmid = word2 >> 8; - /* Subtract distance from JUMP descriptor to find MID + /* Subtract distance from JUMP descriptor to find MID * of requested BD. */ slot_no = GR1553BC_SLOTID_FROM_ID(bdmid); @@ -1445,7 +1463,7 @@ void gr1553bc_device_init(struct gr1553bc_priv *priv) /* Stop BC if not already stopped */ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); - /* Since RT can not be used at the same time as BC, we stop + /* Since RT can not be used at the same time as BC, we stop * RT rx, it should already be stopped... */ GR1553BC_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); @@ -1463,7 +1481,7 @@ void gr1553bc_device_init(struct gr1553bc_priv *priv) priv->alist = NULL; priv->irq_log_base = (uint32_t *) - (((uint32_t)priv->irq_log_p + (GR1553BC_IRQLOG_SIZE-1)) & + (((uint32_t)priv->irq_log_p + (GR1553BC_IRQLOG_SIZE-1)) & ~(GR1553BC_IRQLOG_SIZE-1)); /* Translate into a hardware accessible address */ drvmgr_translate_check( @@ -1487,7 +1505,7 @@ void gr1553bc_device_uninit(struct gr1553bc_priv *priv) /* Stop BC if not already stopped */ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); - /* Since RT can not be used at the same time as BC, we stop + /* Since RT can not be used at the same time as BC, we stop * RT rx, it should already be stopped... */ GR1553BC_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); @@ -1518,7 +1536,7 @@ void gr1553bc_isr(void *arg) /* Clear handled IRQs */ GR1553BC_WRITE_REG(&priv->regs->irq, irq); - /* DMA error. This IRQ does not affect the IRQ log. + /* DMA error. This IRQ does not affect the IRQ log. * We let standard IRQ handle handle it. */ if ( irq & GR1553B_IRQEN_BCDE ) { @@ -1563,7 +1581,7 @@ void gr1553bc_isr(void *arg) bd = NULL; } - /* Handle Descriptor that cased IRQ + /* Handle Descriptor that cased IRQ * * If someone have inserted an IRQ descriptor and tied * that to a custom function we call that function, otherwise diff --git a/bsps/shared/grlib/1553/gr1553bm.c b/bsps/shared/grlib/1553/gr1553bm.c index 482e574d78..0672c468a4 100644 --- a/bsps/shared/grlib/1553/gr1553bm.c +++ b/bsps/shared/grlib/1553/gr1553bm.c @@ -89,7 +89,7 @@ static void gr1553bm_hw_start(struct gr1553bm_priv *priv) /* Start logging */ priv->regs->bm_ctrl = - (priv->cfg.filt_error_options & + (priv->cfg.filt_error_options & (GR1553B_BM_CTRL_MANL|GR1553B_BM_CTRL_UDWL|GR1553B_BM_CTRL_IMCL)) | GR1553B_BM_CTRL_BMEN; @@ -178,6 +178,7 @@ void gr1553bm_close(void *bm) /* Configure the BM driver */ int gr1553bm_config(void *bm, struct gr1553bm_config *cfg) { + int retval = 0; struct gr1553bm_priv *priv = bm; if ( priv->started ) @@ -193,12 +194,11 @@ int gr1553bm_config(void *bm, struct gr1553bm_config *cfg) } priv->buffer_size = cfg->buffer_size & ~0x7; /* on 8 byte bounadry */ if ((unsigned int)cfg->buffer_custom & 1) { - /* Custom Address Given in Remote address. We need - * to convert it intoTranslate into Hardware a - * hardware accessible address + /* Custom address given in remote address. We need + * to convert it into a hardware accessible address */ - priv->buffer_base_hw = (unsigned int)cfg->buffer_custom & ~1; - priv->buffer = cfg->buffer_custom; + priv->buffer = (void*)((unsigned int)cfg->buffer_custom & ~1); + priv->buffer_base_hw = (unsigned int)priv->buffer; drvmgr_translate_check( *priv->pdev, DMAMEM_TO_CPU, @@ -209,17 +209,19 @@ int gr1553bm_config(void *bm, struct gr1553bm_config *cfg) if (cfg->buffer_custom == NULL) { /* Allocate new buffer dynamically */ priv->buffer = grlib_malloc(priv->buffer_size + 8); - if (priv->buffer == NULL) - return -1; + if (priv->buffer == NULL) { + retval = -1; + goto err; + } + /* Align to 8 bytes */ + priv->buffer_base = ((unsigned int)priv->buffer + (8-1)) & ~(8-1); } else { /* Address given in CPU accessible address, no * translation required. */ priv->buffer = cfg->buffer_custom; + priv->buffer_base = (unsigned int)priv->buffer; } - /* Align to 16 bytes */ - priv->buffer_base = ((unsigned int)priv->buffer + (8-1)) & - ~(8-1); /* Translate address of buffer base into address that Hardware must * use to access the buffer. */ @@ -229,13 +231,28 @@ int gr1553bm_config(void *bm, struct gr1553bm_config *cfg) (void *)priv->buffer_base, (void **)&priv->buffer_base_hw, priv->buffer_size); - + + } + + /* Verify alignment */ + if (priv->buffer_base_hw & (8-1)) { + retval = -2; + goto err; } /* Copy valid config */ priv->cfg = *cfg; - return 0; +err: + if (retval) { + if (cfg->buffer_custom == NULL && priv->buffer) { + free(priv->buffer); + } + priv->buffer_base_hw = (unsigned int)NULL; + priv->buffer_base = (unsigned int)NULL; + priv->buffer = NULL; + } + return retval; } /* Start logging */ @@ -249,7 +266,7 @@ int gr1553bm_start(void *bm) return -2; /* Start at Time = 0 */ - priv->regs->bm_ttag = + priv->regs->bm_ttag = priv->cfg.time_resolution << GR1553B_BM_TTAG_RES_BIT; /* Configure Filters */ @@ -282,7 +299,7 @@ void gr1553bm_stop(void *bm) /* Stop Hardware */ gr1553bm_hw_stop(priv); - /* At this point the hardware must be stopped and IRQ + /* At this point the hardware must be stopped and IRQ * sources unmasked. */ @@ -331,7 +348,7 @@ resample: hwtime2 = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL; if ( hwtime > hwtime2 ) { /* priv->time and hwtime may be out of sync if - * IRQ updated priv->time just after bm_ttag was read + * IRQ updated priv->time just after bm_ttag was read * here, we resample if we detect inconsistancy. */ goto resample; diff --git a/bsps/shared/grlib/1553/gr1553rt.c b/bsps/shared/grlib/1553/gr1553rt.c index 339e856c76..7f35138376 100644 --- a/bsps/shared/grlib/1553/gr1553rt.c +++ b/bsps/shared/grlib/1553/gr1553rt.c @@ -147,7 +147,6 @@ static int gr1553rt_list_reg(struct gr1553rt_list *list) return -1; } -#if 0 /* unused for now */ /* Unregister List from device */ static void gr1553rt_list_unreg(struct gr1553rt_list *list) { @@ -156,16 +155,15 @@ static void gr1553rt_list_unreg(struct gr1553rt_list *list) priv->lists[list->listid] = NULL; list->listid = -1; } -#endif static int gr1553rt_bdid(void *rt, struct gr1553rt_sw_bd *bd) { struct gr1553rt_priv *priv = rt; - + unsigned short index; /* Get Index of Software BD */ - index = ((unsigned int)bd - (unsigned int)&priv->swbds[0]) / + index = ((unsigned int)bd - (unsigned int)&priv->swbds[0]) / sizeof(struct gr1553rt_sw_bd); return index; @@ -197,10 +195,9 @@ static int gr1553rt_bd_alloc(void *rt, struct gr1553rt_sw_bd **bd, int cnt) } *bd = &priv->swbds[priv->swbd_free]; + curr = &priv->swbds[priv->swbd_free]; for (i=0; i<cnt; i++) { - if ( i == 0) { - curr = &priv->swbds[priv->swbd_free]; - } else { + if ( i != 0) { curr = &priv->swbds[curr->this_next]; } if ( curr->this_next == 0xffff ) { @@ -240,7 +237,7 @@ int gr1553rt_list_init { struct gr1553rt_priv *priv = rt; size_t size; - int i; + int i, malloc_used; struct gr1553rt_sw_bd *swbd; unsigned short index; struct gr1553rt_list *list; @@ -251,6 +248,7 @@ int gr1553rt_list_init * If the IN/OUT plist argument points to NULL a list * dynamically allocated here. */ + malloc_used = 0; list = *plist; if ( list == NULL ) { /* Dynamically allocate LIST */ @@ -258,20 +256,27 @@ int gr1553rt_list_init (cfg->bd_cnt * sizeof(list->bds[0])); list = grlib_malloc(size); if ( list == NULL ) - return -1; + return -1; /* Out of Memory */ *plist = list; + malloc_used = 1; } list->rt = rt; list->subadr = -1; list->listid = gr1553rt_list_reg(list); - if ( list->listid == -1 ) + if ( list->listid == -1 ) { + if (malloc_used) + free(list); return -2; /* Too many lists */ + } list->cfg = cfg; list->bd_cnt = cfg->bd_cnt; /* Allocate all BDs needed by list */ if ( gr1553rt_bd_alloc(rt, &swbd, list->bd_cnt) ) { + gr1553rt_list_unreg(list); + if (malloc_used) + free(list); return -3; /* Too few descriptors */ } @@ -352,7 +357,7 @@ int gr1553rt_bd_init( bd->next = nextbd; SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); - return 0; + return 0; } int gr1553rt_bd_update( @@ -410,7 +415,7 @@ int gr1553rt_bd_update( } *dptr = (uint16_t *)tmp; } - SPIN_LOCK_IRQ(&priv->devlock, irqflags); + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); return 0; } @@ -727,7 +732,7 @@ void gr1553rt_hw_stop(struct gr1553rt_priv *priv) GR1553RT_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); /* Stop BC if not already stopped: BC can not be used simultaneously - * as the RT anyway + * as the RT anyway */ GR1553RT_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); @@ -766,17 +771,17 @@ void gr1553rt_sw_free(struct gr1553rt_priv *priv) } } -/* Free dynamically allocated buffers, if any */ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) { int size; + int retval = 0; /* Allocate Event log */ if ((unsigned int)priv->cfg.evlog_buffer & 1) { /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ - priv->evlog_hw_base = (unsigned int *) + priv->evlog_buffer = (void *) ((unsigned int)priv->cfg.evlog_buffer & ~0x1); - priv->evlog_buffer = priv->cfg.evlog_buffer; + priv->evlog_hw_base = (unsigned int*)priv->evlog_buffer; drvmgr_translate_check( *priv->pdev, DMAMEM_TO_CPU, @@ -788,16 +793,19 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) if (priv->cfg.evlog_buffer == NULL) { priv->evlog_buffer = grlib_malloc( priv->cfg.evlog_size * 2); - if (priv->evlog_buffer == NULL) - return -1; + if (priv->evlog_buffer == NULL) { + retval = -1; + goto err; + } + /* Align to SIZE bytes boundary */ + priv->evlog_cpu_base = (unsigned int *) + (((unsigned int)priv->evlog_buffer + + (priv->cfg.evlog_size-1)) & ~(priv->cfg.evlog_size-1)); } else { /* Addess already CPU-LOCAL */ priv->evlog_buffer = priv->cfg.evlog_buffer; + priv->evlog_cpu_base = (unsigned int *)priv->evlog_buffer; } - /* Align to SIZE bytes boundary */ - priv->evlog_cpu_base = (unsigned int *) - (((unsigned int)priv->evlog_buffer + - (priv->cfg.evlog_size-1)) & ~(priv->cfg.evlog_size-1)); drvmgr_translate_check( *priv->pdev, @@ -807,6 +815,11 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) priv->cfg.evlog_size ); } + /* Verify alignment */ + if ((unsigned int)priv->evlog_hw_base & (priv->cfg.evlog_size-1)) { + retval = -2; + goto err; + } priv->evlog_cpu_end = priv->evlog_cpu_base + priv->cfg.evlog_size/sizeof(unsigned int *); @@ -815,9 +828,9 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) size = priv->bds_cnt * sizeof(struct gr1553rt_bd); if ((unsigned int)priv->cfg.bd_buffer & 1) { /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ - priv->bds_hw = (struct gr1553rt_bd *) + priv->bd_buffer = (void *) ((unsigned int)priv->cfg.bd_buffer & ~0x1); - priv->bd_buffer = priv->cfg.bd_buffer; + priv->bds_hw = (struct gr1553rt_bd *)priv->bd_buffer; drvmgr_translate_check( *priv->pdev, DMAMEM_TO_CPU, @@ -828,15 +841,18 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) } else { if ( priv->cfg.bd_buffer == NULL ) { priv->bd_buffer = grlib_malloc(size + 0xf); - if (priv->bd_buffer == NULL) - return -1; + if (priv->bd_buffer == NULL) { + retval = -1; + goto err; + } + /* Align to 16 bytes boundary */ + priv->bds_cpu = (struct gr1553rt_bd *) + (((unsigned int)priv->bd_buffer + 0xf) & ~0xf); } else { /* Addess already CPU-LOCAL */ priv->bd_buffer = priv->cfg.bd_buffer; + priv->bds_cpu = (struct gr1553rt_bd *)priv->bd_buffer; } - /* Align to 16 bytes boundary */ - priv->bds_cpu = (struct gr1553rt_bd *) - (((unsigned int)priv->bd_buffer + 0xf) & ~0xf); /* Translate from CPU address to hardware address */ drvmgr_translate_check( @@ -847,21 +863,28 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) size ); } + /* Verify alignment */ + if ((unsigned int)priv->bds_hw & (0xf)) { + retval = -2; + goto err; + } #if (RTBD_MAX == 0) /* Allocate software description of */ priv->swbds = grlib_malloc(priv->cfg.bd_count * sizeof(*priv->swbds)); if ( priv->swbds == NULL ) { - return -1; + retval = -1; + goto err; } #endif /* Allocate Sub address table */ if ((unsigned int)priv->cfg.satab_buffer & 1) { /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ - priv->sas_hw = (struct gr1553rt_sa *) + priv->satab_buffer = (void *) ((unsigned int)priv->cfg.satab_buffer & ~0x1); - priv->satab_buffer = priv->cfg.satab_buffer; + priv->sas_hw = (struct gr1553rt_sa *)priv->satab_buffer; + drvmgr_translate_check( *priv->pdev, DMAMEM_TO_CPU, @@ -871,16 +894,18 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) } else { if (priv->cfg.satab_buffer == NULL) { priv->satab_buffer = grlib_malloc((16 * 32) * 2); - if (priv->satab_buffer == NULL) - return -1; + if (priv->satab_buffer == NULL) { + retval = -1; + goto err; + } + /* Align to 512 bytes boundary */ + priv->sas_cpu = (struct gr1553rt_sa *) + (((unsigned int)priv->satab_buffer + 0x1ff) & ~0x1ff); } else { /* Addess already CPU-LOCAL */ priv->satab_buffer = priv->cfg.satab_buffer; + priv->sas_cpu = (struct gr1553rt_sa *)priv->satab_buffer; } - /* Align to 512 bytes boundary */ - priv->sas_cpu = (struct gr1553rt_sa *) - (((unsigned int)priv->satab_buffer + 0x1ff) & - ~0x1ff); /* Translate Address from CPU-LOCAL to HARDWARE (REMOTE) */ drvmgr_translate_check( @@ -890,8 +915,17 @@ static int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) (void **)&priv->sas_hw, 16 * 32); } + /* Verify alignment */ + if ((unsigned int)priv->sas_hw & (0x1ff)) { + retval = -2; + goto err; + } - return 0; +err: + if (retval) { + gr1553rt_sw_free(priv); + } + return retval; } void gr1553rt_sw_init(struct gr1553rt_priv *priv) @@ -937,7 +971,7 @@ void gr1553rt_sw_init(struct gr1553rt_priv *priv) int gr1553rt_config(void *rt, struct gr1553rt_cfg *cfg) { struct gr1553rt_priv *priv = rt; - + int retval = 0; if ( priv->started ) return -1; @@ -949,9 +983,9 @@ int gr1553rt_config(void *rt, struct gr1553rt_cfg *cfg) if ( cfg->rtaddress > 30 ) return -1; if ( (cfg->evlog_size & (cfg->evlog_size-1)) != 0) - return -1; /* SIZE: Not aligned to a power of 2 */ + return -2; /* SIZE: Not aligned to a power of 2 */ if ( ((unsigned int)priv->cfg.evlog_buffer & (cfg->evlog_size-1)) != 0 ) - return -1; /* Buffer: Not aligned to size */ + return -2; /* Buffer: Not aligned to size */ #if (RTBD_MAX > 0) if ( cfg->bd_count > RTBD_MAX ) return -1; @@ -962,8 +996,9 @@ int gr1553rt_config(void *rt, struct gr1553rt_cfg *cfg) /*** Adapt to new config ***/ - if ( gr1553rt_sw_alloc(priv) != 0 ) - return -1; + if ( (retval=gr1553rt_sw_alloc(priv)) != 0 ) { + return retval; + } gr1553rt_sw_init(priv); diff --git a/bsps/shared/grlib/amba/ahbstat.c b/bsps/shared/grlib/amba/ahbstat.c index af3d778feb..3ab0262e1e 100644 --- a/bsps/shared/grlib/amba/ahbstat.c +++ b/bsps/shared/grlib/amba/ahbstat.c @@ -46,12 +46,20 @@ int (*ahbstat_error)( uint32_t failing_address ) __attribute__((weak)) = NULL; +#define AHBSTAT_STS_ME_BIT 13 +#define AHBSTAT_STS_FW_BIT 12 +#define AHBSTAT_STS_CF_BIT 11 +#define AHBSTAT_STS_AF_BIT 10 #define AHBSTAT_STS_CE_BIT 9 #define AHBSTAT_STS_NE_BIT 8 #define AHBSTAT_STS_HW_BIT 7 #define AHBSTAT_STS_HM_BIT 3 #define AHBSTAT_STS_HS_BIT 0 +#define AHBSTAT_STS_ME (1 << AHBSTAT_STS_ME_BIT) +#define AHBSTAT_STS_FW (1 << AHBSTAT_STS_FW_BIT) +#define AHBSTAT_STS_CF (1 << AHBSTAT_STS_CF_BIT) +#define AHBSTAT_STS_AF (1 << AHBSTAT_STS_AF_BIT) #define AHBSTAT_STS_CE (1 << AHBSTAT_STS_CE_BIT) #define AHBSTAT_STS_NE (1 << AHBSTAT_STS_NE_BIT) #define AHBSTAT_STS_HW (1 << AHBSTAT_STS_HW_BIT) diff --git a/bsps/shared/grlib/amba/ambapp_names.c b/bsps/shared/grlib/amba/ambapp_names.c index 8d168f283b..4ccb0621e6 100644 --- a/bsps/shared/grlib/amba/ambapp_names.c +++ b/bsps/shared/grlib/amba/ambapp_names.c @@ -199,7 +199,26 @@ static ambapp_device_name GAISLER_devices[] = {GAISLER_TCCOP, "TCCOP"}, {GAISLER_SPIMASTER, "SPIMASTER"}, {GAISLER_SPISLAVE, "SPISLAVE"}, - {GAISLER_GRSRIO, "GRSRIO"}, + {GAISLER_GRSRIO, "GRSRIO"}, + {GAISLER_AHBLM2AHB, "AHBLM2AHB"}, + {GAISLER_AHBS2NOC, "AHBS2NOC"}, + {GAISLER_TCAU, "TCAU"}, + {GAISLER_GRTMDYNVCID, "GRTMDYNVCID"}, + {GAISLER_RNOCIRQPROP, "RNOCIRQPROP"}, + {GAISLER_FTADDR, "FTADDR"}, + {GAISLER_ATG, "ATG"}, + {GAISLER_DFITRACE, "DFITRACE"}, + {GAISLER_SELFTEST, "SELFTEST"}, + {GAISLER_DFIERRINJ, "DFIERRINJ"}, + {GAISLER_DFICHECK, "DFICHECK"}, + {GAISLER_GRCANFD, "GRCANFD"}, + {GAISLER_NIM, "NIM"}, + {GAISLER_BANDGAP, "BANDGAP"}, + {GAISLER_MPROT, "MPROT"}, + {GAISLER_ADC, "ADC"}, + {GAISLER_BO, "BO"}, + {GAISLER_DAC, "DAC"}, + {GAISLER_PLL, "PLL"}, {0, NULL} }; diff --git a/bsps/shared/grlib/btimer/tlib_ckinit.c b/bsps/shared/grlib/btimer/tlib_ckinit.c index 4f679984d8..5ac325052c 100644 --- a/bsps/shared/grlib/btimer/tlib_ckinit.c +++ b/bsps/shared/grlib/btimer/tlib_ckinit.c @@ -209,15 +209,14 @@ static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc) static rtems_device_driver simple_initialize_counter(void) { - uint64_t frequency; - unsigned int tick_hz; + unsigned int tick_hz, frequency; - frequency = 1000000; + tlib_get_freq(priv.tlib_tick, &frequency, NULL); tick_hz = rtems_configuration_get_microseconds_per_tick(); rtems_timecounter_simple_install( &priv.tc_simple, - frequency, + (uint64_t)frequency, tick_hz, simple_tlib_tc_get_timecount ); diff --git a/bsps/shared/grlib/can/canbtrs.c b/bsps/shared/grlib/can/canbtrs.c new file mode 100644 index 0000000000..307547475c --- /dev/null +++ b/bsps/shared/grlib/can/canbtrs.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup can + * + * @brief Common CAN baud-rate routines for OCCAN/GRCAN/GRCANFD controllers + * + * Implements common routines for calculating CAN baud-rate parameters from + * a user provided baud-rate speed. + */ + +/* + * Copyright (C) 2019, 2020 Cobham Gailer AB + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <grlib/canbtrs.h> + +/*#define GRLIB_CANBTRS_DEBUG*/ + +/* Calculate CAN baud-rate generation parameters from requested baud-rate */ +int grlib_canbtrs_calc_timing( + unsigned int baud, + unsigned int core_hz, + unsigned int sampl_pt, + struct grlib_canbtrs_ranges *br, + struct grlib_canbtrs_timing *timing + ) +{ + int best_error = 2000000000, best_tseg=0, best_scaler=0; + int tseg=0, tseg1=0, tseg2=0, sc, tmp, error; + + /* Default to 80% sample point */ + if ((sampl_pt < 50) || (sampl_pt > 99)) + sampl_pt = 80; + + /* step though all TSEG1+TSEG2 values possible */ + for (tseg = (br->min_tseg1 + br->min_tseg2); + tseg <= (br->max_tseg1 + br->max_tseg2); + tseg++) { + /* calculate scaler */ + tmp = ((br->divfactor + tseg) * baud); + sc = (core_hz * 2)/ tmp - core_hz / tmp; + if (sc <= 0 || sc > br->max_scaler) + continue; + if (br->has_bpr && + (((sc > 256 * 1) && (sc <= 256 * 2) && (sc & 0x1)) || + ((sc > 256 * 2) && (sc <= 256 * 4) && (sc & 0x3)) || + ((sc > 256 * 4) && (sc <= 256 * 8) && (sc & 0x7)))) + continue; + + error = baud - core_hz / (sc * (br->divfactor + tseg)); +#ifdef GRLIB_CANBTRS_DEBUG + printf(" baud=%d, tseg=%d, sc=%d, error=%d\n", + baud, tseg, sc, error); +#endif + if (error < 0) + error = -error; + + /* tseg is increasing, so we accept higher tseg with the same + * baudrate to get better sampling point. + */ + if (error <= best_error) { + best_error = error; + best_tseg = tseg; + best_scaler = sc; +#ifdef GRLIB_CANBTRS_DEBUG + printf(" ! best baud=%d\n", + core_hz/(sc * (br->divfactor + tseg))); +#endif + } + } + + /* return an error if 5% off baud-rate */ + if (best_error && (baud / best_error <= 5)) { + return -2; + } else if (!timing) { + return 0; /* nothing to store result in, but a valid bitrate can be calculated */ + } + + tseg2 = (best_tseg + br->divfactor) - + ((sampl_pt * (best_tseg + br->divfactor)) / 100); + if (tseg2 < br->min_tseg2) { + tseg2 = br->min_tseg2; + } else if (tseg2 > br->max_tseg2) { + tseg2 = br->max_tseg2; + } + + tseg1 = best_tseg - tseg2; + if (tseg1 > br->max_tseg1) { + tseg1 = br->max_tseg1; + tseg2 = best_tseg - tseg1; + } else if (tseg1 < br->min_tseg1) { + tseg1 = br->min_tseg1; + tseg2 = best_tseg - tseg1; + } + + /* Get scaler and BPR from pseudo SCALER clock */ + if (best_scaler <= 256) { + timing->scaler = best_scaler - 1; + timing->bpr = 0; + } else if (best_scaler <= 256 * 2) { + timing->scaler = ((best_scaler + 1) >> 1) - 1; + timing->bpr = 1; + } else if (best_scaler <= 256 * 4) { + timing->scaler = ((best_scaler + 1) >> 2) - 1; + timing->bpr = 2; + } else { + timing->scaler = ((best_scaler + 1) >> 3) - 1; + timing->bpr = 3; + } + + timing->ps1 = tseg1; + timing->ps2 = tseg2; + timing->rsj = 1; + +#ifdef GRLIB_CANBTRS_DEBUG + printf(" ! result: sc=%d,bpr=%d,ps1=%d,ps2=%d\n", timing->scaler, timing->bpr, timing->ps1, timing->ps2); +#endif + + return 0; +} diff --git a/bsps/shared/grlib/can/grcan.c b/bsps/shared/grlib/can/grcan.c index d69d99d85a..69f64cc285 100644 --- a/bsps/shared/grlib/can/grcan.c +++ b/bsps/shared/grlib/can/grcan.c @@ -1,7 +1,7 @@ /* * GRCAN driver * - * COPYRIGHT (c) 2007. + * COPYRIGHT (c) 2007-2019. * Cobham Gaisler AB. * * The license and distribution terms for this file may be @@ -18,18 +18,17 @@ #include <rtems/bspIo.h> #include <grlib/grcan.h> +#include <grlib/canbtrs.h> #include <drvmgr/drvmgr.h> #include <grlib/ambapp_bus.h> #include <grlib/ambapp.h> #include <grlib/grlib_impl.h> +#include "grcan_internal.h" /* Maximum number of GRCAN devices supported by driver */ #define GRCAN_COUNT_MAX 8 -#define WRAP_AROUND_TX_MSGS 1 -#define WRAP_AROUND_RX_MSGS 2 -#define GRCAN_MSG_SIZE sizeof(struct grcan_msg) #define BLOCK_SIZE (16*4) /* grcan needs to have it buffers aligned to 1k boundaries */ @@ -57,15 +56,6 @@ #define IRQ_MASK(irqno) #endif -#ifndef GRCAN_DEFAULT_BAUD - /* default to 500kbits/s */ - #define GRCAN_DEFAULT_BAUD 500000 -#endif - -#ifndef GRCAN_SAMPLING_POINT - #define GRCAN_SAMPLING_POINT 80 -#endif - /* Uncomment for debug output */ /****************** DEBUG Definitions ********************/ #define DBG_TX 2 @@ -88,70 +78,10 @@ int state2err[4] = { /* STATE_AHBERR */ GRCAN_RET_AHBERR }; -struct grcan_msg { - unsigned int head[2]; - unsigned char data[8]; -}; - -struct grcan_config { - struct grcan_timing timing; - struct grcan_selection selection; - int abort; - int silent; -}; - -struct grcan_priv { - struct drvmgr_dev *dev; /* Driver manager device */ - char devName[32]; /* Device Name */ - unsigned int baseaddr, ram_base; - struct grcan_regs *regs; - int irq; - int minor; - int open; - int started; - unsigned int channel; - int flushing; - unsigned int corefreq_hz; - - /* Circular DMA buffers */ - void *_rx, *_rx_hw; - void *_tx, *_tx_hw; - void *txbuf_adr; - void *rxbuf_adr; - struct grcan_msg *rx; - struct grcan_msg *tx; - unsigned int rxbuf_size; /* requested RX buf size in bytes */ - unsigned int txbuf_size; /* requested TX buf size in bytes */ - - int txblock, rxblock; - int txcomplete, rxcomplete; - - struct grcan_filter sfilter; - struct grcan_filter afilter; - int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */ - struct grcan_config config; - struct grcan_stats stats; - - rtems_id rx_sem, tx_sem, txempty_sem, dev_sem; - SPIN_DECLARE(devlock); -}; - static void __inline__ grcan_hw_reset(struct grcan_regs *regs); -static int grcan_hw_read_try( - struct grcan_priv *pDev, - struct grcan_regs *regs, - CANMsg *buffer, - int max); - -static int grcan_hw_write_try( - struct grcan_priv *pDev, - struct grcan_regs *regs, - CANMsg *buffer, - int count); - static void grcan_hw_config( - struct grcan_regs *regs, + struct grcan_priv *pDev, struct grcan_config *conf); static void grcan_hw_accept( @@ -164,21 +94,40 @@ static void grcan_hw_sync( static void grcan_interrupt(void *arg); -#ifdef GRCAN_REG_BYPASS_CACHE -#define READ_REG(address) grlib_read_uncached32((unsigned int)(address)) -#else -#define READ_REG(address) (*(volatile unsigned int *)(address)) -#endif +#define NELEM(a) ((int) (sizeof (a) / sizeof (a[0]))) -#ifdef GRCAN_DMA_BYPASS_CACHE -#define READ_DMA_WORD(address) grlib_read_uncached32((unsigned int)(address)) -#define READ_DMA_BYTE(address) grlib_read_uncached8((unsigned int)(address)) -#else -#define READ_DMA_WORD(address) (*(volatile unsigned int *)(address)) -#define READ_DMA_BYTE(address) (*(volatile unsigned char *)(address)) -#endif +/* GRCAN nominal boundaries for baud-rate paramters */ +struct grlib_canbtrs_ranges grcan_btrs_ranges = { + .max_scaler = 256*8, /* scaler is multiplied by BPR in steps 1,2,4,8 */ + .has_bpr = 1, + .divfactor = 2, + .min_tseg1 = 1, + .max_tseg1 = 15, + .min_tseg2 = 2, + .max_tseg2 = 8, +}; -#define NELEM(a) ((int) (sizeof (a) / sizeof (a[0]))) +/* GRCANFD nominal boundaries */ +struct grlib_canbtrs_ranges grcanfd_nom_btrs_ranges = { + .max_scaler = 256, + .has_bpr = 0, + .divfactor = 1, + .min_tseg1 = 2, + .max_tseg1 = 63, + .min_tseg2 = 2, + .max_tseg2 = 16, +}; + +/* GRCANFD flexible baud-rate boundaries */ +struct grlib_canbtrs_ranges grcanfd_fd_btrs_ranges = { + .max_scaler = 256, + .has_bpr = 0, + .divfactor = 1, + .min_tseg1 = 1, + .max_tseg1 = 15, + .min_tseg2 = 2, + .max_tseg2 = 8, +}; static int grcan_count = 0; static struct grcan_priv *priv_tab[GRCAN_COUNT_MAX]; @@ -202,6 +151,7 @@ struct amba_dev_id grcan_ids[] = { {VENDOR_GAISLER, GAISLER_GRCAN}, {VENDOR_GAISLER, GAISLER_GRHCAN}, + {VENDOR_GAISLER, GAISLER_GRCANFD}, {0, 0} /* Mark end of table */ }; @@ -294,6 +244,8 @@ int grcan_device_init(struct grcan_priv *pDev) pDev->irq = pnpinfo->irq; pDev->regs = (struct grcan_regs *)pnpinfo->apb_slv->start; pDev->minor = pDev->dev->minor_drv; + if (ambadev->id.device == GAISLER_GRCANFD) + pDev->fd_capable = 1; /* Get frequency in Hz */ if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &pDev->corefreq_hz) ) { @@ -373,7 +325,7 @@ static rtems_device_driver grcan_hw_start(struct grcan_priv *pDev) * and Setup timing */ if (pDev->config_changed) { - grcan_hw_config(pDev->regs, &pDev->config); + grcan_hw_config(pDev, &pDev->config); pDev->config_changed = 0; } @@ -456,9 +408,10 @@ static void grcan_sw_stop(struct grcan_priv *pDev) rtems_semaphore_release(pDev->txempty_sem); } -static void grcan_hw_config(struct grcan_regs *regs, struct grcan_config *conf) +static void grcan_hw_config(struct grcan_priv *pDev, struct grcan_config *conf) { unsigned int config = 0; + struct grcan_regs *regs = pDev->regs; /* Reset HurriCANe Core */ regs->ctrl = 0; @@ -479,13 +432,29 @@ static void grcan_hw_config(struct grcan_regs *regs, struct grcan_config *conf) config |= GRCAN_CFG_ENABLE1; /* Timing */ - config |= (conf->timing.bpr << GRCAN_CFG_BPR_BIT) & GRCAN_CFG_BPR; - config |= (conf->timing.rsj << GRCAN_CFG_RSJ_BIT) & GRCAN_CFG_RSJ; - config |= (conf->timing.ps1 << GRCAN_CFG_PS1_BIT) & GRCAN_CFG_PS1; - config |= (conf->timing.ps2 << GRCAN_CFG_PS2_BIT) & GRCAN_CFG_PS2; - config |= - (conf->timing.scaler << GRCAN_CFG_SCALER_BIT) & GRCAN_CFG_SCALER; - + if (!pDev->fd_capable) { + config |= (conf->timing.bpr << GRCAN_CFG_BPR_BIT) & + GRCAN_CFG_BPR; + config |= (conf->timing.rsj << GRCAN_CFG_RSJ_BIT) & + GRCAN_CFG_RSJ; + config |= (conf->timing.ps1 << GRCAN_CFG_PS1_BIT) & + GRCAN_CFG_PS1; + config |= (conf->timing.ps2 << GRCAN_CFG_PS2_BIT) & + GRCAN_CFG_PS2; + config |= (conf->timing.scaler << GRCAN_CFG_SCALER_BIT) & + GRCAN_CFG_SCALER; + } else { + regs->nbtr = + (conf->timing.scaler << GRCANFD_NBTR_SCALER_BIT) | + (conf->timing.ps1 << GRCANFD_NBTR_PS1_BIT) | + (conf->timing.ps2 << GRCANFD_NBTR_PS2_BIT) | + (conf->timing.rsj << GRCANFD_NBTR_SJW_BIT); + regs->fdbtr = + (conf->timing_fd.scaler << GRCANFD_FDBTR_SCALER_BIT) | + (conf->timing_fd.ps1 << GRCANFD_FDBTR_PS1_BIT) | + (conf->timing_fd.ps2 << GRCANFD_FDBTR_PS2_BIT) | + (conf->timing_fd.sjw << GRCANFD_FDBTR_SJW_BIT); + } /* Write configuration */ regs->conf = config; @@ -520,338 +489,7 @@ static void grcan_hw_sync(struct grcan_regs *regs, struct grcan_filter *sfilter) regs->smask = sfilter->mask; } -static unsigned int grcan_hw_rxavail( - unsigned int rp, - unsigned int wp, unsigned int size -) -{ - if (rp == wp) { - /* read pointer and write pointer is equal only - * when RX buffer is empty. - */ - return 0; - } - - if (wp > rp) { - return (wp - rp) / GRCAN_MSG_SIZE; - } else { - return (size - (rp - wp)) / GRCAN_MSG_SIZE; - } -} - -static unsigned int grcan_hw_txspace( - unsigned int rp, - unsigned int wp, - unsigned int size -) -{ - unsigned int left; - - if (rp == wp) { - /* read pointer and write pointer is equal only - * when TX buffer is empty. - */ - return size / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS; - } - - /* size - 4 - abs(read-write) */ - if (wp > rp) { - left = size - (wp - rp); - } else { - left = rp - wp; - } - - return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS; -} - -#define MIN_TSEG1 1 -#define MIN_TSEG2 2 -#define MAX_TSEG1 14 -#define MAX_TSEG2 8 - -static int grcan_calc_timing( - unsigned int baud, /* The requested BAUD to calculate timing for */ - unsigned int core_hz, /* Frequency in Hz of GRCAN Core */ - unsigned int sampl_pt, - struct grcan_timing *timing /* result is placed here */ -) -{ - int best_error = 1000000000; - int error; - int best_tseg = 0, best_brp = 0, brp = 0; - int tseg = 0, tseg1 = 0, tseg2 = 0; - int sjw = 1; - - /* Default to 90% */ - if ((sampl_pt < 50) || (sampl_pt > 99)) { - sampl_pt = GRCAN_SAMPLING_POINT; - } - - if ((baud < 5000) || (baud > 1000000)) { - /* invalid speed mode */ - return -1; - } - - /* find best match, return -2 if no good reg - * combination is available for this frequency - */ - - /* some heuristic specials */ - if (baud > ((1000000 + 500000) / 2)) - sampl_pt = 75; - - if (baud < ((12500 + 10000) / 2)) - sampl_pt = 75; - - /* tseg even = round down, odd = round up */ - for ( - tseg = (MIN_TSEG1 + MIN_TSEG2 + 2) * 2; - tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1; - tseg++ - ) { - brp = core_hz / ((1 + tseg / 2) * baud) + tseg % 2; - if ( - (brp <= 0) || - ((brp > 256 * 1) && (brp <= 256 * 2) && (brp & 0x1)) || - ((brp > 256 * 2) && (brp <= 256 * 4) && (brp & 0x3)) || - ((brp > 256 * 4) && (brp <= 256 * 8) && (brp & 0x7)) || - (brp > 256 * 8) - ) - continue; - - error = baud - core_hz / (brp * (1 + tseg / 2)); - if (error < 0) { - error = -error; - } - - if (error <= best_error) { - best_error = error; - best_tseg = tseg / 2; - best_brp = brp - 1; - } - } - - if (best_error && (baud / best_error < 10)) { - return -2; - } else if (!timing) - return 0; /* nothing to store result in, but a valid bitrate can be calculated */ - - tseg2 = best_tseg - (sampl_pt * (best_tseg + 1)) / 100; - - if (tseg2 < MIN_TSEG2) { - tseg2 = MIN_TSEG2; - } - - if (tseg2 > MAX_TSEG2) { - tseg2 = MAX_TSEG2; - } - - tseg1 = best_tseg - tseg2 - 2; - - if (tseg1 > MAX_TSEG1) { - tseg1 = MAX_TSEG1; - tseg2 = best_tseg - tseg1 - 2; - } - - /* Get scaler and BRP from pseudo BRP */ - if (best_brp <= 256) { - timing->scaler = best_brp; - timing->bpr = 0; - } else if (best_brp <= 256 * 2) { - timing->scaler = ((best_brp + 1) >> 1) - 1; - timing->bpr = 1; - } else if (best_brp <= 256 * 4) { - timing->scaler = ((best_brp + 1) >> 2) - 1; - timing->bpr = 2; - } else { - timing->scaler = ((best_brp + 1) >> 3) - 1; - timing->bpr = 3; - } - - timing->ps1 = tseg1 + 1; - timing->ps2 = tseg2; - timing->rsj = sjw; - - return 0; -} - -static int grcan_hw_read_try( - struct grcan_priv *pDev, - struct grcan_regs *regs, - CANMsg * buffer, - int max -) -{ - int i, j; - CANMsg *dest; - struct grcan_msg *source, tmp; - unsigned int wp, rp, size, rxmax, addr; - int trunk_msg_cnt; - - FUNCDBG(); - - wp = READ_REG(®s->rx0wr); - rp = READ_REG(®s->rx0rd); - - /* - * Due to hardware wrap around simplification write pointer will - * never reach the read pointer, at least a gap of 8 bytes. - * The only time they are equal is when the read pointer has - * reached the write pointer (empty buffer) - * - */ - if (wp != rp) { - /* Not empty, we have received chars... - * Read as much as possible from DMA buffer - */ - size = READ_REG(®s->rx0size); - - /* Get number of bytes available in RX buffer */ - trunk_msg_cnt = grcan_hw_rxavail(rp, wp, size); - - /* truncate size if user space buffer hasn't room for - * all received chars. - */ - if (trunk_msg_cnt > max) - trunk_msg_cnt = max; - - /* Read until i is 0 */ - i = trunk_msg_cnt; - - addr = (unsigned int)pDev->rx; - source = (struct grcan_msg *)(addr + rp); - dest = buffer; - rxmax = addr + (size - GRCAN_MSG_SIZE); - - /* Read as many can messages as possible */ - while (i > 0) { - /* Read CAN message from DMA buffer */ - tmp.head[0] = READ_DMA_WORD(&source->head[0]); - tmp.head[1] = READ_DMA_WORD(&source->head[1]); - if (tmp.head[1] & 0x4) { - DBGC(DBG_RX, "overrun\n"); - } - if (tmp.head[1] & 0x2) { - DBGC(DBG_RX, "bus-off mode\n"); - } - if (tmp.head[1] & 0x1) { - DBGC(DBG_RX, "error-passive mode\n"); - } - /* Convert one grcan CAN message to one "software" CAN message */ - dest->extended = tmp.head[0] >> 31; - dest->rtr = (tmp.head[0] >> 30) & 0x1; - if (dest->extended) { - dest->id = tmp.head[0] & 0x3fffffff; - } else { - dest->id = (tmp.head[0] >> 18) & 0xfff; - } - dest->len = tmp.head[1] >> 28; - for (j = 0; j < dest->len; j++) - dest->data[j] = READ_DMA_BYTE(&source->data[j]); - - /* wrap around if neccessary */ - source = - ((unsigned int)source >= rxmax) ? - (struct grcan_msg *)addr : source + 1; - dest++; /* straight user buffer */ - i--; - } - { - /* A bus off interrupt may have occured after checking pDev->started */ - SPIN_IRQFLAGS(oldLevel); - - SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); - if (pDev->started == STATE_STARTED) { - regs->rx0rd = (unsigned int) source - addr; - regs->rx0ctrl = GRCAN_RXCTRL_ENABLE; - } else { - DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); - trunk_msg_cnt = state2err[pDev->started]; - } - SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); - } - return trunk_msg_cnt; - } - return 0; -} - -static int grcan_hw_write_try( - struct grcan_priv *pDev, - struct grcan_regs *regs, - CANMsg * buffer, - int count -) -{ - unsigned int rp, wp, size, txmax, addr; - int ret; - struct grcan_msg *dest; - CANMsg *source; - int space_left; - unsigned int tmp; - int i; - - DBGC(DBG_TX, "\n"); - /*FUNCDBG(); */ - - rp = READ_REG(®s->tx0rd); - wp = READ_REG(®s->tx0wr); - size = READ_REG(®s->tx0size); - - space_left = grcan_hw_txspace(rp, wp, size); - - /* is circular fifo full? */ - if (space_left < 1) - return 0; - - /* Truncate size */ - if (space_left > count) - space_left = count; - ret = space_left; - - addr = (unsigned int)pDev->tx; - - dest = (struct grcan_msg *)(addr + wp); - source = (CANMsg *) buffer; - txmax = addr + (size - GRCAN_MSG_SIZE); - - while (space_left > 0) { - /* Convert and write CAN message to DMA buffer */ - if (source->extended) { - tmp = (1 << 31) | (source->id & 0x3fffffff); - } else { - tmp = (source->id & 0xfff) << 18; - } - if (source->rtr) - tmp |= (1 << 30); - dest->head[0] = tmp; - dest->head[1] = source->len << 28; - for (i = 0; i < source->len; i++) - dest->data[i] = source->data[i]; - source++; /* straight user buffer */ - dest = - ((unsigned int)dest >= txmax) ? - (struct grcan_msg *)addr : dest + 1; - space_left--; - } - - { - /* A bus off interrupt may have occured after checking pDev->started */ - SPIN_IRQFLAGS(oldLevel); - - SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); - if (pDev->started == STATE_STARTED) { - regs->tx0wr = (unsigned int) dest - addr; - regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; - } else { - DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); - ret = state2err[pDev->started]; - } - SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); - } - return ret; -} - -static int grcan_wait_rxdata(struct grcan_priv *pDev, int min) +int grcan_wait_rxdata(struct grcan_priv *pDev, int min) { unsigned int wp, rp, size, irq; unsigned int irq_trunk, dataavail; @@ -934,7 +572,7 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min) * min must be at least WRAP_AROUND_TX_MSGS less than max buffer capacity * (pDev->txbuf_size/GRCAN_MSG_SIZE) for this algo to work. */ -static int grcan_wait_txspace(struct grcan_priv *pDev, int min) +int grcan_wait_txspace(struct grcan_priv *pDev, int min) { int wait, state; unsigned int irq, rp, wp, size, space_left; @@ -1007,7 +645,7 @@ static int grcan_wait_txspace(struct grcan_priv *pDev, int min) return state2err[pDev->started]; } - /* At this point the TxIRQ has been masked, we ned not to mask it */ + /* At this point the TxIRQ has been masked, we need not to mask it */ return 0; } @@ -1211,6 +849,7 @@ void *grcan_open(int dev_no) struct grcan_priv *pDev; void *ret; union drvmgr_key_value *value; + struct grlib_canbtrs_ranges *br; FUNCDBG(); @@ -1282,7 +921,13 @@ void *grcan_open(int dev_no) pDev->sfilter.code = 0x00000000; /* Calculate default timing register values */ - grcan_calc_timing(GRCAN_DEFAULT_BAUD,pDev->corefreq_hz,GRCAN_SAMPLING_POINT,&pDev->config.timing); + if (pDev->fd_capable) + br = &grcanfd_nom_btrs_ranges; + else + br = &grcan_btrs_ranges; + grlib_canbtrs_calc_timing( + GRCAN_DEFAULT_BAUD, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, + br, (struct grlib_canbtrs_timing *)&pDev->config.timing); if ( grcan_alloc_buffers(pDev,1,1) ) { ret = NULL; @@ -1316,174 +961,11 @@ int grcan_close(void *d) return 0; } -int grcan_read(void *d, CANMsg *msg, size_t ucount) -{ - struct grcan_priv *pDev = d; - CANMsg *dest; - unsigned int count, left; - int nread; - int req_cnt; - - FUNCDBG(); - - dest = msg; - req_cnt = ucount; - - if ( (!dest) || (req_cnt<1) ) - return GRCAN_RET_INVARG; - - if (pDev->started != STATE_STARTED) { - return GRCAN_RET_NOTSTARTED; - } - - DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount); - - nread = grcan_hw_read_try(pDev,pDev->regs,dest,req_cnt); - if (nread < 0) { - return nread; - } - count = nread; - if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){ - if ( count > 0 ) { - /* Successfully received messages (at least one) */ - return count; - } - - /* nothing read, shall we block? */ - if ( !pDev->rxblock ) { - /* non-blocking mode */ - return GRCAN_RET_TIMEOUT; - } - } - - while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) { - if (!pDev->rxcomplete) { - left = 1; /* return as soon as there is one message available */ - } else { - left = req_cnt - count; /* return as soon as all data are available */ - - /* never wait for more than the half the maximum size of the receive buffer - * Why? We need some time to copy buffer before to catch up with hw, - * otherwise we would have to copy everything when the data has been - * received. - */ - if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){ - left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2; - } - } - - nread = grcan_wait_rxdata(pDev, left); - if (nread) { - /* The wait has been aborted, probably due to - * the device driver has been closed by another - * thread or a bus-off. Return error code. - */ - return nread; - } - - /* Try read bytes from circular buffer */ - nread = grcan_hw_read_try( - pDev, - pDev->regs, - dest+count, - req_cnt-count); - - if (nread < 0) { - /* The read was aborted by bus-off. */ - return nread; - } - count += nread; - } - /* no need to unmask IRQ as IRQ Handler do that for us. */ - return count; -} - -int grcan_write(void *d, CANMsg *msg, size_t ucount) +int grcan_canfd_capable(void *d) { struct grcan_priv *pDev = d; - CANMsg *source; - unsigned int count, left; - int nwritten; - int req_cnt; - - DBGC(DBG_TX,"\n"); - - if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing) - return GRCAN_RET_NOTSTARTED; - - req_cnt = ucount; - source = (CANMsg *) msg; - - /* check proper length and buffer pointer */ - if (( req_cnt < 1) || (source == NULL) ){ - return GRCAN_RET_INVARG; - } - - nwritten = grcan_hw_write_try(pDev,pDev->regs,source,req_cnt); - if (nwritten < 0) { - return nwritten; - } - count = nwritten; - if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) { - if ( count > 0 ) { - /* Successfully transmitted chars (at least one char) */ - return count; - } - - /* nothing written, shall we block? */ - if ( !pDev->txblock ) { - /* non-blocking mode */ - return GRCAN_RET_TIMEOUT; - } - } - /* if in txcomplete mode we need to transmit all chars */ - while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){ - /*** block until room to fit all or as much of transmit buffer as possible - * IRQ comes. Set up a valid IRQ point so that an IRQ is received - * when we can put a chunk of data into transmit fifo - */ - if ( !pDev->txcomplete ){ - left = 1; /* wait for anything to fit buffer */ - }else{ - left = req_cnt - count; /* wait for all data to fit in buffer */ - - /* never wait for more than the half the maximum size of the transmit - * buffer - * Why? We need some time to fill buffer before hw catches up. - */ - if ( left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2) ){ - left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2; - } - } - - nwritten = grcan_wait_txspace(pDev,left); - /* Wait until more room in transmit buffer */ - if ( nwritten ) { - /* The wait has been aborted, probably due to - * the device driver has been closed by another - * thread. To avoid deadlock we return directly - * with error status. - */ - return nwritten; - } - - /* Try read bytes from circular buffer */ - nwritten = grcan_hw_write_try( - pDev, - pDev->regs, - source+count, - req_cnt-count); - - if (nwritten < 0) { - /* Write was aborted by bus-off. */ - return nwritten; - } - count += nwritten; - } - /* no need to unmask IRQ as IRQ Handler do that for us. */ - - return count; + return pDev->fd_capable; } int grcan_start(void *d) @@ -1712,51 +1194,6 @@ int grcan_clr_stats(void *d) return 0; } -int grcan_set_speed(void *d, unsigned int speed) -{ - struct grcan_priv *pDev = d; - struct grcan_timing timing; - int ret; - - FUNCDBG(); - - /* cannot change speed during run mode */ - if (pDev->started == STATE_STARTED) - return -1; - - /* get speed rate from argument */ - ret = grcan_calc_timing(speed, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, &timing); - if ( ret ) - return -2; - - /* save timing/speed */ - pDev->config.timing = timing; - pDev->config_changed = 1; - - return 0; -} - -int grcan_set_btrs(void *d, const struct grcan_timing *timing) -{ - struct grcan_priv *pDev = d; - - FUNCDBG(); - - /* Set BTR registers manually - * Read GRCAN/HurriCANe Manual. - */ - if (pDev->started == STATE_STARTED) - return -1; - - if ( !timing ) - return -2; - - pDev->config.timing = *timing; - pDev->config_changed = 1; - - return 0; -} - int grcan_set_afilter(void *d, const struct grcan_filter *filter) { struct grcan_priv *pDev = d; diff --git a/bsps/shared/grlib/can/grcan_internal.h b/bsps/shared/grlib/can/grcan_internal.h new file mode 100644 index 0000000000..86ccda1997 --- /dev/null +++ b/bsps/shared/grlib/can/grcan_internal.h @@ -0,0 +1,140 @@ +/* + * GRCAN driver + * + * COPYRIGHT (c) 2007-2019. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef GRCAN_DEFAULT_BAUD + /* default to 500kbits/s */ + #define GRCAN_DEFAULT_BAUD 500000 +#endif + +#ifndef GRCAN_SAMPLING_POINT + #define GRCAN_SAMPLING_POINT 80 +#endif + +#define WRAP_AROUND_TX_MSGS 1 +#define WRAP_AROUND_RX_MSGS 2 +#define GRCAN_MSG_SIZE sizeof(struct grcan_msg) + +struct grcan_msg { + unsigned int head[2]; + unsigned char data[8]; +}; + +struct grcan_config { + struct grcan_timing timing; + struct grcanfd_timing timing_fd; + struct grcan_selection selection; + int abort; + int silent; +}; + +struct grcan_priv { + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + unsigned int baseaddr, ram_base; + struct grcan_regs *regs; + int irq; + int minor; + int open; + int started; + unsigned int channel; + int flushing; + unsigned int corefreq_hz; + int fd_capable; + + /* Circular DMA buffers */ + void *_rx, *_rx_hw; + void *_tx, *_tx_hw; + void *txbuf_adr; + void *rxbuf_adr; + struct grcan_msg *rx; + struct grcan_msg *tx; + unsigned int rxbuf_size; /* requested RX buf size in bytes */ + unsigned int txbuf_size; /* requested TX buf size in bytes */ + + int txblock, rxblock; + int txcomplete, rxcomplete; + + struct grcan_filter sfilter; + struct grcan_filter afilter; + int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */ + struct grcan_config config; + struct grcan_stats stats; + + rtems_id rx_sem, tx_sem, txempty_sem, dev_sem; + SPIN_DECLARE(devlock); +}; + +#ifdef GRCAN_REG_BYPASS_CACHE +#define READ_REG(address) grlib_read_uncached32((unsigned int)(address)) +#else +#define READ_REG(address) (*(volatile unsigned int *)(address)) +#endif + +#ifdef GRCAN_DMA_BYPASS_CACHE +#define READ_DMA_DOUBLE(address) grlib_read_uncached64((uint64_t *)(address)) +#define READ_DMA_WORD(address) grlib_read_uncached32((unsigned int)(address)) +#define READ_DMA_BYTE(address) grlib_read_uncached8((unsigned int)(address)) +#else +#define READ_DMA_DOUBLE(address) (*(volatile uint64_t *)(address)) +#define READ_DMA_WORD(address) (*(volatile unsigned int *)(address)) +#define READ_DMA_BYTE(address) (*(volatile unsigned char *)(address)) +#endif + +extern int state2err[4]; +extern struct grlib_canbtrs_ranges grcan_btrs_ranges; +extern struct grlib_canbtrs_ranges grcanfd_nom_btrs_ranges; +extern struct grlib_canbtrs_ranges grcanfd_fd_btrs_ranges; + +int grcan_wait_rxdata(struct grcan_priv *pDev, int min); +int grcan_wait_txspace(struct grcan_priv *pDev, int min); + +static inline unsigned int grcan_hw_rxavail( + unsigned int rp, + unsigned int wp, + unsigned int size) +{ + if (rp == wp) { + /* read pointer and write pointer is equal only + * when RX buffer is empty. + */ + return 0; + } + + if (wp > rp) { + return (wp - rp) / GRCAN_MSG_SIZE; + } else { + return (size - (rp - wp)) / GRCAN_MSG_SIZE; + } +} + +static inline unsigned int grcan_hw_txspace( + unsigned int rp, + unsigned int wp, + unsigned int size) +{ + unsigned int left; + + if (rp == wp) { + /* read pointer and write pointer is equal only + * when TX buffer is empty. + */ + return size / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS; + } + + /* size - 4 - abs(read-write) */ + if (wp > rp) { + left = size - (wp - rp); + } else { + left = rp - wp; + } + + return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS; +} diff --git a/bsps/shared/grlib/can/grcanfd.c b/bsps/shared/grlib/can/grcanfd.c new file mode 100644 index 0000000000..00eb4b6432 --- /dev/null +++ b/bsps/shared/grlib/can/grcanfd.c @@ -0,0 +1,535 @@ +/* + * FD extenstions to the GRCAN driver + * + * COPYRIGHT (c) 2007-2019. + * Cobham Gaisler AB. + * + * 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 <bsp.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <rtems/bspIo.h> + +#include <grlib/grcan.h> +#include <grlib/canbtrs.h> +#include <drvmgr/drvmgr.h> +#include <grlib/ambapp_bus.h> +#include <grlib/ambapp.h> + +#include <grlib/grlib_impl.h> +#include "grcan_internal.h" + +/* Uncomment for debug output */ +/****************** DEBUG Definitions ********************/ +#define DBG_TX 2 +#define DBG_RX 4 +#define DBG_STATE 8 + +#define DEBUG_FLAGS (DBG_STATE | DBG_RX | DBG_TX ) +/* +#define DEBUG +#define DEBUGFUNCS +*/ +#include <grlib/debug_defs.h> + +/*********************************************************/ + +struct grcanfd_bd0 { + uint32_t head[2]; + uint64_t data0; /* variable size, from 1 to 8 dwords */ +}; + +struct grcanfd_bd1 { + unsigned long long data[2]; +}; + +static uint8_t dlc2len[16] = { + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 12, 16, 20, + 24, 32, 48, 64 +}; + +static uint8_t len2fddlc[14] = { + /* 12,13 */ 0x9, + /* 16,17 */ 0xA, + /* 20,21 */ 0xB, + /* 24,25 */ 0xC, + /* 28,29 */ -1, + /* 32,33 */ 0xD, + /* 36,37 */ -1, + /* 40,41 */ -1, + /* 44,45 */ -1, + /* 48,49 */ 0xE, + /* 52,53 */ -1, + /* 56,57 */ -1, + /* 60,61 */ -1, + /* 64,65 */ 0xF, +}; + +/* Convert length in bytes to descriptor length field */ +static inline uint8_t grcan_len2dlc(int len) +{ + if (len <= 8) + return len; + if (len > 64) + return -1; + if (len & 0x3) + return -1; + return len2fddlc[(len - 12) >> 2]; +} + +static inline int grcan_numbds(int len) +{ + return 1 + ((len + 7) >> 4); +} + +static int grcan_hw_read_try_fd( + struct grcan_priv *pDev, + struct grcan_regs *regs, + CANFDMsg * buffer, + int max) +{ + int j; + CANFDMsg *dest; + struct grcanfd_bd0 *source, tmp, *rxmax; + unsigned int wp, rp, size, addr; + int bds_hw_avail, bds_tot, bds, ret, dlc; + uint64_t *dp; + SPIN_IRQFLAGS(oldLevel); + + FUNCDBG(); + + wp = READ_REG(®s->rx0wr); + rp = READ_REG(®s->rx0rd); + + /* + * Due to hardware wrap around simplification write pointer will + * never reach the read pointer, at least a gap of 8 bytes. + * The only time they are equal is when the read pointer has + * reached the write pointer (empty buffer) + * + */ + if (wp != rp) { + /* Not empty, we have received chars... + * Read as much as possible from DMA buffer + */ + size = READ_REG(®s->rx0size); + + /* Get number of bytes available in RX buffer */ + bds_hw_avail = grcan_hw_rxavail(rp, wp, size); + + addr = (unsigned int)pDev->rx; + source = (struct grcanfd_bd0 *)(addr + rp); + dest = buffer; + rxmax = (struct grcanfd_bd0 *)(addr + size); + ret = bds_tot = 0; + + /* Read as many can messages as possible */ + while ((ret < max) && (bds_tot < bds_hw_avail)) { + /* Read CAN message from DMA buffer */ + *(uint64_t *)&tmp = READ_DMA_DOUBLE(source); + if (tmp.head[1] & 0x4) { + DBGC(DBG_RX, "overrun\n"); + } + if (tmp.head[1] & 0x2) { + DBGC(DBG_RX, "bus-off mode\n"); + } + if (tmp.head[1] & 0x1) { + DBGC(DBG_RX, "error-passive mode\n"); + } + /* Convert one grcan CAN message to one "software" CAN message */ + dest->extended = tmp.head[0] >> 31; + dest->rtr = (tmp.head[0] >> 30) & 0x1; + if (dest->extended) { + dest->id = tmp.head[0] & 0x3fffffff; + } else { + dest->id = (tmp.head[0] >> 18) & 0xfff; + } + dest->fdopts = (tmp.head[1] >> 25) & GRCAN_FDMASK; + dlc = tmp.head[1] >> 28; + if (dest->fdopts & GRCAN_FDOPT_FDFRM) { + dest->len = dlc2len[dlc]; + } else { + dest->len = dlc; + if (dlc > 8) + dest->len = 8; + } + + dp = (uint64_t *)&source->data0; + for (j = 0; j < ((dest->len + 7) / 8); j++) { + dest->data.dwords[j] = READ_DMA_DOUBLE(dp); + if (++dp >= (uint64_t *)rxmax) + dp = (uint64_t *)addr; /* wrap around */ + } + + /* wrap around if neccessary */ + bds = grcan_numbds(dest->len); + source += bds; + if (source >= rxmax) { + source = (struct grcanfd_bd0 *) + ((void *)source - size); + } + dest++; /* straight user buffer */ + ret++; + bds_tot += bds; + } + + /* A bus off interrupt may have occured after checking pDev->started */ + SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); + if (pDev->started == STATE_STARTED) { + regs->rx0rd = (unsigned int) source - addr; + regs->rx0ctrl = GRCAN_RXCTRL_ENABLE; + } else { + DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); + ret = state2err[pDev->started]; + } + SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); + + return ret; + } + return 0; +} + +int grcanfd_read(void *d, CANFDMsg *msg, size_t ucount) +{ + struct grcan_priv *pDev = d; + CANFDMsg *dest; + unsigned int count, left; + int nread; + int req_cnt; + + FUNCDBG(); + + dest = msg; + req_cnt = ucount; + + if ( (!dest) || (req_cnt<1) ) + return GRCAN_RET_INVARG; + + if (pDev->started != STATE_STARTED) { + return GRCAN_RET_NOTSTARTED; + } + + DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount); + + nread = grcan_hw_read_try_fd(pDev,pDev->regs,dest,req_cnt); + if (nread < 0) { + return nread; + } + count = nread; + if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){ + if ( count > 0 ) { + /* Successfully received messages (at least one) */ + return count; + } + + /* nothing read, shall we block? */ + if ( !pDev->rxblock ) { + /* non-blocking mode */ + return GRCAN_RET_TIMEOUT; + } + } + + while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) { + if (!pDev->rxcomplete) { + left = 1; /* return as soon as there is one message available */ + } else { + left = req_cnt - count; /* return as soon as all data are available */ + + /* never wait for more than the half the maximum size of the receive buffer + * Why? We need some time to copy buffer before to catch up with hw, + * otherwise we would have to copy everything when the data has been + * received. + */ + if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){ + left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2; + } + } + + nread = grcan_wait_rxdata(pDev, left); + if (nread) { + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread or a bus-off. Return error code. + */ + return nread; + } + + /* Try read bytes from circular buffer */ + nread = grcan_hw_read_try_fd( + pDev, + pDev->regs, + dest+count, + req_cnt-count); + + if (nread < 0) { + /* The read was aborted by bus-off. */ + return nread; + } + count += nread; + } + /* no need to unmask IRQ as IRQ Handler do that for us. */ + return count; +} + +static int grcan_hw_write_try_fd( + struct grcan_priv *pDev, + struct grcan_regs *regs, + CANFDMsg *buffer, + int count) +{ + unsigned int rp, wp, size, addr; + int ret; + struct grcanfd_bd0 *dest, *txmax; + CANFDMsg *source = (CANFDMsg *) buffer; + int space_left; + unsigned int tmp; + int i, bds; + uint64_t *dp; + uint8_t dlc; + SPIN_IRQFLAGS(oldLevel); + + DBGC(DBG_TX, "\n"); + + rp = READ_REG(®s->tx0rd); + wp = READ_REG(®s->tx0wr); + size = READ_REG(®s->tx0size); + space_left = grcan_hw_txspace(rp, wp, size); + + addr = (unsigned int)pDev->tx; + dest = (struct grcanfd_bd0 *)(addr + wp); + txmax = (struct grcanfd_bd0 *)(addr + size); + ret = 0; + + while (source < &buffer[count]) { + /* Get the number of descriptors to wait for */ + if (source->fdopts & GRCAN_FDOPT_FDFRM) + bds = grcan_numbds(source->len); /* next msg's buffers */ + else + bds = 1; + if (space_left < bds) + break; + + /* Convert and write CAN message to DMA buffer */ + dlc = grcan_len2dlc(source->len); + if (dlc < 0) { + /* Bad user input. Report the number of written messages + * or an error when non sent. + */ + if (ret <= 0) + return GRCAN_RET_INVARG; + break; + } + dest->head[1] = (dlc << 28) | + ((source->fdopts & GRCAN_FDMASK) << 25); + dp = &dest->data0; + for (i = 0; i < ((source->len + 7) / 8); i++) { + *dp++ = source->data.dwords[i]; + if (dp >= (uint64_t *)txmax) + dp = (uint64_t *)addr; /* wrap around */ + } + if (source->extended) { + tmp = (1 << 31) | (source->id & 0x3fffffff); + } else { + tmp = (source->id & 0xfff) << 18; + } + if (source->rtr) + tmp |= (1 << 30); + dest->head[0] = tmp; + source++; /* straight user buffer */ + dest += bds; + if (dest >= txmax) + dest = (struct grcanfd_bd0 *)((void *)dest - size); + space_left -= bds; + ret++; + } + + /* A bus off interrupt may have occured after checking pDev->started */ + SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); + if (pDev->started == STATE_STARTED) { + regs->tx0wr = (unsigned int) dest - addr; + regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; + } else { + DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); + ret = state2err[pDev->started]; + } + SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); + + return ret; +} + +int grcanfd_write( + void *d, + CANFDMsg *msg, + size_t ucount) +{ + struct grcan_priv *pDev = d; + CANFDMsg *source, *curr; + unsigned int count, left; + int nwritten; + int req_cnt; + + DBGC(DBG_TX,"\n"); + + if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing) + return GRCAN_RET_NOTSTARTED; + + req_cnt = ucount; + curr = source = (CANFDMsg *) msg; + + /* check proper length and buffer pointer */ + if (( req_cnt < 1) || (source == NULL) ){ + return GRCAN_RET_INVARG; + } + + nwritten = grcan_hw_write_try_fd(pDev,pDev->regs,source,req_cnt); + if (nwritten < 0) { + return nwritten; + } + count = nwritten; + if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) { + if ( count > 0 ) { + /* Successfully transmitted chars (at least one char) */ + return count; + } + + /* nothing written, shall we block? */ + if ( !pDev->txblock ) { + /* non-blocking mode */ + return GRCAN_RET_TIMEOUT; + } + } + + /* if in txcomplete mode we need to transmit all chars */ + while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){ + /*** block until room to fit all or as much of transmit buffer + * as possible before IRQ comes. Set up a valid IRQ point so + * that an IRQ is triggered when we can put a chunk of data + * into transmit fifo. + */ + + /* Get the number of descriptors to wait for */ + curr = &source[count]; + if (curr->fdopts & GRCAN_FDOPT_FDFRM) + left = grcan_numbds(curr->len); /* next msg's buffers */ + else + left = 1; + + if (pDev->txcomplete) { + /* Wait for all messages to fit into descriptor table. + * Assume all following msgs are single descriptors. + */ + left += req_cnt - count - 1; + if (left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2)) { + left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2; + } + + } + + nwritten = grcan_wait_txspace(pDev,left); + /* Wait until more room in transmit buffer */ + if ( nwritten ) { + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread. To avoid deadlock we return directly + * with error status. + */ + return nwritten; + } + + /* Try read bytes from circular buffer */ + nwritten = grcan_hw_write_try_fd( + pDev, + pDev->regs, + source+count, + req_cnt-count); + + if (nwritten < 0) { + /* Write was aborted by bus-off. */ + return nwritten; + } + count += nwritten; + } + /* no need to unmask IRQ as IRQ Handler do that for us. */ + + return count; +} + +int grcanfd_set_speed(void *d, unsigned int nom_hz, unsigned int fd_hz) +{ + struct grcan_priv *pDev = d; + struct grlib_canbtrs_timing nom, fd; + int ret; + + FUNCDBG(); + + /* cannot change speed during run mode */ + if ((pDev->started == STATE_STARTED) || !pDev->fd_capable) + return -1; + + /* get speed rate from argument */ + ret = grlib_canbtrs_calc_timing( + nom_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, + &grcanfd_nom_btrs_ranges, &nom); + if ( ret ) + return -2; + ret = grlib_canbtrs_calc_timing( + fd_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, + &grcanfd_fd_btrs_ranges, &fd); + if ( ret ) + return -2; + + /* save timing/speed */ + pDev->config.timing = *(struct grcan_timing *)&nom; + pDev->config.timing_fd.scaler = fd.scaler; + pDev->config.timing_fd.ps1 = fd.ps1; + pDev->config.timing_fd.ps2 = fd.ps2; + pDev->config.timing_fd.sjw = fd.rsj; + pDev->config.timing_fd.resv_zero = 0; + pDev->config_changed = 1; + + return 0; + +} + +int grcanfd_set_btrs( + void *d, + const struct grcanfd_timing *nominal, + const struct grcanfd_timing *fd) +{ + struct grcan_priv *pDev = d; + + FUNCDBG(); + + /* Set BTR registers manually + * Read GRCAN/HurriCANe Manual. + */ + if ((pDev->started == STATE_STARTED) || !pDev->fd_capable) + return -1; + + if (!nominal) + return -2; + + pDev->config.timing.scaler = nominal->scaler; + pDev->config.timing.ps1 = nominal->ps1; + pDev->config.timing.ps2 = nominal->ps2; + pDev->config.timing.rsj = nominal->sjw; + pDev->config.timing.bpr = 0; + if (fd) { + pDev->config.timing_fd = *fd; + } else { + memset(&pDev->config.timing_fd, 0, + sizeof(struct grcanfd_timing)); + } + pDev->config_changed = 1; + + return 0; +} diff --git a/bsps/shared/grlib/can/grcanstd.c b/bsps/shared/grlib/can/grcanstd.c new file mode 100644 index 0000000000..b92c15a32b --- /dev/null +++ b/bsps/shared/grlib/can/grcanstd.c @@ -0,0 +1,435 @@ +/* + * non-FD specific function for GRCAN driver + * + * COPYRIGHT (c) 2007-2019. + * Cobham Gaisler AB. + * + * 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 <bsp.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <rtems/bspIo.h> + +#include <grlib/grcan.h> +#include <grlib/canbtrs.h> +#include <drvmgr/drvmgr.h> +#include <grlib/ambapp_bus.h> +#include <grlib/ambapp.h> + +#include <grlib/grlib_impl.h> +#include "grcan_internal.h" + +/* Uncomment for debug output */ +/****************** DEBUG Definitions ********************/ +#define DBG_TX 2 +#define DBG_RX 4 +#define DBG_STATE 8 + +#define DEBUG_FLAGS (DBG_STATE | DBG_RX | DBG_TX ) +/* +#define DEBUG +#define DEBUGFUNCS +*/ +#include <grlib/debug_defs.h> + +static int grcan_hw_read_try( + struct grcan_priv *pDev, + struct grcan_regs *regs, + CANMsg * buffer, + int max +) +{ + int i, j; + CANMsg *dest; + struct grcan_msg *source, tmp; + unsigned int wp, rp, size, rxmax, addr; + int trunk_msg_cnt; + + FUNCDBG(); + + wp = READ_REG(®s->rx0wr); + rp = READ_REG(®s->rx0rd); + + /* + * Due to hardware wrap around simplification write pointer will + * never reach the read pointer, at least a gap of 8 bytes. + * The only time they are equal is when the read pointer has + * reached the write pointer (empty buffer) + * + */ + if (wp != rp) { + /* Not empty, we have received chars... + * Read as much as possible from DMA buffer + */ + size = READ_REG(®s->rx0size); + + /* Get number of bytes available in RX buffer */ + trunk_msg_cnt = grcan_hw_rxavail(rp, wp, size); + + /* truncate size if user space buffer hasn't room for + * all received chars. + */ + if (trunk_msg_cnt > max) + trunk_msg_cnt = max; + + /* Read until i is 0 */ + i = trunk_msg_cnt; + + addr = (unsigned int)pDev->rx; + source = (struct grcan_msg *)(addr + rp); + dest = buffer; + rxmax = addr + (size - GRCAN_MSG_SIZE); + + /* Read as many can messages as possible */ + while (i > 0) { + /* Read CAN message from DMA buffer */ + tmp.head[0] = READ_DMA_WORD(&source->head[0]); + tmp.head[1] = READ_DMA_WORD(&source->head[1]); + if (tmp.head[1] & 0x4) { + DBGC(DBG_RX, "overrun\n"); + } + if (tmp.head[1] & 0x2) { + DBGC(DBG_RX, "bus-off mode\n"); + } + if (tmp.head[1] & 0x1) { + DBGC(DBG_RX, "error-passive mode\n"); + } + /* Convert one grcan CAN message to one "software" CAN message */ + dest->extended = tmp.head[0] >> 31; + dest->rtr = (tmp.head[0] >> 30) & 0x1; + if (dest->extended) { + dest->id = tmp.head[0] & 0x3fffffff; + } else { + dest->id = (tmp.head[0] >> 18) & 0xfff; + } + dest->len = tmp.head[1] >> 28; + for (j = 0; j < dest->len; j++) + dest->data[j] = READ_DMA_BYTE(&source->data[j]); + + /* wrap around if neccessary */ + source = + ((unsigned int)source >= rxmax) ? + (struct grcan_msg *)addr : source + 1; + dest++; /* straight user buffer */ + i--; + } + { + /* A bus off interrupt may have occured after checking pDev->started */ + SPIN_IRQFLAGS(oldLevel); + + SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); + if (pDev->started == STATE_STARTED) { + regs->rx0rd = (unsigned int) source - addr; + regs->rx0ctrl = GRCAN_RXCTRL_ENABLE; + } else { + DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); + trunk_msg_cnt = state2err[pDev->started]; + } + SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); + } + return trunk_msg_cnt; + } + return 0; +} + +static int grcan_hw_write_try( + struct grcan_priv *pDev, + struct grcan_regs *regs, + CANMsg * buffer, + int count +) +{ + unsigned int rp, wp, size, txmax, addr; + int ret; + struct grcan_msg *dest; + CANMsg *source; + int space_left; + unsigned int tmp; + int i; + + DBGC(DBG_TX, "\n"); + /*FUNCDBG(); */ + + rp = READ_REG(®s->tx0rd); + wp = READ_REG(®s->tx0wr); + size = READ_REG(®s->tx0size); + + space_left = grcan_hw_txspace(rp, wp, size); + + /* is circular fifo full? */ + if (space_left < 1) + return 0; + + /* Truncate size */ + if (space_left > count) + space_left = count; + ret = space_left; + + addr = (unsigned int)pDev->tx; + + dest = (struct grcan_msg *)(addr + wp); + source = (CANMsg *) buffer; + txmax = addr + (size - GRCAN_MSG_SIZE); + + while (space_left > 0) { + /* Convert and write CAN message to DMA buffer */ + if (source->extended) { + tmp = (1 << 31) | (source->id & 0x3fffffff); + } else { + tmp = (source->id & 0xfff) << 18; + } + if (source->rtr) + tmp |= (1 << 30); + dest->head[0] = tmp; + dest->head[1] = source->len << 28; + for (i = 0; i < source->len; i++) + dest->data[i] = source->data[i]; + source++; /* straight user buffer */ + dest = + ((unsigned int)dest >= txmax) ? + (struct grcan_msg *)addr : dest + 1; + space_left--; + } + + { + /* A bus off interrupt may have occured after checking pDev->started */ + SPIN_IRQFLAGS(oldLevel); + + SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); + if (pDev->started == STATE_STARTED) { + regs->tx0wr = (unsigned int) dest - addr; + regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; + } else { + DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); + ret = state2err[pDev->started]; + } + SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); + } + return ret; +} + + +int grcan_read(void *d, CANMsg *msg, size_t ucount) +{ + struct grcan_priv *pDev = d; + CANMsg *dest; + unsigned int count, left; + int nread; + int req_cnt; + + FUNCDBG(); + + dest = msg; + req_cnt = ucount; + + if ( (!dest) || (req_cnt<1) ) + return GRCAN_RET_INVARG; + + if (pDev->started != STATE_STARTED) { + return GRCAN_RET_NOTSTARTED; + } + + DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount); + + nread = grcan_hw_read_try(pDev,pDev->regs,dest,req_cnt); + if (nread < 0) { + return nread; + } + count = nread; + if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){ + if ( count > 0 ) { + /* Successfully received messages (at least one) */ + return count; + } + + /* nothing read, shall we block? */ + if ( !pDev->rxblock ) { + /* non-blocking mode */ + return GRCAN_RET_TIMEOUT; + } + } + + while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) { + if (!pDev->rxcomplete) { + left = 1; /* return as soon as there is one message available */ + } else { + left = req_cnt - count; /* return as soon as all data are available */ + + /* never wait for more than the half the maximum size of the receive buffer + * Why? We need some time to copy buffer before to catch up with hw, + * otherwise we would have to copy everything when the data has been + * received. + */ + if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){ + left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2; + } + } + + nread = grcan_wait_rxdata(pDev, left); + if (nread) { + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread or a bus-off. Return error code. + */ + return nread; + } + + /* Try read bytes from circular buffer */ + nread = grcan_hw_read_try( + pDev, + pDev->regs, + dest+count, + req_cnt-count); + + if (nread < 0) { + /* The read was aborted by bus-off. */ + return nread; + } + count += nread; + } + /* no need to unmask IRQ as IRQ Handler do that for us. */ + return count; +} + +int grcan_write(void *d, CANMsg *msg, size_t ucount) +{ + struct grcan_priv *pDev = d; + CANMsg *source; + unsigned int count, left; + int nwritten; + int req_cnt; + + DBGC(DBG_TX,"\n"); + + if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing) + return GRCAN_RET_NOTSTARTED; + + req_cnt = ucount; + source = (CANMsg *) msg; + + /* check proper length and buffer pointer */ + if (( req_cnt < 1) || (source == NULL) ){ + return GRCAN_RET_INVARG; + } + + nwritten = grcan_hw_write_try(pDev,pDev->regs,source,req_cnt); + if (nwritten < 0) { + return nwritten; + } + count = nwritten; + if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) { + if ( count > 0 ) { + /* Successfully transmitted chars (at least one char) */ + return count; + } + + /* nothing written, shall we block? */ + if ( !pDev->txblock ) { + /* non-blocking mode */ + return GRCAN_RET_TIMEOUT; + } + } + + /* if in txcomplete mode we need to transmit all chars */ + while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){ + /*** block until room to fit all or as much of transmit buffer as possible + * IRQ comes. Set up a valid IRQ point so that an IRQ is received + * when we can put a chunk of data into transmit fifo + */ + if ( !pDev->txcomplete ){ + left = 1; /* wait for anything to fit buffer */ + }else{ + left = req_cnt - count; /* wait for all data to fit in buffer */ + + /* never wait for more than the half the maximum size of the transmit + * buffer + * Why? We need some time to fill buffer before hw catches up. + */ + if ( left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2) ){ + left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2; + } + } + + nwritten = grcan_wait_txspace(pDev,left); + /* Wait until more room in transmit buffer */ + if ( nwritten ) { + /* The wait has been aborted, probably due to + * the device driver has been closed by another + * thread. To avoid deadlock we return directly + * with error status. + */ + return nwritten; + } + + /* Try read bytes from circular buffer */ + nwritten = grcan_hw_write_try( + pDev, + pDev->regs, + source+count, + req_cnt-count); + + if (nwritten < 0) { + /* Write was aborted by bus-off. */ + return nwritten; + } + count += nwritten; + } + /* no need to unmask IRQ as IRQ Handler do that for us. */ + + return count; +} + + +int grcan_set_speed(void *d, unsigned int speed) +{ + struct grcan_priv *pDev = d; + struct grcan_timing timing; + int ret; + + FUNCDBG(); + + /* cannot change speed during run mode */ + if ((pDev->started == STATE_STARTED) || pDev->fd_capable) + return -1; + + /* get speed rate from argument */ + ret = grlib_canbtrs_calc_timing( + speed, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, + &grcan_btrs_ranges, (struct grlib_canbtrs_timing *)&timing); + if (ret) + return -2; + + /* save timing/speed */ + pDev->config.timing = timing; + pDev->config_changed = 1; + + return 0; +} + +int grcan_set_btrs(void *d, const struct grcan_timing *timing) +{ + struct grcan_priv *pDev = d; + + FUNCDBG(); + + /* Set BTR registers manually + * Read GRCAN/HurriCANe Manual. + */ + if ((pDev->started == STATE_STARTED) || pDev->fd_capable) + return -1; + + if ( !timing ) + return -2; + + pDev->config.timing = *timing; + pDev->config_changed = 1; + + return 0; +} diff --git a/bsps/shared/grlib/can/occan.c b/bsps/shared/grlib/can/occan.c index 59b4f234f6..01537f9390 100644 --- a/bsps/shared/grlib/can/occan.c +++ b/bsps/shared/grlib/can/occan.c @@ -19,6 +19,7 @@ #include <drvmgr/drvmgr.h> #include <grlib/ambapp_bus.h> #include <grlib/occan.h> +#include <grlib/canbtrs.h> #include <grlib/grlib_impl.h> @@ -185,19 +186,20 @@ typedef struct { #define pelican_regs pelican32_regs #endif +/* Default sampling point in % */ +#define OCCAN_SAMPLING_POINT 90 + +/* OCCAN baud-rate paramter boundaries */ +struct grlib_canbtrs_ranges occan_btrs_ranges = { + .max_scaler = 64, + .has_bpr = 0, + .divfactor = 1, + .min_tseg1 = 1, + .max_tseg1 = 16, + .min_tseg2 = 1, + .max_tseg2 = 8, +}; -#define MAX_TSEG2 7 -#define MAX_TSEG1 15 - -#if 0 -typedef struct { - unsigned char brp; - unsigned char sjw; - unsigned char tseg1; - unsigned char tseg2; - unsigned char sam; -} occan_speed_regs; -#endif typedef struct { unsigned char btr0; unsigned char btr1; @@ -251,7 +253,9 @@ static CANMsg *occan_fifo_claim_get(occan_fifo *fifo); static void occan_fifo_clr(occan_fifo *fifo); /**** Hardware related Interface ****/ -static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result); +static void convert_timing_to_btrs( + struct grlib_canbtrs_timing *t, + occan_speed_regs *btrs); static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing); static void pelican_init(occan_priv *priv); static void pelican_open(occan_priv *priv); @@ -765,6 +769,7 @@ static void pelican_init(occan_priv *priv){ static void pelican_open(occan_priv *priv){ int ret; + struct grlib_canbtrs_timing timing; /* Set defaults */ priv->speed = OCCAN_SPEED_250K; @@ -783,13 +788,19 @@ static void pelican_open(occan_priv *priv){ */ WRITE_REG(priv, &priv->regs->clkdiv, (1<<PELICAN_CDR_MODE_BITS) | (DEFAULT_CLKDIV & PELICAN_CDR_DIV)); - ret = occan_calc_speedregs(priv->sys_freq_hz,priv->speed,&priv->timing); - if ( ret ){ + ret = grlib_canbtrs_calc_timing( + priv->speed, priv->sys_freq_hz, + OCCAN_SAMPLING_POINT, &occan_btrs_ranges, + (struct grlib_canbtrs_timing *)&timing); + if ( ret ) { /* failed to set speed for this system freq, try with 50K instead */ priv->speed = OCCAN_SPEED_50K; - occan_calc_speedregs(priv->sys_freq_hz, priv->speed, - &priv->timing); + grlib_canbtrs_calc_timing( + priv->speed, priv->sys_freq_hz, + OCCAN_SAMPLING_POINT, &occan_btrs_ranges, + (struct grlib_canbtrs_timing *)&timing); } + convert_timing_to_btrs(&timing, &priv->timing); /* disable all interrupts */ WRITE_REG(priv, &priv->regs->inten, 0); @@ -983,97 +994,13 @@ static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned WRITE_REG(priv, amask3, amask[3]); } - -/* This function calculates BTR0 and BTR1 values for a given bitrate. - * - * Set communication parameters. - * \param clock_hz OC_CAN Core frequency in Hz. - * \param rate Requested baud rate in bits/second. - * \param result Pointer to where resulting BTRs will be stored. - * \return zero if successful to calculate a baud rate. - */ -static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result) +static void convert_timing_to_btrs( + struct grlib_canbtrs_timing *t, + occan_speed_regs *btrs) { - int best_error = 1000000000; - int error; - int best_tseg=0, best_brp=0, brp=0; - int tseg=0, tseg1=0, tseg2=0; - int sjw = 0; - int clock = clock_hz / 2; - int sampl_pt = 90; - - if ( (rate<5000) || (rate>1000000) ){ - /* invalid speed mode */ - return -1; - } - - /* find best match, return -2 if no good reg - * combination is available for this frequency */ - - /* some heuristic specials */ - if (rate > ((1000000 + 500000) / 2)) - sampl_pt = 75; - - if (rate < ((12500 + 10000) / 2)) - sampl_pt = 75; - - if (rate < ((100000 + 125000) / 2)) - sjw = 1; - - /* tseg even = round down, odd = round up */ - for (tseg = (0 + 0 + 2) * 2; - tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1; - tseg++) - { - brp = clock / ((1 + tseg / 2) * rate) + tseg % 2; - if ((brp == 0) || (brp > 64)) - continue; - - error = rate - clock / (brp * (1 + tseg / 2)); - if (error < 0) - { - error = -error; - } - - if (error <= best_error) - { - best_error = error; - best_tseg = tseg/2; - best_brp = brp-1; - } - } - - if (best_error && (rate / best_error < 10)) - { - printk("OCCAN: bitrate %d is not possible with %d Hz clock\n\r",rate, clock); - return -2; - }else if ( !result ) - return 0; /* nothing to store result in, but a valid bitrate can be calculated */ - - tseg2 = best_tseg - (sampl_pt * (best_tseg + 1)) / 100; - - if (tseg2 < 0) - { - tseg2 = 0; - } - - if (tseg2 > MAX_TSEG2) - { - tseg2 = MAX_TSEG2; - } - - tseg1 = best_tseg - tseg2 - 2; - - if (tseg1 > MAX_TSEG1) - { - tseg1 = MAX_TSEG1; - tseg2 = best_tseg - tseg1 - 2; - } - - result->btr0 = (sjw<<OCCAN_BUSTIM_SJW_BIT) | (best_brp&OCCAN_BUSTIM_BRP); - result->btr1 = (0<<7) | (tseg2<<OCCAN_BUSTIM_TSEG2_BIT) | tseg1; - - return 0; + btrs->btr0 = (t->rsj << OCCAN_BUSTIM_SJW_BIT) | + (t->scaler & OCCAN_BUSTIM_BRP); + btrs->btr1 = (0<<7) | (t->ps2 << OCCAN_BUSTIM_TSEG2_BIT) | t->ps1; } static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing) @@ -1441,7 +1368,7 @@ static rtems_device_driver occan_write(rtems_device_major_number major, rtems_de static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { int ret; - occan_speed_regs timing; + struct grlib_canbtrs_timing timing; occan_priv *can; struct drvmgr_dev *dev; unsigned int speed; @@ -1467,7 +1394,11 @@ static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_de /* get speed rate from argument */ speed = (unsigned int)ioarg->buffer; - ret = occan_calc_speedregs(can->sys_freq_hz,speed,&timing); + /* Calculate default timing register values */ + ret = grlib_canbtrs_calc_timing( + speed, can->sys_freq_hz, + OCCAN_SAMPLING_POINT, &occan_btrs_ranges, + (struct grlib_canbtrs_timing *)&timing); if ( ret ) return RTEMS_INVALID_NAME; /* EINVAL */ @@ -1476,7 +1407,7 @@ static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_de /* save timing/speed */ can->speed = speed; - can->timing = timing; + convert_timing_to_btrs(&timing, &can->timing); break; case OCCAN_IOC_SET_BTRS: diff --git a/bsps/shared/grlib/irq/genirq.c b/bsps/shared/grlib/irq/genirq.c index 285416b0d3..ca80445c70 100644 --- a/bsps/shared/grlib/irq/genirq.c +++ b/bsps/shared/grlib/irq/genirq.c @@ -188,8 +188,8 @@ static int genirq_set_active( return 1; } e = isrentry; - } else { - enabled += isrentry->enabled; + } else if ( isrentry->enabled ) { + enabled = 1; } isrentry = isrentry->next; } diff --git a/bsps/shared/grlib/l2c/l2c.c b/bsps/shared/grlib/l2c/l2c.c index ddef0ada5c..4a443ed7cf 100644 --- a/bsps/shared/grlib/l2c/l2c.c +++ b/bsps/shared/grlib/l2c/l2c.c @@ -894,9 +894,9 @@ int l2cache_diag_tag( int way, int index, struct l2cache_tag * tag) return L2CACHE_ERR_EINVAL; } - unsigned int val = l2cache_reg_diagtag(way,index); - if (tag){ + unsigned int val = l2cache_reg_diagtag(way,index); + tag->tag = l2cache_get_tag(val); tag->valid = l2cache_tag_valid(val); tag->dirty = l2cache_tag_dirty(val); diff --git a/bsps/shared/grlib/net/greth.c b/bsps/shared/grlib/net/greth.c index bc4d3cc40f..09cfb325ba 100644 --- a/bsps/shared/grlib/net/greth.c +++ b/bsps/shared/grlib/net/greth.c @@ -186,6 +186,7 @@ struct greth_softc unsigned int advmodes; /* advertise ethernet speed modes. 0 = all modes. */ struct timespec auto_neg_time; int mc_available; + int num_descs; /* * Statistics @@ -422,7 +423,7 @@ greth_initialize_hardware (struct greth_softc *sc) int tmp2; struct timespec tstart, tnow; greth_regs *regs; - unsigned int advmodes, speed; + unsigned int advmodes, speed, tabsize; regs = sc->regs; @@ -616,8 +617,9 @@ auto_neg_done: /* Initialize rx/tx descriptor table pointers. Due to alignment we * always allocate maximum table size. */ - sc->txdesc = (greth_rxtxdesc *) almalloc(0x800, 0x400); - sc->rxdesc = (greth_rxtxdesc *) &sc->txdesc[128]; + tabsize = sc->num_descs * 8; + sc->txdesc = (greth_rxtxdesc *) almalloc(tabsize * 2, tabsize); + sc->rxdesc = (greth_rxtxdesc *) (tabsize + (void *)sc->txdesc); sc->tx_ptr = 0; sc->tx_dptr = 0; sc->tx_cnt = 0; @@ -631,8 +633,8 @@ auto_neg_done: CPUMEM_TO_DMA, (void *)sc->txdesc, (void **)&sc->txdesc_remote, - 0x800); - sc->rxdesc_remote = sc->txdesc_remote + 0x400; + tabsize * 2); + sc->rxdesc_remote = sc->txdesc_remote + tabsize; regs->txdesc = (int) sc->txdesc_remote; regs->rxdesc = (int) sc->rxdesc_remote; @@ -1555,6 +1557,7 @@ int greth_device_init(struct greth_softc *sc) struct ambapp_core *pnpinfo; union drvmgr_key_value *value; unsigned int speed; + int i, nrd; /* Get device information from AMBA PnP information */ ambadev = (struct amba_dev_info *)sc->dev->businfo; @@ -1608,12 +1611,17 @@ int greth_device_init(struct greth_softc *sc) sc->rxbufs = 32; sc->phyaddr = -1; + /* Probe the number of descriptors available the */ + nrd = (sc->regs->status & GRETH_STATUS_NRD) >> 24; + for (sc->num_descs = 128, i = 0; i < nrd; i++) + sc->num_descs = sc->num_descs * 2; + value = drvmgr_dev_key_get(sc->dev, "txDescs", DRVMGR_KT_INT); - if ( value && (value->i <= 128) ) + if ( value && (value->i <= sc->num_descs) ) sc->txbufs = value->i; value = drvmgr_dev_key_get(sc->dev, "rxDescs", DRVMGR_KT_INT); - if ( value && (value->i <= 128) ) + if ( value && (value->i <= sc->num_descs) ) sc->rxbufs = value->i; value = drvmgr_dev_key_get(sc->dev, "phyAdr", DRVMGR_KT_INT); diff --git a/bsps/shared/grlib/spw/grspw.c b/bsps/shared/grlib/spw/grspw.c index 2e1e8e90e9..dd61b7a472 100644 --- a/bsps/shared/grlib/spw/grspw.c +++ b/bsps/shared/grlib/spw/grspw.c @@ -261,6 +261,7 @@ static void check_rx_errors(GRSPW_DEV *pDev, int ctrl); static void grspw_rxnext(GRSPW_DEV *pDev); static void grspw_interrupt(void *arg); static int grspw_buffer_alloc(GRSPW_DEV *pDev); +static int grspw_dmatables_alloc(GRSPW_DEV *pDev); static rtems_device_driver grspw_initialize( rtems_device_major_number major, @@ -553,6 +554,8 @@ int grspw_device_init(GRSPW_DEV *pDev) if (grspw_buffer_alloc(pDev)) return RTEMS_NO_MEMORY; + if (grspw_dmatables_alloc(pDev)) + return RTEMS_NO_MEMORY; /* Create semaphores */ rtems_semaphore_create( @@ -678,7 +681,11 @@ static int grspw_buffer_alloc(GRSPW_DEV *pDev) (void **)&pDev->ptr_txhbuf0_remote, pDev->txhbufsize * pDev->txbufcnt); } + return 0; +} +static int grspw_dmatables_alloc(GRSPW_DEV *pDev) +{ /* DMA DESCRIPTOR TABLES */ if (pDev->bd_dma_area & 1) { /* Address given in remote address */ diff --git a/bsps/shared/grlib/spw/grspw_router.c b/bsps/shared/grlib/spw/grspw_router.c index d8ba8feef3..9a31c3b49a 100644 --- a/bsps/shared/grlib/spw/grspw_router.c +++ b/bsps/shared/grlib/spw/grspw_router.c @@ -1431,6 +1431,11 @@ int router_port_link_start(void *d, int port) return router_port_ctrl_rmw(d, port, NULL, PCTRL_LD | PCTRL_LS, PCTRL_LS); } +int router_port_link_div(void *d, int port, int rundiv) +{ + return router_port_ctrl_rmw(d, port, NULL, PCTRL_RD, (rundiv << PCTRL_RD_BIT) & PCTRL_RD); +} + int router_port_link_receive_spill(void *d, int port) { struct router_priv *priv = d; diff --git a/bsps/shared/irq/irq-default-handler.c b/bsps/shared/irq/irq-default-handler.c index 4f4b4be673..cfe91f4202 100644 --- a/bsps/shared/irq/irq-default-handler.c +++ b/bsps/shared/irq/irq-default-handler.c @@ -1,15 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup bsp_interrupt + * + * @brief This source file contains the default implementation of + * bsp_interrupt_handler_default(). + */ + /* - * Copyright (c) 2008-2012 embedded brains GmbH. All rights reserved. + * Copyright (C) 2008, 2012 embedded brains GmbH (http://www.embedded-brains.de) * - * embedded brains GmbH - * Obere Lagerstr. 30 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <inttypes.h> diff --git a/bsps/shared/irq/irq-default.c b/bsps/shared/irq/irq-default.c index 9295f1fb44..943b4ea191 100644 --- a/bsps/shared/irq/irq-default.c +++ b/bsps/shared/irq/irq-default.c @@ -1,13 +1,17 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * - * @ingroup RTEMSBSPsShared + * @ingroup bsp_interrupt + * + * @brief This source file contains the default implementation of + * bsp_interrupt_vector_enable(), bsp_interrupt_vector_disable(), and + * bsp_interrupt_facility_initialize(). */ /* - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (C) 2019 embedded brains GmbH + * Copyright (C) 2019 embedded brains GmbH (http://www.embedded-brains.de) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/bsps/shared/irq/irq-generic.c b/bsps/shared/irq/irq-generic.c index fd129333ba..1e83a6f249 100644 --- a/bsps/shared/irq/irq-generic.c +++ b/bsps/shared/irq/irq-generic.c @@ -1,25 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt support implementation. + * @brief This source file contains the generic interrupt controller support + * implementation. */ /* - * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette. - * - * Copyright (c) 2008, 2018 embedded brains GmbH. + * Copyright (C) 2008, 2018 embedded brains GmbH (http://www.embedded-brains.de) * - * embedded brains GmbH - * Dornierstr. 4 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <bsp/irq-generic.h> diff --git a/bsps/shared/irq/irq-info.c b/bsps/shared/irq/irq-info.c index ef965d3d07..b1df3b2915 100644 --- a/bsps/shared/irq/irq-info.c +++ b/bsps/shared/irq/irq-info.c @@ -1,22 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt information implementation. + * @brief This source file contains the implementation of + * bsp_interrupt_report() and bsp_interrupt_report_with_plugin(). */ /* - * Copyright (c) 2008, 2009, 2010 - * embedded brains GmbH - * Obere Lagerstr. 30 - * D-82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Copyright (C) 2008, 2010 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <inttypes.h> diff --git a/bsps/shared/irq/irq-legacy.c b/bsps/shared/irq/irq-legacy.c index 64c324b472..649b850095 100644 --- a/bsps/shared/irq/irq-legacy.c +++ b/bsps/shared/irq/irq-legacy.c @@ -1,22 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt support legacy implementation. + * @brief This source file contains the legacy interrupt controller support + * implementation. */ /* - * Copyright (c) 2008, 2009 - * embedded brains GmbH - * Obere Lagerstr. 30 - * D-82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Copyright (C) 2008, 2009 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <string.h> diff --git a/bsps/shared/irq/irq-lock.c b/bsps/shared/irq/irq-lock.c index 1398aff3eb..58b7020a3b 100644 --- a/bsps/shared/irq/irq-lock.c +++ b/bsps/shared/irq/irq-lock.c @@ -1,25 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief BSP interrupt support lock implementation. + * @brief This source file contains the implementation of + * bsp_interrupt_lock() and bsp_interrupt_unlock(). */ /* - * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette. - * - * Copyright (c) 2008, 2018 embedded brains GmbH. + * Copyright (C) 2008, 2018 embedded brains GmbH (http://www.embedded-brains.de) * - * embedded brains GmbH - * Dornierstr. 4 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <bsp/irq-generic.h> diff --git a/bsps/shared/irq/irq-server.c b/bsps/shared/irq/irq-server.c index 2c8df4952c..fa2153fcb0 100644 --- a/bsps/shared/irq/irq-server.c +++ b/bsps/shared/irq/irq-server.c @@ -1,17 +1,36 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt server implementation. + * @brief This source file contains the interrupt server implementation. */ /* * Copyright (C) 2009, 2020 embedded brains GmbH (http://www.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.org/license/LICENSE. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <stdlib.h> diff --git a/bsps/shared/irq/irq-shell.c b/bsps/shared/irq/irq-shell.c index ca936f8038..cf70f2ce1b 100644 --- a/bsps/shared/irq/irq-shell.c +++ b/bsps/shared/irq/irq-shell.c @@ -1,22 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /** * @file * * @ingroup bsp_interrupt * - * @brief Generic BSP interrupt shell implementation. + * @brief This source file contains the definition of + * ::bsp_interrupt_shell_command. */ /* - * Copyright (c) 2009 - * embedded brains GmbH - * Obere Lagerstr. 30 - * D-82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Copyright (C) 2009 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> diff --git a/bsps/sparc/leon3/start/amba.c b/bsps/sparc/leon3/start/amba.c index e7ff29808c..1de206139f 100644 --- a/bsps/sparc/leon3/start/amba.c +++ b/bsps/sparc/leon3/start/amba.c @@ -34,18 +34,6 @@ struct ambapp_bus ambapp_plb; #include <drvmgr/drvmgr.h> #include <grlib/ambapp_bus_grlib.h> -extern void gptimer_register_drv (void); -extern void apbuart_cons_register_drv(void); -/* All drivers included by BSP, this is overridden by the user by including - * the drvmgr_confdefs.h. By default the Timer and UART driver are included. - */ -drvmgr_drv_reg_func drvmgr_drivers[] __attribute__((weak)) = -{ - gptimer_register_drv, - apbuart_cons_register_drv, - NULL /* End array with NULL */ -}; - /* Driver resources configuration for AMBA root bus. It is declared weak * so that the user may override it, if the defualt settings are not * enough. diff --git a/bsps/sparc/leon3/start/cpucounter.c b/bsps/sparc/leon3/start/cpucounter.c index 007bb6d8ec..4a0a5fe116 100644 --- a/bsps/sparc/leon3/start/cpucounter.c +++ b/bsps/sparc/leon3/start/cpucounter.c @@ -60,7 +60,10 @@ static void leon3_counter_initialize(void) counter->counter_register = &gpt->timer[LEON3_COUNTER_GPTIMER_INDEX].value; /* Enable timer just in case no clock driver is configured */ - gpt->timer[LEON3_COUNTER_GPTIMER_INDEX].ctrl |= GPTIMER_TIMER_CTRL_EN; + gpt->timer[LEON3_COUNTER_GPTIMER_INDEX].reload = 0xffffffff; + gpt->timer[LEON3_COUNTER_GPTIMER_INDEX].ctrl |= GPTIMER_TIMER_CTRL_EN | + GPTIMER_TIMER_CTRL_RS | + GPTIMER_TIMER_CTRL_LD; leon3_counter_frequency = ambapp_freq_get(&ambapp_plb, LEON3_Timer_Adev) / (gpt->scaler_reload + 1); diff --git a/bsps/sparc/leon3/start/drvmgr_def_drivers.c b/bsps/sparc/leon3/start/drvmgr_def_drivers.c new file mode 100644 index 0000000000..688b37bab9 --- /dev/null +++ b/bsps/sparc/leon3/start/drvmgr_def_drivers.c @@ -0,0 +1,28 @@ +/* + * Default BSP drivers when Driver Manager enabled + * + * COPYRIGHT (c) 2019. + * Cobham Gaisler + * + * 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 <bsp.h> + +#ifdef RTEMS_DRVMGR_STARTUP +#include <drvmgr/drvmgr.h> + +extern void gptimer_register_drv (void); +extern void apbuart_cons_register_drv(void); +/* All drivers included by BSP, this is overridden by the user by including + * the drvmgr_confdefs.h. By default the Timer and UART driver are included. + */ +drvmgr_drv_reg_func drvmgr_drivers[] __attribute__((weak)) = +{ + gptimer_register_drv, + apbuart_cons_register_drv, + NULL /* End array with NULL */ +}; + +#endif diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am b/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am index cfd59475c2..449053b831 100644 --- a/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am +++ b/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am @@ -71,6 +71,10 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/clock/clock-a9mpcore. # I2C librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/xilinx-zynq/i2c/cadence-i2c.c +# SPI +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/spi/cadence-spi.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/spi/xilinx-axi-spi.c + # Cache librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/cache/cache-l2c-310.c diff --git a/c/src/lib/libbsp/arm/xilinx-zynqmp/Makefile.am b/c/src/lib/libbsp/arm/xilinx-zynqmp/Makefile.am index 0b49990ce7..6686ca4e04 100644 --- a/c/src/lib/libbsp/arm/xilinx-zynqmp/Makefile.am +++ b/c/src/lib/libbsp/arm/xilinx-zynqmp/Makefile.am @@ -66,6 +66,10 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/serial/zynq-uart-poll # Clock librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/clock/clock-generic-timer.c +# SPI +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/spi/cadence-spi.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/spi/xilinx-axi-spi.c + # Cache librtemsbsp_a_SOURCES += ../../../../../../bsps/arm/shared/cache/cache-cp15.c diff --git a/c/src/lib/libbsp/powerpc/motorola_powerpc/Makefile.am b/c/src/lib/libbsp/powerpc/motorola_powerpc/Makefile.am index caa4abbb78..83abcdee18 100644 --- a/c/src/lib/libbsp/powerpc/motorola_powerpc/Makefile.am +++ b/c/src/lib/libbsp/powerpc/motorola_powerpc/Makefile.am @@ -92,9 +92,10 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/btimer/btimer-ppc librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/mmu/bat.c librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/mmu/mmuAsm.S librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/mmu/pte121.c -librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/irq/ppc-irq-legacy.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/powerpc/shared/irq/ppc-irq-generic.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-default-handler.c -include $(srcdir)/../../../../../../bsps/shared/irq-default-sources.am +include $(srcdir)/../../../../../../bsps/shared/irq-sources.am include $(srcdir)/../../../../../../bsps/shared/shared-sources.am include $(top_srcdir)/../../../../automake/subdirs.am include $(srcdir)/../../../../../../bsps/powerpc/shared/shared-sources.am diff --git a/c/src/lib/libbsp/sparc/leon3/Makefile.am b/c/src/lib/libbsp/sparc/leon3/Makefile.am index 7933a48024..beb1b96767 100644 --- a/c/src/lib/libbsp/sparc/leon3/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon3/Makefile.am @@ -38,6 +38,7 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/start/bspreset-empty.c librtemsbsp_a_SOURCES += ../../../../../../bsps/sparc/leon3/start/cpucounter.c librtemsbsp_a_SOURCES += ../../../../../../bsps/sparc/shared/start/bsp_fatal_exit.c librtemsbsp_a_SOURCES += ../../../../../../bsps/sparc/leon3/start/bsp_fatal_halt.c +librtemsbsp_a_SOURCES += ../../../../../../bsps/sparc/leon3/start/drvmgr_def_drivers.c # gnatsupp librtemsbsp_a_SOURCES += ../../../../../../bsps/sparc/leon3/gnatsupp/gnatsupp.c diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index 51f38c84c7..202cf3c346 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -930,6 +930,7 @@ librtemscpu_a_SOURCES += score/src/schedulercbssetparameters.c librtemscpu_a_SOURCES += score/src/schedulercbsreleasejob.c librtemscpu_a_SOURCES += score/src/schedulercbsunblock.c librtemscpu_a_SOURCES += score/src/stackallocator.c +librtemscpu_a_SOURCES += score/src/stackallocatorforidle.c librtemscpu_a_SOURCES += score/src/pheapallocate.c librtemscpu_a_SOURCES += score/src/pheapextend.c librtemscpu_a_SOURCES += score/src/pheapfree.c @@ -1483,6 +1484,10 @@ librtemscpu_a_SOURCES += libmisc/shell/login_prompt.c librtemscpu_a_SOURCES += libmisc/shell/login_check.c librtemscpu_a_SOURCES += libmisc/shell/fdisk.c librtemscpu_a_SOURCES += libmisc/shell/main_rtc.c +librtemscpu_a_SOURCES += libmisc/shell/main_spi.c +librtemscpu_a_SOURCES += libmisc/shell/main_i2cdetect.c +librtemscpu_a_SOURCES += libmisc/shell/main_i2cset.c +librtemscpu_a_SOURCES += libmisc/shell/main_i2cget.c librtemscpu_a_SOURCES += libmisc/shell/dd-args.c librtemscpu_a_SOURCES += libmisc/shell/main_dd.c librtemscpu_a_SOURCES += libmisc/shell/dd-conv.c diff --git a/cpukit/headers.am b/cpukit/headers.am index fcf679f09d..6bd0535a74 100644 --- a/cpukit/headers.am +++ b/cpukit/headers.am @@ -416,6 +416,7 @@ include_rtems_score_HEADERS += include/rtems/score/threadimpl.h include_rtems_score_HEADERS += include/rtems/score/threadmp.h include_rtems_score_HEADERS += include/rtems/score/threadq.h include_rtems_score_HEADERS += include/rtems/score/threadqimpl.h +include_rtems_score_HEADERS += include/rtems/score/threadqops.h include_rtems_score_HEADERS += include/rtems/score/timecounter.h include_rtems_score_HEADERS += include/rtems/score/timecounterimpl.h include_rtems_score_HEADERS += include/rtems/score/timespec.h diff --git a/cpukit/include/link_elf.h b/cpukit/include/link_elf.h index 6cab3c2c7f..6483c021fd 100644 --- a/cpukit/include/link_elf.h +++ b/cpukit/include/link_elf.h @@ -13,6 +13,10 @@ #include <stdint.h> #include <rtems/rtl/rtl-obj-fwd.h> +#ifdef __cplusplus +extern "C" { +#endif + enum sections { rap_text = 0, @@ -76,4 +80,8 @@ int _rtld_linkmap_add (rtems_rtl_obj* obj); * Remove link map from the list. */ void _rtld_linkmap_delete (rtems_rtl_obj* obj); + +#ifdef __cplusplus +} +#endif #endif /* _LINK_ELF_H_ */ diff --git a/cpukit/include/linux/rbtree.h b/cpukit/include/linux/rbtree.h index 8fc575240f..ec0fac1af9 100644 --- a/cpukit/include/linux/rbtree.h +++ b/cpukit/include/linux/rbtree.h @@ -17,6 +17,10 @@ #include <rtems/score/rbtree.h> +#ifdef __cplusplus +extern "C" { +#endif + #define rb_node RBTree_Node #define rb_left Node.rbe_left @@ -96,7 +100,7 @@ static inline struct rb_node *rb_last( struct rb_root *root ) static inline void rb_replace_node( struct rb_node *victim, - struct rb_node *replacement, + struct rb_node *replacement, struct rb_root *root ) { @@ -138,4 +142,8 @@ static inline struct rb_node *rb_parent( struct rb_node *node ) node = next \ ) +#ifdef __cplusplus +} +#endif + #endif /* _LINUX_RBTREE_H */ diff --git a/cpukit/include/rtems/capture.h b/cpukit/include/rtems/capture.h index c37d652211..4ab638ec24 100644 --- a/cpukit/include/rtems/capture.h +++ b/cpukit/include/rtems/capture.h @@ -838,7 +838,7 @@ rtems_capture_task_flags (rtems_tcb* tcb) static inline rtems_capture_control* rtems_capture_task_control (rtems_tcb* tcb) { - return tcb->Capture.control; + return (rtems_capture_control*) tcb->Capture.control; } /** @@ -853,7 +853,7 @@ rtems_capture_task_control (rtems_tcb* tcb) static inline uint32_t rtems_capture_task_control_flags (rtems_tcb* tcb) { - rtems_capture_control* control = tcb->Capture.control; + rtems_capture_control* control = rtems_capture_task_control (tcb); if (!control) return 0; return control->flags; diff --git a/cpukit/include/rtems/confdefs/percpu.h b/cpukit/include/rtems/confdefs/percpu.h index f3a9a4f3e7..b7baebea05 100644 --- a/cpukit/include/rtems/confdefs/percpu.h +++ b/cpukit/include/rtems/confdefs/percpu.h @@ -133,11 +133,19 @@ RTEMS_DEFINE_GLOBAL_SYMBOL( const size_t _Thread_Idle_stack_size = CONFIGURE_IDLE_TASK_STACK_SIZE; -char _Thread_Idle_stacks[ - _CONFIGURE_MAXIMUM_PROCESSORS - * ( CONFIGURE_IDLE_TASK_STACK_SIZE + CPU_IDLE_TASK_IS_FP * CONTEXT_FP_SIZE ) -] RTEMS_ALIGNED( CPU_INTERRUPT_STACK_ALIGNMENT ) -RTEMS_SECTION( ".rtemsstack.idle" ); +/* + * If the user provides a custom idle stack allocator, then we do not need + * memory reserved for the stacks but the symbol is still referenced in + * threadcreateidle.c. The code path just never uses it. Make it minimal + * size to proceed. + */ +#ifndef CONFIGURE_TASK_STACK_ALLOCATOR_FOR_IDLE + char _Thread_Idle_stacks[ + _CONFIGURE_MAXIMUM_PROCESSORS + * ( CONFIGURE_IDLE_TASK_STACK_SIZE + CPU_IDLE_TASK_IS_FP * CONTEXT_FP_SIZE ) + ] RTEMS_ALIGNED( CPU_INTERRUPT_STACK_ALIGNMENT ) + RTEMS_SECTION( ".rtemsstack.idle" ); +#endif #if defined(CONFIGURE_IDLE_TASK_INITIALIZES_APPLICATION) && \ !defined(CONFIGURE_IDLE_TASK_BODY) diff --git a/cpukit/include/rtems/confdefs/wkspace.h b/cpukit/include/rtems/confdefs/wkspace.h index 484dde20ea..9ef9d90b85 100644 --- a/cpukit/include/rtems/confdefs/wkspace.h +++ b/cpukit/include/rtems/confdefs/wkspace.h @@ -132,12 +132,14 @@ const uintptr_t _Stack_Space_size = _CONFIGURE_STACK_SPACE_SIZE; #if defined(CONFIGURE_TASK_STACK_ALLOCATOR) \ && defined(CONFIGURE_TASK_STACK_DEALLOCATOR) + /* Custom allocator may or may not use the work space. */ #ifdef CONFIGURE_TASK_STACK_ALLOCATOR_AVOIDS_WORK_SPACE const bool _Stack_Allocator_avoids_workspace = true; #else const bool _Stack_Allocator_avoids_workspace = false; #endif + /* Custom allocator may or may not need initialization. */ #ifdef CONFIGURE_TASK_STACK_ALLOCATOR_INIT const Stack_Allocator_initialize _Stack_Allocator_initialize = CONFIGURE_TASK_STACK_ALLOCATOR_INIT; @@ -145,16 +147,30 @@ const uintptr_t _Stack_Space_size = _CONFIGURE_STACK_SPACE_SIZE; const Stack_Allocator_initialize _Stack_Allocator_initialize = NULL; #endif + /* Custom allocator must include allocate and free */ const Stack_Allocator_allocate _Stack_Allocator_allocate = CONFIGURE_TASK_STACK_ALLOCATOR; const Stack_Allocator_free _Stack_Allocator_free = CONFIGURE_TASK_STACK_DEALLOCATOR; + +/* + * Must provide both a custom stack allocator and deallocator + */ #elif defined(CONFIGURE_TASK_STACK_ALLOCATOR) \ || defined(CONFIGURE_TASK_STACK_DEALLOCATOR) #error "CONFIGURE_TASK_STACK_ALLOCATOR and CONFIGURE_TASK_STACK_DEALLOCATOR must be both defined or both undefined" #endif +/* + * Custom IDLE thread stacks allocator. If this is provided, it is assumed + * that the allocator is providing its own memory for these stacks. + */ +#ifdef CONFIGURE_TASK_STACK_ALLOCATOR_FOR_IDLE + const Stack_Allocator_allocate_for_idle _Stack_Allocator_allocate_for_idle = + CONFIGURE_TASK_STACK_ALLOCATOR_FOR_IDLE; +#endif + #ifdef CONFIGURE_DIRTY_MEMORY RTEMS_SYSINIT_ITEM( _Memory_Dirty_free_areas, diff --git a/cpukit/include/rtems/config.h b/cpukit/include/rtems/config.h index e82c7abf11..a826581658 100644 --- a/cpukit/include/rtems/config.h +++ b/cpukit/include/rtems/config.h @@ -129,6 +129,9 @@ uint32_t rtems_configuration_get_maximum_extensions( void ); #define rtems_configuration_get_stack_free_hook() \ (_Stack_Allocator_free) +#define rtems_configuration_get_stack_allocate_for_idle_hook() \ + (_Stack_Allocator_allocate_for_idle) + /** * This macro assists in accessing the field which indicates whether * RTEMS is responsible for zeroing the Executive Workspace. diff --git a/cpukit/include/rtems/linkersets.h b/cpukit/include/rtems/linkersets.h index 844130f0d4..d3ed76043a 100644 --- a/cpukit/include/rtems/linkersets.h +++ b/cpukit/include/rtems/linkersets.h @@ -38,7 +38,7 @@ extern "C" { RTEMS_SECTION( ".rtemsroset." #set ".end" ) RTEMS_USED #define RTEMS_LINKER_ROSET_ITEM_ORDERED_DECLARE( set, type, item, order ) \ - type const _Linker_set_##set##_##item \ + extern type const _Linker_set_##set##_##item \ RTEMS_SECTION( ".rtemsroset." #set ".content.0." RTEMS_XSTRING( order ) ) #define RTEMS_LINKER_ROSET_ITEM_DECLARE( set, type, item ) \ diff --git a/cpukit/include/rtems/posix/muteximpl.h b/cpukit/include/rtems/posix/muteximpl.h index 435b43634d..3d717420f2 100644 --- a/cpukit/include/rtems/posix/muteximpl.h +++ b/cpukit/include/rtems/posix/muteximpl.h @@ -2,8 +2,8 @@ * @file * * @brief Private Inlined Routines for POSIX Mutex's. - * - * This include file contains the static inline implementation of the private + * + * This include file contains the static inline implementation of the private * inlined routines for POSIX mutex's. */ @@ -104,7 +104,7 @@ RTEMS_INLINE_ROUTINE POSIX_Mutex_Protocol _POSIX_Mutex_Get_protocol( unsigned long flags ) { - return flags & POSIX_MUTEX_PROTOCOL_MASK; + return (POSIX_Mutex_Protocol) (flags & POSIX_MUTEX_PROTOCOL_MASK); } RTEMS_INLINE_ROUTINE bool _POSIX_Mutex_Is_recursive( @@ -484,4 +484,3 @@ bool _POSIX_Mutex_Auto_initialization( POSIX_Mutex_Control *the_mutex ); #endif /* end of include file */ - diff --git a/cpukit/include/rtems/posix/pthreadattrimpl.h b/cpukit/include/rtems/posix/pthreadattrimpl.h index b5e02ec1c7..7cd69142a6 100644 --- a/cpukit/include/rtems/posix/pthreadattrimpl.h +++ b/cpukit/include/rtems/posix/pthreadattrimpl.h @@ -72,7 +72,7 @@ RTEMS_INLINE_ROUTINE void _POSIX_Threads_Get_sched_param_sporadic( #if defined(RTEMS_POSIX_API) const POSIX_API_Control *api; - api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + api = (const POSIX_API_Control*) the_thread->API_Extensions[ THREAD_API_POSIX ]; param->sched_ss_low_priority = _POSIX_Priority_From_core( scheduler, api->Sporadic.Low_priority.priority diff --git a/cpukit/include/rtems/rtl/rtl-obj.h b/cpukit/include/rtems/rtl/rtl-obj.h index f27ae3259d..c1fe51306b 100644 --- a/cpukit/include/rtems/rtl/rtl-obj.h +++ b/cpukit/include/rtems/rtl/rtl-obj.h @@ -231,7 +231,7 @@ struct rtems_rtl_obj size_t tramps_size; /**< Size of the trampoline memory. */ void* tramp_brk; /**< Trampoline memory allocator. MD * relocators can take memory from the - * break upto the size. */ + * break up to the size. */ size_t tramp_relocs; /**< Number of slots reserved for * relocs. The remainder are for * unresolved symbols. */ @@ -333,7 +333,7 @@ static inline bool rtems_rtl_obj_text_inside (const rtems_rtl_obj* obj, { return (address >= obj->text_base) && - (address < (obj->text_base + obj->text_size)); + ((char*) address < ((char*) obj->text_base + obj->text_size)); } /** @@ -374,11 +374,23 @@ static inline bool rtems_rtl_obj_has_symbol (const rtems_rtl_obj* obj, * @param size The size to be allocated. * @retval bool Returns @true if the space is available. */ +static inline size_t rtems_rtl_obj_tramp_avail_space (const rtems_rtl_obj* obj) +{ + return (char*) obj->tramp_brk - (char*) obj->trampoline; +} + +/** + * Is there space in the trampoline memory for a trapoline. + * + * @param obj The object file's descriptor to check for available space. + * @param size The size to be allocated. + * @retval bool Returns @true if the space is available. + */ static inline bool rtems_rtl_obj_has_tramp_space (const rtems_rtl_obj* obj, const size_t size) { return (obj->trampoline != NULL && - ((obj->tramp_brk - obj->trampoline) + size) <= obj->tramps_size); + (rtems_rtl_obj_tramp_avail_space (obj) + size) <= obj->tramps_size); } /** @@ -402,7 +414,7 @@ static inline size_t rtems_rtl_obj_trampoline_slots (const rtems_rtl_obj* obj) static inline size_t rtems_rtl_obj_trampolines (const rtems_rtl_obj* obj) { return obj->trampoline == NULL || obj->tramp_size == 0 ? - 0 : (obj->tramp_brk - obj->trampoline) / obj->tramp_size; + 0 : rtems_rtl_obj_tramp_avail_space (obj) / obj->tramp_size; } /** diff --git a/cpukit/include/rtems/rtl/rtl.h b/cpukit/include/rtems/rtl/rtl.h index 67d7e96be3..3c74370187 100644 --- a/cpukit/include/rtems/rtl/rtl.h +++ b/cpukit/include/rtems/rtl/rtl.h @@ -88,7 +88,7 @@ extern struct r_debug _rtld_debug; * Debugger break function. Call when debugging to have it read the _rtld_debug * variable. */ -extern void _rtld_debug_state (void); +void _rtld_debug_state (void); /** * The type of constructor/destructor function. diff --git a/cpukit/include/rtems/score/corebarrierimpl.h b/cpukit/include/rtems/score/corebarrierimpl.h index 9e9ddfd81e..13e052dc57 100644 --- a/cpukit/include/rtems/score/corebarrierimpl.h +++ b/cpukit/include/rtems/score/corebarrierimpl.h @@ -35,7 +35,14 @@ extern "C" { * @{ */ -#define CORE_BARRIER_TQ_OPERATIONS &_Thread_queue_Operations_FIFO +/** + * @brief These thread queue operations are used for core barriers. + * + * They are a specialization of ::_Thread_queue_Operations_FIFO. The only + * difference is that the extract operation decrements + * CORE_barrier_Control::number_of_waiting_threads. + */ +extern const Thread_queue_Operations _CORE_barrier_Thread_queue_operations; /** * @brief Initializes the core barrier. diff --git a/cpukit/include/rtems/score/cpustdatomic.h b/cpukit/include/rtems/score/cpustdatomic.h index 31a17932c6..8401f68e31 100644 --- a/cpukit/include/rtems/score/cpustdatomic.h +++ b/cpukit/include/rtems/score/cpustdatomic.h @@ -294,7 +294,7 @@ static inline uintptr_t _CPU_atomic_Load_uintptr( const CPU_atomic_Uintptr *obj, static inline void _CPU_atomic_Store_uint( CPU_atomic_Uint *obj, unsigned int desired, CPU_atomic_Order order ) { #if defined(_RTEMS_SCORE_CPUSTDATOMIC_USE_ATOMIC) - obj->store( desired ); + obj->store( desired, order ); #elif defined(_RTEMS_SCORE_CPUSTDATOMIC_USE_STDATOMIC) atomic_store_explicit( obj, desired, order ); #else @@ -314,7 +314,7 @@ static inline void _CPU_atomic_Store_uint( CPU_atomic_Uint *obj, unsigned int de static inline void _CPU_atomic_Store_ulong( CPU_atomic_Ulong *obj, unsigned long desired, CPU_atomic_Order order ) { #if defined(_RTEMS_SCORE_CPUSTDATOMIC_USE_ATOMIC) - obj->store( desired ); + obj->store( desired, order ); #elif defined(_RTEMS_SCORE_CPUSTDATOMIC_USE_STDATOMIC) atomic_store_explicit( obj, desired, order ); #else @@ -334,7 +334,7 @@ static inline void _CPU_atomic_Store_ulong( CPU_atomic_Ulong *obj, unsigned long static inline void _CPU_atomic_Store_uintptr( CPU_atomic_Uintptr *obj, uintptr_t desired, CPU_atomic_Order order ) { #if defined(_RTEMS_SCORE_CPUSTDATOMIC_USE_ATOMIC) - obj->store( desired ); + obj->store( desired, order ); #elif defined(_RTEMS_SCORE_CPUSTDATOMIC_USE_STDATOMIC) atomic_store_explicit( obj, desired, order ); #else diff --git a/cpukit/include/rtems/score/heapimpl.h b/cpukit/include/rtems/score/heapimpl.h index d3ee0ff88a..f74b5fc562 100644 --- a/cpukit/include/rtems/score/heapimpl.h +++ b/cpukit/include/rtems/score/heapimpl.h @@ -407,15 +407,7 @@ Heap_Block *_Heap_Block_allocate( (*heap->Protection.block_error)( heap, block, reason ); } - static inline void _Heap_Protection_free_all_delayed_blocks( Heap_Control *heap ) - { - uintptr_t large = 0 - - (uintptr_t) HEAP_BLOCK_HEADER_SIZE - - (uintptr_t) HEAP_ALLOC_BONUS - - (uintptr_t) 1; - void *p = _Heap_Allocate( heap, large ); - _Heap_Free( heap, p ); - } + void _Heap_Protection_free_all_delayed_blocks( Heap_Control *heap ); #endif /** diff --git a/cpukit/include/rtems/score/objectimpl.h b/cpukit/include/rtems/score/objectimpl.h index c540f90166..ed0ce2aa10 100644 --- a/cpukit/include/rtems/score/objectimpl.h +++ b/cpukit/include/rtems/score/objectimpl.h @@ -938,6 +938,25 @@ RTEMS_INLINE_ROUTINE void _Objects_Free( } /** + * @brief Returns true, if the object associated with the zero-based index is + * contained in an allocated block of objects, otherwise false. + * + * @param index is the zero-based object index. + * @param objects_per_block is the object count per block. + * + * @retval true The object associated with the zero-based index is in an + * allocated block of objects. + * @retval false Otherwise. + */ +RTEMS_INLINE_ROUTINE bool _Objects_Is_in_allocated_block( + Objects_Maximum index, + Objects_Maximum objects_per_block +) +{ + return index >= objects_per_block; +} + +/** * @brief Activate the object. * * This function must be only used in case this objects information supports @@ -952,15 +971,17 @@ RTEMS_INLINE_ROUTINE void _Objects_Activate_unlimited( ) { Objects_Maximum objects_per_block; - Objects_Maximum block; + Objects_Maximum index; _Assert( _Objects_Is_auto_extend( information ) ); objects_per_block = information->objects_per_block; - block = _Objects_Get_index( the_object->id ) - OBJECTS_INDEX_MINIMUM; + index = _Objects_Get_index( the_object->id ) - OBJECTS_INDEX_MINIMUM; + + if ( _Objects_Is_in_allocated_block( index, objects_per_block ) ) { + Objects_Maximum block; - if ( block > objects_per_block ) { - block /= objects_per_block; + block = index / objects_per_block; information->inactive_per_block[ block ]--; information->inactive--; diff --git a/cpukit/include/rtems/score/priority.h b/cpukit/include/rtems/score/priority.h index 54b91a871b..a6c65d3c4c 100644 --- a/cpukit/include/rtems/score/priority.h +++ b/cpukit/include/rtems/score/priority.h @@ -24,12 +24,12 @@ #include <rtems/score/cpu.h> #include <rtems/score/rbtree.h> -struct _Scheduler_Control; - #ifdef __cplusplus extern "C" { #endif +struct _Scheduler_Control; + /** * @defgroup RTEMSScorePriority Priority Handler * diff --git a/cpukit/include/rtems/score/priorityimpl.h b/cpukit/include/rtems/score/priorityimpl.h index b33419acdb..3b92d3375a 100644 --- a/cpukit/include/rtems/score/priorityimpl.h +++ b/cpukit/include/rtems/score/priorityimpl.h @@ -389,7 +389,7 @@ RTEMS_INLINE_ROUTINE bool _Priority_Less( const Priority_Control *the_left; const Priority_Node *the_right; - the_left = left; + the_left = (const Priority_Control*) left; the_right = RTEMS_CONTAINER_OF( right, Priority_Node, Node.RBTree ); return *the_left < the_right->priority; diff --git a/cpukit/include/rtems/score/scheduleredfimpl.h b/cpukit/include/rtems/score/scheduleredfimpl.h index f2bec2dfbe..fc8c67c163 100644 --- a/cpukit/include/rtems/score/scheduleredfimpl.h +++ b/cpukit/include/rtems/score/scheduleredfimpl.h @@ -100,7 +100,7 @@ RTEMS_INLINE_ROUTINE bool _Scheduler_EDF_Less( Priority_Control prio_left; Priority_Control prio_right; - the_left = left; + the_left = (const Priority_Control*) left; the_right = RTEMS_CONTAINER_OF( right, Scheduler_EDF_Node, Node ); prio_left = *the_left; @@ -128,7 +128,7 @@ RTEMS_INLINE_ROUTINE bool _Scheduler_EDF_Priority_less_equal( Priority_Control prio_left; Priority_Control prio_right; - the_left = left; + the_left = (const Priority_Control*) left; the_right = RTEMS_CONTAINER_OF( right, Scheduler_EDF_Node, Node ); prio_left = *the_left; diff --git a/cpukit/include/rtems/score/stack.h b/cpukit/include/rtems/score/stack.h index df1df74867..bad89e66fc 100644 --- a/cpukit/include/rtems/score/stack.h +++ b/cpukit/include/rtems/score/stack.h @@ -82,6 +82,23 @@ typedef void *( *Stack_Allocator_allocate )( size_t stack_size ); typedef void ( *Stack_Allocator_free )( void *addr ); /** + * @brief Stack allocator allocate for idle handler. + * + * The allocate for idle handler is optional even when the user thread stack + * allocator and deallocator are configured. + * + * @param cpu Index of the CPU for the IDLE thread using this stack + * @param stack_size The size of the stack area to allocate in bytes. + * + * @retval NULL Not enough memory. + * @retval other Pointer to begin of stack area. + */ +typedef void *( *Stack_Allocator_allocate_for_idle )( + uint32_t cpu, + size_t stack_size +); + +/** * @brief The minimum stack size. * * Application provided via <rtems/confdefs.h>. @@ -124,6 +141,13 @@ extern const Stack_Allocator_allocate _Stack_Allocator_allocate; extern const Stack_Allocator_free _Stack_Allocator_free; /** @} */ +/** + * @brief The stack allocator allocate stack for idle thread handler. + * + * Application provided via <rtems/confdefs.h>. + */ +extern const Stack_Allocator_allocate_for_idle + _Stack_Allocator_allocate_for_idle; #ifdef __cplusplus } diff --git a/cpukit/include/rtems/score/threadq.h b/cpukit/include/rtems/score/threadq.h index 522be03970..29d3fafda6 100644 --- a/cpukit/include/rtems/score/threadq.h +++ b/cpukit/include/rtems/score/threadq.h @@ -497,16 +497,17 @@ typedef Thread_Control *( *Thread_queue_Surrender_operation )( ); /** - * @brief Thread queue first operation. + * @brief Gets the first thread on the queue. * - * @param[in] heads The thread queue heads. + * @param heads are heads of the thread queue. * - * @retval NULL No thread is present on the thread queue. - * @retval first The first thread of the thread queue according to the insert - * order. This thread remains on the thread queue. + * @retval NULL No thread is enqueued on the thread queue. + * + * @return Returns the first thread on the thread queue according to the queue + * order. This thread remains on the thread queue. */ typedef Thread_Control *( *Thread_queue_First_operation )( - Thread_queue_Heads *heads + const Thread_queue_Heads *heads ); /** diff --git a/cpukit/include/rtems/score/threadqops.h b/cpukit/include/rtems/score/threadqops.h new file mode 100644 index 0000000000..504383e98d --- /dev/null +++ b/cpukit/include/rtems/score/threadqops.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSScoreThreadQueue + * + * @brief This header file provides interfaces related to thread queue + * operations. + */ + +/* + * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTEMS_SCORE_THREADQOPS_H +#define _RTEMS_SCORE_THREADQOPS_H + +#include <rtems/score/threadq.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup RTEMSScoreThreadQueue + * + * @{ + */ + +/** + * @brief Initializes the priority actions so that no actions are performed. + * + * @param queue is unused. + * + * @param[out] priority_actions is initialized so that no actions are + * performed. + */ +void _Thread_queue_Do_nothing_priority_actions( + Thread_queue_Queue *queue, + Priority_Actions *priority_actions +); + +/** + * @brief Enqueues the thread to the FIFO thread queue. + * + * @param[in, out] queue is the thread queue. + * + * @param[in, out] the_thread is the thread to enqueue. + * + * @param[in, out] queue_context is the thread queue context. + */ +void _Thread_queue_FIFO_enqueue( + Thread_queue_Queue *queue, + Thread_Control *the_thread, + Thread_queue_Context *queue_context +); + +/** + * @brief Extracts the thread from the FIFO thread queue. + * + * @param[in, out] queue is the thread queue. + * + * @param[in, out] the_thread is the thread to extract. + * + * @param[in, out] queue_context is the thread queue context. + */ +void _Thread_queue_FIFO_extract( + Thread_queue_Queue *queue, + Thread_Control *the_thread, + Thread_queue_Context *queue_context +); + +/** + * @brief Surrenders the thread queue to the first thread on the FIFO thread + * queue. + * + * @param[in, out] queue is the thread queue. + * + * @param[in, out] heads are heads of the thread queue. + * + * @param previous_owner is unused. + * + * @param[in, out] queue_context is the thread queue context. + * + * @return Returns the first thread on the thread queue according to the queue + * order. + */ +Thread_Control *_Thread_queue_FIFO_surrender( + Thread_queue_Queue *queue, + Thread_queue_Heads *heads, + Thread_Control *previous_owner, + Thread_queue_Context *queue_context +); + +/** + * @brief Gets the first thread on the FIFO thread queue. + * + * @param[in, out] queue is the thread queue. + * + * @param[in, out] the_thread is the thread to extract. + * + * @param[in, out] queue_context is the thread queue context. + */ +Thread_Control *_Thread_queue_FIFO_first( const Thread_queue_Heads *heads ); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _RTEMS_SCORE_THREADQOPS_H */ diff --git a/cpukit/include/rtems/score/timecounter.h b/cpukit/include/rtems/score/timecounter.h index b71ccd3948..64429eae7b 100644 --- a/cpukit/include/rtems/score/timecounter.h +++ b/cpukit/include/rtems/score/timecounter.h @@ -7,7 +7,7 @@ */ /* - * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * Copyright (c) 2015, 2021 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 @@ -247,6 +247,28 @@ extern volatile int32_t _Timecounter_Time_uptime; */ extern struct timecounter *_Timecounter; +/** + * @brief Handler doing the NTP update second processing shall have this type. + * + * @param[in, out] adjustment is the NTP time adjustment. + * + * @param[in, out] newsec is the number of seconds since Unix epoch. + */ +typedef void ( *Timecounter_NTP_update_second )( + int64_t *adjustment, + time_t *newsec +); + +/** + * @brief Sets the NTP update second handler. + * + * @param handler is the new NTP update second handler used to carry out the + * NTP update second processing. + */ +void _Timecounter_Set_NTP_update_second( + Timecounter_NTP_update_second handler +); + /** @} */ #ifdef __cplusplus diff --git a/cpukit/include/rtems/score/tls.h b/cpukit/include/rtems/score/tls.h index 65a49d87be..322be30c7a 100644 --- a/cpukit/include/rtems/score/tls.h +++ b/cpukit/include/rtems/score/tls.h @@ -222,7 +222,7 @@ static inline void *_TLS_TCB_at_area_begin_initialize( void *tls_area ) { void *tls_block = (char *) tls_area + _TLS_Get_thread_control_block_area_size( (uintptr_t) _TLS_Alignment ); - TLS_Thread_control_block *tcb = tls_area; + TLS_Thread_control_block *tcb = (TLS_Thread_control_block*) tls_area; uintptr_t aligned_size = _TLS_Heap_align_up( (uintptr_t) _TLS_Size ); TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *) ((char *) tls_block + aligned_size); diff --git a/cpukit/include/rtems/score/watchdogimpl.h b/cpukit/include/rtems/score/watchdogimpl.h index a52fb1c2cb..ed9d1ef5cd 100644 --- a/cpukit/include/rtems/score/watchdogimpl.h +++ b/cpukit/include/rtems/score/watchdogimpl.h @@ -150,7 +150,7 @@ RTEMS_INLINE_ROUTINE Watchdog_State _Watchdog_Get_state( const Watchdog_Control *the_watchdog ) { - return RB_COLOR( &the_watchdog->Node.RBTree, Node ); + return (Watchdog_State) RB_COLOR( &the_watchdog->Node.RBTree, Node ); } /** diff --git a/cpukit/include/rtems/shellconfig.h b/cpukit/include/rtems/shellconfig.h index 3e87d472d6..c5fcf4a45e 100644 --- a/cpukit/include/rtems/shellconfig.h +++ b/cpukit/include/rtems/shellconfig.h @@ -78,6 +78,10 @@ extern rtems_shell_cmd_t rtems_shell_DF_Command; extern rtems_shell_cmd_t rtems_shell_MD5_Command; extern rtems_shell_cmd_t rtems_shell_RTC_Command; +extern rtems_shell_cmd_t rtems_shell_SPI_Command; +extern rtems_shell_cmd_t rtems_shell_I2CDETECT_Command; +extern rtems_shell_cmd_t rtems_shell_I2CGET_Command; +extern rtems_shell_cmd_t rtems_shell_I2CSET_Command; extern rtems_shell_cmd_t rtems_shell_SHUTDOWN_Command; extern rtems_shell_cmd_t rtems_shell_CPUINFO_Command; @@ -521,6 +525,30 @@ extern rtems_shell_alias_t * const rtems_shell_Initial_aliases[]; &rtems_shell_RTC_Command, #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ + && !defined(CONFIGURE_SHELL_NO_COMMAND_SPI)) \ + || defined(CONFIGURE_SHELL_COMMAND_SPI) + &rtems_shell_SPI_Command, + #endif + + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ + && !defined(CONFIGURE_SHELL_NO_COMMAND_I2CDETECT)) \ + || defined(CONFIGURE_SHELL_COMMAND_I2CDETECT) + &rtems_shell_I2CDETECT_Command, + #endif + + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ + && !defined(CONFIGURE_SHELL_NO_COMMAND_I2CGET)) \ + || defined(CONFIGURE_SHELL_COMMAND_I2CGET) + &rtems_shell_I2CGET_Command, + #endif + + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ + && !defined(CONFIGURE_SHELL_NO_COMMAND_I2CSET)) \ + || defined(CONFIGURE_SHELL_COMMAND_I2CSET) + &rtems_shell_I2CSET_Command, + #endif + /* * System related commands */ diff --git a/cpukit/include/rtems/termiostypes.h b/cpukit/include/rtems/termiostypes.h index 6930e5958b..59b1b3dd96 100644 --- a/cpukit/include/rtems/termiostypes.h +++ b/cpukit/include/rtems/termiostypes.h @@ -367,6 +367,10 @@ typedef struct rtems_termios_tty { */ rtems_id rxTaskId; rtems_id txTaskId; + /* + * Information for the tx task how many characters have been dequeued. + */ + int txTaskCharsDequeued; /* * line discipline related stuff @@ -482,7 +486,7 @@ struct rtems_termios_linesw { int (*l_read )(struct rtems_termios_tty *tp,rtems_libio_rw_args_t *args); int (*l_write)(struct rtems_termios_tty *tp,rtems_libio_rw_args_t *args); int (*l_rint )(int c,struct rtems_termios_tty *tp); - int (*l_start)(struct rtems_termios_tty *tp); + int (*l_start)(struct rtems_termios_tty *tp,int len); int (*l_ioctl)(struct rtems_termios_tty *tp,rtems_libio_ioctl_args_t *args); int (*l_modem)(struct rtems_termios_tty *tp,int flags); }; diff --git a/cpukit/include/rtems/version.h b/cpukit/include/rtems/version.h index a8aff732f3..24104022b5 100644 --- a/cpukit/include/rtems/version.h +++ b/cpukit/include/rtems/version.h @@ -29,6 +29,27 @@ extern "C" { * * @brief The Version API provides functions to return the version or parts of * the version of RTEMS you are using. + * + * A branch in the version control system will always fall back to a + * NOT-RELEASED version number with a minor number of 0. Only the release + * archives have a VERSION file with a final release number. That means for + * example that the 5 development branch will still show a version 5.0.0 even + * after the 5.1 release. + * + * The reason for that are the following: + * + * 1. All pre-release tests are performed with a specific git hash. A committed + * VERSION file would need to be changed and committed afterwards for releasing + * with the required release version causing the released version to have a + * different git hash and the test results couldn't be linked to the released + * version. + * + * 2. Users deploying RTEMS would need to commit a local change to a committed + * VERSION file and that would clash with the project changes. Deployment can + * use the project repos directly. + * + * 3. The VERSION file management and generation is the responsibility of the + * release manager and the release process. */ /**@{**/ diff --git a/cpukit/libcsupport/src/posix_devctl.c b/cpukit/libcsupport/src/posix_devctl.c index 3ff9dd929f..d875895b84 100644 --- a/cpukit/libcsupport/src/posix_devctl.c +++ b/cpukit/libcsupport/src/posix_devctl.c @@ -35,6 +35,7 @@ #include <rtems/seterr.h> #include <unistd.h> +#include <fcntl.h> int posix_devctl( int fd, @@ -44,6 +45,15 @@ int posix_devctl( int *__restrict dev_info_ptr ) { + int rv = 0; + + /* + * posix_devctl() is supposed to return an errno. eerno needs to be + * preserved in spite of calling methods (e.g., close, fcntl, and ioctl) + * that set it. + */ + int errno_copy = errno; + /* * The POSIX 1003.26 standard allows for library implementations * that implement posix_devctl() using ioctl(). In this case, @@ -72,15 +82,69 @@ int posix_devctl( } /* - * The FACE Technical Standard Edition 3.0 and newer requires the SOCKCLOSE - * ioctl command. This is because the Security Profile does not include - * close() and applications need a way to close sockets. Closing sockets is - * a minimum requirement so using close() in the implementation meets that - * requirement but also lets the application close other file types. + * */ - if (dcmd == SOCKCLOSE ) { - return close(fd); + switch (dcmd) { + + /* + * The FACE Technical Standard Edition 3.0 and newer requires the SOCKCLOSE + * ioctl command. This is because the Security Profile does not include + * close() and applications need a way to close sockets. Closing sockets is + * a minimum requirement so using close() in the implementation meets that + * requirement but also lets the application close other file types. + */ + case SOCKCLOSE: + if (close(fd) != 0) { + rv = errno; + errno = errno_copy; + + return rv; + } + break; + + /* + * The FACE Technical Standard Edition 3.0 and newer requires the + * posix_devctl command to support the FIONBIO subcommand. + */ + case FIONBIO: { + int tmp_flag; + int flag; + + if (nbyte != sizeof(int)) { + return EINVAL; + } + + tmp_flag = fcntl(fd, F_GETFL, 0); + if (tmp_flag == -1) { + rv = errno; + errno = errno_copy; + + return rv; + } + + flag = *(int *)dev_data_ptr; + + if (flag != 0) { + tmp_flag |= O_NONBLOCK; + } else { + tmp_flag &= ~O_NONBLOCK; + } + + (void) fcntl(fd, F_SETFL, tmp_flag); + break; + } + + default: + if (ioctl(fd, dcmd, dev_data_ptr) != 0) { + rv = errno; + errno = errno_copy; + + return errno; + } + break; } - return ioctl(fd, dcmd, dev_data_ptr); + errno = errno_copy; + + return rv; } diff --git a/cpukit/libcsupport/src/sync.c b/cpukit/libcsupport/src/sync.c index 265c6f07c9..0ea77422f1 100644 --- a/cpukit/libcsupport/src/sync.c +++ b/cpukit/libcsupport/src/sync.c @@ -1,13 +1,13 @@ /** - * @file + * @file * - * @brief Synchronize Data on Disk with Memory - * @ingroup libcsupport + * @ingroup libcsupport + * + * @brief This source file contains the implementation of sync(). */ /* - * COPYRIGHT (c) 1989-2008. - * On-Line Applications Research Corporation (OAR). + * Copyright (C) 2022 embedded brains GmbH * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at @@ -18,85 +18,16 @@ #include "config.h" #endif -/* Since we compile with strict ANSI we need to undef it to get - * prototypes for extensions - */ -#undef __STRICT_ANSI__ -int fdatasync(int); /* still not always prototyped */ - - #include <unistd.h> -#include <stdio.h> - -#include <rtems.h> -#include <rtems/score/thread.h> -#include <rtems/score/percpu.h> - -/* XXX check standards -- Linux version appears to be void */ -void _fwalk(struct _reent *, void *); - - -static void sync_wrapper(FILE *f) -{ - int fn = fileno(f); - - /* - * There is no way to report errors here. So this is a best-effort approach. - */ - (void) fsync(fn); - (void) fdatasync(fn); -} -/* iterate over all FILE *'s for this thread */ -static bool sync_per_thread(Thread_Control *t, void *arg) -{ - struct _reent *current_reent; - struct _reent *this_reent; - - /* - * The sync_wrapper() function will operate on the current thread's - * reent structure so we will temporarily use that. - */ - this_reent = t->libc_reent; - if ( this_reent ) { - Thread_Control *executing = _Thread_Get_executing(); - current_reent = executing->libc_reent; - executing->libc_reent = this_reent; - _fwalk (t->libc_reent, sync_wrapper); - executing->libc_reent = current_reent; - } - - return false; -} +#include <rtems/libio_.h> -/* - * _global_impure_ptr is not prototyped in any .h files. - * We have to extern it here. - */ -extern struct _reent * const _global_impure_ptr __ATTRIBUTE_IMPURE_PTR__; - -/** - * This function operates by as follows: - * for all threads - * for all FILE * - * fsync() - * fdatasync() - */ -void sync(void) +void sync( void ) { + int fd; - /* - * Walk the one used initially by RTEMS. - */ - _fwalk(_global_impure_ptr, sync_wrapper); - - /* - * XXX Do we walk the one used globally by newlib? - * XXX Do we need the RTEMS global one? - */ - - /* - * Now walk all the per-thread reentrancy structures. - */ - rtems_task_iterate(sync_per_thread, NULL); + for ( fd = 0; fd < (int) rtems_libio_number_iops; ++fd ) { + (void) fsync( fd ); + (void) fdatasync( fd ); + } } diff --git a/cpukit/libcsupport/src/termios.c b/cpukit/libcsupport/src/termios.c index 75925cf8ec..2b354203e9 100644 --- a/cpukit/libcsupport/src/termios.c +++ b/cpukit/libcsupport/src/termios.c @@ -1966,6 +1966,7 @@ rtems_termios_dequeue_characters (void *ttyp, int len) /* * send wake up to transmitter task */ + tty->txTaskCharsDequeued = len; sc = rtems_event_send(tty->txTaskId, TERMIOS_TX_START_EVENT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); @@ -1977,7 +1978,7 @@ rtems_termios_dequeue_characters (void *ttyp, int len) * call PPP line discipline start function */ if (rtems_termios_linesw[tty->t_line].l_start != NULL) { - rtems_termios_linesw[tty->t_line].l_start(tty); + rtems_termios_linesw[tty->t_line].l_start(tty, len); } return 0; /* nothing to output in IRQ... */ } @@ -2012,7 +2013,7 @@ static rtems_task rtems_termios_txdaemon(rtems_task_argument argument) * call any line discipline start function */ if (rtems_termios_linesw[tty->t_line].l_start != NULL) { - rtems_termios_linesw[tty->t_line].l_start(tty); + rtems_termios_linesw[tty->t_line].l_start(tty, tty->txTaskCharsDequeued); if (tty->t_line == PPPDISC) { /* diff --git a/cpukit/libfs/src/imfs/imfs_memfile.c b/cpukit/libfs/src/imfs/imfs_memfile.c index 0f19859fac..b6cd7812f2 100644 --- a/cpukit/libfs/src/imfs/imfs_memfile.c +++ b/cpukit/libfs/src/imfs/imfs_memfile.c @@ -189,9 +189,10 @@ static int IMFS_memfile_extend( offset = 0; } } else { - for ( ; block>=old_blocks ; block-- ) { + for ( ; block>old_blocks ; block-- ) { IMFS_memfile_remove_block( memfile, block ); } + IMFS_memfile_remove_block( memfile, old_blocks ); rtems_set_errno_and_return_minus_one( ENOSPC ); } } diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt.c b/cpukit/libmisc/rtems-fdt/rtems-fdt.c index 39e70bffec..0c8ccfd2c2 100644 --- a/cpukit/libmisc/rtems-fdt/rtems-fdt.c +++ b/cpukit/libmisc/rtems-fdt/rtems-fdt.c @@ -163,7 +163,7 @@ rtems_fdt_init_index (rtems_fdt_handle* fdt, rtems_fdt_blob* blob) } names = calloc(1, total_name_memory); - if (!entries) + if (!names) { free(entries); return -RTEMS_FDT_ERR_NO_MEMORY; @@ -582,7 +582,7 @@ rtems_fdt_load (const char* filename, rtems_fdt_handle* handle) close (bf); return -RTEMS_FDT_ERR_READ_FAIL; } - r -= size; + size -= r; buf += r; } } @@ -680,14 +680,14 @@ rtems_fdt_unload (rtems_fdt_handle* handle) rtems_chain_extract_unprotected (&handle->blob->node); + rtems_fdt_release_index(&handle->blob->index); + free (handle->blob); handle->blob = NULL; rtems_fdt_unlock (fdt); - rtems_fdt_release_index(&handle->blob->index); - return 0; } diff --git a/cpukit/libmisc/shell/main_chmod.c b/cpukit/libmisc/shell/main_chmod.c index 0c39072f6b..288fd52f4c 100644 --- a/cpukit/libmisc/shell/main_chmod.c +++ b/cpukit/libmisc/shell/main_chmod.c @@ -53,7 +53,7 @@ static int rtems_shell_main_chmod( * Now change the files modes */ for (n=2 ; n < argc ; n++) - chmod(argv[n++], mode); + chmod(argv[n], mode); return 0; } diff --git a/cpukit/libmisc/shell/main_edit.c b/cpukit/libmisc/shell/main_edit.c index e43ff68d2b..681e8c5f3c 100644 --- a/cpukit/libmisc/shell/main_edit.c +++ b/cpukit/libmisc/shell/main_edit.c @@ -407,6 +407,9 @@ static void move_gap(struct editor *ed, int pos, int minsize) { if (gapsize + MINEXTEND > minsize) minsize = gapsize + MINEXTEND; newsize = (ed->end - ed->start) - gapsize + minsize; start = (unsigned char *) malloc(newsize); // TODO check for out of memory + if (start == NULL) { + return; + } gap = start + pos; rest = gap + minsize; end = start + newsize; @@ -1789,14 +1792,14 @@ static void save_editor(struct editor *ed) { ed->refresh = 1; } -static void close_editor(struct editor *ed) { +static struct editor* close_editor(struct editor *ed) { struct env *env = ed->env; if (ed->dirty) { display_message(ed, "Close %s without saving changes (y/n)? ", ed->filename); if (!ask()) { ed->refresh = 1; - return; + return ed; } } @@ -1808,6 +1811,7 @@ static void close_editor(struct editor *ed) { new_file(ed, ""); } ed->refresh = 1; + return ed; } static void pipe_command(struct editor *ed) { @@ -2131,15 +2135,7 @@ static void edit(struct editor *ed) { case ctrl('s'): save_editor(ed); break; case ctrl('p'): pipe_command(ed); break; #endif -#if defined(__rtems__) - /* - * Coverity spotted this as using ed after free() so changing - * the order of the statements. - */ - case ctrl('w'): ed = ed->env->current; close_editor(ed); break; -#else - case ctrl('w'): close_editor(ed); ed = ed->env->current; break; -#endif + case ctrl('w'): ed = close_editor(ed); break; } } } diff --git a/cpukit/libmisc/shell/main_i2cdetect.c b/cpukit/libmisc/shell/main_i2cdetect.c new file mode 100644 index 0000000000..e953b4eaef --- /dev/null +++ b/cpukit/libmisc/shell/main_i2cdetect.c @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The command implemented here has a similar interface like the one from Linux + * i2c tools. Think of it as a heavily simplified version of them. Instead of + * the bus number they expect a bus path. + */ + +#include <dev/i2c/i2c.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <rtems/shell.h> + +static const char rtems_i2cdetect_shell_usage [] = + "i2cdetect <I2C_BUS>\n" + "\ttry to detect i2c devices on the given bus\n"; + +static int rtems_i2cdetect_shell_main(int argc, char *argv[]) +{ + int fd; + int rv; + const char *bus; + const uint16_t first = 1; + const uint16_t last = 0x7f; + uint16_t current; + + if (argc != 2 || strcmp(argv[1], "-h") == 0) { + printf(rtems_i2cdetect_shell_usage); + return 1; + } + + bus = argv[1]; + fd = open(bus, O_RDWR); + if (fd < 0) { + perror("Couldn't open bus"); + return 1; + } + + printf(" x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF\n" + "0x "); + for (current = first; current <= last; ++current) { + i2c_msg msg = { + .addr = current, + .flags = 0, + .len = 0, + .buf = NULL, + }; + + struct i2c_rdwr_ioctl_data payload = { + .msgs = &msg, + .nmsgs = 1, + }; + + if ((current & 0x0F) == 0) { + printf("\n%1xx ", current >> 4); + } + + rv = ioctl(fd, I2C_RDWR, &payload); + if (rv < 0) { + if (errno != EIO) { + perror("ioctl failed"); + } + printf(" --"); + } else { + printf(" %02x", current); + } + } + printf("\n"); + close(fd); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_I2CDETECT_Command = { + .name = "i2cdetect", + .usage = rtems_i2cdetect_shell_usage, + .topic = "misc", + .command = rtems_i2cdetect_shell_main, +}; diff --git a/cpukit/libmisc/shell/main_i2cget.c b/cpukit/libmisc/shell/main_i2cget.c new file mode 100644 index 0000000000..ffa551308b --- /dev/null +++ b/cpukit/libmisc/shell/main_i2cget.c @@ -0,0 +1,145 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The command implemented here has a similar interface like the one from Linux + * i2c tools. Think of it as a heavily simplified version of them. Instead of + * the bus number they expect a bus path. + * + * Additionally the i2cget has a continuous read mode that isn't available on + * Linux but does something similar to i2cdump. + */ + +#include <dev/i2c/i2c.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> + +#include <rtems/shell.h> + +static const char rtems_i2cget_shell_usage [] = + "i2cget <I2C_BUS> <CHIP-ADDRESS> <DATA-ADDRESS> [<NR-BYTES>]\n" + "\tGet one or more bytes from an EEPROM like i2c device.\n" + "\tNote that multiple bytes will be read in continuous mode.\n"; + +static int read_bytes( + int fd, + uint16_t i2c_address, + uint8_t data_address, + uint16_t nr_bytes +) +{ + int rv; + uint8_t value[nr_bytes]; + i2c_msg msgs[] = {{ + .addr = i2c_address, + .flags = 0, + .buf = &data_address, + .len = 1, + }, { + .addr = i2c_address, + .flags = I2C_M_RD, + .buf = value, + .len = nr_bytes, + }}; + struct i2c_rdwr_ioctl_data payload = { + .msgs = msgs, + .nmsgs = sizeof(msgs)/sizeof(msgs[0]), + }; + uint16_t i; + + rv = ioctl(fd, I2C_RDWR, &payload); + if (rv < 0) { + perror("ioctl failed"); + } else { + for (i = 0; i < nr_bytes; ++i) { + printf("0x%02x ", value[i]); + } + printf("\n"); + } + + return rv; +} + +static int rtems_i2cget_shell_main(int argc, char *argv[]) +{ + int fd; + int rv; + const char *bus; + uint16_t chip_address; + uint8_t data_address; + uint16_t nr_bytes; + + if (argc < 4 || argc > 5) { + printf(rtems_i2cget_shell_usage); + return 1; + } + + errno = 0; + chip_address = (uint16_t) strtoul(argv[2], NULL, 0); + if (errno != 0) { + perror("Couldn't read chip address"); + return 1; + } + + errno = 0; + data_address = (uint8_t) strtoul(argv[3], NULL, 0); + if (errno != 0) { + perror("Couldn't read data address"); + return 1; + } + + nr_bytes = 1; + if (argc == 5) { + errno = 0; + nr_bytes = (uint16_t) strtoul(argv[4], NULL, 0); + if (errno != 0) { + perror("Couldn't read number of bytes"); + return 1; + } + } + + bus = argv[1]; + fd = open(bus, O_RDWR); + if (fd < 0) { + perror("Couldn't open bus"); + return 1; + } + + rv = read_bytes(fd, chip_address, data_address, nr_bytes); + + close(fd); + + return rv; +} + +rtems_shell_cmd_t rtems_shell_I2CGET_Command = { + .name = "i2cget", + .usage = rtems_i2cget_shell_usage, + .topic = "misc", + .command = rtems_i2cget_shell_main, +}; diff --git a/cpukit/libmisc/shell/main_i2cset.c b/cpukit/libmisc/shell/main_i2cset.c new file mode 100644 index 0000000000..d9025b3b28 --- /dev/null +++ b/cpukit/libmisc/shell/main_i2cset.c @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The command implemented here has a similar interface like the one from Linux + * i2c tools. Think of it as a heavily simplified version of them. Instead of + * the bus number they expect a bus path. + * + * Additionally it is possible to write multiple values as a continuous write. + */ + +#include <dev/i2c/i2c.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> + +#include <rtems/shell.h> + +static const char rtems_i2cset_shell_usage [] = + "i2cset <I2C_BUS> <CHIP-ADDRESS> <DATA-ADDRESS> <VALUE> [<VALUE> [...]]\n" + "\tset one byte of an EEPROM like i2c device\n"; + +static int +rtems_i2cset_shell_main(int argc, char *argv[]) +{ + int fd; + int rv; + const char *bus; + uint16_t chip_address; + /* Necessary: data-address and values. This will be a bit more. */ + uint8_t writebuff[argc]; + size_t len; + size_t i; + i2c_msg msgs[] = {{ + .flags = 0, + .buf = writebuff, + .len = 0, + }}; + struct i2c_rdwr_ioctl_data payload = { + .msgs = msgs, + .nmsgs = sizeof(msgs)/sizeof(msgs[0]), + }; + + if (argc < 5) { + printf(rtems_i2cset_shell_usage); + return 1; + } + + errno = 0; + chip_address = (uint16_t) strtoul(argv[2], NULL, 0); + if (errno != 0) { + perror("Couldn't read CHIP_ADDRESS"); + return 1; + } + msgs[0].addr = chip_address; + + errno = 0; + writebuff[0] = (uint8_t) strtoul(argv[3], NULL, 0); + if (errno != 0) { + perror("Couldn't read DATA_ADDRESS"); + return 1; + } + + /* Read values starting from the fifth argument (index 4) */ + i = 4; + len = 0; + while (i < argc) { + errno = 0; + writebuff[len + 1] = (uint8_t) strtoul(argv[i], NULL, 0); + if (errno != 0) { + perror("Couldn't read VALUE"); + return 1; + } + ++i; + ++len; + } + msgs[0].len = len + 1; /* Don't forget address */ + + bus = argv[1]; + fd = open(bus, O_RDWR); + if (fd < 0) { + perror("Couldn't open bus"); + return 1; + } + + rv = ioctl(fd, I2C_RDWR, &payload); + if (rv < 0) { + perror("ioctl failed"); + } + close(fd); + + return rv; +} + +rtems_shell_cmd_t rtems_shell_I2CSET_Command = { + .name = "i2cset", + .usage = rtems_i2cset_shell_usage, + .topic = "misc", + .command = rtems_i2cset_shell_main, +}; diff --git a/cpukit/libmisc/shell/main_spi.c b/cpukit/libmisc/shell/main_spi.c new file mode 100644 index 0000000000..487a22fc6c --- /dev/null +++ b/cpukit/libmisc/shell/main_spi.c @@ -0,0 +1,157 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2020 embedded brains GmbH. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <dev/spi/spi.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> + +#include <rtems/shell.h> + +static const char rtems_spi_shell_usage [] = + "simple SPI read / write\n" + "\n" + "spi [-loh] [-c <cs>] [-s <speed>] [-m <mode>] <SPI_BUS> xx [xx [..]]\n" + " <SPI_BUS> Bus device to use\n" + " xx Hex value of a byte to send\n" + " -c <cs> Use chip select <cs> (default: None)\n" + " -m <mode> Use SPI mode <mode> (default: 0)\n" + " -l Send LSB first\n" + " -o Use loopback mode\n" + " -s <speed> Bus speed in hz\n" + " -h Print this help\n"; + +static int rtems_spi_shell_main(int argc, char *argv[]) +{ + uint8_t buffer[argc - 1]; + size_t len = 0; + int i; + size_t j; + int rv; + int fd; + char *bus = NULL; + unsigned long mode; + spi_ioc_transfer msg = { + .len = 0, + .rx_buf = buffer, + .tx_buf = buffer, + .speed_hz = 100000, + .bits_per_word = 8, + .cs_change = true, + .mode = SPI_MODE_0 | SPI_NO_CS, + }; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case ('c'): + errno = 0; + msg.mode &= ~SPI_NO_CS; + msg.cs = (uint8_t) strtoul(argv[i+1], NULL, 0); + ++i; + if (errno != 0) { + printf("Couldn't process chip select\n"); + return 1; + } + break; + case ('m'): + errno = 0; + mode = strtoul(argv[i+1], NULL, 0); + ++i; + if (errno != 0 || mode > 3) { + printf("Couldn't process mode\n"); + return 1; + } + msg.mode &= ~(SPI_CPOL | SPI_CPHA); + msg.mode |= mode; + break; + case ('s'): + errno = 0; + msg.speed_hz = (uint32_t) strtoul(argv[i+1], NULL, 0); + ++i; + if (errno != 0) { + printf("Couldn't process speed\n"); + return 1; + } + break; + case ('l'): + msg.mode |= SPI_LSB_FIRST; + break; + case ('o'): + msg.mode |= SPI_LOOP; + break; + case ('h'): + /* fallthrough */ + default: + printf(rtems_spi_shell_usage); + return 1; + } + } else if (bus == NULL) { + bus = argv[i]; + } else { + errno = 0; + buffer[len] = (uint8_t) strtol(argv[i], NULL, 16); + if (errno != 0) { + printf("Couldn't process '%s'\n", argv[i]); + return 1; + } + ++len; + } + } + + if (len == 0) { + printf("Nothing to do\n"); + return 0; + } + + fd = open(bus, O_RDWR); + if (fd < 0) { + perror("Couldn't open bus"); + return 1; + } + msg.len = len; + rv = ioctl(fd, SPI_IOC_MESSAGE(1), &msg); + if (rv == -1) { + perror("Couldn't send the message"); + } else { + printf("received:"); + for (j = 0; j < len; ++j) { + printf(" %02x", buffer[j]); + } + printf("\n"); + } + close(fd); + + return 0; +} + +rtems_shell_cmd_t rtems_shell_SPI_Command = { + .name = "spi", + .usage = rtems_spi_shell_usage, + .topic = "misc", + .command = rtems_spi_shell_main, +}; diff --git a/cpukit/libmisc/untar/untar.c b/cpukit/libmisc/untar/untar.c index a2f09fb99f..8888ab2c57 100644 --- a/cpukit/libmisc/untar/untar.c +++ b/cpukit/libmisc/untar/untar.c @@ -126,30 +126,25 @@ Make_Path(const rtems_printer *printer, char *path) *p = '\0'; if (p[1] == '\0') { - /* Speculatively unlink the last component so that it can be re-created */ - unlink(path); return 0; } if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { - if (errno == EEXIST || errno == EISDIR) { + if (errno == EEXIST) { + /* If it exists already: Check whether it is a directory */ struct stat sb; - - if (stat(path, &sb) != 0) { + if (lstat(path, &sb) != 0) { + Print_Error(printer, "lstat", path); + return -1; + } else if (!S_ISDIR(sb.st_mode)) { + rtems_printf(printer, + "untar: mkdir: %s: exists but is not a directory\n", + path); return -1; } - - if (!S_ISDIR(sb.st_mode)) { - if (unlink(path) != 0) { - Print_Error(printer, "unlink", path); - return -1; - } - - if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { - Print_Error(printer, "mkdir (unlink)", path); - return -1; - } - } + } else { + Print_Error(printer, "mkdir", path); + return -1; } } @@ -206,6 +201,12 @@ Untar_ProcessHeader( if (Make_Path(ctx->printer, ctx->file_path) != 0) { retval = UNTAR_FAIL; + } else { + /* + * Speculatively unlink. This should unlink everything but non-empty + * directories or write protected stuff. + */ + unlink(ctx->file_path); } if (ctx->linkflag == SYMTYPE) { @@ -225,8 +226,22 @@ Untar_ProcessHeader( rtems_printf(ctx->printer, "untar: dir: %s\n", ctx->file_path); r = mkdir(ctx->file_path, ctx->mode); if (r != 0) { - Print_Error(ctx->printer, "mkdir", ctx->file_path); - retval = UNTAR_FAIL; + if (errno == EEXIST) { + /* If it exists already: Check whether it is a directory */ + struct stat sb; + if (lstat(ctx->file_path, &sb) != 0) { + Print_Error(ctx->printer, "lstat", ctx->file_path); + retval = UNTAR_FAIL; + } else if (!S_ISDIR(sb.st_mode)) { + rtems_printf(ctx->printer, + "untar: mkdir: %s: exists but is not a directory\n", + ctx->file_path); + retval = UNTAR_FAIL; + } + } else { + Print_Error(ctx->printer, "mkdir", ctx->file_path); + retval = UNTAR_FAIL; + } } } @@ -426,7 +441,9 @@ Untar_FromFile_Print( */ if ((out_fd = creat(ctx.file_path, ctx.mode)) == -1) { - (void) lseek(fd, SEEK_CUR, 512UL * ctx.nblocks); + /* Couldn't create that file. Abort. */ + retval = UNTAR_FAIL; + break; } else { for (i = 0; i < ctx.nblocks; i++) { n = read(fd, bufr, 512); diff --git a/cpukit/posix/src/nanosleep.c b/cpukit/posix/src/nanosleep.c index 8d1a4b84e4..792d222e2b 100644 --- a/cpukit/posix/src/nanosleep.c +++ b/cpukit/posix/src/nanosleep.c @@ -64,7 +64,7 @@ int clock_nanosleep( ) { Thread_queue_Context queue_context; - struct timespec uptime; + struct timespec now; const struct timespec *end; Thread_Control *executing; int eno; @@ -81,21 +81,22 @@ int clock_nanosleep( if ( ( flags & TIMER_ABSTIME ) != 0 ) { end = rqtp; - + } else { if ( clock_id == CLOCK_REALTIME ) { - _Thread_queue_Context_set_enqueue_timeout_realtime_timespec( - &queue_context, - end - ); + _Timecounter_Nanotime( &now ); } else { - _Thread_queue_Context_set_enqueue_timeout_monotonic_timespec( - &queue_context, - end - ); + _Timecounter_Nanouptime( &now ); } + + end = _Watchdog_Future_timespec( &now, rqtp ); + } + + if ( clock_id == CLOCK_REALTIME ) { + _Thread_queue_Context_set_enqueue_timeout_realtime_timespec( + &queue_context, + end + ); } else { - _Timecounter_Nanouptime( &uptime ); - end = _Watchdog_Future_timespec( &uptime, rqtp ); _Thread_queue_Context_set_enqueue_timeout_monotonic_timespec( &queue_context, end @@ -120,7 +121,11 @@ int clock_nanosleep( if ( eno == EINTR ) { struct timespec actual_end; - _Timecounter_Nanouptime( &actual_end ); + if ( clock_id == CLOCK_REALTIME ) { + _Timecounter_Nanotime( &actual_end ); + } else { + _Timecounter_Nanouptime( &actual_end ); + } if ( _Timespec_Less_than( &actual_end, end ) ) { _Timespec_Subtract( &actual_end, end, rmtp ); diff --git a/cpukit/rtems/src/ratemoncreate.c b/cpukit/rtems/src/ratemoncreate.c index 3eff5f0716..2e6b434d9e 100644 --- a/cpukit/rtems/src/ratemoncreate.c +++ b/cpukit/rtems/src/ratemoncreate.c @@ -53,6 +53,7 @@ rtems_status_code rtems_rate_monotonic_create( the_period->owner = _Thread_Get_executing(); the_period->state = RATE_MONOTONIC_INACTIVE; + the_period->postponed_jobs = 0; _Watchdog_Preinitialize( &the_period->Timer, _Per_CPU_Get_by_index( 0 ) ); _Watchdog_Initialize( &the_period->Timer, _Rate_monotonic_Timeout ); diff --git a/cpukit/rtems/src/semcreate.c b/cpukit/rtems/src/semcreate.c index b57b635d85..ea89800685 100644 --- a/cpukit/rtems/src/semcreate.c +++ b/cpukit/rtems/src/semcreate.c @@ -92,9 +92,14 @@ rtems_status_code rtems_semaphore_create( ) { variant = SEMAPHORE_VARIANT_MUTEX_PRIORITY_CEILING; } else if ( - mutex_with_protocol + ( mutex_with_protocol & ~RTEMS_PRIORITY ) == ( RTEMS_BINARY_SEMAPHORE | RTEMS_MULTIPROCESSOR_RESOURCE_SHARING ) ) { + /* + * In RTEMS 5.2 using RTEMS_FIFO and RTEMS_PRIORITY for MrsP semaphores is + * allowed. In RTEMS 6, RTEMS_PRIORITY is required for MrsP semaphores + * analogous to priority ceiling semaphores. + */ #if defined(RTEMS_SMP) variant = SEMAPHORE_VARIANT_MRSP; #else diff --git a/cpukit/rtems/src/taskstart.c b/cpukit/rtems/src/taskstart.c index 247756fcb1..08bdfe8027 100644 --- a/cpukit/rtems/src/taskstart.c +++ b/cpukit/rtems/src/taskstart.c @@ -41,6 +41,10 @@ rtems_status_code rtems_task_start( ISR_lock_Context lock_context; bool ok; + if ( entry_point == NULL ) { + return RTEMS_INVALID_ADDRESS; + } + the_thread = _Thread_Get( id, &lock_context ); if ( the_thread == NULL ) { diff --git a/cpukit/score/cpu/sparc/cpu_asm.S b/cpukit/score/cpu/sparc/cpu_asm.S index d5afd5f7b0..1251faa2f7 100644 --- a/cpukit/score/cpu/sparc/cpu_asm.S +++ b/cpukit/score/cpu/sparc/cpu_asm.S @@ -523,8 +523,9 @@ dont_do_the_window: * context. */ andcc %l0, %l5, %g0 - bne,a dont_switch_stacks - st %fsr, [%g6 + SPARC_PER_CPU_FSR_OFFSET] + beq dont_switch_stacks + nop + st %fsr, [%g6 + SPARC_PER_CPU_FSR_OFFSET] #endif dont_switch_stacks: @@ -683,11 +684,13 @@ isr_dispatch: cmp %l6, %l7 bne,a .Ldisable_fp andn %l0, %l5, %l0 + st %g0, [%g6 + SPARC_PER_CPU_FP_OWNER_OFFSET] ba .Lthread_dispatch_done - st %g0, [%g6 + SPARC_PER_CPU_FP_OWNER_OFFSET] + nop .Ldisable_fp: + st %l0, [%fp + ISF_PSR_OFFSET] ba .Lthread_dispatch_done - st %l0, [%fp + ISF_PSR_OFFSET] + nop .Lnon_fp_thread_dispatch: #elif defined(SPARC_USE_SYNCHRONOUS_FP_SWITCH) /* Test if we interrupted a floating point thread (PSR[EF] == 1) */ diff --git a/cpukit/score/cpu/sparc/sparc-access.S b/cpukit/score/cpu/sparc/sparc-access.S index 9397cb815b..277fb7e652 100644 --- a/cpukit/score/cpu/sparc/sparc-access.S +++ b/cpukit/score/cpu/sparc/sparc-access.S @@ -80,7 +80,9 @@ SYM(_st32): SYM(_st_be64): SYM(_st64): - std %o1, [%o0] + mov %o2, %o3 + mov %o1, %o2 + std %o2, [%o0] retl nop diff --git a/cpukit/score/cpu/sparc/sparc-counter-asm.S b/cpukit/score/cpu/sparc/sparc-counter-asm.S index a1e18ae5b7..fb7783e096 100644 --- a/cpukit/score/cpu/sparc/sparc-counter-asm.S +++ b/cpukit/score/cpu/sparc/sparc-counter-asm.S @@ -31,8 +31,9 @@ SYM(_SPARC_Counter_read_default): sethi %hi(_SPARC_Counter + 12), %o1 ld [%o1 + %lo(_SPARC_Counter + 12)], %o0 add %o0, 1, %o0 + st %o0, [%o1 + %lo(_SPARC_Counter + 12)] jmp %o7 + 8 - st %o0, [%o1 + %lo(_SPARC_Counter + 12)] + nop PUBLIC(_SPARC_Counter_read_up) PUBLIC(_SPARC_Get_timecount_up) diff --git a/cpukit/score/cpu/sparc/syscall.S b/cpukit/score/cpu/sparc/syscall.S index 4f4ef32c53..737a501098 100644 --- a/cpukit/score/cpu/sparc/syscall.S +++ b/cpukit/score/cpu/sparc/syscall.S @@ -218,9 +218,10 @@ SYM(syscall_lazy_fp_switch): .Lfp_save_done: /* Restore the floating point context if necessary */ + st %g0, [%l4 + %lo(SPARC_THREAD_CONTROL_REGISTERS_FP_CONTEXT_OFFSET)] cmp %l6, 0 be .Lfp_restore_done - st %g0, [%l4 + %lo(SPARC_THREAD_CONTROL_REGISTERS_FP_CONTEXT_OFFSET)] + nop ldd [%l6 + SPARC_FP_CONTEXT_OFFSET_F0_F1], %f0 ldd [%l6 + SPARC_FP_CONTEXT_OFFSET_F2_F3], %f2 ldd [%l6 + SPARC_FP_CONTEXT_OFFSET_F4_F5], %f4 diff --git a/cpukit/score/src/corebarrierrelease.c b/cpukit/score/src/corebarrierrelease.c index a2e68862b3..da8da0a7d5 100644 --- a/cpukit/score/src/corebarrierrelease.c +++ b/cpukit/score/src/corebarrierrelease.c @@ -27,10 +27,9 @@ uint32_t _CORE_barrier_Do_flush( Thread_queue_Context *queue_context ) { - the_barrier->number_of_waiting_threads = 0; return _Thread_queue_Flush_critical( &the_barrier->Wait_queue.Queue, - CORE_BARRIER_TQ_OPERATIONS, + &_CORE_barrier_Thread_queue_operations, filter, queue_context ); diff --git a/cpukit/score/src/corebarrierwait.c b/cpukit/score/src/corebarrierwait.c index f45873ccb8..2c59460c69 100644 --- a/cpukit/score/src/corebarrierwait.c +++ b/cpukit/score/src/corebarrierwait.c @@ -21,6 +21,36 @@ #include <rtems/score/corebarrierimpl.h> #include <rtems/score/statesimpl.h> #include <rtems/score/threadimpl.h> +#include <rtems/score/threadqops.h> + +static void _CORE_barrier_Thread_queue_extract( + Thread_queue_Queue *queue, + Thread_Control *the_thread, + Thread_queue_Context *queue_context +) +{ + CORE_barrier_Control *the_barrier; + + the_barrier = RTEMS_CONTAINER_OF( + queue, + CORE_barrier_Control, + Wait_queue.Queue + ); + --the_barrier->number_of_waiting_threads; + _Thread_queue_FIFO_extract( + &the_barrier->Wait_queue.Queue, + the_thread, + queue_context + ); +} + +const Thread_queue_Operations _CORE_barrier_Thread_queue_operations = { + .priority_actions = _Thread_queue_Do_nothing_priority_actions, + .enqueue = _Thread_queue_FIFO_enqueue, + .extract = _CORE_barrier_Thread_queue_extract, + .surrender = _Thread_queue_FIFO_surrender, + .first = _Thread_queue_FIFO_first +}; Status_Control _CORE_barrier_Seize( CORE_barrier_Control *the_barrier, @@ -50,7 +80,7 @@ Status_Control _CORE_barrier_Seize( ); _Thread_queue_Enqueue( &the_barrier->Wait_queue.Queue, - CORE_BARRIER_TQ_OPERATIONS, + &_CORE_barrier_Thread_queue_operations, executing, queue_context ); diff --git a/cpukit/score/src/heapallocate.c b/cpukit/score/src/heapallocate.c index 4b8b3f0de6..1c71e4a037 100644 --- a/cpukit/score/src/heapallocate.c +++ b/cpukit/score/src/heapallocate.c @@ -73,6 +73,15 @@ return search_again; } + + void _Heap_Protection_free_all_delayed_blocks( Heap_Control *heap ) + { + bool search_again; + + do { + search_again = _Heap_Protection_free_delayed_blocks( heap, 0 ); + } while ( search_again ); + } #endif #ifdef RTEMS_HEAP_DEBUG diff --git a/cpukit/score/src/kern_tc.c b/cpukit/score/src/kern_tc.c index 1b65cf41ee..56ec4751ce 100644 --- a/cpukit/score/src/kern_tc.c +++ b/cpukit/score/src/kern_tc.c @@ -1,4 +1,6 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you @@ -7,7 +9,6 @@ * ---------------------------------------------------------------------------- * * Copyright (c) 2011, 2015, 2016 The FreeBSD Foundation - * All rights reserved. * * Portions of this software were developed by Julien Ridoux at the University * of Melbourne under sponsorship from the FreeBSD Foundation. @@ -44,9 +45,8 @@ #include <rtems/score/watchdogimpl.h> #endif /* __rtems__ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD: head/sys/kern/kern_tc.c 324528 2017-10-11 11:03:11Z kib $"); +__FBSDID("$FreeBSD$"); -#include "opt_compat.h" #include "opt_ntp.h" #include "opt_ffclock.h" @@ -79,16 +79,7 @@ ISR_LOCK_DEFINE(, _Timecounter_Lock, "Timecounter") _ISR_lock_Release_and_ISR_enable(&_Timecounter_Lock, lock_context) #define hz rtems_clock_get_ticks_per_second() #define printf(...) -#define bcopy(x, y, z) memcpy(y, x, z); #define log(...) -static inline int -builtin_fls(int x) -{ - return x ? sizeof(x) * 8 - __builtin_clz(x) : 0; -} -#define fls(x) builtin_fls(x) -/* FIXME: https://devel.rtems.org/ticket/2348 */ -#define ntp_update_second(a, b) do { (void) a; (void) b; } while (0) static inline void atomic_thread_fence_acq(void) @@ -117,6 +108,24 @@ atomic_store_rel_int(Atomic_Uint *i, u_int val) _Atomic_Store_uint(i, val, ATOMIC_ORDER_RELEASE); } + +static inline void * +atomic_load_ptr(void *ptr) +{ + + return ((void *)_Atomic_Load_uintptr(ptr, ATOMIC_ORDER_RELAXED)); +} + +static Timecounter_NTP_update_second _Timecounter_NTP_update_second; + +void +_Timecounter_Set_NTP_update_second(Timecounter_NTP_update_second handler) +{ + + _Timecounter_NTP_update_second = handler; +} + +#define ntp_update_second(a, b) (*ntp_update_second_handler)(a, b) #endif /* __rtems__ */ /* @@ -158,6 +167,7 @@ struct timehands { struct timecounter *th_counter; int64_t th_adjustment; uint64_t th_scale; + uint32_t th_large_delta; uint32_t th_offset_count; struct bintime th_offset; struct bintime th_bintime; @@ -173,6 +183,40 @@ struct timehands { struct timehands *th_next; }; +#ifndef __rtems__ +static struct timehands ths[16] = { + [0] = { + .th_counter = &dummy_timecounter, + .th_scale = (uint64_t)-1 / 1000000, + .th_large_delta = 1000000, + .th_offset = { .sec = 1 }, + .th_generation = 1, + }, +}; + +static struct timehands *volatile timehands = &ths[0]; +struct timecounter *timecounter = &dummy_timecounter; +static struct timecounter *timecounters = &dummy_timecounter; + +/* Mutex to protect the timecounter list. */ +static struct mtx tc_lock; +MTX_SYSINIT(tc_lock, &tc_lock, "tc", MTX_DEF); + +int tc_min_ticktock_freq = 1; +#else /* __rtems__ */ +/* + * In FreeBSD, the timehands count is a tuning option from two to 16. The + * tuning option was added since it is inexpensive and some FreeBSD users asked + * for it to play around. The default value is two. One system which did not + * work with two timehands was a system with one processor and a specific PPS + * device. + * + * For RTEMS, in uniprocessor configurations, just use one timehand since the + * update is done with interrupt disabled. + * + * In SMP configurations, use a fixed set of two timehands until someone + * reports an issue. + */ #if defined(RTEMS_SMP) static struct timehands th0; static struct timehands th1 = { @@ -183,7 +227,8 @@ static struct timehands th0 = { .th_counter = &dummy_timecounter, .th_scale = (uint64_t)-1 / 1000000, .th_offset = { .sec = 1 }, - .th_generation = 1, + .th_large_delta = 1000000, + .th_generation = UINT_MAX, #ifdef __rtems__ .th_bintime = { .sec = TOD_SECONDS_1970_THROUGH_1988 }, .th_microtime = { TOD_SECONDS_1970_THROUGH_1988, 0 }, @@ -199,10 +244,6 @@ static struct timehands th0 = { static struct timehands *volatile timehands = &th0; struct timecounter *timecounter = &dummy_timecounter; -#ifndef __rtems__ -static struct timecounter *timecounters = &dummy_timecounter; - -int tc_min_ticktock_freq = 1; #endif /* __rtems__ */ #ifndef __rtems__ @@ -214,17 +255,33 @@ volatile int32_t time_uptime = 1; #endif /* __rtems__ */ #ifndef __rtems__ +/* + * The system time is always computed by summing the estimated boot time and the + * system uptime. The timehands track boot time, but it changes when the system + * time is set by the user, stepped by ntpd or adjusted when resuming. It + * is set to new_time - uptime. + */ static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS); -SYSCTL_PROC(_kern, KERN_BOOTTIME, boottime, CTLTYPE_STRUCT|CTLFLAG_RD, - NULL, 0, sysctl_kern_boottime, "S,timeval", "System boottime"); +SYSCTL_PROC(_kern, KERN_BOOTTIME, boottime, + CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, + sysctl_kern_boottime, "S,timeval", + "Estimated system boottime"); -SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, ""); -static SYSCTL_NODE(_kern_timecounter, OID_AUTO, tc, CTLFLAG_RW, 0, ""); +SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + ""); +static SYSCTL_NODE(_kern_timecounter, OID_AUTO, tc, + CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + ""); static int timestepwarnings; -SYSCTL_INT(_kern_timecounter, OID_AUTO, stepwarnings, CTLFLAG_RW, +SYSCTL_INT(_kern_timecounter, OID_AUTO, stepwarnings, CTLFLAG_RWTUN, ×tepwarnings, 0, "Log time steps"); +static int timehands_count = 2; +SYSCTL_INT(_kern_timecounter, OID_AUTO, timehands_count, + CTLFLAG_RDTUN | CTLFLAG_NOFETCH, + &timehands_count, 0, "Count of timehands in rotation"); + struct bintime bt_timethreshold; struct bintime bt_tickthreshold; sbintime_t sbt_timethreshold; @@ -242,6 +299,7 @@ SYSCTL_PROC(_kern_timecounter, OID_AUTO, alloweddeviation, volatile int rtc_generation = 1; static int tc_chosen; /* Non-zero if a specific tc was chosen via sysctl. */ +static char tc_from_tunable[16]; #endif /* __rtems__ */ static void tc_windup(struct bintime *new_boottimebin); @@ -253,6 +311,7 @@ static void _Timecounter_Windup(struct bintime *new_boottimebin, #endif /* __rtems__ */ void dtrace_getnanotime(struct timespec *tsp); +void dtrace_getnanouptime(struct timespec *tsp); #ifndef __rtems__ static int @@ -262,7 +321,8 @@ sysctl_kern_boottime(SYSCTL_HANDLER_ARGS) getboottime(&boottime); -#ifndef __mips__ +/* i386 is the only arch which uses a 32bits time_t */ +#ifdef __amd64__ #ifdef SCTL_MASK32 int tv[2]; @@ -317,20 +377,85 @@ tc_delta(struct timehands *th) * the comment in <sys/time.h> for a description of these 12 functions. */ -#ifdef FFCLOCK -void -fbclock_binuptime(struct bintime *bt) +static __inline void +bintime_off(struct bintime *bt, u_int off) { struct timehands *th; - unsigned int gen; + struct bintime *btp; + uint64_t scale, x; +#ifndef __rtems__ + u_int delta, gen, large_delta; +#else /* __rtems__ */ + uint32_t delta, large_delta; + u_int gen; +#endif /* __rtems__ */ do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_offset; - bintime_addx(bt, th->th_scale * tc_delta(th)); + btp = (struct bintime *)((vm_offset_t)th + off); + *bt = *btp; + scale = th->th_scale; + delta = tc_delta(th); + large_delta = th->th_large_delta; atomic_thread_fence_acq(); +#if defined(RTEMS_SMP) } while (gen == 0 || gen != th->th_generation); +#else + } while (gen != th->th_generation); +#endif + + if (__predict_false(delta >= large_delta)) { + /* Avoid overflow for scale * delta. */ + x = (scale >> 32) * delta; + bt->sec += x >> 32; + bintime_addx(bt, x << 32); + bintime_addx(bt, (scale & 0xffffffff) * delta); + } else { + bintime_addx(bt, scale * delta); + } +} +#define GETTHBINTIME(dst, member) \ +do { \ + _Static_assert(_Generic(((struct timehands *)NULL)->member, \ + struct bintime: 1, default: 0) == 1, \ + "struct timehands member is not of struct bintime type"); \ + bintime_off(dst, __offsetof(struct timehands, member)); \ +} while (0) + +static __inline void +getthmember(void *out, size_t out_size, u_int off) +{ + struct timehands *th; + u_int gen; + + do { + th = timehands; + gen = atomic_load_acq_int(&th->th_generation); + memcpy(out, (char *)th + off, out_size); + atomic_thread_fence_acq(); +#if defined(RTEMS_SMP) + } while (gen == 0 || gen != th->th_generation); +#else + } while (gen != th->th_generation); +#endif +} +#define GETTHMEMBER(dst, member) \ +do { \ + _Static_assert(_Generic(*dst, \ + __typeof(((struct timehands *)NULL)->member): 1, \ + default: 0) == 1, \ + "*dst and struct timehands member have different types"); \ + getthmember(dst, sizeof(*dst), __offsetof(struct timehands, \ + member)); \ +} while (0) + +#ifdef FFCLOCK +void +fbclock_binuptime(struct bintime *bt) +{ + + GETTHBINTIME(bt, th_offset); } void @@ -354,16 +479,8 @@ fbclock_microuptime(struct timeval *tvp) void fbclock_bintime(struct bintime *bt) { - struct timehands *th; - unsigned int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_bintime; - bintime_addx(bt, th->th_scale * tc_delta(th)); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHBINTIME(bt, th_bintime); } void @@ -387,116 +504,88 @@ fbclock_microtime(struct timeval *tvp) void fbclock_getbinuptime(struct bintime *bt) { - struct timehands *th; - unsigned int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_offset; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(bt, th_offset); } void fbclock_getnanouptime(struct timespec *tsp) { - struct timehands *th; - unsigned int gen; + struct bintime bt; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - bintime2timespec(&th->th_offset, tsp); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(&bt, th_offset); + bintime2timespec(&bt, tsp); } void fbclock_getmicrouptime(struct timeval *tvp) { - struct timehands *th; - unsigned int gen; + struct bintime bt; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - bintime2timeval(&th->th_offset, tvp); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(&bt, th_offset); + bintime2timeval(&bt, tvp); } void fbclock_getbintime(struct bintime *bt) { - struct timehands *th; - unsigned int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_bintime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(bt, th_bintime); } void fbclock_getnanotime(struct timespec *tsp) { - struct timehands *th; - unsigned int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *tsp = th->th_nanotime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(tsp, th_nanotime); } void fbclock_getmicrotime(struct timeval *tvp) { - struct timehands *th; - unsigned int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *tvp = th->th_microtime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(tvp, th_microtime); } #else /* !FFCLOCK */ + void binuptime(struct bintime *bt) { - struct timehands *th; - uint32_t gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_offset; - bintime_addx(bt, th->th_scale * tc_delta(th)); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHBINTIME(bt, th_offset); } #ifdef __rtems__ sbintime_t _Timecounter_Sbinuptime(void) { struct timehands *th; - uint32_t gen; sbintime_t sbt; + uint64_t scale; + uint32_t delta; + uint32_t large_delta; + u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); sbt = bttosbt(th->th_offset); - sbt += (th->th_scale * tc_delta(th)) >> 32; + scale = th->th_scale; + delta = tc_delta(th); + large_delta = th->th_large_delta; atomic_thread_fence_acq(); +#if defined(RTEMS_SMP) } while (gen == 0 || gen != th->th_generation); +#else + } while (gen != th->th_generation); +#endif + + if (__predict_false(delta >= large_delta)) { + /* Avoid overflow for scale * delta. */ + sbt += (scale >> 32) * delta; + sbt += ((scale & 0xffffffff) * delta) >> 32; + } else { + sbt += (scale * delta) >> 32; + } return (sbt); } @@ -523,16 +612,8 @@ microuptime(struct timeval *tvp) void bintime(struct bintime *bt) { - struct timehands *th; - u_int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_bintime; - bintime_addx(bt, th->th_scale * tc_delta(th)); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHBINTIME(bt, th_bintime); } void @@ -556,85 +637,47 @@ microtime(struct timeval *tvp) void getbinuptime(struct bintime *bt) { - struct timehands *th; - uint32_t gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_offset; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(bt, th_offset); } void getnanouptime(struct timespec *tsp) { - struct timehands *th; - uint32_t gen; + struct bintime bt; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - bintime2timespec(&th->th_offset, tsp); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(&bt, th_offset); + bintime2timespec(&bt, tsp); } void getmicrouptime(struct timeval *tvp) { - struct timehands *th; - uint32_t gen; + struct bintime bt; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - bintime2timeval(&th->th_offset, tvp); - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(&bt, th_offset); + bintime2timeval(&bt, tvp); } void getbintime(struct bintime *bt) { - struct timehands *th; - uint32_t gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *bt = th->th_bintime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(bt, th_bintime); } void getnanotime(struct timespec *tsp) { - struct timehands *th; - uint32_t gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *tsp = th->th_nanotime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(tsp, th_nanotime); } void getmicrotime(struct timeval *tvp) { - struct timehands *th; - uint32_t gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *tvp = th->th_microtime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(tvp, th_microtime); } #endif /* FFCLOCK */ @@ -650,15 +693,8 @@ getboottime(struct timeval *boottime) void getboottimebin(struct bintime *boottimebin) { - struct timehands *th; - u_int gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *boottimebin = th->th_boottime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(boottimebin, th_boottime); } #ifdef FFCLOCK @@ -1175,15 +1211,22 @@ getmicrotime(struct timeval *tvp) void dtrace_getnanotime(struct timespec *tsp) { - struct timehands *th; - uint32_t gen; - do { - th = timehands; - gen = atomic_load_acq_int(&th->th_generation); - *tsp = th->th_nanotime; - atomic_thread_fence_acq(); - } while (gen == 0 || gen != th->th_generation); + GETTHMEMBER(tsp, th_nanotime); +} + +/* + * This is a clone of getnanouptime used for time since boot. + * The dtrace_ prefix prevents fbt from creating probes for + * it so an uptime that can be safely used in all fbt probes. + */ +void +dtrace_getnanouptime(struct timespec *tsp) +{ + struct bintime bt; + + GETTHMEMBER(&bt, th_offset); + bintime2timespec(&bt, tsp); } #endif /* __rtems__ */ @@ -1364,26 +1407,32 @@ tc_init(struct timecounter *tc) tc->tc_quality); } - tc->tc_next = timecounters; - timecounters = tc; /* * Set up sysctl tree for this counter. */ tc_root = SYSCTL_ADD_NODE_WITH_LABEL(NULL, SYSCTL_STATIC_CHILDREN(_kern_timecounter_tc), OID_AUTO, tc->tc_name, - CTLFLAG_RW, 0, "timecounter description", "timecounter"); + CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "timecounter description", "timecounter"); SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "mask", CTLFLAG_RD, &(tc->tc_counter_mask), 0, "mask for implemented bits"); SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, - "counter", CTLTYPE_UINT | CTLFLAG_RD, tc, sizeof(*tc), - sysctl_kern_timecounter_get, "IU", "current timecounter value"); + "counter", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, tc, + sizeof(*tc), sysctl_kern_timecounter_get, "IU", + "current timecounter value"); SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, - "frequency", CTLTYPE_U64 | CTLFLAG_RD, tc, sizeof(*tc), - sysctl_kern_timecounter_freq, "QU", "timecounter frequency"); + "frequency", CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE, tc, + sizeof(*tc), sysctl_kern_timecounter_freq, "QU", + "timecounter frequency"); SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "quality", CTLFLAG_RD, &(tc->tc_quality), 0, "goodness of time counter"); + + mtx_lock(&tc_lock); + tc->tc_next = timecounters; + timecounters = tc; + /* * Do not automatically switch if the current tc was specifically * chosen. Never automatically use a timecounter with negative quality. @@ -1391,21 +1440,31 @@ tc_init(struct timecounter *tc) * worse since this timecounter may not be monotonic. */ if (tc_chosen) - return; + goto unlock; if (tc->tc_quality < 0) - return; -#endif /* __rtems__ */ + goto unlock; + if (tc_from_tunable[0] != '\0' && + strcmp(tc->tc_name, tc_from_tunable) == 0) { + tc_chosen = 1; + tc_from_tunable[0] = '\0'; + } else { + if (tc->tc_quality < timecounter->tc_quality) + goto unlock; + if (tc->tc_quality == timecounter->tc_quality && + tc->tc_frequency < timecounter->tc_frequency) + goto unlock; + } + (void)tc->tc_get_timecount(tc); + timecounter = tc; +unlock: + mtx_unlock(&tc_lock); +#else /* __rtems__ */ if (tc->tc_quality < timecounter->tc_quality) return; if (tc->tc_quality == timecounter->tc_quality && tc->tc_frequency < timecounter->tc_frequency) return; -#ifndef __rtems__ - (void)tc->tc_get_timecount(tc); - (void)tc->tc_get_timecount(tc); -#endif /* __rtems__ */ timecounter = tc; -#ifdef __rtems__ tc_windup(NULL); #endif /* __rtems__ */ } @@ -1493,6 +1552,40 @@ _Timecounter_Set_clock(const struct bintime *_bt, } /* + * Recalculate the scaling factor. We want the number of 1/2^64 + * fractions of a second per period of the hardware counter, taking + * into account the th_adjustment factor which the NTP PLL/adjtime(2) + * processing provides us with. + * + * The th_adjustment is nanoseconds per second with 32 bit binary + * fraction and we want 64 bit binary fraction of second: + * + * x = a * 2^32 / 10^9 = a * 4.294967296 + * + * The range of th_adjustment is +/- 5000PPM so inside a 64bit int + * we can only multiply by about 850 without overflowing, that + * leaves no suitably precise fractions for multiply before divide. + * + * Divide before multiply with a fraction of 2199/512 results in a + * systematic undercompensation of 10PPM of th_adjustment. On a + * 5000PPM adjustment this is a 0.05PPM error. This is acceptable. + * + * We happily sacrifice the lowest of the 64 bits of our result + * to the goddess of code clarity. + */ +static void +recalculate_scaling_factor_and_large_delta(struct timehands *th) +{ + uint64_t scale; + + scale = (uint64_t)1 << 63; + scale += (th->th_adjustment / 1024) * 2199; + scale /= th->th_counter->tc_frequency; + th->th_scale = scale * 2; + th->th_large_delta = MIN(((uint64_t)1 << 63) / scale, UINT_MAX); +} + +/* * Initialize the next struct timehands in the ring and make * it the active timehands. Along the way we might switch to a different * timecounter and/or do seconds processing in NTP. Slightly magic. @@ -1513,11 +1606,17 @@ _Timecounter_Windup(struct bintime *new_boottimebin, #endif /* __rtems__ */ { struct bintime bt; + struct timecounter *tc; struct timehands *th, *tho; - uint64_t scale; - uint32_t delta, ncount, ogen; + uint32_t delta, ncount; +#if defined(RTEMS_SMP) + u_int ogen; +#endif int i; time_t t; +#ifdef __rtems__ + Timecounter_NTP_update_second ntp_update_second_handler; +#endif /* * Make the next timehands a copy of the current one, but do @@ -1531,14 +1630,12 @@ _Timecounter_Windup(struct bintime *new_boottimebin, tho = timehands; #if defined(RTEMS_SMP) th = tho->th_next; -#else - th = tho; -#endif ogen = th->th_generation; th->th_generation = 0; atomic_thread_fence_rel(); -#if defined(RTEMS_SMP) - bcopy(tho, th, offsetof(struct timehands, th_generation)); + memcpy(th, tho, offsetof(struct timehands, th_generation)); +#else + th = tho; #endif if (new_boottimebin != NULL) th->th_boottime = *new_boottimebin; @@ -1548,9 +1645,10 @@ _Timecounter_Windup(struct bintime *new_boottimebin, * changing timecounters, a counter value from the new timecounter. * Update the offset fields accordingly. */ + tc = atomic_load_ptr(&timecounter); delta = tc_delta(th); - if (th->th_counter != timecounter) - ncount = timecounter->tc_get_timecount(timecounter); + if (th->th_counter != tc) + ncount = tc->tc_get_timecount(tc); else ncount = 0; #ifdef FFCLOCK @@ -1584,7 +1682,7 @@ _Timecounter_Windup(struct bintime *new_boottimebin, #endif /* __rtems__ */ /* - * Deal with NTP second processing. The for loop normally + * Deal with NTP second processing. The loop normally * iterates at most once, but in extreme situations it might * keep NTP sane if timeouts are not run for several seconds. * At boot, the time step can be large when the TOD hardware @@ -1594,69 +1692,57 @@ _Timecounter_Windup(struct bintime *new_boottimebin, */ bt = th->th_offset; bintime_add(&bt, &th->th_boottime); +#ifdef __rtems__ + ntp_update_second_handler = _Timecounter_NTP_update_second; + if (ntp_update_second_handler != NULL) { +#endif /* __rtems__ */ i = bt.sec - tho->th_microtime.tv_sec; - if (i > LARGE_STEP) - i = 2; - for (; i > 0; i--) { - t = bt.sec; - ntp_update_second(&th->th_adjustment, &bt.sec); - if (bt.sec != t) - th->th_boottime.sec += bt.sec - t; + if (i > 0) { + if (i > LARGE_STEP) + i = 2; + + do { + t = bt.sec; + ntp_update_second(&th->th_adjustment, &bt.sec); + if (bt.sec != t) + th->th_boottime.sec += bt.sec - t; + --i; + } while (i > 0); + + recalculate_scaling_factor_and_large_delta(th); } +#ifdef __rtems__ + } +#endif /* __rtems__ */ + /* Update the UTC timestamps used by the get*() functions. */ th->th_bintime = bt; bintime2timeval(&bt, &th->th_microtime); bintime2timespec(&bt, &th->th_nanotime); /* Now is a good time to change timecounters. */ - if (th->th_counter != timecounter) { + if (th->th_counter != tc) { #ifndef __rtems__ #ifndef __arm__ - if ((timecounter->tc_flags & TC_FLAGS_C2STOP) != 0) + if ((tc->tc_flags & TC_FLAGS_C2STOP) != 0) cpu_disable_c2_sleep++; if ((th->th_counter->tc_flags & TC_FLAGS_C2STOP) != 0) cpu_disable_c2_sleep--; #endif #endif /* __rtems__ */ - th->th_counter = timecounter; + th->th_counter = tc; th->th_offset_count = ncount; #ifndef __rtems__ - tc_min_ticktock_freq = max(1, timecounter->tc_frequency / - (((uint64_t)timecounter->tc_counter_mask + 1) / 3)); + tc_min_ticktock_freq = max(1, tc->tc_frequency / + (((uint64_t)tc->tc_counter_mask + 1) / 3)); #endif /* __rtems__ */ + recalculate_scaling_factor_and_large_delta(th); #ifdef FFCLOCK ffclock_change_tc(th); #endif } - /*- - * Recalculate the scaling factor. We want the number of 1/2^64 - * fractions of a second per period of the hardware counter, taking - * into account the th_adjustment factor which the NTP PLL/adjtime(2) - * processing provides us with. - * - * The th_adjustment is nanoseconds per second with 32 bit binary - * fraction and we want 64 bit binary fraction of second: - * - * x = a * 2^32 / 10^9 = a * 4.294967296 - * - * The range of th_adjustment is +/- 5000PPM so inside a 64bit int - * we can only multiply by about 850 without overflowing, that - * leaves no suitably precise fractions for multiply before divide. - * - * Divide before multiply with a fraction of 2199/512 results in a - * systematic undercompensation of 10PPM of th_adjustment. On a - * 5000PPM adjustment this is a 0.05PPM error. This is acceptable. - * - * We happily sacrifice the lowest of the 64 bits of our result - * to the goddess of code clarity. - * - */ - scale = (uint64_t)1 << 63; - scale += (th->th_adjustment / 1024) * 2199; - scale /= th->th_counter->tc_frequency; - th->th_scale = scale * 2; - +#if defined(RTEMS_SMP) /* * Now that the struct timehands is again consistent, set the new * generation number, making sure to not make it zero. @@ -1664,6 +1750,9 @@ _Timecounter_Windup(struct bintime *new_boottimebin, if (++ogen == 0) ogen = 1; atomic_store_rel_int(&th->th_generation, ogen); +#else + atomic_store_rel_int(&th->th_generation, th->th_generation + 1); +#endif /* Go live with the new struct timehands. */ #ifdef FFCLOCK @@ -1701,23 +1790,28 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) struct timecounter *newtc, *tc; int error; + mtx_lock(&tc_lock); tc = timecounter; strlcpy(newname, tc->tc_name, sizeof(newname)); + mtx_unlock(&tc_lock); error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req); if (error != 0 || req->newptr == NULL) return (error); + + mtx_lock(&tc_lock); /* Record that the tc in use now was specifically chosen. */ tc_chosen = 1; - if (strcmp(newname, tc->tc_name) == 0) + if (strcmp(newname, tc->tc_name) == 0) { + mtx_unlock(&tc_lock); return (0); + } for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) { if (strcmp(newname, newtc->tc_name) != 0) continue; /* Warm up new timecounter. */ (void)newtc->tc_get_timecount(newtc); - (void)newtc->tc_get_timecount(newtc); timecounter = newtc; @@ -1729,16 +1823,16 @@ sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) * use any locking and that it can be called in hard interrupt * context via 'tc_windup()'. */ - return (0); + break; } - return (EINVAL); + mtx_unlock(&tc_lock); + return (newtc != NULL ? 0 : EINVAL); } - -SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW, - 0, 0, sysctl_kern_timecounter_hardware, "A", +SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, + CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE, 0, 0, + sysctl_kern_timecounter_hardware, "A", "Timecounter hardware selected"); - /* Report the available timecounter hardware. */ static int sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS) @@ -1747,19 +1841,26 @@ sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS) struct timecounter *tc; int error; + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); sbuf_new_for_sysctl(&sb, NULL, 0, req); + mtx_lock(&tc_lock); for (tc = timecounters; tc != NULL; tc = tc->tc_next) { if (tc != timecounters) sbuf_putc(&sb, ' '); sbuf_printf(&sb, "%s(%d)", tc->tc_name, tc->tc_quality); } + mtx_unlock(&tc_lock); error = sbuf_finish(&sb); sbuf_delete(&sb); return (error); } -SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD, - 0, 0, sysctl_kern_timecounter_choice, "A", "Timecounter hardware detected"); +SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 0, + sysctl_kern_timecounter_choice, "A", + "Timecounter hardware detected"); #endif /* __rtems__ */ #ifndef __rtems__ @@ -1803,10 +1904,10 @@ pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) tv.tv_usec = fapi->timeout.tv_nsec / 1000; timo = tvtohz(&tv); } - aseq = pps->ppsinfo.assert_sequence; - cseq = pps->ppsinfo.clear_sequence; - while (aseq == pps->ppsinfo.assert_sequence && - cseq == pps->ppsinfo.clear_sequence) { + aseq = atomic_load_int(&pps->ppsinfo.assert_sequence); + cseq = atomic_load_int(&pps->ppsinfo.clear_sequence); + while (aseq == atomic_load_int(&pps->ppsinfo.assert_sequence) && + cseq == atomic_load_int(&pps->ppsinfo.clear_sequence)) { if (abi_aware(pps, 1) && pps->driver_mtx != NULL) { if (pps->flags & PPSFLAG_MTX_SPIN) { err = msleep_spin(pps, pps->driver_mtx, @@ -2144,27 +2245,38 @@ _Timecounter_Tick_simple(uint32_t delta, uint32_t offset, { struct bintime bt; struct timehands *th; - uint32_t ogen; +#if defined(RTEMS_SMP) + u_int ogen; +#endif th = timehands; +#if defined(RTEMS_SMP) ogen = th->th_generation; + th->th_generation = 0; + atomic_thread_fence_rel(); +#endif + th->th_offset_count = offset; bintime_addx(&th->th_offset, th->th_scale * delta); - bt = th->th_offset; bintime_add(&bt, &th->th_boottime); + /* Update the UTC timestamps used by the get*() functions. */ th->th_bintime = bt; bintime2timeval(&bt, &th->th_microtime); bintime2timespec(&bt, &th->th_nanotime); +#if defined(RTEMS_SMP) /* * Now that the struct timehands is again consistent, set the new * generation number, making sure to not make it zero. */ if (++ogen == 0) ogen = 1; - th->th_generation = ogen; + atomic_store_rel_int(&th->th_generation, ogen); +#else + atomic_store_rel_int(&th->th_generation, th->th_generation + 1); +#endif /* Go live with the new struct timehands. */ time_second = th->th_microtime.tv_sec; @@ -2218,6 +2330,28 @@ done: return (0); } +/* Set up the requested number of timehands. */ +static void +inittimehands(void *dummy) +{ + struct timehands *thp; + int i; + + TUNABLE_INT_FETCH("kern.timecounter.timehands_count", + &timehands_count); + if (timehands_count < 1) + timehands_count = 1; + if (timehands_count > nitems(ths)) + timehands_count = nitems(ths); + for (i = 1, thp = &ths[0]; i < timehands_count; thp = &ths[i++]) + thp->th_next = &ths[i]; + thp->th_next = &ths[0]; + + TUNABLE_STR_FETCH("kern.timecounter.hardware", tc_from_tunable, + sizeof(tc_from_tunable)); +} +SYSINIT(timehands, SI_SUB_TUNABLES, SI_ORDER_ANY, inittimehands, NULL); + static void inittimecounter(void *dummy) { @@ -2248,9 +2382,9 @@ inittimecounter(void *dummy) #ifdef FFCLOCK ffclock_init(); #endif + /* warm up new timecounter (again) and get rolling. */ (void)timecounter->tc_get_timecount(timecounter); - (void)timecounter->tc_get_timecount(timecounter); mtx_lock_spin(&tc_setclock_mtx); tc_windup(NULL); mtx_unlock_spin(&tc_setclock_mtx); @@ -2263,8 +2397,8 @@ SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL); static int cpu_tick_variable; static uint64_t cpu_tick_frequency; -static DPCPU_DEFINE(uint64_t, tc_cpu_ticks_base); -static DPCPU_DEFINE(unsigned, tc_cpu_ticks_last); +DPCPU_DEFINE_STATIC(uint64_t, tc_cpu_ticks_base); +DPCPU_DEFINE_STATIC(unsigned, tc_cpu_ticks_last); static uint64_t tc_cpu_ticks(void) @@ -2438,7 +2572,6 @@ tc_fill_vdso_timehands(struct vdso_timehands *vdso_th) enabled = 0; return (enabled); } -#endif /* __rtems__ */ #ifdef COMPAT_FREEBSD32 uint32_t @@ -2465,3 +2598,34 @@ tc_fill_vdso_timehands32(struct vdso_timehands32 *vdso_th32) return (enabled); } #endif + +#include "opt_ddb.h" +#ifdef DDB +#include <ddb/ddb.h> + +DB_SHOW_COMMAND(timecounter, db_show_timecounter) +{ + struct timehands *th; + struct timecounter *tc; + u_int val1, val2; + + th = timehands; + tc = th->th_counter; + val1 = tc->tc_get_timecount(tc); + __compiler_membar(); + val2 = tc->tc_get_timecount(tc); + + db_printf("timecounter %p %s\n", tc, tc->tc_name); + db_printf(" mask %#x freq %ju qual %d flags %#x priv %p\n", + tc->tc_counter_mask, (uintmax_t)tc->tc_frequency, tc->tc_quality, + tc->tc_flags, tc->tc_priv); + db_printf(" val %#x %#x\n", val1, val2); + db_printf("timehands adj %#jx scale %#jx ldelta %d off_cnt %d gen %d\n", + (uintmax_t)th->th_adjustment, (uintmax_t)th->th_scale, + th->th_large_delta, th->th_offset_count, th->th_generation); + db_printf(" offset %jd %jd boottime %jd %jd\n", + (intmax_t)th->th_offset.sec, (uintmax_t)th->th_offset.frac, + (intmax_t)th->th_boottime.sec, (uintmax_t)th->th_boottime.frac); +} +#endif +#endif /* __rtems__ */ diff --git a/cpukit/score/src/objectactivecount.c b/cpukit/score/src/objectactivecount.c index c658fc21e3..12c15147c7 100644 --- a/cpukit/score/src/objectactivecount.c +++ b/cpukit/score/src/objectactivecount.c @@ -24,14 +24,22 @@ Objects_Maximum _Objects_Active_count( const Objects_Information *information ) { - Objects_Maximum inactive; - Objects_Maximum maximum; + Objects_Maximum active; + Objects_Maximum index; + Objects_Maximum maximum; + Objects_Control **local_table; _Assert( _Objects_Allocator_is_owner() ); - inactive = (Objects_Maximum) - _Chain_Node_count_unprotected( &information->Inactive ); + active = 0; maximum = _Objects_Get_maximum_index( information ); + local_table = information->local_table; - return maximum - inactive; + for ( index = 0; index < maximum; ++index ) { + if ( local_table[ index ] != NULL ) { + ++active; + } + } + + return active; } diff --git a/cpukit/score/src/objectfree.c b/cpukit/score/src/objectfree.c index 1f7aa37e3f..964adbc62a 100644 --- a/cpukit/score/src/objectfree.c +++ b/cpukit/score/src/objectfree.c @@ -30,14 +30,16 @@ void _Objects_Free_unlimited( if ( _Objects_Is_auto_extend( information ) ) { Objects_Maximum objects_per_block; - Objects_Maximum block; - Objects_Maximum inactive; + Objects_Maximum index; objects_per_block = information->objects_per_block; - block = _Objects_Get_index( the_object->id ) - OBJECTS_INDEX_MINIMUM; + index = _Objects_Get_index( the_object->id ) - OBJECTS_INDEX_MINIMUM; - if ( block > objects_per_block ) { - block /= objects_per_block; + if ( _Objects_Is_in_allocated_block( index, objects_per_block ) ) { + Objects_Maximum block; + Objects_Maximum inactive; + + block = index / objects_per_block; ++information->inactive_per_block[ block ]; diff --git a/cpukit/score/src/stackallocatorforidle.c b/cpukit/score/src/stackallocatorforidle.c new file mode 100644 index 0000000000..7c4fd10c7d --- /dev/null +++ b/cpukit/score/src/stackallocatorforidle.c @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2021 OAR Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/score/stack.h> +#include <rtems/score/thread.h> + +/** + * @brief Default stack allocator allocate for idle handler. + * + * The allocate for idle handler is optional even when the user thread stack + * allocator and deallocator are configured. + * + * The default allocator for IDLE thread stacks gets the memory from a + * statically allocated area provided via confdefs.h. + * + * @param cpu Index of the CPU for the IDLE thread using this stack + * @param stack_size The size of the stack area to allocate in bytes. + * + * @retval NULL Not enough memory (never returned). + * @retval other Pointer to begin of stack area. + */ +static void *_Stack_Allocator_allocate_for_idle_default( + uint32_t cpu, + size_t stack_size +) +{ + return &_Thread_Idle_stacks[ cpu * stack_size ]; +} + +const Stack_Allocator_allocate_for_idle _Stack_Allocator_allocate_for_idle = + _Stack_Allocator_allocate_for_idle_default; diff --git a/cpukit/score/src/threadcreateidle.c b/cpukit/score/src/threadcreateidle.c index 1e18ad07cc..e888aa111f 100644 --- a/cpukit/score/src/threadcreateidle.c +++ b/cpukit/score/src/threadcreateidle.c @@ -53,9 +53,15 @@ static void _Thread_Create_idle_for_CPU( Per_CPU_Control *cpu ) config.is_preemptible = true; config.stack_size = _Thread_Idle_stack_size + CPU_IDLE_TASK_IS_FP * CONTEXT_FP_SIZE; - config.stack_area = &_Thread_Idle_stacks[ - _Per_CPU_Get_index( cpu ) * config.stack_size - ]; + + /* + * The IDLE thread stacks may be statically allocated or there may be a + * custom allocator provided just as with user threads. + */ + config.stack_area = (*_Stack_Allocator_allocate_for_idle)( + _Per_CPU_Get_index( cpu ), + config.stack_size + ); /* * The entire workspace is zeroed during its initialization. Thus, all diff --git a/cpukit/score/src/threadqops.c b/cpukit/score/src/threadqops.c index 9894c3628b..8c3e0cb1dc 100644 --- a/cpukit/score/src/threadqops.c +++ b/cpukit/score/src/threadqops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016 embedded brains GmbH. All rights reserved. + * Copyright (c) 2015, 2021 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 @@ -16,6 +16,7 @@ #include "config.h" #endif +#include <rtems/score/threadqops.h> #include <rtems/score/threadimpl.h> #include <rtems/score/assert.h> #include <rtems/score/chainimpl.h> @@ -38,7 +39,7 @@ Queue \ ) -static void _Thread_queue_Do_nothing_priority_actions( +void _Thread_queue_Do_nothing_priority_actions( Thread_queue_Queue *queue, Priority_Actions *priority_actions ) @@ -187,7 +188,7 @@ static void _Thread_queue_FIFO_do_extract( _Chain_Extract_unprotected( &scheduler_node->Wait.Priority.Node.Node.Chain ); } -static void _Thread_queue_FIFO_enqueue( +void _Thread_queue_FIFO_enqueue( Thread_queue_Queue *queue, Thread_Control *the_thread, Thread_queue_Context *queue_context @@ -202,7 +203,7 @@ static void _Thread_queue_FIFO_enqueue( ); } -static void _Thread_queue_FIFO_extract( +void _Thread_queue_FIFO_extract( Thread_queue_Queue *queue, Thread_Control *the_thread, Thread_queue_Context *queue_context @@ -218,23 +219,21 @@ static void _Thread_queue_FIFO_extract( ); } -static Thread_Control *_Thread_queue_FIFO_first( - Thread_queue_Heads *heads -) +Thread_Control *_Thread_queue_FIFO_first( const Thread_queue_Heads *heads ) { - Chain_Control *fifo; - Chain_Node *first; - Scheduler_Node *scheduler_node; + const Chain_Control *fifo; + const Chain_Node *first; + const Scheduler_Node *scheduler_node; fifo = &heads->Heads.Fifo; _Assert( !_Chain_Is_empty( fifo ) ); - first = _Chain_First( fifo ); + first = _Chain_Immutable_first( fifo ); scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY_NODE( first ); return _Scheduler_Node_get_owner( scheduler_node ); } -static Thread_Control *_Thread_queue_FIFO_surrender( +Thread_Control *_Thread_queue_FIFO_surrender( Thread_queue_Queue *queue, Thread_queue_Heads *heads, Thread_Control *previous_owner, @@ -243,6 +242,8 @@ static Thread_Control *_Thread_queue_FIFO_surrender( { Thread_Control *first; + (void) previous_owner; + first = _Thread_queue_FIFO_first( heads ); _Thread_queue_Queue_extract( queue, @@ -589,7 +590,7 @@ static void _Thread_queue_Priority_extract( } static Thread_Control *_Thread_queue_Priority_first( - Thread_queue_Heads *heads + const Thread_queue_Heads *heads ) { Thread_queue_Priority_queue *priority_queue; diff --git a/testsuites/libtests/malloctest/init.c b/testsuites/libtests/malloctest/init.c index 1d91385683..1e5c14fe4b 100644 --- a/testsuites/libtests/malloctest/init.c +++ b/testsuites/libtests/malloctest/init.c @@ -122,9 +122,13 @@ static void test_heap_default_init(void) static void test_free( void *addr ) { + uint32_t failed_allocs; + rtems_test_assert( _Heap_Free( &TestHeap, addr ) ); + failed_allocs = TestHeap.stats.failed_allocs; _Heap_Protection_free_all_delayed_blocks( &TestHeap ); + rtems_test_assert( failed_allocs == TestHeap.stats.failed_allocs ); } static void test_heap_cases_1(void) diff --git a/testsuites/libtests/tar01/init.c b/testsuites/libtests/tar01/init.c index 4cad67a6ae..2deff3a482 100644 --- a/testsuites/libtests/tar01/init.c +++ b/testsuites/libtests/tar01/init.c @@ -7,6 +7,33 @@ * http://www.rtems.org/license/LICENSE. */ +/* + * Note on the used tar file: Generate the file on a system that supports + * symlinks with the following commands (tested on Linux - you might have to + * adapt on other systems): + * + * export WORK=some_work_directory + * rm -r ${WORK} + * mkdir -p ${WORK}/home/abc/def + * mkdir -p ${WORK}/home/dir + * cd ${WORK} + * echo "#! joel" > home/abc/def/test_script + * echo "ls -las /dev" >> home/abc/def/test_script + * chmod 755 home/abc/def/test_script + * echo "This is a test of loading an RTEMS filesystem from an" > home/test_file + * echo "initial tar image." >> home/test_file + * echo "Hello world" >> home/dir/file + * ln -s home/test_file symlink + * tar cf tar01.tar --format=ustar \ + * symlink \ + * home/test_file \ + * home/abc/def/test_script \ + * home/dir + * + * Note that "home/dir" is in the archive as separate directory. "home/abc" is + * only in the archive as a parent of the file "test_script". + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -95,6 +122,84 @@ void test_untar_from_memory(void) } +static void assert_file_content( + const char *name, + const char *expected_content, + ssize_t expected_size +) +{ + char buf[16]; + int fd; + int rd; + + fd = open(name, O_RDONLY); + rtems_test_assert( fd >= 0 ); + do { + rd = read(fd, buf, sizeof(buf)); + rtems_test_assert( rd >= 0 ); + if (rd > 0) { + rtems_test_assert( expected_size - rd >= 0 ); + rtems_test_assert( memcmp(buf, expected_content, rd) == 0 ); + expected_content += rd; + expected_size -= rd; + } + } while(rd > 0); + rtems_test_assert( expected_size == 0 ); + close(fd); +} + +static void assert_content_like_expected(void) +{ + const char *directories[] = { + "home", + "home/abc", + "home/abc/def", + "home/dir", + }; + const char *symlinks[] = { + "symlink", + }; + const struct { + const char *name; + const char *content; + } files[] = { + { + .name = "home/abc/def/test_script", + .content = "#! joel\nls -las /dev\n", + }, { + .name = "home/test_file", + .content = "This is a test of loading an RTEMS filesystem from an\n" + "initial tar image.\n", + }, { + .name = "home/dir/file", + .content = "Hello world\n", + } + }; + size_t i; + struct stat st; + + for(i = 0; i < RTEMS_ARRAY_SIZE(directories); ++i) { + lstat(directories[i], &st); + rtems_test_assert( S_ISDIR(st.st_mode) ); + } + + for(i = 0; i < RTEMS_ARRAY_SIZE(symlinks); ++i) { + lstat(symlinks[i], &st); + rtems_test_assert( S_ISLNK(st.st_mode) ); + } + + for(i = 0; i < RTEMS_ARRAY_SIZE(files); ++i) { + lstat(files[i].name, &st); + rtems_test_assert( S_ISREG(st.st_mode) ); + + assert_file_content( + files[i].name, + files[i].content, + strlen(files[i].content) + ); + } +} + void test_untar_from_file(void) { int fd; @@ -119,13 +224,105 @@ void test_untar_from_file(void) rv = chdir( "/dest" ); rtems_test_assert( rv == 0 ); - /* Untar it */ + /* Case 1: Untar it into empty directory */ rv = Untar_FromFile( "/test.tar" ); printf("Untaring from file - "); if (rv != UNTAR_SUCCESSFUL) { printf ("error: untar failed: %i\n", rv); exit(1); } + assert_content_like_expected(); + printf ("successful\n"); + + /* Case 2: Most files exist */ + rv = unlink("/dest/home/test_file"); + rtems_test_assert( rv == 0 ); + + rv = Untar_FromFile( "/test.tar" ); + printf("Untar from file into existing structure with one missing file - "); + if (rv != UNTAR_SUCCESSFUL) { + printf ("error: untar failed: %i\n", rv); + exit(1); + } + assert_content_like_expected(); + printf ("successful\n"); + + /* Case 3: An empty directory exists where a file should be */ + rv = unlink("/dest/home/test_file"); + rtems_test_assert( rv == 0 ); + rv = mkdir("/dest/home/test_file", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + rtems_test_assert( rv == 0 ); + + rv = Untar_FromFile( "/test.tar" ); + printf("Untar from file; overwrite empty directory with file - "); + if (rv != UNTAR_SUCCESSFUL) { + printf ("error: untar failed: %i\n", rv); + exit(1); + } + assert_content_like_expected(); + printf ("successful\n"); + + /* Case 4: A file exists where a parent directory should be created */ + rv = unlink("/dest/home/abc/def/test_script"); + rtems_test_assert( rv == 0 ); + rv = unlink("/dest/home/abc/def"); + rtems_test_assert( rv == 0 ); + rv = unlink("/dest/home/abc"); + rtems_test_assert( rv == 0 ); + fd = creat("/dest/home/abc", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + rtems_test_assert( fd >= 0 ); + close(fd); + + rv = Untar_FromFile( "/test.tar" ); + printf("Untar from file; file exists where parent dir should be created - "); + if (rv != UNTAR_FAIL) { + printf ("error: untar didn't fail like expected: %i\n", rv); + exit(1); + } + printf ("expected fail\n"); + /* cleanup so that the next one works */ + rv = unlink("/dest/home/abc"); + rtems_test_assert( rv == 0 ); + + /* Case 5: A non-empty directory exists where a file should be created */ + rv = unlink("/dest/home/test_file"); + rtems_test_assert( rv == 0 ); + rv = mkdir("/dest/home/test_file", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + rtems_test_assert( rv == 0 ); + fd = creat("/dest/home/test_file/file", + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + rtems_test_assert( fd >= 0 ); + close(fd); + + rv = Untar_FromFile( "/test.tar" ); + printf("Untar from file; non-empty dir where file should be created - "); + if (rv != UNTAR_FAIL) { + printf ("error: untar didn't fail like expected: %i\n", rv); + exit(1); + } + printf ("expected fail\n"); + /* cleanup so that the next one works */ + rv = unlink("/dest/home/test_file/file"); + rtems_test_assert( rv == 0 ); + rv = unlink("/dest/home/test_file"); + rtems_test_assert( rv == 0 ); + + /* Case 6: A file exists where a directory is explicitly in the archive */ + rv = unlink("/dest/home/dir/file"); + rtems_test_assert( rv == 0 ); + rv = unlink("/dest/home/dir"); + rtems_test_assert( rv == 0 ); + fd = creat("/dest/home/dir", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + rtems_test_assert( fd >= 0 ); + close(fd); + + rv = Untar_FromFile( "/test.tar" ); + printf("Untar from file; overwrite file with explicit directory - "); + if (rv != UNTAR_SUCCESSFUL) { + printf ("error: untar failed: %i\n", rv); + exit(1); + } + assert_content_like_expected(); printf ("successful\n"); /******************/ diff --git a/testsuites/libtests/tar01/tar01.doc b/testsuites/libtests/tar01/tar01.doc index 060f98a813..adffdca291 100644 --- a/testsuites/libtests/tar01/tar01.doc +++ b/testsuites/libtests/tar01/tar01.doc @@ -20,3 +20,4 @@ directives: concepts: + exercise these routines ++ check whether existing files are overwritten or not overwritten like expected diff --git a/testsuites/libtests/tar01/tar01.scn b/testsuites/libtests/tar01/tar01.scn index 68fa951881..dd72f9517b 100644 --- a/testsuites/libtests/tar01/tar01.scn +++ b/testsuites/libtests/tar01/tar01.scn @@ -1,9 +1,24 @@ -*** TAR01 TEST *** -Untaring from memory - successful +*** BEGIN OF TEST TAR 1 *** +*** TEST VERSION: 6.0.0.e1efb4eb8a9d6dd5f6f37dafc9feb0a9e6a888f1 +*** TEST STATE: EXPECTED_PASS +*** TEST BUILD: RTEMS_POSIX_API +*** TEST TOOLS: 10.3.1 20210409 (RTEMS 6, RSB ad54d1dd3cf8249d9d39deb1dd28b2f294df062d-modified, Newlib eb03ac1) +Untaring from memory - untar: memory at 0x11ece8 (10240) +untar: symlink: home/test_file -> symlink +untar: file: home/test_file (s:73,m:0644) +untar: file: home/abc/def/test_script (s:21,m:0755) +untar: dir: home/dir +untar: file: home/dir/file (s:12,m:0644) +successful ========= /home/test_file ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. +========= /home/abc/def/test_script ========= +(0)#! joel +ls -las /dev + + /home/abc/def/test_script: mode: 0755 want: 0755 ========= /symlink ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. @@ -11,35 +26,58 @@ initial tar image. Copy tar image to test.tar Untaring from file - successful +Untar from file into existing structure with one missing file - successful +Untar from file; overwrite empty directory with file - successful +Untar from file; file exists where parent dir should be created - expected fail +Untar from file; non-empty dir where file should be created - expected fail +Untar from file; overwrite file with explicit directory - successful ========= /dest/home/test_file ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. +========= /dest/home/abc/def/test_script ========= +(0)#! joel +ls -las /dev + + /dest/home/abc/def/test_script: mode: 0755 want: 0755 ========= /dest/symlink ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. -Untaring chunks from memory - untar: dir: home -untar: file: home/test_file (73) +Untaring chunks from memory - untar: symlink: home/test_file -> symlink +untar: file: home/test_file (s:73,m:0644) +untar: file: home/abc/def/test_script (s:21,m:0755) +untar: dir: home/dir +untar: file: home/dir/file (s:12,m:0644) successful ========= /dest2/home/test_file ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. +========= /dest2/home/abc/def/test_script ========= +(0)#! joel +ls -las /dev + + /dest2/home/abc/def/test_script: mode: 0755 want: 0755 ========= /dest2/symlink ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. -Untaring chunks from tgz- untar: dir: home -untar: file: home/test_file (73) -successful +Untaring chunks from tgz - successful ========= /dest3/home/test_file ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. +========= /dest3/home/abc/def/test_script ========= +(0)#! joel +ls -las /dev + + /dest3/home/abc/def/test_script: mode: 0755 want: 0755 ========= /dest3/symlink ========= (0)This is a test of loading an RTEMS filesystem from an initial tar image. -*** END OF TAR01 TEST *** + + +*** END OF TEST TAR 1 *** diff --git a/testsuites/libtests/tar01/tar01.tar b/testsuites/libtests/tar01/tar01.tar Binary files differindex 6c6952ef18..9874f426d1 100644 --- a/testsuites/libtests/tar01/tar01.tar +++ b/testsuites/libtests/tar01/tar01.tar diff --git a/testsuites/psxtests/psxdevctl01/test.c b/testsuites/psxtests/psxdevctl01/test.c index b45725cb58..2fe7df1834 100644 --- a/testsuites/psxtests/psxdevctl01/test.c +++ b/testsuites/psxtests/psxdevctl01/test.c @@ -53,37 +53,16 @@ int main( int dev_data; void *dev_data_ptr; size_t nbyte; - int dev_info; TEST_BEGIN(); - puts( "posix_devctl() FIONBIO on stdin return dev_info -- EBADF" ); - fd = 0; - dcmd = FIONBIO; - dev_data_ptr = &dev_data; - nbyte = sizeof(dev_data); - status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, &dev_info ); - rtems_test_assert( status == -1 ); - rtems_test_assert( errno == EBADF ); - rtems_test_assert( dev_info == 0 ); - - puts( "posix_devctl() FIONBIO on stdin NULL dev_info -- EBADF" ); - fd = 0; - dcmd = FIONBIO; - dev_data_ptr = NULL; - nbyte = 0; - status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); - rtems_test_assert( status == -1 ); - rtems_test_assert( errno == EBADF ); - puts( "posix_devctl() SOCKCLOSE on invalid file descriptor -- EBADF" ); - fd = 21; + fd = -1; dcmd = SOCKCLOSE; dev_data_ptr = NULL; nbyte = 0; status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); - rtems_test_assert( status == -1 ); - rtems_test_assert( errno == EBADF ); + rtems_test_assert( status == EBADF ); /* * Create a file, open it, and close it via posix_devctl(). @@ -102,6 +81,50 @@ int main( status = close( fd ); rtems_test_assert( status == -1 ); rtems_test_assert( errno == EBADF ); + + puts( "posix_devctl() FIONBIO with invalid nbyte -- EINVAL" ); + fd = 0; + dcmd = FIONBIO; + dev_data_ptr = NULL; + nbyte = 0; + status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); + rtems_test_assert( status == EINVAL ); + + puts( "posix_devctl() FIONBIO with invalid file descriptor -- EBADF" ); + fd = -1; + dcmd = FIONBIO; + dev_data_ptr = NULL; + nbyte = sizeof(int); + status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); + rtems_test_assert( status == EBADF ); + + puts( "posix_devctl() FIONBIO flag not zero -- 0" ); + fd = 0; + dcmd = FIONBIO; + dev_data = 1; + dev_data_ptr = &dev_data; + nbyte = sizeof(int); + status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); + rtems_test_assert( status == 0 ); + + puts( "posix_devctl() FIONBIO flag is zero -- 0" ); + fd = 0; + dcmd = FIONBIO; + dev_data = 0; + dev_data_ptr = &dev_data; + nbyte = sizeof(int); + status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); + rtems_test_assert( status == 0 ); + + puts( "posix_devctl() dcmd not valid value -- EBADF" ); + fd = 0; + dcmd = 1; + dev_data = 0; + dev_data_ptr = &dev_data; + nbyte = sizeof(int); + status = posix_devctl( fd, dcmd, dev_data_ptr, nbyte, NULL ); + rtems_test_assert( status == EBADF ); + TEST_END(); exit(0); } diff --git a/testsuites/psxtests/psximfs02/init.c b/testsuites/psxtests/psximfs02/init.c index 15b9137121..04f806f565 100644 --- a/testsuites/psxtests/psximfs02/init.c +++ b/testsuites/psxtests/psximfs02/init.c @@ -23,6 +23,8 @@ #include <rtems/malloc.h> #include <rtems/libcsupport.h> +#define MEMFILE_BYTES_PER_BLOCK 16 + const char rtems_test_name[] = "PSXIMFS 2"; /* forward declarations to avoid warnings */ @@ -43,12 +45,17 @@ rtems_task Init( static const uintptr_t slink_2_name_size [] = { sizeof( slink_2_name ) }; + static const uintptr_t some_blocks [] = { + MEMFILE_BYTES_PER_BLOCK * 10 + }; + static const char some_data[MEMFILE_BYTES_PER_BLOCK * 11]; int status = 0; void *opaque; char linkname_n[32] = {0}; char linkname_p[32] = {0}; int i; + int fd; struct stat stat_buf; TEST_BEGIN(); @@ -102,6 +109,27 @@ rtems_task Init( rtems_test_assert( status == -1 ); rtems_test_assert( errno == EACCES ); + puts( "Allocate most of heap with a little bit left" ); + opaque = rtems_heap_greedy_allocate( some_blocks, 1 ); + + puts( "Create an empty file."); + status = mknod( "/foo", S_IFREG | S_IRWXU, 0LL ); + rtems_test_assert( status == 0 ); + + puts( "Then increase it's size to more than remaining space" ); + fd = open( "/foo", O_WRONLY | O_TRUNC); + rtems_test_assert( fd >= 0 ); + status = write(fd, some_data, sizeof(some_data)); + rtems_test_assert( status == -1); + rtems_test_assert( errno == ENOSPC ); + + puts( "Clean up again" ); + status = close(fd); + rtems_test_assert( status == 0); + status = remove( "/foo" ); + rtems_test_assert( status == 0); + rtems_heap_greedy_free( opaque ); + puts( "Allocate most of heap" ); opaque = rtems_heap_greedy_allocate( mount_table_entry_size, 1 ); @@ -202,7 +230,7 @@ rtems_task Init( #define CONFIGURE_FILESYSTEM_IMFS #define CONFIGURE_MAXIMUM_TASKS 1 -#define CONFIGURE_IMFS_MEMFILE_BYTES_PER_BLOCK 16 +#define CONFIGURE_IMFS_MEMFILE_BYTES_PER_BLOCK MEMFILE_BYTES_PER_BLOCK #define CONFIGURE_IMFS_ENABLE_MKFIFO #define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 4 #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION diff --git a/testsuites/sptests/Makefile.am b/testsuites/sptests/Makefile.am index 62bb1aa685..1ea2501bd7 100644 --- a/testsuites/sptests/Makefile.am +++ b/testsuites/sptests/Makefile.am @@ -1783,6 +1783,24 @@ spstkalloc02_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_spstkalloc02) \ $(support_includes) endif +if TEST_spstkalloc03 +sp_tests += spstkalloc03 +sp_screens += spstkalloc03/spstkalloc03.scn +sp_docs += spstkalloc03/spstkalloc03.doc +spstkalloc03_SOURCES = spstkalloc03/init.c +spstkalloc03_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_spstkalloc03) \ + $(support_includes) +endif + +if TEST_spstkalloc04 +sp_tests += spstkalloc04 +sp_screens += spstkalloc04/spstkalloc04.scn +sp_docs += spstkalloc04/spstkalloc04.doc +spstkalloc04_SOURCES = spstkalloc04/init.c +spstkalloc04_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_FLAGS_spstkalloc04) \ + $(support_includes) +endif + if TEST_spsysinit01 sp_tests += spsysinit01 sp_screens += spsysinit01/spsysinit01.scn diff --git a/testsuites/sptests/configure.ac b/testsuites/sptests/configure.ac index 9476e3b0d7..24ff0383e6 100644 --- a/testsuites/sptests/configure.ac +++ b/testsuites/sptests/configure.ac @@ -228,6 +228,8 @@ RTEMS_TEST_CHECK([spsize]) RTEMS_TEST_CHECK([spstdthreads01]) RTEMS_TEST_CHECK([spstkalloc]) RTEMS_TEST_CHECK([spstkalloc02]) +RTEMS_TEST_CHECK([spstkalloc03]) +RTEMS_TEST_CHECK([spstkalloc04]) RTEMS_TEST_CHECK([spsysinit01]) RTEMS_TEST_CHECK([spsyslock01]) RTEMS_TEST_CHECK([sptask_err01]) diff --git a/testsuites/sptests/spmrsp01/init.c b/testsuites/sptests/spmrsp01/init.c index a5f645d534..801b679ec9 100644 --- a/testsuites/sptests/spmrsp01/init.c +++ b/testsuites/sptests/spmrsp01/init.c @@ -72,12 +72,6 @@ static void test_mrsp_create_errors(void) create_not_defined( RTEMS_MULTIPROCESSOR_RESOURCE_SHARING - | RTEMS_BINARY_SEMAPHORE - | RTEMS_PRIORITY - ); - - create_not_defined( - RTEMS_MULTIPROCESSOR_RESOURCE_SHARING | RTEMS_INHERIT_PRIORITY | RTEMS_BINARY_SEMAPHORE ); diff --git a/testsuites/sptests/spstkalloc03/init.c b/testsuites/sptests/spstkalloc03/init.c new file mode 100644 index 0000000000..348afe7328 --- /dev/null +++ b/testsuites/sptests/spstkalloc03/init.c @@ -0,0 +1,103 @@ +/* + * COPYRIGHT (c) 2021. + * On-Line Applications Research Corporation (OAR). + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <tmacros.h> + +#include <rtems/score/percpu.h> + +const char rtems_test_name[] = "SPSTKALLOC 3"; + +static int thread_stacks_count = 0xff; + +static rtems_task Init( + rtems_task_argument ignored +) +{ + rtems_print_printer_fprintf_putc(&rtems_test_printer); + TEST_BEGIN(); + rtems_test_assert(thread_stacks_count == 2); + TEST_END(); + rtems_test_exit( 0 ); +} + +static uint8_t stack_memory[RTEMS_MINIMUM_STACK_SIZE * 4]; + +static int stack_offset_next; + +static void *allocate_helper(size_t size) +{ + size_t next; + void *alloc; + + next = stack_offset_next + size; + rtems_test_assert( next < sizeof(stack_memory) ); + + alloc = &stack_memory[stack_offset_next]; + stack_offset_next = next; + return alloc; +} + +static void thread_stacks_initialize(size_t stack_space_size) +{ + rtems_test_assert(thread_stacks_count == 0xff); + thread_stacks_count = 0; +} + +static void *thread_stacks_allocate(size_t stack_size) +{ + rtems_test_assert(thread_stacks_count == 1); + thread_stacks_count++; + return allocate_helper(stack_size); +} + +static void thread_stacks_free(void *addr) +{ + rtems_test_assert(false); +} + +static void *thread_stacks_allocate_for_idle( + uint32_t cpu, + size_t stack_size +) +{ + rtems_test_assert(thread_stacks_count == 0); + thread_stacks_count++; + return allocate_helper(stack_size); +} + +/* + * Configure the thread stack allocators to not use the workspace. This should + * eliminate all uses of the Workspace for most BSPs. + */ +#define CONFIGURE_TASK_STACK_ALLOCATOR_AVOIDS_WORK_SPACE +#define CONFIGURE_TASK_STACK_ALLOCATOR_INIT thread_stacks_initialize +#define CONFIGURE_TASK_STACK_ALLOCATOR thread_stacks_allocate +#define CONFIGURE_TASK_STACK_DEALLOCATOR thread_stacks_free +#define CONFIGURE_TASK_STACK_ALLOCATOR_FOR_IDLE thread_stacks_allocate_for_idle + + +/* NOTICE: the clock driver is explicitly disabled */ +#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER + +#define CONFIGURE_MAXIMUM_TASKS 1 + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_INIT +#include <rtems/confdefs.h> diff --git a/testsuites/sptests/spstkalloc03/spstkalloc03.doc b/testsuites/sptests/spstkalloc03/spstkalloc03.doc new file mode 100644 index 0000000000..797716b623 --- /dev/null +++ b/testsuites/sptests/spstkalloc03/spstkalloc03.doc @@ -0,0 +1,19 @@ +# COPYRIGHT (c) 2021. +# On-Line Applications Research Corporation (OAR). +# +# 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. +# + +This file describes the directives and concepts tested by this test set. + +test set name: spstkalloc03 + +directives: + +concepts: + ++ Ensure that the task stack allocator including IDLE thread + stack allocator works. + diff --git a/testsuites/sptests/spstkalloc03/spstkalloc03.scn b/testsuites/sptests/spstkalloc03/spstkalloc03.scn new file mode 100644 index 0000000000..45c67ef1fb --- /dev/null +++ b/testsuites/sptests/spstkalloc03/spstkalloc03.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST SPSTKALLOC 3 *** +*** END OF TEST SPSTKALLOC 3 *** diff --git a/testsuites/sptests/spstkalloc04/init.c b/testsuites/sptests/spstkalloc04/init.c new file mode 100644 index 0000000000..16f4ec65ca --- /dev/null +++ b/testsuites/sptests/spstkalloc04/init.c @@ -0,0 +1,82 @@ +/* + * COPYRIGHT (c) 2021. + * On-Line Applications Research Corporation (OAR). + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <tmacros.h> + +#include <rtems/score/percpu.h> + +const char rtems_test_name[] = "SPSTKALLOC 4"; + +static int thread_stacks_count = 0; + +static rtems_task Init( + rtems_task_argument ignored +) +{ + rtems_print_printer_fprintf_putc(&rtems_test_printer); + TEST_BEGIN(); + rtems_test_assert(thread_stacks_count == 1); + TEST_END(); + rtems_test_exit( 0 ); +} + +static uint8_t stack_memory[RTEMS_MINIMUM_STACK_SIZE * 4]; + +static int stack_offset_next; + +static void *allocate_helper(size_t size) +{ + size_t next; + void *alloc; + + next = stack_offset_next + size; + rtems_test_assert( next < sizeof(stack_memory) ); + + alloc = &stack_memory[stack_offset_next]; + stack_offset_next = next; + return alloc; +} + +static void *thread_stacks_allocate_for_idle( + uint32_t cpu, + size_t stack_size +) +{ + rtems_test_assert(thread_stacks_count == 0); + thread_stacks_count++; + return allocate_helper(stack_size); +} + +/* + * Configure the IDLE thread stack allocators. This is a special + * case where there is an IDLE thread stack allocator but no custom + * allocator set for other threads. + */ +#define CONFIGURE_TASK_STACK_ALLOCATOR_FOR_IDLE thread_stacks_allocate_for_idle + + +/* NOTICE: the clock driver is explicitly disabled */ +#define CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER + +#define CONFIGURE_MAXIMUM_TASKS 1 + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT + +#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION + +#define CONFIGURE_INIT +#include <rtems/confdefs.h> diff --git a/testsuites/sptests/spstkalloc04/spstkalloc04.doc b/testsuites/sptests/spstkalloc04/spstkalloc04.doc new file mode 100644 index 0000000000..a1ed5b22cb --- /dev/null +++ b/testsuites/sptests/spstkalloc04/spstkalloc04.doc @@ -0,0 +1,20 @@ +# COPYRIGHT (c) 2021. +# On-Line Applications Research Corporation (OAR). +# +# 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. +# + +This file describes the directives and concepts tested by this test set. + +test set name: spstkalloc04 + +directives: + +concepts: + ++ Ensure that the task stack allocator including IDLE thread + stack allocator works when a custom allocator is NOT provided + for other threads. + diff --git a/testsuites/sptests/spstkalloc04/spstkalloc04.scn b/testsuites/sptests/spstkalloc04/spstkalloc04.scn new file mode 100644 index 0000000000..f0919ec2d4 --- /dev/null +++ b/testsuites/sptests/spstkalloc04/spstkalloc04.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST SPSTKALLOC 4 *** +*** END OF TEST SPSTKALLOC 4 *** diff --git a/testsuites/sptests/sptask_err04/task1.c b/testsuites/sptests/sptask_err04/task1.c index 29b63db5f4..c1f59d81fb 100644 --- a/testsuites/sptests/sptask_err04/task1.c +++ b/testsuites/sptests/sptask_err04/task1.c @@ -165,6 +165,15 @@ rtems_task Task_1( ); puts( "TA1 - rtems_task_start - RTEMS_INVALID_ID" ); + /* NULL entry point */ + status = rtems_task_start( RTEMS_SELF, NULL, 0 ); + fatal_directive_status( + status, + RTEMS_INVALID_ADDRESS, + "rtems_task_start with NULL entry point" + ); + puts( "TA1 - rtems_task_start - RTEMS_INVALID_ADDRESS" ); + /* already started */ status = rtems_task_start( RTEMS_SELF, Task_1, 0 ); fatal_directive_status( diff --git a/testsuites/sptests/sptimecounter01/init.c b/testsuites/sptests/sptimecounter01/init.c index 81b705473e..12d5a7820d 100644 --- a/testsuites/sptests/sptimecounter01/init.c +++ b/testsuites/sptests/sptimecounter01/init.c @@ -196,11 +196,11 @@ void boot_card(const char *cmdline) assert(bt.sec == 1); assert(bt.frac == 18446744073708); - /* Ensure that the fraction overflows and the second remains constant */ + /* Check that a large delta yields a correct time */ ctx->counter = (0xf0000000 | 1) + TEST_FREQ; rtems_bsd_binuptime(&bt); assert(ctx->counter == (0xf0000000 | 2) + TEST_FREQ); - assert(bt.sec == 1); + assert(bt.sec == 2); assert(bt.frac == 18446742522092); test_install(ctx); |