1 /******************************************************************************
2 *                                                                             *
3 * License Agreement                                                           *
4 *                                                                             *
5 * Copyright (c) 2006 Altera Corporation, San Jose, California, USA.           *
6 * All rights reserved.                                                        *
7 *                                                                             *
8 * Permission is hereby granted, free of charge, to any person obtaining a     *
9 * copy of this software and associated documentation files (the "Software"),  *
10 * to deal in the Software without restriction, including without limitation   *
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,    *
12 * and/or sell copies of the Software, and to permit persons to whom the       *
13 * Software is furnished to do so, subject to the following conditions:        *
14 *                                                                             *
15 * The above copyright notice and this permission notice shall be included in  *
16 * all copies or substantial portions of the Software.                         *
17 *                                                                             *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  *
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    *
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      *
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING     *
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER         *
24 * DEALINGS IN THE SOFTWARE.                                                   *
25 *                                                                             *
26 *                                                                             *
27 ******************************************************************************/
28 
29 #include <fcntl.h>
30 
31 #include "sys/alt_irq.h"
32 #include "sys/ioctl.h"
33 #include "sys/alt_errno.h"
34 
35 #include "altera_avalon_uart.h"
36 #include "altera_avalon_uart_regs.h"
37 
38 #if defined(ALT_USE_SMALL_DRIVERS) || defined(ALTERA_AVALON_UART_SMALL)
39 
40 /* ----------------------------------------------------------- */
41 /* ----------------------- SMALL DRIVER ---------------------- */
42 /* ----------------------------------------------------------- */
43 
44 /*
45  * altera_avalon_uart_read() is called by the system read() function in order to
46  * read a block of data from the UART. "len" is the maximum length of the data
47  * to read, and "ptr" indicates the destination address. "fd" is the file
48  * descriptor for the device to be read from.
49  *
50  * Permission checks are made before the call to altera_avalon_uart_read(), so
51  * we know that the file descriptor has been opened with the correct permissions
52  * for this operation.
53  *
54  * The return value is the number of bytes actually read.
55  *
56  * This implementation polls the device waiting for characters. At most it can
57  * only return one character, regardless of how many are requested. If the
58  * device is being accessed in non-blocking mode then it is possible for this
59  * function to return without reading any characters. In this case errno is
60  * set to EWOULDBLOCK.
61  */
62 
63 int
altera_avalon_uart_read(altera_avalon_uart_state * sp,char * ptr,int len,int flags)64 altera_avalon_uart_read(altera_avalon_uart_state* sp, char* ptr, int len,
65   int flags)
66 {
67   int block;
68   unsigned int status;
69 
70   block = !(flags & O_NONBLOCK);
71 
72   do
73   {
74     status = IORD_ALTERA_AVALON_UART_STATUS(sp->base);
75 
76     /* clear any error flags */
77 
78     IOWR_ALTERA_AVALON_UART_STATUS(sp->base, 0);
79 
80     if (status & ALTERA_AVALON_UART_CONTROL_RRDY_MSK)
81     {
82       ptr[0] = IORD_ALTERA_AVALON_UART_RXDATA(sp->base);
83 
84       if (!(status & (ALTERA_AVALON_UART_STATUS_PE_MSK |
85       ALTERA_AVALON_UART_STATUS_FE_MSK)))
86       {
87         return 1;
88       }
89     }
90   }
91   while (block);
92 
93   ALT_ERRNO = EWOULDBLOCK;
94 
95   return 0;
96 }
97 
98 #else
99 
100 /* ----------------------------------------------------------- */
101 /* ----------------------- FAST DRIVER ----------------------- */
102 /* ----------------------------------------------------------- */
103 
104 /*
105  * altera_avalon_uart_read() is called by the system read() function in order to
106  * read a block of data from the UART. "len" is the maximum length of the data
107  * to read, and "ptr" indicates the destination address. "sp" is the state
108  * pointer for the device to be read from.
109  *
110  * Permission checks are made before the call to altera_avalon_uart_read(), so
111  * we know that the file descriptor has been opened with the correct permissions
112  * for this operation.
113  *
114  * The return value is the number of bytes actually read.
115  *
116  * This function does not communicate with the device directly. Instead data is
117  * transfered from a circular buffer. The interrupt handler is then responsible
118  * for copying data from the device into this buffer.
119  */
120 
121 int
altera_avalon_uart_read(altera_avalon_uart_state * sp,char * ptr,int len,int flags)122 altera_avalon_uart_read(altera_avalon_uart_state* sp, char* ptr, int len,
123   int flags)
124 {
125   alt_irq_context context;
126   int             block;
127   alt_u8          read_would_block = 0;
128   int             count = 0;
129 
130   /*
131    * Construct a flag to indicate whether the device is being accessed in
132    * blocking or non-blocking mode.
133    */
134 
135   block = !(flags & O_NONBLOCK);
136 
137   /*
138    * When running in a multi threaded environment, obtain the "read_lock"
139    * semaphore. This ensures that reading from the device is thread-safe.
140    */
141 
142   ALT_SEM_PEND (sp->read_lock, 0);
143 
144   /*
145    * Loop, copying data from the circular buffer to the destination address
146    * supplied in "ptr". This loop is terminated when the required number of
147    * bytes have been read. If the circular buffer is empty, and no data has
148    * been read, then the loop will block (when in blocking mode).
149    *
150    * If the circular buffer is empty, and some data has already been
151    * transferred, or the device is being accessed in non-blocking mode, then
152    * the loop terminates without necessarily reading all the requested data.
153    */
154 
155   do
156   {
157     /*
158      * Read the required amount of data, until the circular buffer runs
159      * empty
160      */
161 
162     while ((count < len) && (sp->rx_start != sp->rx_end))
163     {
164       count++;
165       *ptr++ = sp->rx_buf[sp->rx_start];
166 
167       sp->rx_start = (sp->rx_start+1) & ALT_AVALON_UART_BUF_MSK;
168     }
169 
170     /*
171      * If no data has been transferred, the circular buffer is empty, and
172      * this is not a non-blocking access, block waiting for data to arrive.
173      */
174 
175     if (!count && (sp->rx_start == sp->rx_end))
176     {
177       if (!block)
178       {
179         /* Set errno to indicate the reason we're not returning any data */
180 
181         ALT_ERRNO = EWOULDBLOCK;
182         read_would_block = 1;
183         break;
184       }
185       else
186       {
187        /* Block waiting for some data to arrive */
188 
189        /* First, ensure read interrupts are enabled to avoid deadlock */
190 
191        context = alt_irq_disable_all ();
192        sp->ctrl |= ALTERA_AVALON_UART_CONTROL_RRDY_MSK;
193        IOWR_ALTERA_AVALON_UART_CONTROL(sp->base, sp->ctrl);
194        alt_irq_enable_all (context);
195 
196        /*
197         * When running in a multi-threaded mode, we pend on the read event
198         * flag set in the interrupt service routine. This avoids wasting CPU
199         * cycles waiting in this thread, when we could be doing something more
200         * profitable elsewhere.
201         */
202 
203        ALT_FLAG_PEND (sp->events,
204                       ALT_UART_READ_RDY,
205                       OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
206                       0);
207       }
208     }
209   }
210   while (!count && len);
211 
212   /*
213    * Now that access to the circular buffer is complete, release the read
214    * semaphore so that other threads can access the buffer.
215    */
216 
217   ALT_SEM_POST (sp->read_lock);
218 
219   /*
220    * Ensure that interrupts are enabled, so that the circular buffer can
221    * re-fill.
222    */
223 
224   context = alt_irq_disable_all ();
225   sp->ctrl |= ALTERA_AVALON_UART_CONTROL_RRDY_MSK;
226   IOWR_ALTERA_AVALON_UART_CONTROL(sp->base, sp->ctrl);
227   alt_irq_enable_all (context);
228 
229   /* Return the number of bytes read */
230   if(read_would_block) {
231     return -EWOULDBLOCK;
232   }
233   else {
234     return count;
235   }
236 }
237 
238 #endif /* fast driver */
239