Revision 3cd035d8 hw/bitbang_i2c.c
b/hw/bitbang_i2c.c | ||
---|---|---|
7 | 7 |
* This code is licenced under the GNU GPL v2. |
8 | 8 |
*/ |
9 | 9 |
#include "hw.h" |
10 |
#include "i2c.h" |
|
10 |
#include "bitbang_i2c.h"
|
|
11 | 11 |
#include "sysbus.h" |
12 | 12 |
|
13 |
//#define DEBUG_BITBANG_I2C |
|
14 |
|
|
15 |
#ifdef DEBUG_BITBANG_I2C |
|
16 |
#define DPRINTF(fmt, ...) \ |
|
17 |
do { printf("bitbang_i2c: " fmt , ## __VA_ARGS__); } while (0) |
|
18 |
#else |
|
19 |
#define DPRINTF(fmt, ...) do {} while(0) |
|
20 |
#endif |
|
21 |
|
|
13 | 22 |
typedef enum bitbang_i2c_state { |
14 | 23 |
STOPPED = 0, |
15 |
INITIALIZING, |
|
16 | 24 |
SENDING_BIT7, |
17 | 25 |
SENDING_BIT6, |
18 | 26 |
SENDING_BIT5, |
... | ... | |
33 | 41 |
SENDING_ACK |
34 | 42 |
} bitbang_i2c_state; |
35 | 43 |
|
36 |
typedef struct bitbang_i2c_interface { |
|
37 |
SysBusDevice busdev; |
|
44 |
struct bitbang_i2c_interface { |
|
38 | 45 |
i2c_bus *bus; |
39 | 46 |
bitbang_i2c_state state; |
40 | 47 |
int last_data; |
41 | 48 |
int last_clock; |
49 |
int device_out; |
|
42 | 50 |
uint8_t buffer; |
43 | 51 |
int current_addr; |
44 |
qemu_irq out; |
|
45 |
} bitbang_i2c_interface; |
|
52 |
}; |
|
46 | 53 |
|
47 | 54 |
static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) |
48 | 55 |
{ |
56 |
DPRINTF("STOP\n"); |
|
49 | 57 |
if (i2c->current_addr >= 0) |
50 | 58 |
i2c_end_transfer(i2c->bus); |
51 | 59 |
i2c->current_addr = -1; |
52 | 60 |
i2c->state = STOPPED; |
53 | 61 |
} |
54 | 62 |
|
55 |
static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) |
|
63 |
/* Set device data pin. */ |
|
64 |
static int bitbang_i2c_ret(bitbang_i2c_interface *i2c, int level) |
|
65 |
{ |
|
66 |
i2c->device_out = level; |
|
67 |
//DPRINTF("%d %d %d\n", i2c->last_clock, i2c->last_data, i2c->device_out); |
|
68 |
return level & i2c->last_data; |
|
69 |
} |
|
70 |
|
|
71 |
/* Leave device data pin unodified. */ |
|
72 |
static int bitbang_i2c_nop(bitbang_i2c_interface *i2c) |
|
73 |
{ |
|
74 |
return bitbang_i2c_ret(i2c, i2c->device_out); |
|
75 |
} |
|
76 |
|
|
77 |
/* Returns data line level. */ |
|
78 |
int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) |
|
56 | 79 |
{ |
57 |
bitbang_i2c_interface *i2c = opaque; |
|
58 | 80 |
int data; |
59 |
int clock; |
|
60 |
int data_goes_up; |
|
61 |
int data_goes_down; |
|
62 |
int clock_goes_up; |
|
63 |
int clock_goes_down; |
|
64 |
|
|
65 |
/* get pins states */ |
|
66 |
data = i2c->last_data; |
|
67 |
clock = i2c->last_clock; |
|
68 |
|
|
69 |
if (irq == 0) |
|
70 |
data = level; |
|
71 |
if (irq == 1) |
|
72 |
clock = level; |
|
73 |
|
|
74 |
/* compute pins changes */ |
|
75 |
data_goes_up = data == 1 && i2c->last_data == 0; |
|
76 |
data_goes_down = data == 0 && i2c->last_data == 1; |
|
77 |
clock_goes_up = clock == 1 && i2c->last_clock == 0; |
|
78 |
clock_goes_down = clock == 0 && i2c->last_clock == 1; |
|
79 |
|
|
80 |
if (data_goes_up == 0 && data_goes_down == 0 && |
|
81 |
clock_goes_up == 0 && clock_goes_down == 0) |
|
82 |
return; |
|
83 |
|
|
84 |
if (!i2c) |
|
85 |
return; |
|
86 |
|
|
87 |
if ((RECEIVING_BIT7 > i2c->state && i2c->state > RECEIVING_BIT0) |
|
88 |
|| i2c->state == WAITING_FOR_ACK) |
|
89 |
qemu_set_irq(i2c->out, 0); |
|
90 | 81 |
|
91 |
switch (i2c->state) { |
|
92 |
case STOPPED: |
|
93 |
if (data_goes_down && clock == 1) |
|
94 |
i2c->state = INITIALIZING; |
|
95 |
break; |
|
82 |
if (level != 0 && level != 1) { |
|
83 |
abort(); |
|
84 |
} |
|
96 | 85 |
|
97 |
case INITIALIZING: |
|
98 |
if (clock_goes_down && data == 0) |
|
86 |
if (line == BITBANG_I2C_SDA) { |
|
87 |
if (level == i2c->last_data) { |
|
88 |
return bitbang_i2c_nop(i2c); |
|
89 |
} |
|
90 |
i2c->last_data = level; |
|
91 |
if (i2c->last_clock == 0) { |
|
92 |
return bitbang_i2c_nop(i2c); |
|
93 |
} |
|
94 |
if (level == 0) { |
|
95 |
DPRINTF("START\n"); |
|
96 |
/* START condition. */ |
|
99 | 97 |
i2c->state = SENDING_BIT7; |
100 |
else |
|
98 |
i2c->current_addr = -1; |
|
99 |
} else { |
|
100 |
/* STOP condition. */ |
|
101 | 101 |
bitbang_i2c_enter_stop(i2c); |
102 |
break; |
|
102 |
} |
|
103 |
return bitbang_i2c_ret(i2c, 1); |
|
104 |
} |
|
105 |
|
|
106 |
data = i2c->last_data; |
|
107 |
if (i2c->last_clock == level) { |
|
108 |
return bitbang_i2c_nop(i2c); |
|
109 |
} |
|
110 |
i2c->last_clock = level; |
|
111 |
if (level == 0) { |
|
112 |
/* State is set/read at the start of the clock pulse. |
|
113 |
release the data line at the end. */ |
|
114 |
return bitbang_i2c_ret(i2c, 1); |
|
115 |
} |
|
116 |
switch (i2c->state) { |
|
117 |
case STOPPED: |
|
118 |
return bitbang_i2c_ret(i2c, 1); |
|
103 | 119 |
|
104 | 120 |
case SENDING_BIT7 ... SENDING_BIT0: |
105 |
if (clock_goes_down) { |
|
106 |
i2c->buffer = (i2c->buffer << 1) | data; |
|
107 |
/* will end up in WAITING_FOR_ACK */ |
|
108 |
i2c->state++; |
|
109 |
} else if (data_goes_up && clock == 1) |
|
110 |
bitbang_i2c_enter_stop(i2c); |
|
111 |
break; |
|
121 |
i2c->buffer = (i2c->buffer << 1) | data; |
|
122 |
/* will end up in WAITING_FOR_ACK */ |
|
123 |
i2c->state++; |
|
124 |
return bitbang_i2c_ret(i2c, 1); |
|
112 | 125 |
|
113 | 126 |
case WAITING_FOR_ACK: |
114 |
if (clock_goes_down) { |
|
115 |
if (i2c->current_addr < 0) { |
|
116 |
i2c->current_addr = i2c->buffer; |
|
117 |
i2c_start_transfer(i2c->bus, (i2c->current_addr & 0xfe) / 2, |
|
118 |
i2c->buffer & 1); |
|
119 |
} else |
|
120 |
i2c_send(i2c->bus, i2c->buffer); |
|
121 |
if (i2c->current_addr & 1) { |
|
122 |
i2c->state = RECEIVING_BIT7; |
|
123 |
i2c->buffer = i2c_recv(i2c->bus); |
|
124 |
} else |
|
125 |
i2c->state = SENDING_BIT7; |
|
126 |
} else if (data_goes_up && clock == 1) |
|
127 |
bitbang_i2c_enter_stop(i2c); |
|
128 |
break; |
|
129 |
|
|
130 |
case RECEIVING_BIT7 ... RECEIVING_BIT0: |
|
131 |
qemu_set_irq(i2c->out, i2c->buffer >> 7); |
|
132 |
if (clock_goes_down) { |
|
133 |
/* will end up in SENDING_ACK */ |
|
134 |
i2c->state++; |
|
135 |
i2c->buffer <<= 1; |
|
136 |
} else if (data_goes_up && clock == 1) |
|
137 |
bitbang_i2c_enter_stop(i2c); |
|
138 |
break; |
|
127 |
if (i2c->current_addr < 0) { |
|
128 |
i2c->current_addr = i2c->buffer; |
|
129 |
DPRINTF("Address 0x%02x\n", i2c->current_addr); |
|
130 |
i2c_start_transfer(i2c->bus, i2c->current_addr >> 1, |
|
131 |
i2c->current_addr & 1); |
|
132 |
} else { |
|
133 |
DPRINTF("Sent 0x%02x\n", i2c->buffer); |
|
134 |
i2c_send(i2c->bus, i2c->buffer); |
|
135 |
} |
|
136 |
if (i2c->current_addr & 1) { |
|
137 |
i2c->state = RECEIVING_BIT7; |
|
138 |
} else { |
|
139 |
i2c->state = SENDING_BIT7; |
|
140 |
} |
|
141 |
return bitbang_i2c_ret(i2c, 0); |
|
142 |
|
|
143 |
case RECEIVING_BIT7: |
|
144 |
i2c->buffer = i2c_recv(i2c->bus); |
|
145 |
DPRINTF("RX byte 0x%02x\n", i2c->buffer); |
|
146 |
/* Fall through... */ |
|
147 |
case RECEIVING_BIT6 ... RECEIVING_BIT0: |
|
148 |
data = i2c->buffer >> 7; |
|
149 |
/* will end up in SENDING_ACK */ |
|
150 |
i2c->state++; |
|
151 |
i2c->buffer <<= 1; |
|
152 |
return bitbang_i2c_ret(i2c, data); |
|
139 | 153 |
|
140 | 154 |
case SENDING_ACK: |
141 |
if (clock_goes_down) { |
|
142 |
i2c->state = RECEIVING_BIT7; |
|
143 |
if (data == 0) |
|
144 |
i2c->buffer = i2c_recv(i2c->bus); |
|
145 |
else |
|
146 |
i2c_nack(i2c->bus); |
|
147 |
} else if (data_goes_up && clock == 1) |
|
148 |
bitbang_i2c_enter_stop(i2c); |
|
149 |
break; |
|
155 |
i2c->state = RECEIVING_BIT7; |
|
156 |
if (data != 0) { |
|
157 |
DPRINTF("NACKED\n"); |
|
158 |
i2c_nack(i2c->bus); |
|
159 |
} else { |
|
160 |
DPRINTF("ACKED\n"); |
|
161 |
} |
|
162 |
return bitbang_i2c_ret(i2c, 1); |
|
150 | 163 |
} |
164 |
abort(); |
|
165 |
} |
|
166 |
|
|
167 |
bitbang_i2c_interface *bitbang_i2c_init(i2c_bus *bus) |
|
168 |
{ |
|
169 |
bitbang_i2c_interface *s; |
|
170 |
|
|
171 |
s = qemu_mallocz(sizeof(bitbang_i2c_interface)); |
|
172 |
|
|
173 |
s->bus = bus; |
|
174 |
s->last_data = 1; |
|
175 |
s->last_clock = 1; |
|
176 |
s->device_out = 1; |
|
177 |
|
|
178 |
return s; |
|
179 |
} |
|
151 | 180 |
|
152 |
i2c->last_data = data; |
|
153 |
i2c->last_clock = clock; |
|
181 |
/* GPIO interface. */ |
|
182 |
typedef struct { |
|
183 |
SysBusDevice busdev; |
|
184 |
bitbang_i2c_interface *bitbang; |
|
185 |
int last_level; |
|
186 |
qemu_irq out; |
|
187 |
} GPIOI2CState; |
|
188 |
|
|
189 |
static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) |
|
190 |
{ |
|
191 |
GPIOI2CState *s = opaque; |
|
192 |
|
|
193 |
level = bitbang_i2c_set(s->bitbang, irq, level); |
|
194 |
if (level != s->last_level) { |
|
195 |
s->last_level = level; |
|
196 |
qemu_set_irq(s->out, level); |
|
197 |
} |
|
154 | 198 |
} |
155 | 199 |
|
156 |
static int bitbang_i2c_init(SysBusDevice *dev)
|
|
200 |
static int gpio_i2c_init(SysBusDevice *dev)
|
|
157 | 201 |
{ |
158 |
bitbang_i2c_interface *s = FROM_SYSBUS(bitbang_i2c_interface, dev);
|
|
202 |
GPIOI2CState *s = FROM_SYSBUS(GPIOI2CState, dev);
|
|
159 | 203 |
i2c_bus *bus; |
160 | 204 |
|
161 | 205 |
sysbus_init_mmio(dev, 0x0, 0); |
162 | 206 |
|
163 | 207 |
bus = i2c_init_bus(&dev->qdev, "i2c"); |
164 |
s->bus = bus; |
|
165 |
|
|
166 |
s->last_data = 1; |
|
167 |
s->last_clock = 1; |
|
208 |
s->bitbang = bitbang_i2c_init(bus); |
|
168 | 209 |
|
169 | 210 |
qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2); |
170 | 211 |
qdev_init_gpio_out(&dev->qdev, &s->out, 1); |
... | ... | |
172 | 213 |
return 0; |
173 | 214 |
} |
174 | 215 |
|
216 |
static SysBusDeviceInfo gpio_i2c_info = { |
|
217 |
.init = gpio_i2c_init, |
|
218 |
.qdev.name = "gpio_i2c", |
|
219 |
.qdev.desc = "Virtual GPIO to I2C bridge", |
|
220 |
.qdev.size = sizeof(GPIOI2CState), |
|
221 |
}; |
|
222 |
|
|
175 | 223 |
static void bitbang_i2c_register(void) |
176 | 224 |
{ |
177 |
sysbus_register_dev("bitbang_i2c", |
|
178 |
sizeof(bitbang_i2c_interface), bitbang_i2c_init); |
|
225 |
sysbus_register_withprop(&gpio_i2c_info); |
|
179 | 226 |
} |
180 | 227 |
|
181 | 228 |
device_init(bitbang_i2c_register) |
Also available in: Unified diff