altos/cc1111: Document SPI bus pin options
[fw/altos] / src / cc1111 / ao_spi.c
1 /*
2  * Copyright © 2010 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 /* Shared mutex to protect SPI bus, must cover the entire
21  * operation, from CS low to CS high. This means that any SPI
22  * user must protect the SPI bus with this mutex
23  */
24 __xdata uint8_t ao_spi_mutex;
25 __xdata uint8_t ao_spi_dma_in_done;
26 __xdata uint8_t ao_spi_dma_out_done;
27
28 uint8_t ao_spi_dma_out_id;
29 uint8_t ao_spi_dma_in_id;
30
31 static __xdata uint8_t ao_spi_const;
32
33 /* Send bytes over SPI.
34  *
35  * This sets up two DMA engines, one writing the data and another reading
36  * bytes coming back.  We use the bytes coming back to tell when the transfer
37  * is complete, as the transmit register is double buffered and hence signals
38  * completion one byte before the transfer is actually complete
39  */
40 void
41 ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant
42 {
43 #if !AO_SPI_SLAVE
44         ao_dma_set_transfer(ao_spi_dma_in_id,
45                             &U0DBUFXADDR,
46                             &ao_spi_const,
47                             len,
48                             DMA_CFG0_WORDSIZE_8 |
49                             DMA_CFG0_TMODE_SINGLE |
50                             DMA_CFG0_TRIGGER_URX0,
51                             DMA_CFG1_SRCINC_0 |
52                             DMA_CFG1_DESTINC_0 |
53                             DMA_CFG1_PRIORITY_NORMAL);
54 #endif
55         ao_dma_set_transfer(ao_spi_dma_out_id,
56                             block,
57                             &U0DBUFXADDR,
58                             len,
59                             DMA_CFG0_WORDSIZE_8 |
60                             DMA_CFG0_TMODE_SINGLE |
61                             DMA_CFG0_TRIGGER_UTX0,
62                             DMA_CFG1_SRCINC_1 |
63                             DMA_CFG1_DESTINC_0 |
64                             DMA_CFG1_PRIORITY_NORMAL);
65
66 #if !AO_SPI_SLAVE
67         ao_dma_start(ao_spi_dma_in_id);
68 #endif
69         ao_dma_start(ao_spi_dma_out_id);
70         ao_dma_trigger(ao_spi_dma_out_id);
71 #if AO_SPI_SLAVE
72         __critical while (!ao_spi_dma_out_done)
73                            ao_sleep(&ao_spi_dma_out_done);
74 #else
75         __critical while (!ao_spi_dma_in_done)
76                 ao_sleep(&ao_spi_dma_in_done);
77 #endif
78 }
79
80 /* Receive bytes over SPI.
81  *
82  * This sets up tow DMA engines, one reading the data and another
83  * writing constant values to the SPI transmitter as that is what
84  * clocks the data coming in.
85  */
86 void
87 ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant
88 {
89         ao_dma_set_transfer(ao_spi_dma_in_id,
90                             &U0DBUFXADDR,
91                             block,
92                             len,
93                             DMA_CFG0_WORDSIZE_8 |
94                             DMA_CFG0_TMODE_SINGLE |
95                             DMA_CFG0_TRIGGER_URX0,
96                             DMA_CFG1_SRCINC_0 |
97                             DMA_CFG1_DESTINC_1 |
98                             DMA_CFG1_PRIORITY_NORMAL);
99
100         ao_spi_const = 0xff;
101
102         ao_dma_set_transfer(ao_spi_dma_out_id,
103                             &ao_spi_const,
104                             &U0DBUFXADDR,
105                             len,
106                             DMA_CFG0_WORDSIZE_8 |
107                             DMA_CFG0_TMODE_SINGLE |
108                             DMA_CFG0_TRIGGER_UTX0,
109                             DMA_CFG1_SRCINC_0 |
110                             DMA_CFG1_DESTINC_0 |
111                             DMA_CFG1_PRIORITY_NORMAL);
112
113         ao_dma_start(ao_spi_dma_in_id);
114         ao_dma_start(ao_spi_dma_out_id);
115         ao_dma_trigger(ao_spi_dma_out_id);
116         __critical while (!ao_spi_dma_in_done)
117                 ao_sleep(&ao_spi_dma_in_done);
118 }
119
120 /*
121  * USART0 SPI config alt 2       (using this one)
122  *
123  *      MO      P1_5
124  *      MI      P1_4
125  *      CLK     P1_3
126  *      CSS     P1_2
127  *
128  * USART0 SPI config alt 1
129  * 
130  *      MO      P0_3
131  *      MI      P0_2
132  *      CLK     P0_5
133  *      SS      P0_4
134  *
135  * USART1 SPI config alt 2
136  *
137  *      MO      P1_6
138  *      MI      P1_7
139  *      CLK     P1_5
140  *      SS      P1_4
141  *
142  * USART1 SPI config alt 1
143  *
144  *      MO      P0_4
145  *      MI      P0_5
146  *      CLK     P0_3
147  *      SS      P0_2
148  *
149  * Chip select is the responsibility of the caller in master mode
150  */
151
152 #if AO_SPI_SLAVE
153 #define CSS     (1 << 2)
154 #define UxCSR_DIRECTION UxCSR_SLAVE
155 #else
156 #define CSS     0
157 #define UxCSR_DIRECTION UxCSR_MASTER
158 #endif
159
160 void
161 ao_spi_init(void)
162 {
163         /* Set up the USART pin assignment */
164         PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;
165
166         /* Ensure that USART0 takes precidence over USART1 for pins that
167          * they share
168          */
169         P2SEL = (P2SEL & ~P2SEL_PRI3P1_MASK) | P2SEL_PRI3P1_USART0;
170
171         /* Make the SPI pins be controlled by the USART peripheral */
172         P1SEL |= ((1 << 5) | (1 << 4) | (1 << 3) | CSS);
173
174         /* Set up OUT DMA */
175         ao_spi_dma_out_id = ao_dma_alloc(&ao_spi_dma_out_done);
176
177         /* Set up IN DMA */
178         ao_spi_dma_in_id = ao_dma_alloc(&ao_spi_dma_in_done);
179
180         /* Set up the USART.
181          *
182          * SPI master mode
183          */
184         U0CSR = (UxCSR_MODE_SPI | UxCSR_RE | UxCSR_DIRECTION);
185
186         /* Set the baud rate and signal parameters
187          *
188          * The cc1111 is limited to a 24/8 MHz SPI clock.
189          * Every peripheral I've ever seen goes faster than that,
190          * so set the clock to 3MHz (BAUD_E 17, BAUD_M 0)
191          */
192         U0BAUD = 0;
193         U0GCR = (UxGCR_CPOL_NEGATIVE |
194                  UxGCR_CPHA_FIRST_EDGE |
195                  UxGCR_ORDER_MSB |
196                  (17 << UxGCR_BAUD_E_SHIFT));
197 }