]>
Commit | Line | Data |
---|---|---|
fc0bdd99 IY |
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 | |
1012e960 BS |
17 | * License along with this library; if not, see |
18 | * <http://www.gnu.org/licenses/>. | |
fc0bdd99 | 19 | */ |
b6a0aa05 | 20 | #include "qemu/osdep.h" |
83c9f4ca | 21 | #include "hw/hw.h" |
0d09e41a PB |
22 | #include "hw/i386/pc.h" |
23 | #include "hw/i2c/pm_smbus.h" | |
24 | #include "hw/i2c/smbus.h" | |
fc0bdd99 IY |
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 | ||
edb5092c M |
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 | ||
b246eebb IY |
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 | ||
fc0bdd99 IY |
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; | |
a5c82852 | 63 | I2CBus *bus = s->smbus; |
c8097612 | 64 | int ret; |
fc0bdd99 | 65 | |
b246eebb | 66 | SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); |
edb5092c M |
67 | /* Transaction isn't exec if STS_DEV_ERR bit set */ |
68 | if ((s->smb_stat & STS_DEV_ERR) != 0) { | |
c8097612 PB |
69 | goto error; |
70 | } | |
fc0bdd99 IY |
71 | switch(prot) { |
72 | case 0x0: | |
c8097612 PB |
73 | ret = smbus_quick_command(bus, addr, read); |
74 | goto done; | |
fc0bdd99 IY |
75 | case 0x1: |
76 | if (read) { | |
c8097612 PB |
77 | ret = smbus_receive_byte(bus, addr); |
78 | goto data8; | |
fc0bdd99 | 79 | } else { |
c8097612 PB |
80 | ret = smbus_send_byte(bus, addr, cmd); |
81 | goto done; | |
fc0bdd99 | 82 | } |
fc0bdd99 IY |
83 | case 0x2: |
84 | if (read) { | |
c8097612 PB |
85 | ret = smbus_read_byte(bus, addr, cmd); |
86 | goto data8; | |
fc0bdd99 | 87 | } else { |
c8097612 PB |
88 | ret = smbus_write_byte(bus, addr, cmd, s->smb_data0); |
89 | goto done; | |
fc0bdd99 IY |
90 | } |
91 | break; | |
92 | case 0x3: | |
93 | if (read) { | |
c8097612 PB |
94 | ret = smbus_read_word(bus, addr, cmd); |
95 | goto data16; | |
fc0bdd99 | 96 | } else { |
c8097612 PB |
97 | ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); |
98 | goto done; | |
fc0bdd99 IY |
99 | } |
100 | break; | |
101 | case 0x5: | |
102 | if (read) { | |
c8097612 PB |
103 | ret = smbus_read_block(bus, addr, cmd, s->smb_data); |
104 | goto data8; | |
fc0bdd99 | 105 | } else { |
c8097612 PB |
106 | ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); |
107 | goto done; | |
fc0bdd99 IY |
108 | } |
109 | break; | |
110 | default: | |
111 | goto error; | |
112 | } | |
c8097612 PB |
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; | |
fc0bdd99 IY |
130 | return; |
131 | ||
c8097612 | 132 | error: |
edb5092c | 133 | s->smb_stat |= STS_DEV_ERR; |
c8097612 PB |
134 | return; |
135 | ||
fc0bdd99 IY |
136 | } |
137 | ||
798512e5 GH |
138 | static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, |
139 | unsigned width) | |
fc0bdd99 IY |
140 | { |
141 | PMSMBus *s = opaque; | |
798512e5 | 142 | |
c5539cb4 GA |
143 | SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx |
144 | " val=0x%02" PRIx64 "\n", addr, val); | |
fc0bdd99 IY |
145 | switch(addr) { |
146 | case SMBHSTSTS: | |
edb5092c | 147 | s->smb_stat = (~(val & 0xff)) & s->smb_stat; |
fc0bdd99 IY |
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 | ||
798512e5 | 177 | static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) |
fc0bdd99 IY |
178 | { |
179 | PMSMBus *s = opaque; | |
180 | uint32_t val; | |
181 | ||
fc0bdd99 IY |
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 | } | |
c5539cb4 | 211 | SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val); |
fc0bdd99 IY |
212 | return val; |
213 | } | |
214 | ||
798512e5 GH |
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 | ||
fc0bdd99 IY |
223 | void pm_smbus_init(DeviceState *parent, PMSMBus *smb) |
224 | { | |
225 | smb->smbus = i2c_init_bus(parent, "i2c"); | |
1437c94b PB |
226 | memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, |
227 | "pm-smbus", 64); | |
fc0bdd99 | 228 | } |