Add missing linefeed in error message
[armpft.git] / hw / bitbang_i2c.c
blob9ab04cc42e2a20d99d3c371c786d5b5c8d98269c
1 /*
2 * Bit-Bang i2c emulation extracted from
3 * Marvell MV88W8618 / Freecom MusicPal emulation.
5 * Copyright (c) 2008 Jan Kiszka
7 * This code is licenced under the GNU GPL v2.
8 */
9 #include "hw.h"
10 #include "i2c.h"
11 #include "sysbus.h"
13 typedef enum bitbang_i2c_state {
14 STOPPED = 0,
15 INITIALIZING,
16 SENDING_BIT7,
17 SENDING_BIT6,
18 SENDING_BIT5,
19 SENDING_BIT4,
20 SENDING_BIT3,
21 SENDING_BIT2,
22 SENDING_BIT1,
23 SENDING_BIT0,
24 WAITING_FOR_ACK,
25 RECEIVING_BIT7,
26 RECEIVING_BIT6,
27 RECEIVING_BIT5,
28 RECEIVING_BIT4,
29 RECEIVING_BIT3,
30 RECEIVING_BIT2,
31 RECEIVING_BIT1,
32 RECEIVING_BIT0,
33 SENDING_ACK
34 } bitbang_i2c_state;
36 typedef struct bitbang_i2c_interface {
37 SysBusDevice busdev;
38 i2c_bus *bus;
39 bitbang_i2c_state state;
40 int last_data;
41 int last_clock;
42 uint8_t buffer;
43 int current_addr;
44 qemu_irq out;
45 } bitbang_i2c_interface;
47 static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c)
49 if (i2c->current_addr >= 0)
50 i2c_end_transfer(i2c->bus);
51 i2c->current_addr = -1;
52 i2c->state = STOPPED;
55 static void bitbang_i2c_gpio_set(void *opaque, int irq, int level)
57 bitbang_i2c_interface *i2c = opaque;
58 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;
65 /* get pins states */
66 data = i2c->last_data;
67 clock = i2c->last_clock;
69 if (irq == 0)
70 data = level;
71 if (irq == 1)
72 clock = level;
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;
80 if (data_goes_up == 0 && data_goes_down == 0 &&
81 clock_goes_up == 0 && clock_goes_down == 0)
82 return;
84 if (!i2c)
85 return;
87 if ((RECEIVING_BIT7 > i2c->state && i2c->state > RECEIVING_BIT0)
88 || i2c->state == WAITING_FOR_ACK)
89 qemu_set_irq(i2c->out, 0);
91 switch (i2c->state) {
92 case STOPPED:
93 if (data_goes_down && clock == 1)
94 i2c->state = INITIALIZING;
95 break;
97 case INITIALIZING:
98 if (clock_goes_down && data == 0)
99 i2c->state = SENDING_BIT7;
100 else
101 bitbang_i2c_enter_stop(i2c);
102 break;
104 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;
113 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;
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;
140 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;
152 i2c->last_data = data;
153 i2c->last_clock = clock;
156 static void bitbang_i2c_init(SysBusDevice *dev)
158 bitbang_i2c_interface *s = FROM_SYSBUS(bitbang_i2c_interface, dev);
159 i2c_bus *bus;
161 sysbus_init_mmio(dev, 0x0, 0);
163 bus = i2c_init_bus(&dev->qdev, "i2c");
164 s->bus = bus;
166 s->last_data = 1;
167 s->last_clock = 1;
169 qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2);
170 qdev_init_gpio_out(&dev->qdev, &s->out, 1);
173 static void bitbang_i2c_register(void)
175 sysbus_register_dev("bitbang_i2c",
176 sizeof(bitbang_i2c_interface), bitbang_i2c_init);
179 device_init(bitbang_i2c_register)