root / hw / i2c.c @ a1bc20df
History | View | Annotate | Download (5.2 kB)
1 |
/*
|
---|---|
2 |
* QEMU I2C bus interface.
|
3 |
*
|
4 |
* Copyright (c) 2007 CodeSourcery.
|
5 |
* Written by Paul Brook
|
6 |
*
|
7 |
* This code is licensed under the LGPL.
|
8 |
*/
|
9 |
|
10 |
#include "i2c.h" |
11 |
|
12 |
struct i2c_bus
|
13 |
{ |
14 |
BusState qbus; |
15 |
I2CSlave *current_dev; |
16 |
I2CSlave *dev; |
17 |
uint8_t saved_address; |
18 |
}; |
19 |
|
20 |
static Property i2c_props[] = {
|
21 |
DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), |
22 |
DEFINE_PROP_END_OF_LIST(), |
23 |
}; |
24 |
|
25 |
#define TYPE_I2C_BUS "i2c-bus" |
26 |
#define I2C_BUS(obj) OBJECT_CHECK(i2c_bus, (obj), TYPE_I2C_BUS)
|
27 |
|
28 |
static const TypeInfo i2c_bus_info = { |
29 |
.name = TYPE_I2C_BUS, |
30 |
.parent = TYPE_BUS, |
31 |
.instance_size = sizeof(i2c_bus),
|
32 |
}; |
33 |
|
34 |
static void i2c_bus_pre_save(void *opaque) |
35 |
{ |
36 |
i2c_bus *bus = opaque; |
37 |
|
38 |
bus->saved_address = bus->current_dev ? bus->current_dev->address : -1;
|
39 |
} |
40 |
|
41 |
static int i2c_bus_post_load(void *opaque, int version_id) |
42 |
{ |
43 |
i2c_bus *bus = opaque; |
44 |
|
45 |
/* The bus is loaded before attached devices, so load and save the
|
46 |
current device id. Devices will check themselves as loaded. */
|
47 |
bus->current_dev = NULL;
|
48 |
return 0; |
49 |
} |
50 |
|
51 |
static const VMStateDescription vmstate_i2c_bus = { |
52 |
.name = "i2c_bus",
|
53 |
.version_id = 1,
|
54 |
.minimum_version_id = 1,
|
55 |
.minimum_version_id_old = 1,
|
56 |
.pre_save = i2c_bus_pre_save, |
57 |
.post_load = i2c_bus_post_load, |
58 |
.fields = (VMStateField []) { |
59 |
VMSTATE_UINT8(saved_address, i2c_bus), |
60 |
VMSTATE_END_OF_LIST() |
61 |
} |
62 |
}; |
63 |
|
64 |
/* Create a new I2C bus. */
|
65 |
i2c_bus *i2c_init_bus(DeviceState *parent, const char *name) |
66 |
{ |
67 |
i2c_bus *bus; |
68 |
|
69 |
bus = FROM_QBUS(i2c_bus, qbus_create(TYPE_I2C_BUS, parent, name)); |
70 |
vmstate_register(NULL, -1, &vmstate_i2c_bus, bus); |
71 |
return bus;
|
72 |
} |
73 |
|
74 |
void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
|
75 |
{ |
76 |
dev->address = address; |
77 |
} |
78 |
|
79 |
/* Return nonzero if bus is busy. */
|
80 |
int i2c_bus_busy(i2c_bus *bus)
|
81 |
{ |
82 |
return bus->current_dev != NULL; |
83 |
} |
84 |
|
85 |
/* Returns non-zero if the address is not valid. */
|
86 |
/* TODO: Make this handle multiple masters. */
|
87 |
int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv) |
88 |
{ |
89 |
BusChild *kid; |
90 |
I2CSlave *slave = NULL;
|
91 |
I2CSlaveClass *sc; |
92 |
|
93 |
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { |
94 |
DeviceState *qdev = kid->child; |
95 |
I2CSlave *candidate = I2C_SLAVE_FROM_QDEV(qdev); |
96 |
if (candidate->address == address) {
|
97 |
slave = candidate; |
98 |
break;
|
99 |
} |
100 |
} |
101 |
|
102 |
if (!slave) {
|
103 |
return 1; |
104 |
} |
105 |
|
106 |
sc = I2C_SLAVE_GET_CLASS(slave); |
107 |
/* If the bus is already busy, assume this is a repeated
|
108 |
start condition. */
|
109 |
bus->current_dev = slave; |
110 |
if (sc->event) {
|
111 |
sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND); |
112 |
} |
113 |
return 0; |
114 |
} |
115 |
|
116 |
void i2c_end_transfer(i2c_bus *bus)
|
117 |
{ |
118 |
I2CSlave *dev = bus->current_dev; |
119 |
I2CSlaveClass *sc; |
120 |
|
121 |
if (!dev) {
|
122 |
return;
|
123 |
} |
124 |
|
125 |
sc = I2C_SLAVE_GET_CLASS(dev); |
126 |
if (sc->event) {
|
127 |
sc->event(dev, I2C_FINISH); |
128 |
} |
129 |
|
130 |
bus->current_dev = NULL;
|
131 |
} |
132 |
|
133 |
int i2c_send(i2c_bus *bus, uint8_t data)
|
134 |
{ |
135 |
I2CSlave *dev = bus->current_dev; |
136 |
I2CSlaveClass *sc; |
137 |
|
138 |
if (!dev) {
|
139 |
return -1; |
140 |
} |
141 |
|
142 |
sc = I2C_SLAVE_GET_CLASS(dev); |
143 |
if (sc->send) {
|
144 |
return sc->send(dev, data);
|
145 |
} |
146 |
|
147 |
return -1; |
148 |
} |
149 |
|
150 |
int i2c_recv(i2c_bus *bus)
|
151 |
{ |
152 |
I2CSlave *dev = bus->current_dev; |
153 |
I2CSlaveClass *sc; |
154 |
|
155 |
if (!dev) {
|
156 |
return -1; |
157 |
} |
158 |
|
159 |
sc = I2C_SLAVE_GET_CLASS(dev); |
160 |
if (sc->recv) {
|
161 |
return sc->recv(dev);
|
162 |
} |
163 |
|
164 |
return -1; |
165 |
} |
166 |
|
167 |
void i2c_nack(i2c_bus *bus)
|
168 |
{ |
169 |
I2CSlave *dev = bus->current_dev; |
170 |
I2CSlaveClass *sc; |
171 |
|
172 |
if (!dev) {
|
173 |
return;
|
174 |
} |
175 |
|
176 |
sc = I2C_SLAVE_GET_CLASS(dev); |
177 |
if (sc->event) {
|
178 |
sc->event(dev, I2C_NACK); |
179 |
} |
180 |
} |
181 |
|
182 |
static int i2c_slave_post_load(void *opaque, int version_id) |
183 |
{ |
184 |
I2CSlave *dev = opaque; |
185 |
i2c_bus *bus; |
186 |
bus = FROM_QBUS(i2c_bus, qdev_get_parent_bus(&dev->qdev)); |
187 |
if (bus->saved_address == dev->address) {
|
188 |
bus->current_dev = dev; |
189 |
} |
190 |
return 0; |
191 |
} |
192 |
|
193 |
const VMStateDescription vmstate_i2c_slave = {
|
194 |
.name = "I2CSlave",
|
195 |
.version_id = 1,
|
196 |
.minimum_version_id = 1,
|
197 |
.minimum_version_id_old = 1,
|
198 |
.post_load = i2c_slave_post_load, |
199 |
.fields = (VMStateField []) { |
200 |
VMSTATE_UINT8(address, I2CSlave), |
201 |
VMSTATE_END_OF_LIST() |
202 |
} |
203 |
}; |
204 |
|
205 |
static int i2c_slave_qdev_init(DeviceState *dev) |
206 |
{ |
207 |
I2CSlave *s = I2C_SLAVE_FROM_QDEV(dev); |
208 |
I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s); |
209 |
|
210 |
return sc->init(s);
|
211 |
} |
212 |
|
213 |
DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr) |
214 |
{ |
215 |
DeviceState *dev; |
216 |
|
217 |
dev = qdev_create(&bus->qbus, name); |
218 |
qdev_prop_set_uint8(dev, "address", addr);
|
219 |
qdev_init_nofail(dev); |
220 |
return dev;
|
221 |
} |
222 |
|
223 |
static void i2c_slave_class_init(ObjectClass *klass, void *data) |
224 |
{ |
225 |
DeviceClass *k = DEVICE_CLASS(klass); |
226 |
k->init = i2c_slave_qdev_init; |
227 |
k->bus_type = TYPE_I2C_BUS; |
228 |
k->props = i2c_props; |
229 |
} |
230 |
|
231 |
static TypeInfo i2c_slave_type_info = {
|
232 |
.name = TYPE_I2C_SLAVE, |
233 |
.parent = TYPE_DEVICE, |
234 |
.instance_size = sizeof(I2CSlave),
|
235 |
.abstract = true,
|
236 |
.class_size = sizeof(I2CSlaveClass),
|
237 |
.class_init = i2c_slave_class_init, |
238 |
}; |
239 |
|
240 |
static void i2c_slave_register_types(void) |
241 |
{ |
242 |
type_register_static(&i2c_bus_info); |
243 |
type_register_static(&i2c_slave_type_info); |
244 |
} |
245 |
|
246 |
type_init(i2c_slave_register_types) |