summaryrefslogtreecommitdiff
path: root/src-avr/ao_spi.c
blob: bd52a0d45694516fa643a9d7c376ac8c68543192 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright © 2010 Keith Packard <keithp@keithp.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

#include "ao.h"

__xdata uint8_t	ao_spi_mutex;
__xdata uint8_t ao_spi_dma_in_done;
__xdata uint8_t ao_spi_dma_out_done;

uint8_t	ao_spi_dma_out_id;
uint8_t ao_spi_dma_in_id;

static __xdata uint8_t ao_spi_const = 0xff;

/* Send bytes over SPI.
 *
 * This sets up two DMA engines, one writing the data and another reading
 * bytes coming back.  We use the bytes coming back to tell when the transfer
 * is complete, as the transmit register is double buffered and hence signals
 * completion one byte before the transfer is actually complete
 */
void
ao_spi_send(void __xdata *block, uint16_t len) __reentrant
{
	ao_mutex_get(&ao_spi_mutex);
	ao_dma_set_transfer(ao_spi_dma_in_id,
			    &U0DBUFXADDR,
			    &ao_spi_const,
			    len,
			    DMA_CFG0_WORDSIZE_8 |
			    DMA_CFG0_TMODE_SINGLE |
			    DMA_CFG0_TRIGGER_URX0,
			    DMA_CFG1_SRCINC_0 |
			    DMA_CFG1_DESTINC_0 |
			    DMA_CFG1_PRIORITY_NORMAL);

	ao_dma_set_transfer(ao_spi_dma_out_id,
			    block,
			    &U0DBUFXADDR,
			    len,
			    DMA_CFG0_WORDSIZE_8 |
			    DMA_CFG0_TMODE_SINGLE |
			    DMA_CFG0_TRIGGER_UTX0,
			    DMA_CFG1_SRCINC_1 |
			    DMA_CFG1_DESTINC_0 |
			    DMA_CFG1_PRIORITY_NORMAL);

	ao_dma_start(ao_spi_dma_in_id);
	ao_dma_start(ao_spi_dma_out_id);
	ao_dma_trigger(ao_spi_dma_out_id);
	__critical while (!ao_spi_dma_in_done)
		ao_sleep(&ao_spi_dma_in_done);
	ao_mutex_put(&ao_spi_mutex);
}

/* Receive bytes over SPI.
 *
 * This sets up tow DMA engines, one reading the data and another
 * writing constant values to the SPI transmitter as that is what
 * clocks the data coming in.
 */
void
ao_spi_recv(void __xdata *block, uint16_t len) __reentrant
{
	ao_mutex_get(&ao_spi_mutex);
	ao_dma_set_transfer(ao_spi_dma_in_id,
			    &U0DBUFXADDR,
			    block,
			    len,
			    DMA_CFG0_WORDSIZE_8 |
			    DMA_CFG0_TMODE_SINGLE |
			    DMA_CFG0_TRIGGER_URX0,
			    DMA_CFG1_SRCINC_0 |
			    DMA_CFG1_DESTINC_1 |
			    DMA_CFG1_PRIORITY_NORMAL);

	ao_dma_set_transfer(ao_spi_dma_out_id,
			    &ao_spi_const,
			    &U0DBUFXADDR,
			    len,
			    DMA_CFG0_WORDSIZE_8 |
			    DMA_CFG0_TMODE_SINGLE |
			    DMA_CFG0_TRIGGER_UTX0,
			    DMA_CFG1_SRCINC_0 |
			    DMA_CFG1_DESTINC_0 |
			    DMA_CFG1_PRIORITY_NORMAL);

	ao_dma_start(ao_spi_dma_in_id);
	ao_dma_start(ao_spi_dma_out_id);
	ao_dma_trigger(ao_spi_dma_out_id);
	__critical while (!ao_spi_dma_in_done)
		ao_sleep(&ao_spi_dma_in_done);
	ao_mutex_put(&ao_spi_mutex);
}

/*
 * Initialize USART0 for SPI using config alt 2
 *
 * 	MO	P1_5
 * 	MI	P1_4
 * 	CLK	P1_3
 *
 * Chip select is the responsibility of the caller
 */

void
ao_spi_init(void)
{
	/* Set up the USART pin assignment */
	PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;

	/* Ensure that USART0 takes precidence over USART1 for pins that
	 * they share
	 */
	P2SEL = (P2SEL & ~P2SEL_PRI3P1_MASK) | P2SEL_PRI3P1_USART0;

	/* Make the SPI pins be controlled by the USART peripheral */
	P1SEL |= ((1 << 5) | (1 << 4) | (1 << 3));

	/* Set up OUT DMA */
	ao_spi_dma_out_id = ao_dma_alloc(&ao_spi_dma_out_done);

	/* Set up IN DMA */
	ao_spi_dma_in_id = ao_dma_alloc(&ao_spi_dma_in_done);

	/* Set up the USART.
	 *
	 * SPI master mode
	 */
	U0CSR = (UxCSR_MODE_SPI | UxCSR_RE | UxCSR_MASTER);

	/* Set the baud rate and signal parameters
	 *
	 * The cc1111 is limited to a 24/8 MHz SPI clock.
	 * Every peripheral I've ever seen goes faster than that,
	 * so set the clock to 3MHz (BAUD_E 17, BAUD_M 0)
	 */
	U0BAUD = 0;
	U0GCR = (UxGCR_CPOL_NEGATIVE |
		 UxGCR_CPHA_FIRST_EDGE |
		 UxGCR_ORDER_MSB |
		 (17 << UxGCR_BAUD_E_SHIFT));
}