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