diff options
Diffstat (limited to 'include/hw/i2c')
-rw-r--r-- | include/hw/i2c/arm_sbcon_i2c.h | 37 | ||||
-rw-r--r-- | include/hw/i2c/aspeed_i2c.h | 98 | ||||
-rw-r--r-- | include/hw/i2c/bitbang_i2c.h | 50 | ||||
-rw-r--r-- | include/hw/i2c/i2c.h | 194 | ||||
-rw-r--r-- | include/hw/i2c/i2c_mux_pca954x.h | 19 | ||||
-rw-r--r-- | include/hw/i2c/imx_i2c.h | 88 | ||||
-rw-r--r-- | include/hw/i2c/microbit_i2c.h | 42 | ||||
-rw-r--r-- | include/hw/i2c/npcm7xx_smbus.h | 113 | ||||
-rw-r--r-- | include/hw/i2c/pm_smbus.h | 56 | ||||
-rw-r--r-- | include/hw/i2c/pmbus_device.h | 517 | ||||
-rw-r--r-- | include/hw/i2c/ppc4xx_i2c.h | 63 | ||||
-rw-r--r-- | include/hw/i2c/smbus_eeprom.h | 36 | ||||
-rw-r--r-- | include/hw/i2c/smbus_master.h | 55 | ||||
-rw-r--r-- | include/hw/i2c/smbus_slave.h | 95 |
14 files changed, 1463 insertions, 0 deletions
diff --git a/include/hw/i2c/arm_sbcon_i2c.h b/include/hw/i2c/arm_sbcon_i2c.h new file mode 100644 index 000000000..ad96781e7 --- /dev/null +++ b/include/hw/i2c/arm_sbcon_i2c.h @@ -0,0 +1,37 @@ +/* + * ARM SBCon two-wire serial bus interface (I2C bitbang) + * a.k.a. + * ARM Versatile I2C controller + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com> + * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_I2C_ARM_SBCON_H +#define HW_I2C_ARM_SBCON_H + +#include "hw/sysbus.h" +#include "hw/i2c/bitbang_i2c.h" +#include "qom/object.h" + +#define TYPE_VERSATILE_I2C "versatile_i2c" +#define TYPE_ARM_SBCON_I2C TYPE_VERSATILE_I2C + +typedef struct ArmSbconI2CState ArmSbconI2CState; +DECLARE_INSTANCE_CHECKER(ArmSbconI2CState, ARM_SBCON_I2C, + TYPE_ARM_SBCON_I2C) + +struct ArmSbconI2CState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + bitbang_i2c_interface bitbang; + int out; + int in; +}; + +#endif /* HW_I2C_ARM_SBCON_H */ diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h new file mode 100644 index 000000000..4b9be0927 --- /dev/null +++ b/include/hw/i2c/aspeed_i2c.h @@ -0,0 +1,98 @@ +/* + * ASPEED AST2400 I2C Controller + * + * Copyright (C) 2016 IBM Corp. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef ASPEED_I2C_H +#define ASPEED_I2C_H + +#include "hw/i2c/i2c.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_ASPEED_I2C "aspeed.i2c" +#define TYPE_ASPEED_2400_I2C TYPE_ASPEED_I2C "-ast2400" +#define TYPE_ASPEED_2500_I2C TYPE_ASPEED_I2C "-ast2500" +#define TYPE_ASPEED_2600_I2C TYPE_ASPEED_I2C "-ast2600" +OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C) + +#define ASPEED_I2C_NR_BUSSES 16 +#define ASPEED_I2C_MAX_POOL_SIZE 0x800 + +struct AspeedI2CState; + +#define TYPE_ASPEED_I2C_BUS "aspeed.i2c.bus" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedI2CBus, ASPEED_I2C_BUS) +struct AspeedI2CBus { + SysBusDevice parent_obj; + + struct AspeedI2CState *controller; + + MemoryRegion mr; + + I2CBus *bus; + uint8_t id; + qemu_irq irq; + + uint32_t ctrl; + uint32_t timing[2]; + uint32_t intr_ctrl; + uint32_t intr_status; + uint32_t cmd; + uint32_t buf; + uint32_t pool_ctrl; + uint32_t dma_addr; + uint32_t dma_len; +}; + +struct AspeedI2CState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + uint32_t intr_status; + uint32_t ctrl_global; + MemoryRegion pool_iomem; + uint8_t pool[ASPEED_I2C_MAX_POOL_SIZE]; + + AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES]; + MemoryRegion *dram_mr; + AddressSpace dram_as; +}; + + +struct AspeedI2CClass { + SysBusDeviceClass parent_class; + + uint8_t num_busses; + uint8_t reg_size; + uint8_t gap; + qemu_irq (*bus_get_irq)(AspeedI2CBus *); + + uint64_t pool_size; + hwaddr pool_base; + uint8_t *(*bus_pool_base)(AspeedI2CBus *); + bool check_sram; + bool has_dma; + +}; + +I2CBus *aspeed_i2c_get_bus(AspeedI2CState *s, int busnr); + +#endif /* ASPEED_I2C_H */ diff --git a/include/hw/i2c/bitbang_i2c.h b/include/hw/i2c/bitbang_i2c.h new file mode 100644 index 000000000..92334e901 --- /dev/null +++ b/include/hw/i2c/bitbang_i2c.h @@ -0,0 +1,50 @@ +#ifndef BITBANG_I2C_H +#define BITBANG_I2C_H + +#include "hw/i2c/i2c.h" + +typedef struct bitbang_i2c_interface bitbang_i2c_interface; + +#define BITBANG_I2C_SDA 0 +#define BITBANG_I2C_SCL 1 + +typedef enum bitbang_i2c_state { + STOPPED = 0, + SENDING_BIT7, + SENDING_BIT6, + SENDING_BIT5, + SENDING_BIT4, + SENDING_BIT3, + SENDING_BIT2, + SENDING_BIT1, + SENDING_BIT0, + WAITING_FOR_ACK, + RECEIVING_BIT7, + RECEIVING_BIT6, + RECEIVING_BIT5, + RECEIVING_BIT4, + RECEIVING_BIT3, + RECEIVING_BIT2, + RECEIVING_BIT1, + RECEIVING_BIT0, + SENDING_ACK, + SENT_NACK +} bitbang_i2c_state; + +struct bitbang_i2c_interface { + I2CBus *bus; + bitbang_i2c_state state; + int last_data; + int last_clock; + int device_out; + uint8_t buffer; + int current_addr; +}; + +/** + * bitbang_i2c_init: in-place initialize the bitbang_i2c_interface struct + */ +void bitbang_i2c_init(bitbang_i2c_interface *s, I2CBus *bus); +int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level); + +#endif diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h new file mode 100644 index 000000000..5ca3b708c --- /dev/null +++ b/include/hw/i2c/i2c.h @@ -0,0 +1,194 @@ +#ifndef QEMU_I2C_H +#define QEMU_I2C_H + +#include "hw/qdev-core.h" +#include "qom/object.h" + +/* The QEMU I2C implementation only supports simple transfers that complete + immediately. It does not support slave devices that need to be able to + defer their response (eg. CPU slave interfaces where the data is supplied + by the device driver in response to an interrupt). */ + +enum i2c_event { + I2C_START_RECV, + I2C_START_SEND, + I2C_FINISH, + I2C_NACK /* Masker NACKed a receive byte. */ +}; + +typedef struct I2CNodeList I2CNodeList; + +#define TYPE_I2C_SLAVE "i2c-slave" +OBJECT_DECLARE_TYPE(I2CSlave, I2CSlaveClass, + I2C_SLAVE) + +struct I2CSlaveClass { + DeviceClass parent_class; + + /* Master to slave. Returns non-zero for a NAK, 0 for success. */ + int (*send)(I2CSlave *s, uint8_t data); + + /* + * Slave to master. This cannot fail, the device should always + * return something here. + */ + uint8_t (*recv)(I2CSlave *s); + + /* + * Notify the slave of a bus state change. For start event, + * returns non-zero to NAK an operation. For other events the + * return code is not used and should be zero. + */ + int (*event)(I2CSlave *s, enum i2c_event event); + + /* + * Check if this device matches the address provided. Returns bool of + * true if it matches (or broadcast), and updates the device list, false + * otherwise. + * + * If broadcast is true, match should add the device and return true. + */ + bool (*match_and_add)(I2CSlave *candidate, uint8_t address, bool broadcast, + I2CNodeList *current_devs); +}; + +struct I2CSlave { + DeviceState qdev; + + /* Remaining fields for internal use by the I2C code. */ + uint8_t address; +}; + +#define TYPE_I2C_BUS "i2c-bus" +OBJECT_DECLARE_SIMPLE_TYPE(I2CBus, I2C_BUS) + +typedef struct I2CNode I2CNode; + +struct I2CNode { + I2CSlave *elt; + QLIST_ENTRY(I2CNode) next; +}; + +typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; + +struct I2CBus { + BusState qbus; + I2CNodeList current_devs; + uint8_t saved_address; + bool broadcast; +}; + +I2CBus *i2c_init_bus(DeviceState *parent, const char *name); +int i2c_bus_busy(I2CBus *bus); + +/** + * i2c_start_transfer: start a transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * @is_recv: indicates the transfer direction + * + * When @is_recv is a known boolean constant, use the + * i2c_start_recv() or i2c_start_send() helper instead. + * + * Returns: 0 on success, -1 on error + */ +int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv); + +/** + * i2c_start_recv: start a 'receive' transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * + * Returns: 0 on success, -1 on error + */ +int i2c_start_recv(I2CBus *bus, uint8_t address); + +/** + * i2c_start_send: start a 'send' transfer on an I2C bus. + * + * @bus: #I2CBus to be used + * @address: address of the slave + * + * Returns: 0 on success, -1 on error + */ +int i2c_start_send(I2CBus *bus, uint8_t address); + +void i2c_end_transfer(I2CBus *bus); +void i2c_nack(I2CBus *bus); +int i2c_send(I2CBus *bus, uint8_t data); +uint8_t i2c_recv(I2CBus *bus); +bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast, + I2CNodeList *current_devs); + +/** + * Create an I2C slave device on the heap. + * @name: a device type name + * @addr: I2C address of the slave when put on a bus + * + * This only initializes the device state structure and allows + * properties to be set. Type @name must exist. The device still + * needs to be realized. See qdev-core.h. + */ +I2CSlave *i2c_slave_new(const char *name, uint8_t addr); + +/** + * Create and realize an I2C slave device on the heap. + * @bus: I2C bus to put it on + * @name: I2C slave device type name + * @addr: I2C address of the slave when put on a bus + * + * Create the device state structure, initialize it, put it on the + * specified @bus, and drop the reference to it (the device is realized). + */ +I2CSlave *i2c_slave_create_simple(I2CBus *bus, const char *name, uint8_t addr); + +/** + * Realize and drop a reference an I2C slave device + * @dev: I2C slave device to realize + * @bus: I2C bus to put it on + * @addr: I2C address of the slave on the bus + * @errp: pointer to NULL initialized error object + * + * Returns: %true on success, %false on failure. + * + * Call 'realize' on @dev, put it on the specified @bus, and drop the + * reference to it. + * + * This function is useful if you have created @dev via qdev_new(), + * i2c_slave_new() or i2c_slave_try_new() (which take a reference to + * the device it returns to you), so that you can set properties on it + * before realizing it. If you don't need to set properties then + * i2c_slave_create_simple() is probably better (as it does the create, + * init and realize in one step). + * + * If you are embedding the I2C slave into another QOM device and + * initialized it via some variant on object_initialize_child() then + * do not use this function, because that family of functions arrange + * for the only reference to the child device to be held by the parent + * via the child<> property, and so the reference-count-drop done here + * would be incorrect. (Instead you would want i2c_slave_realize(), + * which doesn't currently exist but would be trivial to create if we + * had any code that wanted it.) + */ +bool i2c_slave_realize_and_unref(I2CSlave *dev, I2CBus *bus, Error **errp); + +/** + * Set the I2C bus address of a slave device + * @dev: I2C slave device + * @address: I2C address of the slave when put on a bus + */ +void i2c_slave_set_address(I2CSlave *dev, uint8_t address); + +extern const VMStateDescription vmstate_i2c_slave; + +#define VMSTATE_I2C_SLAVE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(I2CSlave), \ + .vmsd = &vmstate_i2c_slave, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, I2CSlave), \ +} + +#endif diff --git a/include/hw/i2c/i2c_mux_pca954x.h b/include/hw/i2c/i2c_mux_pca954x.h new file mode 100644 index 000000000..8aaf9bbc3 --- /dev/null +++ b/include/hw/i2c/i2c_mux_pca954x.h @@ -0,0 +1,19 @@ +#ifndef QEMU_I2C_MUX_PCA954X +#define QEMU_I2C_MUX_PCA954X + +#include "hw/i2c/i2c.h" + +#define TYPE_PCA9546 "pca9546" +#define TYPE_PCA9548 "pca9548" + +/** + * Retrieves the i2c bus associated with the specified channel on this i2c + * mux. + * @mux: an i2c mux device. + * @channel: the i2c channel requested + * + * Returns: a pointer to the associated i2c bus. + */ +I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel); + +#endif diff --git a/include/hw/i2c/imx_i2c.h b/include/hw/i2c/imx_i2c.h new file mode 100644 index 000000000..e4f91339f --- /dev/null +++ b/include/hw/i2c/imx_i2c.h @@ -0,0 +1,88 @@ +/* + * i.MX I2C Bus Serial Interface registers definition + * + * Copyright (C) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef IMX_I2C_H +#define IMX_I2C_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_IMX_I2C "imx.i2c" +OBJECT_DECLARE_SIMPLE_TYPE(IMXI2CState, IMX_I2C) + +#define IMX_I2C_MEM_SIZE 0x14 + +/* i.MX I2C memory map */ +#define IADR_ADDR 0x00 /* address register */ +#define IFDR_ADDR 0x04 /* frequency divider register */ +#define I2CR_ADDR 0x08 /* control register */ +#define I2SR_ADDR 0x0c /* status register */ +#define I2DR_ADDR 0x10 /* data register */ + +#define IADR_MASK 0xFE +#define IADR_RESET 0 + +#define IFDR_MASK 0x3F +#define IFDR_RESET 0 + +#define I2CR_IEN (1 << 7) +#define I2CR_IIEN (1 << 6) +#define I2CR_MSTA (1 << 5) +#define I2CR_MTX (1 << 4) +#define I2CR_TXAK (1 << 3) +#define I2CR_RSTA (1 << 2) +#define I2CR_MASK 0xFC +#define I2CR_RESET 0 + +#define I2SR_ICF (1 << 7) +#define I2SR_IAAF (1 << 6) +#define I2SR_IBB (1 << 5) +#define I2SR_IAL (1 << 4) +#define I2SR_SRW (1 << 2) +#define I2SR_IIF (1 << 1) +#define I2SR_RXAK (1 << 0) +#define I2SR_MASK 0xE9 +#define I2SR_RESET 0x81 + +#define I2DR_MASK 0xFF +#define I2DR_RESET 0 + +#define ADDR_RESET 0xFF00 + +struct IMXI2CState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + I2CBus *bus; + qemu_irq irq; + + uint16_t address; + + uint16_t iadr; + uint16_t ifdr; + uint16_t i2cr; + uint16_t i2sr; + uint16_t i2dr_read; + uint16_t i2dr_write; +}; + +#endif /* IMX_I2C_H */ diff --git a/include/hw/i2c/microbit_i2c.h b/include/hw/i2c/microbit_i2c.h new file mode 100644 index 000000000..3c29e09bf --- /dev/null +++ b/include/hw/i2c/microbit_i2c.h @@ -0,0 +1,42 @@ +/* + * Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface + * http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf + * + * Copyright 2019 Red Hat, Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef MICROBIT_I2C_H +#define MICROBIT_I2C_H + +#include "hw/sysbus.h" +#include "hw/arm/nrf51.h" +#include "qom/object.h" + +#define NRF51_TWI_TASK_STARTRX 0x000 +#define NRF51_TWI_TASK_STARTTX 0x008 +#define NRF51_TWI_TASK_STOP 0x014 +#define NRF51_TWI_EVENT_STOPPED 0x104 +#define NRF51_TWI_EVENT_RXDREADY 0x108 +#define NRF51_TWI_EVENT_TXDSENT 0x11c +#define NRF51_TWI_REG_ENABLE 0x500 +#define NRF51_TWI_REG_RXD 0x518 +#define NRF51_TWI_REG_TXD 0x51c +#define NRF51_TWI_REG_ADDRESS 0x588 + +#define TYPE_MICROBIT_I2C "microbit.i2c" +OBJECT_DECLARE_SIMPLE_TYPE(MicrobitI2CState, MICROBIT_I2C) + +#define MICROBIT_I2C_NREGS (NRF51_PERIPHERAL_SIZE / sizeof(uint32_t)) + +struct MicrobitI2CState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t regs[MICROBIT_I2C_NREGS]; + uint32_t read_idx; +}; + +#endif /* MICROBIT_I2C_H */ diff --git a/include/hw/i2c/npcm7xx_smbus.h b/include/hw/i2c/npcm7xx_smbus.h new file mode 100644 index 000000000..7d59ee917 --- /dev/null +++ b/include/hw/i2c/npcm7xx_smbus.h @@ -0,0 +1,113 @@ +/* + * 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. + */ +#ifndef NPCM7XX_SMBUS_H +#define NPCM7XX_SMBUS_H + +#include "exec/memory.h" +#include "hw/i2c/i2c.h" +#include "hw/irq.h" +#include "hw/sysbus.h" + +/* + * Number of addresses this module contains. Do not change this without + * incrementing the version_id in the vmstate. + */ +#define NPCM7XX_SMBUS_NR_ADDRS 10 + +/* Size of the FIFO buffer. */ +#define NPCM7XX_SMBUS_FIFO_SIZE 16 + +typedef enum NPCM7xxSMBusStatus { + NPCM7XX_SMBUS_STATUS_IDLE, + NPCM7XX_SMBUS_STATUS_SENDING, + NPCM7XX_SMBUS_STATUS_RECEIVING, + NPCM7XX_SMBUS_STATUS_NEGACK, + NPCM7XX_SMBUS_STATUS_STOPPING_LAST_RECEIVE, + NPCM7XX_SMBUS_STATUS_STOPPING_NEGACK, +} NPCM7xxSMBusStatus; + +/* + * struct NPCM7xxSMBusState - System Management Bus device state. + * @bus: The underlying I2C Bus. + * @irq: GIC interrupt line to fire on events (if enabled). + * @sda: The serial data register. + * @st: The status register. + * @cst: The control status register. + * @cst2: The control status register 2. + * @cst3: The control status register 3. + * @ctl1: The control register 1. + * @ctl2: The control register 2. + * @ctl3: The control register 3. + * @ctl4: The control register 4. + * @ctl5: The control register 5. + * @addr: The SMBus module's own addresses on the I2C bus. + * @scllt: The SCL low time register. + * @sclht: The SCL high time register. + * @fif_ctl: The FIFO control register. + * @fif_cts: The FIFO control status register. + * @fair_per: The fair preriod register. + * @txf_ctl: The transmit FIFO control register. + * @t_out: The SMBus timeout register. + * @txf_sts: The transmit FIFO status register. + * @rxf_sts: The receive FIFO status register. + * @rxf_ctl: The receive FIFO control register. + * @rx_fifo: The FIFO buffer for receiving in FIFO mode. + * @rx_cur: The current position of rx_fifo. + * @status: The current status of the SMBus. + */ +typedef struct NPCM7xxSMBusState { + SysBusDevice parent; + + MemoryRegion iomem; + + I2CBus *bus; + qemu_irq irq; + + uint8_t sda; + uint8_t st; + uint8_t cst; + uint8_t cst2; + uint8_t cst3; + uint8_t ctl1; + uint8_t ctl2; + uint8_t ctl3; + uint8_t ctl4; + uint8_t ctl5; + uint8_t addr[NPCM7XX_SMBUS_NR_ADDRS]; + + uint8_t scllt; + uint8_t sclht; + + uint8_t fif_ctl; + uint8_t fif_cts; + uint8_t fair_per; + uint8_t txf_ctl; + uint8_t t_out; + uint8_t txf_sts; + uint8_t rxf_sts; + uint8_t rxf_ctl; + + uint8_t rx_fifo[NPCM7XX_SMBUS_FIFO_SIZE]; + uint8_t rx_cur; + + NPCM7xxSMBusStatus status; +} NPCM7xxSMBusState; + +#define TYPE_NPCM7XX_SMBUS "npcm7xx-smbus" +#define NPCM7XX_SMBUS(obj) OBJECT_CHECK(NPCM7xxSMBusState, (obj), \ + TYPE_NPCM7XX_SMBUS) + +#endif /* NPCM7XX_SMBUS_H */ diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h new file mode 100644 index 000000000..0d74207ef --- /dev/null +++ b/include/hw/i2c/pm_smbus.h @@ -0,0 +1,56 @@ +#ifndef PM_SMBUS_H +#define PM_SMBUS_H + +#include "exec/memory.h" +#include "hw/i2c/smbus_master.h" + +#define PM_SMBUS_MAX_MSG_SIZE 32 + +typedef struct PMSMBus { + I2CBus *smbus; + MemoryRegion io; + + uint8_t smb_stat; + uint8_t smb_ctl; + uint8_t smb_cmd; + uint8_t smb_addr; + uint8_t smb_data0; + uint8_t smb_data1; + uint8_t smb_data[PM_SMBUS_MAX_MSG_SIZE]; + uint8_t smb_blkdata; + uint8_t smb_auxctl; + uint32_t smb_index; + + /* Set by pm_smbus.c */ + void (*reset)(struct PMSMBus *s); + + /* Set by the user. */ + bool i2c_enable; + void (*set_irq)(struct PMSMBus *s, bool enabled); + void *opaque; + + /* Internally used by pm_smbus. */ + + /* Set on block transfers after the last byte has been read, so the + INTR bit can be set at the right time. */ + bool op_done; + + /* Set during an I2C block read, so we know how to handle data. */ + bool in_i2c_block_read; + + /* Used to work around a bug in AMIBIOS, see smb_transaction_start() */ + bool start_transaction_on_status_read; +} PMSMBus; + +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk); + +/* + * For backwards compatibility on migration, older versions don't have + * working migration for pm_smbus, this lets us ignore the migrations + * for older machine versions. + */ +bool pm_smbus_vmstate_needed(void); + +extern const VMStateDescription pmsmb_vmstate; + +#endif /* PM_SMBUS_H */ diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h new file mode 100644 index 000000000..62bd38c83 --- /dev/null +++ b/include/hw/i2c/pmbus_device.h @@ -0,0 +1,517 @@ +/* + * QEMU PMBus device emulation + * + * Copyright 2021 Google LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PMBUS_DEVICE_H +#define HW_PMBUS_DEVICE_H + +#include "qemu/bitops.h" +#include "hw/i2c/smbus_slave.h" + +enum pmbus_registers { + PMBUS_PAGE = 0x00, /* R/W byte */ + PMBUS_OPERATION = 0x01, /* R/W byte */ + PMBUS_ON_OFF_CONFIG = 0x02, /* R/W byte */ + PMBUS_CLEAR_FAULTS = 0x03, /* Send Byte */ + PMBUS_PHASE = 0x04, /* R/W byte */ + PMBUS_PAGE_PLUS_WRITE = 0x05, /* Block Write-only */ + PMBUS_PAGE_PLUS_READ = 0x06, /* Block Read-only */ + PMBUS_WRITE_PROTECT = 0x10, /* R/W byte */ + PMBUS_STORE_DEFAULT_ALL = 0x11, /* Send Byte */ + PMBUS_RESTORE_DEFAULT_ALL = 0x12, /* Send Byte */ + PMBUS_STORE_DEFAULT_CODE = 0x13, /* Write-only Byte */ + PMBUS_RESTORE_DEFAULT_CODE = 0x14, /* Write-only Byte */ + PMBUS_STORE_USER_ALL = 0x15, /* Send Byte */ + PMBUS_RESTORE_USER_ALL = 0x16, /* Send Byte */ + PMBUS_STORE_USER_CODE = 0x17, /* Write-only Byte */ + PMBUS_RESTORE_USER_CODE = 0x18, /* Write-only Byte */ + PMBUS_CAPABILITY = 0x19, /* Read-Only byte */ + PMBUS_QUERY = 0x1A, /* Write-Only */ + PMBUS_SMBALERT_MASK = 0x1B, /* Block read, Word write */ + PMBUS_VOUT_MODE = 0x20, /* R/W byte */ + PMBUS_VOUT_COMMAND = 0x21, /* R/W word */ + PMBUS_VOUT_TRIM = 0x22, /* R/W word */ + PMBUS_VOUT_CAL_OFFSET = 0x23, /* R/W word */ + PMBUS_VOUT_MAX = 0x24, /* R/W word */ + PMBUS_VOUT_MARGIN_HIGH = 0x25, /* R/W word */ + PMBUS_VOUT_MARGIN_LOW = 0x26, /* R/W word */ + PMBUS_VOUT_TRANSITION_RATE = 0x27, /* R/W word */ + PMBUS_VOUT_DROOP = 0x28, /* R/W word */ + PMBUS_VOUT_SCALE_LOOP = 0x29, /* R/W word */ + PMBUS_VOUT_SCALE_MONITOR = 0x2A, /* R/W word */ + PMBUS_COEFFICIENTS = 0x30, /* Read-only block 5 bytes */ + PMBUS_POUT_MAX = 0x31, /* R/W word */ + PMBUS_MAX_DUTY = 0x32, /* R/W word */ + PMBUS_FREQUENCY_SWITCH = 0x33, /* R/W word */ + PMBUS_VIN_ON = 0x35, /* R/W word */ + PMBUS_VIN_OFF = 0x36, /* R/W word */ + PMBUS_INTERLEAVE = 0x37, /* R/W word */ + PMBUS_IOUT_CAL_GAIN = 0x38, /* R/W word */ + PMBUS_IOUT_CAL_OFFSET = 0x39, /* R/W word */ + PMBUS_FAN_CONFIG_1_2 = 0x3A, /* R/W byte */ + PMBUS_FAN_COMMAND_1 = 0x3B, /* R/W word */ + PMBUS_FAN_COMMAND_2 = 0x3C, /* R/W word */ + PMBUS_FAN_CONFIG_3_4 = 0x3D, /* R/W byte */ + PMBUS_FAN_COMMAND_3 = 0x3E, /* R/W word */ + PMBUS_FAN_COMMAND_4 = 0x3F, /* R/W word */ + PMBUS_VOUT_OV_FAULT_LIMIT = 0x40, /* R/W word */ + PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41, /* R/W byte */ + PMBUS_VOUT_OV_WARN_LIMIT = 0x42, /* R/W word */ + PMBUS_VOUT_UV_WARN_LIMIT = 0x43, /* R/W word */ + PMBUS_VOUT_UV_FAULT_LIMIT = 0x44, /* R/W word */ + PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45, /* R/W byte */ + PMBUS_IOUT_OC_FAULT_LIMIT = 0x46, /* R/W word */ + PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47, /* R/W byte */ + PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48, /* R/W word */ + PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49, /* R/W byte */ + PMBUS_IOUT_OC_WARN_LIMIT = 0x4A, /* R/W word */ + PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B, /* R/W word */ + PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C, /* R/W byte */ + PMBUS_OT_FAULT_LIMIT = 0x4F, /* R/W word */ + PMBUS_OT_FAULT_RESPONSE = 0x50, /* R/W byte */ + PMBUS_OT_WARN_LIMIT = 0x51, /* R/W word */ + PMBUS_UT_WARN_LIMIT = 0x52, /* R/W word */ + PMBUS_UT_FAULT_LIMIT = 0x53, /* R/W word */ + PMBUS_UT_FAULT_RESPONSE = 0x54, /* R/W byte */ + PMBUS_VIN_OV_FAULT_LIMIT = 0x55, /* R/W word */ + PMBUS_VIN_OV_FAULT_RESPONSE = 0x56, /* R/W byte */ + PMBUS_VIN_OV_WARN_LIMIT = 0x57, /* R/W word */ + PMBUS_VIN_UV_WARN_LIMIT = 0x58, /* R/W word */ + PMBUS_VIN_UV_FAULT_LIMIT = 0x59, /* R/W word */ + PMBUS_VIN_UV_FAULT_RESPONSE = 0x5A, /* R/W byte */ + PMBUS_IIN_OC_FAULT_LIMIT = 0x5B, /* R/W word */ + PMBUS_IIN_OC_FAULT_RESPONSE = 0x5C, /* R/W byte */ + PMBUS_IIN_OC_WARN_LIMIT = 0x5D, /* R/W word */ + PMBUS_POWER_GOOD_ON = 0x5E, /* R/W word */ + PMBUS_POWER_GOOD_OFF = 0x5F, /* R/W word */ + PMBUS_TON_DELAY = 0x60, /* R/W word */ + PMBUS_TON_RISE = 0x61, /* R/W word */ + PMBUS_TON_MAX_FAULT_LIMIT = 0x62, /* R/W word */ + PMBUS_TON_MAX_FAULT_RESPONSE = 0x63, /* R/W byte */ + PMBUS_TOFF_DELAY = 0x64, /* R/W word */ + PMBUS_TOFF_FALL = 0x65, /* R/W word */ + PMBUS_TOFF_MAX_WARN_LIMIT = 0x66, /* R/W word */ + PMBUS_POUT_OP_FAULT_LIMIT = 0x68, /* R/W word */ + PMBUS_POUT_OP_FAULT_RESPONSE = 0x69, /* R/W byte */ + PMBUS_POUT_OP_WARN_LIMIT = 0x6A, /* R/W word */ + PMBUS_PIN_OP_WARN_LIMIT = 0x6B, /* R/W word */ + PMBUS_STATUS_BYTE = 0x78, /* R/W byte */ + PMBUS_STATUS_WORD = 0x79, /* R/W word */ + PMBUS_STATUS_VOUT = 0x7A, /* R/W byte */ + PMBUS_STATUS_IOUT = 0x7B, /* R/W byte */ + PMBUS_STATUS_INPUT = 0x7C, /* R/W byte */ + PMBUS_STATUS_TEMPERATURE = 0x7D, /* R/W byte */ + PMBUS_STATUS_CML = 0x7E, /* R/W byte */ + PMBUS_STATUS_OTHER = 0x7F, /* R/W byte */ + PMBUS_STATUS_MFR_SPECIFIC = 0x80, /* R/W byte */ + PMBUS_STATUS_FANS_1_2 = 0x81, /* R/W byte */ + PMBUS_STATUS_FANS_3_4 = 0x82, /* R/W byte */ + PMBUS_READ_EIN = 0x86, /* Read-Only block 5 bytes */ + PMBUS_READ_EOUT = 0x87, /* Read-Only block 5 bytes */ + PMBUS_READ_VIN = 0x88, /* Read-Only word */ + PMBUS_READ_IIN = 0x89, /* Read-Only word */ + PMBUS_READ_VCAP = 0x8A, /* Read-Only word */ + PMBUS_READ_VOUT = 0x8B, /* Read-Only word */ + PMBUS_READ_IOUT = 0x8C, /* Read-Only word */ + PMBUS_READ_TEMPERATURE_1 = 0x8D, /* Read-Only word */ + PMBUS_READ_TEMPERATURE_2 = 0x8E, /* Read-Only word */ + PMBUS_READ_TEMPERATURE_3 = 0x8F, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_1 = 0x90, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_2 = 0x91, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_3 = 0x92, /* Read-Only word */ + PMBUS_READ_FAN_SPEED_4 = 0x93, /* Read-Only word */ + PMBUS_READ_DUTY_CYCLE = 0x94, /* Read-Only word */ + PMBUS_READ_FREQUENCY = 0x95, /* Read-Only word */ + PMBUS_READ_POUT = 0x96, /* Read-Only word */ + PMBUS_READ_PIN = 0x97, /* Read-Only word */ + PMBUS_REVISION = 0x98, /* Read-Only byte */ + PMBUS_MFR_ID = 0x99, /* R/W block */ + PMBUS_MFR_MODEL = 0x9A, /* R/W block */ + PMBUS_MFR_REVISION = 0x9B, /* R/W block */ + PMBUS_MFR_LOCATION = 0x9C, /* R/W block */ + PMBUS_MFR_DATE = 0x9D, /* R/W block */ + PMBUS_MFR_SERIAL = 0x9E, /* R/W block */ + PMBUS_APP_PROFILE_SUPPORT = 0x9F, /* Read-Only block-read */ + PMBUS_MFR_VIN_MIN = 0xA0, /* Read-Only word */ + PMBUS_MFR_VIN_MAX = 0xA1, /* Read-Only word */ + PMBUS_MFR_IIN_MAX = 0xA2, /* Read-Only word */ + PMBUS_MFR_PIN_MAX = 0xA3, /* Read-Only word */ + PMBUS_MFR_VOUT_MIN = 0xA4, /* Read-Only word */ + PMBUS_MFR_VOUT_MAX = 0xA5, /* Read-Only word */ + PMBUS_MFR_IOUT_MAX = 0xA6, /* Read-Only word */ + PMBUS_MFR_POUT_MAX = 0xA7, /* Read-Only word */ + PMBUS_MFR_TAMBIENT_MAX = 0xA8, /* Read-Only word */ + PMBUS_MFR_TAMBIENT_MIN = 0xA9, /* Read-Only word */ + PMBUS_MFR_EFFICIENCY_LL = 0xAA, /* Read-Only block 14 bytes */ + PMBUS_MFR_EFFICIENCY_HL = 0xAB, /* Read-Only block 14 bytes */ + PMBUS_MFR_PIN_ACCURACY = 0xAC, /* Read-Only byte */ + PMBUS_IC_DEVICE_ID = 0xAD, /* Read-Only block-read */ + PMBUS_IC_DEVICE_REV = 0xAE, /* Read-Only block-read */ + PMBUS_MFR_MAX_TEMP_1 = 0xC0, /* R/W word */ + PMBUS_MFR_MAX_TEMP_2 = 0xC1, /* R/W word */ + PMBUS_MFR_MAX_TEMP_3 = 0xC2, /* R/W word */ +}; + +/* STATUS_WORD */ +#define PB_STATUS_VOUT BIT(15) +#define PB_STATUS_IOUT_POUT BIT(14) +#define PB_STATUS_INPUT BIT(13) +#define PB_STATUS_WORD_MFR BIT(12) +#define PB_STATUS_POWER_GOOD_N BIT(11) +#define PB_STATUS_FAN BIT(10) +#define PB_STATUS_OTHER BIT(9) +#define PB_STATUS_UNKNOWN BIT(8) +/* STATUS_BYTE */ +#define PB_STATUS_BUSY BIT(7) +#define PB_STATUS_OFF BIT(6) +#define PB_STATUS_VOUT_OV BIT(5) +#define PB_STATUS_IOUT_OC BIT(4) +#define PB_STATUS_VIN_UV BIT(3) +#define PB_STATUS_TEMPERATURE BIT(2) +#define PB_STATUS_CML BIT(1) +#define PB_STATUS_NONE_ABOVE BIT(0) + +/* STATUS_VOUT */ +#define PB_STATUS_VOUT_OV_FAULT BIT(7) /* Output Overvoltage Fault */ +#define PB_STATUS_VOUT_OV_WARN BIT(6) /* Output Overvoltage Warning */ +#define PB_STATUS_VOUT_UV_WARN BIT(5) /* Output Undervoltage Warning */ +#define PB_STATUS_VOUT_UV_FAULT BIT(4) /* Output Undervoltage Fault */ +#define PB_STATUS_VOUT_MAX BIT(3) +#define PB_STATUS_VOUT_TON_MAX_FAULT BIT(2) +#define PB_STATUS_VOUT_TOFF_MAX_WARN BIT(1) + +/* STATUS_IOUT */ +#define PB_STATUS_IOUT_OC_FAULT BIT(7) /* Output Overcurrent Fault */ +#define PB_STATUS_IOUT_OC_LV_FAULT BIT(6) /* Output OC And Low Voltage Fault */ +#define PB_STATUS_IOUT_OC_WARN BIT(5) /* Output Overcurrent Warning */ +#define PB_STATUS_IOUT_UC_FAULT BIT(4) /* Output Undercurrent Fault */ +#define PB_STATUS_CURR_SHARE BIT(3) /* Current Share Fault */ +#define PB_STATUS_PWR_LIM_MODE BIT(2) /* In Power Limiting Mode */ +#define PB_STATUS_POUT_OP_FAULT BIT(1) /* Output Overpower Fault */ +#define PB_STATUS_POUT_OP_WARN BIT(0) /* Output Overpower Warning */ + +/* STATUS_INPUT */ +#define PB_STATUS_INPUT_VIN_OV_FAULT BIT(7) /* Input Overvoltage Fault */ +#define PB_STATUS_INPUT_VIN_OV_WARN BIT(6) /* Input Overvoltage Warning */ +#define PB_STATUS_INPUT_VIN_UV_WARN BIT(5) /* Input Undervoltage Warning */ +#define PB_STATUS_INPUT_VIN_UV_FAULT BIT(4) /* Input Undervoltage Fault */ +#define PB_STATUS_INPUT_IIN_OC_FAULT BIT(2) /* Input Overcurrent Fault */ +#define PB_STATUS_INPUT_IIN_OC_WARN BIT(1) /* Input Overcurrent Warning */ +#define PB_STATUS_INPUT_PIN_OP_WARN BIT(0) /* Input Overpower Warning */ + +/* STATUS_TEMPERATURE */ +#define PB_STATUS_OT_FAULT BIT(7) /* Overtemperature Fault */ +#define PB_STATUS_OT_WARN BIT(6) /* Overtemperature Warning */ +#define PB_STATUS_UT_WARN BIT(5) /* Undertemperature Warning */ +#define PB_STATUS_UT_FAULT BIT(4) /* Undertemperature Fault */ + +/* STATUS_CML */ +#define PB_CML_FAULT_INVALID_CMD BIT(7) /* Invalid/Unsupported Command */ +#define PB_CML_FAULT_INVALID_DATA BIT(6) /* Invalid/Unsupported Data */ +#define PB_CML_FAULT_PEC BIT(5) /* Packet Error Check Failed */ +#define PB_CML_FAULT_MEMORY BIT(4) /* Memory Fault Detected */ +#define PB_CML_FAULT_PROCESSOR BIT(3) /* Processor Fault Detected */ +#define PB_CML_FAULT_OTHER_COMM BIT(1) /* Other communication fault */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0) /* Other Memory Or Logic Fault */ + +/* OPERATION*/ +#define PB_OP_ON BIT(7) /* PSU is switched on */ +#define PB_OP_MARGIN_HIGH BIT(5) /* PSU vout is set to margin high */ +#define PB_OP_MARGIN_LOW BIT(4) /* PSU vout is set to margin low */ + +/* PAGES */ +#define PB_MAX_PAGES 0x1F +#define PB_ALL_PAGES 0xFF + +#define TYPE_PMBUS_DEVICE "pmbus-device" +OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass, + PMBUS_DEVICE) + +/* flags */ +#define PB_HAS_COEFFICIENTS BIT_ULL(9) +#define PB_HAS_VIN BIT_ULL(10) +#define PB_HAS_VOUT BIT_ULL(11) +#define PB_HAS_VOUT_MARGIN BIT_ULL(12) +#define PB_HAS_VIN_RATING BIT_ULL(13) +#define PB_HAS_VOUT_RATING BIT_ULL(14) +#define PB_HAS_VOUT_MODE BIT_ULL(15) +#define PB_HAS_IOUT BIT_ULL(21) +#define PB_HAS_IIN BIT_ULL(22) +#define PB_HAS_IOUT_RATING BIT_ULL(23) +#define PB_HAS_IIN_RATING BIT_ULL(24) +#define PB_HAS_IOUT_GAIN BIT_ULL(25) +#define PB_HAS_POUT BIT_ULL(30) +#define PB_HAS_PIN BIT_ULL(31) +#define PB_HAS_EIN BIT_ULL(32) +#define PB_HAS_EOUT BIT_ULL(33) +#define PB_HAS_POUT_RATING BIT_ULL(34) +#define PB_HAS_PIN_RATING BIT_ULL(35) +#define PB_HAS_TEMPERATURE BIT_ULL(40) +#define PB_HAS_TEMP2 BIT_ULL(41) +#define PB_HAS_TEMP3 BIT_ULL(42) +#define PB_HAS_TEMP_RATING BIT_ULL(43) +#define PB_HAS_MFR_INFO BIT_ULL(50) + +struct PMBusDeviceClass { + SMBusDeviceClass parent_class; + uint8_t device_num_pages; + + /** + * Implement quick_cmd, receive byte, and write_data to support non-standard + * PMBus functionality + */ + void (*quick_cmd)(PMBusDevice *dev, uint8_t read); + int (*write_data)(PMBusDevice *dev, const uint8_t *buf, uint8_t len); + uint8_t (*receive_byte)(PMBusDevice *dev); +}; + +/* + * According to the spec, each page may offer the full range of PMBus commands + * available for each output or non-PMBus device. + * Therefore, we can't assume that any registers will always be the same across + * all pages. + * The page 0xFF is intended for writes to all pages + */ +typedef struct PMBusPage { + uint64_t page_flags; + + uint8_t page; /* R/W byte */ + uint8_t operation; /* R/W byte */ + uint8_t on_off_config; /* R/W byte */ + uint8_t write_protect; /* R/W byte */ + uint8_t phase; /* R/W byte */ + uint8_t vout_mode; /* R/W byte */ + uint16_t vout_command; /* R/W word */ + uint16_t vout_trim; /* R/W word */ + uint16_t vout_cal_offset; /* R/W word */ + uint16_t vout_max; /* R/W word */ + uint16_t vout_margin_high; /* R/W word */ + uint16_t vout_margin_low; /* R/W word */ + uint16_t vout_transition_rate; /* R/W word */ + uint16_t vout_droop; /* R/W word */ + uint16_t vout_scale_loop; /* R/W word */ + uint16_t vout_scale_monitor; /* R/W word */ + uint8_t coefficients[5]; /* Read-only block 5 bytes */ + uint16_t pout_max; /* R/W word */ + uint16_t max_duty; /* R/W word */ + uint16_t frequency_switch; /* R/W word */ + uint16_t vin_on; /* R/W word */ + uint16_t vin_off; /* R/W word */ + uint16_t iout_cal_gain; /* R/W word */ + uint16_t iout_cal_offset; /* R/W word */ + uint8_t fan_config_1_2; /* R/W byte */ + uint16_t fan_command_1; /* R/W word */ + uint16_t fan_command_2; /* R/W word */ + uint8_t fan_config_3_4; /* R/W byte */ + uint16_t fan_command_3; /* R/W word */ + uint16_t fan_command_4; /* R/W word */ + uint16_t vout_ov_fault_limit; /* R/W word */ + uint8_t vout_ov_fault_response; /* R/W byte */ + uint16_t vout_ov_warn_limit; /* R/W word */ + uint16_t vout_uv_warn_limit; /* R/W word */ + uint16_t vout_uv_fault_limit; /* R/W word */ + uint8_t vout_uv_fault_response; /* R/W byte */ + uint16_t iout_oc_fault_limit; /* R/W word */ + uint8_t iout_oc_fault_response; /* R/W byte */ + uint16_t iout_oc_lv_fault_limit; /* R/W word */ + uint8_t iout_oc_lv_fault_response; /* R/W byte */ + uint16_t iout_oc_warn_limit; /* R/W word */ + uint16_t iout_uc_fault_limit; /* R/W word */ + uint8_t iout_uc_fault_response; /* R/W byte */ + uint16_t ot_fault_limit; /* R/W word */ + uint8_t ot_fault_response; /* R/W byte */ + uint16_t ot_warn_limit; /* R/W word */ + uint16_t ut_warn_limit; /* R/W word */ + uint16_t ut_fault_limit; /* R/W word */ + uint8_t ut_fault_response; /* R/W byte */ + uint16_t vin_ov_fault_limit; /* R/W word */ + uint8_t vin_ov_fault_response; /* R/W byte */ + uint16_t vin_ov_warn_limit; /* R/W word */ + uint16_t vin_uv_warn_limit; /* R/W word */ + uint16_t vin_uv_fault_limit; /* R/W word */ + uint8_t vin_uv_fault_response; /* R/W byte */ + uint16_t iin_oc_fault_limit; /* R/W word */ + uint8_t iin_oc_fault_response; /* R/W byte */ + uint16_t iin_oc_warn_limit; /* R/W word */ + uint16_t power_good_on; /* R/W word */ + uint16_t power_good_off; /* R/W word */ + uint16_t ton_delay; /* R/W word */ + uint16_t ton_rise; /* R/W word */ + uint16_t ton_max_fault_limit; /* R/W word */ + uint8_t ton_max_fault_response; /* R/W byte */ + uint16_t toff_delay; /* R/W word */ + uint16_t toff_fall; /* R/W word */ + uint16_t toff_max_warn_limit; /* R/W word */ + uint16_t pout_op_fault_limit; /* R/W word */ + uint8_t pout_op_fault_response; /* R/W byte */ + uint16_t pout_op_warn_limit; /* R/W word */ + uint16_t pin_op_warn_limit; /* R/W word */ + uint16_t status_word; /* R/W word */ + uint8_t status_vout; /* R/W byte */ + uint8_t status_iout; /* R/W byte */ + uint8_t status_input; /* R/W byte */ + uint8_t status_temperature; /* R/W byte */ + uint8_t status_cml; /* R/W byte */ + uint8_t status_other; /* R/W byte */ + uint8_t status_mfr_specific; /* R/W byte */ + uint8_t status_fans_1_2; /* R/W byte */ + uint8_t status_fans_3_4; /* R/W byte */ + uint8_t read_ein[5]; /* Read-Only block 5 bytes */ + uint8_t read_eout[5]; /* Read-Only block 5 bytes */ + uint16_t read_vin; /* Read-Only word */ + uint16_t read_iin; /* Read-Only word */ + uint16_t read_vcap; /* Read-Only word */ + uint16_t read_vout; /* Read-Only word */ + uint16_t read_iout; /* Read-Only word */ + uint16_t read_temperature_1; /* Read-Only word */ + uint16_t read_temperature_2; /* Read-Only word */ + uint16_t read_temperature_3; /* Read-Only word */ + uint16_t read_fan_speed_1; /* Read-Only word */ + uint16_t read_fan_speed_2; /* Read-Only word */ + uint16_t read_fan_speed_3; /* Read-Only word */ + uint16_t read_fan_speed_4; /* Read-Only word */ + uint16_t read_duty_cycle; /* Read-Only word */ + uint16_t read_frequency; /* Read-Only word */ + uint16_t read_pout; /* Read-Only word */ + uint16_t read_pin; /* Read-Only word */ + uint8_t revision; /* Read-Only byte */ + const char *mfr_id; /* R/W block */ + const char *mfr_model; /* R/W block */ + const char *mfr_revision; /* R/W block */ + const char *mfr_location; /* R/W block */ + const char *mfr_date; /* R/W block */ + const char *mfr_serial; /* R/W block */ + const char *app_profile_support; /* Read-Only block-read */ + uint16_t mfr_vin_min; /* Read-Only word */ + uint16_t mfr_vin_max; /* Read-Only word */ + uint16_t mfr_iin_max; /* Read-Only word */ + uint16_t mfr_pin_max; /* Read-Only word */ + uint16_t mfr_vout_min; /* Read-Only word */ + uint16_t mfr_vout_max; /* Read-Only word */ + uint16_t mfr_iout_max; /* Read-Only word */ + uint16_t mfr_pout_max; /* Read-Only word */ + uint16_t mfr_tambient_max; /* Read-Only word */ + uint16_t mfr_tambient_min; /* Read-Only word */ + uint8_t mfr_efficiency_ll[14]; /* Read-Only block 14 bytes */ + uint8_t mfr_efficiency_hl[14]; /* Read-Only block 14 bytes */ + uint8_t mfr_pin_accuracy; /* Read-Only byte */ + uint16_t mfr_max_temp_1; /* R/W word */ + uint16_t mfr_max_temp_2; /* R/W word */ + uint16_t mfr_max_temp_3; /* R/W word */ +} PMBusPage; + +/* State */ +struct PMBusDevice { + SMBusDevice smb; + + uint8_t num_pages; + uint8_t code; + uint8_t page; + + /* + * PMBus registers are stored in a PMBusPage structure allocated by + * calling pmbus_pages_alloc() + */ + PMBusPage *pages; + uint8_t capability; + + + int32_t in_buf_len; + uint8_t *in_buf; + int32_t out_buf_len; + uint8_t out_buf[SMBUS_DATA_MAX_LEN]; +}; + +/** + * Direct mode coefficients + * @var m - mantissa + * @var b - offset + * @var R - exponent + */ +typedef struct PMBusCoefficients { + int32_t m; /* mantissa */ + int64_t b; /* offset */ + int32_t R; /* exponent */ +} PMBusCoefficients; + +/** + * Convert sensor values to direct mode format + * + * Y = (m * x - b) * 10^R + * + * @return uint32_t + */ +uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value); + +/** + * Convert direct mode formatted data into sensor reading + * + * X = (Y * 10^-R - b) / m + * + * @return uint32_t + */ +uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value); + +/** + * @brief Send a block of data over PMBus + * Assumes that the bytes in the block are already ordered correctly, + * also assumes the length has been prepended to the block if necessary + * | low_byte | ... | high_byte | + * @param state - maintains state of the PMBus device + * @param data - byte array to be sent by device + * @param len - number + */ +void pmbus_send(PMBusDevice *state, const uint8_t *data, uint16_t len); +void pmbus_send8(PMBusDevice *state, uint8_t data); +void pmbus_send16(PMBusDevice *state, uint16_t data); +void pmbus_send32(PMBusDevice *state, uint32_t data); +void pmbus_send64(PMBusDevice *state, uint64_t data); + +/** + * @brief Send a string over PMBus with length prepended. + * Length is calculated using str_len() + */ +void pmbus_send_string(PMBusDevice *state, const char *data); + +/** + * @brief Receive data over PMBus + * These methods help track how much data is being received over PMBus + * Log to GUEST_ERROR if too much or too little is sent. + */ +uint8_t pmbus_receive8(PMBusDevice *pmdev); +uint16_t pmbus_receive16(PMBusDevice *pmdev); +uint32_t pmbus_receive32(PMBusDevice *pmdev); +uint64_t pmbus_receive64(PMBusDevice *pmdev); + +/** + * PMBus page config must be called before any page is first used. + * It will allocate memory for all the pages if needed. + * Passed in flags overwrite existing flags if any. + * @param page_index the page to which the flags are applied, setting page_index + * to 0xFF applies the passed in flags to all pages. + * @param flags + */ +int pmbus_page_config(PMBusDevice *pmdev, uint8_t page_index, uint64_t flags); + +/** + * Update the status registers when sensor values change. + * Useful if modifying sensors through qmp, this way status registers get + * updated + */ +void pmbus_check_limits(PMBusDevice *pmdev); + +extern const VMStateDescription vmstate_pmbus_device; + +#define VMSTATE_PMBUS_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PMBusDevice), \ + .vmsd = &vmstate_pmbus_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PMBusDevice), \ +} + +#endif diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h new file mode 100644 index 000000000..4e882fa3c --- /dev/null +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -0,0 +1,63 @@ +/* + * PPC4xx I2C controller emulation + * + * Copyright (c) 2007 Jocelyn Mayer + * Copyright (c) 2012 François Revol + * Copyright (c) 2016-2018 BALATON Zoltan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PPC4XX_I2C_H +#define PPC4XX_I2C_H + +#include "hw/sysbus.h" +#include "hw/i2c/bitbang_i2c.h" +#include "qom/object.h" + +#define TYPE_PPC4xx_I2C "ppc4xx-i2c" +OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxI2CState, PPC4xx_I2C) + +struct PPC4xxI2CState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + I2CBus *bus; + qemu_irq irq; + MemoryRegion iomem; + bitbang_i2c_interface bitbang; + int mdidx; + uint8_t mdata[4]; + uint8_t lmadr; + uint8_t hmadr; + uint8_t cntl; + uint8_t mdcntl; + uint8_t sts; + uint8_t extsts; + uint8_t lsadr; + uint8_t hsadr; + uint8_t clkdiv; + uint8_t intrmsk; + uint8_t xfrcnt; + uint8_t xtcntlss; + uint8_t directcntl; +}; + +#endif /* PPC4XX_I2C_H */ diff --git a/include/hw/i2c/smbus_eeprom.h b/include/hw/i2c/smbus_eeprom.h new file mode 100644 index 000000000..68b0063ab --- /dev/null +++ b/include/hw/i2c/smbus_eeprom.h @@ -0,0 +1,36 @@ +/* + * QEMU SMBus EEPROM API + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_SMBUS_EEPROM_H +#define HW_SMBUS_EEPROM_H + +#include "exec/cpu-common.h" +#include "hw/i2c/i2c.h" + +void smbus_eeprom_init_one(I2CBus *bus, uint8_t address, uint8_t *eeprom_buf); +void smbus_eeprom_init(I2CBus *bus, int nb_eeprom, + const uint8_t *eeprom_spd, int size); + +enum sdram_type { SDR = 0x4, DDR = 0x7, DDR2 = 0x8 }; +uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t size); + +#endif diff --git a/include/hw/i2c/smbus_master.h b/include/hw/i2c/smbus_master.h new file mode 100644 index 000000000..bb13bc423 --- /dev/null +++ b/include/hw/i2c/smbus_master.h @@ -0,0 +1,55 @@ +/* + * QEMU SMBus host (master) API + * + * Copyright (c) 2007 Arastra, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_SMBUS_MASTER_H +#define HW_SMBUS_MASTER_H + +#include "hw/i2c/i2c.h" + +/* Master device commands. */ +int smbus_quick_command(I2CBus *bus, uint8_t addr, int read); +int smbus_receive_byte(I2CBus *bus, uint8_t addr); +int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data); +int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command); +int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data); +int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command); +int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data); + +/* + * Do a block transfer from an I2C device. If recv_len is set, then the + * first received byte is a length field and is used to know how much data + * to receive. Otherwise receive "len" bytes. If send_cmd is set, send + * the command byte first before receiving the data. + */ +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd); + +/* + * Do a block transfer to an I2C device. If send_len is set, send the + * "len" value before the data. + */ +int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool send_len); + +#endif diff --git a/include/hw/i2c/smbus_slave.h b/include/hw/i2c/smbus_slave.h new file mode 100644 index 000000000..86bfe0a79 --- /dev/null +++ b/include/hw/i2c/smbus_slave.h @@ -0,0 +1,95 @@ +/* + * QEMU SMBus device (slave) API + * + * Copyright (c) 2007 Arastra, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_SMBUS_SLAVE_H +#define HW_SMBUS_SLAVE_H + +#include "hw/i2c/i2c.h" +#include "qom/object.h" + +#define TYPE_SMBUS_DEVICE "smbus-device" +OBJECT_DECLARE_TYPE(SMBusDevice, SMBusDeviceClass, + SMBUS_DEVICE) + + +struct SMBusDeviceClass { + I2CSlaveClass parent_class; + + /* + * An operation with no data, special in SMBus. + * This may be NULL, quick commands are ignore in that case. + */ + void (*quick_cmd)(SMBusDevice *dev, uint8_t read); + + /* + * We can't distinguish between a word write and a block write with + * length 1, so pass the whole data block including the length byte + * (if present). The device is responsible figuring out what type of + * command this is. + * This may be NULL if no data is written to the device. Writes + * will be ignore in that case. + */ + int (*write_data)(SMBusDevice *dev, uint8_t *buf, uint8_t len); + + /* + * Likewise we can't distinguish between different reads, or even know + * the length of the read until the read is complete, so read data a + * byte at a time. The device is responsible for adding the length + * byte on block reads. This call cannot fail, it should return + * something, preferably 0xff if nothing is available. + * This may be NULL if no data is read from the device. Reads will + * return 0xff in that case. + */ + uint8_t (*receive_byte)(SMBusDevice *dev); +}; + +#define SMBUS_DATA_MAX_LEN 34 /* command + len + 32 bytes of data. */ + +struct SMBusDevice { + /* The SMBus protocol is implemented on top of I2C. */ + I2CSlave i2c; + + /* Remaining fields for internal use only. */ + int32_t mode; + int32_t data_len; + uint8_t data_buf[SMBUS_DATA_MAX_LEN]; +}; + +extern const VMStateDescription vmstate_smbus_device; + +#define VMSTATE_SMBUS_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SMBusDevice), \ + .vmsd = &vmstate_smbus_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SMBusDevice), \ +} + +/* + * Users should call this in their .needed functions to know if the + * SMBus slave data needs to be transferred. + */ +bool smbus_vmstate_needed(SMBusDevice *dev); + +#endif |