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_dev.h"
32 #include "sys/alt_irq.h"
33 #include "sys/ioctl.h"
34 #include "sys/alt_errno.h"
35
36 #include "altera_avalon_uart_regs.h"
37 #include "altera_avalon_uart.h"
38
39 #if defined(ALT_USE_SMALL_DRIVERS) || defined(ALTERA_AVALON_UART_SMALL)
40
41 /* ----------------------------------------------------------- */
42 /* ------------------------ SMALL DRIVER --------------------- */
43 /* ----------------------------------------------------------- */
44
45 /*
46 * altera_avalon_uart_write() is called by the system write() function in
47 * order to write a block of data to the UART.
48 * "len" is the length of the data to write,
49 * and "ptr" indicates the source address. "fd" is the file descriptor for the
50 * device to be read from.
51 *
52 * Permission checks are made before the call to altera_avalon_uart_write(), so
53 * we know that the file descriptor has been opened with the correct permissions
54 * for this operation.
55 *
56 * The return value is the number of bytes actually written.
57 *
58 * This function will block on the devices transmit register, until all
59 * characters have been transmitted. This is unless the device is being
60 * accessed in non-blocking mode. In this case this function will return as
61 * soon as the device reports that it is not ready to transmit.
62 *
63 * Since this is the small footprint version of the UART driver, the value of
64 * CTS is ignored.
65 */
66
67 int
altera_avalon_uart_write(altera_avalon_uart_state * sp,const char * ptr,int len,int flags)68 altera_avalon_uart_write(altera_avalon_uart_state* sp, const char* ptr, int len,
69 int flags)
70 {
71 int block;
72 unsigned int status;
73 int count;
74
75 block = !(flags & O_NONBLOCK);
76 count = len;
77
78 do
79 {
80 status = IORD_ALTERA_AVALON_UART_STATUS(sp->base);
81
82 if (status & ALTERA_AVALON_UART_STATUS_TRDY_MSK)
83 {
84 IOWR_ALTERA_AVALON_UART_TXDATA(sp->base, *ptr++);
85 count--;
86 }
87 }
88 while (block && count);
89
90 if (count)
91 {
92 ALT_ERRNO = EWOULDBLOCK;
93 }
94
95 return (len - count);
96 }
97
98 #else /* Using the "fast" version of the driver */
99
100 /* ----------------------------------------------------------- */
101 /* ------------------------- FAST DRIVER --------------------- */
102 /* ----------------------------------------------------------- */
103
104 /*
105 * altera_avalon_uart_write() is called by the system write() function in order
106 * to write a block of data to the UART. "len" is the length of the data to
107 * write, and "ptr" indicates the source address. "sp" is the state pointer
108 * for the device to be written to.
109 *
110 * Permission checks are made before the call to altera_avalon_uart_write(), 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 written.
115 *
116 * This function does not communicate with the device directly. Instead data is
117 * transfered to a circular buffer. The interrupt handler is then responsible
118 * for copying data from this buffer into the device.
119 */
120
121 int
altera_avalon_uart_write(altera_avalon_uart_state * sp,const char * ptr,int len,int flags)122 altera_avalon_uart_write(altera_avalon_uart_state* sp, const char* ptr, int len,
123 int flags)
124 {
125 alt_irq_context context;
126 int no_block;
127 alt_u32 next;
128 int count = len;
129
130 /*
131 * Construct a flag to indicate whether the device is being accessed in
132 * blocking or non-blocking mode.
133 */
134
135 no_block = (flags & O_NONBLOCK);
136
137 /*
138 * When running in a multi threaded environment, obtain the "write_lock"
139 * semaphore. This ensures that writing to the device is thread-safe.
140 */
141
142 ALT_SEM_PEND (sp->write_lock, 0);
143
144 /*
145 * Loop transferring data from the input buffer to the transmit circular
146 * buffer. The loop is terminated once all the data has been transferred,
147 * or, (if in non-blocking mode) the buffer becomes full.
148 */
149
150 while (count)
151 {
152 /* Determine the next slot in the buffer to access */
153
154 next = (sp->tx_end + 1) & ALT_AVALON_UART_BUF_MSK;
155
156 /* block waiting for space if necessary */
157
158 if (next == sp->tx_start)
159 {
160 if (no_block)
161 {
162 /* Set errno to indicate why this function returned early */
163
164 ALT_ERRNO = EWOULDBLOCK;
165 break;
166 }
167 else
168 {
169 /* Block waiting for space in the circular buffer */
170
171 /* First, ensure transmit interrupts are enabled to avoid deadlock */
172
173 context = alt_irq_disable_all ();
174 sp->ctrl |= (ALTERA_AVALON_UART_CONTROL_TRDY_MSK |
175 ALTERA_AVALON_UART_CONTROL_DCTS_MSK);
176 IOWR_ALTERA_AVALON_UART_CONTROL(sp->base, sp->ctrl);
177 alt_irq_enable_all (context);
178
179 /* wait for space to come free */
180
181 do
182 {
183 /*
184 * When running in a multi-threaded mode, we pend on the write event
185 * flag set in the interrupt service routine. This avoids wasting CPU
186 * cycles waiting in this thread, when we could be doing something
187 * more profitable elsewhere.
188 */
189
190 ALT_FLAG_PEND (sp->events,
191 ALT_UART_WRITE_RDY,
192 OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
193 0);
194 }
195 while ((next == sp->tx_start));
196 }
197 }
198
199 count--;
200
201 /* Add the next character to the transmit buffer */
202
203 sp->tx_buf[sp->tx_end] = *ptr++;
204 sp->tx_end = next;
205 }
206
207 /*
208 * Now that access to the circular buffer is complete, release the write
209 * semaphore so that other threads can access the buffer.
210 */
211
212 ALT_SEM_POST (sp->write_lock);
213
214 /*
215 * Ensure that interrupts are enabled, so that the circular buffer can
216 * drain.
217 */
218
219 context = alt_irq_disable_all ();
220 sp->ctrl |= ALTERA_AVALON_UART_CONTROL_TRDY_MSK |
221 ALTERA_AVALON_UART_CONTROL_DCTS_MSK;
222 IOWR_ALTERA_AVALON_UART_CONTROL(sp->base, sp->ctrl);
223 alt_irq_enable_all (context);
224
225 /* return the number of bytes written */
226
227 return (len - count);
228 }
229
230 #endif /* fast driver */
231