xref: /illumos-kvm-cmd/hw/bitbang_i2c.c (revision 68396ea9)
1 /*
2  * Bit-Bang i2c emulation extracted from
3  * Marvell MV88W8618 / Freecom MusicPal emulation.
4  *
5  * Copyright (c) 2008 Jan Kiszka
6  *
7  * This code is licenced under the GNU GPL v2.
8  */
9 #include "hw.h"
10 #include "bitbang_i2c.h"
11 #include "sysbus.h"
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 
22 typedef enum bitbang_i2c_state {
23     STOPPED = 0,
24     SENDING_BIT7,
25     SENDING_BIT6,
26     SENDING_BIT5,
27     SENDING_BIT4,
28     SENDING_BIT3,
29     SENDING_BIT2,
30     SENDING_BIT1,
31     SENDING_BIT0,
32     WAITING_FOR_ACK,
33     RECEIVING_BIT7,
34     RECEIVING_BIT6,
35     RECEIVING_BIT5,
36     RECEIVING_BIT4,
37     RECEIVING_BIT3,
38     RECEIVING_BIT2,
39     RECEIVING_BIT1,
40     RECEIVING_BIT0,
41     SENDING_ACK
42 } bitbang_i2c_state;
43 
44 struct bitbang_i2c_interface {
45     i2c_bus *bus;
46     bitbang_i2c_state state;
47     int last_data;
48     int last_clock;
49     int device_out;
50     uint8_t buffer;
51     int current_addr;
52 };
53 
bitbang_i2c_enter_stop(bitbang_i2c_interface * i2c)54 static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c)
55 {
56     DPRINTF("STOP\n");
57     if (i2c->current_addr >= 0)
58         i2c_end_transfer(i2c->bus);
59     i2c->current_addr = -1;
60     i2c->state = STOPPED;
61 }
62 
63 /* Set device data pin.  */
bitbang_i2c_ret(bitbang_i2c_interface * i2c,int level)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.  */
bitbang_i2c_nop(bitbang_i2c_interface * i2c)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.  */
bitbang_i2c_set(bitbang_i2c_interface * i2c,int line,int level)78 int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level)
79 {
80     int data;
81 
82     if (level != 0 && level != 1) {
83         abort();
84     }
85 
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.  */
97             i2c->state = SENDING_BIT7;
98             i2c->current_addr = -1;
99         } else {
100             /* STOP condition.  */
101             bitbang_i2c_enter_stop(i2c);
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);
119 
120     case SENDING_BIT7 ... SENDING_BIT0:
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);
125 
126     case WAITING_FOR_ACK:
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);
153 
154     case SENDING_ACK:
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);
163     }
164     abort();
165 }
166 
bitbang_i2c_init(i2c_bus * bus)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 }
180 
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 
bitbang_i2c_gpio_set(void * opaque,int irq,int level)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     }
198 }
199 
gpio_i2c_init(SysBusDevice * dev)200 static int gpio_i2c_init(SysBusDevice *dev)
201 {
202     GPIOI2CState *s = FROM_SYSBUS(GPIOI2CState, dev);
203     i2c_bus *bus;
204 
205     sysbus_init_mmio(dev, 0x0, 0);
206 
207     bus = i2c_init_bus(&dev->qdev, "i2c");
208     s->bitbang = bitbang_i2c_init(bus);
209 
210     qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2);
211     qdev_init_gpio_out(&dev->qdev, &s->out, 1);
212 
213     return 0;
214 }
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 
bitbang_i2c_register(void)223 static void bitbang_i2c_register(void)
224 {
225     sysbus_register_withprop(&gpio_i2c_info);
226 }
227 
228 device_init(bitbang_i2c_register)
229