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