altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / kernel / ao_i2c_bit.c
1 /*
2  * Copyright © 2021 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, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <ao_i2c_bit.h>
20 #include <ao_exti.h>
21
22 #define ao_i2c_bit_set_pin(port, pin) do {                              \
23                 ao_enable_output(port, pin, 1);                         \
24                 ao_gpio_set_output_mode(port, pin, AO_OUTPUT_OPEN_DRAIN); \
25                 ao_gpio_set_mode(port, pin, AO_EXTI_MODE_PULL_UP);      \
26                 stm_ospeedr_set(port, pin, STM_OSPEEDR_10MHz);          \
27         } while(0)
28
29
30 /* 2µS per half-cycle, for a 125kHz i2c clock */
31 #define AO_I2C_TICK     10000
32
33 static void
34 tick(void)
35 {
36         int i;
37
38         for (i = 0; i < 12; i++)
39                 ao_arch_nop();
40 #if 0
41         uint64_t        target = ao_time_ns() + AO_I2C_TICK;
42
43         do {
44                 ao_yield();
45         } while ((int64_t) (ao_time_ns() - target) < 0);
46 #endif
47 }
48
49 static void sda(uint8_t v)
50 {
51         ao_gpio_set(AO_I2C_SDA_PORT, AO_I2C_SDA_PIN, v);
52         tick();
53 }
54
55 static uint8_t in(void)
56 {
57         uint8_t v = ao_gpio_get(AO_I2C_SDA_PORT, AO_I2C_SDA_PIN);
58         tick();
59         return v;
60 }
61
62 static void scl(uint8_t v)
63 {
64         ao_gpio_set(AO_I2C_SCL_PORT, AO_I2C_SCL_PIN, v);
65         tick();
66 }
67
68 static void
69 i2c_start(void)
70 {
71         sda(0);
72         scl(0);
73 }
74
75 static bool
76 ack(void)
77 {
78         bool v;
79         int j;
80
81         sda(1);
82         scl(1);
83         v = false;
84         for (j = 0; j < 100; j++) {
85                 if (in() == 0) {
86                         v = true;
87                         break;
88                 }
89         }
90         scl(0);
91         sda(1);
92         ao_yield();
93         return v;
94 }
95
96 static bool
97 send(uint8_t byte)
98 {
99         uint8_t bit;
100
101         for (bit = 0; bit < 8; bit++) {
102                 sda(byte>>7);
103                 scl(1);
104                 tick();
105                 scl(0);
106                 byte <<= 1;
107         }
108         return ack();
109 }
110
111 static uint8_t
112 recv(void)
113 {
114         uint8_t byte = 0;
115         uint8_t bit;
116
117         for (bit = 0; bit < 8; bit++) {
118                 sda(1);
119                 scl(1);
120                 byte = (byte << 1) | in();
121                 scl(0);
122         }
123         ack();
124         return byte;
125 }
126
127 static void
128 stop(void)
129 {
130         sda(0);
131         scl(1);
132         sda(1);
133 }
134
135 static void
136 restart(void)
137 {
138         sda(1);
139         scl(1);
140         sda(0);
141         scl(0);
142 }
143
144 static uint8_t
145 ao_i2c_bit_mutex;
146
147 void
148 ao_i2c_bit_get(void)
149 {
150         ao_mutex_get(&ao_i2c_bit_mutex);
151 }
152
153 void
154 ao_i2c_bit_put(void)
155 {
156         ao_mutex_put(&ao_i2c_bit_mutex);
157 }
158
159 bool
160 ao_i2c_bit_start(uint8_t addr)
161 {
162         i2c_start();
163         return send(addr);
164 }
165
166 bool
167 ao_i2c_bit_restart(uint8_t addr)
168 {
169         restart();
170         return send(addr);
171 }
172
173 void
174 ao_i2c_bit_stop(void)
175 {
176         stop();
177 }
178
179 void
180 ao_i2c_bit_send(void *block, uint16_t len)
181 {
182         uint8_t *b = block;
183         while (len--)
184                 send(*b++);
185 }
186
187 void
188 ao_i2c_bit_recv(void *block, uint16_t len)
189 {
190         uint8_t *b = block;
191
192         while (len--)
193                 *b++ = recv();
194 }
195
196 void
197 ao_i2c_bit_init(void)
198 {
199         ao_i2c_bit_set_pin(AO_I2C_SCL_PORT, AO_I2C_SCL_PIN);
200         ao_i2c_bit_set_pin(AO_I2C_SDA_PORT, AO_I2C_SDA_PIN);
201 }