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