]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/blob - hw/i2c.c
i2c: smbus: convert to QEMU Object Model
[lisovros/qemu_apohw.git] / hw / i2c.c
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 struct BusInfo i2c_bus_info = {
21     .name = "I2C",
22     .size = sizeof(i2c_bus),
23     .props = (Property[]) {
24         DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0),
25         DEFINE_PROP_END_OF_LIST(),
26     }
27 };
28
29 static void i2c_bus_pre_save(void *opaque)
30 {
31     i2c_bus *bus = opaque;
32
33     bus->saved_address = bus->current_dev ? bus->current_dev->address : -1;
34 }
35
36 static int i2c_bus_post_load(void *opaque, int version_id)
37 {
38     i2c_bus *bus = opaque;
39
40     /* The bus is loaded before attached devices, so load and save the
41        current device id.  Devices will check themselves as loaded.  */
42     bus->current_dev = NULL;
43     return 0;
44 }
45
46 static const VMStateDescription vmstate_i2c_bus = {
47     .name = "i2c_bus",
48     .version_id = 1,
49     .minimum_version_id = 1,
50     .minimum_version_id_old = 1,
51     .pre_save = i2c_bus_pre_save,
52     .post_load = i2c_bus_post_load,
53     .fields      = (VMStateField []) {
54         VMSTATE_UINT8(saved_address, i2c_bus),
55         VMSTATE_END_OF_LIST()
56     }
57 };
58
59 /* Create a new I2C bus.  */
60 i2c_bus *i2c_init_bus(DeviceState *parent, const char *name)
61 {
62     i2c_bus *bus;
63
64     bus = FROM_QBUS(i2c_bus, qbus_create(&i2c_bus_info, parent, name));
65     vmstate_register(NULL, -1, &vmstate_i2c_bus, bus);
66     return bus;
67 }
68
69 void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
70 {
71     dev->address = address;
72 }
73
74 /* Return nonzero if bus is busy.  */
75 int i2c_bus_busy(i2c_bus *bus)
76 {
77     return bus->current_dev != NULL;
78 }
79
80 /* Returns non-zero if the address is not valid.  */
81 /* TODO: Make this handle multiple masters.  */
82 int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
83 {
84     DeviceState *qdev;
85     I2CSlave *slave = NULL;
86     I2CSlaveClass *sc;
87
88     QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
89         I2CSlave *candidate = I2C_SLAVE_FROM_QDEV(qdev);
90         if (candidate->address == address) {
91             slave = candidate;
92             break;
93         }
94     }
95
96     if (!slave) {
97         return 1;
98     }
99
100     sc = I2C_SLAVE_GET_CLASS(slave);
101     /* If the bus is already busy, assume this is a repeated
102        start condition.  */
103     bus->current_dev = slave;
104     if (sc->event) {
105         sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND);
106     }
107     return 0;
108 }
109
110 void i2c_end_transfer(i2c_bus *bus)
111 {
112     I2CSlave *dev = bus->current_dev;
113     I2CSlaveClass *sc;
114
115     if (!dev) {
116         return;
117     }
118
119     sc = I2C_SLAVE_GET_CLASS(dev);
120     if (sc->event) {
121         sc->event(dev, I2C_FINISH);
122     }
123
124     bus->current_dev = NULL;
125 }
126
127 int i2c_send(i2c_bus *bus, uint8_t data)
128 {
129     I2CSlave *dev = bus->current_dev;
130     I2CSlaveClass *sc;
131
132     if (!dev) {
133         return -1;
134     }
135
136     sc = I2C_SLAVE_GET_CLASS(dev);
137     if (sc->send) {
138         return sc->send(dev, data);
139     }
140
141     return -1;
142 }
143
144 int i2c_recv(i2c_bus *bus)
145 {
146     I2CSlave *dev = bus->current_dev;
147     I2CSlaveClass *sc;
148
149     if (!dev) {
150         return -1;
151     }
152
153     sc = I2C_SLAVE_GET_CLASS(dev);
154     if (sc->recv) {
155         return sc->recv(dev);
156     }
157
158     return -1;
159 }
160
161 void i2c_nack(i2c_bus *bus)
162 {
163     I2CSlave *dev = bus->current_dev;
164     I2CSlaveClass *sc;
165
166     if (!dev) {
167         return;
168     }
169
170     sc = I2C_SLAVE_GET_CLASS(dev);
171     if (sc->event) {
172         sc->event(dev, I2C_NACK);
173     }
174 }
175
176 static int i2c_slave_post_load(void *opaque, int version_id)
177 {
178     I2CSlave *dev = opaque;
179     i2c_bus *bus;
180     bus = FROM_QBUS(i2c_bus, qdev_get_parent_bus(&dev->qdev));
181     if (bus->saved_address == dev->address) {
182         bus->current_dev = dev;
183     }
184     return 0;
185 }
186
187 const VMStateDescription vmstate_i2c_slave = {
188     .name = "I2CSlave",
189     .version_id = 1,
190     .minimum_version_id = 1,
191     .minimum_version_id_old = 1,
192     .post_load = i2c_slave_post_load,
193     .fields      = (VMStateField []) {
194         VMSTATE_UINT8(address, I2CSlave),
195         VMSTATE_END_OF_LIST()
196     }
197 };
198
199 static int i2c_slave_qdev_init(DeviceState *dev, DeviceInfo *base)
200 {
201     I2CSlave *s = I2C_SLAVE_FROM_QDEV(dev);
202     I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s);
203
204     return sc->init(s);
205 }
206
207 void i2c_register_slave_subclass(DeviceInfo *info, const char *parent)
208 {
209     assert(info->size >= sizeof(I2CSlave));
210     info->init = i2c_slave_qdev_init;
211     info->bus_info = &i2c_bus_info;
212     qdev_register_subclass(info, parent);
213 }
214
215 void i2c_register_slave(DeviceInfo *info)
216 {
217     i2c_register_slave_subclass(info, TYPE_I2C_SLAVE);
218 }
219
220 DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr)
221 {
222     DeviceState *dev;
223
224     dev = qdev_create(&bus->qbus, name);
225     qdev_prop_set_uint8(dev, "address", addr);
226     qdev_init_nofail(dev);
227     return dev;
228 }
229
230 static TypeInfo i2c_slave_type_info = {
231     .name = TYPE_I2C_SLAVE,
232     .parent = TYPE_DEVICE,
233     .instance_size = sizeof(I2CSlave),
234     .abstract = true,
235     .class_size = sizeof(I2CSlaveClass),
236 };
237
238 static void i2c_slave_register_devices(void)
239 {
240     type_register_static(&i2c_slave_type_info);
241 }
242
243 device_init(i2c_slave_register_devices);