]>
Commit | Line | Data |
---|---|---|
16020011 CLG |
1 | /* |
2 | * ARM Aspeed I2C controller | |
3 | * | |
4 | * Copyright (C) 2016 IBM Corp. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version 2 | |
9 | * of the License, or (at your option) any later version. | |
10 | * | |
11 | * This program 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 | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include "qemu/osdep.h" | |
22 | #include "hw/sysbus.h" | |
d6454270 | 23 | #include "migration/vmstate.h" |
16020011 | 24 | #include "qemu/log.h" |
0b8fa32f | 25 | #include "qemu/module.h" |
545d6bef CLG |
26 | #include "qemu/error-report.h" |
27 | #include "qapi/error.h" | |
16020011 | 28 | #include "hw/i2c/aspeed_i2c.h" |
64552b6b | 29 | #include "hw/irq.h" |
545d6bef | 30 | #include "hw/qdev-properties.h" |
3be3d6cc | 31 | #include "hw/registerfields.h" |
66cc84a1 | 32 | #include "trace.h" |
16020011 | 33 | |
16020011 CLG |
34 | static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) |
35 | { | |
51dd4923 | 36 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); |
ba2cccd6 JK |
37 | uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); |
38 | uint32_t intr_ctrl_reg = aspeed_i2c_bus_intr_ctrl_offset(bus); | |
39 | bool raise_irq; | |
40 | ||
41 | trace_aspeed_i2c_bus_raise_interrupt(bus->regs[reg_intr_sts], | |
0efec47b JK |
42 | aspeed_i2c_bus_pkt_mode_en(bus) && |
43 | ARRAY_FIELD_EX32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE) ? | |
44 | "pktdone|" : "", | |
ba2cccd6 JK |
45 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_NAK) ? "nak|" : "", |
46 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_ACK) ? "ack|" : "", | |
47 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE) ? "done|" | |
2260fc6f | 48 | : "", |
ba2cccd6 JK |
49 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, NORMAL_STOP) ? |
50 | "normal|" : "", | |
51 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, ABNORMAL) ? "abnormal" | |
52 | : ""); | |
53 | raise_irq = bus->regs[reg_intr_sts] & bus->regs[intr_ctrl_reg]; | |
54 | /* In packet mode we don't mask off INTR_STS */ | |
55 | if (!aspeed_i2c_bus_pkt_mode_en(bus)) { | |
56 | bus->regs[reg_intr_sts] &= bus->regs[intr_ctrl_reg]; | |
57 | } | |
58 | if (raise_irq) { | |
16020011 | 59 | bus->controller->intr_status |= 1 << bus->id; |
51dd4923 | 60 | qemu_irq_raise(aic->bus_get_irq(bus)); |
16020011 CLG |
61 | } |
62 | } | |
63 | ||
ba2cccd6 JK |
64 | static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, |
65 | unsigned size) | |
16020011 | 66 | { |
545d6bef | 67 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); |
2260fc6f | 68 | uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; |
16020011 CLG |
69 | |
70 | switch (offset) { | |
3be3d6cc | 71 | case A_I2CD_FUN_CTRL: |
3be3d6cc | 72 | case A_I2CD_AC_TIMING1: |
3be3d6cc | 73 | case A_I2CD_AC_TIMING2: |
3be3d6cc | 74 | case A_I2CD_INTR_CTRL: |
3be3d6cc | 75 | case A_I2CD_INTR_STS: |
3be3d6cc | 76 | case A_I2CD_POOL_CTRL: |
3be3d6cc | 77 | case A_I2CD_BYTE_BUF: |
2260fc6f | 78 | /* Value is already set, don't do anything. */ |
66cc84a1 | 79 | break; |
3be3d6cc | 80 | case A_I2CD_CMD: |
ba2cccd6 | 81 | value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); |
66cc84a1 | 82 | break; |
3be3d6cc | 83 | case A_I2CD_DMA_ADDR: |
545d6bef CLG |
84 | if (!aic->has_dma) { |
85 | qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); | |
2260fc6f | 86 | value = -1; |
545d6bef | 87 | } |
66cc84a1 | 88 | break; |
3be3d6cc | 89 | case A_I2CD_DMA_LEN: |
545d6bef CLG |
90 | if (!aic->has_dma) { |
91 | qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); | |
2260fc6f | 92 | value = -1; |
545d6bef | 93 | } |
66cc84a1 CLG |
94 | break; |
95 | ||
16020011 CLG |
96 | default: |
97 | qemu_log_mask(LOG_GUEST_ERROR, | |
98 | "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); | |
66cc84a1 CLG |
99 | value = -1; |
100 | break; | |
16020011 | 101 | } |
66cc84a1 CLG |
102 | |
103 | trace_aspeed_i2c_bus_read(bus->id, offset, size, value); | |
104 | return value; | |
16020011 CLG |
105 | } |
106 | ||
ba2cccd6 JK |
107 | static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, |
108 | unsigned size) | |
109 | { | |
110 | uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; | |
111 | ||
112 | switch (offset) { | |
113 | case A_I2CC_FUN_CTRL: | |
114 | case A_I2CC_AC_TIMING: | |
115 | case A_I2CC_POOL_CTRL: | |
116 | case A_I2CM_INTR_CTRL: | |
117 | case A_I2CM_INTR_STS: | |
118 | case A_I2CC_MS_TXRX_BYTE_BUF: | |
119 | case A_I2CM_DMA_LEN: | |
120 | case A_I2CM_DMA_TX_ADDR: | |
121 | case A_I2CM_DMA_RX_ADDR: | |
122 | case A_I2CM_DMA_LEN_STS: | |
123 | case A_I2CC_DMA_ADDR: | |
124 | case A_I2CC_DMA_LEN: | |
125 | /* Value is already set, don't do anything. */ | |
126 | break; | |
127 | case A_I2CM_CMD: | |
128 | value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); | |
129 | break; | |
130 | default: | |
131 | qemu_log_mask(LOG_GUEST_ERROR, | |
132 | "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); | |
133 | value = -1; | |
134 | break; | |
135 | } | |
136 | ||
137 | trace_aspeed_i2c_bus_read(bus->id, offset, size, value); | |
138 | return value; | |
139 | } | |
140 | ||
141 | static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, | |
142 | unsigned size) | |
143 | { | |
144 | AspeedI2CBus *bus = opaque; | |
145 | if (aspeed_i2c_is_new_mode(bus->controller)) { | |
146 | return aspeed_i2c_bus_new_read(bus, offset, size); | |
147 | } | |
148 | return aspeed_i2c_bus_old_read(bus, offset, size); | |
149 | } | |
150 | ||
4960f084 CLG |
151 | static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) |
152 | { | |
ba2cccd6 JK |
153 | if (aspeed_i2c_is_new_mode(bus->controller)) { |
154 | SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_STATE, | |
155 | state); | |
156 | } else { | |
157 | SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_CMD, TX_STATE, state); | |
158 | } | |
4960f084 CLG |
159 | } |
160 | ||
161 | static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) | |
162 | { | |
ba2cccd6 JK |
163 | if (aspeed_i2c_is_new_mode(bus->controller)) { |
164 | return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, | |
165 | TX_STATE); | |
166 | } | |
167 | return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, TX_STATE); | |
4960f084 CLG |
168 | } |
169 | ||
545d6bef CLG |
170 | static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) |
171 | { | |
172 | MemTxResult result; | |
173 | AspeedI2CState *s = bus->controller; | |
ba2cccd6 JK |
174 | uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); |
175 | uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); | |
545d6bef | 176 | |
ba2cccd6 | 177 | result = address_space_read(&s->dram_as, bus->regs[reg_dma_addr], |
545d6bef CLG |
178 | MEMTXATTRS_UNSPECIFIED, data, 1); |
179 | if (result != MEMTX_OK) { | |
180 | qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n", | |
ba2cccd6 | 181 | __func__, bus->regs[reg_dma_addr]); |
545d6bef CLG |
182 | return -1; |
183 | } | |
184 | ||
ba2cccd6 JK |
185 | bus->regs[reg_dma_addr]++; |
186 | bus->regs[reg_dma_len]--; | |
545d6bef CLG |
187 | return 0; |
188 | } | |
189 | ||
6054fc73 CLG |
190 | static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) |
191 | { | |
192 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); | |
193 | int ret = -1; | |
194 | int i; | |
ba2cccd6 JK |
195 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); |
196 | uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); | |
197 | uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); | |
198 | uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); | |
199 | int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, | |
200 | TX_COUNT); | |
201 | ||
202 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { | |
3be3d6cc | 203 | for (i = pool_start; i < pool_tx_count; i++) { |
6054fc73 CLG |
204 | uint8_t *pool_base = aic->bus_pool_base(bus); |
205 | ||
3be3d6cc | 206 | trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, |
66cc84a1 | 207 | pool_base[i]); |
6054fc73 CLG |
208 | ret = i2c_send(bus->bus, pool_base[i]); |
209 | if (ret) { | |
210 | break; | |
211 | } | |
212 | } | |
ba2cccd6 JK |
213 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0); |
214 | } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { | |
215 | /* In new mode, clear how many bytes we TXed */ | |
216 | if (aspeed_i2c_is_new_mode(bus->controller)) { | |
217 | ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); | |
218 | } | |
219 | while (bus->regs[reg_dma_len]) { | |
545d6bef CLG |
220 | uint8_t data; |
221 | aspeed_i2c_dma_read(bus, &data); | |
ba2cccd6 JK |
222 | trace_aspeed_i2c_bus_send("DMA", bus->regs[reg_dma_len], |
223 | bus->regs[reg_dma_len], data); | |
545d6bef CLG |
224 | ret = i2c_send(bus->bus, data); |
225 | if (ret) { | |
226 | break; | |
227 | } | |
ba2cccd6 JK |
228 | /* In new mode, keep track of how many bytes we TXed */ |
229 | if (aspeed_i2c_is_new_mode(bus->controller)) { | |
230 | ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, | |
231 | ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, | |
232 | TX_LEN) + 1); | |
233 | } | |
545d6bef | 234 | } |
ba2cccd6 | 235 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); |
6054fc73 | 236 | } else { |
2260fc6f | 237 | trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, |
ba2cccd6 JK |
238 | bus->regs[reg_byte_buf]); |
239 | ret = i2c_send(bus->bus, bus->regs[reg_byte_buf]); | |
6054fc73 CLG |
240 | } |
241 | ||
242 | return ret; | |
243 | } | |
244 | ||
245 | static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) | |
7bd9c60d | 246 | { |
6054fc73 CLG |
247 | AspeedI2CState *s = bus->controller; |
248 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); | |
249 | uint8_t data; | |
250 | int i; | |
ba2cccd6 JK |
251 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); |
252 | uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); | |
253 | uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); | |
254 | uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); | |
255 | uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); | |
256 | int pool_rx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, | |
257 | RX_COUNT); | |
258 | ||
259 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { | |
6054fc73 | 260 | uint8_t *pool_base = aic->bus_pool_base(bus); |
7bd9c60d | 261 | |
3be3d6cc | 262 | for (i = 0; i < pool_rx_count; i++) { |
6054fc73 | 263 | pool_base[i] = i2c_recv(bus->bus); |
3be3d6cc | 264 | trace_aspeed_i2c_bus_recv("BUF", i + 1, pool_rx_count, |
66cc84a1 | 265 | pool_base[i]); |
6054fc73 CLG |
266 | } |
267 | ||
268 | /* Update RX count */ | |
ba2cccd6 JK |
269 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_pool_ctrl, RX_COUNT, i & 0xff); |
270 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_BUFF_EN, 0); | |
271 | } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { | |
545d6bef | 272 | uint8_t data; |
ba2cccd6 JK |
273 | /* In new mode, clear how many bytes we RXed */ |
274 | if (aspeed_i2c_is_new_mode(bus->controller)) { | |
275 | ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); | |
276 | } | |
545d6bef | 277 | |
ba2cccd6 | 278 | while (bus->regs[reg_dma_len]) { |
545d6bef CLG |
279 | MemTxResult result; |
280 | ||
281 | data = i2c_recv(bus->bus); | |
ba2cccd6 JK |
282 | trace_aspeed_i2c_bus_recv("DMA", bus->regs[reg_dma_len], |
283 | bus->regs[reg_dma_len], data); | |
284 | result = address_space_write(&s->dram_as, bus->regs[reg_dma_addr], | |
545d6bef CLG |
285 | MEMTXATTRS_UNSPECIFIED, &data, 1); |
286 | if (result != MEMTX_OK) { | |
287 | qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n", | |
ba2cccd6 | 288 | __func__, bus->regs[reg_dma_addr]); |
545d6bef CLG |
289 | return; |
290 | } | |
ba2cccd6 JK |
291 | bus->regs[reg_dma_addr]++; |
292 | bus->regs[reg_dma_len]--; | |
293 | /* In new mode, keep track of how many bytes we RXed */ | |
294 | if (aspeed_i2c_is_new_mode(bus->controller)) { | |
295 | ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, | |
296 | ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, | |
297 | RX_LEN) + 1); | |
298 | } | |
545d6bef | 299 | } |
ba2cccd6 | 300 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); |
6054fc73 CLG |
301 | } else { |
302 | data = i2c_recv(bus->bus); | |
ba2cccd6 JK |
303 | trace_aspeed_i2c_bus_recv("BYTE", 1, 1, bus->regs[reg_byte_buf]); |
304 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); | |
6054fc73 CLG |
305 | } |
306 | } | |
307 | ||
308 | static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) | |
309 | { | |
ba2cccd6 JK |
310 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); |
311 | uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); | |
312 | ||
7bd9c60d | 313 | aspeed_i2c_set_state(bus, I2CD_MRXD); |
6054fc73 | 314 | aspeed_i2c_bus_recv(bus); |
ba2cccd6 JK |
315 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); |
316 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) { | |
7bd9c60d GR |
317 | i2c_nack(bus->bus); |
318 | } | |
ba2cccd6 JK |
319 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_RX_CMD, 0); |
320 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_S_RX_CMD_LAST, 0); | |
7bd9c60d GR |
321 | aspeed_i2c_set_state(bus, I2CD_MACTIVE); |
322 | } | |
323 | ||
6054fc73 CLG |
324 | static uint8_t aspeed_i2c_get_addr(AspeedI2CBus *bus) |
325 | { | |
326 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); | |
ba2cccd6 JK |
327 | uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); |
328 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); | |
6054fc73 | 329 | |
ba2cccd6 JK |
330 | if (aspeed_i2c_bus_pkt_mode_en(bus)) { |
331 | return (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, PKT_DEV_ADDR) << 1) | | |
332 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD); | |
333 | } | |
334 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { | |
6054fc73 CLG |
335 | uint8_t *pool_base = aic->bus_pool_base(bus); |
336 | ||
337 | return pool_base[0]; | |
ba2cccd6 | 338 | } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { |
545d6bef CLG |
339 | uint8_t data; |
340 | ||
341 | aspeed_i2c_dma_read(bus, &data); | |
342 | return data; | |
6054fc73 | 343 | } else { |
ba2cccd6 | 344 | return bus->regs[reg_byte_buf]; |
6054fc73 CLG |
345 | } |
346 | } | |
347 | ||
aab90b1c CLG |
348 | static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) |
349 | { | |
350 | AspeedI2CState *s = bus->controller; | |
351 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); | |
ba2cccd6 JK |
352 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); |
353 | bool dma_en = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) || | |
354 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) || | |
355 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) || | |
356 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN); | |
aab90b1c CLG |
357 | if (!aic->check_sram) { |
358 | return true; | |
359 | } | |
360 | ||
361 | /* | |
362 | * AST2500: SRAM must be enabled before using the Buffer Pool or | |
363 | * DMA mode. | |
364 | */ | |
3be3d6cc | 365 | if (!FIELD_EX32(s->ctrl_global, I2C_CTRL_GLOBAL, SRAM_EN) && dma_en) { |
aab90b1c CLG |
366 | qemu_log_mask(LOG_GUEST_ERROR, "%s: SRAM is not enabled\n", __func__); |
367 | return false; | |
368 | } | |
369 | ||
370 | return true; | |
371 | } | |
372 | ||
66cc84a1 CLG |
373 | static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) |
374 | { | |
f821bac4 | 375 | g_autofree char *cmd_flags = NULL; |
66cc84a1 | 376 | uint32_t count; |
ba2cccd6 JK |
377 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); |
378 | uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); | |
379 | uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); | |
380 | uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); | |
381 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { | |
382 | count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT); | |
383 | } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { | |
384 | count = bus->regs[reg_dma_len]; | |
66cc84a1 CLG |
385 | } else { /* BYTE mode */ |
386 | count = 1; | |
387 | } | |
388 | ||
389 | cmd_flags = g_strdup_printf("%s%s%s%s%s%s%s%s%s", | |
ba2cccd6 JK |
390 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD) ? "start|" : "", |
391 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) ? "rxdma|" : "", | |
392 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) ? "txdma|" : "", | |
393 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) ? "rxbuf|" : "", | |
394 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN) ? "txbuf|" : "", | |
395 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD) ? "tx|" : "", | |
396 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) ? "rx|" : "", | |
397 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST) ? "last|" : "", | |
398 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD) ? "stop|" : ""); | |
399 | ||
400 | trace_aspeed_i2c_bus_cmd(bus->regs[reg_cmd], cmd_flags, count, | |
401 | bus->regs[reg_intr_sts]); | |
66cc84a1 CLG |
402 | } |
403 | ||
4960f084 CLG |
404 | /* |
405 | * The state machine needs some refinement. It is only used to track | |
406 | * invalid STOP commands for the moment. | |
407 | */ | |
16020011 CLG |
408 | static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) |
409 | { | |
6054fc73 | 410 | uint8_t pool_start = 0; |
ba2cccd6 JK |
411 | uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); |
412 | uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); | |
413 | uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); | |
414 | uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); | |
16020011 | 415 | |
aab90b1c CLG |
416 | if (!aspeed_i2c_check_sram(bus)) { |
417 | return; | |
418 | } | |
419 | ||
66cc84a1 CLG |
420 | if (trace_event_get_state_backends(TRACE_ASPEED_I2C_BUS_CMD)) { |
421 | aspeed_i2c_bus_cmd_dump(bus); | |
422 | } | |
423 | ||
ba2cccd6 | 424 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) { |
4960f084 CLG |
425 | uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? |
426 | I2CD_MSTARTR : I2CD_MSTART; | |
6054fc73 | 427 | uint8_t addr; |
4960f084 CLG |
428 | |
429 | aspeed_i2c_set_state(bus, state); | |
430 | ||
6054fc73 | 431 | addr = aspeed_i2c_get_addr(bus); |
6054fc73 CLG |
432 | if (i2c_start_transfer(bus->bus, extract32(addr, 1, 7), |
433 | extract32(addr, 0, 1))) { | |
ba2cccd6 JK |
434 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); |
435 | if (aspeed_i2c_bus_pkt_mode_en(bus)) { | |
436 | ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); | |
437 | } | |
16020011 | 438 | } else { |
ba2cccd6 JK |
439 | /* START doesn't set TX_ACK in packet mode */ |
440 | if (!aspeed_i2c_bus_pkt_mode_en(bus)) { | |
441 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); | |
442 | } | |
16020011 CLG |
443 | } |
444 | ||
ba2cccd6 | 445 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_START_CMD, 0); |
6054fc73 CLG |
446 | |
447 | /* | |
448 | * The START command is also a TX command, as the slave | |
449 | * address is sent on the bus. Drop the TX flag if nothing | |
450 | * else needs to be sent in this sequence. | |
451 | */ | |
ba2cccd6 JK |
452 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { |
453 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) | |
454 | == 1) { | |
455 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); | |
6054fc73 CLG |
456 | } else { |
457 | /* | |
458 | * Increase the start index in the TX pool buffer to | |
459 | * skip the address byte. | |
460 | */ | |
461 | pool_start++; | |
462 | } | |
ba2cccd6 JK |
463 | } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { |
464 | if (bus->regs[reg_dma_len] == 0) { | |
465 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); | |
545d6bef | 466 | } |
6054fc73 | 467 | } else { |
ba2cccd6 | 468 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); |
6054fc73 | 469 | } |
ddabca75 CLG |
470 | |
471 | /* No slave found */ | |
472 | if (!i2c_bus_busy(bus->bus)) { | |
ba2cccd6 JK |
473 | if (aspeed_i2c_bus_pkt_mode_en(bus)) { |
474 | ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); | |
475 | ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); | |
476 | } | |
ddabca75 CLG |
477 | return; |
478 | } | |
4960f084 | 479 | aspeed_i2c_set_state(bus, I2CD_MACTIVE); |
ddabca75 CLG |
480 | } |
481 | ||
ba2cccd6 | 482 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { |
4960f084 | 483 | aspeed_i2c_set_state(bus, I2CD_MTXD); |
6054fc73 | 484 | if (aspeed_i2c_bus_send(bus, pool_start)) { |
ba2cccd6 | 485 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); |
16020011 CLG |
486 | i2c_end_transfer(bus->bus); |
487 | } else { | |
ba2cccd6 | 488 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); |
16020011 | 489 | } |
ba2cccd6 | 490 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); |
4960f084 | 491 | aspeed_i2c_set_state(bus, I2CD_MACTIVE); |
ddabca75 | 492 | } |
16020011 | 493 | |
ba2cccd6 JK |
494 | if ((SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) || |
495 | SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) && | |
496 | !SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE)) { | |
7bd9c60d | 497 | aspeed_i2c_handle_rx_cmd(bus); |
16020011 CLG |
498 | } |
499 | ||
ba2cccd6 | 500 | if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD)) { |
4960f084 CLG |
501 | if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { |
502 | qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); | |
ba2cccd6 JK |
503 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, ABNORMAL, 1); |
504 | if (aspeed_i2c_bus_pkt_mode_en(bus)) { | |
505 | ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); | |
506 | } | |
16020011 | 507 | } else { |
4960f084 | 508 | aspeed_i2c_set_state(bus, I2CD_MSTOP); |
16020011 | 509 | i2c_end_transfer(bus->bus); |
ba2cccd6 | 510 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1); |
16020011 | 511 | } |
ba2cccd6 | 512 | SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_STOP_CMD, 0); |
4960f084 | 513 | aspeed_i2c_set_state(bus, I2CD_IDLE); |
16020011 | 514 | } |
ba2cccd6 JK |
515 | |
516 | if (aspeed_i2c_bus_pkt_mode_en(bus)) { | |
517 | ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); | |
518 | } | |
16020011 CLG |
519 | } |
520 | ||
ba2cccd6 JK |
521 | static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, |
522 | uint64_t value, unsigned size) | |
523 | { | |
524 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); | |
525 | bool handle_rx; | |
526 | bool w1t; | |
527 | ||
528 | trace_aspeed_i2c_bus_write(bus->id, offset, size, value); | |
529 | ||
530 | switch (offset) { | |
531 | case A_I2CC_FUN_CTRL: | |
532 | if (SHARED_FIELD_EX32(value, SLAVE_EN)) { | |
533 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
534 | __func__); | |
535 | break; | |
536 | } | |
537 | bus->regs[R_I2CD_FUN_CTRL] = value & 0x007dc3ff; | |
538 | break; | |
539 | case A_I2CC_AC_TIMING: | |
540 | bus->regs[R_I2CC_AC_TIMING] = value & 0x1ffff0ff; | |
541 | break; | |
542 | case A_I2CC_MS_TXRX_BYTE_BUF: | |
543 | SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_BUF, | |
544 | value); | |
545 | break; | |
546 | case A_I2CC_POOL_CTRL: | |
547 | bus->regs[R_I2CC_POOL_CTRL] &= ~0xffffff; | |
548 | bus->regs[R_I2CC_POOL_CTRL] |= (value & 0xffffff); | |
549 | break; | |
550 | case A_I2CM_INTR_CTRL: | |
551 | bus->regs[R_I2CM_INTR_CTRL] = value & 0x0007f07f; | |
552 | break; | |
553 | case A_I2CM_INTR_STS: | |
554 | handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_INTR_STS, RX_DONE) | |
555 | && SHARED_FIELD_EX32(value, RX_DONE); | |
556 | ||
557 | /* In packet mode, clearing PKT_CMD_DONE clears other interrupts. */ | |
558 | if (aspeed_i2c_bus_pkt_mode_en(bus) && | |
559 | FIELD_EX32(value, I2CM_INTR_STS, PKT_CMD_DONE)) { | |
560 | bus->regs[R_I2CM_INTR_STS] &= 0xf0001000; | |
561 | if (!bus->regs[R_I2CM_INTR_STS]) { | |
562 | bus->controller->intr_status &= ~(1 << bus->id); | |
563 | qemu_irq_lower(aic->bus_get_irq(bus)); | |
564 | } | |
565 | break; | |
566 | } | |
567 | bus->regs[R_I2CM_INTR_STS] &= ~(value & 0xf007f07f); | |
568 | if (!bus->regs[R_I2CM_INTR_STS]) { | |
569 | bus->controller->intr_status &= ~(1 << bus->id); | |
570 | qemu_irq_lower(aic->bus_get_irq(bus)); | |
571 | } | |
572 | if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, | |
573 | M_RX_CMD) || | |
574 | SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, | |
575 | M_S_RX_CMD_LAST))) { | |
576 | aspeed_i2c_handle_rx_cmd(bus); | |
577 | aspeed_i2c_bus_raise_interrupt(bus); | |
578 | } | |
579 | break; | |
580 | case A_I2CM_CMD: | |
581 | if (!aspeed_i2c_bus_is_enabled(bus)) { | |
582 | break; | |
583 | } | |
584 | ||
585 | if (!aspeed_i2c_bus_is_master(bus)) { | |
586 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
587 | __func__); | |
588 | break; | |
589 | } | |
590 | ||
591 | if (!aic->has_dma && | |
592 | (SHARED_FIELD_EX32(value, RX_DMA_EN) || | |
593 | SHARED_FIELD_EX32(value, TX_DMA_EN))) { | |
594 | qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); | |
595 | break; | |
596 | } | |
597 | ||
598 | if (bus->regs[R_I2CM_INTR_STS] & 0xffff0000) { | |
599 | qemu_log_mask(LOG_UNIMP, "%s: Packet mode is not implemented\n", | |
600 | __func__); | |
601 | break; | |
602 | } | |
603 | ||
604 | value &= 0xff0ffbfb; | |
605 | if (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, W1_CTRL)) { | |
606 | bus->regs[R_I2CM_CMD] |= value; | |
607 | } else { | |
608 | bus->regs[R_I2CM_CMD] = value; | |
609 | } | |
610 | ||
611 | aspeed_i2c_bus_handle_cmd(bus, value); | |
612 | aspeed_i2c_bus_raise_interrupt(bus); | |
613 | break; | |
614 | case A_I2CM_DMA_TX_ADDR: | |
615 | bus->regs[R_I2CM_DMA_TX_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, | |
616 | ADDR); | |
617 | bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, ADDR); | |
618 | bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, | |
619 | TX_BUF_LEN) + 1; | |
620 | break; | |
621 | case A_I2CM_DMA_RX_ADDR: | |
622 | bus->regs[R_I2CM_DMA_RX_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, | |
623 | ADDR); | |
624 | bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, ADDR); | |
625 | bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, | |
626 | RX_BUF_LEN) + 1; | |
627 | break; | |
628 | case A_I2CM_DMA_LEN: | |
629 | w1t = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN_W1T) || | |
630 | ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN_W1T); | |
631 | /* If none of the w1t bits are set, just write to the reg as normal. */ | |
632 | if (!w1t) { | |
633 | bus->regs[R_I2CM_DMA_LEN] = value; | |
634 | break; | |
635 | } | |
636 | if (ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN_W1T)) { | |
637 | ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN, | |
638 | FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN)); | |
639 | } | |
640 | if (ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN_W1T)) { | |
641 | ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN, | |
642 | FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN)); | |
643 | } | |
644 | break; | |
645 | case A_I2CM_DMA_LEN_STS: | |
646 | /* Writes clear to 0 */ | |
647 | bus->regs[R_I2CM_DMA_LEN_STS] = 0; | |
648 | break; | |
649 | case A_I2CC_DMA_ADDR: | |
650 | case A_I2CC_DMA_LEN: | |
651 | /* RO */ | |
652 | break; | |
653 | case A_I2CS_DMA_LEN_STS: | |
654 | case A_I2CS_DMA_TX_ADDR: | |
655 | case A_I2CS_DMA_RX_ADDR: | |
656 | case A_I2CS_DEV_ADDR: | |
657 | case A_I2CS_INTR_CTRL: | |
658 | case A_I2CS_INTR_STS: | |
659 | case A_I2CS_CMD: | |
660 | case A_I2CS_DMA_LEN: | |
661 | qemu_log_mask(LOG_UNIMP, "%s: Slave mode is not implemented\n", | |
662 | __func__); | |
663 | break; | |
664 | default: | |
665 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
666 | __func__, offset); | |
667 | } | |
668 | } | |
669 | ||
670 | static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, | |
671 | uint64_t value, unsigned size) | |
16020011 | 672 | { |
51dd4923 | 673 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); |
bb626e5b | 674 | bool handle_rx; |
16020011 | 675 | |
66cc84a1 CLG |
676 | trace_aspeed_i2c_bus_write(bus->id, offset, size, value); |
677 | ||
16020011 | 678 | switch (offset) { |
3be3d6cc | 679 | case A_I2CD_FUN_CTRL: |
ba2cccd6 | 680 | if (SHARED_FIELD_EX32(value, SLAVE_EN)) { |
16020011 CLG |
681 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", |
682 | __func__); | |
683 | break; | |
684 | } | |
2260fc6f | 685 | bus->regs[R_I2CD_FUN_CTRL] = value & 0x0071C3FF; |
16020011 | 686 | break; |
3be3d6cc | 687 | case A_I2CD_AC_TIMING1: |
2260fc6f | 688 | bus->regs[R_I2CD_AC_TIMING1] = value & 0xFFFFF0F; |
16020011 | 689 | break; |
3be3d6cc | 690 | case A_I2CD_AC_TIMING2: |
2260fc6f | 691 | bus->regs[R_I2CD_AC_TIMING2] = value & 0x7; |
16020011 | 692 | break; |
3be3d6cc | 693 | case A_I2CD_INTR_CTRL: |
2260fc6f | 694 | bus->regs[R_I2CD_INTR_CTRL] = value & 0x7FFF; |
16020011 | 695 | break; |
3be3d6cc | 696 | case A_I2CD_INTR_STS: |
ba2cccd6 JK |
697 | handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_INTR_STS, RX_DONE) |
698 | && SHARED_FIELD_EX32(value, RX_DONE); | |
2260fc6f JK |
699 | bus->regs[R_I2CD_INTR_STS] &= ~(value & 0x7FFF); |
700 | if (!bus->regs[R_I2CD_INTR_STS]) { | |
5540cb97 | 701 | bus->controller->intr_status &= ~(1 << bus->id); |
51dd4923 | 702 | qemu_irq_lower(aic->bus_get_irq(bus)); |
5540cb97 | 703 | } |
ba2cccd6 JK |
704 | if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, |
705 | M_RX_CMD) || | |
706 | SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, | |
707 | M_S_RX_CMD_LAST))) { | |
bb626e5b GR |
708 | aspeed_i2c_handle_rx_cmd(bus); |
709 | aspeed_i2c_bus_raise_interrupt(bus); | |
710 | } | |
16020011 | 711 | break; |
3be3d6cc | 712 | case A_I2CD_DEV_ADDR: |
16020011 CLG |
713 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", |
714 | __func__); | |
715 | break; | |
3be3d6cc | 716 | case A_I2CD_POOL_CTRL: |
2260fc6f JK |
717 | bus->regs[R_I2CD_POOL_CTRL] &= ~0xffffff; |
718 | bus->regs[R_I2CD_POOL_CTRL] |= (value & 0xffffff); | |
6054fc73 CLG |
719 | break; |
720 | ||
3be3d6cc | 721 | case A_I2CD_BYTE_BUF: |
ba2cccd6 | 722 | SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_BYTE_BUF, TX_BUF, value); |
16020011 | 723 | break; |
3be3d6cc | 724 | case A_I2CD_CMD: |
16020011 CLG |
725 | if (!aspeed_i2c_bus_is_enabled(bus)) { |
726 | break; | |
727 | } | |
728 | ||
729 | if (!aspeed_i2c_bus_is_master(bus)) { | |
730 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
731 | __func__); | |
732 | break; | |
733 | } | |
734 | ||
545d6bef | 735 | if (!aic->has_dma && |
ba2cccd6 JK |
736 | (SHARED_FIELD_EX32(value, RX_DMA_EN) || |
737 | SHARED_FIELD_EX32(value, TX_DMA_EN))) { | |
545d6bef CLG |
738 | qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); |
739 | break; | |
740 | } | |
741 | ||
ba2cccd6 JK |
742 | bus->regs[R_I2CD_CMD] &= ~0xFFFF; |
743 | bus->regs[R_I2CD_CMD] |= value & 0xFFFF; | |
744 | ||
16020011 | 745 | aspeed_i2c_bus_handle_cmd(bus, value); |
ddabca75 | 746 | aspeed_i2c_bus_raise_interrupt(bus); |
16020011 | 747 | break; |
3be3d6cc | 748 | case A_I2CD_DMA_ADDR: |
545d6bef CLG |
749 | if (!aic->has_dma) { |
750 | qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); | |
751 | break; | |
752 | } | |
753 | ||
2260fc6f | 754 | bus->regs[R_I2CD_DMA_ADDR] = value & 0x3ffffffc; |
545d6bef CLG |
755 | break; |
756 | ||
3be3d6cc | 757 | case A_I2CD_DMA_LEN: |
545d6bef CLG |
758 | if (!aic->has_dma) { |
759 | qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); | |
760 | break; | |
761 | } | |
762 | ||
2260fc6f JK |
763 | bus->regs[R_I2CD_DMA_LEN] = value & 0xfff; |
764 | if (!bus->regs[R_I2CD_DMA_LEN]) { | |
545d6bef CLG |
765 | qemu_log_mask(LOG_UNIMP, "%s: invalid DMA length\n", __func__); |
766 | } | |
767 | break; | |
16020011 CLG |
768 | |
769 | default: | |
770 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
771 | __func__, offset); | |
772 | } | |
773 | } | |
774 | ||
ba2cccd6 JK |
775 | static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, |
776 | uint64_t value, unsigned size) | |
777 | { | |
778 | AspeedI2CBus *bus = opaque; | |
779 | if (aspeed_i2c_is_new_mode(bus->controller)) { | |
780 | aspeed_i2c_bus_new_write(bus, offset, value, size); | |
781 | } else { | |
782 | aspeed_i2c_bus_old_write(bus, offset, value, size); | |
783 | } | |
784 | } | |
785 | ||
16020011 CLG |
786 | static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, |
787 | unsigned size) | |
788 | { | |
789 | AspeedI2CState *s = opaque; | |
790 | ||
791 | switch (offset) { | |
3be3d6cc | 792 | case A_I2C_CTRL_STATUS: |
16020011 | 793 | return s->intr_status; |
3be3d6cc | 794 | case A_I2C_CTRL_GLOBAL: |
aab90b1c | 795 | return s->ctrl_global; |
ba2cccd6 JK |
796 | case A_I2C_CTRL_NEW_CLK_DIVIDER: |
797 | if (aspeed_i2c_is_new_mode(s)) { | |
798 | return s->new_clk_divider; | |
799 | } | |
800 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
801 | __func__, offset); | |
802 | break; | |
16020011 CLG |
803 | default: |
804 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
805 | __func__, offset); | |
806 | break; | |
807 | } | |
808 | ||
809 | return -1; | |
810 | } | |
811 | ||
812 | static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, | |
813 | uint64_t value, unsigned size) | |
814 | { | |
aab90b1c CLG |
815 | AspeedI2CState *s = opaque; |
816 | ||
16020011 | 817 | switch (offset) { |
3be3d6cc | 818 | case A_I2C_CTRL_GLOBAL: |
aab90b1c CLG |
819 | s->ctrl_global = value; |
820 | break; | |
ba2cccd6 JK |
821 | case A_I2C_CTRL_NEW_CLK_DIVIDER: |
822 | if (aspeed_i2c_is_new_mode(s)) { | |
823 | s->new_clk_divider = value; | |
824 | } else { | |
825 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx | |
826 | "\n", __func__, offset); | |
827 | } | |
828 | break; | |
3be3d6cc | 829 | case A_I2C_CTRL_STATUS: |
16020011 CLG |
830 | default: |
831 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
832 | __func__, offset); | |
833 | break; | |
834 | } | |
835 | } | |
836 | ||
837 | static const MemoryRegionOps aspeed_i2c_bus_ops = { | |
838 | .read = aspeed_i2c_bus_read, | |
839 | .write = aspeed_i2c_bus_write, | |
840 | .endianness = DEVICE_LITTLE_ENDIAN, | |
841 | }; | |
842 | ||
843 | static const MemoryRegionOps aspeed_i2c_ctrl_ops = { | |
844 | .read = aspeed_i2c_ctrl_read, | |
845 | .write = aspeed_i2c_ctrl_write, | |
846 | .endianness = DEVICE_LITTLE_ENDIAN, | |
847 | }; | |
848 | ||
6054fc73 CLG |
849 | static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, |
850 | unsigned size) | |
851 | { | |
852 | AspeedI2CState *s = opaque; | |
853 | uint64_t ret = 0; | |
854 | int i; | |
855 | ||
856 | for (i = 0; i < size; i++) { | |
857 | ret |= (uint64_t) s->pool[offset + i] << (8 * i); | |
858 | } | |
859 | ||
860 | return ret; | |
861 | } | |
862 | ||
863 | static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, | |
864 | uint64_t value, unsigned size) | |
865 | { | |
866 | AspeedI2CState *s = opaque; | |
867 | int i; | |
868 | ||
869 | for (i = 0; i < size; i++) { | |
870 | s->pool[offset + i] = (value >> (8 * i)) & 0xFF; | |
871 | } | |
872 | } | |
873 | ||
874 | static const MemoryRegionOps aspeed_i2c_pool_ops = { | |
875 | .read = aspeed_i2c_pool_read, | |
876 | .write = aspeed_i2c_pool_write, | |
877 | .endianness = DEVICE_LITTLE_ENDIAN, | |
878 | .valid = { | |
879 | .min_access_size = 1, | |
880 | .max_access_size = 4, | |
881 | }, | |
882 | }; | |
883 | ||
16020011 CLG |
884 | static const VMStateDescription aspeed_i2c_bus_vmstate = { |
885 | .name = TYPE_ASPEED_I2C, | |
ba2cccd6 JK |
886 | .version_id = 5, |
887 | .minimum_version_id = 5, | |
16020011 | 888 | .fields = (VMStateField[]) { |
ba2cccd6 | 889 | VMSTATE_UINT32_ARRAY(regs, AspeedI2CBus, ASPEED_I2C_NEW_NUM_REG), |
16020011 CLG |
890 | VMSTATE_END_OF_LIST() |
891 | } | |
892 | }; | |
893 | ||
894 | static const VMStateDescription aspeed_i2c_vmstate = { | |
895 | .name = TYPE_ASPEED_I2C, | |
6054fc73 CLG |
896 | .version_id = 2, |
897 | .minimum_version_id = 2, | |
16020011 CLG |
898 | .fields = (VMStateField[]) { |
899 | VMSTATE_UINT32(intr_status, AspeedI2CState), | |
900 | VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, | |
901 | ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, | |
902 | AspeedI2CBus), | |
6054fc73 | 903 | VMSTATE_UINT8_ARRAY(pool, AspeedI2CState, ASPEED_I2C_MAX_POOL_SIZE), |
16020011 CLG |
904 | VMSTATE_END_OF_LIST() |
905 | } | |
906 | }; | |
907 | ||
908 | static void aspeed_i2c_reset(DeviceState *dev) | |
909 | { | |
16020011 CLG |
910 | AspeedI2CState *s = ASPEED_I2C(dev); |
911 | ||
912 | s->intr_status = 0; | |
60261038 CLG |
913 | } |
914 | ||
915 | static void aspeed_i2c_instance_init(Object *obj) | |
916 | { | |
917 | AspeedI2CState *s = ASPEED_I2C(obj); | |
918 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); | |
919 | int i; | |
16020011 | 920 | |
f7da1aa8 | 921 | for (i = 0; i < aic->num_busses; i++) { |
60261038 CLG |
922 | object_initialize_child(obj, "bus[*]", &s->busses[i], |
923 | TYPE_ASPEED_I2C_BUS); | |
16020011 CLG |
924 | } |
925 | } | |
926 | ||
927 | /* | |
f7da1aa8 | 928 | * Address Definitions (AST2400 and AST2500) |
16020011 CLG |
929 | * |
930 | * 0x000 ... 0x03F: Global Register | |
931 | * 0x040 ... 0x07F: Device 1 | |
932 | * 0x080 ... 0x0BF: Device 2 | |
933 | * 0x0C0 ... 0x0FF: Device 3 | |
934 | * 0x100 ... 0x13F: Device 4 | |
935 | * 0x140 ... 0x17F: Device 5 | |
936 | * 0x180 ... 0x1BF: Device 6 | |
937 | * 0x1C0 ... 0x1FF: Device 7 | |
938 | * 0x200 ... 0x2FF: Buffer Pool (unused in linux driver) | |
939 | * 0x300 ... 0x33F: Device 8 | |
940 | * 0x340 ... 0x37F: Device 9 | |
941 | * 0x380 ... 0x3BF: Device 10 | |
942 | * 0x3C0 ... 0x3FF: Device 11 | |
943 | * 0x400 ... 0x43F: Device 12 | |
944 | * 0x440 ... 0x47F: Device 13 | |
945 | * 0x480 ... 0x4BF: Device 14 | |
946 | * 0x800 ... 0xFFF: Buffer Pool (unused in linux driver) | |
947 | */ | |
948 | static void aspeed_i2c_realize(DeviceState *dev, Error **errp) | |
949 | { | |
950 | int i; | |
951 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
952 | AspeedI2CState *s = ASPEED_I2C(dev); | |
f7da1aa8 | 953 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); |
16020011 CLG |
954 | |
955 | sysbus_init_irq(sbd, &s->irq); | |
956 | memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, | |
957 | "aspeed.i2c", 0x1000); | |
958 | sysbus_init_mmio(sbd, &s->iomem); | |
959 | ||
f7da1aa8 | 960 | for (i = 0; i < aic->num_busses; i++) { |
60261038 | 961 | Object *bus = OBJECT(&s->busses[i]); |
f7da1aa8 | 962 | int offset = i < aic->gap ? 1 : 5; |
51dd4923 | 963 | |
60261038 CLG |
964 | if (!object_property_set_link(bus, "controller", OBJECT(s), errp)) { |
965 | return; | |
966 | } | |
967 | ||
968 | if (!object_property_set_uint(bus, "bus-id", i, errp)) { | |
969 | return; | |
970 | } | |
971 | ||
972 | if (!sysbus_realize(SYS_BUS_DEVICE(bus), errp)) { | |
973 | return; | |
974 | } | |
975 | ||
f7da1aa8 | 976 | memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset), |
16020011 CLG |
977 | &s->busses[i].mr); |
978 | } | |
6054fc73 CLG |
979 | |
980 | memory_region_init_io(&s->pool_iomem, OBJECT(s), &aspeed_i2c_pool_ops, s, | |
981 | "aspeed.i2c-pool", aic->pool_size); | |
982 | memory_region_add_subregion(&s->iomem, aic->pool_base, &s->pool_iomem); | |
545d6bef CLG |
983 | |
984 | if (aic->has_dma) { | |
985 | if (!s->dram_mr) { | |
986 | error_setg(errp, TYPE_ASPEED_I2C ": 'dram' link not set"); | |
987 | return; | |
988 | } | |
989 | ||
3f7a53b2 CLG |
990 | address_space_init(&s->dram_as, s->dram_mr, |
991 | TYPE_ASPEED_I2C "-dma-dram"); | |
545d6bef | 992 | } |
16020011 CLG |
993 | } |
994 | ||
545d6bef CLG |
995 | static Property aspeed_i2c_properties[] = { |
996 | DEFINE_PROP_LINK("dram", AspeedI2CState, dram_mr, | |
997 | TYPE_MEMORY_REGION, MemoryRegion *), | |
998 | DEFINE_PROP_END_OF_LIST(), | |
999 | }; | |
1000 | ||
16020011 CLG |
1001 | static void aspeed_i2c_class_init(ObjectClass *klass, void *data) |
1002 | { | |
1003 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1004 | ||
1005 | dc->vmsd = &aspeed_i2c_vmstate; | |
1006 | dc->reset = aspeed_i2c_reset; | |
4f67d30b | 1007 | device_class_set_props(dc, aspeed_i2c_properties); |
16020011 CLG |
1008 | dc->realize = aspeed_i2c_realize; |
1009 | dc->desc = "Aspeed I2C Controller"; | |
1010 | } | |
1011 | ||
1012 | static const TypeInfo aspeed_i2c_info = { | |
1013 | .name = TYPE_ASPEED_I2C, | |
1014 | .parent = TYPE_SYS_BUS_DEVICE, | |
60261038 | 1015 | .instance_init = aspeed_i2c_instance_init, |
16020011 CLG |
1016 | .instance_size = sizeof(AspeedI2CState), |
1017 | .class_init = aspeed_i2c_class_init, | |
f7da1aa8 CLG |
1018 | .class_size = sizeof(AspeedI2CClass), |
1019 | .abstract = true, | |
1020 | }; | |
1021 | ||
60261038 CLG |
1022 | static void aspeed_i2c_bus_reset(DeviceState *dev) |
1023 | { | |
1024 | AspeedI2CBus *s = ASPEED_I2C_BUS(dev); | |
1025 | ||
2260fc6f | 1026 | memset(s->regs, 0, sizeof(s->regs)); |
60261038 CLG |
1027 | i2c_end_transfer(s->bus); |
1028 | } | |
1029 | ||
1030 | static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) | |
1031 | { | |
1032 | AspeedI2CBus *s = ASPEED_I2C_BUS(dev); | |
1033 | AspeedI2CClass *aic; | |
1034 | g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I2C_BUS ".%d", s->id); | |
1035 | ||
1036 | if (!s->controller) { | |
1037 | error_setg(errp, TYPE_ASPEED_I2C_BUS ": 'controller' link not set"); | |
1038 | return; | |
1039 | } | |
1040 | ||
1041 | aic = ASPEED_I2C_GET_CLASS(s->controller); | |
1042 | ||
1043 | sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); | |
1044 | ||
1045 | s->bus = i2c_init_bus(dev, name); | |
1046 | ||
1047 | memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops, | |
1048 | s, name, aic->reg_size); | |
1049 | sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); | |
1050 | } | |
1051 | ||
1052 | static Property aspeed_i2c_bus_properties[] = { | |
1053 | DEFINE_PROP_UINT8("bus-id", AspeedI2CBus, id, 0), | |
1054 | DEFINE_PROP_LINK("controller", AspeedI2CBus, controller, TYPE_ASPEED_I2C, | |
1055 | AspeedI2CState *), | |
1056 | DEFINE_PROP_END_OF_LIST(), | |
1057 | }; | |
1058 | ||
1059 | static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) | |
1060 | { | |
1061 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1062 | ||
1063 | dc->desc = "Aspeed I2C Bus"; | |
1064 | dc->realize = aspeed_i2c_bus_realize; | |
1065 | dc->reset = aspeed_i2c_bus_reset; | |
1066 | device_class_set_props(dc, aspeed_i2c_bus_properties); | |
1067 | } | |
1068 | ||
1069 | static const TypeInfo aspeed_i2c_bus_info = { | |
1070 | .name = TYPE_ASPEED_I2C_BUS, | |
1071 | .parent = TYPE_SYS_BUS_DEVICE, | |
1072 | .instance_size = sizeof(AspeedI2CBus), | |
1073 | .class_init = aspeed_i2c_bus_class_init, | |
1074 | }; | |
1075 | ||
51dd4923 CLG |
1076 | static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) |
1077 | { | |
1078 | return bus->controller->irq; | |
1079 | } | |
1080 | ||
6054fc73 CLG |
1081 | static uint8_t *aspeed_2400_i2c_bus_pool_base(AspeedI2CBus *bus) |
1082 | { | |
1083 | uint8_t *pool_page = | |
2260fc6f JK |
1084 | &bus->controller->pool[ARRAY_FIELD_EX32(bus->regs, I2CD_FUN_CTRL, |
1085 | POOL_PAGE_SEL) * 0x100]; | |
6054fc73 | 1086 | |
2260fc6f | 1087 | return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; |
6054fc73 CLG |
1088 | } |
1089 | ||
f7da1aa8 CLG |
1090 | static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) |
1091 | { | |
1092 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1093 | AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); | |
1094 | ||
1095 | dc->desc = "ASPEED 2400 I2C Controller"; | |
1096 | ||
1097 | aic->num_busses = 14; | |
1098 | aic->reg_size = 0x40; | |
1099 | aic->gap = 7; | |
51dd4923 | 1100 | aic->bus_get_irq = aspeed_2400_i2c_bus_get_irq; |
6054fc73 CLG |
1101 | aic->pool_size = 0x800; |
1102 | aic->pool_base = 0x800; | |
1103 | aic->bus_pool_base = aspeed_2400_i2c_bus_pool_base; | |
f7da1aa8 CLG |
1104 | } |
1105 | ||
1106 | static const TypeInfo aspeed_2400_i2c_info = { | |
1107 | .name = TYPE_ASPEED_2400_I2C, | |
1108 | .parent = TYPE_ASPEED_I2C, | |
1109 | .class_init = aspeed_2400_i2c_class_init, | |
1110 | }; | |
1111 | ||
51dd4923 CLG |
1112 | static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus) |
1113 | { | |
1114 | return bus->controller->irq; | |
1115 | } | |
1116 | ||
6054fc73 CLG |
1117 | static uint8_t *aspeed_2500_i2c_bus_pool_base(AspeedI2CBus *bus) |
1118 | { | |
1119 | return &bus->controller->pool[bus->id * 0x10]; | |
1120 | } | |
1121 | ||
f7da1aa8 CLG |
1122 | static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) |
1123 | { | |
1124 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1125 | AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); | |
1126 | ||
1127 | dc->desc = "ASPEED 2500 I2C Controller"; | |
1128 | ||
1129 | aic->num_busses = 14; | |
1130 | aic->reg_size = 0x40; | |
1131 | aic->gap = 7; | |
51dd4923 | 1132 | aic->bus_get_irq = aspeed_2500_i2c_bus_get_irq; |
6054fc73 CLG |
1133 | aic->pool_size = 0x100; |
1134 | aic->pool_base = 0x200; | |
1135 | aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; | |
aab90b1c | 1136 | aic->check_sram = true; |
545d6bef | 1137 | aic->has_dma = true; |
f7da1aa8 CLG |
1138 | } |
1139 | ||
1140 | static const TypeInfo aspeed_2500_i2c_info = { | |
1141 | .name = TYPE_ASPEED_2500_I2C, | |
1142 | .parent = TYPE_ASPEED_I2C, | |
1143 | .class_init = aspeed_2500_i2c_class_init, | |
16020011 CLG |
1144 | }; |
1145 | ||
51dd4923 CLG |
1146 | static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) |
1147 | { | |
1148 | return bus->irq; | |
1149 | } | |
1150 | ||
6054fc73 CLG |
1151 | static uint8_t *aspeed_2600_i2c_bus_pool_base(AspeedI2CBus *bus) |
1152 | { | |
1153 | return &bus->controller->pool[bus->id * 0x20]; | |
1154 | } | |
1155 | ||
51dd4923 CLG |
1156 | static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) |
1157 | { | |
1158 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1159 | AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); | |
1160 | ||
1161 | dc->desc = "ASPEED 2600 I2C Controller"; | |
1162 | ||
1163 | aic->num_busses = 16; | |
1164 | aic->reg_size = 0x80; | |
1165 | aic->gap = -1; /* no gap */ | |
1166 | aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; | |
6054fc73 CLG |
1167 | aic->pool_size = 0x200; |
1168 | aic->pool_base = 0xC00; | |
1169 | aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; | |
545d6bef | 1170 | aic->has_dma = true; |
51dd4923 CLG |
1171 | } |
1172 | ||
1173 | static const TypeInfo aspeed_2600_i2c_info = { | |
1174 | .name = TYPE_ASPEED_2600_I2C, | |
1175 | .parent = TYPE_ASPEED_I2C, | |
1176 | .class_init = aspeed_2600_i2c_class_init, | |
1177 | }; | |
1178 | ||
b35802ce CLG |
1179 | static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) |
1180 | { | |
1181 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1182 | AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); | |
1183 | ||
1184 | dc->desc = "ASPEED 1030 I2C Controller"; | |
1185 | ||
1186 | aic->num_busses = 14; | |
1187 | aic->reg_size = 0x80; | |
1188 | aic->gap = -1; /* no gap */ | |
1189 | aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; | |
1190 | aic->pool_size = 0x200; | |
1191 | aic->pool_base = 0xC00; | |
1192 | aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; | |
1193 | aic->has_dma = true; | |
1194 | } | |
1195 | ||
1196 | static const TypeInfo aspeed_1030_i2c_info = { | |
1197 | .name = TYPE_ASPEED_1030_I2C, | |
1198 | .parent = TYPE_ASPEED_I2C, | |
1199 | .class_init = aspeed_1030_i2c_class_init, | |
1200 | }; | |
1201 | ||
16020011 CLG |
1202 | static void aspeed_i2c_register_types(void) |
1203 | { | |
60261038 | 1204 | type_register_static(&aspeed_i2c_bus_info); |
16020011 | 1205 | type_register_static(&aspeed_i2c_info); |
f7da1aa8 CLG |
1206 | type_register_static(&aspeed_2400_i2c_info); |
1207 | type_register_static(&aspeed_2500_i2c_info); | |
51dd4923 | 1208 | type_register_static(&aspeed_2600_i2c_info); |
b35802ce | 1209 | type_register_static(&aspeed_1030_i2c_info); |
16020011 CLG |
1210 | } |
1211 | ||
1212 | type_init(aspeed_i2c_register_types) | |
1213 | ||
1214 | ||
7a204cbd | 1215 | I2CBus *aspeed_i2c_get_bus(AspeedI2CState *s, int busnr) |
16020011 | 1216 | { |
f7da1aa8 | 1217 | AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); |
16020011 CLG |
1218 | I2CBus *bus = NULL; |
1219 | ||
f7da1aa8 | 1220 | if (busnr >= 0 && busnr < aic->num_busses) { |
16020011 CLG |
1221 | bus = s->busses[busnr].bus; |
1222 | } | |
1223 | ||
1224 | return bus; | |
1225 | } |