Add dual-license information.
[bertos.git] / drv / ser_avr.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
5  * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
6  * This file is part of DevLib - See devlib/README for information.
7  * -->
8  *
9  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  *
13  * \brief AVR UART and SPI I/O driver
14  */
15
16 /*
17  * $Log$
18  * Revision 1.4  2004/06/03 11:27:09  bernie
19  * Add dual-license information.
20  *
21  * Revision 1.3  2004/06/02 21:35:24  aleph
22  * Serial enhancements: interruptible receive handler and 8 bit serial status for AVR; remove volatile attribute to FIFOBuffer, useless for new fifobuf routens
23  *
24  * Revision 1.2  2004/05/23 18:21:53  bernie
25  * Trim CVS logs and cleanup header info.
26  *
27  */
28
29 #include "ser.h"
30 #include "ser_p.h"
31 #include "kdebug.h"
32 #include "config.h"
33 #include "hw.h"
34 #include <mware/fifobuf.h>
35
36 extern struct Serial ser_handles[SER_CNT];
37
38 struct AvrSerial
39 {
40         struct SerialHardware hw;
41         struct Serial* serial;
42 };
43
44
45 /* Hardware handshake */
46 #define RTS_ON
47 #define RTS_OFF
48 #define IS_CTS_ON   true
49 #define IS_CTS_OFF  false
50
51
52 /* SPI port and pin configuration */
53 #define SPI_PORT      PORTB
54 #define SPI_DDR       DDRB
55 #define SPI_SCK_BIT   PORTB1
56 #define SPI_MOSI_BIT  PORTB2
57 #define SPI_MISO_BIT  PORTB3
58
59
60 #ifdef __AVR_ATmega103__
61         /* Macro for ATmega103 compatibility */
62         #define UCSR0B UCR
63         #define UDR0   UDR
64         #define UCSR0A USR
65         #define UBRR0L UBRR
66 #else
67         #define UCR  UCSR0B
68         #define UDR  UDR0
69         #define USR  UCSR0A
70 #endif
71
72
73 /* Transmission fill byte */
74 #define SER_FILL_BYTE 0xAA
75
76
77 static void uart0_enabletxirq(UNUSED(struct SerialHardware *ctx))
78 {
79 #ifdef CONFIG_SER_TXFILL
80         UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN) | BV(UCSZ2);
81 #else
82         UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
83 #endif
84 }
85
86 static void uart0_init(struct SerialHardware *_hw, struct Serial *ser)
87 {
88         struct AvrSerial *hw = (struct AvrSerial *)_hw;
89         hw->serial = ser;
90
91         /* Set TX port as input with pull-up enabled to avoid
92          * noise on the remote RX when TX is disabled */
93         cpuflags_t flags;
94         DISABLE_IRQSAVE(flags);
95         DDRE &= ~BV(PORTE1);
96         PORTE |= BV(PORTE1);
97         ENABLE_IRQRESTORE(flags);
98
99         /* TODO: explain why TX is disabled whenever possible */
100 #ifdef CONFIG_SER_TXFILL
101         /*!
102          * Set multiprocessor mode and 9 bit data frame.
103          * The receiver keep MPCM bit always on. When useful data
104          * is trasmitted the ninth bit is set. Receiver consider the
105          * frame as address info and receive it.
106          * When useless fill bytes are sent the ninth bit is cleared
107          * and the receiver will ignore them, avoiding useless triggering
108          * of RXC interrupt.
109          */
110         UCSR0A = BV(MPCM);
111         UCSR0B = BV(RXCIE) | BV(RXEN) | BV(UCSZ2);
112 #else
113         UCSR0B = BV(RXCIE) | BV(RXEN);
114 #endif
115
116         RTS_ON;
117 }
118
119 static void uart0_cleanup(UNUSED(struct SerialHardware *ctx))
120 {
121         UCSR0B = 0;
122 }
123
124 static void uart0_setbaudrate(UNUSED(struct SerialHardware *ctx), unsigned long rate)
125 {
126         // Compute baud-rate period
127         uint16_t period = (((CLOCK_FREQ / 16UL) + (rate / 2)) / rate) - 1;
128
129 #ifndef __AVR_ATmega103__
130         UBRR0H = (period) >> 8;
131 #endif
132         UBRR0L = (period);
133 }
134
135
136 #ifndef __AVR_ATmega103__
137
138 static void uart1_enabletxirq(UNUSED(struct SerialHardware *ctx))
139 {
140         UCSR1B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
141 }
142
143 static void uart1_init(struct SerialHardware *_hw, struct Serial *ser)
144 {
145         struct AvrSerial *hw = (struct AvrSerial *)_hw;
146         hw->serial = ser;
147
148         /* Set TX port as input with pull-up enabled to avoid
149          * noise on the remote RX when TX is disabled */
150         cpuflags_t flags;
151         DISABLE_IRQSAVE(flags);
152         DDRD &= ~BV(PORTD3);
153         PORTD |= BV(PORTD3);
154         ENABLE_IRQRESTORE(flags);
155
156         /* TODO: explain why TX is disabled whenever possible */
157         UCSR1B = BV(RXCIE) | BV(RXEN);
158
159         RTS_ON;
160 }
161
162 static void uart1_cleanup(UNUSED(struct SerialHardware *ctx))
163 {
164         UCSR1B = 0;
165 }
166
167 static void uart1_setbaudrate(UNUSED(struct SerialHardware *ctx), unsigned long rate)
168 {
169         // Compute baud-rate period
170         uint16_t period = (((CLOCK_FREQ / 16UL) + (rate / 2)) / rate) - 1;
171
172         UBRR1H = (period) >> 8;
173         UBRR1L = (period);
174 }
175
176 static void uart0_setparity(UNUSED(struct SerialHardware *ctx), int parity)
177 {
178         UCSR0C |= (parity) << UPM0;
179 }
180
181 static void uart1_setparity(UNUSED(struct SerialHardware *ctx), int parity)
182 {
183         UCSR1C |= (parity) << UPM0;
184 }
185
186 #endif /* !__AVR_ATmega103__ */
187
188
189 static void spi_init(struct SerialHardware *_hw, struct Serial *ser)
190 {
191         struct AvrSerial *hw = (struct AvrSerial *)_hw;
192         hw->serial = ser;
193
194         /* MOSI and SCK out, MISO in */
195         SPI_DDR |= BV(SPI_MOSI_BIT) | BV(SPI_SCK_BIT);
196         SPI_DDR &= ~BV(SPI_MISO_BIT);
197         /* Enable SPI, IRQ on, Master, CPU_CLOCK/16 */
198         SPCR = BV(SPE) | BV(SPIE) | BV(MSTR) | BV(SPR0);
199 }
200
201 static void spi_cleanup(UNUSED(struct SerialHardware *ctx))
202 {
203         SPCR = 0;
204         /* Set all pins as inputs */
205         SPI_DDR &= ~(BV(SPI_MISO_BIT) | BV(SPI_MOSI_BIT) | BV(SPI_SCK_BIT));
206 }
207
208
209
210 #if defined(CONFIG_SER_HW_HANDSHAKE)
211
212 //! This interrupt is triggered when the CTS line goes high
213 SIGNAL(SIG_CTS)
214 {
215         // Re-enable UDR empty interrupt and TX, then disable CTS interrupt
216         UCR = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
217         cbi(EIMSK, EIMSKB_CTS);
218 }
219
220 #endif // CONFIG_SER_HW_HANDSHAKE
221
222
223 /*!
224  * Serial 0 TX interrupt handler
225  */
226 #ifdef __AVR_ATmega103__
227 SIGNAL(SIG_UART_DATA)
228 #else
229 SIGNAL(SIG_UART0_DATA)
230 #endif
231 {
232         if (fifo_isempty(&ser_handles[SER_UART0].txfifo))
233         {
234 #ifdef CONFIG_SER_TXFILL
235                 /*
236                  * To avoid audio interference: always transmit useless char.
237                  * Send the byte with the ninth bit cleared, the receiver in MCPM mode
238                  * will ignore it.
239                  */
240                 UCSR0B &= ~BV(TXB8);
241                 UDR0 = SER_FILL_BYTE;
242 #else
243                 /* Disable UDR empty interrupt and transmitter */
244                 UCR = BV(RXCIE) | BV(RXEN);
245 #endif
246         }
247 #if defined(CONFIG_SER_HWHANDSHAKE)
248         else if (IS_CTS_OFF)
249         {
250                 // disable rx interrupt and tx, enable CTS interrupt
251                 UCR = BV(RXCIE) | BV(RXEN);
252                 sbi(EIFR, EIMSKB_CTS);
253                 sbi(EIMSK, EIMSKB_CTS);
254         }
255 #endif // CONFIG_SER_HWHANDSHAKE
256         else
257         {
258 #ifdef CONFIG_SER_TXFILL
259                 /* Send with ninth bit set. Receiver in MCPM mode will receive it */
260                 UCSR0B |= BV(TXB8);
261 #endif
262                 UDR = fifo_pop(&ser_handles[SER_UART0].txfifo);
263         }
264 }
265
266 /*!
267  * Serial 1 TX interrupt handler
268  */
269 #ifndef __AVR_ATmega103__
270 SIGNAL(SIG_UART1_DATA)
271 {
272         if (fifo_isempty(&ser_handles[SER_UART1].txfifo))
273         {
274                 /* Disable UDR empty interrupt and transmitter */
275                 UCSR1B = BV(RXCIE) | BV(RXEN);
276         }
277 #if defined(CONFIG_SER_HWHANDSHAKE)
278         else if (IS_CTS_OFF)
279         {
280                 // disable rx interrupt and tx, enable CTS interrupt
281                 UCSR1B = BV(RXCIE) | BV(RXEN);
282                 sbi(EIFR, EIMSKB_CTS);
283                 sbi(EIMSK, EIMSKB_CTS);
284         }
285 #endif // CONFIG_SER_HWHANDSHAKE
286         else
287                 UDR1 = fifo_pop(&ser_handles[SER_UART1].txfifo);
288 }
289 #endif /* !__AVR_ATmega103__ */
290
291
292 /*!
293  * Serial 0 RX complete interrupt handler.
294  *
295  * This handler is interruptible.
296  * Interrupt are reenabled as soon as recv complete interrupt is
297  * disabled. Using INTERRUPT() is troublesome when the serial
298  * is heavily loaded, because and interrupt could be retriggered
299  * when executing the handler prologue before RXCIE is disabled.
300  */
301 #ifdef __AVR_ATmega103__
302 SIGNAL(SIG_UART_RECV)
303 #else
304 SIGNAL(SIG_UART0_RECV)
305 #endif
306 {
307         /* Disable Recv complete IRQ */
308         UCR &= ~BV(RXCIE);
309         ENABLE_INTS;
310
311         /* Should be read before UDR */
312         ser_handles[SER_UART0].status |= USR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
313
314         /* To clear the RXC flag we must _always_ read the UDR even when we're
315          * not going to accept the incoming data, otherwise a new interrupt
316          * will occur once the handler terminates.
317          */
318         char c = UDR;
319
320         if (fifo_isfull(&ser_handles[SER_UART0].rxfifo))
321                 ser_handles[SER_UART0].status |= SERRF_RXFIFOOVERRUN;
322         else
323         {
324                 fifo_push(&ser_handles[SER_UART0].rxfifo, c);
325 #if defined(CONFIG_SER_HW_HANDSHAKE)
326                 if (fifo_isfull(&ser_handles[SER_UART0].rxfifo))
327                         RTS_OFF;
328 #endif
329         }
330         /* Reenable receive complete int */
331         UCR |= BV(RXCIE);
332 }
333
334 /*!
335  * Serial 1 RX complete interrupt handler.
336  *
337  * This handler is interruptible.
338  * Interrupt are reenabled as soon as recv complete interrupt is
339  * disabled. Using INTERRUPT() is troublesome when the serial
340  * is heavily loaded, because and interrupt could be retriggered
341  * when executing the handler prologue before RXCIE is disabled.
342  */
343 #ifndef __AVR_ATmega103__
344 SIGNAL(SIG_UART1_RECV)
345 {
346         /* Disable Recv complete IRQ */
347         UCSR0B &= ~BV(RXCIE);
348         ENABLE_INTS;
349
350         /* Should be read before UDR */
351         ser_handles[SER_UART1].status |= UCSR1A & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
352
353         /* To avoid an IRQ storm, we must _always_ read the UDR even when we're
354          * not going to accept the incoming data
355          */
356         char c = UDR1;
357
358         if (fifo_isfull(&ser_handles[SER_UART1].rxfifo))
359                 ser_handles[SER_UART1].status |= SERRF_RXFIFOOVERRUN;
360         else
361         {
362                 fifo_push(&ser_handles[SER_UART1].rxfifo, c);
363 #if defined(CONFIG_SER_HW_HANDSHAKE)
364                 if (fifo_isfull(&ser_handles[SER_UART1].rxfifo))
365                         RTS_OFF;
366 #endif
367         }
368         /* Reenable receive complete int */
369         UCSR0B |= BV(RXCIE);
370 }
371 #endif /* !__AVR_ATmega103__ */
372
373
374 /*
375  * SPI Flag: true if we are transmitting/receiving with the SPI.
376  *
377  * This kludge is necessary because the SPI sends and receives bytes
378  * at the same time and the SPI IRQ is unique for send/receive.
379  * The only way to start transmission is to write data in SPDR (this
380  * is done by ser_spi_starttx()). We do this *only* if a transfer is
381  * not already started.
382  */
383 static volatile bool spi_sending = false;
384
385 static void spi_starttx(UNUSED(struct SerialHardware *ctx))
386 {
387         cpuflags_t flags;
388
389         DISABLE_IRQSAVE(flags);
390
391         /* Send data only if the SPI is not already transmitting */
392         if (!spi_sending && !fifo_isempty(&ser_handles[SER_SPI].txfifo))
393         {
394                 SPDR = fifo_pop(&ser_handles[SER_SPI].txfifo);
395                 spi_sending = true;
396         }
397
398         ENABLE_IRQRESTORE(flags);
399 }
400
401 /*!
402  * SPI interrupt handler
403  */
404 SIGNAL(SIG_SPI)
405 {
406         /* Read incoming byte. */
407         if (!fifo_isfull(&ser_handles[SER_SPI].rxfifo))
408                 fifo_push(&ser_handles[SER_SPI].rxfifo, SPDR);
409         /*
410          * FIXME
411         else
412                 ser_handles[SER_SPI].status |= SERRF_RXFIFOOVERRUN;
413         */
414
415         /* Send */
416         if (!fifo_isempty(&ser_handles[SER_SPI].txfifo))
417                 SPDR = fifo_pop(&ser_handles[SER_SPI].txfifo);
418         else
419                 spi_sending = false;
420 }
421
422
423 /*
424
425 #pragma vector = UART_TXC_vect
426 __interrupt void UART_TXC_interrupt(void)
427 {
428   UCSRB &= ~TXCIE;
429   ReceiveMode();
430   UCSRB = RXCIE | RXEN | TXEN;  //Abilito l'Interrupt in ricezione e RX e TX
431 }
432 */
433
434
435 static const struct SerialHardwareVT UART0_VT =
436 {
437         .init = uart0_init,
438         .cleanup = uart0_cleanup,
439         .setbaudrate = uart0_setbaudrate,
440         .setparity = uart0_setparity,
441         .enabletxirq = uart0_enabletxirq,
442 };
443
444 static const struct SerialHardwareVT UART1_VT =
445 {
446         .init = uart1_init,
447         .cleanup = uart1_cleanup,
448         .setbaudrate = uart1_setbaudrate,
449         .setparity = uart1_setparity,
450         .enabletxirq = uart1_enabletxirq,
451 };
452
453 static const struct SerialHardwareVT SPI_VT =
454 {
455         .init = spi_init,
456         .cleanup = spi_cleanup,
457         .enabletxirq = spi_starttx,
458 };
459
460 static struct AvrSerial UARTDescs[SER_CNT] =
461 {
462         {
463                 .hw = { .table = &UART0_VT },
464         },
465
466         {
467                 .hw = { .table = &UART1_VT },
468         },
469
470         {
471                 .hw = { .table = &SPI_VT },
472         },
473 };
474
475 struct SerialHardware* ser_hw_getdesc(int unit)
476 {
477         ASSERT(unit < SER_CNT);
478         return &UARTDescs[unit].hw;
479 }