]>
Commit | Line | Data |
---|---|---|
5fafdf24 | 1 | /* |
0ff596d0 PB |
2 | * QEMU SMBus device emulation. |
3 | * | |
93198b6c CM |
4 | * This code is a helper for SMBus device emulation. It implements an |
5 | * I2C device inteface and runs the SMBus protocol from the device | |
6 | * point of view and maps those to simple calls to emulate. | |
7 | * | |
0ff596d0 PB |
8 | * Copyright (c) 2007 CodeSourcery. |
9 | * Written by Paul Brook | |
10 | * | |
8e31bf38 | 11 | * This code is licensed under the LGPL. |
0ff596d0 PB |
12 | */ |
13 | ||
14 | /* TODO: Implement PEC. */ | |
15 | ||
0430891c | 16 | #include "qemu/osdep.h" |
83c9f4ca | 17 | #include "hw/hw.h" |
0d09e41a | 18 | #include "hw/i2c/i2c.h" |
93198b6c | 19 | #include "hw/i2c/smbus_slave.h" |
0ff596d0 PB |
20 | |
21 | //#define DEBUG_SMBUS 1 | |
22 | ||
23 | #ifdef DEBUG_SMBUS | |
001faf32 BS |
24 | #define DPRINTF(fmt, ...) \ |
25 | do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) | |
26 | #define BADF(fmt, ...) \ | |
27 | do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) | |
0ff596d0 | 28 | #else |
001faf32 BS |
29 | #define DPRINTF(fmt, ...) do {} while(0) |
30 | #define BADF(fmt, ...) \ | |
31 | do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) | |
0ff596d0 PB |
32 | #endif |
33 | ||
34 | enum { | |
35 | SMBUS_IDLE, | |
36 | SMBUS_WRITE_DATA, | |
0ff596d0 PB |
37 | SMBUS_READ_DATA, |
38 | SMBUS_DONE, | |
39 | SMBUS_CONFUSED = -1 | |
40 | }; | |
41 | ||
42 | static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) | |
43 | { | |
b5ea9327 | 44 | SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); |
1ea96673 | 45 | |
0ff596d0 | 46 | DPRINTF("Quick Command %d\n", recv); |
b5ea9327 AL |
47 | if (sc->quick_cmd) { |
48 | sc->quick_cmd(dev, recv); | |
49 | } | |
0ff596d0 PB |
50 | } |
51 | ||
52 | static void smbus_do_write(SMBusDevice *dev) | |
53 | { | |
b5ea9327 | 54 | SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); |
1ea96673 | 55 | |
9cf27d74 CM |
56 | DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len); |
57 | if (sc->write_data) { | |
58 | sc->write_data(dev, dev->data_buf, dev->data_len); | |
0ff596d0 PB |
59 | } |
60 | } | |
61 | ||
d307c28c | 62 | static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) |
0ff596d0 | 63 | { |
b5ea9327 | 64 | SMBusDevice *dev = SMBUS_DEVICE(s); |
1ea96673 | 65 | |
0ff596d0 PB |
66 | switch (event) { |
67 | case I2C_START_SEND: | |
68 | switch (dev->mode) { | |
69 | case SMBUS_IDLE: | |
70 | DPRINTF("Incoming data\n"); | |
71 | dev->mode = SMBUS_WRITE_DATA; | |
72 | break; | |
8b38e532 | 73 | |
0ff596d0 PB |
74 | default: |
75 | BADF("Unexpected send start condition in state %d\n", dev->mode); | |
76 | dev->mode = SMBUS_CONFUSED; | |
77 | break; | |
78 | } | |
79 | break; | |
80 | ||
81 | case I2C_START_RECV: | |
82 | switch (dev->mode) { | |
83 | case SMBUS_IDLE: | |
84 | DPRINTF("Read mode\n"); | |
031ac498 | 85 | dev->mode = SMBUS_READ_DATA; |
0ff596d0 | 86 | break; |
8b38e532 | 87 | |
0ff596d0 PB |
88 | case SMBUS_WRITE_DATA: |
89 | if (dev->data_len == 0) { | |
90 | BADF("Read after write with no data\n"); | |
91 | dev->mode = SMBUS_CONFUSED; | |
92 | } else { | |
9cf27d74 | 93 | smbus_do_write(dev); |
0ff596d0 | 94 | DPRINTF("Read mode\n"); |
0ff596d0 PB |
95 | dev->mode = SMBUS_READ_DATA; |
96 | } | |
97 | break; | |
8b38e532 | 98 | |
0ff596d0 PB |
99 | default: |
100 | BADF("Unexpected recv start condition in state %d\n", dev->mode); | |
101 | dev->mode = SMBUS_CONFUSED; | |
102 | break; | |
103 | } | |
104 | break; | |
105 | ||
106 | case I2C_FINISH: | |
905cec6d CM |
107 | if (dev->data_len == 0) { |
108 | if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) { | |
109 | smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA); | |
110 | } | |
111 | } else { | |
112 | switch (dev->mode) { | |
113 | case SMBUS_WRITE_DATA: | |
114 | smbus_do_write(dev); | |
115 | break; | |
116 | ||
117 | case SMBUS_READ_DATA: | |
118 | BADF("Unexpected stop during receive\n"); | |
119 | break; | |
120 | ||
121 | default: | |
122 | /* Nothing to do. */ | |
123 | break; | |
124 | } | |
0ff596d0 PB |
125 | } |
126 | dev->mode = SMBUS_IDLE; | |
127 | dev->data_len = 0; | |
128 | break; | |
129 | ||
130 | case I2C_NACK: | |
131 | switch (dev->mode) { | |
132 | case SMBUS_DONE: | |
133 | /* Nothing to do. */ | |
134 | break; | |
8b38e532 | 135 | |
0ff596d0 PB |
136 | case SMBUS_READ_DATA: |
137 | dev->mode = SMBUS_DONE; | |
138 | break; | |
8b38e532 | 139 | |
0ff596d0 PB |
140 | default: |
141 | BADF("Unexpected NACK in state %d\n", dev->mode); | |
142 | dev->mode = SMBUS_CONFUSED; | |
143 | break; | |
144 | } | |
145 | } | |
d307c28c CM |
146 | |
147 | return 0; | |
0ff596d0 PB |
148 | } |
149 | ||
2ac4c5f4 | 150 | static uint8_t smbus_i2c_recv(I2CSlave *s) |
0ff596d0 | 151 | { |
b5ea9327 AL |
152 | SMBusDevice *dev = SMBUS_DEVICE(s); |
153 | SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); | |
031ac498 | 154 | uint8_t ret = 0xff; |
0ff596d0 PB |
155 | |
156 | switch (dev->mode) { | |
031ac498 | 157 | case SMBUS_READ_DATA: |
b5ea9327 AL |
158 | if (sc->receive_byte) { |
159 | ret = sc->receive_byte(dev); | |
0ff596d0 PB |
160 | } |
161 | DPRINTF("Read data %02x\n", ret); | |
162 | break; | |
8b38e532 | 163 | |
0ff596d0 PB |
164 | default: |
165 | BADF("Unexpected read in state %d\n", dev->mode); | |
166 | dev->mode = SMBUS_CONFUSED; | |
0ff596d0 PB |
167 | break; |
168 | } | |
8b38e532 | 169 | |
0ff596d0 PB |
170 | return ret; |
171 | } | |
172 | ||
9e07bdf8 | 173 | static int smbus_i2c_send(I2CSlave *s, uint8_t data) |
0ff596d0 | 174 | { |
b5ea9327 | 175 | SMBusDevice *dev = SMBUS_DEVICE(s); |
1ea96673 | 176 | |
0ff596d0 PB |
177 | switch (dev->mode) { |
178 | case SMBUS_WRITE_DATA: | |
179 | DPRINTF("Write data %02x\n", data); | |
629457a1 CM |
180 | if (dev->data_len >= sizeof(dev->data_buf)) { |
181 | BADF("Too many bytes sent\n"); | |
182 | } else { | |
183 | dev->data_buf[dev->data_len++] = data; | |
184 | } | |
0ff596d0 | 185 | break; |
8b38e532 | 186 | |
0ff596d0 PB |
187 | default: |
188 | BADF("Unexpected write in state %d\n", dev->mode); | |
189 | break; | |
190 | } | |
8b38e532 | 191 | |
0ff596d0 PB |
192 | return 0; |
193 | } | |
194 | ||
b5ea9327 AL |
195 | static void smbus_device_class_init(ObjectClass *klass, void *data) |
196 | { | |
197 | I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); | |
198 | ||
b5ea9327 AL |
199 | sc->event = smbus_i2c_event; |
200 | sc->recv = smbus_i2c_recv; | |
201 | sc->send = smbus_i2c_send; | |
202 | } | |
203 | ||
8c43a6f0 | 204 | static const TypeInfo smbus_device_type_info = { |
b5ea9327 AL |
205 | .name = TYPE_SMBUS_DEVICE, |
206 | .parent = TYPE_I2C_SLAVE, | |
207 | .instance_size = sizeof(SMBusDevice), | |
208 | .abstract = true, | |
209 | .class_size = sizeof(SMBusDeviceClass), | |
210 | .class_init = smbus_device_class_init, | |
211 | }; | |
212 | ||
83f7d43a | 213 | static void smbus_device_register_types(void) |
b5ea9327 AL |
214 | { |
215 | type_register_static(&smbus_device_type_info); | |
216 | } | |
217 | ||
83f7d43a | 218 | type_init(smbus_device_register_types) |