1 | /* |
2 | * PC SMBus implementation |
3 | * splitted from acpi.c |
4 | * |
5 | * Copyright (c) 2006 Fabrice Bellard |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License version 2 as published by the Free Software Foundation. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, see |
18 | * <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "qemu/osdep.h" |
22 | #include "hw/boards.h" |
23 | #include "hw/i2c/pm_smbus.h" |
24 | #include "hw/i2c/smbus_master.h" |
25 | #include "migration/vmstate.h" |
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 | #define SMBAUXCTL 0x0d |
35 | |
36 | #define STS_HOST_BUSY (1 << 0) |
37 | #define STS_INTR (1 << 1) |
38 | #define STS_DEV_ERR (1 << 2) |
39 | #define STS_BUS_ERR (1 << 3) |
40 | #define STS_FAILED (1 << 4) |
41 | #define STS_SMBALERT (1 << 5) |
42 | #define STS_INUSE_STS (1 << 6) |
43 | #define STS_BYTE_DONE (1 << 7) |
44 | /* Signs of successfully transaction end : |
45 | * ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) |
46 | */ |
47 | |
48 | #define CTL_INTREN (1 << 0) |
49 | #define CTL_KILL (1 << 1) |
50 | #define CTL_LAST_BYTE (1 << 5) |
51 | #define CTL_START (1 << 6) |
52 | #define CTL_PEC_EN (1 << 7) |
53 | #define CTL_RETURN_MASK 0x1f |
54 | |
55 | #define PROT_QUICK 0 |
56 | #define PROT_BYTE 1 |
57 | #define PROT_BYTE_DATA 2 |
58 | #define PROT_WORD_DATA 3 |
59 | #define PROT_PROC_CALL 4 |
60 | #define PROT_BLOCK_DATA 5 |
61 | #define PROT_I2C_BLOCK_READ 6 |
62 | |
63 | #define AUX_PEC (1 << 0) |
64 | #define AUX_BLK (1 << 1) |
65 | #define AUX_MASK 0x3 |
66 | |
67 | /*#define DEBUG*/ |
68 | |
69 | #ifdef DEBUG |
70 | # define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) |
71 | #else |
72 | # define SMBUS_DPRINTF(format, ...) do { } while (0) |
73 | #endif |
74 | |
75 | |
76 | static void smb_transaction(PMSMBus *s) |
77 | { |
78 | uint8_t prot = (s->smb_ctl >> 2) & 0x07; |
79 | uint8_t read = s->smb_addr & 0x01; |
80 | uint8_t cmd = s->smb_cmd; |
81 | uint8_t addr = s->smb_addr >> 1; |
82 | I2CBus *bus = s->smbus; |
83 | int ret; |
84 | |
85 | SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n" , addr, prot); |
86 | /* Transaction isn't exec if STS_DEV_ERR bit set */ |
87 | if ((s->smb_stat & STS_DEV_ERR) != 0) { |
88 | goto error; |
89 | } |
90 | |
91 | switch(prot) { |
92 | case PROT_QUICK: |
93 | ret = smbus_quick_command(bus, addr, read); |
94 | goto done; |
95 | case PROT_BYTE: |
96 | if (read) { |
97 | ret = smbus_receive_byte(bus, addr); |
98 | goto data8; |
99 | } else { |
100 | ret = smbus_send_byte(bus, addr, cmd); |
101 | goto done; |
102 | } |
103 | case PROT_BYTE_DATA: |
104 | if (read) { |
105 | ret = smbus_read_byte(bus, addr, cmd); |
106 | goto data8; |
107 | } else { |
108 | ret = smbus_write_byte(bus, addr, cmd, s->smb_data0); |
109 | goto done; |
110 | } |
111 | break; |
112 | case PROT_WORD_DATA: |
113 | if (read) { |
114 | ret = smbus_read_word(bus, addr, cmd); |
115 | goto data16; |
116 | } else { |
117 | ret = smbus_write_word(bus, addr, cmd, |
118 | (s->smb_data1 << 8) | s->smb_data0); |
119 | goto done; |
120 | } |
121 | break; |
122 | case PROT_I2C_BLOCK_READ: |
123 | /* According to the Linux i2c-i801 driver: |
124 | * NB: page 240 of ICH5 datasheet shows that the R/#W |
125 | * bit should be cleared here, even when reading. |
126 | * However if SPD Write Disable is set (Lynx Point and later), |
127 | * the read will fail if we don't set the R/#W bit. |
128 | * So at least Linux may or may not set the read bit here. |
129 | * So just ignore the read bit for this command. |
130 | */ |
131 | if (i2c_start_transfer(bus, addr, 0)) { |
132 | goto error; |
133 | } |
134 | ret = i2c_send(bus, s->smb_data1); |
135 | if (ret) { |
136 | goto error; |
137 | } |
138 | if (i2c_start_transfer(bus, addr, 1)) { |
139 | goto error; |
140 | } |
141 | s->in_i2c_block_read = true; |
142 | s->smb_blkdata = i2c_recv(s->smbus); |
143 | s->op_done = false; |
144 | s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; |
145 | goto out; |
146 | |
147 | case PROT_BLOCK_DATA: |
148 | if (read) { |
149 | ret = smbus_read_block(bus, addr, cmd, s->smb_data, |
150 | sizeof(s->smb_data), !s->i2c_enable, |
151 | !s->i2c_enable); |
152 | if (ret < 0) { |
153 | goto error; |
154 | } |
155 | s->smb_index = 0; |
156 | s->op_done = false; |
157 | if (s->smb_auxctl & AUX_BLK) { |
158 | s->smb_stat |= STS_INTR; |
159 | } else { |
160 | s->smb_blkdata = s->smb_data[0]; |
161 | s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; |
162 | } |
163 | s->smb_data0 = ret; |
164 | goto out; |
165 | } else { |
166 | if (s->smb_auxctl & AUX_BLK) { |
167 | if (s->smb_index != s->smb_data0) { |
168 | s->smb_index = 0; |
169 | goto error; |
170 | } |
171 | /* Data is already all written to the queue, just do |
172 | the operation. */ |
173 | s->smb_index = 0; |
174 | ret = smbus_write_block(bus, addr, cmd, s->smb_data, |
175 | s->smb_data0, !s->i2c_enable); |
176 | if (ret < 0) { |
177 | goto error; |
178 | } |
179 | s->op_done = true; |
180 | s->smb_stat |= STS_INTR; |
181 | s->smb_stat &= ~STS_HOST_BUSY; |
182 | } else { |
183 | s->op_done = false; |
184 | s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; |
185 | s->smb_data[0] = s->smb_blkdata; |
186 | s->smb_index = 0; |
187 | ret = 0; |
188 | } |
189 | goto out; |
190 | } |
191 | break; |
192 | default: |
193 | goto error; |
194 | } |
195 | abort(); |
196 | |
197 | data16: |
198 | if (ret < 0) { |
199 | goto error; |
200 | } |
201 | s->smb_data1 = ret >> 8; |
202 | data8: |
203 | if (ret < 0) { |
204 | goto error; |
205 | } |
206 | s->smb_data0 = ret; |
207 | done: |
208 | if (ret < 0) { |
209 | goto error; |
210 | } |
211 | s->smb_stat |= STS_INTR; |
212 | out: |
213 | return; |
214 | |
215 | error: |
216 | s->smb_stat |= STS_DEV_ERR; |
217 | return; |
218 | } |
219 | |
220 | static void smb_transaction_start(PMSMBus *s) |
221 | { |
222 | if (s->smb_ctl & CTL_INTREN) { |
223 | smb_transaction(s); |
224 | s->start_transaction_on_status_read = false; |
225 | } else { |
226 | /* Do not execute immediately the command; it will be |
227 | * executed when guest will read SMB_STAT register. This |
228 | * is to work around a bug in AMIBIOS (that is working |
229 | * around another bug in some specific hardware) where |
230 | * it waits for STS_HOST_BUSY to be set before waiting |
231 | * checking for status. If STS_HOST_BUSY doesn't get |
232 | * set, it gets stuck. */ |
233 | s->smb_stat |= STS_HOST_BUSY; |
234 | s->start_transaction_on_status_read = true; |
235 | } |
236 | } |
237 | |
238 | static bool |
239 | smb_irq_value(PMSMBus *s) |
240 | { |
241 | return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN); |
242 | } |
243 | |
244 | static bool |
245 | smb_byte_by_byte(PMSMBus *s) |
246 | { |
247 | if (s->op_done) { |
248 | return false; |
249 | } |
250 | if (s->in_i2c_block_read) { |
251 | return true; |
252 | } |
253 | return !(s->smb_auxctl & AUX_BLK); |
254 | } |
255 | |
256 | static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, |
257 | unsigned width) |
258 | { |
259 | PMSMBus *s = opaque; |
260 | uint8_t clear_byte_done; |
261 | |
262 | SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx |
263 | " val=0x%02" PRIx64 "\n" , addr, val); |
264 | switch(addr) { |
265 | case SMBHSTSTS: |
266 | clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; |
267 | s->smb_stat &= ~(val & ~STS_HOST_BUSY); |
268 | if (clear_byte_done && smb_byte_by_byte(s)) { |
269 | uint8_t read = s->smb_addr & 0x01; |
270 | |
271 | if (s->in_i2c_block_read) { |
272 | /* See comment below PROT_I2C_BLOCK_READ above. */ |
273 | read = 1; |
274 | } |
275 | |
276 | s->smb_index++; |
277 | if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { |
278 | s->smb_index = 0; |
279 | } |
280 | if (!read && s->smb_index == s->smb_data0) { |
281 | uint8_t prot = (s->smb_ctl >> 2) & 0x07; |
282 | uint8_t cmd = s->smb_cmd; |
283 | uint8_t addr = s->smb_addr >> 1; |
284 | int ret; |
285 | |
286 | if (prot == PROT_I2C_BLOCK_READ) { |
287 | s->smb_stat |= STS_DEV_ERR; |
288 | goto out; |
289 | } |
290 | |
291 | ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data, |
292 | s->smb_data0, !s->i2c_enable); |
293 | if (ret < 0) { |
294 | s->smb_stat |= STS_DEV_ERR; |
295 | goto out; |
296 | } |
297 | s->op_done = true; |
298 | s->smb_stat |= STS_INTR; |
299 | s->smb_stat &= ~STS_HOST_BUSY; |
300 | } else if (!read) { |
301 | s->smb_data[s->smb_index] = s->smb_blkdata; |
302 | s->smb_stat |= STS_BYTE_DONE; |
303 | } else if (s->smb_ctl & CTL_LAST_BYTE) { |
304 | s->op_done = true; |
305 | if (s->in_i2c_block_read) { |
306 | s->in_i2c_block_read = false; |
307 | s->smb_blkdata = i2c_recv(s->smbus); |
308 | i2c_nack(s->smbus); |
309 | i2c_end_transfer(s->smbus); |
310 | } else { |
311 | s->smb_blkdata = s->smb_data[s->smb_index]; |
312 | } |
313 | s->smb_index = 0; |
314 | s->smb_stat |= STS_INTR; |
315 | s->smb_stat &= ~STS_HOST_BUSY; |
316 | } else { |
317 | if (s->in_i2c_block_read) { |
318 | s->smb_blkdata = i2c_recv(s->smbus); |
319 | } else { |
320 | s->smb_blkdata = s->smb_data[s->smb_index]; |
321 | } |
322 | s->smb_stat |= STS_BYTE_DONE; |
323 | } |
324 | } |
325 | break; |
326 | case SMBHSTCNT: |
327 | s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */ |
328 | if (val & CTL_START) { |
329 | if (!s->op_done) { |
330 | s->smb_index = 0; |
331 | s->op_done = true; |
332 | if (s->in_i2c_block_read) { |
333 | s->in_i2c_block_read = false; |
334 | i2c_end_transfer(s->smbus); |
335 | } |
336 | } |
337 | smb_transaction_start(s); |
338 | } |
339 | if (s->smb_ctl & CTL_KILL) { |
340 | s->op_done = true; |
341 | s->smb_index = 0; |
342 | s->smb_stat |= STS_FAILED; |
343 | s->smb_stat &= ~STS_HOST_BUSY; |
344 | } |
345 | break; |
346 | case SMBHSTCMD: |
347 | s->smb_cmd = val; |
348 | break; |
349 | case SMBHSTADD: |
350 | s->smb_addr = val; |
351 | break; |
352 | case SMBHSTDAT0: |
353 | s->smb_data0 = val; |
354 | break; |
355 | case SMBHSTDAT1: |
356 | s->smb_data1 = val; |
357 | break; |
358 | case SMBBLKDAT: |
359 | if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { |
360 | s->smb_index = 0; |
361 | } |
362 | if (s->smb_auxctl & AUX_BLK) { |
363 | s->smb_data[s->smb_index++] = val; |
364 | } else { |
365 | s->smb_blkdata = val; |
366 | } |
367 | break; |
368 | case SMBAUXCTL: |
369 | s->smb_auxctl = val & AUX_MASK; |
370 | break; |
371 | default: |
372 | break; |
373 | } |
374 | |
375 | out: |
376 | if (s->set_irq) { |
377 | s->set_irq(s, smb_irq_value(s)); |
378 | } |
379 | } |
380 | |
381 | static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) |
382 | { |
383 | PMSMBus *s = opaque; |
384 | uint32_t val; |
385 | |
386 | switch(addr) { |
387 | case SMBHSTSTS: |
388 | val = s->smb_stat; |
389 | if (s->start_transaction_on_status_read) { |
390 | /* execute command now */ |
391 | s->start_transaction_on_status_read = false; |
392 | s->smb_stat &= ~STS_HOST_BUSY; |
393 | smb_transaction(s); |
394 | } |
395 | break; |
396 | case SMBHSTCNT: |
397 | val = s->smb_ctl & CTL_RETURN_MASK; |
398 | break; |
399 | case SMBHSTCMD: |
400 | val = s->smb_cmd; |
401 | break; |
402 | case SMBHSTADD: |
403 | val = s->smb_addr; |
404 | break; |
405 | case SMBHSTDAT0: |
406 | val = s->smb_data0; |
407 | break; |
408 | case SMBHSTDAT1: |
409 | val = s->smb_data1; |
410 | break; |
411 | case SMBBLKDAT: |
412 | if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) { |
413 | if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { |
414 | s->smb_index = 0; |
415 | } |
416 | val = s->smb_data[s->smb_index++]; |
417 | if (!s->op_done && s->smb_index == s->smb_data0) { |
418 | s->op_done = true; |
419 | s->smb_index = 0; |
420 | s->smb_stat &= ~STS_HOST_BUSY; |
421 | } |
422 | } else { |
423 | val = s->smb_blkdata; |
424 | } |
425 | break; |
426 | case SMBAUXCTL: |
427 | val = s->smb_auxctl; |
428 | break; |
429 | default: |
430 | val = 0; |
431 | break; |
432 | } |
433 | SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n" , |
434 | addr, val); |
435 | |
436 | if (s->set_irq) { |
437 | s->set_irq(s, smb_irq_value(s)); |
438 | } |
439 | |
440 | return val; |
441 | } |
442 | |
443 | static void pm_smbus_reset(PMSMBus *s) |
444 | { |
445 | s->op_done = true; |
446 | s->smb_index = 0; |
447 | s->smb_stat = 0; |
448 | } |
449 | |
450 | static const MemoryRegionOps pm_smbus_ops = { |
451 | .read = smb_ioport_readb, |
452 | .write = smb_ioport_writeb, |
453 | .valid.min_access_size = 1, |
454 | .valid.max_access_size = 1, |
455 | .endianness = DEVICE_LITTLE_ENDIAN, |
456 | }; |
457 | |
458 | bool pm_smbus_vmstate_needed(void) |
459 | { |
460 | MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); |
461 | |
462 | return !mc->smbus_no_migration_support; |
463 | } |
464 | |
465 | const VMStateDescription pmsmb_vmstate = { |
466 | .name = "pmsmb" , |
467 | .version_id = 1, |
468 | .minimum_version_id = 1, |
469 | .fields = (VMStateField[]) { |
470 | VMSTATE_UINT8(smb_stat, PMSMBus), |
471 | VMSTATE_UINT8(smb_ctl, PMSMBus), |
472 | VMSTATE_UINT8(smb_cmd, PMSMBus), |
473 | VMSTATE_UINT8(smb_addr, PMSMBus), |
474 | VMSTATE_UINT8(smb_data0, PMSMBus), |
475 | VMSTATE_UINT8(smb_data1, PMSMBus), |
476 | VMSTATE_UINT32(smb_index, PMSMBus), |
477 | VMSTATE_UINT8_ARRAY(smb_data, PMSMBus, PM_SMBUS_MAX_MSG_SIZE), |
478 | VMSTATE_UINT8(smb_auxctl, PMSMBus), |
479 | VMSTATE_UINT8(smb_blkdata, PMSMBus), |
480 | VMSTATE_BOOL(i2c_enable, PMSMBus), |
481 | VMSTATE_BOOL(op_done, PMSMBus), |
482 | VMSTATE_BOOL(in_i2c_block_read, PMSMBus), |
483 | VMSTATE_BOOL(start_transaction_on_status_read, PMSMBus), |
484 | VMSTATE_END_OF_LIST() |
485 | } |
486 | }; |
487 | |
488 | void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk) |
489 | { |
490 | smb->op_done = true; |
491 | smb->reset = pm_smbus_reset; |
492 | smb->smbus = i2c_init_bus(parent, "i2c" ); |
493 | if (force_aux_blk) { |
494 | smb->smb_auxctl |= AUX_BLK; |
495 | } |
496 | memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, |
497 | "pm-smbus" , 64); |
498 | } |
499 | |