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