diff --git a/arch/c5471/defconfig b/arch/c5471/defconfig index efb0d7cc6d..4b397e4056 100644 --- a/arch/c5471/defconfig +++ b/arch/c5471/defconfig @@ -43,6 +43,37 @@ CONFIG_ARCH=c5471 CONFIG_ARCH_C5471=y CONFIG_ROM_VECTORS=n +# +# C5471 specific device driver settings +# +# CONFIG_SERIAL_IRDA_CONSOLE - selects the IRDA UART for the +# console ant ttys0 (default is the modem UART). +# CONFIG_UART_*_HWFLOWCONTROL - enables hardware flow control +# CONFIG_UART_*_RXBUFSIZE - Characters are buffered as received. +# This specific the size of the receive buffer +# CONFIG_UART_*_TXBUFSIZE - Characters are buffered before +# being sent. This specific the size of the transmit buffer +# CONFIG_UART_*_BAUD - The configure BAUD of the UART. Must be +# CONFIG_UART_*_BITS - The number of bits. Must be either 7 or 8. +# CONFIG_UART_*_PARTIY - 0=no parity, 1=odd parity, 2=even parity +# CONFIG_UART_*_2STOP - Two stop bits +# +CONFIG_SERIAL_IRDA_CONSOLE=n +CONFIG_UART_IRDA_HWFLOWCONTROL=y +CONFIG_UART_MODEM_HWFLOWCONTROL=y +CONFIG_UART_IRDA_RXBUFSIZE=256 +CONFIG_UART_MODEM_RXBUFSIZE=256 +CONFIG_UART_IRDA_TXBUFSIZE=256 +CONFIG_UART_MODEM_TXBUFSIZE=256 +CONFIG_UART_IRDA_BAUD=115200 +CONFIG_UART_MODEM_BAUD=115200 +CONFIG_UART_IRDA_BITS=8 +CONFIG_UART_MODEM_BITS=8 +CONFIG_UART_IRDA_PARITY=0 +CONFIG_UART_MODEM_PARITY=0 +CONFIG_UART_IRDA_2STOP=0 +CONFIG_UART_MODEM_2STOP=0 + # # General OS setup # @@ -69,7 +100,7 @@ CONFIG_ROM_VECTORS=n CONFIG_EXAMPLE=ostest CONFIG_DEBUG=y CONFIG_DEBUG_VERBOSE=n -CONFIG_ARCH_LOWPUTC=n +CONFIG_ARCH_LOWPUTC=y CONFIG_RR_INTERVAL=200 CONFIG_SCHED_INSTRUMENTATION=n CONFIG_TASK_NAME_SIZE=0 @@ -77,7 +108,7 @@ CONFIG_START_YEAR=2007 CONFIG_START_MONTH=2 CONFIG_START_DAY=13 CONFIG_JULIAN_TIME=n -CONFIG_DEV_CONSOLE=n +CONFIG_DEV_CONSOLE=y # # Allow for artchitecture optimized implementations @@ -157,9 +188,9 @@ CONFIG_PREALLOC_WDOGS=32 # CONFIG_HEAP_SIZE - The size of the heap # CONFIG_BOOT_FROM_FLASH=n -CONFIG_STACK_POINTER=0x02100000 -CONFIG_PROC_STACK_SIZE=0x00001000 +CONFIG_STACK_POINTER= +CONFIG_PROC_STACK_SIZE=4096 CONFIG_PTHREAD_STACK_MIN=256 CONFIG_PTHREAD_STACK_DEFAULT=4096 -CONFIG_HEAP_BASE=0x02100000 -CONFIG_HEAP_SIZE=0x00100000 +CONFIG_HEAP_BASE=(0x10300000+90*1024+4096) +CONFIG_HEAP_SIZE=(0x11000000-CONFIG_HEAP_BASE) diff --git a/arch/c5471/include/irq.h b/arch/c5471/include/irq.h index 1626e87e44..0745ba6aab 100644 --- a/arch/c5471/include/irq.h +++ b/arch/c5471/include/irq.h @@ -120,7 +120,7 @@ #define C5471_IRQ_API 15 #define C5471_IRQ_WATCHDOG C5471_IRQ_TIMER0 -#define C5471_IRQ_SYSTIMER C5471_IRQ_TIMER1 +#define C5471_IRQ_SYSTIMER C5471_IRQ_TIMER2 #define NR_IRQS (C5471_IRQ_API+1) /************************************************************ diff --git a/arch/c5471/include/serial.h b/arch/c5471/include/serial.h new file mode 100644 index 0000000000..7c0e5a4f0e --- /dev/null +++ b/arch/c5471/include/serial.h @@ -0,0 +1,66 @@ +/************************************************************ + * serial.h + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 __ARCH_SERIAL_H +#define __ARCH_SERIAL_H + +/************************************************************ + * Included Files + ************************************************************/ + +/************************************************************ + * Definitions + ************************************************************/ + +/* IOCTL commands supported by the C5471 serial driver */ + +#define TIOCSBRK 0x5401 /* BSD compatibility */ +#define TIOCCBRK 0x5402 /* " " " " */ +#define TIOCSERCONFIG 0x5403 /* Reconfigure the port */ +#define TIOCSERGSTRUCT 0x5458 /* Get up_dev_t for port */ + +/************************************************************ + * Private Data + ************************************************************/ + +/************************************************************ + * Private Functions + ************************************************************/ + +/************************************************************ + * Public Functions + ************************************************************/ + +#endif /* __ARCH_SERIAL_H */ diff --git a/arch/c5471/ld.script b/arch/c5471/ld.script index f8bdf630ed..ed083cb70a 100644 --- a/arch/c5471/ld.script +++ b/arch/c5471/ld.script @@ -62,7 +62,7 @@ SECTIONS /* The OS entry point is here */ - . = 0x01030000; + . = 0x10300000; .text : { _stext = ABSOLUTE(.); *(.text) diff --git a/arch/c5471/src/Makefile b/arch/c5471/src/Makefile index a9c0fb117d..b75b1daf62 100644 --- a/arch/c5471/src/Makefile +++ b/arch/c5471/src/Makefile @@ -38,8 +38,10 @@ MKDEP = $(TOPDIR)/tools/mkdeps.sh CFLAGS += -I$(TOPDIR)/sched -ASRCS = up_vectors.S up_saveusercontext.S up_fullcontextrestore.S \ - up_lowputc.S +ASRCS = up_vectors.S up_saveusercontext.S up_fullcontextrestore.S +ifeq ($(CONFIG_DEBUG),y) +ASRCS += up_lowputc.S +endif AOBJS = $(ASRCS:.S=.o) CSRCS = up_initialize.c up_initialstate.c up_idle.c \ @@ -48,7 +50,7 @@ CSRCS = up_initialize.c up_initialstate.c up_idle.c \ up_createstack.c up_usestack.c up_releasestack.c \ up_exit.c up_assert.c up_blocktask.c up_unblocktask.c \ up_releasepending.c up_reprioritizertr.c up_copystate.c \ - up_schedulesigaction.c up_sigdeliver.c + up_schedulesigaction.c up_sigdeliver.c up_serial.c COBJS = $(CSRCS:.c=.o) SRCS = $(ASRCS) $(CSRCS) diff --git a/arch/c5471/src/c5471.h b/arch/c5471/src/c5471.h index c7104be621..c126be1cd1 100644 --- a/arch/c5471/src/c5471.h +++ b/arch/c5471/src/c5471.h @@ -72,6 +72,10 @@ #define CC_Z_BIT (1 << 30) #define CC_N_BIT (1 << 31) +/* Clocking *************************************************/ + +#define C5471_CLOCK 47500000 /* 47.5 MHz */ + /* UARTs ****************************************************/ #define UART_IRDA_BASE 0xffff0800 diff --git a/arch/c5471/src/up_head.S b/arch/c5471/src/up_head.S index f4540c54b6..8d7fbec58e 100644 --- a/arch/c5471/src/up_head.S +++ b/arch/c5471/src/up_head.S @@ -44,6 +44,18 @@ * Definitions ************************************************************/ + /* This macro will modify r0, r1, r2 and r14 */ + +#ifdef CONFIG_DEBUG + .macro showprogress, code + mov r0, #\code + bl up_lowputc + .endm +#else + .macro showprogress, code + .endm +#endif + /************************************************************ * OS Entry Point ************************************************************/ @@ -56,24 +68,28 @@ .type __start, #function __start: -/* First, setup initial processor mode */ + /* First, setup initial processor mode */ mov r0, #(SVC_MODE | I_BIT | F_BIT ) msr cpsr, r0 -/* Setup system stack (and get the BSS range) */ + showprogress 'A' + + /* Setup system stack (and get the BSS range) */ adr r0, LC0 ldmia r0, {r4, r5, sp} -/* Clear system BSS section */ + /* Clear system BSS section */ mov r0, #0 1: cmp r4, r5 strcc r0, [r4], #4 bcc 1b -/* Copy system .data sections to new home in RAM. */ + showprogress 'B' + + /* Copy system .data sections to new home in RAM. */ #ifdef CONFIG_BOOT_FROM_FLASH @@ -86,40 +102,33 @@ __start: blt 1b #endif - -/* Initialize Kernel Stack Contents */ - -#if 0 - mov r1, sp - sub r1, r1, #INITIAL_STACK_SIZE - ldr r0, L_STACK_MAGIC - str r0, [r1], #4 - ldr r0, L_STACK_UNTOUCHED_MAGIC -1: cmp r1, sp - strcc r0, [r1], #4 - bcc 1b -#endif - -/* Jump to OS entry */ + /* Perform early serial initialization */ mov fp, #0 + bl up_earlyserialinit + +#ifdef CONFIG_DEBUG + mov r0, #'C' + bl up_putc + mov r0, #'\n' + bl up_putc +#endif + + /* Then jump to OS entry */ + b os_start -/* Variables */ + /* Variables */ LC0: .long _sbss .long _ebss - .long CONFIG_STACK_POINTER+CONFIG_PROC_STACK_SIZE-4 + .long _ebss+CONFIG_PROC_STACK_SIZE-4 #ifdef CONFIG_BOOT_FROM_FLASH -LC2: .long _eronly @ Where .data defaults are stored in Flash. - .long _sdata @ Where .data needs to reside in SDRAM. +LC2: .long _eronly /* Where .data defaults are stored in FLASH */ + .long _sdata /* Where .data needs to reside in SDRAM */ .long _edata #endif -#if 0 -L_STACK_UNTOUCHED_MAGIC: .long 0xfeef1ef0 -L_STACK_MAGIC: .long 0xdeadbeef -#endif .end diff --git a/arch/c5471/src/up_initialize.c b/arch/c5471/src/up_initialize.c index 62fdfd3874..8b85ecd719 100644 --- a/arch/c5471/src/up_initialize.c +++ b/arch/c5471/src/up_initialize.c @@ -41,7 +41,6 @@ #include #include #include -#include #include "up_internal.h" /************************************************************ @@ -86,13 +85,15 @@ void up_initialize(void) up_irqinitialize(); - /* Attach and enable the timer interrupt */ + /* Initialize the system timer interrupt */ - up_disable_irq(C5471_IRQ_SYSTIMER); - irq_attach(C5471_IRQ_SYSTIMER, (xcpt_t)up_timerisr); - up_enable_irq(C5471_IRQ_SYSTIMER); + up_timerinit(); /* Register devices */ devnull_register(); /* Standard /dev/null */ + + /* Initialize the serial device driver */ + + up_serialinit(); } diff --git a/arch/c5471/src/up_internal.h b/arch/c5471/src/up_internal.h index d3e806d12b..e11783e023 100644 --- a/arch/c5471/src/up_internal.h +++ b/arch/c5471/src/up_internal.h @@ -84,6 +84,12 @@ extern void up_syscall(uint32 *regs); extern int up_timerisr(int irq, uint32 *regs); extern void up_undefinedinsn(uint32 *regs); +#ifdef CONFIG_DEBUG +extern void up_lowputc(char ch); +#else +# define up_lowputc(ch) +#endif + /* Defined in up_vectors.S */ extern void up_vectorundefinsn(void); @@ -94,6 +100,15 @@ extern void up_vectoraddrexcptn(void); extern void up_vectorirq(void); extern void up_vectorfiq(void); +/* Defined in up_serial.c */ + +extern void up_earlyserialinit(void); +extern void up_serialinit(void); + +/* Defined in up_timerisr.c */ + +extern void up_timerinit(void); + #endif /* __ASSEMBLY__ */ #endif /* __UP_INTERNAL_H */ diff --git a/arch/c5471/src/up_lowputc.S b/arch/c5471/src/up_lowputc.S index 24bf551feb..050870c4c5 100644 --- a/arch/c5471/src/up_lowputc.S +++ b/arch/c5471/src/up_lowputc.S @@ -79,7 +79,7 @@ */ .text - .global up_putc + .global up_lowputc .type up_lowputc, function up_lowputc: /* On entry, r0 holds the character to be printed */ diff --git a/arch/c5471/src/up_serial.c b/arch/c5471/src/up_serial.c new file mode 100644 index 0000000000..63fd5bbedd --- /dev/null +++ b/arch/c5471/src/up_serial.c @@ -0,0 +1,1192 @@ +/************************************************************ + * up_serial.c + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * 3. Neither the name Gregory Nutt nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ************************************************************/ + +/************************************************************ + * Included Files + ************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "c5471.h" +#include "os_internal.h" +#include "up_internal.h" + +/************************************************************ + * Definitions + ************************************************************/ + +#define BASE_BAUD 115200 + +/************************************************************ + * Private Types + ************************************************************/ + +/* This is the internal structure for each serial port's state. */ + +struct uart_regs_s +{ + uint32 ier; + uint32 lcr; + uint32 fcr; + uint32 efr; + uint32 tcr; +}; + +struct uart_buffer_s +{ + int head; + int tail; + int size; + char *buffer; +}; + +struct up_dev_s +{ + int open_count; /* The number of times + * the device has been opened */ + unsigned int uartbase; /* Base address of UART + * registers */ + unsigned int baud_base; /* Base baud for conversions */ + unsigned int baud; /* Configured baud */ + int quotient; /* Quotient for configured baud */ + ubyte xmit_fifo_size; /* Size of transmit FIFO */ + ubyte irq; /* IRQ associated with + * this UART */ + boolean parity; /* 0=none, 1=odd, 2=even */ + boolean bits; /* Number of bits (7 or 8) */ + boolean flowcontrol; /* TRUE: Hardware flow control + * is enabled. */ + boolean stopbits2; /* TRUE: Configure with 2 + * stop bits instead of 1 */ + boolean xmitwaiting; /* TRUE: User is waiting + * for space in xmit.buffer */ + boolean recvwaiting; /* TRUE: User is waiting + * for space in recv.buffer */ + sem_t closesem; /* Looks out new opens while + * close is in progress */ + sem_t xmitsem; /* Used to wakeup user waiting + * for space in xmit.buffer */ + sem_t recvsem; /* Used to wakeup user waiting + * for sapce in recv.buffer */ + struct uart_buffer_s xmit; /* Describes transmit buffer */ + struct uart_buffer_s recv; /* Describes receive buffer */ + struct uart_regs_s regs; /* Shadow copy of readonly regs */ +}; +typedef struct up_dev_s up_dev_t; + +/************************************************************ + * Private Function Prototypes + ************************************************************/ + +static int up_open(struct file *filep); +static int up_close(struct file *filep); +static ssize_t up_read(struct file *filep, char *buffer, size_t buflen); +static ssize_t up_write(struct file *filep, const char *buffer, size_t buflen); +static int up_ioctl(struct file *filep, int cmd, unsigned long arg); +static void up_consoleinit(up_dev_t *dev); +static void up_uartsetup(up_dev_t *dev); +static void up_delay(int milliseconds); + +/************************************************************ + * Private Variables + ************************************************************/ + +struct file_operations g_serialops = +{ + .open = up_open, + .close = up_close, + .read = up_read, + .write = up_write, + .ioctl = up_ioctl, +}; + +/* This describes the state of the C5471 serial IRDA port. */ + +static up_dev_t g_irdaport = +{ + .xmit_fifo_size = UART_IRDA_XMIT_FIFO_SIZE, + .baud_base = BASE_BAUD, + .irq = C5471_IRQ_UART_IRDA, + .uartbase = UART_IRDA_BASE, + .baud = CONFIG_UART_IRDA_BAUD, + .parity = CONFIG_UART_IRDA_PARITY, + .bits = CONFIG_UART_IRDA_BITS, + .flowcontrol = CONFIG_UART_IRDA_HWFLOWCONTROL, + .stopbits2 = CONFIG_UART_IRDA_2STOP, +}; + +/* This describes the state of the C5471 serial Modem port. */ + +static up_dev_t g_modemport = +{ + .xmit_fifo_size = UART_XMIT_FIFO_SIZE, + .baud_base = BASE_BAUD, + .irq = C5471_IRQ_UART, + .uartbase = UART_MODEM_BASE, + .baud = CONFIG_UART_MODEM_BAUD, + .parity = CONFIG_UART_MODEM_PARITY, + .bits = CONFIG_UART_MODEM_BITS, + .flowcontrol = CONFIG_UART_MODEM_HWFLOWCONTROL, + .stopbits2 = CONFIG_UART_MODEM_2STOP, +}; + +/* I/O buffers */ + +static char g_irdarxbuffer[CONFIG_UART_IRDA_RXBUFSIZE]; +static char g_irdatxbuffer[CONFIG_UART_IRDA_TXBUFSIZE]; +static char g_modemrxbuffer[CONFIG_UART_IRDA_RXBUFSIZE]; +static char g_modemtxbuffer[CONFIG_UART_MODEM_TXBUFSIZE]; + +/* Now, which one with be tty0/console and which tty1? */ + +#ifdef CONFIG_SERIAL_IRDA_CONSOLE +# define CONSOLE_DEV g_irdaport +# define TTYS0_DEV g_irdaport +# define TTYS0_RXBUFFER g_irdarxbuffer +# define TTYS0_RXBUFSIZE CONFIG_UART_IRDA_RXBUFSIZE +# define TTYS0_TXBUFFER g_irdatxbuffer +# define TTYS0_TXBUFSIZE CONFIG_UART_IRDA_TXBUFSIZE +# define TTYS1_DEV g_modemport +# define TTYS1_RXBUFFER g_modemrxbuffer +# define TTYS1_RXBUFSIZE CONFIG_UART_MODEM_RXBUFSIZE +# define TTYS1_TXBUFFER g_modemtxbuffer +# define TTYS1_TXBUFSIZE CONFIG_UART_MODEM_TXBUFSIZE +#else +# define CONSOLE_DEV g_modemport +# define TTYS0_DEV g_modemport +# define TTYS0_RXBUFFER g_modemrxbuffer +# define TTYS0_RXBUFSIZE CONFIG_UART_MODEM_RXBUFSIZE +# define TTYS0_TXBUFFER g_modemtxbuffer +# define TTYS0_TXBUFSIZE CONFIG_UART_MODEM_TXBUFSIZE +# define TTYS1_DEV g_irdaport +# define TTYS1_RXBUFFER g_irdarxbuffer +# define TTYS1_RXBUFSIZE CONFIG_UART_IRDA_RXBUFSIZE +# define TTYS1_TXBUFFER g_irdatxbuffer +# define TTYS1_TXBUFSIZE CONFIG_UART_IRDA_TXBUFSIZE +#endif + +/************************************************************ + * Private Functions + ************************************************************/ + +static inline uint32 up_inserial(up_dev_t *dev, uint32 offset) +{ + return getreg32(dev->uartbase + offset); +} + +static inline void up_serialout(up_dev_t *dev, + uint32 offset, + uint32 value) +{ + putreg32(value, dev->uartbase + offset); +} + +static inline void up_serialreset(up_dev_t *dev) +{ + /* Both the IrDA and MODEM UARTs support RESET and UART mode. */ + + up_serialout(dev, UART_MDR_OFFS, MDR_RESET_MODE); + up_delay(5); + up_serialout(dev, UART_MDR_OFFS, MDR_UART_MODE); + up_delay(5); +} + +static inline void up_disabletxint(up_dev_t *dev) +{ + dev->regs.ier &= ~UART_IER_XmitInt; + up_serialout(dev, UART_IER_OFFS, dev->regs.ier); +} + +static inline void up_disablerxint(up_dev_t *dev) +{ + dev->regs.ier &= ~UART_IER_RecvInt; + up_serialout(dev, UART_IER_OFFS, dev->regs.ier); +} + +static inline void up_enabletxint(up_dev_t *dev) +{ + dev->regs.ier |= UART_IER_XmitInt; + up_serialout(dev, UART_IER_OFFS, dev->regs.ier); +} + +static inline void up_enablerxint(up_dev_t *dev) +{ + dev->regs.ier |= UART_IER_RecvInt; + up_serialout(dev, UART_IER_OFFS, dev->regs.ier); +} + +static inline void up_disableuartint(up_dev_t *dev, uint16 *msr) +{ + *msr = dev->regs.ier & UART_IER_AllInts; + dev->regs.ier &= ~UART_IER_AllInts; + up_serialout(dev, UART_IER_OFFS, dev->regs.ier); +} + +static inline void up_restoreuartint(up_dev_t *dev, uint16 msr) +{ + dev->regs.ier |= msr & (UART_IER_RecvInt|UART_IER_XmitInt); + up_serialout(dev, UART_IER_OFFS, dev->regs.ier); +} + +static inline void up_clearfifos(up_dev_t *dev) +{ + up_serialout(dev, UART_EFR_OFFS, 0x0010); /* Enable fifo control */ + up_serialout(dev, UART_TFCR_OFFS, 0); /* Reset to 0 */ + up_serialout(dev, UART_RFCR_OFFS, UART_FCR_RX_CLR); /* Clear RX fifo */ + up_serialout(dev, UART_TFCR_OFFS, UART_FCR_TX_CLR); /* Clear TX fifo */ + up_serialout(dev, UART_TFCR_OFFS, UART_FCR_FIFO_EN); /* Enable RX/TX fifos */ +} + +static inline void up_disablebreaks(up_dev_t *dev) +{ + dev->regs.lcr &= ~UART_LCR_BOC; + up_serialout(dev, UART_LCR_OFFS, dev->regs.lcr); +} + +static inline void up_enablebreaks(up_dev_t *dev) +{ + dev->regs.lcr |= UART_LCR_BOC; + up_serialout(dev, UART_LCR_OFFS, dev->regs.lcr); +} + +static inline void up_setrate(up_dev_t *dev, unsigned int rate) +{ + uint32 div_bit_rate; + + switch (rate) + { + case 115200: + div_bit_rate = BAUD_115200; + break; + case 57600: + div_bit_rate = BAUD_57600; + break; + case 38400: + div_bit_rate = BAUD_38400; + break; + case 19200: + div_bit_rate = BAUD_19200; + break; + case 4800: + div_bit_rate = BAUD_4800; + break; + case 2400: + div_bit_rate = BAUD_2400; + break; + case 1200: + div_bit_rate = BAUD_1200; + break; + case 9600: + default: + div_bit_rate = BAUD_9600; + break; + } + + up_serialout(dev, UART_DIV_BIT_RATE_OFFS, div_bit_rate); +} + +static inline void up_setmode(up_dev_t *dev, uint16 mode) +{ + dev->regs.lcr &= 0xffffffe0; /* clear original field, and... */ + dev->regs.lcr |= (uint32)mode; /* Set new bits in that field. */ + up_serialout(dev, UART_LCR_OFFS, dev->regs.lcr); +} + +static inline void up_setrxtrigger(up_dev_t *dev, uint16 val) +{ + /* does val need to be shifted to the right spot? */ + dev->regs.fcr = (dev->regs.fcr & 0xffffff3f) | (val & 0xc0); + up_serialout(dev, UART_RFCR_OFFS, dev->regs.fcr); +} + +static inline void up_settxtrigger(up_dev_t *dev, uint16 val) +{ + /* does val need to be shifted to the right spot? */ + dev->regs.fcr = (dev->regs.fcr & 0xffffffcf) | (val & 0x30); + up_serialout(dev, UART_RFCR_OFFS, dev->regs.fcr); +} + +static inline void up_outserialchar(up_dev_t *dev, ubyte val) +{ + up_serialout(dev, UART_THR_OFFS, val); +} + +static inline unsigned char up_inserialchar(up_dev_t *dev, + uint16 *status) +{ + uint32 rhr; + uint32 lsr; + + /* Next, construct a 16bit status word that + * uses the high byte to hold the status bits + * associated with framing,parity,break and + * a low byte that holds error bits of LSR for + * conditions such as overflow, etc. + */ + + rhr = up_inserial(dev, UART_RHR_OFFS); + lsr = up_inserial(dev, UART_LSR_OFFS); + + *status = (uint16)((rhr & 0x0000ff00) | (lsr & 0x000000ff)); + + return rhr & 0x000000ff; +} + +static inline int up_errorcondition(uint16 status) +{ + return status & 0x0782; +} + +static inline int up_parityerror(uint16 status) +{ + return status & 0x0100; +} + +static inline int up_framingerror(uint16 status) +{ + return status & 0x0200; +} + +static inline int up_overrunerror(uint16 status) +{ + return status & 0x0002; +} + +static inline uint32 up_getintcause(up_dev_t *dev) +{ + return up_inserial(dev, UART_ISR_OFFS) & 0x0000003f; +} + +static inline int up_xmitint(up_dev_t *dev, + uint32 cause) +{ + return (cause & 0x00000002) == 0x00000002; +} + +static inline int up_recvint(up_dev_t *dev, uint32 cause) +{ + return (cause & 0x0000000c) == 0x00000004; +} + +static inline int up_recvtimeoutint(up_dev_t *dev, uint32 cause) +{ + return (cause & 0x0000000c) == 0x0000000c; +} + +static inline ubyte up_txfifoempty(up_dev_t *dev) +{ + return (up_inserial(dev, UART_LSR_OFFS) & UART_LSR_TREF) != 0; +} + +static inline ubyte up_txfifonotfull(up_dev_t *dev) +{ + return (up_inserial(dev, UART_SSR_OFFS) & UART_SSR_TXFULL) == 0; +} + +static inline void up_waitforxmtr(up_dev_t *dev) +{ + int tmp; + for (tmp = 1000 ; tmp > 0 ; tmp--) + { + if (up_txfifonotfull(dev)) + { + break; + } + } +} + +static inline ubyte up_rxfifonotempty(up_dev_t *dev) +{ + return up_inserial(dev, UART_LSR_OFFS) & UART_RX_FIFO_NOEMPTY; +} + +static inline void serial_enable_hw_flow_control(up_dev_t *dev) +{ + /* Set the FIFO level triggers for flow control + * Halt = 48 bytes, resume = 12 bytes + */ + + dev->regs.tcr = (dev->regs.tcr & 0xffffff00) | 0x0000003c; + up_serialout(dev, UART_TCR_OFFS, dev->regs.tcr); + + /* Enable RTS/CTS flow control */ + + dev->regs.efr |= 0x000000c0; + up_serialout(dev, UART_EFR_OFFS, dev->regs.efr); +} + +static inline void serial_disable_hw_flow_control(up_dev_t *dev) +{ + /* Disable RTS/CTS flow control */ + + dev->regs.efr &= 0xffffff3f; + up_serialout(dev, UART_EFR_OFFS, dev->regs.efr); +} + +static inline void up_saveregisters(up_dev_t *dev) +{ + dev->regs.ier = up_inserial(dev, UART_IER_OFFS); + dev->regs.lcr = up_inserial(dev, UART_LCR_OFFS); + if (dev->flowcontrol) + { + dev->regs.efr = up_inserial(dev, UART_EFR_OFFS); + dev->regs.tcr = up_inserial(dev, UART_TCR_OFFS); + } +} + +#define LOOPS_PER_MSEC 1250 + +static void up_delay(int milliseconds) +{ + volatile int i, j; + for (i = 0; i < milliseconds; i++) { + for (j = 0; j < LOOPS_PER_MSEC; j++) { + } + } +} + +/************************************************************ + * up_takesem + ************************************************************/ + +static void up_takesem(sem_t *sem) +{ + while (sem_wait(sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + ASSERT(*get_errno_ptr() == EINTR); + } +} + +/************************************************************ + * Name: up_givesem + ************************************************************/ + +static inline void up_givesem(sem_t *sem) +{ + (void)sem_post(sem); +} + +/************************************************************ + * Name: up_receivechars + ************************************************************/ + +/* Add chars to head of receive buffer. up_read will take + * characters from the tail of the buffer. + */ + +static inline void up_recvchars(up_dev_t *dev) +{ + uint16 status; + int nexthead = dev->recv.head + 1; + + if (nexthead >= dev->recv.size) + { + nexthead = 0; + } + + while (nexthead != dev->recv.tail && up_rxfifonotempty(dev)) + { + dev->recv.buffer[dev->recv.head] = up_inserialchar(dev, &status); + + dev->recv.head = nexthead; + if (++nexthead >= dev->recv.size) + { + nexthead = 0; + } + + if (dev->recvwaiting) + { + dev->recvwaiting = FALSE; + up_givesem(&dev->recvsem); + } + } +} + +/************************************************************ + * up_xmitchars + ************************************************************/ + +/* Transmit characters from the tail of the xmit buffer while + * up_write adds data to the head of the xmit buffer. + */ + +static inline void up_xmitchars(up_dev_t *dev) +{ + /* Send while we still have data & room in the fifo */ + + while (dev->xmit.head != dev->xmit.tail && up_txfifonotfull(dev)) + { + up_outserialchar(dev, dev->xmit.buffer[dev->xmit.tail]); + + if (++(dev->xmit.tail) >= dev->xmit.size) + { + dev->xmit.tail = 0; + } + + if (dev->xmitwaiting) + { + dev->xmitwaiting = FALSE; + up_givesem(&dev->xmitsem); + } + } + + /* When all of the characters have been sent from the buffer + * disable the TX interrupt. + */ + + if (dev->xmit.head == dev->xmit.tail) + { + up_disabletxint(dev); + } +} + +/************************************************************ + * Name: up_interrupt + ************************************************************/ + +/* This routine is used to handle the "top half" processing for the + * serial driver. + */ + +static int up_interrupt(int irq, struct xcptcontext *xcp) +{ + up_dev_t *dev; + volatile uint32 cause; + + if (g_irdaport.irq == irq) + { + dev = &g_irdaport; + } + else if (g_modemport.irq == irq) + { + dev = &g_modemport; + } + else + { + PANIC(OSERR_INTERNAL); + } + + cause = up_getintcause(dev); + + if (up_recvtimeoutint(dev, cause)) + { + uint32 ier_val = 0; + + /* Is this an interrupt from the IrDA UART? */ + + if (irq == C5471_IRQ_UART_IRDA) + { + /* Save the currently enabled IrDA UART interrupts + * so that we can restore the IrDA interrupt state + * below. + */ + + ier_val = up_inserial(dev, UART_IER_OFFS); + + /* Then disable all IrDA UART interrupts */ + + up_serialout(dev, UART_IER_OFFS, 0); + } + + /* Receive characters from the RX fifo */ + + up_recvchars(dev); + + /* read UART_RHR to clear int condition + * toss = up_inserialchar(dev,&status); + */ + + /* Is this an interrupt from the IrDA UART? */ + + if (irq == C5471_IRQ_UART_IRDA) + { + /* Restore the IrDA UART interrupt enables */ + + up_serialout(dev, UART_IER_OFFS, ier_val); + } + } + else if (up_recvint(dev, cause)) + { + up_recvchars(dev); + } + + if (up_xmitint(dev, cause)) + { + up_xmitchars(dev); + } + + return OK; +} + +/************************************************************ + * Name: up_uartsetup + ************************************************************/ + +static void up_uartsetup(up_dev_t *dev) +{ + uint32 flags; + uint16 mrs; + + flags = irqsave(); + + up_saveregisters(dev); + up_disableuartint(dev, &mrs); + up_clearfifos(dev); + up_disablebreaks(dev); + up_settxtrigger(dev, UART_FCR_FTL); + up_setrxtrigger(dev, UART_FCR_FTL); + + irqrestore(flags); +} + +/************************************************************ + * up_cval + ************************************************************/ + +static unsigned int up_cval(up_dev_t *dev) +{ + unsigned int cval; + + if (dev->bits == 7) + { + cval = UART_LCR_7bits; + } + else + { + cval = UART_LCR_8bits; + } + + if (dev->stopbits2) + { + cval |= UART_LCR_2stop; + } + + if (dev->parity == 1) /* Odd parity */ + { + cval |= (UART_LCR_ParEn|UART_LCR_ParOdd); + } + else if (dev->parity == 2) /* Even parity */ + { + cval |= (UART_LCR_ParEn|UART_LCR_ParEven); + } + + return cval; +} + +/************************************************************ + * Name: up_portstartup + ************************************************************/ + +static int up_portstartup(up_dev_t *dev) +{ + uint32 flags; + unsigned cval = up_cval(dev); + int quotient = 0; + uint16 ier; + int ret = OK; + + flags = irqsave(); + + up_saveregisters(dev); + up_disableuartint(dev, &ier); + up_clearfifos(dev); + up_disablebreaks(dev); + + /* Attache and enabled the IRQ */ + + ret = irq_attach(dev->irq, up_interrupt); + if (ret != OK) + { + goto errout; + } + + /* Mark the io buffers empty */ + + dev->xmit.head = 0; + dev->xmit.tail = 0; + dev->recv.head = 0; + dev->recv.tail = 0; + + /* And set the speed of the serial port */ + + /* Determine divisor based on baud rate */ + + if (dev->baud == 134) + { + /* Special case since 134 is really 134.5 */ + + quotient = (2*dev->baud_base / 269); + } + else if (dev->baud) + { + quotient = (dev->baud_base / dev->baud) - 1; + } + + /* If the quotient is zero, default to 9600 bps */ + + if (!quotient && dev->baud_base != dev->baud) + { + quotient = (dev->baud_base / 9600) - 1; + } + + /* Set up parity check flag */ + + up_setrate(dev, dev->baud); + up_setmode(dev, cval); + + if (dev->flowcontrol) + { + serial_enable_hw_flow_control(dev); + } + else + { + serial_disable_hw_flow_control(dev); + } + + /* Finally, enable interrupts */ + + up_enable_irq(dev->irq); + up_enablerxint(dev); + ret = 0; + +errout: + irqrestore(flags); + return ret; +} + +/************************************************************ + * shutdown + ************************************************************/ + +/* This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ + +static void shutdown(up_dev_t * dev) +{ + uint32 flags; + uint16 msr; + + /* Free the IRQ */ + + flags = irqsave(); /* Disable interrupts */ + up_disable_irq(dev->irq); + irq_detach(dev->irq); + up_disableuartint(dev, &msr); + irqrestore(flags); +} + +/************************************************************ + * Name: up_write + ************************************************************/ + +static ssize_t up_write(struct file *filep, const char *buffer, size_t buflen) +{ + struct inode *inode = filep->f_inode; + up_dev_t *dev = inode->i_private; + ssize_t ret = buflen; + + /* Loop while we still have data to copy to the transmit buffer. + * we add data to the head of the buffer; up_xmitchars takes the + * data from the end of the buffer. + */ + + up_disabletxint(dev); + while (buflen) + { + int nexthead = dev->xmit.head + 1; + if (nexthead >= dev->xmit.size) + { + nexthead = 0; + } + + if (nexthead != dev->xmit.tail) + { + dev->xmit.buffer[dev->xmit.head] = *buffer++; + dev->xmit.head = nexthead; + buflen--; + } + else + { + /* Transfer some characters with interrupts disabled */ + + up_xmitchars(dev); + + /* If we unsuccessful in making room in the buffer. + * then transmit the characters with interrupts + * enabled and wait for result. + */ + + if (nexthead == dev->xmit.tail) + { + /* Still no space */ + + dev->xmitwaiting = TRUE; + + /* Wait for some characters to be sent from the buffer + * with the TX interrupt disabled. + */ + + up_enabletxint(dev); + up_takesem(&dev->xmitsem); + up_disabletxint(dev); + } + } + } + + if (dev->xmit.head != dev->xmit.tail) + { + up_xmitchars(dev); + up_enabletxint(dev); + } + + return ret; +} + +/************************************************************ + * Name: up_read + ************************************************************/ + +static ssize_t up_read(struct file *filep, char *buffer, size_t buflen) +{ + struct inode *inode = filep->f_inode; + up_dev_t *dev = inode->i_private; + ssize_t ret = buflen; + + /* Loop while we still have data to copy to the receive buffer. + * we add data to the head of the buffer; up_xmitchars takes the + * data from the end of the buffer. + */ + + up_disablerxint(dev); + while (buflen) + { + if (dev->recv.head != dev->recv.tail) + { + *buffer++ = dev->recv.buffer[dev->recv.tail]; + buflen--; + + if (++dev->recv.tail >= dev->recv.size) + { + dev->recv.tail = 0; + } + } + else + { + /* Wait for some characters to be sent from the buffer + * with the TX interrupt disabled. + */ + + dev->recvwaiting = TRUE; + up_enablerxint(dev); + up_takesem(&dev->recvsem); + up_disablerxint(dev); + } + } + + up_enabletxint(dev); + + return ret; +} + +/************************************************************ + * Name: up_ioctl + ************************************************************/ + +static int up_ioctl(struct file *filep, int cmd, unsigned long arg) +{ + struct inode *inode = filep->f_inode; + up_dev_t *dev = inode->i_private; + int ret = OK; + + switch (cmd) + { + case TIOCSERCONFIG: + { + shutdown(dev); + up_uartsetup(dev); + ret = up_portstartup(dev); + } + break; + + case TIOCSERGSTRUCT: + { + up_dev_t *user = (up_dev_t*)arg; + if (!user) + { + *get_errno_ptr() = EINVAL; + ret = ERROR; + } + else + { + memcpy(user, dev, sizeof(up_dev_t)); + } + } + break; + + case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ + { + uint32 flags = irqsave(); + up_enablebreaks(dev); + irqrestore(flags); + } + break; + + case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ + { + uint32 flags; + flags = irqsave(); + up_disablebreaks(dev); + irqrestore(flags); + } + break; + + default: + *get_errno_ptr() = ENOTTY; + ret = ERROR; + break; + } + + return ret; +} + +/************************************************************ + * Name: up_close + * + * Description: + * This routine is called when the serial port gets closed. + * It waits for the last remaining data to be sent. + * + ************************************************************/ + +static int up_close(struct file *filep) +{ + struct inode *inode = filep->f_inode; + up_dev_t *dev = inode->i_private; + + up_takesem(&dev->closesem); + if (dev->open_count > 1) + { + dev->open_count--; + up_givesem(&dev->closesem); + return OK; + } + + /* Stop accepting input */ + + up_disablerxint(dev); + + /* Now we wait for the transmit buffer to clear */ + + while (dev->xmit.head != dev->xmit.tail) + { + usleep(500*1000); + } + + /* And wait for the TX fifo to drain */ + + while (!up_txfifoempty(dev)) + { + usleep(500*1000); + } + + /* That's it! */ + + shutdown(dev); + up_givesem(&dev->closesem); + } + +/************************************************************ + * Name: up_open + * + * Description: + * This routine is called whenever a serial port is opened. + * + ************************************************************/ + +static int up_open(struct file *filep) +{ + struct inode *inode = filep->f_inode; + up_dev_t *dev = inode->i_private; + int ret = OK; + + /* If the port is the middle of closing, wait until the close + * is finished + */ + + up_takesem(&dev->closesem); + + /* Start up serial port */ + + if (++dev->open_count == 1) + { + up_uartsetup(dev); + ret = up_portstartup(dev); + } + + up_givesem(&dev->closesem); + return ret; +} + +/************************************************************ + * Name: up_devinit + ************************************************************/ + +static void up_devinit(up_dev_t *dev, + char *rxbuffer, int rxbufsize, + char *txbuffer, int txbufsize) +{ + /* Initialize fields in the dev structure that were not + * statically initialized. + */ + + dev->xmit.size = txbufsize; + dev->xmit.buffer = txbuffer; + dev->recv.size = rxbufsize; + dev->recv.buffer = rxbuffer; + + sem_init(&dev->closesem, 0, 1); + sem_init(&dev->xmitsem, 0, 0); + sem_init(&dev->recvsem, 0, 0); +} + +/************************************************************ + * Name: up_consoleinit + ************************************************************/ + +static void up_consoleinit(up_dev_t *dev) +{ + unsigned cval = up_cval(dev); + uint16 ier; + + up_serialreset(dev); + up_saveregisters(dev); + up_disableuartint(dev, &ier); + up_clearfifos(dev); + up_setrate(dev, dev->baud); + up_setmode(dev, cval); + up_delay(5); + + dev->open_count = 1; +} + +/************************************************************ + * Public Funtions + ************************************************************/ + +/************************************************************ + * Name: up_serialinit + * + * Description: + * Performs the low level UART initialization early in + * debug so that the serial console will be available + * during bootup. This must be called before up_serialinit. + * + ************************************************************/ + +void up_earlyserialinit(void) +{ + /* Set up default port setings */ + + up_devinit(&TTYS0_DEV, + TTYS0_RXBUFFER, TTYS0_RXBUFSIZE, + TTYS0_TXBUFFER, TTYS0_TXBUFSIZE); + up_devinit(&TTYS1_DEV, + TTYS1_RXBUFFER, TTYS1_RXBUFSIZE, + TTYS1_TXBUFFER, TTYS1_TXBUFSIZE); + + /* Configure the console for use now */ + + up_consoleinit(&CONSOLE_DEV); +} + +/************************************************************ + * Name: up_serialinit + * + * Description: + * Register serial console and serial ports. This assumes + * that up_earlyserialinit was called previously. + * + ************************************************************/ + +void up_serialinit(void) +{ + (void)register_inode("/dev/console", &g_serialops, 0666, &CONSOLE_DEV); + (void)register_inode("/dev/ttyS0", &g_serialops, 0666, &TTYS0_DEV); + (void)register_inode("/dev/ttyS1", &g_serialops, 0666, &TTYS1_DEV); +} + +/************************************************************ + * Name: up_putc + * + * Description: + * Provide priority, low-level access to support OS debug + * writes + * + ************************************************************/ + +int up_putc(int ch) +{ + uint16 ier; + + up_disableuartint(&CONSOLE_DEV, &ier); + up_waitforxmtr(&CONSOLE_DEV); + up_outserialchar(&CONSOLE_DEV, ch); + + /* Check for LF */ + + if (ch == 10) + { + /* Add CR */ + + up_waitforxmtr(&CONSOLE_DEV); + up_outserialchar(&CONSOLE_DEV, 13); + } + + up_waitforxmtr(&CONSOLE_DEV); + up_restoreuartint(&CONSOLE_DEV, ier); +} diff --git a/arch/c5471/src/up_timerisr.c b/arch/c5471/src/up_timerisr.c index b636d10844..ab1155b71b 100644 --- a/arch/c5471/src/up_timerisr.c +++ b/arch/c5471/src/up_timerisr.c @@ -41,7 +41,30 @@ #include #include #include +#include "clock_internal.h" #include "up_internal.h" +#include "c5471.h" + +/************************************************************ + * Definitions + ************************************************************/ + +/* We want the general purpose timer running at the rate + * MSEC_PER_TICK. The C5471 clock is 47.5MHz and we're using + * a timer PTV value of 3 (3 == divide incoming frequency by + * 16) which then yields a 16 bitCLKS_PER_INT value + * of 29687. + * + * 47500000 / 16 = 2968750 clocks/sec + * 2968750 / 100 = 29687 clocks/ 100Hz interrupt + * + */ + +#define CLKS_PER_INT 29687 +#define CLKS_PER_INT_SHIFT 5 +#define AR 0x00000010 +#define ST 0x00000008 +#define PTV 0x00000003 /************************************************************ * Private Types @@ -91,3 +114,23 @@ int up_timerisr(int irq, uint32 *regs) current_regs = saved_regs; return 0; } + +void up_timerinit(void) +{ + uint32 val; + + up_disable_irq(C5471_IRQ_SYSTIMER); + + /* Start the general purpose timer running in auto-reload mode + * so that an interrupt is generated at the rate MSEC_PER_TICK. + */ + + val = ((CLKS_PER_INT-1) << CLKS_PER_INT_SHIFT) | AR | ST | PTV; + putreg32(val, C5471_TIMER2_CTRL); + + /* Attach and enable the timer interrupt */ + + irq_attach(C5471_IRQ_SYSTIMER, (xcpt_t)up_timerisr); + up_enable_irq(C5471_IRQ_SYSTIMER); +} +