altos: add STM I2C driver.
[fw/altos] / src / stm / ao_i2c_stm.c
1 /*
2  * Copyright © 2012 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 struct ao_i2c_stm_info {
21         uint8_t tx_dma_index;
22         uint8_t rx_dma_index;
23         struct stm_i2c  *stm_i2c;
24 };
25
26 #define I2C_IDLE        0
27 #define I2C_RUNNING     1
28 #define I2C_ERROR       2
29
30 static uint8_t  ao_i2c_state[STM_NUM_I2C];
31 static uint16_t ao_i2c_addr[STM_NUM_I2C];
32 uint8_t         ao_i2c_mutex[STM_NUM_I2C];
33
34 #define AO_STM_I2C_CR1 ((0 << STM_I2C_CR1_SWRST) |      \
35                         (0 << STM_I2C_CR1_ALERT) |      \
36                         (0 << STM_I2C_CR1_PEC) |        \
37                         (0 << STM_I2C_CR1_POS) |        \
38                         (0 << STM_I2C_CR1_ACK) |        \
39                         (0 << STM_I2C_CR1_STOP) |       \
40                         (0 << STM_I2C_CR1_START) |      \
41                         (0 << STM_I2C_CR1_NOSTRETCH) |  \
42                         (0 << STM_I2C_CR1_ENGC) |       \
43                         (0 << STM_I2C_CR1_ENPEC) |      \
44                         (0 << STM_I2C_CR1_ENARP) |      \
45                         (0 << STM_I2C_CR1_SMBTYPE) |    \
46                         (0 << STM_I2C_CR1_SMBUS) |      \
47                         (1 << STM_I2C_CR1_PE))
48
49 #define AO_STM_I2C_CR2  ((0 << STM_I2C_CR2_LAST) |                      \
50                          (1 << STM_I2C_CR2_DMAEN) |                     \
51                          (0 << STM_I2C_CR2_ITBUFEN) |                   \
52                          (0 << STM_I2C_CR2_ITEVTEN) |                   \
53                          (0 << STM_I2C_CR2_ITERREN) |                   \
54                          (STM_I2C_CR2_FREQ_16_MHZ << STM_I2C_CR2_FREQ))
55
56 static const struct ao_i2c_stm_info     ao_i2c_stm_info[STM_NUM_I2C] = {
57         {
58                 .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_TX),
59                 .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_RX),
60                 .stm_i2c = &stm_i2c1
61         },
62         {
63                 .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_TX),
64                 .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_RX),
65                 .stm_i2c = &stm_i2c2
66         },
67 };
68
69 static void
70 ao_i2c_ev_isr(uint8_t index)
71 {
72         struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
73         uint32_t        sr1;
74
75         sr1 = stm_i2c->sr1;
76         if (sr1 & (1 << STM_I2C_SR1_SB))
77                 stm_i2c->dr = ao_i2c_addr[index];
78         if (sr1 & (1 << STM_I2C_SR1_ADDR)) {
79                 (void) stm_i2c->sr2;
80                 ao_i2c_state[index] = I2C_RUNNING;
81                 ao_wakeup(&ao_i2c_state[index]);
82         }
83 }
84
85 void stm_i2c1_ev_isr(void) { ao_i2c_ev_isr(0); }
86 void stm_i2c2_ev_isr(void) { ao_i2c_ev_isr(1); }
87
88 static void
89 ao_i2c_er_isr(uint8_t index)
90 {
91         struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
92         uint32_t        sr1;
93
94         sr1 = stm_i2c->sr1;
95         if (sr1 & (1 << STM_I2C_SR1_AF)) {
96                 ao_i2c_state[index] = I2C_ERROR;
97                 stm_i2c->sr1 = sr1 & ~(1 << STM_I2C_SR1_AF);
98                 ao_wakeup(&ao_i2c_state[index]);
99         }
100 }
101
102 void stm_i2c1_er_isr(void) { ao_i2c_er_isr(0); }
103 void stm_i2c2_er_isr(void) { ao_i2c_er_isr(1); }
104
105 void
106 ao_i2c_get(uint8_t index)
107 {
108         struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
109         ao_mutex_get(&ao_i2c_mutex[index]);
110
111         stm_i2c->sr1 = 0;
112         stm_i2c->sr2 = 0;
113 }
114
115 void
116 ao_i2c_put(uint8_t index)
117 {
118         ao_mutex_put(&ao_i2c_mutex[index]);
119 }
120
121 uint8_t
122 ao_i2c_start(uint8_t index, uint16_t addr)
123 {
124         struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
125         
126         ao_i2c_state[index] = I2C_IDLE;
127         ao_i2c_addr[index] = addr;
128         stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
129         stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_START);
130         ao_arch_critical(
131                 while (ao_i2c_state[index] == I2C_IDLE)
132                         ao_sleep(&ao_i2c_state[index]);
133                 );
134         return ao_i2c_state[index] == I2C_RUNNING;
135 }
136
137 void
138 ao_i2c_send(void *block, uint16_t len, uint8_t index)
139 {
140         struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
141         uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
142
143         ao_dma_set_transfer(tx_dma_index,
144                             &stm_i2c->dr,
145                             block,
146                             len,
147                             (0 << STM_DMA_CCR_MEM2MEM) |
148                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
149                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
150                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
151                             (1 << STM_DMA_CCR_MINC) |
152                             (0 << STM_DMA_CCR_PINC) |
153                             (0 << STM_DMA_CCR_CIRC) |
154                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
155                            
156         ao_dma_start(tx_dma_index);
157         ao_arch_critical(
158                 while (!ao_dma_done[tx_dma_index])
159                         ao_sleep(&ao_dma_done[tx_dma_index]);
160                 );
161         ao_dma_done_transfer(tx_dma_index);
162 }
163
164 void
165 ao_i2c_channel_init(uint8_t index)
166 {
167         struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
168
169         /* Turn I2C off while configuring */
170         stm_i2c->cr1 = 0;
171         stm_i2c->cr2 = AO_STM_I2C_CR2;
172
173         (void) stm_i2c->sr1;
174         (void) stm_i2c->sr2;
175         (void) stm_i2c->dr;
176
177         stm_i2c->sr1 = 0;
178         stm_i2c->sr2 = 0;
179
180         stm_i2c->ccr = ((1 << STM_I2C_CCR_FS) |
181                         (0 << STM_I2C_CCR_DUTY) |
182                         (20 << STM_I2C_CCR_CCR));
183         
184         stm_i2c->cr1 = AO_STM_I2C_CR1;
185 }
186
187 void
188 ao_i2c_init(void)
189 {
190         /* All of the I2C configurations are on port B */
191         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
192 #if HAS_I2C_1
193 # if I2C_1_PB6_PB7
194         stm_afr_set(&stm_gpiob, 6, STM_AFR_AF4);
195         stm_afr_set(&stm_gpiob, 7, STM_AFR_AF4);
196 # else
197 #  if I2C_1_PB8_PB9
198         stm_afr_set(&stm_gpiob, 8, STM_AFR_AF4);
199         stm_afr_set(&stm_gpiob, 9, STM_AFR_AF4);
200 #  else
201 #   error "No I2C_1 port configuration specified"
202 #  endif
203 # endif
204
205         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C1EN);
206         ao_i2c_channel_init(0);
207
208         stm_nvic_set_enable(STM_ISR_I2C1_EV_POS);
209         stm_nvic_set_priority(STM_ISR_I2C1_EV_POS, 3);
210         stm_nvic_set_enable(STM_ISR_I2C1_ER_POS);
211         stm_nvic_set_priority(STM_ISR_I2C1_ER_POS, 3);
212 #endif
213
214 #if HAS_I2C_2
215 # if I2C_2_PB10_PB11
216         stm_afr_set(&stm_gpiob, 10, STM_AFR_AF4);
217         stm_afr_set(&stm_gpiob, 11, STM_AFR_AF4);
218 # else
219 #  error "No I2C_2 port configuration specified"
220 # endif
221         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C2EN);
222         ao_i2c_channel_init(1);
223
224         stm_nvic_set_enable(STM_ISR_I2C2_EV_POS);
225         stm_nvic_set_priority(STM_ISR_I2C2_EV_POS, 3);
226         stm_nvic_set_enable(STM_ISR_I2C2_ER_POS);
227         stm_nvic_set_priority(STM_ISR_I2C2_ER_POS, 3);
228 #endif
229 }
230