altos: Replace C code attiny async output with inline asm
[fw/altos] / src / attiny / ao_spi_attiny.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19
20 /*
21  * ATtiny USI as a SPI interface
22  */
23
24 /*
25  * Transfer one byte in/out of the USI interface
26  */
27
28 /* Three wire mode */
29 #define SPI_USICR_WM    ((0 << USIWM1) | (1 << USIWM0)) 
30 #define SPI_USICR_CS    ((1 << USICS1) | (0 << USICS0) | (1 << USICLK))
31
32 #define SPI_USICR_FAST_2 ((1 << USIWM0) | (1 << USITC))
33 #define SPI_USICR_FAST_1 ((1 << USIWM0) | (1 << USITC) | (1 << USICLK))
34
35
36 static uint8_t
37 ao_spi_transfer(uint8_t i)
38 {
39         /* Load data register */
40         USIDR = i;
41
42 #if 1
43         USISR = (1 << USIOIF);
44         do {
45                 USICR = SPI_USICR_WM | SPI_USICR_CS | (1 << USITC);
46         } while ((USISR & (1 << USIOIF)) == 0);
47 #else
48         /* 8 clocks */
49         USICR = SPI_USICR_FAST_1;
50         USICR = SPI_USICR_FAST_2;
51
52         USICR = SPI_USICR_FAST_1;
53         USICR = SPI_USICR_FAST_2;
54
55         USICR = SPI_USICR_FAST_1;
56         USICR = SPI_USICR_FAST_2;
57
58         USICR = SPI_USICR_FAST_1;
59         USICR = SPI_USICR_FAST_2;
60
61         USICR = SPI_USICR_FAST_1;
62         USICR = SPI_USICR_FAST_2;
63
64         USICR = SPI_USICR_FAST_1;
65         USICR = SPI_USICR_FAST_2;
66
67         USICR = SPI_USICR_FAST_1;
68         USICR = SPI_USICR_FAST_2;
69
70         USICR = SPI_USICR_FAST_1;
71         USICR = SPI_USICR_FAST_2;
72 #endif
73
74         /* Pull data from the port */
75         return USIDR;
76 }
77
78 /* Send bytes over SPI.
79  *
80  * This just polls; the SPI is set to go as fast as possible,
81  * so using interrupts would take way too long
82  */
83 void
84 ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant
85 {
86         uint8_t *d = block;
87
88         while (len--)
89                 ao_spi_transfer (*d++);
90 }
91
92 /* Receive bytes over SPI.
93  *
94  * Poll, sending zeros and reading data back
95  */
96 void
97 ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant
98 {
99         uint8_t *d = block;
100
101         while (len--)
102                 *d++ = ao_spi_transfer (0xff);
103 }
104
105 /*
106  * Initialize USI
107  *
108  * Chip select is the responsibility of the caller
109  */
110
111 void
112 ao_spi_init(void)
113 {
114 #if 1
115         USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USICLK);
116 #else
117         USICR = SPI_USICR_FAST_2;
118 #endif
119         SPI_DIR &= ~(1 << DDB0);        /* DI */
120         SPI_DIR |= (1 << DDB1);         /* DO */
121         SPI_DIR |= (1 << DDB2);         /* SCLK */
122 }