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