116020011SCédric Le Goater /* 216020011SCédric Le Goater * ARM Aspeed I2C controller 316020011SCédric Le Goater * 416020011SCédric Le Goater * Copyright (C) 2016 IBM Corp. 516020011SCédric Le Goater * 616020011SCédric Le Goater * This program is free software; you can redistribute it and/or 716020011SCédric Le Goater * modify it under the terms of the GNU General Public License 816020011SCédric Le Goater * as published by the Free Software Foundation; either version 2 916020011SCédric Le Goater * of the License, or (at your option) any later version. 1016020011SCédric Le Goater * 1116020011SCédric Le Goater * This program is distributed in the hope that it will be useful, 1216020011SCédric Le Goater * but WITHOUT ANY WARRANTY; without even the implied warranty of 1316020011SCédric Le Goater * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1416020011SCédric Le Goater * GNU General Public License for more details. 1516020011SCédric Le Goater * 1616020011SCédric Le Goater * You should have received a copy of the GNU General Public License 1716020011SCédric Le Goater * along with this program; if not, see <http://www.gnu.org/licenses/>. 1816020011SCédric Le Goater * 1916020011SCédric Le Goater */ 2016020011SCédric Le Goater 2116020011SCédric Le Goater #include "qemu/osdep.h" 2216020011SCédric Le Goater #include "hw/sysbus.h" 23d6454270SMarkus Armbruster #include "migration/vmstate.h" 24b03ec4ffSKlaus Jensen #include "qemu/cutils.h" 2516020011SCédric Le Goater #include "qemu/log.h" 260b8fa32fSMarkus Armbruster #include "qemu/module.h" 27545d6befSCédric Le Goater #include "qemu/error-report.h" 28545d6befSCédric Le Goater #include "qapi/error.h" 2916020011SCédric Le Goater #include "hw/i2c/aspeed_i2c.h" 3064552b6bSMarkus Armbruster #include "hw/irq.h" 31545d6befSCédric Le Goater #include "hw/qdev-properties.h" 323be3d6ccSJoe Komlodi #include "hw/registerfields.h" 3366cc84a1SCédric Le Goater #include "trace.h" 3416020011SCédric Le Goater 35*33e30f11SCédric Le Goater /* Enable SLAVE_ADDR_RX_MATCH always */ 36*33e30f11SCédric Le Goater #define R_I2CD_INTR_STS_ALWAYS_ENABLE R_I2CD_INTR_STS_SLAVE_ADDR_RX_MATCH_MASK 37*33e30f11SCédric Le Goater 3816020011SCédric Le Goater static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) 3916020011SCédric Le Goater { 4051dd4923SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); 41ba2cccd6SJoe Komlodi uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); 42ba2cccd6SJoe Komlodi uint32_t intr_ctrl_reg = aspeed_i2c_bus_intr_ctrl_offset(bus); 43*33e30f11SCédric Le Goater uint32_t intr_ctrl_mask = bus->regs[intr_ctrl_reg] | 44*33e30f11SCédric Le Goater R_I2CD_INTR_STS_ALWAYS_ENABLE; 45ba2cccd6SJoe Komlodi bool raise_irq; 4651dd4923SCédric Le Goater 47b03ec4ffSKlaus Jensen if (trace_event_get_state_backends(TRACE_ASPEED_I2C_BUS_RAISE_INTERRUPT)) { 48*33e30f11SCédric Le Goater g_autofree char *buf = g_strdup_printf("%s%s%s%s%s%s%s", 490efec47bSJoe Komlodi aspeed_i2c_bus_pkt_mode_en(bus) && 500efec47bSJoe Komlodi ARRAY_FIELD_EX32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE) ? 510efec47bSJoe Komlodi "pktdone|" : "", 52b03ec4ffSKlaus Jensen SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_NAK) ? 53b03ec4ffSKlaus Jensen "nak|" : "", 54b03ec4ffSKlaus Jensen SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, TX_ACK) ? 55b03ec4ffSKlaus Jensen "ack|" : "", 56b03ec4ffSKlaus Jensen SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE) ? 57b03ec4ffSKlaus Jensen "done|" : "", 58*33e30f11SCédric Le Goater ARRAY_FIELD_EX32(bus->regs, I2CD_INTR_STS, SLAVE_ADDR_RX_MATCH) ? 59*33e30f11SCédric Le Goater "slave-match|" : "", 60ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, NORMAL_STOP) ? 61ba2cccd6SJoe Komlodi "normal|" : "", 62b03ec4ffSKlaus Jensen SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, ABNORMAL) ? 63b03ec4ffSKlaus Jensen "abnormal" : ""); 64b03ec4ffSKlaus Jensen 65b03ec4ffSKlaus Jensen trace_aspeed_i2c_bus_raise_interrupt(bus->regs[reg_intr_sts], buf); 66b03ec4ffSKlaus Jensen } 67b03ec4ffSKlaus Jensen 68*33e30f11SCédric Le Goater raise_irq = bus->regs[reg_intr_sts] & intr_ctrl_mask ; 69b03ec4ffSKlaus Jensen 70ba2cccd6SJoe Komlodi /* In packet mode we don't mask off INTR_STS */ 71ba2cccd6SJoe Komlodi if (!aspeed_i2c_bus_pkt_mode_en(bus)) { 72*33e30f11SCédric Le Goater bus->regs[reg_intr_sts] &= intr_ctrl_mask; 73ba2cccd6SJoe Komlodi } 74b03ec4ffSKlaus Jensen 75ba2cccd6SJoe Komlodi if (raise_irq) { 7616020011SCédric Le Goater bus->controller->intr_status |= 1 << bus->id; 7751dd4923SCédric Le Goater qemu_irq_raise(aic->bus_get_irq(bus)); 7816020011SCédric Le Goater } 7916020011SCédric Le Goater } 8016020011SCédric Le Goater 81ba2cccd6SJoe Komlodi static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, 8216020011SCédric Le Goater unsigned size) 8316020011SCédric Le Goater { 84545d6befSCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); 852260fc6fSJoe Komlodi uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; 8616020011SCédric Le Goater 8716020011SCédric Le Goater switch (offset) { 883be3d6ccSJoe Komlodi case A_I2CD_FUN_CTRL: 893be3d6ccSJoe Komlodi case A_I2CD_AC_TIMING1: 903be3d6ccSJoe Komlodi case A_I2CD_AC_TIMING2: 913be3d6ccSJoe Komlodi case A_I2CD_INTR_CTRL: 923be3d6ccSJoe Komlodi case A_I2CD_INTR_STS: 93d72a712cSKlaus Jensen case A_I2CD_DEV_ADDR: 943be3d6ccSJoe Komlodi case A_I2CD_POOL_CTRL: 953be3d6ccSJoe Komlodi case A_I2CD_BYTE_BUF: 962260fc6fSJoe Komlodi /* Value is already set, don't do anything. */ 9766cc84a1SCédric Le Goater break; 983be3d6ccSJoe Komlodi case A_I2CD_CMD: 99ba2cccd6SJoe Komlodi value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); 10066cc84a1SCédric Le Goater break; 1013be3d6ccSJoe Komlodi case A_I2CD_DMA_ADDR: 102545d6befSCédric Le Goater if (!aic->has_dma) { 103545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); 1042260fc6fSJoe Komlodi value = -1; 105545d6befSCédric Le Goater } 10666cc84a1SCédric Le Goater break; 1073be3d6ccSJoe Komlodi case A_I2CD_DMA_LEN: 108545d6befSCédric Le Goater if (!aic->has_dma) { 109545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); 1102260fc6fSJoe Komlodi value = -1; 111545d6befSCédric Le Goater } 11266cc84a1SCédric Le Goater break; 11366cc84a1SCédric Le Goater 11416020011SCédric Le Goater default: 11516020011SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 11616020011SCédric Le Goater "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); 11766cc84a1SCédric Le Goater value = -1; 11866cc84a1SCédric Le Goater break; 11916020011SCédric Le Goater } 12066cc84a1SCédric Le Goater 12166cc84a1SCédric Le Goater trace_aspeed_i2c_bus_read(bus->id, offset, size, value); 12266cc84a1SCédric Le Goater return value; 12316020011SCédric Le Goater } 12416020011SCédric Le Goater 125ba2cccd6SJoe Komlodi static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, 126ba2cccd6SJoe Komlodi unsigned size) 127ba2cccd6SJoe Komlodi { 128ba2cccd6SJoe Komlodi uint64_t value = bus->regs[offset / sizeof(*bus->regs)]; 129ba2cccd6SJoe Komlodi 130ba2cccd6SJoe Komlodi switch (offset) { 131ba2cccd6SJoe Komlodi case A_I2CC_FUN_CTRL: 132ba2cccd6SJoe Komlodi case A_I2CC_AC_TIMING: 133ba2cccd6SJoe Komlodi case A_I2CC_POOL_CTRL: 134ba2cccd6SJoe Komlodi case A_I2CM_INTR_CTRL: 135ba2cccd6SJoe Komlodi case A_I2CM_INTR_STS: 136ba2cccd6SJoe Komlodi case A_I2CC_MS_TXRX_BYTE_BUF: 137ba2cccd6SJoe Komlodi case A_I2CM_DMA_LEN: 138ba2cccd6SJoe Komlodi case A_I2CM_DMA_TX_ADDR: 139ba2cccd6SJoe Komlodi case A_I2CM_DMA_RX_ADDR: 140ba2cccd6SJoe Komlodi case A_I2CM_DMA_LEN_STS: 141ba2cccd6SJoe Komlodi case A_I2CC_DMA_ADDR: 142ba2cccd6SJoe Komlodi case A_I2CC_DMA_LEN: 143ba2cccd6SJoe Komlodi /* Value is already set, don't do anything. */ 144ba2cccd6SJoe Komlodi break; 145ba2cccd6SJoe Komlodi case A_I2CM_CMD: 146ba2cccd6SJoe Komlodi value = SHARED_FIELD_DP32(value, BUS_BUSY_STS, i2c_bus_busy(bus->bus)); 147ba2cccd6SJoe Komlodi break; 148ba2cccd6SJoe Komlodi default: 149ba2cccd6SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, 150ba2cccd6SJoe Komlodi "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); 151ba2cccd6SJoe Komlodi value = -1; 152ba2cccd6SJoe Komlodi break; 153ba2cccd6SJoe Komlodi } 154ba2cccd6SJoe Komlodi 155ba2cccd6SJoe Komlodi trace_aspeed_i2c_bus_read(bus->id, offset, size, value); 156ba2cccd6SJoe Komlodi return value; 157ba2cccd6SJoe Komlodi } 158ba2cccd6SJoe Komlodi 159ba2cccd6SJoe Komlodi static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, 160ba2cccd6SJoe Komlodi unsigned size) 161ba2cccd6SJoe Komlodi { 162ba2cccd6SJoe Komlodi AspeedI2CBus *bus = opaque; 163ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 164ba2cccd6SJoe Komlodi return aspeed_i2c_bus_new_read(bus, offset, size); 165ba2cccd6SJoe Komlodi } 166ba2cccd6SJoe Komlodi return aspeed_i2c_bus_old_read(bus, offset, size); 167ba2cccd6SJoe Komlodi } 168ba2cccd6SJoe Komlodi 1694960f084SCédric Le Goater static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) 1704960f084SCédric Le Goater { 171ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 172ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_STATE, 173ba2cccd6SJoe Komlodi state); 174ba2cccd6SJoe Komlodi } else { 175ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_CMD, TX_STATE, state); 176ba2cccd6SJoe Komlodi } 1774960f084SCédric Le Goater } 1784960f084SCédric Le Goater 1794960f084SCédric Le Goater static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) 1804960f084SCédric Le Goater { 181ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 182ba2cccd6SJoe Komlodi return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, 183ba2cccd6SJoe Komlodi TX_STATE); 184ba2cccd6SJoe Komlodi } 185ba2cccd6SJoe Komlodi return SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, TX_STATE); 1864960f084SCédric Le Goater } 1874960f084SCédric Le Goater 188545d6befSCédric Le Goater static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) 189545d6befSCédric Le Goater { 190545d6befSCédric Le Goater MemTxResult result; 191545d6befSCédric Le Goater AspeedI2CState *s = bus->controller; 192ba2cccd6SJoe Komlodi uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); 193ba2cccd6SJoe Komlodi uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); 194545d6befSCédric Le Goater 195ba2cccd6SJoe Komlodi result = address_space_read(&s->dram_as, bus->regs[reg_dma_addr], 196545d6befSCédric Le Goater MEMTXATTRS_UNSPECIFIED, data, 1); 197545d6befSCédric Le Goater if (result != MEMTX_OK) { 198545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n", 199ba2cccd6SJoe Komlodi __func__, bus->regs[reg_dma_addr]); 200545d6befSCédric Le Goater return -1; 201545d6befSCédric Le Goater } 202545d6befSCédric Le Goater 203ba2cccd6SJoe Komlodi bus->regs[reg_dma_addr]++; 204ba2cccd6SJoe Komlodi bus->regs[reg_dma_len]--; 205545d6befSCédric Le Goater return 0; 206545d6befSCédric Le Goater } 207545d6befSCédric Le Goater 2086054fc73SCédric Le Goater static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) 2096054fc73SCédric Le Goater { 2106054fc73SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); 2116054fc73SCédric Le Goater int ret = -1; 2126054fc73SCédric Le Goater int i; 213ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 214ba2cccd6SJoe Komlodi uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); 215ba2cccd6SJoe Komlodi uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); 216ba2cccd6SJoe Komlodi uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); 217ba2cccd6SJoe Komlodi int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, 218ba2cccd6SJoe Komlodi TX_COUNT); 2196054fc73SCédric Le Goater 220ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { 2213be3d6ccSJoe Komlodi for (i = pool_start; i < pool_tx_count; i++) { 2226054fc73SCédric Le Goater uint8_t *pool_base = aic->bus_pool_base(bus); 2236054fc73SCédric Le Goater 2243be3d6ccSJoe Komlodi trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count, 22566cc84a1SCédric Le Goater pool_base[i]); 2266054fc73SCédric Le Goater ret = i2c_send(bus->bus, pool_base[i]); 2276054fc73SCédric Le Goater if (ret) { 2286054fc73SCédric Le Goater break; 2296054fc73SCédric Le Goater } 2306054fc73SCédric Le Goater } 231ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0); 232ba2cccd6SJoe Komlodi } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { 233ba2cccd6SJoe Komlodi /* In new mode, clear how many bytes we TXed */ 234ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 235ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); 236ba2cccd6SJoe Komlodi } 237ba2cccd6SJoe Komlodi while (bus->regs[reg_dma_len]) { 238545d6befSCédric Le Goater uint8_t data; 239545d6befSCédric Le Goater aspeed_i2c_dma_read(bus, &data); 240ba2cccd6SJoe Komlodi trace_aspeed_i2c_bus_send("DMA", bus->regs[reg_dma_len], 241ba2cccd6SJoe Komlodi bus->regs[reg_dma_len], data); 242545d6befSCédric Le Goater ret = i2c_send(bus->bus, data); 243545d6befSCédric Le Goater if (ret) { 244545d6befSCédric Le Goater break; 245545d6befSCédric Le Goater } 246ba2cccd6SJoe Komlodi /* In new mode, keep track of how many bytes we TXed */ 247ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 248ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 249ba2cccd6SJoe Komlodi ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, 250ba2cccd6SJoe Komlodi TX_LEN) + 1); 251545d6befSCédric Le Goater } 252ba2cccd6SJoe Komlodi } 253ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); 2546054fc73SCédric Le Goater } else { 2552260fc6fSJoe Komlodi trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, 256ba2cccd6SJoe Komlodi bus->regs[reg_byte_buf]); 257ba2cccd6SJoe Komlodi ret = i2c_send(bus->bus, bus->regs[reg_byte_buf]); 2586054fc73SCédric Le Goater } 2596054fc73SCédric Le Goater 2606054fc73SCédric Le Goater return ret; 2616054fc73SCédric Le Goater } 2626054fc73SCédric Le Goater 2636054fc73SCédric Le Goater static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) 2646054fc73SCédric Le Goater { 2656054fc73SCédric Le Goater AspeedI2CState *s = bus->controller; 2666054fc73SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); 2676054fc73SCédric Le Goater uint8_t data; 2686054fc73SCédric Le Goater int i; 269ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 270ba2cccd6SJoe Komlodi uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); 271ba2cccd6SJoe Komlodi uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); 272ba2cccd6SJoe Komlodi uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); 273ba2cccd6SJoe Komlodi uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus); 274ba2cccd6SJoe Komlodi int pool_rx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, 275ba2cccd6SJoe Komlodi RX_COUNT); 2766054fc73SCédric Le Goater 277ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { 2786054fc73SCédric Le Goater uint8_t *pool_base = aic->bus_pool_base(bus); 2796054fc73SCédric Le Goater 2803be3d6ccSJoe Komlodi for (i = 0; i < pool_rx_count; i++) { 2816054fc73SCédric Le Goater pool_base[i] = i2c_recv(bus->bus); 2823be3d6ccSJoe Komlodi trace_aspeed_i2c_bus_recv("BUF", i + 1, pool_rx_count, 28366cc84a1SCédric Le Goater pool_base[i]); 2846054fc73SCédric Le Goater } 2856054fc73SCédric Le Goater 2866054fc73SCédric Le Goater /* Update RX count */ 287ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_pool_ctrl, RX_COUNT, i & 0xff); 288ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_BUFF_EN, 0); 289ba2cccd6SJoe Komlodi } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { 290545d6befSCédric Le Goater uint8_t data; 291ba2cccd6SJoe Komlodi /* In new mode, clear how many bytes we RXed */ 292ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 293ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); 294ba2cccd6SJoe Komlodi } 295545d6befSCédric Le Goater 296ba2cccd6SJoe Komlodi while (bus->regs[reg_dma_len]) { 297545d6befSCédric Le Goater MemTxResult result; 298545d6befSCédric Le Goater 299545d6befSCédric Le Goater data = i2c_recv(bus->bus); 300ba2cccd6SJoe Komlodi trace_aspeed_i2c_bus_recv("DMA", bus->regs[reg_dma_len], 301ba2cccd6SJoe Komlodi bus->regs[reg_dma_len], data); 302ba2cccd6SJoe Komlodi result = address_space_write(&s->dram_as, bus->regs[reg_dma_addr], 303545d6befSCédric Le Goater MEMTXATTRS_UNSPECIFIED, &data, 1); 304545d6befSCédric Le Goater if (result != MEMTX_OK) { 305545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n", 306ba2cccd6SJoe Komlodi __func__, bus->regs[reg_dma_addr]); 307545d6befSCédric Le Goater return; 308545d6befSCédric Le Goater } 309ba2cccd6SJoe Komlodi bus->regs[reg_dma_addr]++; 310ba2cccd6SJoe Komlodi bus->regs[reg_dma_len]--; 311ba2cccd6SJoe Komlodi /* In new mode, keep track of how many bytes we RXed */ 312ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 313ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 314ba2cccd6SJoe Komlodi ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN_STS, 315ba2cccd6SJoe Komlodi RX_LEN) + 1); 316545d6befSCédric Le Goater } 317ba2cccd6SJoe Komlodi } 318ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); 3196054fc73SCédric Le Goater } else { 3206054fc73SCédric Le Goater data = i2c_recv(bus->bus); 321ba2cccd6SJoe Komlodi trace_aspeed_i2c_bus_recv("BYTE", 1, 1, bus->regs[reg_byte_buf]); 322ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); 3236054fc73SCédric Le Goater } 3246054fc73SCédric Le Goater } 3256054fc73SCédric Le Goater 3267bd9c60dSGuenter Roeck static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) 3277bd9c60dSGuenter Roeck { 328ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 329ba2cccd6SJoe Komlodi uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); 330ba2cccd6SJoe Komlodi 3317bd9c60dSGuenter Roeck aspeed_i2c_set_state(bus, I2CD_MRXD); 3326054fc73SCédric Le Goater aspeed_i2c_bus_recv(bus); 333ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, RX_DONE, 1); 334ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) { 3357bd9c60dSGuenter Roeck i2c_nack(bus->bus); 3367bd9c60dSGuenter Roeck } 337ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_RX_CMD, 0); 338ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_S_RX_CMD_LAST, 0); 3397bd9c60dSGuenter Roeck aspeed_i2c_set_state(bus, I2CD_MACTIVE); 3407bd9c60dSGuenter Roeck } 3417bd9c60dSGuenter Roeck 3426054fc73SCédric Le Goater static uint8_t aspeed_i2c_get_addr(AspeedI2CBus *bus) 3436054fc73SCédric Le Goater { 3446054fc73SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); 345ba2cccd6SJoe Komlodi uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); 346ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 3476054fc73SCédric Le Goater 348ba2cccd6SJoe Komlodi if (aspeed_i2c_bus_pkt_mode_en(bus)) { 349ba2cccd6SJoe Komlodi return (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, PKT_DEV_ADDR) << 1) | 350ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD); 351ba2cccd6SJoe Komlodi } 352ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { 3536054fc73SCédric Le Goater uint8_t *pool_base = aic->bus_pool_base(bus); 3546054fc73SCédric Le Goater 3556054fc73SCédric Le Goater return pool_base[0]; 356ba2cccd6SJoe Komlodi } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { 357545d6befSCédric Le Goater uint8_t data; 358545d6befSCédric Le Goater 359545d6befSCédric Le Goater aspeed_i2c_dma_read(bus, &data); 360545d6befSCédric Le Goater return data; 3616054fc73SCédric Le Goater } else { 362ba2cccd6SJoe Komlodi return bus->regs[reg_byte_buf]; 3636054fc73SCédric Le Goater } 3646054fc73SCédric Le Goater } 3656054fc73SCédric Le Goater 366aab90b1cSCédric Le Goater static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) 367aab90b1cSCédric Le Goater { 368aab90b1cSCédric Le Goater AspeedI2CState *s = bus->controller; 369aab90b1cSCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); 370ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 371ba2cccd6SJoe Komlodi bool dma_en = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) || 372ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) || 373ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) || 374ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN); 375aab90b1cSCédric Le Goater if (!aic->check_sram) { 376aab90b1cSCédric Le Goater return true; 377aab90b1cSCédric Le Goater } 378aab90b1cSCédric Le Goater 379aab90b1cSCédric Le Goater /* 380aab90b1cSCédric Le Goater * AST2500: SRAM must be enabled before using the Buffer Pool or 381aab90b1cSCédric Le Goater * DMA mode. 382aab90b1cSCédric Le Goater */ 3833be3d6ccSJoe Komlodi if (!FIELD_EX32(s->ctrl_global, I2C_CTRL_GLOBAL, SRAM_EN) && dma_en) { 384aab90b1cSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: SRAM is not enabled\n", __func__); 385aab90b1cSCédric Le Goater return false; 386aab90b1cSCédric Le Goater } 387aab90b1cSCédric Le Goater 388aab90b1cSCédric Le Goater return true; 389aab90b1cSCédric Le Goater } 390aab90b1cSCédric Le Goater 39166cc84a1SCédric Le Goater static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) 39266cc84a1SCédric Le Goater { 393f821bac4SMiroslav Rezanina g_autofree char *cmd_flags = NULL; 39466cc84a1SCédric Le Goater uint32_t count; 395ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 396ba2cccd6SJoe Komlodi uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); 397ba2cccd6SJoe Komlodi uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); 398ba2cccd6SJoe Komlodi uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); 399ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) { 400ba2cccd6SJoe Komlodi count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT); 401ba2cccd6SJoe Komlodi } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { 402ba2cccd6SJoe Komlodi count = bus->regs[reg_dma_len]; 40366cc84a1SCédric Le Goater } else { /* BYTE mode */ 40466cc84a1SCédric Le Goater count = 1; 40566cc84a1SCédric Le Goater } 40666cc84a1SCédric Le Goater 40766cc84a1SCédric Le Goater cmd_flags = g_strdup_printf("%s%s%s%s%s%s%s%s%s", 408ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD) ? "start|" : "", 409ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN) ? "rxdma|" : "", 410ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN) ? "txdma|" : "", 411ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN) ? "rxbuf|" : "", 412ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN) ? "txbuf|" : "", 413ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD) ? "tx|" : "", 414ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) ? "rx|" : "", 415ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST) ? "last|" : "", 416ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD) ? "stop|" : ""); 41766cc84a1SCédric Le Goater 418ba2cccd6SJoe Komlodi trace_aspeed_i2c_bus_cmd(bus->regs[reg_cmd], cmd_flags, count, 419ba2cccd6SJoe Komlodi bus->regs[reg_intr_sts]); 42066cc84a1SCédric Le Goater } 42166cc84a1SCédric Le Goater 4224960f084SCédric Le Goater /* 4234960f084SCédric Le Goater * The state machine needs some refinement. It is only used to track 4244960f084SCédric Le Goater * invalid STOP commands for the moment. 4254960f084SCédric Le Goater */ 42616020011SCédric Le Goater static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) 42716020011SCédric Le Goater { 4286054fc73SCédric Le Goater uint8_t pool_start = 0; 429ba2cccd6SJoe Komlodi uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus); 430ba2cccd6SJoe Komlodi uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); 431ba2cccd6SJoe Komlodi uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); 432ba2cccd6SJoe Komlodi uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); 43316020011SCédric Le Goater 434aab90b1cSCédric Le Goater if (!aspeed_i2c_check_sram(bus)) { 435aab90b1cSCédric Le Goater return; 436aab90b1cSCédric Le Goater } 437aab90b1cSCédric Le Goater 43866cc84a1SCédric Le Goater if (trace_event_get_state_backends(TRACE_ASPEED_I2C_BUS_CMD)) { 43966cc84a1SCédric Le Goater aspeed_i2c_bus_cmd_dump(bus); 44066cc84a1SCédric Le Goater } 44166cc84a1SCédric Le Goater 442ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_START_CMD)) { 4434960f084SCédric Le Goater uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? 4444960f084SCédric Le Goater I2CD_MSTARTR : I2CD_MSTART; 4456054fc73SCédric Le Goater uint8_t addr; 4464960f084SCédric Le Goater 4474960f084SCédric Le Goater aspeed_i2c_set_state(bus, state); 4484960f084SCédric Le Goater 4496054fc73SCédric Le Goater addr = aspeed_i2c_get_addr(bus); 4506054fc73SCédric Le Goater if (i2c_start_transfer(bus->bus, extract32(addr, 1, 7), 4516054fc73SCédric Le Goater extract32(addr, 0, 1))) { 452ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); 453ba2cccd6SJoe Komlodi if (aspeed_i2c_bus_pkt_mode_en(bus)) { 454ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); 455ba2cccd6SJoe Komlodi } 45616020011SCédric Le Goater } else { 457ba2cccd6SJoe Komlodi /* START doesn't set TX_ACK in packet mode */ 458ba2cccd6SJoe Komlodi if (!aspeed_i2c_bus_pkt_mode_en(bus)) { 459ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); 460ba2cccd6SJoe Komlodi } 46116020011SCédric Le Goater } 46216020011SCédric Le Goater 463ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_START_CMD, 0); 4646054fc73SCédric Le Goater 4656054fc73SCédric Le Goater /* 4666054fc73SCédric Le Goater * The START command is also a TX command, as the slave 4676054fc73SCédric Le Goater * address is sent on the bus. Drop the TX flag if nothing 4686054fc73SCédric Le Goater * else needs to be sent in this sequence. 4696054fc73SCédric Le Goater */ 470ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { 471ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) 472ba2cccd6SJoe Komlodi == 1) { 473ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); 4746054fc73SCédric Le Goater } else { 4756054fc73SCédric Le Goater /* 4766054fc73SCédric Le Goater * Increase the start index in the TX pool buffer to 4776054fc73SCédric Le Goater * skip the address byte. 4786054fc73SCédric Le Goater */ 4796054fc73SCédric Le Goater pool_start++; 4806054fc73SCédric Le Goater } 481ba2cccd6SJoe Komlodi } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { 482ba2cccd6SJoe Komlodi if (bus->regs[reg_dma_len] == 0) { 483ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); 484545d6befSCédric Le Goater } 4856054fc73SCédric Le Goater } else { 486ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); 4876054fc73SCédric Le Goater } 488ddabca75SCédric Le Goater 489ddabca75SCédric Le Goater /* No slave found */ 490ddabca75SCédric Le Goater if (!i2c_bus_busy(bus->bus)) { 491ba2cccd6SJoe Komlodi if (aspeed_i2c_bus_pkt_mode_en(bus)) { 492ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); 493ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); 494ba2cccd6SJoe Komlodi } 495ddabca75SCédric Le Goater return; 496ddabca75SCédric Le Goater } 4974960f084SCédric Le Goater aspeed_i2c_set_state(bus, I2CD_MACTIVE); 498ddabca75SCédric Le Goater } 499ddabca75SCédric Le Goater 500ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) { 5014960f084SCédric Le Goater aspeed_i2c_set_state(bus, I2CD_MTXD); 5026054fc73SCédric Le Goater if (aspeed_i2c_bus_send(bus, pool_start)) { 503ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); 50416020011SCédric Le Goater i2c_end_transfer(bus->bus); 50516020011SCédric Le Goater } else { 506ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_ACK, 1); 50716020011SCédric Le Goater } 508ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); 5094960f084SCédric Le Goater aspeed_i2c_set_state(bus, I2CD_MACTIVE); 510ddabca75SCédric Le Goater } 51116020011SCédric Le Goater 512ba2cccd6SJoe Komlodi if ((SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_RX_CMD) || 513ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_S_RX_CMD_LAST)) && 514ba2cccd6SJoe Komlodi !SHARED_ARRAY_FIELD_EX32(bus->regs, reg_intr_sts, RX_DONE)) { 5157bd9c60dSGuenter Roeck aspeed_i2c_handle_rx_cmd(bus); 51616020011SCédric Le Goater } 51716020011SCédric Le Goater 518ba2cccd6SJoe Komlodi if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_STOP_CMD)) { 5194960f084SCédric Le Goater if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { 5204960f084SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); 521ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, ABNORMAL, 1); 522ba2cccd6SJoe Komlodi if (aspeed_i2c_bus_pkt_mode_en(bus)) { 523ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_FAIL, 1); 524ba2cccd6SJoe Komlodi } 52516020011SCédric Le Goater } else { 5264960f084SCédric Le Goater aspeed_i2c_set_state(bus, I2CD_MSTOP); 52716020011SCédric Le Goater i2c_end_transfer(bus->bus); 528ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, NORMAL_STOP, 1); 52916020011SCédric Le Goater } 530ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_STOP_CMD, 0); 5314960f084SCédric Le Goater aspeed_i2c_set_state(bus, I2CD_IDLE); 53216020011SCédric Le Goater } 533ba2cccd6SJoe Komlodi 534ba2cccd6SJoe Komlodi if (aspeed_i2c_bus_pkt_mode_en(bus)) { 535ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_INTR_STS, PKT_CMD_DONE, 1); 536ba2cccd6SJoe Komlodi } 53716020011SCédric Le Goater } 53816020011SCédric Le Goater 539ba2cccd6SJoe Komlodi static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, 54016020011SCédric Le Goater uint64_t value, unsigned size) 54116020011SCédric Le Goater { 542ba2cccd6SJoe Komlodi AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); 543ba2cccd6SJoe Komlodi bool handle_rx; 544ba2cccd6SJoe Komlodi bool w1t; 545ba2cccd6SJoe Komlodi 546ba2cccd6SJoe Komlodi trace_aspeed_i2c_bus_write(bus->id, offset, size, value); 547ba2cccd6SJoe Komlodi 548ba2cccd6SJoe Komlodi switch (offset) { 549ba2cccd6SJoe Komlodi case A_I2CC_FUN_CTRL: 550ba2cccd6SJoe Komlodi if (SHARED_FIELD_EX32(value, SLAVE_EN)) { 551ba2cccd6SJoe Komlodi qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", 552ba2cccd6SJoe Komlodi __func__); 553ba2cccd6SJoe Komlodi break; 554ba2cccd6SJoe Komlodi } 555ba2cccd6SJoe Komlodi bus->regs[R_I2CD_FUN_CTRL] = value & 0x007dc3ff; 556ba2cccd6SJoe Komlodi break; 557ba2cccd6SJoe Komlodi case A_I2CC_AC_TIMING: 558ba2cccd6SJoe Komlodi bus->regs[R_I2CC_AC_TIMING] = value & 0x1ffff0ff; 559ba2cccd6SJoe Komlodi break; 560ba2cccd6SJoe Komlodi case A_I2CC_MS_TXRX_BYTE_BUF: 561ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CC_MS_TXRX_BYTE_BUF, TX_BUF, 562ba2cccd6SJoe Komlodi value); 563ba2cccd6SJoe Komlodi break; 564ba2cccd6SJoe Komlodi case A_I2CC_POOL_CTRL: 565ba2cccd6SJoe Komlodi bus->regs[R_I2CC_POOL_CTRL] &= ~0xffffff; 566ba2cccd6SJoe Komlodi bus->regs[R_I2CC_POOL_CTRL] |= (value & 0xffffff); 567ba2cccd6SJoe Komlodi break; 568ba2cccd6SJoe Komlodi case A_I2CM_INTR_CTRL: 569ba2cccd6SJoe Komlodi bus->regs[R_I2CM_INTR_CTRL] = value & 0x0007f07f; 570ba2cccd6SJoe Komlodi break; 571ba2cccd6SJoe Komlodi case A_I2CM_INTR_STS: 572ba2cccd6SJoe Komlodi handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_INTR_STS, RX_DONE) 573ba2cccd6SJoe Komlodi && SHARED_FIELD_EX32(value, RX_DONE); 574ba2cccd6SJoe Komlodi 575ba2cccd6SJoe Komlodi /* In packet mode, clearing PKT_CMD_DONE clears other interrupts. */ 576ba2cccd6SJoe Komlodi if (aspeed_i2c_bus_pkt_mode_en(bus) && 577ba2cccd6SJoe Komlodi FIELD_EX32(value, I2CM_INTR_STS, PKT_CMD_DONE)) { 578ba2cccd6SJoe Komlodi bus->regs[R_I2CM_INTR_STS] &= 0xf0001000; 579ba2cccd6SJoe Komlodi if (!bus->regs[R_I2CM_INTR_STS]) { 580ba2cccd6SJoe Komlodi bus->controller->intr_status &= ~(1 << bus->id); 581ba2cccd6SJoe Komlodi qemu_irq_lower(aic->bus_get_irq(bus)); 582ba2cccd6SJoe Komlodi } 583ba2cccd6SJoe Komlodi break; 584ba2cccd6SJoe Komlodi } 585ba2cccd6SJoe Komlodi bus->regs[R_I2CM_INTR_STS] &= ~(value & 0xf007f07f); 586ba2cccd6SJoe Komlodi if (!bus->regs[R_I2CM_INTR_STS]) { 587ba2cccd6SJoe Komlodi bus->controller->intr_status &= ~(1 << bus->id); 588ba2cccd6SJoe Komlodi qemu_irq_lower(aic->bus_get_irq(bus)); 589ba2cccd6SJoe Komlodi } 590ba2cccd6SJoe Komlodi if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, 591ba2cccd6SJoe Komlodi M_RX_CMD) || 592ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CM_CMD, 593ba2cccd6SJoe Komlodi M_S_RX_CMD_LAST))) { 594ba2cccd6SJoe Komlodi aspeed_i2c_handle_rx_cmd(bus); 595ba2cccd6SJoe Komlodi aspeed_i2c_bus_raise_interrupt(bus); 596ba2cccd6SJoe Komlodi } 597ba2cccd6SJoe Komlodi break; 598ba2cccd6SJoe Komlodi case A_I2CM_CMD: 599ba2cccd6SJoe Komlodi if (!aspeed_i2c_bus_is_enabled(bus)) { 600ba2cccd6SJoe Komlodi break; 601ba2cccd6SJoe Komlodi } 602ba2cccd6SJoe Komlodi 603ba2cccd6SJoe Komlodi if (!aspeed_i2c_bus_is_master(bus)) { 604ba2cccd6SJoe Komlodi qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", 605ba2cccd6SJoe Komlodi __func__); 606ba2cccd6SJoe Komlodi break; 607ba2cccd6SJoe Komlodi } 608ba2cccd6SJoe Komlodi 609ba2cccd6SJoe Komlodi if (!aic->has_dma && 610ba2cccd6SJoe Komlodi (SHARED_FIELD_EX32(value, RX_DMA_EN) || 611ba2cccd6SJoe Komlodi SHARED_FIELD_EX32(value, TX_DMA_EN))) { 612ba2cccd6SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); 613ba2cccd6SJoe Komlodi break; 614ba2cccd6SJoe Komlodi } 615ba2cccd6SJoe Komlodi 616ba2cccd6SJoe Komlodi if (bus->regs[R_I2CM_INTR_STS] & 0xffff0000) { 617ba2cccd6SJoe Komlodi qemu_log_mask(LOG_UNIMP, "%s: Packet mode is not implemented\n", 618ba2cccd6SJoe Komlodi __func__); 619ba2cccd6SJoe Komlodi break; 620ba2cccd6SJoe Komlodi } 621ba2cccd6SJoe Komlodi 622ba2cccd6SJoe Komlodi value &= 0xff0ffbfb; 623ba2cccd6SJoe Komlodi if (ARRAY_FIELD_EX32(bus->regs, I2CM_CMD, W1_CTRL)) { 624ba2cccd6SJoe Komlodi bus->regs[R_I2CM_CMD] |= value; 625ba2cccd6SJoe Komlodi } else { 626ba2cccd6SJoe Komlodi bus->regs[R_I2CM_CMD] = value; 627ba2cccd6SJoe Komlodi } 628ba2cccd6SJoe Komlodi 629ba2cccd6SJoe Komlodi aspeed_i2c_bus_handle_cmd(bus, value); 630ba2cccd6SJoe Komlodi aspeed_i2c_bus_raise_interrupt(bus); 631ba2cccd6SJoe Komlodi break; 632ba2cccd6SJoe Komlodi case A_I2CM_DMA_TX_ADDR: 633ba2cccd6SJoe Komlodi bus->regs[R_I2CM_DMA_TX_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, 634ba2cccd6SJoe Komlodi ADDR); 635ba2cccd6SJoe Komlodi bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_TX_ADDR, ADDR); 636ba2cccd6SJoe Komlodi bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, 637ba2cccd6SJoe Komlodi TX_BUF_LEN) + 1; 638ba2cccd6SJoe Komlodi break; 639ba2cccd6SJoe Komlodi case A_I2CM_DMA_RX_ADDR: 640ba2cccd6SJoe Komlodi bus->regs[R_I2CM_DMA_RX_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, 641ba2cccd6SJoe Komlodi ADDR); 642ba2cccd6SJoe Komlodi bus->regs[R_I2CC_DMA_ADDR] = FIELD_EX32(value, I2CM_DMA_RX_ADDR, ADDR); 643ba2cccd6SJoe Komlodi bus->regs[R_I2CC_DMA_LEN] = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, 644ba2cccd6SJoe Komlodi RX_BUF_LEN) + 1; 645ba2cccd6SJoe Komlodi break; 646ba2cccd6SJoe Komlodi case A_I2CM_DMA_LEN: 647ba2cccd6SJoe Komlodi w1t = ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN_W1T) || 648ba2cccd6SJoe Komlodi ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN_W1T); 649ba2cccd6SJoe Komlodi /* If none of the w1t bits are set, just write to the reg as normal. */ 650ba2cccd6SJoe Komlodi if (!w1t) { 651ba2cccd6SJoe Komlodi bus->regs[R_I2CM_DMA_LEN] = value; 652ba2cccd6SJoe Komlodi break; 653ba2cccd6SJoe Komlodi } 654ba2cccd6SJoe Komlodi if (ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN_W1T)) { 655ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, RX_BUF_LEN, 656ba2cccd6SJoe Komlodi FIELD_EX32(value, I2CM_DMA_LEN, RX_BUF_LEN)); 657ba2cccd6SJoe Komlodi } 658ba2cccd6SJoe Komlodi if (ARRAY_FIELD_EX32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN_W1T)) { 659ba2cccd6SJoe Komlodi ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN, TX_BUF_LEN, 660ba2cccd6SJoe Komlodi FIELD_EX32(value, I2CM_DMA_LEN, TX_BUF_LEN)); 661ba2cccd6SJoe Komlodi } 662ba2cccd6SJoe Komlodi break; 663ba2cccd6SJoe Komlodi case A_I2CM_DMA_LEN_STS: 664ba2cccd6SJoe Komlodi /* Writes clear to 0 */ 665ba2cccd6SJoe Komlodi bus->regs[R_I2CM_DMA_LEN_STS] = 0; 666ba2cccd6SJoe Komlodi break; 667ba2cccd6SJoe Komlodi case A_I2CC_DMA_ADDR: 668ba2cccd6SJoe Komlodi case A_I2CC_DMA_LEN: 669ba2cccd6SJoe Komlodi /* RO */ 670ba2cccd6SJoe Komlodi break; 671ba2cccd6SJoe Komlodi case A_I2CS_DMA_LEN_STS: 672ba2cccd6SJoe Komlodi case A_I2CS_DMA_TX_ADDR: 673ba2cccd6SJoe Komlodi case A_I2CS_DMA_RX_ADDR: 674ba2cccd6SJoe Komlodi case A_I2CS_DEV_ADDR: 675ba2cccd6SJoe Komlodi case A_I2CS_INTR_CTRL: 676ba2cccd6SJoe Komlodi case A_I2CS_INTR_STS: 677ba2cccd6SJoe Komlodi case A_I2CS_CMD: 678ba2cccd6SJoe Komlodi case A_I2CS_DMA_LEN: 679ba2cccd6SJoe Komlodi qemu_log_mask(LOG_UNIMP, "%s: Slave mode is not implemented\n", 680ba2cccd6SJoe Komlodi __func__); 681ba2cccd6SJoe Komlodi break; 682ba2cccd6SJoe Komlodi default: 683ba2cccd6SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", 684ba2cccd6SJoe Komlodi __func__, offset); 685ba2cccd6SJoe Komlodi } 686ba2cccd6SJoe Komlodi } 687ba2cccd6SJoe Komlodi 688ba2cccd6SJoe Komlodi static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, 689ba2cccd6SJoe Komlodi uint64_t value, unsigned size) 690ba2cccd6SJoe Komlodi { 69151dd4923SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); 692bb626e5bSGuenter Roeck bool handle_rx; 69316020011SCédric Le Goater 69466cc84a1SCédric Le Goater trace_aspeed_i2c_bus_write(bus->id, offset, size, value); 69566cc84a1SCédric Le Goater 69616020011SCédric Le Goater switch (offset) { 6973be3d6ccSJoe Komlodi case A_I2CD_FUN_CTRL: 698ba2cccd6SJoe Komlodi if (SHARED_FIELD_EX32(value, SLAVE_EN)) { 69916020011SCédric Le Goater qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", 70016020011SCédric Le Goater __func__); 70116020011SCédric Le Goater break; 70216020011SCédric Le Goater } 7032260fc6fSJoe Komlodi bus->regs[R_I2CD_FUN_CTRL] = value & 0x0071C3FF; 70416020011SCédric Le Goater break; 7053be3d6ccSJoe Komlodi case A_I2CD_AC_TIMING1: 7062260fc6fSJoe Komlodi bus->regs[R_I2CD_AC_TIMING1] = value & 0xFFFFF0F; 70716020011SCédric Le Goater break; 7083be3d6ccSJoe Komlodi case A_I2CD_AC_TIMING2: 7092260fc6fSJoe Komlodi bus->regs[R_I2CD_AC_TIMING2] = value & 0x7; 71016020011SCédric Le Goater break; 7113be3d6ccSJoe Komlodi case A_I2CD_INTR_CTRL: 7122260fc6fSJoe Komlodi bus->regs[R_I2CD_INTR_CTRL] = value & 0x7FFF; 71316020011SCédric Le Goater break; 7143be3d6ccSJoe Komlodi case A_I2CD_INTR_STS: 715ba2cccd6SJoe Komlodi handle_rx = SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_INTR_STS, RX_DONE) 716ba2cccd6SJoe Komlodi && SHARED_FIELD_EX32(value, RX_DONE); 7172260fc6fSJoe Komlodi bus->regs[R_I2CD_INTR_STS] &= ~(value & 0x7FFF); 7182260fc6fSJoe Komlodi if (!bus->regs[R_I2CD_INTR_STS]) { 71916020011SCédric Le Goater bus->controller->intr_status &= ~(1 << bus->id); 72051dd4923SCédric Le Goater qemu_irq_lower(aic->bus_get_irq(bus)); 7215540cb97SCédric Le Goater } 722ba2cccd6SJoe Komlodi if (handle_rx && (SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, 723ba2cccd6SJoe Komlodi M_RX_CMD) || 724ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_EX32(bus->regs, R_I2CD_CMD, 725ba2cccd6SJoe Komlodi M_S_RX_CMD_LAST))) { 726bb626e5bSGuenter Roeck aspeed_i2c_handle_rx_cmd(bus); 727bb626e5bSGuenter Roeck aspeed_i2c_bus_raise_interrupt(bus); 728bb626e5bSGuenter Roeck } 72916020011SCédric Le Goater break; 7303be3d6ccSJoe Komlodi case A_I2CD_DEV_ADDR: 731d72a712cSKlaus Jensen bus->regs[R_I2CD_DEV_ADDR] = value; 73216020011SCédric Le Goater break; 7333be3d6ccSJoe Komlodi case A_I2CD_POOL_CTRL: 7342260fc6fSJoe Komlodi bus->regs[R_I2CD_POOL_CTRL] &= ~0xffffff; 7352260fc6fSJoe Komlodi bus->regs[R_I2CD_POOL_CTRL] |= (value & 0xffffff); 7366054fc73SCédric Le Goater break; 7376054fc73SCédric Le Goater 7383be3d6ccSJoe Komlodi case A_I2CD_BYTE_BUF: 739ba2cccd6SJoe Komlodi SHARED_ARRAY_FIELD_DP32(bus->regs, R_I2CD_BYTE_BUF, TX_BUF, value); 74016020011SCédric Le Goater break; 7413be3d6ccSJoe Komlodi case A_I2CD_CMD: 74216020011SCédric Le Goater if (!aspeed_i2c_bus_is_enabled(bus)) { 74316020011SCédric Le Goater break; 74416020011SCédric Le Goater } 74516020011SCédric Le Goater 74616020011SCédric Le Goater if (!aspeed_i2c_bus_is_master(bus)) { 74716020011SCédric Le Goater qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", 74816020011SCédric Le Goater __func__); 74916020011SCédric Le Goater break; 75016020011SCédric Le Goater } 75116020011SCédric Le Goater 752545d6befSCédric Le Goater if (!aic->has_dma && 753ba2cccd6SJoe Komlodi (SHARED_FIELD_EX32(value, RX_DMA_EN) || 754ba2cccd6SJoe Komlodi SHARED_FIELD_EX32(value, TX_DMA_EN))) { 755545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); 756545d6befSCédric Le Goater break; 757545d6befSCédric Le Goater } 758545d6befSCédric Le Goater 759ba2cccd6SJoe Komlodi bus->regs[R_I2CD_CMD] &= ~0xFFFF; 760ba2cccd6SJoe Komlodi bus->regs[R_I2CD_CMD] |= value & 0xFFFF; 761ba2cccd6SJoe Komlodi 76216020011SCédric Le Goater aspeed_i2c_bus_handle_cmd(bus, value); 763ddabca75SCédric Le Goater aspeed_i2c_bus_raise_interrupt(bus); 76416020011SCédric Le Goater break; 7653be3d6ccSJoe Komlodi case A_I2CD_DMA_ADDR: 766545d6befSCédric Le Goater if (!aic->has_dma) { 767545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); 768545d6befSCédric Le Goater break; 769545d6befSCédric Le Goater } 770545d6befSCédric Le Goater 7712260fc6fSJoe Komlodi bus->regs[R_I2CD_DMA_ADDR] = value & 0x3ffffffc; 772545d6befSCédric Le Goater break; 773545d6befSCédric Le Goater 7743be3d6ccSJoe Komlodi case A_I2CD_DMA_LEN: 775545d6befSCédric Le Goater if (!aic->has_dma) { 776545d6befSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); 777545d6befSCédric Le Goater break; 778545d6befSCédric Le Goater } 779545d6befSCédric Le Goater 7802260fc6fSJoe Komlodi bus->regs[R_I2CD_DMA_LEN] = value & 0xfff; 7812260fc6fSJoe Komlodi if (!bus->regs[R_I2CD_DMA_LEN]) { 782545d6befSCédric Le Goater qemu_log_mask(LOG_UNIMP, "%s: invalid DMA length\n", __func__); 783545d6befSCédric Le Goater } 784545d6befSCédric Le Goater break; 78516020011SCédric Le Goater 78616020011SCédric Le Goater default: 78716020011SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", 78816020011SCédric Le Goater __func__, offset); 78916020011SCédric Le Goater } 79016020011SCédric Le Goater } 79116020011SCédric Le Goater 792ba2cccd6SJoe Komlodi static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, 793ba2cccd6SJoe Komlodi uint64_t value, unsigned size) 794ba2cccd6SJoe Komlodi { 795ba2cccd6SJoe Komlodi AspeedI2CBus *bus = opaque; 796ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(bus->controller)) { 797ba2cccd6SJoe Komlodi aspeed_i2c_bus_new_write(bus, offset, value, size); 798ba2cccd6SJoe Komlodi } else { 799ba2cccd6SJoe Komlodi aspeed_i2c_bus_old_write(bus, offset, value, size); 800ba2cccd6SJoe Komlodi } 801ba2cccd6SJoe Komlodi } 802ba2cccd6SJoe Komlodi 80316020011SCédric Le Goater static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, 80416020011SCédric Le Goater unsigned size) 80516020011SCédric Le Goater { 80616020011SCédric Le Goater AspeedI2CState *s = opaque; 80716020011SCédric Le Goater 80816020011SCédric Le Goater switch (offset) { 8093be3d6ccSJoe Komlodi case A_I2C_CTRL_STATUS: 81016020011SCédric Le Goater return s->intr_status; 8113be3d6ccSJoe Komlodi case A_I2C_CTRL_GLOBAL: 812aab90b1cSCédric Le Goater return s->ctrl_global; 813ba2cccd6SJoe Komlodi case A_I2C_CTRL_NEW_CLK_DIVIDER: 814ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(s)) { 815ba2cccd6SJoe Komlodi return s->new_clk_divider; 816ba2cccd6SJoe Komlodi } 817ba2cccd6SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", 818ba2cccd6SJoe Komlodi __func__, offset); 819ba2cccd6SJoe Komlodi break; 82016020011SCédric Le Goater default: 82116020011SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", 82216020011SCédric Le Goater __func__, offset); 82316020011SCédric Le Goater break; 82416020011SCédric Le Goater } 82516020011SCédric Le Goater 82616020011SCédric Le Goater return -1; 82716020011SCédric Le Goater } 82816020011SCédric Le Goater 82916020011SCédric Le Goater static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, 83016020011SCédric Le Goater uint64_t value, unsigned size) 83116020011SCédric Le Goater { 832aab90b1cSCédric Le Goater AspeedI2CState *s = opaque; 833aab90b1cSCédric Le Goater 83416020011SCédric Le Goater switch (offset) { 8353be3d6ccSJoe Komlodi case A_I2C_CTRL_GLOBAL: 836aab90b1cSCédric Le Goater s->ctrl_global = value; 837aab90b1cSCédric Le Goater break; 838ba2cccd6SJoe Komlodi case A_I2C_CTRL_NEW_CLK_DIVIDER: 839ba2cccd6SJoe Komlodi if (aspeed_i2c_is_new_mode(s)) { 840ba2cccd6SJoe Komlodi s->new_clk_divider = value; 841ba2cccd6SJoe Komlodi } else { 842ba2cccd6SJoe Komlodi qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx 843ba2cccd6SJoe Komlodi "\n", __func__, offset); 844ba2cccd6SJoe Komlodi } 845ba2cccd6SJoe Komlodi break; 8463be3d6ccSJoe Komlodi case A_I2C_CTRL_STATUS: 84716020011SCédric Le Goater default: 84816020011SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", 84916020011SCédric Le Goater __func__, offset); 85016020011SCédric Le Goater break; 85116020011SCédric Le Goater } 85216020011SCédric Le Goater } 85316020011SCédric Le Goater 85416020011SCédric Le Goater static const MemoryRegionOps aspeed_i2c_bus_ops = { 85516020011SCédric Le Goater .read = aspeed_i2c_bus_read, 85616020011SCédric Le Goater .write = aspeed_i2c_bus_write, 85716020011SCédric Le Goater .endianness = DEVICE_LITTLE_ENDIAN, 85816020011SCédric Le Goater }; 85916020011SCédric Le Goater 86016020011SCédric Le Goater static const MemoryRegionOps aspeed_i2c_ctrl_ops = { 86116020011SCédric Le Goater .read = aspeed_i2c_ctrl_read, 86216020011SCédric Le Goater .write = aspeed_i2c_ctrl_write, 86316020011SCédric Le Goater .endianness = DEVICE_LITTLE_ENDIAN, 86416020011SCédric Le Goater }; 86516020011SCédric Le Goater 8666054fc73SCédric Le Goater static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, 8676054fc73SCédric Le Goater unsigned size) 8686054fc73SCédric Le Goater { 8696054fc73SCédric Le Goater AspeedI2CState *s = opaque; 8706054fc73SCédric Le Goater uint64_t ret = 0; 8716054fc73SCédric Le Goater int i; 8726054fc73SCédric Le Goater 8736054fc73SCédric Le Goater for (i = 0; i < size; i++) { 8746054fc73SCédric Le Goater ret |= (uint64_t) s->pool[offset + i] << (8 * i); 8756054fc73SCédric Le Goater } 8766054fc73SCédric Le Goater 8776054fc73SCédric Le Goater return ret; 8786054fc73SCédric Le Goater } 8796054fc73SCédric Le Goater 8806054fc73SCédric Le Goater static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, 8816054fc73SCédric Le Goater uint64_t value, unsigned size) 8826054fc73SCédric Le Goater { 8836054fc73SCédric Le Goater AspeedI2CState *s = opaque; 8846054fc73SCédric Le Goater int i; 8856054fc73SCédric Le Goater 8866054fc73SCédric Le Goater for (i = 0; i < size; i++) { 8876054fc73SCédric Le Goater s->pool[offset + i] = (value >> (8 * i)) & 0xFF; 8886054fc73SCédric Le Goater } 8896054fc73SCédric Le Goater } 8906054fc73SCédric Le Goater 8916054fc73SCédric Le Goater static const MemoryRegionOps aspeed_i2c_pool_ops = { 8926054fc73SCédric Le Goater .read = aspeed_i2c_pool_read, 8936054fc73SCédric Le Goater .write = aspeed_i2c_pool_write, 8946054fc73SCédric Le Goater .endianness = DEVICE_LITTLE_ENDIAN, 8956054fc73SCédric Le Goater .valid = { 8966054fc73SCédric Le Goater .min_access_size = 1, 8976054fc73SCédric Le Goater .max_access_size = 4, 8986054fc73SCédric Le Goater }, 8996054fc73SCédric Le Goater }; 9006054fc73SCédric Le Goater 90116020011SCédric Le Goater static const VMStateDescription aspeed_i2c_bus_vmstate = { 90216020011SCédric Le Goater .name = TYPE_ASPEED_I2C, 903ba2cccd6SJoe Komlodi .version_id = 5, 904ba2cccd6SJoe Komlodi .minimum_version_id = 5, 90516020011SCédric Le Goater .fields = (VMStateField[]) { 906ba2cccd6SJoe Komlodi VMSTATE_UINT32_ARRAY(regs, AspeedI2CBus, ASPEED_I2C_NEW_NUM_REG), 90716020011SCédric Le Goater VMSTATE_END_OF_LIST() 90816020011SCédric Le Goater } 90916020011SCédric Le Goater }; 91016020011SCédric Le Goater 91116020011SCédric Le Goater static const VMStateDescription aspeed_i2c_vmstate = { 91216020011SCédric Le Goater .name = TYPE_ASPEED_I2C, 9136054fc73SCédric Le Goater .version_id = 2, 9146054fc73SCédric Le Goater .minimum_version_id = 2, 91516020011SCédric Le Goater .fields = (VMStateField[]) { 91616020011SCédric Le Goater VMSTATE_UINT32(intr_status, AspeedI2CState), 91716020011SCédric Le Goater VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, 91816020011SCédric Le Goater ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, 91916020011SCédric Le Goater AspeedI2CBus), 9206054fc73SCédric Le Goater VMSTATE_UINT8_ARRAY(pool, AspeedI2CState, ASPEED_I2C_MAX_POOL_SIZE), 92116020011SCédric Le Goater VMSTATE_END_OF_LIST() 92216020011SCédric Le Goater } 92316020011SCédric Le Goater }; 92416020011SCédric Le Goater 92516020011SCédric Le Goater static void aspeed_i2c_reset(DeviceState *dev) 92616020011SCédric Le Goater { 92716020011SCédric Le Goater AspeedI2CState *s = ASPEED_I2C(dev); 92816020011SCédric Le Goater 92916020011SCédric Le Goater s->intr_status = 0; 93060261038SCédric Le Goater } 93160261038SCédric Le Goater 93260261038SCédric Le Goater static void aspeed_i2c_instance_init(Object *obj) 93360261038SCédric Le Goater { 93460261038SCédric Le Goater AspeedI2CState *s = ASPEED_I2C(obj); 93560261038SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); 93660261038SCédric Le Goater int i; 93716020011SCédric Le Goater 938f7da1aa8SCédric Le Goater for (i = 0; i < aic->num_busses; i++) { 93960261038SCédric Le Goater object_initialize_child(obj, "bus[*]", &s->busses[i], 94060261038SCédric Le Goater TYPE_ASPEED_I2C_BUS); 94116020011SCédric Le Goater } 94216020011SCédric Le Goater } 94316020011SCédric Le Goater 94416020011SCédric Le Goater /* 945f7da1aa8SCédric Le Goater * Address Definitions (AST2400 and AST2500) 94616020011SCédric Le Goater * 94716020011SCédric Le Goater * 0x000 ... 0x03F: Global Register 94816020011SCédric Le Goater * 0x040 ... 0x07F: Device 1 94916020011SCédric Le Goater * 0x080 ... 0x0BF: Device 2 95016020011SCédric Le Goater * 0x0C0 ... 0x0FF: Device 3 95116020011SCédric Le Goater * 0x100 ... 0x13F: Device 4 95216020011SCédric Le Goater * 0x140 ... 0x17F: Device 5 95316020011SCédric Le Goater * 0x180 ... 0x1BF: Device 6 95416020011SCédric Le Goater * 0x1C0 ... 0x1FF: Device 7 95516020011SCédric Le Goater * 0x200 ... 0x2FF: Buffer Pool (unused in linux driver) 95616020011SCédric Le Goater * 0x300 ... 0x33F: Device 8 95716020011SCédric Le Goater * 0x340 ... 0x37F: Device 9 95816020011SCédric Le Goater * 0x380 ... 0x3BF: Device 10 95916020011SCédric Le Goater * 0x3C0 ... 0x3FF: Device 11 96016020011SCédric Le Goater * 0x400 ... 0x43F: Device 12 96116020011SCédric Le Goater * 0x440 ... 0x47F: Device 13 96216020011SCédric Le Goater * 0x480 ... 0x4BF: Device 14 96316020011SCédric Le Goater * 0x800 ... 0xFFF: Buffer Pool (unused in linux driver) 96416020011SCédric Le Goater */ 96516020011SCédric Le Goater static void aspeed_i2c_realize(DeviceState *dev, Error **errp) 96616020011SCédric Le Goater { 96716020011SCédric Le Goater int i; 96816020011SCédric Le Goater SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 96916020011SCédric Le Goater AspeedI2CState *s = ASPEED_I2C(dev); 970f7da1aa8SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); 97116020011SCédric Le Goater 97216020011SCédric Le Goater sysbus_init_irq(sbd, &s->irq); 97316020011SCédric Le Goater memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, 97416020011SCédric Le Goater "aspeed.i2c", 0x1000); 97516020011SCédric Le Goater sysbus_init_mmio(sbd, &s->iomem); 97616020011SCédric Le Goater 977f7da1aa8SCédric Le Goater for (i = 0; i < aic->num_busses; i++) { 97860261038SCédric Le Goater Object *bus = OBJECT(&s->busses[i]); 979f7da1aa8SCédric Le Goater int offset = i < aic->gap ? 1 : 5; 98051dd4923SCédric Le Goater 98160261038SCédric Le Goater if (!object_property_set_link(bus, "controller", OBJECT(s), errp)) { 98260261038SCédric Le Goater return; 98360261038SCédric Le Goater } 98460261038SCédric Le Goater 98560261038SCédric Le Goater if (!object_property_set_uint(bus, "bus-id", i, errp)) { 98660261038SCédric Le Goater return; 98760261038SCédric Le Goater } 98860261038SCédric Le Goater 98960261038SCédric Le Goater if (!sysbus_realize(SYS_BUS_DEVICE(bus), errp)) { 99060261038SCédric Le Goater return; 99160261038SCédric Le Goater } 99260261038SCédric Le Goater 993f7da1aa8SCédric Le Goater memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset), 99416020011SCédric Le Goater &s->busses[i].mr); 99516020011SCédric Le Goater } 9966054fc73SCédric Le Goater 9976054fc73SCédric Le Goater memory_region_init_io(&s->pool_iomem, OBJECT(s), &aspeed_i2c_pool_ops, s, 9986054fc73SCédric Le Goater "aspeed.i2c-pool", aic->pool_size); 9996054fc73SCédric Le Goater memory_region_add_subregion(&s->iomem, aic->pool_base, &s->pool_iomem); 1000545d6befSCédric Le Goater 1001545d6befSCédric Le Goater if (aic->has_dma) { 1002545d6befSCédric Le Goater if (!s->dram_mr) { 1003545d6befSCédric Le Goater error_setg(errp, TYPE_ASPEED_I2C ": 'dram' link not set"); 1004545d6befSCédric Le Goater return; 100516020011SCédric Le Goater } 100616020011SCédric Le Goater 10073f7a53b2SCédric Le Goater address_space_init(&s->dram_as, s->dram_mr, 10083f7a53b2SCédric Le Goater TYPE_ASPEED_I2C "-dma-dram"); 1009545d6befSCédric Le Goater } 1010545d6befSCédric Le Goater } 1011545d6befSCédric Le Goater 1012545d6befSCédric Le Goater static Property aspeed_i2c_properties[] = { 1013545d6befSCédric Le Goater DEFINE_PROP_LINK("dram", AspeedI2CState, dram_mr, 1014545d6befSCédric Le Goater TYPE_MEMORY_REGION, MemoryRegion *), 1015545d6befSCédric Le Goater DEFINE_PROP_END_OF_LIST(), 1016545d6befSCédric Le Goater }; 1017545d6befSCédric Le Goater 101816020011SCédric Le Goater static void aspeed_i2c_class_init(ObjectClass *klass, void *data) 101916020011SCédric Le Goater { 102016020011SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 102116020011SCédric Le Goater 102216020011SCédric Le Goater dc->vmsd = &aspeed_i2c_vmstate; 102316020011SCédric Le Goater dc->reset = aspeed_i2c_reset; 10244f67d30bSMarc-André Lureau device_class_set_props(dc, aspeed_i2c_properties); 102516020011SCédric Le Goater dc->realize = aspeed_i2c_realize; 102616020011SCédric Le Goater dc->desc = "Aspeed I2C Controller"; 102716020011SCédric Le Goater } 102816020011SCédric Le Goater 102916020011SCédric Le Goater static const TypeInfo aspeed_i2c_info = { 103016020011SCédric Le Goater .name = TYPE_ASPEED_I2C, 103116020011SCédric Le Goater .parent = TYPE_SYS_BUS_DEVICE, 103260261038SCédric Le Goater .instance_init = aspeed_i2c_instance_init, 103316020011SCédric Le Goater .instance_size = sizeof(AspeedI2CState), 103416020011SCédric Le Goater .class_init = aspeed_i2c_class_init, 1035f7da1aa8SCédric Le Goater .class_size = sizeof(AspeedI2CClass), 1036f7da1aa8SCédric Le Goater .abstract = true, 1037f7da1aa8SCédric Le Goater }; 1038f7da1aa8SCédric Le Goater 103960261038SCédric Le Goater static void aspeed_i2c_bus_reset(DeviceState *dev) 104060261038SCédric Le Goater { 104160261038SCédric Le Goater AspeedI2CBus *s = ASPEED_I2C_BUS(dev); 104260261038SCédric Le Goater 10432260fc6fSJoe Komlodi memset(s->regs, 0, sizeof(s->regs)); 104460261038SCédric Le Goater i2c_end_transfer(s->bus); 104560261038SCédric Le Goater } 104660261038SCédric Le Goater 104760261038SCédric Le Goater static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) 104860261038SCédric Le Goater { 104960261038SCédric Le Goater AspeedI2CBus *s = ASPEED_I2C_BUS(dev); 105060261038SCédric Le Goater AspeedI2CClass *aic; 105160261038SCédric Le Goater g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I2C_BUS ".%d", s->id); 105260261038SCédric Le Goater 105360261038SCédric Le Goater if (!s->controller) { 105460261038SCédric Le Goater error_setg(errp, TYPE_ASPEED_I2C_BUS ": 'controller' link not set"); 105560261038SCédric Le Goater return; 105660261038SCédric Le Goater } 105760261038SCédric Le Goater 105860261038SCédric Le Goater aic = ASPEED_I2C_GET_CLASS(s->controller); 105960261038SCédric Le Goater 106060261038SCédric Le Goater sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); 106160261038SCédric Le Goater 106260261038SCédric Le Goater s->bus = i2c_init_bus(dev, name); 106360261038SCédric Le Goater 106460261038SCédric Le Goater memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops, 106560261038SCédric Le Goater s, name, aic->reg_size); 106660261038SCédric Le Goater sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); 106760261038SCédric Le Goater } 106860261038SCédric Le Goater 106960261038SCédric Le Goater static Property aspeed_i2c_bus_properties[] = { 107060261038SCédric Le Goater DEFINE_PROP_UINT8("bus-id", AspeedI2CBus, id, 0), 107160261038SCédric Le Goater DEFINE_PROP_LINK("controller", AspeedI2CBus, controller, TYPE_ASPEED_I2C, 107260261038SCédric Le Goater AspeedI2CState *), 107360261038SCédric Le Goater DEFINE_PROP_END_OF_LIST(), 107460261038SCédric Le Goater }; 107560261038SCédric Le Goater 107660261038SCédric Le Goater static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) 107760261038SCédric Le Goater { 107860261038SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 107960261038SCédric Le Goater 108060261038SCédric Le Goater dc->desc = "Aspeed I2C Bus"; 108160261038SCédric Le Goater dc->realize = aspeed_i2c_bus_realize; 108260261038SCédric Le Goater dc->reset = aspeed_i2c_bus_reset; 108360261038SCédric Le Goater device_class_set_props(dc, aspeed_i2c_bus_properties); 108460261038SCédric Le Goater } 108560261038SCédric Le Goater 108660261038SCédric Le Goater static const TypeInfo aspeed_i2c_bus_info = { 108760261038SCédric Le Goater .name = TYPE_ASPEED_I2C_BUS, 108860261038SCédric Le Goater .parent = TYPE_SYS_BUS_DEVICE, 108960261038SCédric Le Goater .instance_size = sizeof(AspeedI2CBus), 109060261038SCédric Le Goater .class_init = aspeed_i2c_bus_class_init, 109160261038SCédric Le Goater }; 109260261038SCédric Le Goater 109351dd4923SCédric Le Goater static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) 109451dd4923SCédric Le Goater { 109551dd4923SCédric Le Goater return bus->controller->irq; 109651dd4923SCédric Le Goater } 109751dd4923SCédric Le Goater 10986054fc73SCédric Le Goater static uint8_t *aspeed_2400_i2c_bus_pool_base(AspeedI2CBus *bus) 10996054fc73SCédric Le Goater { 11006054fc73SCédric Le Goater uint8_t *pool_page = 11012260fc6fSJoe Komlodi &bus->controller->pool[ARRAY_FIELD_EX32(bus->regs, I2CD_FUN_CTRL, 11023be3d6ccSJoe Komlodi POOL_PAGE_SEL) * 0x100]; 11036054fc73SCédric Le Goater 11042260fc6fSJoe Komlodi return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; 11056054fc73SCédric Le Goater } 11066054fc73SCédric Le Goater 1107f7da1aa8SCédric Le Goater static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) 1108f7da1aa8SCédric Le Goater { 1109f7da1aa8SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 1110f7da1aa8SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); 1111f7da1aa8SCédric Le Goater 1112f7da1aa8SCédric Le Goater dc->desc = "ASPEED 2400 I2C Controller"; 1113f7da1aa8SCédric Le Goater 1114f7da1aa8SCédric Le Goater aic->num_busses = 14; 1115f7da1aa8SCédric Le Goater aic->reg_size = 0x40; 1116f7da1aa8SCédric Le Goater aic->gap = 7; 111751dd4923SCédric Le Goater aic->bus_get_irq = aspeed_2400_i2c_bus_get_irq; 11186054fc73SCédric Le Goater aic->pool_size = 0x800; 11196054fc73SCédric Le Goater aic->pool_base = 0x800; 11206054fc73SCédric Le Goater aic->bus_pool_base = aspeed_2400_i2c_bus_pool_base; 1121f7da1aa8SCédric Le Goater } 1122f7da1aa8SCédric Le Goater 1123f7da1aa8SCédric Le Goater static const TypeInfo aspeed_2400_i2c_info = { 1124f7da1aa8SCédric Le Goater .name = TYPE_ASPEED_2400_I2C, 1125f7da1aa8SCédric Le Goater .parent = TYPE_ASPEED_I2C, 1126f7da1aa8SCédric Le Goater .class_init = aspeed_2400_i2c_class_init, 1127f7da1aa8SCédric Le Goater }; 1128f7da1aa8SCédric Le Goater 112951dd4923SCédric Le Goater static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus) 113051dd4923SCédric Le Goater { 113151dd4923SCédric Le Goater return bus->controller->irq; 113251dd4923SCédric Le Goater } 113351dd4923SCédric Le Goater 11346054fc73SCédric Le Goater static uint8_t *aspeed_2500_i2c_bus_pool_base(AspeedI2CBus *bus) 11356054fc73SCédric Le Goater { 11366054fc73SCédric Le Goater return &bus->controller->pool[bus->id * 0x10]; 11376054fc73SCédric Le Goater } 11386054fc73SCédric Le Goater 1139f7da1aa8SCédric Le Goater static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) 1140f7da1aa8SCédric Le Goater { 1141f7da1aa8SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 1142f7da1aa8SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); 1143f7da1aa8SCédric Le Goater 1144f7da1aa8SCédric Le Goater dc->desc = "ASPEED 2500 I2C Controller"; 1145f7da1aa8SCédric Le Goater 1146f7da1aa8SCédric Le Goater aic->num_busses = 14; 1147f7da1aa8SCédric Le Goater aic->reg_size = 0x40; 1148f7da1aa8SCédric Le Goater aic->gap = 7; 114951dd4923SCédric Le Goater aic->bus_get_irq = aspeed_2500_i2c_bus_get_irq; 11506054fc73SCédric Le Goater aic->pool_size = 0x100; 11516054fc73SCédric Le Goater aic->pool_base = 0x200; 11526054fc73SCédric Le Goater aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; 1153aab90b1cSCédric Le Goater aic->check_sram = true; 1154545d6befSCédric Le Goater aic->has_dma = true; 1155f7da1aa8SCédric Le Goater } 1156f7da1aa8SCédric Le Goater 1157f7da1aa8SCédric Le Goater static const TypeInfo aspeed_2500_i2c_info = { 1158f7da1aa8SCédric Le Goater .name = TYPE_ASPEED_2500_I2C, 1159f7da1aa8SCédric Le Goater .parent = TYPE_ASPEED_I2C, 1160f7da1aa8SCédric Le Goater .class_init = aspeed_2500_i2c_class_init, 116116020011SCédric Le Goater }; 116216020011SCédric Le Goater 116351dd4923SCédric Le Goater static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) 116451dd4923SCédric Le Goater { 116551dd4923SCédric Le Goater return bus->irq; 116651dd4923SCédric Le Goater } 116751dd4923SCédric Le Goater 11686054fc73SCédric Le Goater static uint8_t *aspeed_2600_i2c_bus_pool_base(AspeedI2CBus *bus) 11696054fc73SCédric Le Goater { 11706054fc73SCédric Le Goater return &bus->controller->pool[bus->id * 0x20]; 11716054fc73SCédric Le Goater } 11726054fc73SCédric Le Goater 117351dd4923SCédric Le Goater static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) 117451dd4923SCédric Le Goater { 117551dd4923SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 117651dd4923SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); 117751dd4923SCédric Le Goater 117851dd4923SCédric Le Goater dc->desc = "ASPEED 2600 I2C Controller"; 117951dd4923SCédric Le Goater 118051dd4923SCédric Le Goater aic->num_busses = 16; 118151dd4923SCédric Le Goater aic->reg_size = 0x80; 118251dd4923SCédric Le Goater aic->gap = -1; /* no gap */ 118351dd4923SCédric Le Goater aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; 11846054fc73SCédric Le Goater aic->pool_size = 0x200; 11856054fc73SCédric Le Goater aic->pool_base = 0xC00; 11866054fc73SCédric Le Goater aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; 1187545d6befSCédric Le Goater aic->has_dma = true; 118851dd4923SCédric Le Goater } 118951dd4923SCédric Le Goater 119051dd4923SCédric Le Goater static const TypeInfo aspeed_2600_i2c_info = { 119151dd4923SCédric Le Goater .name = TYPE_ASPEED_2600_I2C, 119251dd4923SCédric Le Goater .parent = TYPE_ASPEED_I2C, 119351dd4923SCédric Le Goater .class_init = aspeed_2600_i2c_class_init, 119451dd4923SCédric Le Goater }; 119551dd4923SCédric Le Goater 1196b35802ceSCédric Le Goater static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) 1197b35802ceSCédric Le Goater { 1198b35802ceSCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 1199b35802ceSCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); 1200b35802ceSCédric Le Goater 1201b35802ceSCédric Le Goater dc->desc = "ASPEED 1030 I2C Controller"; 1202b35802ceSCédric Le Goater 1203b35802ceSCédric Le Goater aic->num_busses = 14; 1204b35802ceSCédric Le Goater aic->reg_size = 0x80; 1205b35802ceSCédric Le Goater aic->gap = -1; /* no gap */ 1206b35802ceSCédric Le Goater aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; 1207b35802ceSCédric Le Goater aic->pool_size = 0x200; 1208b35802ceSCédric Le Goater aic->pool_base = 0xC00; 1209b35802ceSCédric Le Goater aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; 1210b35802ceSCédric Le Goater aic->has_dma = true; 1211b35802ceSCédric Le Goater } 1212b35802ceSCédric Le Goater 1213b35802ceSCédric Le Goater static const TypeInfo aspeed_1030_i2c_info = { 1214b35802ceSCédric Le Goater .name = TYPE_ASPEED_1030_I2C, 1215b35802ceSCédric Le Goater .parent = TYPE_ASPEED_I2C, 1216b35802ceSCédric Le Goater .class_init = aspeed_1030_i2c_class_init, 1217b35802ceSCédric Le Goater }; 1218b35802ceSCédric Le Goater 121916020011SCédric Le Goater static void aspeed_i2c_register_types(void) 122016020011SCédric Le Goater { 122160261038SCédric Le Goater type_register_static(&aspeed_i2c_bus_info); 122216020011SCédric Le Goater type_register_static(&aspeed_i2c_info); 1223f7da1aa8SCédric Le Goater type_register_static(&aspeed_2400_i2c_info); 1224f7da1aa8SCédric Le Goater type_register_static(&aspeed_2500_i2c_info); 122551dd4923SCédric Le Goater type_register_static(&aspeed_2600_i2c_info); 1226b35802ceSCédric Le Goater type_register_static(&aspeed_1030_i2c_info); 122716020011SCédric Le Goater } 122816020011SCédric Le Goater 122916020011SCédric Le Goater type_init(aspeed_i2c_register_types) 123016020011SCédric Le Goater 123116020011SCédric Le Goater 12327a204cbdSPhilippe Mathieu-Daudé I2CBus *aspeed_i2c_get_bus(AspeedI2CState *s, int busnr) 123316020011SCédric Le Goater { 1234f7da1aa8SCédric Le Goater AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); 123516020011SCédric Le Goater I2CBus *bus = NULL; 123616020011SCédric Le Goater 1237f7da1aa8SCédric Le Goater if (busnr >= 0 && busnr < aic->num_busses) { 123816020011SCédric Le Goater bus = s->busses[busnr].bus; 123916020011SCédric Le Goater } 124016020011SCédric Le Goater 124116020011SCédric Le Goater return bus; 124216020011SCédric Le Goater } 1243