1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "qemu/osdep.h"
21#include "hw/hw.h"
22#include "hw/i2c/pm_smbus.h"
23#include "hw/i2c/smbus.h"
24
25
26
27#define SMBHSTSTS 0x00
28#define SMBHSTCNT 0x02
29#define SMBHSTCMD 0x03
30#define SMBHSTADD 0x04
31#define SMBHSTDAT0 0x05
32#define SMBHSTDAT1 0x06
33#define SMBBLKDAT 0x07
34
35#define STS_HOST_BUSY (1)
36#define STS_INTR (1<<1)
37#define STS_DEV_ERR (1<<2)
38#define STS_BUS_ERR (1<<3)
39#define STS_FAILED (1<<4)
40#define STS_SMBALERT (1<<5)
41#define STS_INUSE_STS (1<<6)
42#define STS_BYTE_DONE (1<<7)
43
44
45
46
47
48
49#ifdef DEBUG
50# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
51#else
52# define SMBUS_DPRINTF(format, ...) do { } while (0)
53#endif
54
55
56static void smb_transaction(PMSMBus *s)
57{
58 uint8_t prot = (s->smb_ctl >> 2) & 0x07;
59 uint8_t read = s->smb_addr & 0x01;
60 uint8_t cmd = s->smb_cmd;
61 uint8_t addr = s->smb_addr >> 1;
62 I2CBus *bus = s->smbus;
63 int ret;
64
65 assert(s->smb_stat & STS_HOST_BUSY);
66 s->smb_stat &= ~STS_HOST_BUSY;
67
68 SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
69
70 if ((s->smb_stat & STS_DEV_ERR) != 0) {
71 goto error;
72 }
73 switch(prot) {
74 case 0x0:
75 ret = smbus_quick_command(bus, addr, read);
76 goto done;
77 case 0x1:
78 if (read) {
79 ret = smbus_receive_byte(bus, addr);
80 goto data8;
81 } else {
82 ret = smbus_send_byte(bus, addr, cmd);
83 goto done;
84 }
85 case 0x2:
86 if (read) {
87 ret = smbus_read_byte(bus, addr, cmd);
88 goto data8;
89 } else {
90 ret = smbus_write_byte(bus, addr, cmd, s->smb_data0);
91 goto done;
92 }
93 break;
94 case 0x3:
95 if (read) {
96 ret = smbus_read_word(bus, addr, cmd);
97 goto data16;
98 } else {
99 ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
100 goto done;
101 }
102 break;
103 case 0x5:
104 if (read) {
105 ret = smbus_read_block(bus, addr, cmd, s->smb_data);
106 goto data8;
107 } else {
108 ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
109 goto done;
110 }
111 break;
112 default:
113 goto error;
114 }
115 abort();
116
117data16:
118 if (ret < 0) {
119 goto error;
120 }
121 s->smb_data1 = ret >> 8;
122data8:
123 if (ret < 0) {
124 goto error;
125 }
126 s->smb_data0 = ret;
127done:
128 if (ret < 0) {
129 goto error;
130 }
131 s->smb_stat |= STS_BYTE_DONE | STS_INTR;
132 return;
133
134error:
135 s->smb_stat |= STS_DEV_ERR;
136 return;
137
138}
139
140static void smb_transaction_start(PMSMBus *s)
141{
142
143
144 s->smb_stat |= STS_HOST_BUSY;
145}
146
147static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
148 unsigned width)
149{
150 PMSMBus *s = opaque;
151
152 SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx
153 " val=0x%02" PRIx64 "\n", addr, val);
154 switch(addr) {
155 case SMBHSTSTS:
156 s->smb_stat = (~(val & 0xff)) & s->smb_stat;
157 s->smb_index = 0;
158 break;
159 case SMBHSTCNT:
160 s->smb_ctl = val;
161 if (val & 0x40)
162 smb_transaction_start(s);
163 break;
164 case SMBHSTCMD:
165 s->smb_cmd = val;
166 break;
167 case SMBHSTADD:
168 s->smb_addr = val;
169 break;
170 case SMBHSTDAT0:
171 s->smb_data0 = val;
172 break;
173 case SMBHSTDAT1:
174 s->smb_data1 = val;
175 break;
176 case SMBBLKDAT:
177 s->smb_data[s->smb_index++] = val;
178 if (s->smb_index > 31)
179 s->smb_index = 0;
180 break;
181 default:
182 break;
183 }
184}
185
186static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
187{
188 PMSMBus *s = opaque;
189 uint32_t val;
190
191 switch(addr) {
192 case SMBHSTSTS:
193 val = s->smb_stat;
194 if (s->smb_stat & STS_HOST_BUSY) {
195
196 smb_transaction(s);
197 }
198 break;
199 case SMBHSTCNT:
200 s->smb_index = 0;
201 val = s->smb_ctl & 0x1f;
202 break;
203 case SMBHSTCMD:
204 val = s->smb_cmd;
205 break;
206 case SMBHSTADD:
207 val = s->smb_addr;
208 break;
209 case SMBHSTDAT0:
210 val = s->smb_data0;
211 break;
212 case SMBHSTDAT1:
213 val = s->smb_data1;
214 break;
215 case SMBBLKDAT:
216 val = s->smb_data[s->smb_index++];
217 if (s->smb_index > 31)
218 s->smb_index = 0;
219 break;
220 default:
221 val = 0;
222 break;
223 }
224 SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val);
225 return val;
226}
227
228static const MemoryRegionOps pm_smbus_ops = {
229 .read = smb_ioport_readb,
230 .write = smb_ioport_writeb,
231 .valid.min_access_size = 1,
232 .valid.max_access_size = 1,
233 .endianness = DEVICE_LITTLE_ENDIAN,
234};
235
236void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
237{
238 smb->smbus = i2c_init_bus(parent, "i2c");
239 memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb,
240 "pm-smbus", 64);
241}
242