xref: /openbmc/qemu/hw/i2c/allwinner-i2c.c (revision 83baec642a13a69398a2643a1f905606c13cd363)
19be8a82cSStrahinja Jankovic /*
29be8a82cSStrahinja Jankovic  *  Allwinner I2C Bus Serial Interface Emulation
39be8a82cSStrahinja Jankovic  *
49be8a82cSStrahinja Jankovic  *  Copyright (C) 2022 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
59be8a82cSStrahinja Jankovic  *
69be8a82cSStrahinja Jankovic  *  This file is derived from IMX I2C controller,
79be8a82cSStrahinja Jankovic  *  by Jean-Christophe DUBOIS .
89be8a82cSStrahinja Jankovic  *
99be8a82cSStrahinja Jankovic  *  This program is free software; you can redistribute it and/or modify it
109be8a82cSStrahinja Jankovic  *  under the terms of the GNU General Public License as published by the
119be8a82cSStrahinja Jankovic  *  Free Software Foundation; either version 2 of the License, or
129be8a82cSStrahinja Jankovic  *  (at your option) any later version.
139be8a82cSStrahinja Jankovic  *
149be8a82cSStrahinja Jankovic  *  This program is distributed in the hope that it will be useful, but WITHOUT
159be8a82cSStrahinja Jankovic  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
169be8a82cSStrahinja Jankovic  *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
179be8a82cSStrahinja Jankovic  *  for more details.
189be8a82cSStrahinja Jankovic  *
199be8a82cSStrahinja Jankovic  *  You should have received a copy of the GNU General Public License along
209be8a82cSStrahinja Jankovic  *  with this program; if not, see <http://www.gnu.org/licenses/>.
219be8a82cSStrahinja Jankovic  *
229be8a82cSStrahinja Jankovic  * SPDX-License-Identifier: MIT
239be8a82cSStrahinja Jankovic  */
249be8a82cSStrahinja Jankovic 
259be8a82cSStrahinja Jankovic #include "qemu/osdep.h"
269be8a82cSStrahinja Jankovic #include "hw/i2c/allwinner-i2c.h"
279be8a82cSStrahinja Jankovic #include "hw/irq.h"
289be8a82cSStrahinja Jankovic #include "migration/vmstate.h"
299be8a82cSStrahinja Jankovic #include "hw/i2c/i2c.h"
309be8a82cSStrahinja Jankovic #include "qemu/log.h"
319be8a82cSStrahinja Jankovic #include "trace.h"
329be8a82cSStrahinja Jankovic #include "qemu/module.h"
339be8a82cSStrahinja Jankovic 
349be8a82cSStrahinja Jankovic /* Allwinner I2C memory map */
359be8a82cSStrahinja Jankovic #define TWI_ADDR_REG            0x00  /* slave address register */
369be8a82cSStrahinja Jankovic #define TWI_XADDR_REG           0x04  /* extended slave address register */
379be8a82cSStrahinja Jankovic #define TWI_DATA_REG            0x08  /* data register */
389be8a82cSStrahinja Jankovic #define TWI_CNTR_REG            0x0c  /* control register */
399be8a82cSStrahinja Jankovic #define TWI_STAT_REG            0x10  /* status register */
409be8a82cSStrahinja Jankovic #define TWI_CCR_REG             0x14  /* clock control register */
419be8a82cSStrahinja Jankovic #define TWI_SRST_REG            0x18  /* software reset register */
429be8a82cSStrahinja Jankovic #define TWI_EFR_REG             0x1c  /* enhance feature register */
439be8a82cSStrahinja Jankovic #define TWI_LCR_REG             0x20  /* line control register */
449be8a82cSStrahinja Jankovic 
459be8a82cSStrahinja Jankovic /* Used only in slave mode, do not set */
469be8a82cSStrahinja Jankovic #define TWI_ADDR_RESET          0
479be8a82cSStrahinja Jankovic #define TWI_XADDR_RESET         0
489be8a82cSStrahinja Jankovic 
499be8a82cSStrahinja Jankovic /* Data register */
509be8a82cSStrahinja Jankovic #define TWI_DATA_MASK           0xFF
519be8a82cSStrahinja Jankovic #define TWI_DATA_RESET          0
529be8a82cSStrahinja Jankovic 
539be8a82cSStrahinja Jankovic /* Control register */
549be8a82cSStrahinja Jankovic #define TWI_CNTR_INT_EN         (1 << 7)
559be8a82cSStrahinja Jankovic #define TWI_CNTR_BUS_EN         (1 << 6)
569be8a82cSStrahinja Jankovic #define TWI_CNTR_M_STA          (1 << 5)
579be8a82cSStrahinja Jankovic #define TWI_CNTR_M_STP          (1 << 4)
589be8a82cSStrahinja Jankovic #define TWI_CNTR_INT_FLAG       (1 << 3)
599be8a82cSStrahinja Jankovic #define TWI_CNTR_A_ACK          (1 << 2)
609be8a82cSStrahinja Jankovic #define TWI_CNTR_MASK           0xFC
619be8a82cSStrahinja Jankovic #define TWI_CNTR_RESET          0
629be8a82cSStrahinja Jankovic 
639be8a82cSStrahinja Jankovic /* Status register */
649be8a82cSStrahinja Jankovic #define TWI_STAT_MASK           0xF8
659be8a82cSStrahinja Jankovic #define TWI_STAT_RESET          0xF8
669be8a82cSStrahinja Jankovic 
679be8a82cSStrahinja Jankovic /* Clock register */
689be8a82cSStrahinja Jankovic #define TWI_CCR_CLK_M_MASK      0x78
699be8a82cSStrahinja Jankovic #define TWI_CCR_CLK_N_MASK      0x07
709be8a82cSStrahinja Jankovic #define TWI_CCR_MASK            0x7F
719be8a82cSStrahinja Jankovic #define TWI_CCR_RESET           0
729be8a82cSStrahinja Jankovic 
739be8a82cSStrahinja Jankovic /* Soft reset */
749be8a82cSStrahinja Jankovic #define TWI_SRST_MASK           0x01
759be8a82cSStrahinja Jankovic #define TWI_SRST_RESET          0
769be8a82cSStrahinja Jankovic 
779be8a82cSStrahinja Jankovic /* Enhance feature */
789be8a82cSStrahinja Jankovic #define TWI_EFR_MASK            0x03
799be8a82cSStrahinja Jankovic #define TWI_EFR_RESET           0
809be8a82cSStrahinja Jankovic 
819be8a82cSStrahinja Jankovic /* Line control */
829be8a82cSStrahinja Jankovic #define TWI_LCR_SCL_STATE       (1 << 5)
839be8a82cSStrahinja Jankovic #define TWI_LCR_SDA_STATE       (1 << 4)
849be8a82cSStrahinja Jankovic #define TWI_LCR_SCL_CTL         (1 << 3)
859be8a82cSStrahinja Jankovic #define TWI_LCR_SCL_CTL_EN      (1 << 2)
869be8a82cSStrahinja Jankovic #define TWI_LCR_SDA_CTL         (1 << 1)
879be8a82cSStrahinja Jankovic #define TWI_LCR_SDA_CTL_EN      (1 << 0)
889be8a82cSStrahinja Jankovic #define TWI_LCR_MASK            0x3F
899be8a82cSStrahinja Jankovic #define TWI_LCR_RESET           0x3A
909be8a82cSStrahinja Jankovic 
919be8a82cSStrahinja Jankovic /* Status value in STAT register is shifted by 3 bits */
929be8a82cSStrahinja Jankovic #define TWI_STAT_SHIFT      3
939be8a82cSStrahinja Jankovic #define STAT_FROM_STA(x)    ((x) << TWI_STAT_SHIFT)
949be8a82cSStrahinja Jankovic #define STAT_TO_STA(x)      ((x) >> TWI_STAT_SHIFT)
959be8a82cSStrahinja Jankovic 
969be8a82cSStrahinja Jankovic enum {
979be8a82cSStrahinja Jankovic     STAT_BUS_ERROR = 0,
989be8a82cSStrahinja Jankovic     /* Master mode */
999be8a82cSStrahinja Jankovic     STAT_M_STA_TX,
1009be8a82cSStrahinja Jankovic     STAT_M_RSTA_TX,
1019be8a82cSStrahinja Jankovic     STAT_M_ADDR_WR_ACK,
1029be8a82cSStrahinja Jankovic     STAT_M_ADDR_WR_NACK,
1039be8a82cSStrahinja Jankovic     STAT_M_DATA_TX_ACK,
1049be8a82cSStrahinja Jankovic     STAT_M_DATA_TX_NACK,
1059be8a82cSStrahinja Jankovic     STAT_M_ARB_LOST,
1069be8a82cSStrahinja Jankovic     STAT_M_ADDR_RD_ACK,
1079be8a82cSStrahinja Jankovic     STAT_M_ADDR_RD_NACK,
1089be8a82cSStrahinja Jankovic     STAT_M_DATA_RX_ACK,
1099be8a82cSStrahinja Jankovic     STAT_M_DATA_RX_NACK,
1109be8a82cSStrahinja Jankovic     /* Slave mode */
1119be8a82cSStrahinja Jankovic     STAT_S_ADDR_WR_ACK,
1129be8a82cSStrahinja Jankovic     STAT_S_ARB_LOST_AW_ACK,
1139be8a82cSStrahinja Jankovic     STAT_S_GCA_ACK,
1149be8a82cSStrahinja Jankovic     STAT_S_ARB_LOST_GCA_ACK,
1159be8a82cSStrahinja Jankovic     STAT_S_DATA_RX_SA_ACK,
1169be8a82cSStrahinja Jankovic     STAT_S_DATA_RX_SA_NACK,
1179be8a82cSStrahinja Jankovic     STAT_S_DATA_RX_GCA_ACK,
1189be8a82cSStrahinja Jankovic     STAT_S_DATA_RX_GCA_NACK,
1199be8a82cSStrahinja Jankovic     STAT_S_STP_RSTA,
1209be8a82cSStrahinja Jankovic     STAT_S_ADDR_RD_ACK,
1219be8a82cSStrahinja Jankovic     STAT_S_ARB_LOST_AR_ACK,
1229be8a82cSStrahinja Jankovic     STAT_S_DATA_TX_ACK,
1239be8a82cSStrahinja Jankovic     STAT_S_DATA_TX_NACK,
1249be8a82cSStrahinja Jankovic     STAT_S_LB_TX_ACK,
1259be8a82cSStrahinja Jankovic     /* Master mode, 10-bit */
1269be8a82cSStrahinja Jankovic     STAT_M_2ND_ADDR_WR_ACK,
1279be8a82cSStrahinja Jankovic     STAT_M_2ND_ADDR_WR_NACK,
1289be8a82cSStrahinja Jankovic     /* Idle */
1299be8a82cSStrahinja Jankovic     STAT_IDLE = 0x1f
1309be8a82cSStrahinja Jankovic } TWI_STAT_STA;
1319be8a82cSStrahinja Jankovic 
allwinner_i2c_get_regname(unsigned offset)1329be8a82cSStrahinja Jankovic static const char *allwinner_i2c_get_regname(unsigned offset)
1339be8a82cSStrahinja Jankovic {
1349be8a82cSStrahinja Jankovic     switch (offset) {
1359be8a82cSStrahinja Jankovic     case TWI_ADDR_REG:
1369be8a82cSStrahinja Jankovic         return "ADDR";
1379be8a82cSStrahinja Jankovic     case TWI_XADDR_REG:
1389be8a82cSStrahinja Jankovic         return "XADDR";
1399be8a82cSStrahinja Jankovic     case TWI_DATA_REG:
1409be8a82cSStrahinja Jankovic         return "DATA";
1419be8a82cSStrahinja Jankovic     case TWI_CNTR_REG:
1429be8a82cSStrahinja Jankovic         return "CNTR";
1439be8a82cSStrahinja Jankovic     case TWI_STAT_REG:
1449be8a82cSStrahinja Jankovic         return "STAT";
1459be8a82cSStrahinja Jankovic     case TWI_CCR_REG:
1469be8a82cSStrahinja Jankovic         return "CCR";
1479be8a82cSStrahinja Jankovic     case TWI_SRST_REG:
1489be8a82cSStrahinja Jankovic         return "SRST";
1499be8a82cSStrahinja Jankovic     case TWI_EFR_REG:
1509be8a82cSStrahinja Jankovic         return "EFR";
1519be8a82cSStrahinja Jankovic     case TWI_LCR_REG:
1529be8a82cSStrahinja Jankovic         return "LCR";
1539be8a82cSStrahinja Jankovic     default:
1549be8a82cSStrahinja Jankovic         return "[?]";
1559be8a82cSStrahinja Jankovic     }
1569be8a82cSStrahinja Jankovic }
1579be8a82cSStrahinja Jankovic 
allwinner_i2c_is_reset(AWI2CState * s)1589be8a82cSStrahinja Jankovic static inline bool allwinner_i2c_is_reset(AWI2CState *s)
1599be8a82cSStrahinja Jankovic {
1609be8a82cSStrahinja Jankovic     return s->srst & TWI_SRST_MASK;
1619be8a82cSStrahinja Jankovic }
1629be8a82cSStrahinja Jankovic 
allwinner_i2c_bus_is_enabled(AWI2CState * s)1639be8a82cSStrahinja Jankovic static inline bool allwinner_i2c_bus_is_enabled(AWI2CState *s)
1649be8a82cSStrahinja Jankovic {
1659be8a82cSStrahinja Jankovic     return s->cntr & TWI_CNTR_BUS_EN;
1669be8a82cSStrahinja Jankovic }
1679be8a82cSStrahinja Jankovic 
allwinner_i2c_interrupt_is_enabled(AWI2CState * s)1689be8a82cSStrahinja Jankovic static inline bool allwinner_i2c_interrupt_is_enabled(AWI2CState *s)
1699be8a82cSStrahinja Jankovic {
1709be8a82cSStrahinja Jankovic     return s->cntr & TWI_CNTR_INT_EN;
1719be8a82cSStrahinja Jankovic }
1729be8a82cSStrahinja Jankovic 
allwinner_i2c_reset_hold(Object * obj,ResetType type)173*ad80e367SPeter Maydell static void allwinner_i2c_reset_hold(Object *obj, ResetType type)
1749be8a82cSStrahinja Jankovic {
1759be8a82cSStrahinja Jankovic     AWI2CState *s = AW_I2C(obj);
1769be8a82cSStrahinja Jankovic 
1779be8a82cSStrahinja Jankovic     if (STAT_TO_STA(s->stat) != STAT_IDLE) {
1789be8a82cSStrahinja Jankovic         i2c_end_transfer(s->bus);
1799be8a82cSStrahinja Jankovic     }
1809be8a82cSStrahinja Jankovic 
1819be8a82cSStrahinja Jankovic     s->addr  = TWI_ADDR_RESET;
1829be8a82cSStrahinja Jankovic     s->xaddr = TWI_XADDR_RESET;
1839be8a82cSStrahinja Jankovic     s->data  = TWI_DATA_RESET;
1849be8a82cSStrahinja Jankovic     s->cntr  = TWI_CNTR_RESET;
1859be8a82cSStrahinja Jankovic     s->stat  = TWI_STAT_RESET;
1869be8a82cSStrahinja Jankovic     s->ccr   = TWI_CCR_RESET;
1879be8a82cSStrahinja Jankovic     s->srst  = TWI_SRST_RESET;
1889be8a82cSStrahinja Jankovic     s->efr   = TWI_EFR_RESET;
1899be8a82cSStrahinja Jankovic     s->lcr   = TWI_LCR_RESET;
1909be8a82cSStrahinja Jankovic }
1919be8a82cSStrahinja Jankovic 
allwinner_i2c_raise_interrupt(AWI2CState * s)1929be8a82cSStrahinja Jankovic static inline void allwinner_i2c_raise_interrupt(AWI2CState *s)
1939be8a82cSStrahinja Jankovic {
1949be8a82cSStrahinja Jankovic     /*
1959be8a82cSStrahinja Jankovic      * Raise an interrupt if the device is not reset and it is configured
1969be8a82cSStrahinja Jankovic      * to generate some interrupts.
1979be8a82cSStrahinja Jankovic      */
1989be8a82cSStrahinja Jankovic     if (!allwinner_i2c_is_reset(s) && allwinner_i2c_bus_is_enabled(s)) {
1999be8a82cSStrahinja Jankovic         if (STAT_TO_STA(s->stat) != STAT_IDLE) {
2009be8a82cSStrahinja Jankovic             s->cntr |= TWI_CNTR_INT_FLAG;
2019be8a82cSStrahinja Jankovic             if (allwinner_i2c_interrupt_is_enabled(s)) {
2029be8a82cSStrahinja Jankovic                 qemu_irq_raise(s->irq);
2039be8a82cSStrahinja Jankovic             }
2049be8a82cSStrahinja Jankovic         }
2059be8a82cSStrahinja Jankovic     }
2069be8a82cSStrahinja Jankovic }
2079be8a82cSStrahinja Jankovic 
allwinner_i2c_read(void * opaque,hwaddr offset,unsigned size)2089be8a82cSStrahinja Jankovic static uint64_t allwinner_i2c_read(void *opaque, hwaddr offset,
2099be8a82cSStrahinja Jankovic                                    unsigned size)
2109be8a82cSStrahinja Jankovic {
2119be8a82cSStrahinja Jankovic     uint16_t value;
2129be8a82cSStrahinja Jankovic     AWI2CState *s = AW_I2C(opaque);
2139be8a82cSStrahinja Jankovic 
2149be8a82cSStrahinja Jankovic     switch (offset) {
2159be8a82cSStrahinja Jankovic     case TWI_ADDR_REG:
2169be8a82cSStrahinja Jankovic         value = s->addr;
2179be8a82cSStrahinja Jankovic         break;
2189be8a82cSStrahinja Jankovic     case TWI_XADDR_REG:
2199be8a82cSStrahinja Jankovic         value = s->xaddr;
2209be8a82cSStrahinja Jankovic         break;
2219be8a82cSStrahinja Jankovic     case TWI_DATA_REG:
2229be8a82cSStrahinja Jankovic         if ((STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) ||
2239be8a82cSStrahinja Jankovic             (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) ||
2249be8a82cSStrahinja Jankovic             (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK)) {
2259be8a82cSStrahinja Jankovic             /* Get the next byte */
2269be8a82cSStrahinja Jankovic             s->data = i2c_recv(s->bus);
2279be8a82cSStrahinja Jankovic 
2289be8a82cSStrahinja Jankovic             if (s->cntr & TWI_CNTR_A_ACK) {
2299be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
2309be8a82cSStrahinja Jankovic             } else {
2319be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
2329be8a82cSStrahinja Jankovic             }
2339be8a82cSStrahinja Jankovic             allwinner_i2c_raise_interrupt(s);
2349be8a82cSStrahinja Jankovic         }
2359be8a82cSStrahinja Jankovic         value = s->data;
2369be8a82cSStrahinja Jankovic         break;
2379be8a82cSStrahinja Jankovic     case TWI_CNTR_REG:
2389be8a82cSStrahinja Jankovic         value = s->cntr;
2399be8a82cSStrahinja Jankovic         break;
2409be8a82cSStrahinja Jankovic     case TWI_STAT_REG:
2419be8a82cSStrahinja Jankovic         value = s->stat;
2429be8a82cSStrahinja Jankovic         /*
2439be8a82cSStrahinja Jankovic          * If polling when reading then change state to indicate data
2449be8a82cSStrahinja Jankovic          * is available
2459be8a82cSStrahinja Jankovic          */
2469be8a82cSStrahinja Jankovic         if (STAT_TO_STA(s->stat) == STAT_M_ADDR_RD_ACK) {
2479be8a82cSStrahinja Jankovic             if (s->cntr & TWI_CNTR_A_ACK) {
2489be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
2499be8a82cSStrahinja Jankovic             } else {
2509be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
2519be8a82cSStrahinja Jankovic             }
2529be8a82cSStrahinja Jankovic             allwinner_i2c_raise_interrupt(s);
2539be8a82cSStrahinja Jankovic         }
2549be8a82cSStrahinja Jankovic         break;
2559be8a82cSStrahinja Jankovic     case TWI_CCR_REG:
2569be8a82cSStrahinja Jankovic         value = s->ccr;
2579be8a82cSStrahinja Jankovic         break;
2589be8a82cSStrahinja Jankovic     case TWI_SRST_REG:
2599be8a82cSStrahinja Jankovic         value = s->srst;
2609be8a82cSStrahinja Jankovic         break;
2619be8a82cSStrahinja Jankovic     case TWI_EFR_REG:
2629be8a82cSStrahinja Jankovic         value = s->efr;
2639be8a82cSStrahinja Jankovic         break;
2649be8a82cSStrahinja Jankovic     case TWI_LCR_REG:
2659be8a82cSStrahinja Jankovic         value = s->lcr;
2669be8a82cSStrahinja Jankovic         break;
2679be8a82cSStrahinja Jankovic     default:
2689be8a82cSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
2699be8a82cSStrahinja Jankovic                       HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
2709be8a82cSStrahinja Jankovic         value = 0;
2719be8a82cSStrahinja Jankovic         break;
2729be8a82cSStrahinja Jankovic     }
2739be8a82cSStrahinja Jankovic 
2749be8a82cSStrahinja Jankovic     trace_allwinner_i2c_read(allwinner_i2c_get_regname(offset), offset, value);
2759be8a82cSStrahinja Jankovic 
2769be8a82cSStrahinja Jankovic     return (uint64_t)value;
2779be8a82cSStrahinja Jankovic }
2789be8a82cSStrahinja Jankovic 
allwinner_i2c_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)2799be8a82cSStrahinja Jankovic static void allwinner_i2c_write(void *opaque, hwaddr offset,
2809be8a82cSStrahinja Jankovic                                 uint64_t value, unsigned size)
2819be8a82cSStrahinja Jankovic {
2829be8a82cSStrahinja Jankovic     AWI2CState *s = AW_I2C(opaque);
2839be8a82cSStrahinja Jankovic 
2849be8a82cSStrahinja Jankovic     value &= 0xff;
2859be8a82cSStrahinja Jankovic 
2869be8a82cSStrahinja Jankovic     trace_allwinner_i2c_write(allwinner_i2c_get_regname(offset), offset, value);
2879be8a82cSStrahinja Jankovic 
2889be8a82cSStrahinja Jankovic     switch (offset) {
2899be8a82cSStrahinja Jankovic     case TWI_ADDR_REG:
2909be8a82cSStrahinja Jankovic         s->addr = (uint8_t)value;
2919be8a82cSStrahinja Jankovic         break;
2929be8a82cSStrahinja Jankovic     case TWI_XADDR_REG:
2939be8a82cSStrahinja Jankovic         s->xaddr = (uint8_t)value;
2949be8a82cSStrahinja Jankovic         break;
2959be8a82cSStrahinja Jankovic     case TWI_DATA_REG:
2969be8a82cSStrahinja Jankovic         /* If the device is in reset or not enabled, nothing to do */
2979be8a82cSStrahinja Jankovic         if (allwinner_i2c_is_reset(s) || (!allwinner_i2c_bus_is_enabled(s))) {
2989be8a82cSStrahinja Jankovic             break;
2999be8a82cSStrahinja Jankovic         }
3009be8a82cSStrahinja Jankovic 
3019be8a82cSStrahinja Jankovic         s->data = value & TWI_DATA_MASK;
3029be8a82cSStrahinja Jankovic 
3039be8a82cSStrahinja Jankovic         switch (STAT_TO_STA(s->stat)) {
3049be8a82cSStrahinja Jankovic         case STAT_M_STA_TX:
3059be8a82cSStrahinja Jankovic         case STAT_M_RSTA_TX:
3069be8a82cSStrahinja Jankovic             /* Send address */
3079be8a82cSStrahinja Jankovic             if (i2c_start_transfer(s->bus, extract32(s->data, 1, 7),
3089be8a82cSStrahinja Jankovic                                 extract32(s->data, 0, 1))) {
3099be8a82cSStrahinja Jankovic                 /* If non zero is returned, the address is not valid */
3109be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_NACK);
3119be8a82cSStrahinja Jankovic             } else {
3129be8a82cSStrahinja Jankovic                 /* Determine if read of write */
3139be8a82cSStrahinja Jankovic                 if (extract32(s->data, 0, 1)) {
3149be8a82cSStrahinja Jankovic                     s->stat = STAT_FROM_STA(STAT_M_ADDR_RD_ACK);
3159be8a82cSStrahinja Jankovic                 } else {
3169be8a82cSStrahinja Jankovic                     s->stat = STAT_FROM_STA(STAT_M_ADDR_WR_ACK);
3179be8a82cSStrahinja Jankovic                 }
3189be8a82cSStrahinja Jankovic                 allwinner_i2c_raise_interrupt(s);
3199be8a82cSStrahinja Jankovic             }
3209be8a82cSStrahinja Jankovic             break;
3219be8a82cSStrahinja Jankovic         case STAT_M_ADDR_WR_ACK:
3229be8a82cSStrahinja Jankovic         case STAT_M_DATA_TX_ACK:
3239be8a82cSStrahinja Jankovic             if (i2c_send(s->bus, s->data)) {
3249be8a82cSStrahinja Jankovic                 /* If the target return non zero then end the transfer */
3259be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_DATA_TX_NACK);
3269be8a82cSStrahinja Jankovic                 i2c_end_transfer(s->bus);
3279be8a82cSStrahinja Jankovic             } else {
3289be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_M_DATA_TX_ACK);
3299be8a82cSStrahinja Jankovic                 allwinner_i2c_raise_interrupt(s);
3309be8a82cSStrahinja Jankovic             }
3319be8a82cSStrahinja Jankovic             break;
3329be8a82cSStrahinja Jankovic         default:
3339be8a82cSStrahinja Jankovic             break;
3349be8a82cSStrahinja Jankovic         }
3359be8a82cSStrahinja Jankovic         break;
3369be8a82cSStrahinja Jankovic     case TWI_CNTR_REG:
3379be8a82cSStrahinja Jankovic         if (!allwinner_i2c_is_reset(s)) {
3389be8a82cSStrahinja Jankovic             /* Do something only if not in software reset */
3399be8a82cSStrahinja Jankovic             s->cntr = value & TWI_CNTR_MASK;
3409be8a82cSStrahinja Jankovic 
3419be8a82cSStrahinja Jankovic             /* Check if start condition should be sent */
3429be8a82cSStrahinja Jankovic             if (s->cntr & TWI_CNTR_M_STA) {
3439be8a82cSStrahinja Jankovic                 /* Update status */
3449be8a82cSStrahinja Jankovic                 if (STAT_TO_STA(s->stat) == STAT_IDLE) {
3459be8a82cSStrahinja Jankovic                     /* Send start condition */
3469be8a82cSStrahinja Jankovic                     s->stat = STAT_FROM_STA(STAT_M_STA_TX);
3479be8a82cSStrahinja Jankovic                 } else {
3489be8a82cSStrahinja Jankovic                     /* Send repeated start condition */
3499be8a82cSStrahinja Jankovic                     s->stat = STAT_FROM_STA(STAT_M_RSTA_TX);
3509be8a82cSStrahinja Jankovic                 }
3519be8a82cSStrahinja Jankovic                 /* Clear start condition */
3529be8a82cSStrahinja Jankovic                 s->cntr &= ~TWI_CNTR_M_STA;
3539be8a82cSStrahinja Jankovic             }
3549be8a82cSStrahinja Jankovic             if (s->cntr & TWI_CNTR_M_STP) {
3559be8a82cSStrahinja Jankovic                 /* Update status */
3569be8a82cSStrahinja Jankovic                 i2c_end_transfer(s->bus);
3579be8a82cSStrahinja Jankovic                 s->stat = STAT_FROM_STA(STAT_IDLE);
3589be8a82cSStrahinja Jankovic                 s->cntr &= ~TWI_CNTR_M_STP;
3599be8a82cSStrahinja Jankovic             }
3608461bfdcSqianfan Zhao 
3618461bfdcSqianfan Zhao             if (!s->irq_clear_inverted && !(s->cntr & TWI_CNTR_INT_FLAG)) {
3628461bfdcSqianfan Zhao                 /* Write 0 to clear this flag */
3638461bfdcSqianfan Zhao                 qemu_irq_lower(s->irq);
3648461bfdcSqianfan Zhao             } else if (s->irq_clear_inverted && (s->cntr & TWI_CNTR_INT_FLAG)) {
3658461bfdcSqianfan Zhao                 /* Write 1 to clear this flag */
3668461bfdcSqianfan Zhao                 s->cntr &= ~TWI_CNTR_INT_FLAG;
3679be8a82cSStrahinja Jankovic                 qemu_irq_lower(s->irq);
3689be8a82cSStrahinja Jankovic             }
3698461bfdcSqianfan Zhao 
3709be8a82cSStrahinja Jankovic             if ((s->cntr & TWI_CNTR_A_ACK) == 0) {
3719be8a82cSStrahinja Jankovic                 if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_ACK) {
3729be8a82cSStrahinja Jankovic                     s->stat = STAT_FROM_STA(STAT_M_DATA_RX_NACK);
3739be8a82cSStrahinja Jankovic                 }
3749be8a82cSStrahinja Jankovic             } else {
3759be8a82cSStrahinja Jankovic                 if (STAT_TO_STA(s->stat) == STAT_M_DATA_RX_NACK) {
3769be8a82cSStrahinja Jankovic                     s->stat = STAT_FROM_STA(STAT_M_DATA_RX_ACK);
3779be8a82cSStrahinja Jankovic                 }
3789be8a82cSStrahinja Jankovic             }
3799be8a82cSStrahinja Jankovic             allwinner_i2c_raise_interrupt(s);
3809be8a82cSStrahinja Jankovic 
3819be8a82cSStrahinja Jankovic         }
3829be8a82cSStrahinja Jankovic         break;
3839be8a82cSStrahinja Jankovic     case TWI_CCR_REG:
3849be8a82cSStrahinja Jankovic         s->ccr = value & TWI_CCR_MASK;
3859be8a82cSStrahinja Jankovic         break;
3869be8a82cSStrahinja Jankovic     case TWI_SRST_REG:
3879be8a82cSStrahinja Jankovic         if (((value & TWI_SRST_MASK) == 0) && (s->srst & TWI_SRST_MASK)) {
388ef6ab292SPeter Maydell             device_cold_reset(DEVICE(s));
3899be8a82cSStrahinja Jankovic         }
3909be8a82cSStrahinja Jankovic         s->srst = value & TWI_SRST_MASK;
3919be8a82cSStrahinja Jankovic         break;
3929be8a82cSStrahinja Jankovic     case TWI_EFR_REG:
3939be8a82cSStrahinja Jankovic         s->efr = value & TWI_EFR_MASK;
3949be8a82cSStrahinja Jankovic         break;
3959be8a82cSStrahinja Jankovic     case TWI_LCR_REG:
3969be8a82cSStrahinja Jankovic         s->lcr = value & TWI_LCR_MASK;
3979be8a82cSStrahinja Jankovic         break;
3989be8a82cSStrahinja Jankovic     default:
3999be8a82cSStrahinja Jankovic         qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
4009be8a82cSStrahinja Jankovic                       HWADDR_PRIx "\n", TYPE_AW_I2C, __func__, offset);
4019be8a82cSStrahinja Jankovic         break;
4029be8a82cSStrahinja Jankovic     }
4039be8a82cSStrahinja Jankovic }
4049be8a82cSStrahinja Jankovic 
4059be8a82cSStrahinja Jankovic static const MemoryRegionOps allwinner_i2c_ops = {
4069be8a82cSStrahinja Jankovic     .read = allwinner_i2c_read,
4079be8a82cSStrahinja Jankovic     .write = allwinner_i2c_write,
4089be8a82cSStrahinja Jankovic     .valid.min_access_size = 1,
4099be8a82cSStrahinja Jankovic     .valid.max_access_size = 4,
4109be8a82cSStrahinja Jankovic     .endianness = DEVICE_NATIVE_ENDIAN,
4119be8a82cSStrahinja Jankovic };
4129be8a82cSStrahinja Jankovic 
4139be8a82cSStrahinja Jankovic static const VMStateDescription allwinner_i2c_vmstate = {
4149be8a82cSStrahinja Jankovic     .name = TYPE_AW_I2C,
4159be8a82cSStrahinja Jankovic     .version_id = 1,
4169be8a82cSStrahinja Jankovic     .minimum_version_id = 1,
41701d9442aSRichard Henderson     .fields = (const VMStateField[]) {
4189be8a82cSStrahinja Jankovic         VMSTATE_UINT8(addr, AWI2CState),
4199be8a82cSStrahinja Jankovic         VMSTATE_UINT8(xaddr, AWI2CState),
4209be8a82cSStrahinja Jankovic         VMSTATE_UINT8(data, AWI2CState),
4219be8a82cSStrahinja Jankovic         VMSTATE_UINT8(cntr, AWI2CState),
4229be8a82cSStrahinja Jankovic         VMSTATE_UINT8(ccr, AWI2CState),
4239be8a82cSStrahinja Jankovic         VMSTATE_UINT8(srst, AWI2CState),
4249be8a82cSStrahinja Jankovic         VMSTATE_UINT8(efr, AWI2CState),
4259be8a82cSStrahinja Jankovic         VMSTATE_UINT8(lcr, AWI2CState),
4269be8a82cSStrahinja Jankovic         VMSTATE_END_OF_LIST()
4279be8a82cSStrahinja Jankovic     }
4289be8a82cSStrahinja Jankovic };
4299be8a82cSStrahinja Jankovic 
allwinner_i2c_realize(DeviceState * dev,Error ** errp)4309be8a82cSStrahinja Jankovic static void allwinner_i2c_realize(DeviceState *dev, Error **errp)
4319be8a82cSStrahinja Jankovic {
4329be8a82cSStrahinja Jankovic     AWI2CState *s = AW_I2C(dev);
4339be8a82cSStrahinja Jankovic 
4349be8a82cSStrahinja Jankovic     memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_i2c_ops, s,
4359be8a82cSStrahinja Jankovic                           TYPE_AW_I2C, AW_I2C_MEM_SIZE);
4369be8a82cSStrahinja Jankovic     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
4379be8a82cSStrahinja Jankovic     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
4389be8a82cSStrahinja Jankovic     s->bus = i2c_init_bus(dev, "i2c");
4399be8a82cSStrahinja Jankovic }
4409be8a82cSStrahinja Jankovic 
allwinner_i2c_class_init(ObjectClass * klass,void * data)4419be8a82cSStrahinja Jankovic static void allwinner_i2c_class_init(ObjectClass *klass, void *data)
4429be8a82cSStrahinja Jankovic {
4439be8a82cSStrahinja Jankovic     DeviceClass *dc = DEVICE_CLASS(klass);
4449be8a82cSStrahinja Jankovic     ResettableClass *rc = RESETTABLE_CLASS(klass);
4459be8a82cSStrahinja Jankovic 
4469be8a82cSStrahinja Jankovic     rc->phases.hold = allwinner_i2c_reset_hold;
4479be8a82cSStrahinja Jankovic     dc->vmsd = &allwinner_i2c_vmstate;
4489be8a82cSStrahinja Jankovic     dc->realize = allwinner_i2c_realize;
4499be8a82cSStrahinja Jankovic     dc->desc = "Allwinner I2C Controller";
4509be8a82cSStrahinja Jankovic }
4519be8a82cSStrahinja Jankovic 
4529be8a82cSStrahinja Jankovic static const TypeInfo allwinner_i2c_type_info = {
4539be8a82cSStrahinja Jankovic     .name = TYPE_AW_I2C,
4549be8a82cSStrahinja Jankovic     .parent = TYPE_SYS_BUS_DEVICE,
4559be8a82cSStrahinja Jankovic     .instance_size = sizeof(AWI2CState),
4569be8a82cSStrahinja Jankovic     .class_init = allwinner_i2c_class_init,
4579be8a82cSStrahinja Jankovic };
4589be8a82cSStrahinja Jankovic 
allwinner_i2c_sun6i_init(Object * obj)4598461bfdcSqianfan Zhao static void allwinner_i2c_sun6i_init(Object *obj)
4608461bfdcSqianfan Zhao {
4618461bfdcSqianfan Zhao     AWI2CState *s = AW_I2C(obj);
4628461bfdcSqianfan Zhao 
4638461bfdcSqianfan Zhao     s->irq_clear_inverted = true;
4648461bfdcSqianfan Zhao }
4658461bfdcSqianfan Zhao 
4668461bfdcSqianfan Zhao static const TypeInfo allwinner_i2c_sun6i_type_info = {
4678461bfdcSqianfan Zhao     .name = TYPE_AW_I2C_SUN6I,
4686c50845aSPeter Maydell     .parent = TYPE_AW_I2C,
4698461bfdcSqianfan Zhao     .instance_init = allwinner_i2c_sun6i_init,
4708461bfdcSqianfan Zhao };
4718461bfdcSqianfan Zhao 
allwinner_i2c_register_types(void)4729be8a82cSStrahinja Jankovic static void allwinner_i2c_register_types(void)
4739be8a82cSStrahinja Jankovic {
4749be8a82cSStrahinja Jankovic     type_register_static(&allwinner_i2c_type_info);
4758461bfdcSqianfan Zhao     type_register_static(&allwinner_i2c_sun6i_type_info);
4769be8a82cSStrahinja Jankovic }
4779be8a82cSStrahinja Jankovic 
4789be8a82cSStrahinja Jankovic type_init(allwinner_i2c_register_types)
479