blob: b1a9a627381282a056157206e2598110fcaa2676 [file] [log] [blame]
Harald Weltec3f170d2019-02-24 09:06:59 +01001/* Bit-banging I2C layer, inspired to a large extent from Linux kernel
2 * i2c-algo-bit.c code (C) 1995-2000 Simon G. Vogl. This particular
3 * implementation is (C) 2019 by Harald Welte <laforge@gnumonks.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <hal_gpio.h>
9#include <hal_delay.h>
10#include <err_codes.h>
11#include "i2c_bitbang.h"
12
13#define setsda(adap, val) gpio_set_pin_level((adap)->pin_sda, val)
14#define setscl(adap, val) gpio_set_pin_level((adap)->pin_scl, val)
15
16static int getsda(const struct i2c_adapter *adap)
17{
18 int rc;
19 gpio_set_pin_direction(adap->pin_sda, GPIO_DIRECTION_IN);
20 rc = gpio_get_pin_level(adap->pin_sda);
21 gpio_set_pin_direction(adap->pin_sda, GPIO_DIRECTION_OUT);
22 return rc;
23}
24
25static int getscl(const struct i2c_adapter *adap)
26{
27 int rc;
28 gpio_set_pin_direction(adap->pin_scl, GPIO_DIRECTION_IN);
29 rc = gpio_get_pin_level(adap->pin_scl);
30 gpio_set_pin_direction(adap->pin_scl, GPIO_DIRECTION_OUT);
31 return rc;
32}
33
34static inline void sdalo(const struct i2c_adapter *adap)
35{
36 setsda(adap, 0);
37 delay_us((adap->udelay+1) / 2);
38}
39
40static inline void sdahi(const struct i2c_adapter *adap)
41{
42 setsda(adap, 1);
43 delay_us((adap->udelay+1) / 2);
44}
45
46static inline void scllo(const struct i2c_adapter *adap)
47{
48 setscl(adap, 0);
49 delay_us(adap->udelay / 2);
50}
51
52
53static int sclhi(const struct i2c_adapter *adap)
54{
55 setscl(adap, 1);
56
57 /* wait for slow slaves' clock stretching to complete */
58 while (!getscl(adap)) {
59 /* FIXME: abort at some point */
60 }
61 return 0;
62}
63
64static void i2c_start(const struct i2c_adapter *adap)
65{
66 /* Assert: SCL + SDA are high */
67 setsda(adap, 0); /* set SDA to low */
68 delay_us(adap->udelay); /* delay */
69 scllo(adap); /* Set SCL to low */
70}
71
72static void i2c_repstart(const struct i2c_adapter *adap)
73{
74 /* Assert: SCL is low */
75 sdahi(adap);
76 sclhi(adap);
77 setsda(adap, 0);
78 delay_us(adap->udelay);
79 scllo(adap);
80}
81
82static void i2c_stop(const struct i2c_adapter *adap)
83{
84 /* Assert: SCL is low */
85 sdalo(adap); /* set SDA low */
86 sclhi(adap); /* set SCL to high */
87 setsda(adap, 1); /* set SDA to high */
88 delay_us(adap->udelay); /* delay */
89}
90
91static int i2c_outb(const struct i2c_adapter *adap, uint8_t outdata)
92{
93 uint8_t sb;
94 int ack, i;
95
96 /* Assert: SCL is low */
97 for (i = 7; i >= 0; i--) {
98 sb = (outdata >> i) & 1;
99 setsda(adap, sb);
100 delay_us((adap->udelay + 1) / 2);
101 if (sclhi(adap) < 0)
102 return -ERR_TIMEOUT;
103 scllo(adap);
104 }
105 sdahi(adap);
106 if (sclhi(adap) < 0)
107 return -ERR_TIMEOUT;
108 ack = !getsda(adap);
109 scllo(adap);
110 return ack;
111}
112
113static int i2c_inb(const struct i2c_adapter *adap)
114{
115 uint8_t indata = 0;
116 int i;
117
118 /* Assert: CSL is low */
119 sdahi(adap);
120 for (i = 0; i < 8; i++) {
121 /* SCL high */
122 if (sclhi(adap) < 0)
123 return -ERR_TIMEOUT;
124 indata = indata << 1;
125 if (getsda(adap))
126 indata |= 0x01;
127 setscl(adap, 0);
128 if (i == 7)
129 delay_us(adap->udelay / 2);
130 else
131 delay_us(adap->udelay);
132 }
133 /* Assert: SCL is low */
134 return indata;
135}
136
137/*! Single-byte register write. Assumes 8bit register address + 8bit values */
138int i2c_write_reg(const struct i2c_adapter *adap, uint8_t addr, uint8_t reg, uint8_t val)
139{
140 int rc;
141
142 i2c_start(adap);
143 rc = i2c_outb(adap, addr << 1);
144 if (rc < 0)
145 goto out_stop;
146 rc = i2c_outb(adap, reg);
147 if (rc < 0)
148 goto out_stop;
149 rc = i2c_outb(adap, val);
150out_stop:
151 i2c_stop(adap);
152 return rc;
153}
154
155/*! Single-byte register read. Assumes 8bit register address + 8bit values */
156int i2c_read_reg(const struct i2c_adapter *adap, uint8_t addr, uint8_t reg)
157{
158 int rc;
159
160 i2c_start(adap);
161 rc = i2c_outb(adap, addr << 1);
162 if (rc < 0)
163 goto out_stop;
164 rc = i2c_outb(adap, reg);
165 if (rc < 0)
166 goto out_stop;
167 i2c_repstart(adap);
168 rc = i2c_outb(adap, addr << 1 | 1);
169 if (rc < 0)
170 goto out_stop;
171 rc = i2c_inb(adap);
172out_stop:
173 i2c_stop(adap);
174 return rc;
175}
176
177/*! Initialize a given I2C adapter/bus */
178int i2c_init(const struct i2c_adapter *adap)
179{
180 gpio_set_pin_direction(adap->pin_sda, GPIO_DIRECTION_OUT);
181 gpio_set_pin_direction(adap->pin_scl, GPIO_DIRECTION_OUT);
182
183 /* Bring bus to a known state. Looks like STOP if bus is not free yet */
184 setscl(adap, 1);
185 delay_us(adap->udelay);
186 setsda(adap, 1);
187
188 return 0;
189}