Statistics
| Branch: | Revision:

root / hw / bitbang_i2c.c @ 78971d57

History | View | Annotate | Download (4.5 kB)

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 "i2c.h"
11
#include "sysbus.h"
12

    
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;
35

    
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;
46

    
47
static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c)
48
{
49
    if (i2c->current_addr >= 0)
50
        i2c_end_transfer(i2c->bus);
51
    i2c->current_addr = -1;
52
    i2c->state = STOPPED;
53
}
54

    
55
static void bitbang_i2c_gpio_set(void *opaque, int irq, int level)
56
{
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;
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

    
91
    switch (i2c->state) {
92
    case STOPPED:
93
        if (data_goes_down && clock == 1)
94
            i2c->state = INITIALIZING;
95
        break;
96

    
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;
103

    
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;
112

    
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;
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;
139

    
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;
150
    }
151

    
152
    i2c->last_data = data;
153
    i2c->last_clock = clock;
154
}
155

    
156
static int bitbang_i2c_init(SysBusDevice *dev)
157
{
158
    bitbang_i2c_interface *s = FROM_SYSBUS(bitbang_i2c_interface, dev);
159
    i2c_bus *bus;
160

    
161
    sysbus_init_mmio(dev, 0x0, 0);
162

    
163
    bus = i2c_init_bus(&dev->qdev, "i2c");
164
    s->bus = bus;
165

    
166
    s->last_data = 1;
167
    s->last_clock = 1;
168

    
169
    qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2);
170
    qdev_init_gpio_out(&dev->qdev, &s->out, 1);
171

    
172
    return 0;
173
}
174

    
175
static void bitbang_i2c_register(void)
176
{
177
    sysbus_register_dev("bitbang_i2c",
178
        sizeof(bitbang_i2c_interface), bitbang_i2c_init);
179
}
180

    
181
device_init(bitbang_i2c_register)