]>
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 | */ |
83c9f4ca | 20 | #include "hw/hw.h" |
0d09e41a PB |
21 | #include "hw/i386/pc.h" |
22 | #include "hw/i2c/pm_smbus.h" | |
23 | #include "hw/i2c/smbus.h" | |
fc0bdd99 IY |
24 | |
25 | /* no save/load? */ | |
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 | ||
edb5092c M |
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 | /* Signs of successfully transaction end : | |
44 | * ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) | |
45 | */ | |
46 | ||
b246eebb IY |
47 | //#define DEBUG |
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 | ||
fc0bdd99 IY |
56 | static 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 | i2c_bus *bus = s->smbus; | |
63 | ||
b246eebb | 64 | SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); |
edb5092c M |
65 | /* Transaction isn't exec if STS_DEV_ERR bit set */ |
66 | if ((s->smb_stat & STS_DEV_ERR) != 0) { | |
67 | goto error; | |
68 | } | |
fc0bdd99 IY |
69 | switch(prot) { |
70 | case 0x0: | |
71 | smbus_quick_command(bus, addr, read); | |
edb5092c | 72 | s->smb_stat |= STS_BYTE_DONE | STS_INTR; |
fc0bdd99 IY |
73 | break; |
74 | case 0x1: | |
75 | if (read) { | |
76 | s->smb_data0 = smbus_receive_byte(bus, addr); | |
77 | } else { | |
78 | smbus_send_byte(bus, addr, cmd); | |
79 | } | |
edb5092c | 80 | s->smb_stat |= STS_BYTE_DONE | STS_INTR; |
fc0bdd99 IY |
81 | break; |
82 | case 0x2: | |
83 | if (read) { | |
84 | s->smb_data0 = smbus_read_byte(bus, addr, cmd); | |
85 | } else { | |
86 | smbus_write_byte(bus, addr, cmd, s->smb_data0); | |
87 | } | |
edb5092c | 88 | s->smb_stat |= STS_BYTE_DONE | STS_INTR; |
fc0bdd99 IY |
89 | break; |
90 | case 0x3: | |
91 | if (read) { | |
92 | uint16_t val; | |
93 | val = smbus_read_word(bus, addr, cmd); | |
94 | s->smb_data0 = val; | |
95 | s->smb_data1 = val >> 8; | |
96 | } else { | |
97 | smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); | |
98 | } | |
edb5092c | 99 | s->smb_stat |= STS_BYTE_DONE | STS_INTR; |
fc0bdd99 IY |
100 | break; |
101 | case 0x5: | |
102 | if (read) { | |
103 | s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); | |
104 | } else { | |
105 | smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); | |
106 | } | |
edb5092c | 107 | s->smb_stat |= STS_BYTE_DONE | STS_INTR; |
fc0bdd99 IY |
108 | break; |
109 | default: | |
110 | goto error; | |
111 | } | |
112 | return; | |
113 | ||
114 | error: | |
edb5092c | 115 | s->smb_stat |= STS_DEV_ERR; |
fc0bdd99 IY |
116 | } |
117 | ||
798512e5 GH |
118 | static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, |
119 | unsigned width) | |
fc0bdd99 IY |
120 | { |
121 | PMSMBus *s = opaque; | |
798512e5 | 122 | |
b246eebb | 123 | SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val); |
fc0bdd99 IY |
124 | switch(addr) { |
125 | case SMBHSTSTS: | |
edb5092c | 126 | s->smb_stat = (~(val & 0xff)) & s->smb_stat; |
fc0bdd99 IY |
127 | s->smb_index = 0; |
128 | break; | |
129 | case SMBHSTCNT: | |
130 | s->smb_ctl = val; | |
131 | if (val & 0x40) | |
132 | smb_transaction(s); | |
133 | break; | |
134 | case SMBHSTCMD: | |
135 | s->smb_cmd = val; | |
136 | break; | |
137 | case SMBHSTADD: | |
138 | s->smb_addr = val; | |
139 | break; | |
140 | case SMBHSTDAT0: | |
141 | s->smb_data0 = val; | |
142 | break; | |
143 | case SMBHSTDAT1: | |
144 | s->smb_data1 = val; | |
145 | break; | |
146 | case SMBBLKDAT: | |
147 | s->smb_data[s->smb_index++] = val; | |
148 | if (s->smb_index > 31) | |
149 | s->smb_index = 0; | |
150 | break; | |
151 | default: | |
152 | break; | |
153 | } | |
154 | } | |
155 | ||
798512e5 | 156 | static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) |
fc0bdd99 IY |
157 | { |
158 | PMSMBus *s = opaque; | |
159 | uint32_t val; | |
160 | ||
fc0bdd99 IY |
161 | switch(addr) { |
162 | case SMBHSTSTS: | |
163 | val = s->smb_stat; | |
164 | break; | |
165 | case SMBHSTCNT: | |
166 | s->smb_index = 0; | |
167 | val = s->smb_ctl & 0x1f; | |
168 | break; | |
169 | case SMBHSTCMD: | |
170 | val = s->smb_cmd; | |
171 | break; | |
172 | case SMBHSTADD: | |
173 | val = s->smb_addr; | |
174 | break; | |
175 | case SMBHSTDAT0: | |
176 | val = s->smb_data0; | |
177 | break; | |
178 | case SMBHSTDAT1: | |
179 | val = s->smb_data1; | |
180 | break; | |
181 | case SMBBLKDAT: | |
182 | val = s->smb_data[s->smb_index++]; | |
183 | if (s->smb_index > 31) | |
184 | s->smb_index = 0; | |
185 | break; | |
186 | default: | |
187 | val = 0; | |
188 | break; | |
189 | } | |
b246eebb | 190 | SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val); |
fc0bdd99 IY |
191 | return val; |
192 | } | |
193 | ||
798512e5 GH |
194 | static const MemoryRegionOps pm_smbus_ops = { |
195 | .read = smb_ioport_readb, | |
196 | .write = smb_ioport_writeb, | |
197 | .valid.min_access_size = 1, | |
198 | .valid.max_access_size = 1, | |
199 | .endianness = DEVICE_LITTLE_ENDIAN, | |
200 | }; | |
201 | ||
fc0bdd99 IY |
202 | void pm_smbus_init(DeviceState *parent, PMSMBus *smb) |
203 | { | |
204 | smb->smbus = i2c_init_bus(parent, "i2c"); | |
1437c94b PB |
205 | memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, |
206 | "pm-smbus", 64); | |
fc0bdd99 | 207 | } |