/****************************************************************************** * * * License Agreement * * * * Copyright (c) 2007 Altera Corporation, San Jose, California, USA. * * All rights reserved. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the "Software"), * * to deal in the Software without restriction, including without limitation * * the rights to use, copy, modify, merge, publish, distribute, sublicense, * * and/or sell copies of the Software, and to permit persons to whom the * * Software is furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * * DEALINGS IN THE SOFTWARE. * * * * This agreement shall be governed in all respects by the laws of the State * * of California and by the laws of the United States of America. * * * ******************************************************************************/ #include #include #include #include #include //#include "sys/alt_irq.h" #include "sys/alt_alarm.h" #include "sys/ioctl.h" #include "alt_types.h" #include "altera_avalon_jtag_uart_regs.h" #include "altera_avalon_jtag_uart.h" #include "sys/alt_log_printf.h" #ifndef ALTERA_AVALON_JTAG_UART_SMALL /* ----------------------------------------------------------- */ /* ------------------------- FAST DRIVER --------------------- */ /* ----------------------------------------------------------- */ #ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT static void altera_avalon_jtag_uart_irq(void* context); #else static void altera_avalon_jtag_uart_irq(void* context, alt_u32 id); #endif static alt_u32 altera_avalon_jtag_uart_timeout(void* context); /* * Driver initialization code. Register interrupts and start a timer * which we can use to check whether the host is there. * Return 1 on sucessful IRQ register and 0 on failure. */ void altera_avalon_jtag_uart_init(altera_avalon_jtag_uart_state* sp, int irq_controller_id, int irq) { ALT_FLAG_CREATE(&sp->events, 0); ALT_SEM_CREATE(&sp->read_lock, 1); ALT_SEM_CREATE(&sp->write_lock, 1); /* enable read interrupts at the device */ sp->irq_enable = ALTERA_AVALON_JTAG_UART_CONTROL_RE_MSK; IOWR_ALTERA_AVALON_JTAG_UART_CONTROL(sp->base, sp->irq_enable); /* register the interrupt handler */ #ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT alt_ic_isr_register(irq_controller_id, irq, altera_avalon_jtag_uart_irq, sp, NULL); #else alt_irq_register(irq, sp, altera_avalon_jtag_uart_irq); #endif /* Register an alarm to go off every second to check for presence of host */ sp->host_inactive = 0; if (alt_alarm_start(&sp->alarm, alt_ticks_per_second(), &altera_avalon_jtag_uart_timeout, sp) < 0) { /* If we can't set the alarm then record "don't know if host present" * and behave as though the host is present. */ sp->timeout = INT_MAX; } /* ALT_LOG - see altera_hal/HAL/inc/sys/alt_log_printf.h */ ALT_LOG_JTAG_UART_ALARM_REGISTER(sp, sp->base); } /* * Interrupt routine */ #ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT static void altera_avalon_jtag_uart_irq(void* context) #else static void altera_avalon_jtag_uart_irq(void* context, alt_u32 id) #endif { altera_avalon_jtag_uart_state* sp = (altera_avalon_jtag_uart_state*) context; unsigned int base = sp->base; /* ALT_LOG - see altera_hal/HAL/inc/sys/alt_log_printf.h */ ALT_LOG_JTAG_UART_ISR_FUNCTION(base, sp); for ( ; ; ) { unsigned int control = IORD_ALTERA_AVALON_JTAG_UART_CONTROL(base); /* Return once nothing more to do */ if ((control & (ALTERA_AVALON_JTAG_UART_CONTROL_RI_MSK | ALTERA_AVALON_JTAG_UART_CONTROL_WI_MSK)) == 0) break; if (control & ALTERA_AVALON_JTAG_UART_CONTROL_RI_MSK) { /* process a read irq. Start by assuming that there is data in the * receive FIFO (otherwise why would we have been interrupted?) */ unsigned int data = 1 << ALTERA_AVALON_JTAG_UART_DATA_RAVAIL_OFST; for ( ; ; ) { /* Check whether there is space in the buffer. If not then we must not * read any characters from the buffer as they will be lost. */ unsigned int next = (sp->rx_in + 1) % ALTERA_AVALON_JTAG_UART_BUF_LEN; if (next == sp->rx_out) break; /* Try to remove a character from the FIFO and find out whether there * are any more characters remaining. */ data = IORD_ALTERA_AVALON_JTAG_UART_DATA(base); if ((data & ALTERA_AVALON_JTAG_UART_DATA_RVALID_MSK) == 0) break; sp->rx_buf[sp->rx_in] = (data & ALTERA_AVALON_JTAG_UART_DATA_DATA_MSK) >> ALTERA_AVALON_JTAG_UART_DATA_DATA_OFST; sp->rx_in = (sp->rx_in + 1) % ALTERA_AVALON_JTAG_UART_BUF_LEN; /* Post an event to notify jtag_uart_read that a character has been read */ ALT_FLAG_POST (sp->events, ALT_JTAG_UART_READ_RDY, OS_FLAG_SET); } if (data & ALTERA_AVALON_JTAG_UART_DATA_RAVAIL_MSK) { /* If there is still data available here then the buffer is full * so turn off receive interrupts until some space becomes available. */ sp->irq_enable &= ~ALTERA_AVALON_JTAG_UART_CONTROL_RE_MSK; IOWR_ALTERA_AVALON_JTAG_UART_CONTROL(base, sp->irq_enable); /* Dummy read to ensure IRQ is cleared prior to ISR completion */ IORD_ALTERA_AVALON_JTAG_UART_CONTROL(base); } } if (control & ALTERA_AVALON_JTAG_UART_CONTROL_WI_MSK) { /* process a write irq */ unsigned int space = (control & ALTERA_AVALON_JTAG_UART_CONTROL_WSPACE_MSK) >> ALTERA_AVALON_JTAG_UART_CONTROL_WSPACE_OFST; while (space > 0 && sp->tx_out != sp->tx_in) { IOWR_ALTERA_AVALON_JTAG_UART_DATA(base, sp->tx_buf[sp->tx_out]); sp->tx_out = (sp->tx_out + 1) % ALTERA_AVALON_JTAG_UART_BUF_LEN; /* Post an event to notify jtag_uart_write that a character has been written */ ALT_FLAG_POST (sp->events, ALT_JTAG_UART_WRITE_RDY, OS_FLAG_SET); space--; } if (space > 0) { /* If we don't have any more data available then turn off the TX interrupt */ sp->irq_enable &= ~ALTERA_AVALON_JTAG_UART_CONTROL_WE_MSK; IOWR_ALTERA_AVALON_JTAG_UART_CONTROL(sp->base, sp->irq_enable); /* Dummy read to ensure IRQ is cleared prior to ISR completion */ IORD_ALTERA_AVALON_JTAG_UART_CONTROL(base); } } } } /* * Timeout routine is called every second */ static alt_u32 altera_avalon_jtag_uart_timeout(void* context) { altera_avalon_jtag_uart_state* sp = (altera_avalon_jtag_uart_state *) context; unsigned int control = IORD_ALTERA_AVALON_JTAG_UART_CONTROL(sp->base); if (control & ALTERA_AVALON_JTAG_UART_CONTROL_AC_MSK) { IOWR_ALTERA_AVALON_JTAG_UART_CONTROL(sp->base, sp->irq_enable | ALTERA_AVALON_JTAG_UART_CONTROL_AC_MSK); sp->host_inactive = 0; } else if (sp->host_inactive < INT_MAX - 2) { sp->host_inactive++; if (sp->host_inactive >= sp->timeout) { /* Post an event to indicate host is inactive (for jtag_uart_read */ ALT_FLAG_POST (sp->events, ALT_JTAG_UART_TIMEOUT, OS_FLAG_SET); } } return alt_ticks_per_second(); } /* * The close() routine is implemented to drain the JTAG UART transmit buffer * when not in "small" mode. This routine will wait for transimt data to be * emptied unless a timeout from host-activity occurs. If the driver flags * have been set to non-blocking mode, this routine will exit immediately if * any data remains. This routine should be called indirectly (i.e. though * the C library close() routine) so that the file descriptor associated * with the relevant stream (i.e. stdout) can be closed as well. This routine * does not manage file descriptors. * * The close routine is not implemented for the small driver; instead it will * map to null. This is because the small driver simply waits while characters * are transmitted; there is no interrupt-serviced buffer to empty */ int altera_avalon_jtag_uart_close(altera_avalon_jtag_uart_state* sp, int flags) { /* * Wait for all transmit data to be emptied by the JTAG UART ISR, or * for a host-inactivity timeout, in which case transmit data will be lost */ while ( (sp->tx_out != sp->tx_in) && (sp->host_inactive < sp->timeout) ) { if (flags & O_NONBLOCK) { return -EWOULDBLOCK; } } return 0; } #endif /* !ALTERA_AVALON_JTAG_UART_SMALL */