ad80e36744
We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Luc Michel <luc.michel@amd.com> Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org
1099 lines
32 KiB
C
1099 lines
32 KiB
C
/*
|
|
* Nuvoton NPCM7xx SMBus Module.
|
|
*
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
|
|
#include "hw/i2c/npcm7xx_smbus.h"
|
|
#include "migration/vmstate.h"
|
|
#include "qemu/bitops.h"
|
|
#include "qemu/guest-random.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/module.h"
|
|
#include "qemu/units.h"
|
|
|
|
#include "trace.h"
|
|
|
|
enum NPCM7xxSMBusCommonRegister {
|
|
NPCM7XX_SMB_SDA = 0x0,
|
|
NPCM7XX_SMB_ST = 0x2,
|
|
NPCM7XX_SMB_CST = 0x4,
|
|
NPCM7XX_SMB_CTL1 = 0x6,
|
|
NPCM7XX_SMB_ADDR1 = 0x8,
|
|
NPCM7XX_SMB_CTL2 = 0xa,
|
|
NPCM7XX_SMB_ADDR2 = 0xc,
|
|
NPCM7XX_SMB_CTL3 = 0xe,
|
|
NPCM7XX_SMB_CST2 = 0x18,
|
|
NPCM7XX_SMB_CST3 = 0x19,
|
|
NPCM7XX_SMB_VER = 0x1f,
|
|
};
|
|
|
|
enum NPCM7xxSMBusBank0Register {
|
|
NPCM7XX_SMB_ADDR3 = 0x10,
|
|
NPCM7XX_SMB_ADDR7 = 0x11,
|
|
NPCM7XX_SMB_ADDR4 = 0x12,
|
|
NPCM7XX_SMB_ADDR8 = 0x13,
|
|
NPCM7XX_SMB_ADDR5 = 0x14,
|
|
NPCM7XX_SMB_ADDR9 = 0x15,
|
|
NPCM7XX_SMB_ADDR6 = 0x16,
|
|
NPCM7XX_SMB_ADDR10 = 0x17,
|
|
NPCM7XX_SMB_CTL4 = 0x1a,
|
|
NPCM7XX_SMB_CTL5 = 0x1b,
|
|
NPCM7XX_SMB_SCLLT = 0x1c,
|
|
NPCM7XX_SMB_FIF_CTL = 0x1d,
|
|
NPCM7XX_SMB_SCLHT = 0x1e,
|
|
};
|
|
|
|
enum NPCM7xxSMBusBank1Register {
|
|
NPCM7XX_SMB_FIF_CTS = 0x10,
|
|
NPCM7XX_SMB_FAIR_PER = 0x11,
|
|
NPCM7XX_SMB_TXF_CTL = 0x12,
|
|
NPCM7XX_SMB_T_OUT = 0x14,
|
|
NPCM7XX_SMB_TXF_STS = 0x1a,
|
|
NPCM7XX_SMB_RXF_STS = 0x1c,
|
|
NPCM7XX_SMB_RXF_CTL = 0x1e,
|
|
};
|
|
|
|
/* ST fields */
|
|
#define NPCM7XX_SMBST_STP BIT(7)
|
|
#define NPCM7XX_SMBST_SDAST BIT(6)
|
|
#define NPCM7XX_SMBST_BER BIT(5)
|
|
#define NPCM7XX_SMBST_NEGACK BIT(4)
|
|
#define NPCM7XX_SMBST_STASTR BIT(3)
|
|
#define NPCM7XX_SMBST_NMATCH BIT(2)
|
|
#define NPCM7XX_SMBST_MODE BIT(1)
|
|
#define NPCM7XX_SMBST_XMIT BIT(0)
|
|
|
|
/* CST fields */
|
|
#define NPCM7XX_SMBCST_ARPMATCH BIT(7)
|
|
#define NPCM7XX_SMBCST_MATCHAF BIT(6)
|
|
#define NPCM7XX_SMBCST_TGSCL BIT(5)
|
|
#define NPCM7XX_SMBCST_TSDA BIT(4)
|
|
#define NPCM7XX_SMBCST_GCMATCH BIT(3)
|
|
#define NPCM7XX_SMBCST_MATCH BIT(2)
|
|
#define NPCM7XX_SMBCST_BB BIT(1)
|
|
#define NPCM7XX_SMBCST_BUSY BIT(0)
|
|
|
|
/* CST2 fields */
|
|
#define NPCM7XX_SMBCST2_INTSTS BIT(7)
|
|
#define NPCM7XX_SMBCST2_MATCH7F BIT(6)
|
|
#define NPCM7XX_SMBCST2_MATCH6F BIT(5)
|
|
#define NPCM7XX_SMBCST2_MATCH5F BIT(4)
|
|
#define NPCM7XX_SMBCST2_MATCH4F BIT(3)
|
|
#define NPCM7XX_SMBCST2_MATCH3F BIT(2)
|
|
#define NPCM7XX_SMBCST2_MATCH2F BIT(1)
|
|
#define NPCM7XX_SMBCST2_MATCH1F BIT(0)
|
|
|
|
/* CST3 fields */
|
|
#define NPCM7XX_SMBCST3_EO_BUSY BIT(7)
|
|
#define NPCM7XX_SMBCST3_MATCH10F BIT(2)
|
|
#define NPCM7XX_SMBCST3_MATCH9F BIT(1)
|
|
#define NPCM7XX_SMBCST3_MATCH8F BIT(0)
|
|
|
|
/* CTL1 fields */
|
|
#define NPCM7XX_SMBCTL1_STASTRE BIT(7)
|
|
#define NPCM7XX_SMBCTL1_NMINTE BIT(6)
|
|
#define NPCM7XX_SMBCTL1_GCMEN BIT(5)
|
|
#define NPCM7XX_SMBCTL1_ACK BIT(4)
|
|
#define NPCM7XX_SMBCTL1_EOBINTE BIT(3)
|
|
#define NPCM7XX_SMBCTL1_INTEN BIT(2)
|
|
#define NPCM7XX_SMBCTL1_STOP BIT(1)
|
|
#define NPCM7XX_SMBCTL1_START BIT(0)
|
|
|
|
/* CTL2 fields */
|
|
#define NPCM7XX_SMBCTL2_SCLFRQ(rv) extract8((rv), 1, 6)
|
|
#define NPCM7XX_SMBCTL2_ENABLE BIT(0)
|
|
|
|
/* CTL3 fields */
|
|
#define NPCM7XX_SMBCTL3_SCL_LVL BIT(7)
|
|
#define NPCM7XX_SMBCTL3_SDA_LVL BIT(6)
|
|
#define NPCM7XX_SMBCTL3_BNK_SEL BIT(5)
|
|
#define NPCM7XX_SMBCTL3_400K_MODE BIT(4)
|
|
#define NPCM7XX_SMBCTL3_IDL_START BIT(3)
|
|
#define NPCM7XX_SMBCTL3_ARPMEN BIT(2)
|
|
#define NPCM7XX_SMBCTL3_SCLFRQ(rv) extract8((rv), 0, 2)
|
|
|
|
/* ADDR fields */
|
|
#define NPCM7XX_ADDR_EN BIT(7)
|
|
#define NPCM7XX_ADDR_A(rv) extract8((rv), 0, 6)
|
|
|
|
/* FIFO Mode Register Fields */
|
|
/* FIF_CTL fields */
|
|
#define NPCM7XX_SMBFIF_CTL_FIFO_EN BIT(4)
|
|
#define NPCM7XX_SMBFIF_CTL_FAIR_RDY_IE BIT(2)
|
|
#define NPCM7XX_SMBFIF_CTL_FAIR_RDY BIT(1)
|
|
#define NPCM7XX_SMBFIF_CTL_FAIR_BUSY BIT(0)
|
|
/* FIF_CTS fields */
|
|
#define NPCM7XX_SMBFIF_CTS_STR BIT(7)
|
|
#define NPCM7XX_SMBFIF_CTS_CLR_FIFO BIT(6)
|
|
#define NPCM7XX_SMBFIF_CTS_RFTE_IE BIT(3)
|
|
#define NPCM7XX_SMBFIF_CTS_RXF_TXE BIT(1)
|
|
/* TXF_CTL fields */
|
|
#define NPCM7XX_SMBTXF_CTL_THR_TXIE BIT(6)
|
|
#define NPCM7XX_SMBTXF_CTL_TX_THR(rv) extract8((rv), 0, 5)
|
|
/* T_OUT fields */
|
|
#define NPCM7XX_SMBT_OUT_ST BIT(7)
|
|
#define NPCM7XX_SMBT_OUT_IE BIT(6)
|
|
#define NPCM7XX_SMBT_OUT_CLKDIV(rv) extract8((rv), 0, 6)
|
|
/* TXF_STS fields */
|
|
#define NPCM7XX_SMBTXF_STS_TX_THST BIT(6)
|
|
#define NPCM7XX_SMBTXF_STS_TX_BYTES(rv) extract8((rv), 0, 5)
|
|
/* RXF_STS fields */
|
|
#define NPCM7XX_SMBRXF_STS_RX_THST BIT(6)
|
|
#define NPCM7XX_SMBRXF_STS_RX_BYTES(rv) extract8((rv), 0, 5)
|
|
/* RXF_CTL fields */
|
|
#define NPCM7XX_SMBRXF_CTL_THR_RXIE BIT(6)
|
|
#define NPCM7XX_SMBRXF_CTL_LAST BIT(5)
|
|
#define NPCM7XX_SMBRXF_CTL_RX_THR(rv) extract8((rv), 0, 5)
|
|
|
|
#define KEEP_OLD_BIT(o, n, b) (((n) & (~(b))) | ((o) & (b)))
|
|
#define WRITE_ONE_CLEAR(o, n, b) ((n) & (b) ? (o) & (~(b)) : (o))
|
|
|
|
#define NPCM7XX_SMBUS_ENABLED(s) ((s)->ctl2 & NPCM7XX_SMBCTL2_ENABLE)
|
|
#define NPCM7XX_SMBUS_FIFO_ENABLED(s) ((s)->fif_ctl & \
|
|
NPCM7XX_SMBFIF_CTL_FIFO_EN)
|
|
|
|
/* VERSION fields values, read-only. */
|
|
#define NPCM7XX_SMBUS_VERSION_NUMBER 1
|
|
#define NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED 1
|
|
|
|
/* Reset values */
|
|
#define NPCM7XX_SMB_ST_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_CST_INIT_VAL 0x10
|
|
#define NPCM7XX_SMB_CST2_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_CST3_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_CTL1_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_CTL2_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_CTL3_INIT_VAL 0xc0
|
|
#define NPCM7XX_SMB_CTL4_INIT_VAL 0x07
|
|
#define NPCM7XX_SMB_CTL5_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_ADDR_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_SCLLT_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_SCLHT_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_FIF_CTL_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_FIF_CTS_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_FAIR_PER_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_TXF_CTL_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_T_OUT_INIT_VAL 0x3f
|
|
#define NPCM7XX_SMB_TXF_STS_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_RXF_STS_INIT_VAL 0x00
|
|
#define NPCM7XX_SMB_RXF_CTL_INIT_VAL 0x01
|
|
|
|
static uint8_t npcm7xx_smbus_get_version(void)
|
|
{
|
|
return NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED << 7 |
|
|
NPCM7XX_SMBUS_VERSION_NUMBER;
|
|
}
|
|
|
|
static void npcm7xx_smbus_update_irq(NPCM7xxSMBusState *s)
|
|
{
|
|
int level;
|
|
|
|
if (s->ctl1 & NPCM7XX_SMBCTL1_INTEN) {
|
|
level = !!((s->ctl1 & NPCM7XX_SMBCTL1_NMINTE &&
|
|
s->st & NPCM7XX_SMBST_NMATCH) ||
|
|
(s->st & NPCM7XX_SMBST_BER) ||
|
|
(s->st & NPCM7XX_SMBST_NEGACK) ||
|
|
(s->st & NPCM7XX_SMBST_SDAST) ||
|
|
(s->ctl1 & NPCM7XX_SMBCTL1_STASTRE &&
|
|
s->st & NPCM7XX_SMBST_SDAST) ||
|
|
(s->ctl1 & NPCM7XX_SMBCTL1_EOBINTE &&
|
|
s->cst3 & NPCM7XX_SMBCST3_EO_BUSY) ||
|
|
(s->rxf_ctl & NPCM7XX_SMBRXF_CTL_THR_RXIE &&
|
|
s->rxf_sts & NPCM7XX_SMBRXF_STS_RX_THST) ||
|
|
(s->txf_ctl & NPCM7XX_SMBTXF_CTL_THR_TXIE &&
|
|
s->txf_sts & NPCM7XX_SMBTXF_STS_TX_THST) ||
|
|
(s->fif_cts & NPCM7XX_SMBFIF_CTS_RFTE_IE &&
|
|
s->fif_cts & NPCM7XX_SMBFIF_CTS_RXF_TXE));
|
|
|
|
if (level) {
|
|
s->cst2 |= NPCM7XX_SMBCST2_INTSTS;
|
|
} else {
|
|
s->cst2 &= ~NPCM7XX_SMBCST2_INTSTS;
|
|
}
|
|
qemu_set_irq(s->irq, level);
|
|
}
|
|
}
|
|
|
|
static void npcm7xx_smbus_nack(NPCM7xxSMBusState *s)
|
|
{
|
|
s->st &= ~NPCM7XX_SMBST_SDAST;
|
|
s->st |= NPCM7XX_SMBST_NEGACK;
|
|
s->status = NPCM7XX_SMBUS_STATUS_NEGACK;
|
|
}
|
|
|
|
static void npcm7xx_smbus_clear_buffer(NPCM7xxSMBusState *s)
|
|
{
|
|
s->fif_cts &= ~NPCM7XX_SMBFIF_CTS_RXF_TXE;
|
|
s->txf_sts = 0;
|
|
s->rxf_sts = 0;
|
|
}
|
|
|
|
static void npcm7xx_smbus_send_byte(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
int rv = i2c_send(s->bus, value);
|
|
|
|
if (rv) {
|
|
npcm7xx_smbus_nack(s);
|
|
} else {
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
s->fif_cts |= NPCM7XX_SMBFIF_CTS_RXF_TXE;
|
|
if (NPCM7XX_SMBTXF_STS_TX_BYTES(s->txf_sts) ==
|
|
NPCM7XX_SMBTXF_CTL_TX_THR(s->txf_ctl)) {
|
|
s->txf_sts = NPCM7XX_SMBTXF_STS_TX_THST;
|
|
} else {
|
|
s->txf_sts = 0;
|
|
}
|
|
}
|
|
}
|
|
trace_npcm7xx_smbus_send_byte((DEVICE(s)->canonical_path), value, !rv);
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_recv_byte(NPCM7xxSMBusState *s)
|
|
{
|
|
s->sda = i2c_recv(s->bus);
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
if (s->st & NPCM7XX_SMBCTL1_ACK) {
|
|
trace_npcm7xx_smbus_nack(DEVICE(s)->canonical_path);
|
|
i2c_nack(s->bus);
|
|
s->st &= NPCM7XX_SMBCTL1_ACK;
|
|
}
|
|
trace_npcm7xx_smbus_recv_byte((DEVICE(s)->canonical_path), s->sda);
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_recv_fifo(NPCM7xxSMBusState *s)
|
|
{
|
|
uint8_t expected_bytes = NPCM7XX_SMBRXF_CTL_RX_THR(s->rxf_ctl);
|
|
uint8_t received_bytes = NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts);
|
|
uint8_t pos;
|
|
|
|
if (received_bytes == expected_bytes) {
|
|
return;
|
|
}
|
|
|
|
while (received_bytes < expected_bytes &&
|
|
received_bytes < NPCM7XX_SMBUS_FIFO_SIZE) {
|
|
pos = (s->rx_cur + received_bytes) % NPCM7XX_SMBUS_FIFO_SIZE;
|
|
s->rx_fifo[pos] = i2c_recv(s->bus);
|
|
trace_npcm7xx_smbus_recv_byte((DEVICE(s)->canonical_path),
|
|
s->rx_fifo[pos]);
|
|
++received_bytes;
|
|
}
|
|
|
|
trace_npcm7xx_smbus_recv_fifo((DEVICE(s)->canonical_path),
|
|
received_bytes, expected_bytes);
|
|
s->rxf_sts = received_bytes;
|
|
if (unlikely(received_bytes < expected_bytes)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: invalid rx_thr value: 0x%02x\n",
|
|
DEVICE(s)->canonical_path, expected_bytes);
|
|
return;
|
|
}
|
|
|
|
s->rxf_sts |= NPCM7XX_SMBRXF_STS_RX_THST;
|
|
if (s->rxf_ctl & NPCM7XX_SMBRXF_CTL_LAST) {
|
|
trace_npcm7xx_smbus_nack(DEVICE(s)->canonical_path);
|
|
i2c_nack(s->bus);
|
|
s->rxf_ctl &= ~NPCM7XX_SMBRXF_CTL_LAST;
|
|
}
|
|
if (received_bytes == NPCM7XX_SMBUS_FIFO_SIZE) {
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
s->fif_cts |= NPCM7XX_SMBFIF_CTS_RXF_TXE;
|
|
} else if (!(s->rxf_ctl & NPCM7XX_SMBRXF_CTL_THR_RXIE)) {
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
} else {
|
|
s->st &= ~NPCM7XX_SMBST_SDAST;
|
|
}
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_read_byte_fifo(NPCM7xxSMBusState *s)
|
|
{
|
|
uint8_t received_bytes = NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts);
|
|
|
|
if (received_bytes == 0) {
|
|
npcm7xx_smbus_recv_fifo(s);
|
|
return;
|
|
}
|
|
|
|
s->sda = s->rx_fifo[s->rx_cur];
|
|
s->rx_cur = (s->rx_cur + 1u) % NPCM7XX_SMBUS_FIFO_SIZE;
|
|
--s->rxf_sts;
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_start(NPCM7xxSMBusState *s)
|
|
{
|
|
/*
|
|
* We can start the bus if one of these is true:
|
|
* 1. The bus is idle (so we can request it)
|
|
* 2. We are the occupier (it's a repeated start condition.)
|
|
*/
|
|
int available = !i2c_bus_busy(s->bus) ||
|
|
s->status != NPCM7XX_SMBUS_STATUS_IDLE;
|
|
|
|
if (available) {
|
|
s->st |= NPCM7XX_SMBST_MODE | NPCM7XX_SMBST_XMIT | NPCM7XX_SMBST_SDAST;
|
|
s->cst |= NPCM7XX_SMBCST_BUSY;
|
|
if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
s->fif_cts |= NPCM7XX_SMBFIF_CTS_RXF_TXE;
|
|
}
|
|
} else {
|
|
s->st &= ~NPCM7XX_SMBST_MODE;
|
|
s->cst &= ~NPCM7XX_SMBCST_BUSY;
|
|
s->st |= NPCM7XX_SMBST_BER;
|
|
}
|
|
|
|
trace_npcm7xx_smbus_start(DEVICE(s)->canonical_path, available);
|
|
s->cst |= NPCM7XX_SMBCST_BB;
|
|
s->status = NPCM7XX_SMBUS_STATUS_IDLE;
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_send_address(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
int recv;
|
|
int rv;
|
|
|
|
recv = value & BIT(0);
|
|
rv = i2c_start_transfer(s->bus, value >> 1, recv);
|
|
trace_npcm7xx_smbus_send_address(DEVICE(s)->canonical_path,
|
|
value >> 1, recv, !rv);
|
|
if (rv) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: requesting i2c bus for 0x%02x failed: %d\n",
|
|
DEVICE(s)->canonical_path, value, rv);
|
|
/* Failed to start transfer. NACK to reject.*/
|
|
if (recv) {
|
|
s->st &= ~NPCM7XX_SMBST_XMIT;
|
|
} else {
|
|
s->st |= NPCM7XX_SMBST_XMIT;
|
|
}
|
|
npcm7xx_smbus_nack(s);
|
|
npcm7xx_smbus_update_irq(s);
|
|
return;
|
|
}
|
|
|
|
s->st &= ~NPCM7XX_SMBST_NEGACK;
|
|
if (recv) {
|
|
s->status = NPCM7XX_SMBUS_STATUS_RECEIVING;
|
|
s->st &= ~NPCM7XX_SMBST_XMIT;
|
|
} else {
|
|
s->status = NPCM7XX_SMBUS_STATUS_SENDING;
|
|
s->st |= NPCM7XX_SMBST_XMIT;
|
|
}
|
|
|
|
if (s->ctl1 & NPCM7XX_SMBCTL1_STASTRE) {
|
|
s->st |= NPCM7XX_SMBST_STASTR;
|
|
if (!recv) {
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
}
|
|
} else if (recv) {
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
npcm7xx_smbus_recv_fifo(s);
|
|
} else {
|
|
npcm7xx_smbus_recv_byte(s);
|
|
}
|
|
} else if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
s->st |= NPCM7XX_SMBST_SDAST;
|
|
s->fif_cts |= NPCM7XX_SMBFIF_CTS_RXF_TXE;
|
|
}
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_execute_stop(NPCM7xxSMBusState *s)
|
|
{
|
|
i2c_end_transfer(s->bus);
|
|
s->st = 0;
|
|
s->cst = 0;
|
|
s->status = NPCM7XX_SMBUS_STATUS_IDLE;
|
|
s->cst3 |= NPCM7XX_SMBCST3_EO_BUSY;
|
|
trace_npcm7xx_smbus_stop(DEVICE(s)->canonical_path);
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
|
|
static void npcm7xx_smbus_stop(NPCM7xxSMBusState *s)
|
|
{
|
|
if (s->st & NPCM7XX_SMBST_MODE) {
|
|
switch (s->status) {
|
|
case NPCM7XX_SMBUS_STATUS_RECEIVING:
|
|
case NPCM7XX_SMBUS_STATUS_STOPPING_LAST_RECEIVE:
|
|
s->status = NPCM7XX_SMBUS_STATUS_STOPPING_LAST_RECEIVE;
|
|
break;
|
|
|
|
case NPCM7XX_SMBUS_STATUS_NEGACK:
|
|
s->status = NPCM7XX_SMBUS_STATUS_STOPPING_NEGACK;
|
|
break;
|
|
|
|
default:
|
|
npcm7xx_smbus_execute_stop(s);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t npcm7xx_smbus_read_sda(NPCM7xxSMBusState *s)
|
|
{
|
|
uint8_t value = s->sda;
|
|
|
|
switch (s->status) {
|
|
case NPCM7XX_SMBUS_STATUS_STOPPING_LAST_RECEIVE:
|
|
if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
if (NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts) <= 1) {
|
|
npcm7xx_smbus_execute_stop(s);
|
|
}
|
|
if (NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts) == 0) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: read to SDA with an empty rx-fifo buffer, "
|
|
"result undefined: %u\n",
|
|
DEVICE(s)->canonical_path, s->sda);
|
|
break;
|
|
}
|
|
npcm7xx_smbus_read_byte_fifo(s);
|
|
value = s->sda;
|
|
} else {
|
|
npcm7xx_smbus_execute_stop(s);
|
|
}
|
|
break;
|
|
|
|
case NPCM7XX_SMBUS_STATUS_RECEIVING:
|
|
if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
npcm7xx_smbus_read_byte_fifo(s);
|
|
value = s->sda;
|
|
} else {
|
|
npcm7xx_smbus_recv_byte(s);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Do nothing */
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_sda(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->sda = value;
|
|
if (s->st & NPCM7XX_SMBST_MODE) {
|
|
switch (s->status) {
|
|
case NPCM7XX_SMBUS_STATUS_IDLE:
|
|
npcm7xx_smbus_send_address(s, value);
|
|
break;
|
|
case NPCM7XX_SMBUS_STATUS_SENDING:
|
|
npcm7xx_smbus_send_byte(s, value);
|
|
break;
|
|
default:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: write to SDA in invalid status %d: %u\n",
|
|
DEVICE(s)->canonical_path, s->status, value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_st(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->st = WRITE_ONE_CLEAR(s->st, value, NPCM7XX_SMBST_STP);
|
|
s->st = WRITE_ONE_CLEAR(s->st, value, NPCM7XX_SMBST_BER);
|
|
s->st = WRITE_ONE_CLEAR(s->st, value, NPCM7XX_SMBST_STASTR);
|
|
s->st = WRITE_ONE_CLEAR(s->st, value, NPCM7XX_SMBST_NMATCH);
|
|
|
|
if (value & NPCM7XX_SMBST_NEGACK) {
|
|
s->st &= ~NPCM7XX_SMBST_NEGACK;
|
|
if (s->status == NPCM7XX_SMBUS_STATUS_STOPPING_NEGACK) {
|
|
npcm7xx_smbus_execute_stop(s);
|
|
}
|
|
}
|
|
|
|
if (value & NPCM7XX_SMBST_STASTR &&
|
|
s->status == NPCM7XX_SMBUS_STATUS_RECEIVING) {
|
|
if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
|
|
npcm7xx_smbus_recv_fifo(s);
|
|
} else {
|
|
npcm7xx_smbus_recv_byte(s);
|
|
}
|
|
}
|
|
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_cst(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
uint8_t new_value = s->cst;
|
|
|
|
s->cst = WRITE_ONE_CLEAR(new_value, value, NPCM7XX_SMBCST_BB);
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_cst3(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->cst3 = WRITE_ONE_CLEAR(s->cst3, value, NPCM7XX_SMBCST3_EO_BUSY);
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_ctl1(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->ctl1 = KEEP_OLD_BIT(s->ctl1, value,
|
|
NPCM7XX_SMBCTL1_START | NPCM7XX_SMBCTL1_STOP | NPCM7XX_SMBCTL1_ACK);
|
|
|
|
if (value & NPCM7XX_SMBCTL1_START) {
|
|
npcm7xx_smbus_start(s);
|
|
}
|
|
|
|
if (value & NPCM7XX_SMBCTL1_STOP) {
|
|
npcm7xx_smbus_stop(s);
|
|
}
|
|
|
|
npcm7xx_smbus_update_irq(s);
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_ctl2(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->ctl2 = value;
|
|
|
|
if (!NPCM7XX_SMBUS_ENABLED(s)) {
|
|
/* Disable this SMBus module. */
|
|
s->ctl1 = 0;
|
|
s->st = 0;
|
|
s->cst3 = s->cst3 & (~NPCM7XX_SMBCST3_EO_BUSY);
|
|
s->cst = 0;
|
|
npcm7xx_smbus_clear_buffer(s);
|
|
}
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_ctl3(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
uint8_t old_ctl3 = s->ctl3;
|
|
|
|
/* Write to SDA and SCL bits are ignored. */
|
|
s->ctl3 = KEEP_OLD_BIT(old_ctl3, value,
|
|
NPCM7XX_SMBCTL3_SCL_LVL | NPCM7XX_SMBCTL3_SDA_LVL);
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_fif_ctl(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
uint8_t new_ctl = value;
|
|
|
|
new_ctl = KEEP_OLD_BIT(s->fif_ctl, new_ctl, NPCM7XX_SMBFIF_CTL_FAIR_RDY);
|
|
new_ctl = WRITE_ONE_CLEAR(new_ctl, value, NPCM7XX_SMBFIF_CTL_FAIR_RDY);
|
|
new_ctl = KEEP_OLD_BIT(s->fif_ctl, new_ctl, NPCM7XX_SMBFIF_CTL_FAIR_BUSY);
|
|
s->fif_ctl = new_ctl;
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_fif_cts(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->fif_cts = WRITE_ONE_CLEAR(s->fif_cts, value, NPCM7XX_SMBFIF_CTS_STR);
|
|
s->fif_cts = WRITE_ONE_CLEAR(s->fif_cts, value, NPCM7XX_SMBFIF_CTS_RXF_TXE);
|
|
s->fif_cts = KEEP_OLD_BIT(value, s->fif_cts, NPCM7XX_SMBFIF_CTS_RFTE_IE);
|
|
|
|
if (value & NPCM7XX_SMBFIF_CTS_CLR_FIFO) {
|
|
npcm7xx_smbus_clear_buffer(s);
|
|
}
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_txf_ctl(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->txf_ctl = value;
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_t_out(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
uint8_t new_t_out = value;
|
|
|
|
if ((value & NPCM7XX_SMBT_OUT_ST) || (!(s->t_out & NPCM7XX_SMBT_OUT_ST))) {
|
|
new_t_out &= ~NPCM7XX_SMBT_OUT_ST;
|
|
} else {
|
|
new_t_out |= NPCM7XX_SMBT_OUT_ST;
|
|
}
|
|
|
|
s->t_out = new_t_out;
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_txf_sts(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
s->txf_sts = WRITE_ONE_CLEAR(s->txf_sts, value, NPCM7XX_SMBTXF_STS_TX_THST);
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_rxf_sts(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
if (value & NPCM7XX_SMBRXF_STS_RX_THST) {
|
|
s->rxf_sts &= ~NPCM7XX_SMBRXF_STS_RX_THST;
|
|
if (s->status == NPCM7XX_SMBUS_STATUS_RECEIVING) {
|
|
npcm7xx_smbus_recv_fifo(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void npcm7xx_smbus_write_rxf_ctl(NPCM7xxSMBusState *s, uint8_t value)
|
|
{
|
|
uint8_t new_ctl = value;
|
|
|
|
if (!(value & NPCM7XX_SMBRXF_CTL_LAST)) {
|
|
new_ctl = KEEP_OLD_BIT(s->rxf_ctl, new_ctl, NPCM7XX_SMBRXF_CTL_LAST);
|
|
}
|
|
s->rxf_ctl = new_ctl;
|
|
}
|
|
|
|
static uint64_t npcm7xx_smbus_read(void *opaque, hwaddr offset, unsigned size)
|
|
{
|
|
NPCM7xxSMBusState *s = opaque;
|
|
uint64_t value = 0;
|
|
uint8_t bank = s->ctl3 & NPCM7XX_SMBCTL3_BNK_SEL;
|
|
|
|
/* The order of the registers are their order in memory. */
|
|
switch (offset) {
|
|
case NPCM7XX_SMB_SDA:
|
|
value = npcm7xx_smbus_read_sda(s);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ST:
|
|
value = s->st;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CST:
|
|
value = s->cst;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL1:
|
|
value = s->ctl1;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR1:
|
|
value = s->addr[0];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL2:
|
|
value = s->ctl2;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR2:
|
|
value = s->addr[1];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL3:
|
|
value = s->ctl3;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CST2:
|
|
value = s->cst2;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CST3:
|
|
value = s->cst3;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_VER:
|
|
value = npcm7xx_smbus_get_version();
|
|
break;
|
|
|
|
/* This register is either invalid or banked at this point. */
|
|
default:
|
|
if (bank) {
|
|
/* Bank 1 */
|
|
switch (offset) {
|
|
case NPCM7XX_SMB_FIF_CTS:
|
|
value = s->fif_cts;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_FAIR_PER:
|
|
value = s->fair_per;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_TXF_CTL:
|
|
value = s->txf_ctl;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_T_OUT:
|
|
value = s->t_out;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_TXF_STS:
|
|
value = s->txf_sts;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_RXF_STS:
|
|
value = s->rxf_sts;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_RXF_CTL:
|
|
value = s->rxf_ctl;
|
|
break;
|
|
|
|
default:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: read from invalid offset 0x%" HWADDR_PRIx "\n",
|
|
DEVICE(s)->canonical_path, offset);
|
|
break;
|
|
}
|
|
} else {
|
|
/* Bank 0 */
|
|
switch (offset) {
|
|
case NPCM7XX_SMB_ADDR3:
|
|
value = s->addr[2];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR7:
|
|
value = s->addr[6];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR4:
|
|
value = s->addr[3];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR8:
|
|
value = s->addr[7];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR5:
|
|
value = s->addr[4];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR9:
|
|
value = s->addr[8];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR6:
|
|
value = s->addr[5];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR10:
|
|
value = s->addr[9];
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL4:
|
|
value = s->ctl4;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL5:
|
|
value = s->ctl5;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_SCLLT:
|
|
value = s->scllt;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_FIF_CTL:
|
|
value = s->fif_ctl;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_SCLHT:
|
|
value = s->sclht;
|
|
break;
|
|
|
|
default:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: read from invalid offset 0x%" HWADDR_PRIx "\n",
|
|
DEVICE(s)->canonical_path, offset);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
trace_npcm7xx_smbus_read(DEVICE(s)->canonical_path, offset, value, size);
|
|
|
|
return value;
|
|
}
|
|
|
|
static void npcm7xx_smbus_write(void *opaque, hwaddr offset, uint64_t value,
|
|
unsigned size)
|
|
{
|
|
NPCM7xxSMBusState *s = opaque;
|
|
uint8_t bank = s->ctl3 & NPCM7XX_SMBCTL3_BNK_SEL;
|
|
|
|
trace_npcm7xx_smbus_write(DEVICE(s)->canonical_path, offset, value, size);
|
|
|
|
/* The order of the registers are their order in memory. */
|
|
switch (offset) {
|
|
case NPCM7XX_SMB_SDA:
|
|
npcm7xx_smbus_write_sda(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ST:
|
|
npcm7xx_smbus_write_st(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CST:
|
|
npcm7xx_smbus_write_cst(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL1:
|
|
npcm7xx_smbus_write_ctl1(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR1:
|
|
s->addr[0] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL2:
|
|
npcm7xx_smbus_write_ctl2(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR2:
|
|
s->addr[1] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL3:
|
|
npcm7xx_smbus_write_ctl3(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CST2:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: write to read-only reg: offset 0x%" HWADDR_PRIx "\n",
|
|
DEVICE(s)->canonical_path, offset);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CST3:
|
|
npcm7xx_smbus_write_cst3(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_VER:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: write to read-only reg: offset 0x%" HWADDR_PRIx "\n",
|
|
DEVICE(s)->canonical_path, offset);
|
|
break;
|
|
|
|
/* This register is either invalid or banked at this point. */
|
|
default:
|
|
if (bank) {
|
|
/* Bank 1 */
|
|
switch (offset) {
|
|
case NPCM7XX_SMB_FIF_CTS:
|
|
npcm7xx_smbus_write_fif_cts(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_FAIR_PER:
|
|
s->fair_per = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_TXF_CTL:
|
|
npcm7xx_smbus_write_txf_ctl(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_T_OUT:
|
|
npcm7xx_smbus_write_t_out(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_TXF_STS:
|
|
npcm7xx_smbus_write_txf_sts(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_RXF_STS:
|
|
npcm7xx_smbus_write_rxf_sts(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_RXF_CTL:
|
|
npcm7xx_smbus_write_rxf_ctl(s, value);
|
|
break;
|
|
|
|
default:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: write to invalid offset 0x%" HWADDR_PRIx "\n",
|
|
DEVICE(s)->canonical_path, offset);
|
|
break;
|
|
}
|
|
} else {
|
|
/* Bank 0 */
|
|
switch (offset) {
|
|
case NPCM7XX_SMB_ADDR3:
|
|
s->addr[2] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR7:
|
|
s->addr[6] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR4:
|
|
s->addr[3] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR8:
|
|
s->addr[7] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR5:
|
|
s->addr[4] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR9:
|
|
s->addr[8] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR6:
|
|
s->addr[5] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_ADDR10:
|
|
s->addr[9] = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL4:
|
|
s->ctl4 = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_CTL5:
|
|
s->ctl5 = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_SCLLT:
|
|
s->scllt = value;
|
|
break;
|
|
|
|
case NPCM7XX_SMB_FIF_CTL:
|
|
npcm7xx_smbus_write_fif_ctl(s, value);
|
|
break;
|
|
|
|
case NPCM7XX_SMB_SCLHT:
|
|
s->sclht = value;
|
|
break;
|
|
|
|
default:
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"%s: write to invalid offset 0x%" HWADDR_PRIx "\n",
|
|
DEVICE(s)->canonical_path, offset);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const MemoryRegionOps npcm7xx_smbus_ops = {
|
|
.read = npcm7xx_smbus_read,
|
|
.write = npcm7xx_smbus_write,
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
.valid = {
|
|
.min_access_size = 1,
|
|
.max_access_size = 1,
|
|
.unaligned = false,
|
|
},
|
|
};
|
|
|
|
static void npcm7xx_smbus_enter_reset(Object *obj, ResetType type)
|
|
{
|
|
NPCM7xxSMBusState *s = NPCM7XX_SMBUS(obj);
|
|
|
|
s->st = NPCM7XX_SMB_ST_INIT_VAL;
|
|
s->cst = NPCM7XX_SMB_CST_INIT_VAL;
|
|
s->cst2 = NPCM7XX_SMB_CST2_INIT_VAL;
|
|
s->cst3 = NPCM7XX_SMB_CST3_INIT_VAL;
|
|
s->ctl1 = NPCM7XX_SMB_CTL1_INIT_VAL;
|
|
s->ctl2 = NPCM7XX_SMB_CTL2_INIT_VAL;
|
|
s->ctl3 = NPCM7XX_SMB_CTL3_INIT_VAL;
|
|
s->ctl4 = NPCM7XX_SMB_CTL4_INIT_VAL;
|
|
s->ctl5 = NPCM7XX_SMB_CTL5_INIT_VAL;
|
|
|
|
for (int i = 0; i < NPCM7XX_SMBUS_NR_ADDRS; ++i) {
|
|
s->addr[i] = NPCM7XX_SMB_ADDR_INIT_VAL;
|
|
}
|
|
s->scllt = NPCM7XX_SMB_SCLLT_INIT_VAL;
|
|
s->sclht = NPCM7XX_SMB_SCLHT_INIT_VAL;
|
|
|
|
s->fif_ctl = NPCM7XX_SMB_FIF_CTL_INIT_VAL;
|
|
s->fif_cts = NPCM7XX_SMB_FIF_CTS_INIT_VAL;
|
|
s->fair_per = NPCM7XX_SMB_FAIR_PER_INIT_VAL;
|
|
s->txf_ctl = NPCM7XX_SMB_TXF_CTL_INIT_VAL;
|
|
s->t_out = NPCM7XX_SMB_T_OUT_INIT_VAL;
|
|
s->txf_sts = NPCM7XX_SMB_TXF_STS_INIT_VAL;
|
|
s->rxf_sts = NPCM7XX_SMB_RXF_STS_INIT_VAL;
|
|
s->rxf_ctl = NPCM7XX_SMB_RXF_CTL_INIT_VAL;
|
|
|
|
npcm7xx_smbus_clear_buffer(s);
|
|
s->status = NPCM7XX_SMBUS_STATUS_IDLE;
|
|
s->rx_cur = 0;
|
|
}
|
|
|
|
static void npcm7xx_smbus_hold_reset(Object *obj, ResetType type)
|
|
{
|
|
NPCM7xxSMBusState *s = NPCM7XX_SMBUS(obj);
|
|
|
|
qemu_irq_lower(s->irq);
|
|
}
|
|
|
|
static void npcm7xx_smbus_init(Object *obj)
|
|
{
|
|
NPCM7xxSMBusState *s = NPCM7XX_SMBUS(obj);
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
|
|
|
sysbus_init_irq(sbd, &s->irq);
|
|
memory_region_init_io(&s->iomem, obj, &npcm7xx_smbus_ops, s,
|
|
"regs", 4 * KiB);
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
|
|
s->bus = i2c_init_bus(DEVICE(s), "i2c-bus");
|
|
}
|
|
|
|
static const VMStateDescription vmstate_npcm7xx_smbus = {
|
|
.name = "npcm7xx-smbus",
|
|
.version_id = 0,
|
|
.minimum_version_id = 0,
|
|
.fields = (const VMStateField[]) {
|
|
VMSTATE_UINT8(sda, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(st, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(cst, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(cst2, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(cst3, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(ctl1, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(ctl2, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(ctl3, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(ctl4, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(ctl5, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8_ARRAY(addr, NPCM7xxSMBusState, NPCM7XX_SMBUS_NR_ADDRS),
|
|
VMSTATE_UINT8(scllt, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(sclht, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(fif_ctl, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(fif_cts, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(fair_per, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(txf_ctl, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(t_out, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(txf_sts, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(rxf_sts, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8(rxf_ctl, NPCM7xxSMBusState),
|
|
VMSTATE_UINT8_ARRAY(rx_fifo, NPCM7xxSMBusState,
|
|
NPCM7XX_SMBUS_FIFO_SIZE),
|
|
VMSTATE_UINT8(rx_cur, NPCM7xxSMBusState),
|
|
VMSTATE_END_OF_LIST(),
|
|
},
|
|
};
|
|
|
|
static void npcm7xx_smbus_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
dc->desc = "NPCM7xx System Management Bus";
|
|
dc->vmsd = &vmstate_npcm7xx_smbus;
|
|
rc->phases.enter = npcm7xx_smbus_enter_reset;
|
|
rc->phases.hold = npcm7xx_smbus_hold_reset;
|
|
}
|
|
|
|
static const TypeInfo npcm7xx_smbus_types[] = {
|
|
{
|
|
.name = TYPE_NPCM7XX_SMBUS,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(NPCM7xxSMBusState),
|
|
.class_init = npcm7xx_smbus_class_init,
|
|
.instance_init = npcm7xx_smbus_init,
|
|
},
|
|
};
|
|
DEFINE_TYPES(npcm7xx_smbus_types);
|