Add firmware for the ATUSB IEEE 802.15.4 USB Adapter
[linux-libre-firmware.git] / atusb / board_app.c
1 /*
2  * fw/board_app.c - Board-specific functions (for the application)
3  *
4  * Written 2011, 2013 by Werner Almesberger
5  * Copyright 2011, 2013 Werner Almesberger
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  */
12
13
14 #include <stddef.h>
15 #include <stdbool.h>
16 #include <stdint.h>
17
18 #include <avr/io.h>
19 #include <avr/interrupt.h>
20
21 #define F_CPU   8000000UL
22 #include <util/delay.h>
23
24 #include "usb.h"
25 #include "at86rf230.h"
26 #include "spi.h"
27 #include "mac.h"
28 #include "board.h"
29
30
31 static volatile uint32_t timer_h = 0;   /* 2^(16+32) / 8 MHz = ~1.1 years */
32
33
34 void reset_cpu(void)
35 {
36         WDTCSR = 1 << WDE;
37 }
38
39
40 uint8_t read_irq(void)
41 {
42         return PIN(IRQ_RF);
43 }
44
45
46 void slp_tr(void)
47 {
48         SET(SLP_TR);
49         CLR(SLP_TR);
50 }
51
52
53 ISR(TIMER1_OVF_vect)
54 {
55         timer_h++;
56 }
57
58
59 uint64_t timer_read(void)
60 {
61         uint32_t high;
62         uint8_t low, mid;
63
64         do {
65                 if (TIFR1 & (1 << TOV1)) {
66                         TIFR1 = 1 << TOV1;
67                         timer_h++;
68                 }
69                 high = timer_h;
70                 low = TCNT1L;
71                 mid = TCNT1H;
72         }
73         while (TIFR1 & (1 << TOV1));
74
75         /*
76          * We need all these casts because the intermediate results are handled
77          * as if they were signed and thus get sign-expanded. Sounds wrong-ish.
78          */
79         return (uint64_t) high << 16 | (uint64_t) mid << 8 | (uint64_t) low;
80 }
81
82
83 void timer_init(void)
84 {
85         /* configure timer 1 as a free-running CLK counter */
86
87         TCCR1A = 0;
88         TCCR1B = 1 << CS10;
89
90         /* enable timer overflow interrupt */
91
92         TIMSK1 = 1 << TOIE1;
93 }
94
95
96 bool gpio(uint8_t port, uint8_t data, uint8_t dir, uint8_t mask, uint8_t *res)
97 {
98         EIMSK = 0; /* recover INT_RF to ATUSB_GPIO_CLEANUP or an MCU reset */
99
100         switch (port) {
101         case 1:
102                 DDRB = (DDRB & ~mask) | dir;
103                 PORTB = (PORTB & ~mask) | data;
104                 break;
105         case 2:
106                 DDRC = (DDRC & ~mask) | dir;
107                 PORTC = (PORTC & ~mask) | data;
108                 break;
109         case 3:
110                 DDRD = (DDRD & ~mask) | dir;
111                 PORTD = (PORTD & ~mask) | data;
112                 break;
113         default:
114                 return 0;
115         }
116
117         /* disable the UART so that we can meddle with these pins as well. */
118         spi_off();
119         _delay_ms(1);
120
121         switch (port) {
122         case 1:
123                 res[0] = PINB;
124                 res[1] = PORTB;
125                 res[2] = DDRB;
126                 break;
127         case 2:
128                 res[0] = PINC;
129                 res[1] = PORTC;
130                 res[2] = DDRC;
131                 break;
132         case 3:
133                 res[0] = PIND;
134                 res[1] = PORTD;
135                 res[2] = DDRD;
136                 break;
137         }
138
139         return 1;
140 }
141
142
143 void gpio_cleanup(void)
144 {
145         EIMSK = 1 << 0;
146 }
147
148
149 static void done(void *user)
150 {
151         led(0);
152 }
153
154
155 uint8_t irq_serial;
156
157 #if defined(ATUSB) || defined(HULUSB)
158 ISR(INT0_vect)
159 #endif
160 #ifdef RZUSB
161 ISR(TIMER1_CAPT_vect)
162 #endif
163 {
164         if (mac_irq) {
165                 if (mac_irq())
166                         return;
167         }
168         if (eps[1].state == EP_IDLE) {
169                 led(1);
170                 irq_serial = (irq_serial+1) | 0x80;
171                 usb_send(&eps[1], &irq_serial, 1, done, NULL);
172         }
173 }