xref: /qemu/hw/i2c/smbus_master.c (revision 310b3fe9)
1 /*
2  * QEMU SMBus host (master) emulation.
3  *
4  * This code emulates SMBus transactions from the master point of view,
5  * it runs the individual I2C transaction to do the SMBus protocol
6  * over I2C.
7  *
8  * Copyright (c) 2007 CodeSourcery.
9  * Written by Paul Brook
10  *
11  * This code is licensed under the LGPL.
12  */
13 
14 #include "qemu/osdep.h"
15 #include "hw/hw.h"
16 #include "hw/i2c/i2c.h"
17 #include "hw/i2c/smbus_master.h"
18 
19 /* Master device commands.  */
20 int smbus_quick_command(I2CBus *bus, uint8_t addr, int read)
21 {
22     if (i2c_start_transfer(bus, addr, read)) {
23         return -1;
24     }
25     i2c_end_transfer(bus);
26     return 0;
27 }
28 
29 int smbus_receive_byte(I2CBus *bus, uint8_t addr)
30 {
31     uint8_t data;
32 
33     if (i2c_start_transfer(bus, addr, 1)) {
34         return -1;
35     }
36     data = i2c_recv(bus);
37     i2c_nack(bus);
38     i2c_end_transfer(bus);
39     return data;
40 }
41 
42 int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
43 {
44     if (i2c_start_transfer(bus, addr, 0)) {
45         return -1;
46     }
47     i2c_send(bus, data);
48     i2c_end_transfer(bus);
49     return 0;
50 }
51 
52 int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
53 {
54     uint8_t data;
55     if (i2c_start_transfer(bus, addr, 0)) {
56         return -1;
57     }
58     i2c_send(bus, command);
59     if (i2c_start_transfer(bus, addr, 1)) {
60         i2c_end_transfer(bus);
61         return -1;
62     }
63     data = i2c_recv(bus);
64     i2c_nack(bus);
65     i2c_end_transfer(bus);
66     return data;
67 }
68 
69 int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data)
70 {
71     if (i2c_start_transfer(bus, addr, 0)) {
72         return -1;
73     }
74     i2c_send(bus, command);
75     i2c_send(bus, data);
76     i2c_end_transfer(bus);
77     return 0;
78 }
79 
80 int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
81 {
82     uint16_t data;
83     if (i2c_start_transfer(bus, addr, 0)) {
84         return -1;
85     }
86     i2c_send(bus, command);
87     if (i2c_start_transfer(bus, addr, 1)) {
88         i2c_end_transfer(bus);
89         return -1;
90     }
91     data = i2c_recv(bus);
92     data |= i2c_recv(bus) << 8;
93     i2c_nack(bus);
94     i2c_end_transfer(bus);
95     return data;
96 }
97 
98 int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
99 {
100     if (i2c_start_transfer(bus, addr, 0)) {
101         return -1;
102     }
103     i2c_send(bus, command);
104     i2c_send(bus, data & 0xff);
105     i2c_send(bus, data >> 8);
106     i2c_end_transfer(bus);
107     return 0;
108 }
109 
110 int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
111                      int len, bool recv_len, bool send_cmd)
112 {
113     int rlen;
114     int i;
115 
116     if (send_cmd) {
117         if (i2c_start_transfer(bus, addr, 0)) {
118             return -1;
119         }
120         i2c_send(bus, command);
121     }
122     if (i2c_start_transfer(bus, addr, 1)) {
123         if (send_cmd) {
124             i2c_end_transfer(bus);
125         }
126         return -1;
127     }
128     if (recv_len) {
129         rlen = i2c_recv(bus);
130     } else {
131         rlen = len;
132     }
133     if (rlen > len) {
134         rlen = 0;
135     }
136     for (i = 0; i < rlen; i++) {
137         data[i] = i2c_recv(bus);
138     }
139     i2c_nack(bus);
140     i2c_end_transfer(bus);
141     return rlen;
142 }
143 
144 int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
145                       int len, bool send_len)
146 {
147     int i;
148 
149     if (len > 32) {
150         len = 32;
151     }
152 
153     if (i2c_start_transfer(bus, addr, 0)) {
154         return -1;
155     }
156     i2c_send(bus, command);
157     if (send_len) {
158         i2c_send(bus, len);
159     }
160     for (i = 0; i < len; i++) {
161         i2c_send(bus, data[i]);
162     }
163     i2c_end_transfer(bus);
164     return 0;
165 }
166