doc: Clarify timer documentation.
[bertos.git] / bertos / cpu / dsp56k / drv / ser_dsp56k.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \version $Id$
34  *
35  * \author Stefano Fedrigo <aleph@develer.com>
36  * \author Giovanni Bajo <rasky@develer.com>
37  *
38  * \brief DSP5680x CPU specific serial I/O driver
39  */
40
41
42 #include <drv/ser.h>
43 #include <drv/ser_p.h>
44 #include <drv/irq.h>
45 #include <cfg/debug.h>
46 #include <hw.h>
47 #include <DSP56F807.h>
48
49 // GPIO E is shared with SPI (in DSP56807). Pins 0&1 are TXD0 and RXD0. To use
50 //  the serial, we need to disable the GPIO functions on them.
51 #define REG_GPIO_SERIAL_0       REG_GPIO_E
52 #define REG_GPIO_SERIAL_MASK_0  0x03
53
54 #define REG_GPIO_SERIAL_1       REG_GPIO_D
55 #define REG_GPIO_SERIAL_MASK_1  0xC0
56
57
58 // Check flag consistency
59 #if (SERRF_PARITYERROR != REG_SCI_SR_PF) || \
60         (SERRF_RXSROVERRUN != REG_SCI_SR_OR) || \
61         (SERRF_FRAMEERROR  != REG_SCI_SR_FE) || \
62         (SERRF_NOISEERROR  != REG_SCI_SR_NF)
63         #error error flags do not match with register bits
64 #endif
65
66 static unsigned char ser0_fifo_rx[CONFIG_SER0_FIFOSIZE_RX];
67 static unsigned char ser0_fifo_tx[CONFIG_SER0_FIFOSIZE_TX];
68 static unsigned char ser1_fifo_rx[CONFIG_SER1_FIFOSIZE_RX];
69 static unsigned char ser1_fifo_tx[CONFIG_SER1_FIFOSIZE_TX];
70
71 #if CONFIG_SER_MULTI
72         #include <kern/sem.h>
73
74         #define MAX_MULTI_GROUPS     1
75
76         struct Semaphore multi_sems[MAX_MULTI_GROUPS];
77 #endif
78
79
80 struct SCI
81 {
82         struct SerialHardware hw;
83         struct Serial* serial;
84         volatile struct REG_SCI_STRUCT* regs;
85         IRQ_VECTOR irq_tx;
86         IRQ_VECTOR irq_rx;
87         int num_group;
88         int id;
89 };
90
91 static inline void enable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
92 {
93         regs->CR |= REG_SCI_CR_TEIE | REG_SCI_CR_TIIE;
94 }
95
96 static inline void enable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
97 {
98         regs->CR |= REG_SCI_CR_RIE;
99 }
100
101 static inline void disable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
102 {
103         regs->CR &= ~(REG_SCI_CR_TEIE | REG_SCI_CR_TIIE);
104 }
105
106 static inline void disable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
107 {
108         regs->CR &= ~(REG_SCI_CR_RIE | REG_SCI_CR_REIE);
109 }
110
111 static inline void disable_tx_irq(struct SerialHardware* _hw)
112 {
113         struct SCI* hw = (struct SCI*)_hw;
114
115         disable_tx_irq_bare(hw->regs);
116 }
117
118 static inline void disable_rx_irq(struct SerialHardware* _hw)
119 {
120         struct SCI* hw = (struct SCI*)_hw;
121
122         disable_rx_irq_bare(hw->regs);
123 }
124
125 static inline void enable_tx_irq(struct SerialHardware* _hw)
126 {
127         struct SCI* hw = (struct SCI*)_hw;
128
129         enable_tx_irq_bare(hw->regs);
130 }
131
132 static inline void enable_rx_irq(struct SerialHardware* _hw)
133 {
134         struct SCI* hw = (struct SCI*)_hw;
135
136         enable_rx_irq_bare(hw->regs);
137 }
138
139 static inline bool tx_irq_enabled(struct SerialHardware* _hw)
140 {
141         struct SCI* hw = (struct SCI*)_hw;
142
143         return (hw->regs->CR & REG_SCI_CR_TEIE);
144 }
145
146 static void tx_isr(const struct SCI *hw)
147 {
148 #pragma interrupt warn
149         volatile struct REG_SCI_STRUCT* regs = hw->regs;
150
151         if (fifo_isempty(&hw->serial->txfifo))
152                 disable_tx_irq_bare(regs);
153         else
154         {
155                 // Clear transmitter flags before sending data
156                 (void)regs->SR;
157                 regs->DR = fifo_pop(&hw->serial->txfifo);
158         }
159 }
160
161 static void rx_isr(const struct SCI *hw)
162 {
163 #pragma interrupt warn
164         volatile struct REG_SCI_STRUCT* regs = hw->regs;
165
166         // Propagate errors
167         hw->serial->status |= regs->SR & (SERRF_PARITYERROR |
168                                           SERRF_RXSROVERRUN |
169                                           SERRF_FRAMEERROR |
170                                           SERRF_NOISEERROR);
171
172         /*
173          * Serial IRQ can happen for two reason: data ready (RDRF) or overrun (OR)
174          * If the data is ready, we need to fetch it from the data register or
175          * the interrupt will retrigger immediatly. In case of overrun, instead,
176          * the value of the data register is meaningless.
177          */
178         if (regs->SR & REG_SCI_SR_RDRF)
179         {
180                 unsigned char data = regs->DR;
181
182                 if (fifo_isfull(&hw->serial->rxfifo))
183                         hw->serial->status |= SERRF_RXFIFOOVERRUN;
184                 else
185                         fifo_push(&hw->serial->rxfifo, data);
186         }
187
188         // Writing anything to the status register clear the error bits.
189         regs->SR = 0;
190 }
191
192 static void init(struct SerialHardware* _hw, struct Serial* ser)
193 {
194         struct SCI* hw = (struct SCI*)_hw;
195         volatile struct REG_SCI_STRUCT* regs = hw->regs;
196
197         // Clear status register (IRQ/status flags)
198         (void)regs->SR;
199         regs->SR = 0;
200
201         // Clear data register
202         (void)regs->DR;
203
204         // Install the handlers and set priorities for both IRQs
205         irq_install(hw->irq_tx, (isr_t)tx_isr, hw);
206         irq_install(hw->irq_rx, (isr_t)rx_isr, hw);
207         irq_setpriority(hw->irq_tx, IRQ_PRIORITY_SCI_TX);
208         irq_setpriority(hw->irq_rx, IRQ_PRIORITY_SCI_RX);
209
210         // Activate the RX error interrupts, and RX/TX transmissions
211         regs->CR = REG_SCI_CR_TE | REG_SCI_CR_RE;
212         enable_rx_irq_bare(regs);
213
214         // Disable GPIO pins for TX and RX lines
215         // \todo this should be divided into serial 0 and 1
216         REG_GPIO_SERIAL_0->PER |= REG_GPIO_SERIAL_MASK_0;
217         REG_GPIO_SERIAL_1->PER |= REG_GPIO_SERIAL_MASK_1;
218
219         hw->serial = ser;
220 }
221
222 static void cleanup(struct SerialHardware* _hw)
223 {
224         struct SCI* hw = (struct SCI*)_hw;
225
226         // Uninstall the ISRs
227         disable_rx_irq(_hw);
228         disable_tx_irq(_hw);
229         irq_uninstall(hw->irq_tx);
230         irq_uninstall(hw->irq_rx);
231 }
232
233 static void setbaudrate(struct SerialHardware* _hw, unsigned long rate)
234 {
235         struct SCI* hw = (struct SCI*)_hw;
236
237         // SCI has an internal 16x divider on the input clock, which comes
238         //  from the IPbus (see the scheme in user manual, 12.7.3). We apply
239         //  it to calculate the period to store in the register.
240         hw->regs->BR = (IPBUS_FREQ + rate * 8ul) / (rate * 16ul);
241 }
242
243 static void setparity(struct SerialHardware* _hw, int parity)
244 {
245         // ???
246         ASSERT(0);
247 }
248
249
250 #if CONFIG_SER_MULTI
251
252 static void multi_init(void)
253 {
254         static bool flag = false;
255         int i;
256
257         if (flag)
258                 return;
259
260         for (i = 0; i < MAX_MULTI_GROUPS; ++i)
261                 sem_init(&multi_sems[i]);
262         flag = true;
263 }
264
265 static void init_lock(struct SerialHardware* _hw, struct Serial *ser)
266 {
267         struct SCI* hw = (struct SCI*)_hw;
268
269         // Initialize the multi engine (if needed)
270         multi_init();
271
272         // Acquire the lock of the semaphore for this group
273         ASSERT(hw->num_group >= 0);
274         ASSERT(hw->num_group < MAX_MULTI_GROUPS);
275         sem_obtain(&multi_sems[hw->num_group]);
276
277         // Do a hardware switch to the given serial
278         ser_hw_switch(hw->num_group, hw->id);
279
280         init(_hw, ser);
281 }
282
283 static void cleanup_unlock(struct SerialHardware* _hw)
284 {
285         struct SCI* hw = (struct SCI*)_hw;
286
287         cleanup(_hw);
288
289         sem_release(&multi_sems[hw->num_group]);
290 }
291
292 #endif /* CONFIG_SER_MULTI */
293
294
295 static const struct SerialHardwareVT SCI_VT =
296 {
297         .init = init,
298         .cleanup = cleanup,
299         .setBaudrate = setbaudrate,
300         .setParity = setparity,
301         .txStart = enable_tx_irq,
302         .txSending = tx_irq_enabled,
303 };
304
305 #if CONFIG_SER_MULTI
306 static const struct SerialHardwareVT SCI_MULTI_VT =
307 {
308         .init = init_lock,
309         .cleanup = cleanup_unlock,
310         .setBaudrate = setbaudrate,
311         .setParity = setparity,
312         .txStart = enable_tx_irq,
313         .txSending = tx_irq_enabled,
314 };
315 #endif /* CONFIG_SER_MULTI */
316
317 #define SCI_DESC_NORMAL(hwch) \
318         { \
319                 .hw = \
320                 { \
321                         .table = &SCI_VT, \
322                         .rxbuffer = ser ## hwch ## _fifo_rx, \
323                         .txbuffer = ser ## hwch ## _fifo_tx, \
324                         .rxbuffer_size = countof(ser ## hwch ## _fifo_rx), \
325                         .txbuffer_size = countof(ser ## hwch ## _fifo_tx), \
326                 }, \
327                 .regs = &REG_SCI[hwch], \
328                 .irq_rx = IRQ_SCI ## hwch ## _RECEIVER_FULL, \
329                 .irq_tx = IRQ_SCI ## hwch ## _TRANSMITTER_READY, \
330                 .num_group = -1, \
331                 .id = -1, \
332         } \
333         /**/
334
335 #if CONFIG_SER_MULTI
336 #define SCI_DESC_MULTI(hwch, group_, id_) \
337         { \
338                 .hw = \
339                 { \
340                         .table = &SCI_MULTI_VT, \
341                         .rxbuffer = ser ## hwch ## _fifo_rx, \
342                         .txbuffer = ser ## hwch ## _fifo_tx, \
343                         .rxbuffer_size = countof(ser ## hwch ## _fifo_rx), \
344                         .txbuffer_size = countof(ser ## hwch ## _fifo_tx), \
345                 }, \
346                 .regs = &REG_SCI[hwch], \
347                 .irq_rx = IRQ_SCI ## hwch ## _RECEIVER_FULL, \
348                 .irq_tx = IRQ_SCI ## hwch ## _TRANSMITTER_READY, \
349                 .num_group = group_, \
350                 .id = id_, \
351         } \
352         /**/
353 #endif /* CONFIG_SER_MULTI */
354
355 // \todo Move this into hw.h, with a little preprocessor magic
356 static struct SCI SCIDescs[] =
357 {
358         SCI_DESC_NORMAL(0),
359         SCI_DESC_MULTI(1, 0, 0),
360         SCI_DESC_MULTI(1, 0, 1),
361 };
362
363 struct SerialHardware* ser_hw_getdesc(int unit)
364 {
365         ASSERT(unit < countof(SCIDescs));
366         return &SCIDescs[unit].hw;
367 }