Merge remote-tracking branch 'mjb/master'
[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  * Initialize USART0 for SPI using config alt 2
122  *
123  *      MO      P1_5
124  *      MI      P1_4
125  *      CLK     P1_3
126  *      CSS     P1_2
127  *
128  * Chip select is the responsibility of the caller in master mode
129  */
130
131 #if AO_SPI_SLAVE
132 #define CSS     (1 << 2)
133 #define UxCSR_DIRECTION UxCSR_SLAVE
134 #else
135 #define CSS     0
136 #define UxCSR_DIRECTION UxCSR_MASTER
137 #endif
138
139 void
140 ao_spi_init(void)
141 {
142         /* Set up the USART pin assignment */
143         PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;
144
145         /* Ensure that USART0 takes precidence over USART1 for pins that
146          * they share
147          */
148         P2SEL = (P2SEL & ~P2SEL_PRI3P1_MASK) | P2SEL_PRI3P1_USART0;
149
150         /* Make the SPI pins be controlled by the USART peripheral */
151         P1SEL |= ((1 << 5) | (1 << 4) | (1 << 3) | CSS);
152
153         /* Set up OUT DMA */
154         ao_spi_dma_out_id = ao_dma_alloc(&ao_spi_dma_out_done);
155
156         /* Set up IN DMA */
157         ao_spi_dma_in_id = ao_dma_alloc(&ao_spi_dma_in_done);
158
159         /* Set up the USART.
160          *
161          * SPI master mode
162          */
163         U0CSR = (UxCSR_MODE_SPI | UxCSR_RE | UxCSR_DIRECTION);
164
165         /* Set the baud rate and signal parameters
166          *
167          * The cc1111 is limited to a 24/8 MHz SPI clock.
168          * Every peripheral I've ever seen goes faster than that,
169          * so set the clock to 3MHz (BAUD_E 17, BAUD_M 0)
170          */
171         U0BAUD = 0;
172         U0GCR = (UxGCR_CPOL_NEGATIVE |
173                  UxGCR_CPHA_FIRST_EDGE |
174                  UxGCR_ORDER_MSB |
175                  (17 << UxGCR_BAUD_E_SHIFT));
176 }