mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-08 02:03:56 -06:00
Some qemu updates for IPMI and I2C
Move some ADC file to where they belong and move some sensors to a sensor directory, since with new BMCs coming in lots of different sensors should be coming in. Keep from cluttering things up. Add support for I2C PMBus devices. Replace the confusing and error-prone i2c_send_recv and i2c_transfer with specific send and receive functions. Several errors have already been made with these, avoid any new errors. Fix the watchdog_expired field in the IPMI watchdog, it's not a bool, it's a u8. After a vmstate transfer, the new value could be wrong. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAmDoeGAACgkQYfOMkJGb /4HpkQ//YLAddy3Kqo5LbyI0BPxcx567htHH34XXp4Pz22DcAoPvZRyRlsGiAAcR HRmqjSXNBan+KhlA82coczHUami8C+9NQAfnABf7Mv4CQnvf9Hziy9YR0riTAgEk Xfc25eYYK5dzfOHVVbp2KJ2IWPo/sY3UqbIKu9MyYzriNpzIuT5NW1aGjIrhW7Js 2feeIHxH4fEXzhkkgMKMJfXdxkYyD78mB3IfnpVCnoODq3fMZXtMskRDHWjdMEnT 0u53zeQd5T0jElLZXlwDKAd9QpiPWVnfTJwND3fwWjSMIAVkd7kTqJbVtHpYJMWB amOR9qsw72r/WIOow4lLbSjS57GbyEjB344dtlrzHhhW68y9SGbtaSXWD/imkAfB NhLlMzNzsIb+dHZhAwdEgmg4Nz/1HZngiK4Awk2YFBVCn7G6BqAvpMwLGdYYr3Az P4EPs0aVdGYBbcN5XxAwkKEqESrzF+iF7iZvNTocdSJSCV+ebFNLlZWIq/aXS1TA IED1mBmyc/drWF+jA0D520uafT1m+zN4HhPvHpRyK65bqqiH2veDpK20kz0lwQe8 e2ZeDVG2oVTMIQ+mmyu7kD9xgxjmapoHz64K1VkUwsnXE3jp9yqfa17B9vHOjqkq e/iaHiek8xeTWWyoShGk2zoAHBChkwSy/Y2JuZA6FXRnoU6kKsg= =8708 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cminyard/tags/for-qemu-6.1-2' into staging Some qemu updates for IPMI and I2C Move some ADC file to where they belong and move some sensors to a sensor directory, since with new BMCs coming in lots of different sensors should be coming in. Keep from cluttering things up. Add support for I2C PMBus devices. Replace the confusing and error-prone i2c_send_recv and i2c_transfer with specific send and receive functions. Several errors have already been made with these, avoid any new errors. Fix the watchdog_expired field in the IPMI watchdog, it's not a bool, it's a u8. After a vmstate transfer, the new value could be wrong. # gpg: Signature made Fri 09 Jul 2021 17:25:04 BST # gpg: using RSA key FD0D5CE67CE0F59A6688268661F38C90919BFF81 # gpg: Good signature from "Corey Minyard <cminyard@mvista.com>" [unknown] # gpg: aka "Corey Minyard <minyard@acm.org>" [unknown] # gpg: aka "Corey Minyard <corey@minyard.net>" [unknown] # gpg: aka "Corey Minyard <minyard@mvista.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: FD0D 5CE6 7CE0 F59A 6688 2686 61F3 8C90 919B FF81 * remotes/cminyard/tags/for-qemu-6.1-2: (24 commits) tests/qtest: add tests for MAX34451 device model hw/misc: add MAX34451 device tests/qtest: add tests for ADM1272 device model hw/misc: add ADM1272 device hw/i2c: add support for PMBus ipmi/sim: fix watchdog_expired data type error in IPMIBmcSim struct hw/i2c: Introduce i2c_start_recv() and i2c_start_send() hw/i2c: Extract i2c_do_start_transfer() from i2c_start_transfer() hw/i2c: Make i2c_start_transfer() direction argument a boolean hw/i2c: Rename i2c_set_slave_address() -> i2c_slave_set_address() hw/i2c: Remove confusing i2c_send_recv() hw/misc/auxbus: Replace i2c_send_recv() by i2c_recv() & i2c_send() hw/misc/auxbus: Replace 'is_write' boolean by its value hw/misc/auxbus: Explode READ_I2C / WRITE_I2C_MOT cases hw/misc/auxbus: Fix MOT/classic I2C mode hw/i2c/ppc4xx_i2c: Replace i2c_send_recv() by i2c_recv() & i2c_send() hw/i2c/ppc4xx_i2c: Add reference to datasheet hw/display/sm501: Replace i2c_send_recv() by i2c_recv() & i2c_send() hw/display/sm501: Simplify sm501_i2c_write() logic hw/input/lm832x: Define TYPE_LM8323 in public header ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
9516034d05
50 changed files with 4497 additions and 126 deletions
|
@ -32,6 +32,7 @@ source remote/Kconfig
|
|||
source rtc/Kconfig
|
||||
source scsi/Kconfig
|
||||
source sd/Kconfig
|
||||
source sensor/Kconfig
|
||||
source smbios/Kconfig
|
||||
source ssi/Kconfig
|
||||
source timer/Kconfig
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
config STM32F2XX_ADC
|
||||
bool
|
||||
|
||||
config MAX111X
|
||||
bool
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/misc/max111x.h"
|
||||
#include "hw/adc/max111x.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/module.h"
|
|
@ -1,2 +1,4 @@
|
|||
softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c'))
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/misc/zynq-xadc.h"
|
||||
#include "hw/adc/zynq-xadc.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/log.h"
|
|
@ -382,9 +382,12 @@ config XLNX_VERSAL
|
|||
config NPCM7XX
|
||||
bool
|
||||
select A9MPCORE
|
||||
select ADM1272
|
||||
select ARM_GIC
|
||||
select AT24C # EEPROM
|
||||
select MAX34451
|
||||
select PL310 # cache controller
|
||||
select PMBUS
|
||||
select SERIAL
|
||||
select SSI
|
||||
select UNIMP
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "hw/i2c/i2c_mux_pca954x.h"
|
||||
#include "hw/i2c/smbus_eeprom.h"
|
||||
#include "hw/misc/pca9552.h"
|
||||
#include "hw/misc/tmp105.h"
|
||||
#include "hw/sensor/tmp105.h"
|
||||
#include "hw/misc/led.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
|
|
@ -34,9 +34,10 @@
|
|||
#include "hw/boards.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/display/blizzard.h"
|
||||
#include "hw/input/lm832x.h"
|
||||
#include "hw/input/tsc2xxx.h"
|
||||
#include "hw/misc/cbus.h"
|
||||
#include "hw/misc/tmp105.h"
|
||||
#include "hw/sensor/tmp105.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/hw.h"
|
||||
|
@ -416,7 +417,7 @@ static void n810_kbd_setup(struct n800_s *s)
|
|||
/* Attach the LM8322 keyboard to the I2C bus,
|
||||
* should happen in n8x0_i2c_setup and s->kbd be initialised here. */
|
||||
s->kbd = DEVICE(i2c_slave_create_simple(omap_i2c_bus(s->mpu->i2c[0]),
|
||||
"lm8323", N810_LM8323_ADDR));
|
||||
TYPE_LM8323, N810_LM8323_ADDR));
|
||||
qdev_connect_gpio_out(s->kbd, 0, kbd_irq);
|
||||
}
|
||||
|
||||
|
|
|
@ -1437,7 +1437,7 @@ static void pxa2xx_i2c_write(void *opaque, hwaddr addr,
|
|||
break;
|
||||
|
||||
case ISAR:
|
||||
i2c_set_slave_address(I2C_SLAVE(s->slave), value & 0x7f);
|
||||
i2c_slave_set_address(I2C_SLAVE(s->slave), value & 0x7f);
|
||||
break;
|
||||
|
||||
case IDBR:
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include "audio/audio.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/misc/max111x.h"
|
||||
#include "hw/adc/max111x.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "cpu.h"
|
||||
|
@ -769,9 +769,9 @@ static void spitz_wm8750_addr(void *opaque, int line, int level)
|
|||
{
|
||||
I2CSlave *wm = (I2CSlave *) opaque;
|
||||
if (level)
|
||||
i2c_set_slave_address(wm, SPITZ_WM_ADDRH);
|
||||
i2c_slave_set_address(wm, SPITZ_WM_ADDRH);
|
||||
else
|
||||
i2c_set_slave_address(wm, SPITZ_WM_ADDRL);
|
||||
i2c_slave_set_address(wm, SPITZ_WM_ADDRL);
|
||||
}
|
||||
|
||||
static void spitz_i2c_setup(PXA2xxState *cpu)
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "hw/boards.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/misc/zynq-xadc.h"
|
||||
#include "hw/adc/zynq-xadc.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "hw/usb/chipidea.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
|
|
@ -968,7 +968,7 @@ static void ati_vga_realize(PCIDevice *dev, Error **errp)
|
|||
I2CBus *i2cbus = i2c_init_bus(DEVICE(s), "ati-vga.ddc");
|
||||
bitbang_i2c_init(&s->bbi2c, i2cbus);
|
||||
I2CSlave *i2cddc = I2C_SLAVE(qdev_new(TYPE_I2CDDC));
|
||||
i2c_set_slave_address(i2cddc, 0x50);
|
||||
i2c_slave_set_address(i2cddc, 0x50);
|
||||
qdev_realize_and_unref(DEVICE(i2cddc), BUS(i2cbus), &error_abort);
|
||||
|
||||
/* mmio register space */
|
||||
|
|
|
@ -1033,16 +1033,18 @@ static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
case SM501_I2C_CONTROL:
|
||||
if (value & SM501_I2C_CONTROL_ENABLE) {
|
||||
if (value & SM501_I2C_CONTROL_START) {
|
||||
bool is_recv = s->i2c_addr & 1;
|
||||
int res = i2c_start_transfer(s->i2c_bus,
|
||||
s->i2c_addr >> 1,
|
||||
s->i2c_addr & 1);
|
||||
s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0);
|
||||
if (!res) {
|
||||
is_recv);
|
||||
if (res) {
|
||||
s->i2c_status |= SM501_I2C_STATUS_ERROR;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i <= s->i2c_byte_count; i++) {
|
||||
res = i2c_send_recv(s->i2c_bus, &s->i2c_data[i],
|
||||
!(s->i2c_addr & 1));
|
||||
if (res) {
|
||||
if (is_recv) {
|
||||
s->i2c_data[i] = i2c_recv(s->i2c_bus);
|
||||
} else if (i2c_send(s->i2c_bus, s->i2c_data[i]) < 0) {
|
||||
s->i2c_status |= SM501_I2C_STATUS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
@ -1826,7 +1828,7 @@ static void sm501_init(SM501State *s, DeviceState *dev,
|
|||
s->i2c_bus = i2c_init_bus(dev, "sm501.i2c");
|
||||
/* ddc */
|
||||
I2CDDCState *ddc = I2CDDC(qdev_new(TYPE_I2CDDC));
|
||||
i2c_set_slave_address(I2C_SLAVE(ddc), 0x50);
|
||||
i2c_slave_set_address(I2C_SLAVE(ddc), 0x50);
|
||||
qdev_realize_and_unref(DEVICE(ddc), BUS(s->i2c_bus), &error_abort);
|
||||
|
||||
/* mmio */
|
||||
|
|
|
@ -1253,7 +1253,7 @@ static void xlnx_dp_init(Object *obj)
|
|||
object_property_add_child(OBJECT(s), "dpcd", OBJECT(s->dpcd));
|
||||
|
||||
s->edid = I2CDDC(qdev_new("i2c-ddc"));
|
||||
i2c_set_slave_address(I2C_SLAVE(s->edid), 0x50);
|
||||
i2c_slave_set_address(I2C_SLAVE(s->edid), 0x50);
|
||||
object_property_add_child(OBJECT(s), "edid", OBJECT(s->edid));
|
||||
|
||||
fifo8_create(&s->rx_fifo, 16);
|
||||
|
|
|
@ -32,3 +32,7 @@ config MPC_I2C
|
|||
config PCA954X
|
||||
bool
|
||||
select I2C
|
||||
|
||||
config PMBUS
|
||||
bool
|
||||
select SMBUS
|
||||
|
|
|
@ -66,7 +66,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name)
|
|||
return bus;
|
||||
}
|
||||
|
||||
void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
|
||||
void i2c_slave_set_address(I2CSlave *dev, uint8_t address)
|
||||
{
|
||||
dev->address = address;
|
||||
}
|
||||
|
@ -114,8 +114,11 @@ bool i2c_scan_bus(I2CBus *bus, uint8_t address, bool broadcast,
|
|||
* protocol uses a start transfer to switch from write to read mode
|
||||
* without releasing the bus. If that fails, the bus is still
|
||||
* in a transaction.
|
||||
*
|
||||
* @event must be I2C_START_RECV or I2C_START_SEND.
|
||||
*/
|
||||
int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
||||
static int i2c_do_start_transfer(I2CBus *bus, uint8_t address,
|
||||
enum i2c_event event)
|
||||
{
|
||||
I2CSlaveClass *sc;
|
||||
I2CNode *node;
|
||||
|
@ -157,7 +160,7 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
|||
|
||||
if (sc->event) {
|
||||
trace_i2c_event("start", s->address);
|
||||
rv = sc->event(s, recv ? I2C_START_RECV : I2C_START_SEND);
|
||||
rv = sc->event(s, event);
|
||||
if (rv && !bus->broadcast) {
|
||||
if (bus_scanned) {
|
||||
/* First call, terminate the transfer. */
|
||||
|
@ -170,6 +173,23 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv)
|
||||
{
|
||||
return i2c_do_start_transfer(bus, address, is_recv
|
||||
? I2C_START_RECV
|
||||
: I2C_START_SEND);
|
||||
}
|
||||
|
||||
int i2c_start_recv(I2CBus *bus, uint8_t address)
|
||||
{
|
||||
return i2c_do_start_transfer(bus, address, I2C_START_RECV);
|
||||
}
|
||||
|
||||
int i2c_start_send(I2CBus *bus, uint8_t address)
|
||||
{
|
||||
return i2c_do_start_transfer(bus, address, I2C_START_SEND);
|
||||
}
|
||||
|
||||
void i2c_end_transfer(I2CBus *bus)
|
||||
{
|
||||
I2CSlaveClass *sc;
|
||||
|
@ -188,50 +208,42 @@ void i2c_end_transfer(I2CBus *bus)
|
|||
bus->broadcast = false;
|
||||
}
|
||||
|
||||
int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send)
|
||||
int i2c_send(I2CBus *bus, uint8_t data)
|
||||
{
|
||||
I2CSlaveClass *sc;
|
||||
I2CSlave *s;
|
||||
I2CNode *node;
|
||||
int ret = 0;
|
||||
|
||||
if (send) {
|
||||
QLIST_FOREACH(node, &bus->current_devs, next) {
|
||||
s = node->elt;
|
||||
sc = I2C_SLAVE_GET_CLASS(s);
|
||||
if (sc->send) {
|
||||
trace_i2c_send(s->address, *data);
|
||||
ret = ret || sc->send(s, *data);
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
QLIST_FOREACH(node, &bus->current_devs, next) {
|
||||
s = node->elt;
|
||||
sc = I2C_SLAVE_GET_CLASS(s);
|
||||
if (sc->send) {
|
||||
trace_i2c_send(s->address, data);
|
||||
ret = ret || sc->send(s, data);
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
return ret ? -1 : 0;
|
||||
} else {
|
||||
ret = 0xff;
|
||||
if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) {
|
||||
sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
|
||||
if (sc->recv) {
|
||||
s = QLIST_FIRST(&bus->current_devs)->elt;
|
||||
ret = sc->recv(s);
|
||||
trace_i2c_recv(s->address, ret);
|
||||
}
|
||||
}
|
||||
*data = ret;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int i2c_send(I2CBus *bus, uint8_t data)
|
||||
{
|
||||
return i2c_send_recv(bus, &data, true);
|
||||
return ret ? -1 : 0;
|
||||
}
|
||||
|
||||
uint8_t i2c_recv(I2CBus *bus)
|
||||
{
|
||||
uint8_t data = 0xff;
|
||||
I2CSlaveClass *sc;
|
||||
I2CSlave *s;
|
||||
|
||||
if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) {
|
||||
sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
|
||||
if (sc->recv) {
|
||||
s = QLIST_FIRST(&bus->current_devs)->elt;
|
||||
data = sc->recv(s);
|
||||
trace_i2c_recv(s->address, data);
|
||||
}
|
||||
}
|
||||
|
||||
i2c_send_recv(bus, &data, false);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ static void imx_i2c_write(void *opaque, hwaddr offset,
|
|||
switch (offset) {
|
||||
case IADR_ADDR:
|
||||
s->iadr = value & IADR_MASK;
|
||||
/* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */
|
||||
/* i2c_slave_set_address(s->bus, (uint8_t)s->iadr); */
|
||||
break;
|
||||
case IFDR_ADDR:
|
||||
s->ifdr = value & IFDR_MASK;
|
||||
|
|
|
@ -15,4 +15,5 @@ i2c_ss.add(when: 'CONFIG_VERSATILE_I2C', if_true: files('versatile_i2c.c'))
|
|||
i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
|
||||
softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
|
||||
|
|
|
@ -128,14 +128,14 @@ static void smb_transaction(PMSMBus *s)
|
|||
* So at least Linux may or may not set the read bit here.
|
||||
* So just ignore the read bit for this command.
|
||||
*/
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
goto error;
|
||||
}
|
||||
ret = i2c_send(bus, s->smb_data1);
|
||||
if (ret) {
|
||||
goto error;
|
||||
}
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
if (i2c_start_recv(bus, addr)) {
|
||||
goto error;
|
||||
}
|
||||
s->in_i2c_block_read = true;
|
||||
|
|
1612
hw/i2c/pmbus_device.c
Normal file
1612
hw/i2c/pmbus_device.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,8 @@
|
|||
/*
|
||||
* PPC4xx I2C controller emulation
|
||||
*
|
||||
* Documentation: PPC405GP User's Manual, Chapter 22. IIC Bus Interface
|
||||
*
|
||||
* Copyright (c) 2007 Jocelyn Mayer
|
||||
* Copyright (c) 2012 François Revol
|
||||
* Copyright (c) 2016-2018 BALATON Zoltan
|
||||
|
@ -238,11 +240,14 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value,
|
|||
i2c->sts &= ~IIC_STS_ERR;
|
||||
}
|
||||
}
|
||||
if (!(i2c->sts & IIC_STS_ERR) &&
|
||||
i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) {
|
||||
i2c->sts |= IIC_STS_ERR;
|
||||
i2c->extsts |= IIC_EXTSTS_XFRA;
|
||||
break;
|
||||
if (!(i2c->sts & IIC_STS_ERR)) {
|
||||
if (recv) {
|
||||
i2c->mdata[i] = i2c_recv(i2c->bus);
|
||||
} else if (i2c_send(i2c->bus, i2c->mdata[i]) < 0) {
|
||||
i2c->sts |= IIC_STS_ERR;
|
||||
i2c->extsts |= IIC_EXTSTS_XFRA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) {
|
||||
i2c_end_transfer(i2c->bus);
|
||||
|
|
|
@ -29,7 +29,7 @@ int smbus_receive_byte(I2CBus *bus, uint8_t addr)
|
|||
{
|
||||
uint8_t data;
|
||||
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
if (i2c_start_recv(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
data = i2c_recv(bus);
|
||||
|
@ -40,7 +40,7 @@ int smbus_receive_byte(I2CBus *bus, uint8_t addr)
|
|||
|
||||
int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
|
||||
{
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, data);
|
||||
|
@ -51,11 +51,11 @@ int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
|
|||
int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
|
||||
{
|
||||
uint8_t data;
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
if (i2c_start_recv(bus, addr)) {
|
||||
i2c_end_transfer(bus);
|
||||
return -1;
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ 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)
|
||||
{
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
|
@ -79,11 +79,11 @@ 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)
|
||||
{
|
||||
uint16_t data;
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
if (i2c_start_recv(bus, addr)) {
|
||||
i2c_end_transfer(bus);
|
||||
return -1;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ 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)
|
||||
{
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
|
@ -113,12 +113,12 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
|
|||
int i;
|
||||
|
||||
if (send_cmd) {
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
}
|
||||
if (i2c_start_transfer(bus, addr, 1)) {
|
||||
if (i2c_start_recv(bus, addr)) {
|
||||
if (send_cmd) {
|
||||
i2c_end_transfer(bus);
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
|
|||
len = 32;
|
||||
}
|
||||
|
||||
if (i2c_start_transfer(bus, addr, 0)) {
|
||||
if (i2c_start_send(bus, addr)) {
|
||||
return -1;
|
||||
}
|
||||
i2c_send(bus, command);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/input/lm832x.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
@ -27,7 +28,6 @@
|
|||
#include "ui/console.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_LM8323 "lm8323"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LM823KbdState, LM8323)
|
||||
|
||||
struct LM823KbdState {
|
||||
|
|
|
@ -189,7 +189,7 @@ struct IPMIBmcSim {
|
|||
uint8_t watchdog_use;
|
||||
uint8_t watchdog_action;
|
||||
uint8_t watchdog_pretimeout; /* In seconds */
|
||||
bool watchdog_expired;
|
||||
uint8_t watchdog_expired;
|
||||
uint16_t watchdog_timeout; /* in 100's of milliseconds */
|
||||
|
||||
bool watchdog_running;
|
||||
|
@ -2110,7 +2110,7 @@ static const VMStateDescription vmstate_ipmi_sim = {
|
|||
VMSTATE_UINT8(watchdog_use, IPMIBmcSim),
|
||||
VMSTATE_UINT8(watchdog_action, IPMIBmcSim),
|
||||
VMSTATE_UINT8(watchdog_pretimeout, IPMIBmcSim),
|
||||
VMSTATE_BOOL(watchdog_expired, IPMIBmcSim),
|
||||
VMSTATE_UINT8(watchdog_expired, IPMIBmcSim),
|
||||
VMSTATE_UINT16(watchdog_timeout, IPMIBmcSim),
|
||||
VMSTATE_BOOL(watchdog_running, IPMIBmcSim),
|
||||
VMSTATE_BOOL(watchdog_preaction_ran, IPMIBmcSim),
|
||||
|
|
|
@ -31,6 +31,7 @@ subdir('rdma')
|
|||
subdir('rtc')
|
||||
subdir('scsi')
|
||||
subdir('sd')
|
||||
subdir('sensor')
|
||||
subdir('smbios')
|
||||
subdir('ssi')
|
||||
subdir('timer')
|
||||
|
|
|
@ -11,21 +11,6 @@ config ARMSSE_MHU
|
|||
config ARMSSE_CPU_PWRCTRL
|
||||
bool
|
||||
|
||||
config MAX111X
|
||||
bool
|
||||
|
||||
config TMP105
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config TMP421
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config EMC141X
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config ISA_DEBUG
|
||||
bool
|
||||
depends on ISA_BUS
|
||||
|
|
|
@ -106,7 +106,6 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
|
|||
AUXReply ret = AUX_NACK;
|
||||
I2CBus *i2c_bus = aux_get_i2c_bus(bus);
|
||||
size_t i;
|
||||
bool is_write = false;
|
||||
|
||||
DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address,
|
||||
cmd, len);
|
||||
|
@ -117,11 +116,10 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
|
|||
*/
|
||||
case WRITE_AUX:
|
||||
case READ_AUX:
|
||||
is_write = cmd == READ_AUX ? false : true;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!address_space_rw(&bus->aux_addr_space, address++,
|
||||
MEMTXATTRS_UNSPECIFIED, data++, 1,
|
||||
is_write)) {
|
||||
cmd == WRITE_AUX)) {
|
||||
ret = AUX_I2C_ACK;
|
||||
} else {
|
||||
ret = AUX_NACK;
|
||||
|
@ -133,24 +131,37 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
|
|||
* Classic I2C transactions..
|
||||
*/
|
||||
case READ_I2C:
|
||||
case WRITE_I2C:
|
||||
is_write = cmd == READ_I2C ? false : true;
|
||||
if (i2c_bus_busy(i2c_bus)) {
|
||||
i2c_end_transfer(i2c_bus);
|
||||
}
|
||||
|
||||
if (i2c_start_transfer(i2c_bus, address, is_write)) {
|
||||
if (i2c_start_recv(i2c_bus, address)) {
|
||||
ret = AUX_I2C_NACK;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = AUX_I2C_ACK;
|
||||
while (len > 0) {
|
||||
if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
|
||||
for (i = 0; i < len; i++) {
|
||||
data[i] = i2c_recv(i2c_bus);
|
||||
}
|
||||
i2c_end_transfer(i2c_bus);
|
||||
break;
|
||||
case WRITE_I2C:
|
||||
if (i2c_bus_busy(i2c_bus)) {
|
||||
i2c_end_transfer(i2c_bus);
|
||||
}
|
||||
|
||||
if (i2c_start_send(i2c_bus, address)) {
|
||||
ret = AUX_I2C_NACK;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = AUX_I2C_ACK;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_send(i2c_bus, data[i]) < 0) {
|
||||
ret = AUX_I2C_NACK;
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
i2c_end_transfer(i2c_bus);
|
||||
break;
|
||||
|
@ -163,14 +174,12 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
|
|||
* - We changed the address.
|
||||
*/
|
||||
case WRITE_I2C_MOT:
|
||||
case READ_I2C_MOT:
|
||||
is_write = cmd == READ_I2C_MOT ? false : true;
|
||||
ret = AUX_I2C_NACK;
|
||||
if (!i2c_bus_busy(i2c_bus)) {
|
||||
/*
|
||||
* No transactions started..
|
||||
*/
|
||||
if (i2c_start_transfer(i2c_bus, address, is_write)) {
|
||||
if (i2c_start_send(i2c_bus, address)) {
|
||||
break;
|
||||
}
|
||||
} else if ((address != bus->last_i2c_address) ||
|
||||
|
@ -179,23 +188,48 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
|
|||
* Transaction started but we need to restart..
|
||||
*/
|
||||
i2c_end_transfer(i2c_bus);
|
||||
if (i2c_start_transfer(i2c_bus, address, is_write)) {
|
||||
if (i2c_start_send(i2c_bus, address)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bus->last_transaction = cmd;
|
||||
bus->last_i2c_address = address;
|
||||
while (len > 0) {
|
||||
if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
|
||||
ret = AUX_I2C_ACK;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_send(i2c_bus, data[i]) < 0) {
|
||||
i2c_end_transfer(i2c_bus);
|
||||
ret = AUX_I2C_NACK;
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
if (len == 0) {
|
||||
ret = AUX_I2C_ACK;
|
||||
break;
|
||||
case READ_I2C_MOT:
|
||||
ret = AUX_I2C_NACK;
|
||||
if (!i2c_bus_busy(i2c_bus)) {
|
||||
/*
|
||||
* No transactions started..
|
||||
*/
|
||||
if (i2c_start_recv(i2c_bus, address)) {
|
||||
break;
|
||||
}
|
||||
} else if ((address != bus->last_i2c_address) ||
|
||||
(bus->last_transaction != cmd)) {
|
||||
/*
|
||||
* Transaction started but we need to restart..
|
||||
*/
|
||||
i2c_end_transfer(i2c_bus);
|
||||
if (i2c_start_recv(i2c_bus, address)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bus->last_transaction = cmd;
|
||||
bus->last_i2c_address = address;
|
||||
for (i = 0; i < len; i++) {
|
||||
data[i] = i2c_recv(i2c_bus);
|
||||
}
|
||||
ret = AUX_I2C_ACK;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "AUX cmd=%u not implemented\n", cmd);
|
||||
|
|
|
@ -3,13 +3,9 @@ softmmu_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c'))
|
|||
softmmu_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SGA', if_true: files('sga.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
|
||||
|
@ -85,7 +81,7 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files(
|
|||
'bcm2835_powermgt.c',
|
||||
))
|
||||
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-xramc.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Texas Instruments TMP105 Temperature Sensor
|
||||
*
|
||||
* Browse the data sheet:
|
||||
*
|
||||
* http://www.ti.com/lit/gpn/tmp105
|
||||
*
|
||||
* Copyright (C) 2012 Alex Horn <alex.horn@cs.ox.ac.uk>
|
||||
* Copyright (C) 2008-2012 Andrzej Zaborowski <balrogg@gmail.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* later. See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef QEMU_TMP105_H
|
||||
#define QEMU_TMP105_H
|
||||
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/misc/tmp105_regs.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_TMP105 "tmp105"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
|
||||
|
||||
/**
|
||||
* TMP105State:
|
||||
* @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
|
||||
* temperature. See Table 8 in the data sheet.
|
||||
*
|
||||
* @see_also: http://www.ti.com/lit/gpn/tmp105
|
||||
*/
|
||||
struct TMP105State {
|
||||
/*< private >*/
|
||||
I2CSlave i2c;
|
||||
/*< public >*/
|
||||
|
||||
uint8_t len;
|
||||
uint8_t buf[2];
|
||||
qemu_irq pin;
|
||||
|
||||
uint8_t pointer;
|
||||
uint8_t config;
|
||||
int16_t temperature;
|
||||
int16_t limit[2];
|
||||
int faults;
|
||||
uint8_t alarm;
|
||||
/*
|
||||
* The TMP105 initially looks for a temperature rising above T_high;
|
||||
* once this is detected, the condition it looks for next is the
|
||||
* temperature falling below T_low. This flag is false when initially
|
||||
* looking for T_high, true when looking for T_low.
|
||||
*/
|
||||
bool detect_falling;
|
||||
};
|
||||
|
||||
#endif
|
19
hw/sensor/Kconfig
Normal file
19
hw/sensor/Kconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
config TMP105
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config TMP421
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config EMC141X
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config ADM1272
|
||||
bool
|
||||
depends on I2C
|
||||
|
||||
config MAX34451
|
||||
bool
|
||||
depends on I2C
|
543
hw/sensor/adm1272.c
Normal file
543
hw/sensor/adm1272.c
Normal file
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
* Analog Devices ADM1272 High Voltage Positive Hot Swap Controller and Digital
|
||||
* Power Monitor with PMBus
|
||||
*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <string.h>
|
||||
#include "hw/i2c/pmbus_device.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define TYPE_ADM1272 "adm1272"
|
||||
#define ADM1272(obj) OBJECT_CHECK(ADM1272State, (obj), TYPE_ADM1272)
|
||||
|
||||
#define ADM1272_RESTART_TIME 0xCC
|
||||
#define ADM1272_MFR_PEAK_IOUT 0xD0
|
||||
#define ADM1272_MFR_PEAK_VIN 0xD1
|
||||
#define ADM1272_MFR_PEAK_VOUT 0xD2
|
||||
#define ADM1272_MFR_PMON_CONTROL 0xD3
|
||||
#define ADM1272_MFR_PMON_CONFIG 0xD4
|
||||
#define ADM1272_MFR_ALERT1_CONFIG 0xD5
|
||||
#define ADM1272_MFR_ALERT2_CONFIG 0xD6
|
||||
#define ADM1272_MFR_PEAK_TEMPERATURE 0xD7
|
||||
#define ADM1272_MFR_DEVICE_CONFIG 0xD8
|
||||
#define ADM1272_MFR_POWER_CYCLE 0xD9
|
||||
#define ADM1272_MFR_PEAK_PIN 0xDA
|
||||
#define ADM1272_MFR_READ_PIN_EXT 0xDB
|
||||
#define ADM1272_MFR_READ_EIN_EXT 0xDC
|
||||
|
||||
#define ADM1272_HYSTERESIS_LOW 0xF2
|
||||
#define ADM1272_HYSTERESIS_HIGH 0xF3
|
||||
#define ADM1272_STATUS_HYSTERESIS 0xF4
|
||||
#define ADM1272_STATUS_GPIO 0xF5
|
||||
#define ADM1272_STRT_UP_IOUT_LIM 0xF6
|
||||
|
||||
/* Defaults */
|
||||
#define ADM1272_OPERATION_DEFAULT 0x80
|
||||
#define ADM1272_CAPABILITY_DEFAULT 0xB0
|
||||
#define ADM1272_CAPABILITY_NO_PEC 0x30
|
||||
#define ADM1272_DIRECT_MODE 0x40
|
||||
#define ADM1272_HIGH_LIMIT_DEFAULT 0x0FFF
|
||||
#define ADM1272_PIN_OP_DEFAULT 0x7FFF
|
||||
#define ADM1272_PMBUS_REVISION_DEFAULT 0x22
|
||||
#define ADM1272_MFR_ID_DEFAULT "ADI"
|
||||
#define ADM1272_MODEL_DEFAULT "ADM1272-A1"
|
||||
#define ADM1272_MFR_DEFAULT_REVISION "25"
|
||||
#define ADM1272_DEFAULT_DATE "160301"
|
||||
#define ADM1272_RESTART_TIME_DEFAULT 0x64
|
||||
#define ADM1272_PMON_CONTROL_DEFAULT 0x1
|
||||
#define ADM1272_PMON_CONFIG_DEFAULT 0x3F35
|
||||
#define ADM1272_DEVICE_CONFIG_DEFAULT 0x8
|
||||
#define ADM1272_HYSTERESIS_HIGH_DEFAULT 0xFFFF
|
||||
#define ADM1272_STRT_UP_IOUT_LIM_DEFAULT 0x000F
|
||||
#define ADM1272_VOLT_DEFAULT 12000
|
||||
#define ADM1272_IOUT_DEFAULT 25000
|
||||
#define ADM1272_PWR_DEFAULT 300 /* 12V 25A */
|
||||
#define ADM1272_SHUNT 300 /* micro-ohms */
|
||||
#define ADM1272_VOLTAGE_COEFF_DEFAULT 1
|
||||
#define ADM1272_CURRENT_COEFF_DEFAULT 3
|
||||
#define ADM1272_PWR_COEFF_DEFAULT 7
|
||||
#define ADM1272_IOUT_OFFSET 0x5000
|
||||
#define ADM1272_IOUT_OFFSET 0x5000
|
||||
|
||||
|
||||
typedef struct ADM1272State {
|
||||
PMBusDevice parent;
|
||||
|
||||
uint64_t ein_ext;
|
||||
uint32_t pin_ext;
|
||||
uint8_t restart_time;
|
||||
|
||||
uint16_t peak_vin;
|
||||
uint16_t peak_vout;
|
||||
uint16_t peak_iout;
|
||||
uint16_t peak_temperature;
|
||||
uint16_t peak_pin;
|
||||
|
||||
uint8_t pmon_control;
|
||||
uint16_t pmon_config;
|
||||
uint16_t alert1_config;
|
||||
uint16_t alert2_config;
|
||||
uint16_t device_config;
|
||||
|
||||
uint16_t hysteresis_low;
|
||||
uint16_t hysteresis_high;
|
||||
uint8_t status_hysteresis;
|
||||
uint8_t status_gpio;
|
||||
|
||||
uint16_t strt_up_iout_lim;
|
||||
|
||||
} ADM1272State;
|
||||
|
||||
static const PMBusCoefficients adm1272_coefficients[] = {
|
||||
[0] = { 6770, 0, -2 }, /* voltage, vrange 60V */
|
||||
[1] = { 4062, 0, -2 }, /* voltage, vrange 100V */
|
||||
[2] = { 1326, 20480, -1 }, /* current, vsense range 15mV */
|
||||
[3] = { 663, 20480, -1 }, /* current, vsense range 30mV */
|
||||
[4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */
|
||||
[5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */
|
||||
[6] = { 17561, 0, -3 }, /* power, vrange 60V, irange 30mV */
|
||||
[7] = { 10535, 0, -3 }, /* power, vrange 100V, irange 30mV */
|
||||
[8] = { 42, 31871, -1 }, /* temperature */
|
||||
};
|
||||
|
||||
static void adm1272_check_limits(ADM1272State *s)
|
||||
{
|
||||
PMBusDevice *pmdev = PMBUS_DEVICE(s);
|
||||
|
||||
pmbus_check_limits(pmdev);
|
||||
|
||||
if (pmdev->pages[0].read_vout > s->peak_vout) {
|
||||
s->peak_vout = pmdev->pages[0].read_vout;
|
||||
}
|
||||
|
||||
if (pmdev->pages[0].read_vin > s->peak_vin) {
|
||||
s->peak_vin = pmdev->pages[0].read_vin;
|
||||
}
|
||||
|
||||
if (pmdev->pages[0].read_iout > s->peak_iout) {
|
||||
s->peak_iout = pmdev->pages[0].read_iout;
|
||||
}
|
||||
|
||||
if (pmdev->pages[0].read_temperature_1 > s->peak_temperature) {
|
||||
s->peak_temperature = pmdev->pages[0].read_temperature_1;
|
||||
}
|
||||
|
||||
if (pmdev->pages[0].read_pin > s->peak_pin) {
|
||||
s->peak_pin = pmdev->pages[0].read_pin;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t adm1272_millivolts_to_direct(uint32_t value)
|
||||
{
|
||||
PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT];
|
||||
c.b = c.b * 1000;
|
||||
c.R = c.R - 3;
|
||||
return pmbus_data2direct_mode(c, value);
|
||||
}
|
||||
|
||||
static uint32_t adm1272_direct_to_millivolts(uint16_t value)
|
||||
{
|
||||
PMBusCoefficients c = adm1272_coefficients[ADM1272_VOLTAGE_COEFF_DEFAULT];
|
||||
c.b = c.b * 1000;
|
||||
c.R = c.R - 3;
|
||||
return pmbus_direct_mode2data(c, value);
|
||||
}
|
||||
|
||||
static uint16_t adm1272_milliamps_to_direct(uint32_t value)
|
||||
{
|
||||
PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT];
|
||||
/* Y = (m * r_sense * x - b) * 10^R */
|
||||
c.m = c.m * ADM1272_SHUNT / 1000; /* micro-ohms */
|
||||
c.b = c.b * 1000;
|
||||
c.R = c.R - 3;
|
||||
return pmbus_data2direct_mode(c, value);
|
||||
}
|
||||
|
||||
static uint32_t adm1272_direct_to_milliamps(uint16_t value)
|
||||
{
|
||||
PMBusCoefficients c = adm1272_coefficients[ADM1272_CURRENT_COEFF_DEFAULT];
|
||||
c.m = c.m * ADM1272_SHUNT / 1000;
|
||||
c.b = c.b * 1000;
|
||||
c.R = c.R - 3;
|
||||
return pmbus_direct_mode2data(c, value);
|
||||
}
|
||||
|
||||
static uint16_t adm1272_watts_to_direct(uint32_t value)
|
||||
{
|
||||
PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT];
|
||||
c.m = c.m * ADM1272_SHUNT / 1000;
|
||||
return pmbus_data2direct_mode(c, value);
|
||||
}
|
||||
|
||||
static uint32_t adm1272_direct_to_watts(uint16_t value)
|
||||
{
|
||||
PMBusCoefficients c = adm1272_coefficients[ADM1272_PWR_COEFF_DEFAULT];
|
||||
c.m = c.m * ADM1272_SHUNT / 1000;
|
||||
return pmbus_direct_mode2data(c, value);
|
||||
}
|
||||
|
||||
static void adm1272_exit_reset(Object *obj)
|
||||
{
|
||||
ADM1272State *s = ADM1272(obj);
|
||||
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
||||
|
||||
pmdev->page = 0;
|
||||
pmdev->pages[0].operation = ADM1272_OPERATION_DEFAULT;
|
||||
|
||||
|
||||
pmdev->capability = ADM1272_CAPABILITY_NO_PEC;
|
||||
pmdev->pages[0].revision = ADM1272_PMBUS_REVISION_DEFAULT;
|
||||
pmdev->pages[0].vout_mode = ADM1272_DIRECT_MODE;
|
||||
pmdev->pages[0].vout_ov_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
|
||||
pmdev->pages[0].vout_uv_warn_limit = 0;
|
||||
pmdev->pages[0].iout_oc_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
|
||||
pmdev->pages[0].ot_fault_limit = ADM1272_HIGH_LIMIT_DEFAULT;
|
||||
pmdev->pages[0].ot_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
|
||||
pmdev->pages[0].vin_ov_warn_limit = ADM1272_HIGH_LIMIT_DEFAULT;
|
||||
pmdev->pages[0].vin_uv_warn_limit = 0;
|
||||
pmdev->pages[0].pin_op_warn_limit = ADM1272_PIN_OP_DEFAULT;
|
||||
|
||||
pmdev->pages[0].status_word = 0;
|
||||
pmdev->pages[0].status_vout = 0;
|
||||
pmdev->pages[0].status_iout = 0;
|
||||
pmdev->pages[0].status_input = 0;
|
||||
pmdev->pages[0].status_temperature = 0;
|
||||
pmdev->pages[0].status_mfr_specific = 0;
|
||||
|
||||
pmdev->pages[0].read_vin
|
||||
= adm1272_millivolts_to_direct(ADM1272_VOLT_DEFAULT);
|
||||
pmdev->pages[0].read_vout
|
||||
= adm1272_millivolts_to_direct(ADM1272_VOLT_DEFAULT);
|
||||
pmdev->pages[0].read_iout
|
||||
= adm1272_milliamps_to_direct(ADM1272_IOUT_DEFAULT);
|
||||
pmdev->pages[0].read_temperature_1 = 0;
|
||||
pmdev->pages[0].read_pin = adm1272_watts_to_direct(ADM1272_PWR_DEFAULT);
|
||||
pmdev->pages[0].revision = ADM1272_PMBUS_REVISION_DEFAULT;
|
||||
pmdev->pages[0].mfr_id = ADM1272_MFR_ID_DEFAULT;
|
||||
pmdev->pages[0].mfr_model = ADM1272_MODEL_DEFAULT;
|
||||
pmdev->pages[0].mfr_revision = ADM1272_MFR_DEFAULT_REVISION;
|
||||
pmdev->pages[0].mfr_date = ADM1272_DEFAULT_DATE;
|
||||
|
||||
s->pin_ext = 0;
|
||||
s->ein_ext = 0;
|
||||
s->restart_time = ADM1272_RESTART_TIME_DEFAULT;
|
||||
|
||||
s->peak_vin = 0;
|
||||
s->peak_vout = 0;
|
||||
s->peak_iout = 0;
|
||||
s->peak_temperature = 0;
|
||||
s->peak_pin = 0;
|
||||
|
||||
s->pmon_control = ADM1272_PMON_CONTROL_DEFAULT;
|
||||
s->pmon_config = ADM1272_PMON_CONFIG_DEFAULT;
|
||||
s->alert1_config = 0;
|
||||
s->alert2_config = 0;
|
||||
s->device_config = ADM1272_DEVICE_CONFIG_DEFAULT;
|
||||
|
||||
s->hysteresis_low = 0;
|
||||
s->hysteresis_high = ADM1272_HYSTERESIS_HIGH_DEFAULT;
|
||||
s->status_hysteresis = 0;
|
||||
s->status_gpio = 0;
|
||||
|
||||
s->strt_up_iout_lim = ADM1272_STRT_UP_IOUT_LIM_DEFAULT;
|
||||
}
|
||||
|
||||
static uint8_t adm1272_read_byte(PMBusDevice *pmdev)
|
||||
{
|
||||
ADM1272State *s = ADM1272(pmdev);
|
||||
|
||||
switch (pmdev->code) {
|
||||
case ADM1272_RESTART_TIME:
|
||||
pmbus_send8(pmdev, s->restart_time);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PEAK_IOUT:
|
||||
pmbus_send16(pmdev, s->peak_iout);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PEAK_VIN:
|
||||
pmbus_send16(pmdev, s->peak_vin);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PEAK_VOUT:
|
||||
pmbus_send16(pmdev, s->peak_vout);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PMON_CONTROL:
|
||||
pmbus_send8(pmdev, s->pmon_control);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PMON_CONFIG:
|
||||
pmbus_send16(pmdev, s->pmon_config);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_ALERT1_CONFIG:
|
||||
pmbus_send16(pmdev, s->alert1_config);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_ALERT2_CONFIG:
|
||||
pmbus_send16(pmdev, s->alert2_config);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PEAK_TEMPERATURE:
|
||||
pmbus_send16(pmdev, s->peak_temperature);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_DEVICE_CONFIG:
|
||||
pmbus_send16(pmdev, s->device_config);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PEAK_PIN:
|
||||
pmbus_send16(pmdev, s->peak_pin);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_READ_PIN_EXT:
|
||||
pmbus_send32(pmdev, s->pin_ext);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_READ_EIN_EXT:
|
||||
pmbus_send64(pmdev, s->ein_ext);
|
||||
break;
|
||||
|
||||
case ADM1272_HYSTERESIS_LOW:
|
||||
pmbus_send16(pmdev, s->hysteresis_low);
|
||||
break;
|
||||
|
||||
case ADM1272_HYSTERESIS_HIGH:
|
||||
pmbus_send16(pmdev, s->hysteresis_high);
|
||||
break;
|
||||
|
||||
case ADM1272_STATUS_HYSTERESIS:
|
||||
pmbus_send16(pmdev, s->status_hysteresis);
|
||||
break;
|
||||
|
||||
case ADM1272_STATUS_GPIO:
|
||||
pmbus_send16(pmdev, s->status_gpio);
|
||||
break;
|
||||
|
||||
case ADM1272_STRT_UP_IOUT_LIM:
|
||||
pmbus_send16(pmdev, s->strt_up_iout_lim);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: reading from unsupported register: 0x%02x\n",
|
||||
__func__, pmdev->code);
|
||||
return 0xFF;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adm1272_write_data(PMBusDevice *pmdev, const uint8_t *buf,
|
||||
uint8_t len)
|
||||
{
|
||||
ADM1272State *s = ADM1272(pmdev);
|
||||
|
||||
if (len == 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pmdev->code = buf[0]; /* PMBus command code */
|
||||
|
||||
if (len == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Exclude command code from buffer */
|
||||
buf++;
|
||||
len--;
|
||||
|
||||
switch (pmdev->code) {
|
||||
|
||||
case ADM1272_RESTART_TIME:
|
||||
s->restart_time = pmbus_receive8(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PMON_CONTROL:
|
||||
s->pmon_control = pmbus_receive8(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_PMON_CONFIG:
|
||||
s->pmon_config = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_ALERT1_CONFIG:
|
||||
s->alert1_config = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_ALERT2_CONFIG:
|
||||
s->alert2_config = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_DEVICE_CONFIG:
|
||||
s->device_config = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_MFR_POWER_CYCLE:
|
||||
adm1272_exit_reset((Object *)s);
|
||||
break;
|
||||
|
||||
case ADM1272_HYSTERESIS_LOW:
|
||||
s->hysteresis_low = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_HYSTERESIS_HIGH:
|
||||
s->hysteresis_high = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case ADM1272_STRT_UP_IOUT_LIM:
|
||||
s->strt_up_iout_lim = pmbus_receive16(pmdev);
|
||||
adm1272_check_limits(s);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: writing to unsupported register: 0x%02x\n",
|
||||
__func__, pmdev->code);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adm1272_get(Object *obj, Visitor *v, const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
if (strcmp(name, "vin") == 0 || strcmp(name, "vout") == 0) {
|
||||
value = adm1272_direct_to_millivolts(*(uint16_t *)opaque);
|
||||
} else if (strcmp(name, "iout") == 0) {
|
||||
value = adm1272_direct_to_milliamps(*(uint16_t *)opaque);
|
||||
} else if (strcmp(name, "pin") == 0) {
|
||||
value = adm1272_direct_to_watts(*(uint16_t *)opaque);
|
||||
} else {
|
||||
value = *(uint16_t *)opaque;
|
||||
}
|
||||
|
||||
visit_type_uint16(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void adm1272_set(Object *obj, Visitor *v, const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
ADM1272State *s = ADM1272(obj);
|
||||
uint16_t *internal = opaque;
|
||||
uint16_t value;
|
||||
|
||||
if (!visit_type_uint16(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(name, "vin") == 0 || strcmp(name, "vout") == 0) {
|
||||
*internal = adm1272_millivolts_to_direct(value);
|
||||
} else if (strcmp(name, "iout") == 0) {
|
||||
*internal = adm1272_milliamps_to_direct(value);
|
||||
} else if (strcmp(name, "pin") == 0) {
|
||||
*internal = adm1272_watts_to_direct(value);
|
||||
} else {
|
||||
*internal = value;
|
||||
}
|
||||
|
||||
adm1272_check_limits(s);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_adm1272 = {
|
||||
.name = "ADM1272",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]){
|
||||
VMSTATE_PMBUS_DEVICE(parent, ADM1272State),
|
||||
VMSTATE_UINT64(ein_ext, ADM1272State),
|
||||
VMSTATE_UINT32(pin_ext, ADM1272State),
|
||||
VMSTATE_UINT8(restart_time, ADM1272State),
|
||||
|
||||
VMSTATE_UINT16(peak_vin, ADM1272State),
|
||||
VMSTATE_UINT16(peak_vout, ADM1272State),
|
||||
VMSTATE_UINT16(peak_iout, ADM1272State),
|
||||
VMSTATE_UINT16(peak_temperature, ADM1272State),
|
||||
VMSTATE_UINT16(peak_pin, ADM1272State),
|
||||
|
||||
VMSTATE_UINT8(pmon_control, ADM1272State),
|
||||
VMSTATE_UINT16(pmon_config, ADM1272State),
|
||||
VMSTATE_UINT16(alert1_config, ADM1272State),
|
||||
VMSTATE_UINT16(alert2_config, ADM1272State),
|
||||
VMSTATE_UINT16(device_config, ADM1272State),
|
||||
|
||||
VMSTATE_UINT16(hysteresis_low, ADM1272State),
|
||||
VMSTATE_UINT16(hysteresis_high, ADM1272State),
|
||||
VMSTATE_UINT8(status_hysteresis, ADM1272State),
|
||||
VMSTATE_UINT8(status_gpio, ADM1272State),
|
||||
|
||||
VMSTATE_UINT16(strt_up_iout_lim, ADM1272State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void adm1272_init(Object *obj)
|
||||
{
|
||||
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
||||
uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VIN | PB_HAS_IOUT |
|
||||
PB_HAS_PIN | PB_HAS_TEMPERATURE | PB_HAS_MFR_INFO;
|
||||
|
||||
pmbus_page_config(pmdev, 0, flags);
|
||||
|
||||
object_property_add(obj, "vin", "uint16",
|
||||
adm1272_get,
|
||||
adm1272_set, NULL, &pmdev->pages[0].read_vin);
|
||||
|
||||
object_property_add(obj, "vout", "uint16",
|
||||
adm1272_get,
|
||||
adm1272_set, NULL, &pmdev->pages[0].read_vout);
|
||||
|
||||
object_property_add(obj, "iout", "uint16",
|
||||
adm1272_get,
|
||||
adm1272_set, NULL, &pmdev->pages[0].read_iout);
|
||||
|
||||
object_property_add(obj, "pin", "uint16",
|
||||
adm1272_get,
|
||||
adm1272_set, NULL, &pmdev->pages[0].read_pin);
|
||||
|
||||
}
|
||||
|
||||
static void adm1272_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
|
||||
|
||||
dc->desc = "Analog Devices ADM1272 Hot Swap controller";
|
||||
dc->vmsd = &vmstate_adm1272;
|
||||
k->write_data = adm1272_write_data;
|
||||
k->receive_byte = adm1272_read_byte;
|
||||
k->device_num_pages = 1;
|
||||
|
||||
rc->phases.exit = adm1272_exit_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo adm1272_info = {
|
||||
.name = TYPE_ADM1272,
|
||||
.parent = TYPE_PMBUS_DEVICE,
|
||||
.instance_size = sizeof(ADM1272State),
|
||||
.instance_init = adm1272_init,
|
||||
.class_init = adm1272_class_init,
|
||||
};
|
||||
|
||||
static void adm1272_register_types(void)
|
||||
{
|
||||
type_register_static(&adm1272_info);
|
||||
}
|
||||
|
||||
type_init(adm1272_register_types)
|
|
@ -25,7 +25,7 @@
|
|||
#include "qapi/visitor.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/misc/emc141x_regs.h"
|
||||
#include "hw/sensor/emc141x_regs.h"
|
||||
|
||||
#define SENSORS_COUNT_MAX 4
|
||||
|
775
hw/sensor/max34451.c
Normal file
775
hw/sensor/max34451.c
Normal file
|
@ -0,0 +1,775 @@
|
|||
/*
|
||||
* Maxim MAX34451 PMBus 16-Channel V/I monitor and 12-Channel Sequencer/Marginer
|
||||
*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/i2c/pmbus_device.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define TYPE_MAX34451 "max34451"
|
||||
#define MAX34451(obj) OBJECT_CHECK(MAX34451State, (obj), TYPE_MAX34451)
|
||||
|
||||
#define MAX34451_MFR_MODE 0xD1
|
||||
#define MAX34451_MFR_PSEN_CONFIG 0xD2
|
||||
#define MAX34451_MFR_VOUT_PEAK 0xD4
|
||||
#define MAX34451_MFR_IOUT_PEAK 0xD5
|
||||
#define MAX34451_MFR_TEMPERATURE_PEAK 0xD6
|
||||
#define MAX34451_MFR_VOUT_MIN 0xD7
|
||||
#define MAX34451_MFR_NV_LOG_CONFIG 0xD8
|
||||
#define MAX34451_MFR_FAULT_RESPONSE 0xD9
|
||||
#define MAX34451_MFR_FAULT_RETRY 0xDA
|
||||
#define MAX34451_MFR_NV_FAULT_LOG 0xDC
|
||||
#define MAX34451_MFR_TIME_COUNT 0xDD
|
||||
#define MAX34451_MFR_MARGIN_CONFIG 0xDF
|
||||
#define MAX34451_MFR_FW_SERIAL 0xE0
|
||||
#define MAX34451_MFR_IOUT_AVG 0xE2
|
||||
#define MAX34451_MFR_CHANNEL_CONFIG 0xE4
|
||||
#define MAX34451_MFR_TON_SEQ_MAX 0xE6
|
||||
#define MAX34451_MFR_PWM_CONFIG 0xE7
|
||||
#define MAX34451_MFR_SEQ_CONFIG 0xE8
|
||||
#define MAX34451_MFR_STORE_ALL 0xEE
|
||||
#define MAX34451_MFR_RESTORE_ALL 0xEF
|
||||
#define MAX34451_MFR_TEMP_SENSOR_CONFIG 0xF0
|
||||
#define MAX34451_MFR_STORE_SINGLE 0xFC
|
||||
#define MAX34451_MFR_CRC 0xFE
|
||||
|
||||
#define MAX34451_NUM_MARGINED_PSU 12
|
||||
#define MAX34451_NUM_PWR_DEVICES 16
|
||||
#define MAX34451_NUM_TEMP_DEVICES 5
|
||||
#define MAX34451_NUM_PAGES 21
|
||||
|
||||
#define DEFAULT_OP_ON 0x80
|
||||
#define DEFAULT_CAPABILITY 0x20
|
||||
#define DEFAULT_ON_OFF_CONFIG 0x1a
|
||||
#define DEFAULT_VOUT_MODE 0x40
|
||||
#define DEFAULT_TEMPERATURE 2500
|
||||
#define DEFAULT_SCALE 0x7FFF
|
||||
#define DEFAULT_OV_LIMIT 0x7FFF
|
||||
#define DEFAULT_OC_LIMIT 0x7FFF
|
||||
#define DEFAULT_OT_LIMIT 0x7FFF
|
||||
#define DEFAULT_VMIN 0x7FFF
|
||||
#define DEFAULT_TON_FAULT_LIMIT 0xFFFF
|
||||
#define DEFAULT_CHANNEL_CONFIG 0x20
|
||||
#define DEFAULT_TEXT 0x3130313031303130
|
||||
|
||||
/**
|
||||
* MAX34451State:
|
||||
* @code: The command code received
|
||||
* @page: Each page corresponds to a device monitored by the Max 34451
|
||||
* The page register determines the available commands depending on device
|
||||
___________________________________________________________________________
|
||||
| 0 | Power supply monitored by RS0, controlled by PSEN0, and |
|
||||
| | margined with PWM0. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 1 | Power supply monitored by RS1, controlled by PSEN1, and |
|
||||
| | margined with PWM1. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 2 | Power supply monitored by RS2, controlled by PSEN2, and |
|
||||
| | margined with PWM2. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 3 | Power supply monitored by RS3, controlled by PSEN3, and |
|
||||
| | margined with PWM3. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 4 | Power supply monitored by RS4, controlled by PSEN4, and |
|
||||
| | margined with PWM4. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 5 | Power supply monitored by RS5, controlled by PSEN5, and |
|
||||
| | margined with PWM5. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 6 | Power supply monitored by RS6, controlled by PSEN6, and |
|
||||
| | margined with PWM6. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 7 | Power supply monitored by RS7, controlled by PSEN7, and |
|
||||
| | margined with PWM7. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 8 | Power supply monitored by RS8, controlled by PSEN8, and |
|
||||
| | optionally margined by OUT0 of external DS4424 at I2C address A0h.|
|
||||
|_______|___________________________________________________________________|
|
||||
| 9 | Power supply monitored by RS9, controlled by PSEN9, and |
|
||||
| | optionally margined by OUT1 of external DS4424 at I2C address A0h.|
|
||||
|_______|___________________________________________________________________|
|
||||
| 10 | Power supply monitored by RS10, controlled by PSEN10, and |
|
||||
| | optionally margined by OUT2 of external DS4424 at I2C address A0h.|
|
||||
|_______|___________________________________________________________________|
|
||||
| 11 | Power supply monitored by RS11, controlled by PSEN11, and |
|
||||
| | optionally margined by OUT3 of external DS4424 at I2C address A0h.|
|
||||
|_______|___________________________________________________________________|
|
||||
| 12 | ADC channel 12 (monitors voltage or current) or GPI. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 13 | ADC channel 13 (monitors voltage or current) or GPI. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 14 | ADC channel 14 (monitors voltage or current) or GPI. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 15 | ADC channel 15 (monitors voltage or current) or GPI. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 16 | Internal temperature sensor. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 17 | External DS75LV temperature sensor with I2C address 90h. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 18 | External DS75LV temperature sensor with I2C address 92h. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 19 | External DS75LV temperature sensor with I2C address 94h. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 20 | External DS75LV temperature sensor with I2C address 96h. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 21=E2=80=93254| Reserved. |
|
||||
|_______|___________________________________________________________________|
|
||||
| 255 | Applies to all pages. |
|
||||
|_______|___________________________________________________________________|
|
||||
*
|
||||
* @operation: Turn on and off power supplies
|
||||
* @on_off_config: Configure the power supply on and off transition behaviour
|
||||
* @write_protect: protect against changes to the device's memory
|
||||
* @vout_margin_high: the voltage when OPERATION is set to margin high
|
||||
* @vout_margin_low: the voltage when OPERATION is set to margin low
|
||||
* @vout_scale: scale ADC reading to actual device reading if different
|
||||
* @iout_cal_gain: set ratio of the voltage at the ADC input to sensed current
|
||||
*/
|
||||
typedef struct MAX34451State {
|
||||
PMBusDevice parent;
|
||||
|
||||
uint16_t power_good_on[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t power_good_off[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t ton_delay[MAX34451_NUM_MARGINED_PSU];
|
||||
uint16_t ton_max_fault_limit[MAX34451_NUM_MARGINED_PSU];
|
||||
uint16_t toff_delay[MAX34451_NUM_MARGINED_PSU];
|
||||
uint8_t status_mfr_specific[MAX34451_NUM_PWR_DEVICES];
|
||||
/* Manufacturer specific function */
|
||||
uint64_t mfr_location;
|
||||
uint64_t mfr_date;
|
||||
uint64_t mfr_serial;
|
||||
uint16_t mfr_mode;
|
||||
uint32_t psen_config[MAX34451_NUM_MARGINED_PSU];
|
||||
uint16_t vout_peak[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t iout_peak[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t temperature_peak[MAX34451_NUM_TEMP_DEVICES];
|
||||
uint16_t vout_min[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t nv_log_config;
|
||||
uint32_t fault_response[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t fault_retry;
|
||||
uint32_t fault_log;
|
||||
uint32_t time_count;
|
||||
uint16_t margin_config[MAX34451_NUM_MARGINED_PSU];
|
||||
uint16_t fw_serial;
|
||||
uint16_t iout_avg[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t channel_config[MAX34451_NUM_PWR_DEVICES];
|
||||
uint16_t ton_seq_max[MAX34451_NUM_MARGINED_PSU];
|
||||
uint32_t pwm_config[MAX34451_NUM_MARGINED_PSU];
|
||||
uint32_t seq_config[MAX34451_NUM_MARGINED_PSU];
|
||||
uint16_t temp_sensor_config[MAX34451_NUM_TEMP_DEVICES];
|
||||
uint16_t store_single;
|
||||
uint16_t crc;
|
||||
} MAX34451State;
|
||||
|
||||
|
||||
static void max34451_check_limits(MAX34451State *s)
|
||||
{
|
||||
PMBusDevice *pmdev = PMBUS_DEVICE(s);
|
||||
|
||||
pmbus_check_limits(pmdev);
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
|
||||
if (pmdev->pages[i].read_vout == 0) { /* PSU disabled */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pmdev->pages[i].read_vout > s->vout_peak[i]) {
|
||||
s->vout_peak[i] = pmdev->pages[i].read_vout;
|
||||
}
|
||||
|
||||
if (pmdev->pages[i].read_vout < s->vout_min[i]) {
|
||||
s->vout_min[i] = pmdev->pages[i].read_vout;
|
||||
}
|
||||
|
||||
if (pmdev->pages[i].read_iout > s->iout_peak[i]) {
|
||||
s->iout_peak[i] = pmdev->pages[i].read_iout;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) {
|
||||
if (pmdev->pages[i + 16].read_temperature_1 > s->temperature_peak[i]) {
|
||||
s->temperature_peak[i] = pmdev->pages[i + 16].read_temperature_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t max34451_read_byte(PMBusDevice *pmdev)
|
||||
{
|
||||
MAX34451State *s = MAX34451(pmdev);
|
||||
switch (pmdev->code) {
|
||||
|
||||
case PMBUS_POWER_GOOD_ON:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->power_good_on[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_POWER_GOOD_OFF:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->power_good_off[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_TON_DELAY:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send16(pmdev, s->ton_delay[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_TON_MAX_FAULT_LIMIT:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send16(pmdev, s->ton_max_fault_limit[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_TOFF_DELAY:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send16(pmdev, s->toff_delay[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_STATUS_MFR_SPECIFIC:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send8(pmdev, s->status_mfr_specific[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_ID:
|
||||
pmbus_send8(pmdev, 0x4d); /* Maxim */
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_MODEL:
|
||||
pmbus_send8(pmdev, 0x59);
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_LOCATION:
|
||||
pmbus_send64(pmdev, s->mfr_location);
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_DATE:
|
||||
pmbus_send64(pmdev, s->mfr_date);
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_SERIAL:
|
||||
pmbus_send64(pmdev, s->mfr_serial);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_MODE:
|
||||
pmbus_send16(pmdev, s->mfr_mode);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_PSEN_CONFIG:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send32(pmdev, s->psen_config[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_VOUT_PEAK:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->vout_peak[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_IOUT_PEAK:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->iout_peak[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TEMPERATURE_PEAK:
|
||||
if (15 < pmdev->page && pmdev->page < 21) {
|
||||
pmbus_send16(pmdev, s->temperature_peak[pmdev->page % 16]);
|
||||
} else {
|
||||
pmbus_send16(pmdev, s->temperature_peak[0]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_VOUT_MIN:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->vout_min[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_NV_LOG_CONFIG:
|
||||
pmbus_send16(pmdev, s->nv_log_config);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_FAULT_RESPONSE:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send32(pmdev, s->fault_response[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_FAULT_RETRY:
|
||||
pmbus_send32(pmdev, s->fault_retry);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_NV_FAULT_LOG:
|
||||
pmbus_send32(pmdev, s->fault_log);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TIME_COUNT:
|
||||
pmbus_send32(pmdev, s->time_count);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_MARGIN_CONFIG:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send16(pmdev, s->margin_config[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_FW_SERIAL:
|
||||
if (pmdev->page == 255) {
|
||||
pmbus_send16(pmdev, 1); /* Firmware revision */
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_IOUT_AVG:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->iout_avg[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_CHANNEL_CONFIG:
|
||||
if (pmdev->page < 16) {
|
||||
pmbus_send16(pmdev, s->channel_config[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TON_SEQ_MAX:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send16(pmdev, s->ton_seq_max[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_PWM_CONFIG:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send32(pmdev, s->pwm_config[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_SEQ_CONFIG:
|
||||
if (pmdev->page < 12) {
|
||||
pmbus_send32(pmdev, s->seq_config[pmdev->page]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TEMP_SENSOR_CONFIG:
|
||||
if (15 < pmdev->page && pmdev->page < 21) {
|
||||
pmbus_send32(pmdev, s->temp_sensor_config[pmdev->page % 16]);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_STORE_SINGLE:
|
||||
pmbus_send32(pmdev, s->store_single);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_CRC:
|
||||
pmbus_send32(pmdev, s->crc);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: reading from unsupported register: 0x%02x\n",
|
||||
__func__, pmdev->code);
|
||||
break;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static int max34451_write_data(PMBusDevice *pmdev, const uint8_t *buf,
|
||||
uint8_t len)
|
||||
{
|
||||
MAX34451State *s = MAX34451(pmdev);
|
||||
|
||||
if (len == 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pmdev->code = buf[0]; /* PMBus command code */
|
||||
|
||||
if (len == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Exclude command code from buffer */
|
||||
buf++;
|
||||
len--;
|
||||
uint8_t index = pmdev->page;
|
||||
|
||||
switch (pmdev->code) {
|
||||
case MAX34451_MFR_STORE_ALL:
|
||||
case MAX34451_MFR_RESTORE_ALL:
|
||||
case MAX34451_MFR_STORE_SINGLE:
|
||||
/*
|
||||
* TODO: hardware behaviour is to move the contents of volatile
|
||||
* memory to non-volatile memory.
|
||||
*/
|
||||
break;
|
||||
|
||||
case PMBUS_POWER_GOOD_ON: /* R/W word */
|
||||
if (pmdev->page < MAX34451_NUM_PWR_DEVICES) {
|
||||
s->power_good_on[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_POWER_GOOD_OFF: /* R/W word */
|
||||
if (pmdev->page < MAX34451_NUM_PWR_DEVICES) {
|
||||
s->power_good_off[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_TON_DELAY: /* R/W word */
|
||||
if (pmdev->page < 12) {
|
||||
s->ton_delay[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_TON_MAX_FAULT_LIMIT: /* R/W word */
|
||||
if (pmdev->page < 12) {
|
||||
s->ton_max_fault_limit[pmdev->page]
|
||||
= pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_TOFF_DELAY: /* R/W word */
|
||||
if (pmdev->page < 12) {
|
||||
s->toff_delay[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_LOCATION: /* R/W 64 */
|
||||
s->mfr_location = pmbus_receive64(pmdev);
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_DATE: /* R/W 64 */
|
||||
s->mfr_date = pmbus_receive64(pmdev);
|
||||
break;
|
||||
|
||||
case PMBUS_MFR_SERIAL: /* R/W 64 */
|
||||
s->mfr_serial = pmbus_receive64(pmdev);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_MODE: /* R/W word */
|
||||
s->mfr_mode = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_PSEN_CONFIG: /* R/W 32 */
|
||||
if (pmdev->page < 12) {
|
||||
s->psen_config[pmdev->page] = pmbus_receive32(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_VOUT_PEAK: /* R/W word */
|
||||
if (pmdev->page < 16) {
|
||||
s->vout_peak[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_IOUT_PEAK: /* R/W word */
|
||||
if (pmdev->page < 16) {
|
||||
s->iout_peak[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TEMPERATURE_PEAK: /* R/W word */
|
||||
if (15 < pmdev->page && pmdev->page < 21) {
|
||||
s->temperature_peak[pmdev->page % 16]
|
||||
= pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_VOUT_MIN: /* R/W word */
|
||||
if (pmdev->page < 16) {
|
||||
s->vout_min[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_NV_LOG_CONFIG: /* R/W word */
|
||||
s->nv_log_config = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_FAULT_RESPONSE: /* R/W 32 */
|
||||
if (pmdev->page < 16) {
|
||||
s->fault_response[pmdev->page] = pmbus_receive32(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_FAULT_RETRY: /* R/W word */
|
||||
s->fault_retry = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TIME_COUNT: /* R/W 32 */
|
||||
s->time_count = pmbus_receive32(pmdev);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_MARGIN_CONFIG: /* R/W word */
|
||||
if (pmdev->page < 12) {
|
||||
s->margin_config[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_CHANNEL_CONFIG: /* R/W word */
|
||||
if (pmdev->page < 16) {
|
||||
s->channel_config[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TON_SEQ_MAX: /* R/W word */
|
||||
if (pmdev->page < 12) {
|
||||
s->ton_seq_max[pmdev->page] = pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_PWM_CONFIG: /* R/W 32 */
|
||||
if (pmdev->page < 12) {
|
||||
s->pwm_config[pmdev->page] = pmbus_receive32(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_SEQ_CONFIG: /* R/W 32 */
|
||||
if (pmdev->page < 12) {
|
||||
s->seq_config[pmdev->page] = pmbus_receive32(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_TEMP_SENSOR_CONFIG: /* R/W word */
|
||||
if (15 < pmdev->page && pmdev->page < 21) {
|
||||
s->temp_sensor_config[pmdev->page % 16]
|
||||
= pmbus_receive16(pmdev);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_CRC: /* R/W word */
|
||||
s->crc = pmbus_receive16(pmdev);
|
||||
break;
|
||||
|
||||
case MAX34451_MFR_NV_FAULT_LOG:
|
||||
case MAX34451_MFR_FW_SERIAL:
|
||||
case MAX34451_MFR_IOUT_AVG:
|
||||
/* Read only commands */
|
||||
pmdev->pages[index].status_word |= PMBUS_STATUS_CML;
|
||||
pmdev->pages[index].status_cml |= PB_CML_FAULT_INVALID_DATA;
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: writing to read-only register 0x%02x\n",
|
||||
__func__, pmdev->code);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: writing to unsupported register: 0x%02x\n",
|
||||
__func__, pmdev->code);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max34451_get(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
visit_type_uint16(v, name, (uint16_t *)opaque, errp);
|
||||
}
|
||||
|
||||
static void max34451_set(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
MAX34451State *s = MAX34451(obj);
|
||||
uint16_t *internal = opaque;
|
||||
uint16_t value;
|
||||
if (!visit_type_uint16(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*internal = value;
|
||||
max34451_check_limits(s);
|
||||
}
|
||||
|
||||
/* used to init uint16_t arrays */
|
||||
static inline void *memset_word(void *s, uint16_t c, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
uint16_t *p = s;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
p[i] = c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void max34451_exit_reset(Object *obj)
|
||||
{
|
||||
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
||||
MAX34451State *s = MAX34451(obj);
|
||||
pmdev->capability = DEFAULT_CAPABILITY;
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_PAGES; i++) {
|
||||
pmdev->pages[i].operation = DEFAULT_OP_ON;
|
||||
pmdev->pages[i].on_off_config = DEFAULT_ON_OFF_CONFIG;
|
||||
pmdev->pages[i].revision = 0x11;
|
||||
pmdev->pages[i].vout_mode = DEFAULT_VOUT_MODE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
|
||||
pmdev->pages[i].vout_scale_monitor = DEFAULT_SCALE;
|
||||
pmdev->pages[i].vout_ov_fault_limit = DEFAULT_OV_LIMIT;
|
||||
pmdev->pages[i].vout_ov_warn_limit = DEFAULT_OV_LIMIT;
|
||||
pmdev->pages[i].iout_oc_warn_limit = DEFAULT_OC_LIMIT;
|
||||
pmdev->pages[i].iout_oc_fault_limit = DEFAULT_OC_LIMIT;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_MARGINED_PSU; i++) {
|
||||
pmdev->pages[i].ton_max_fault_limit = DEFAULT_TON_FAULT_LIMIT;
|
||||
}
|
||||
|
||||
for (int i = 16; i < MAX34451_NUM_TEMP_DEVICES + 16; i++) {
|
||||
pmdev->pages[i].read_temperature_1 = DEFAULT_TEMPERATURE;
|
||||
pmdev->pages[i].ot_warn_limit = DEFAULT_OT_LIMIT;
|
||||
pmdev->pages[i].ot_fault_limit = DEFAULT_OT_LIMIT;
|
||||
}
|
||||
|
||||
memset_word(s->ton_max_fault_limit, DEFAULT_TON_FAULT_LIMIT,
|
||||
MAX34451_NUM_MARGINED_PSU);
|
||||
memset_word(s->channel_config, DEFAULT_CHANNEL_CONFIG,
|
||||
MAX34451_NUM_PWR_DEVICES);
|
||||
memset_word(s->vout_min, DEFAULT_VMIN, MAX34451_NUM_PWR_DEVICES);
|
||||
|
||||
s->mfr_location = DEFAULT_TEXT;
|
||||
s->mfr_date = DEFAULT_TEXT;
|
||||
s->mfr_serial = DEFAULT_TEXT;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_max34451 = {
|
||||
.name = TYPE_MAX34451,
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]){
|
||||
VMSTATE_PMBUS_DEVICE(parent, MAX34451State),
|
||||
VMSTATE_UINT16_ARRAY(power_good_on, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(power_good_off, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(ton_delay, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT16_ARRAY(ton_max_fault_limit, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT16_ARRAY(toff_delay, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT8_ARRAY(status_mfr_specific, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT64(mfr_location, MAX34451State),
|
||||
VMSTATE_UINT64(mfr_date, MAX34451State),
|
||||
VMSTATE_UINT64(mfr_serial, MAX34451State),
|
||||
VMSTATE_UINT16(mfr_mode, MAX34451State),
|
||||
VMSTATE_UINT32_ARRAY(psen_config, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT16_ARRAY(vout_peak, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(iout_peak, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(temperature_peak, MAX34451State,
|
||||
MAX34451_NUM_TEMP_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(vout_min, MAX34451State, MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16(nv_log_config, MAX34451State),
|
||||
VMSTATE_UINT32_ARRAY(fault_response, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16(fault_retry, MAX34451State),
|
||||
VMSTATE_UINT32(fault_log, MAX34451State),
|
||||
VMSTATE_UINT32(time_count, MAX34451State),
|
||||
VMSTATE_UINT16_ARRAY(margin_config, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT16(fw_serial, MAX34451State),
|
||||
VMSTATE_UINT16_ARRAY(iout_avg, MAX34451State, MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(channel_config, MAX34451State,
|
||||
MAX34451_NUM_PWR_DEVICES),
|
||||
VMSTATE_UINT16_ARRAY(ton_seq_max, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT32_ARRAY(pwm_config, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT32_ARRAY(seq_config, MAX34451State,
|
||||
MAX34451_NUM_MARGINED_PSU),
|
||||
VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX34451State,
|
||||
MAX34451_NUM_TEMP_DEVICES),
|
||||
VMSTATE_UINT16(store_single, MAX34451State),
|
||||
VMSTATE_UINT16(crc, MAX34451State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void max34451_init(Object *obj)
|
||||
{
|
||||
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
||||
uint64_t psu_flags = PB_HAS_VOUT | PB_HAS_IOUT | PB_HAS_VOUT_MODE |
|
||||
PB_HAS_IOUT_GAIN;
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
|
||||
pmbus_page_config(pmdev, i, psu_flags);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX34451_NUM_MARGINED_PSU; i++) {
|
||||
pmbus_page_config(pmdev, i, psu_flags | PB_HAS_VOUT_MARGIN);
|
||||
}
|
||||
|
||||
for (int i = 16; i < MAX34451_NUM_TEMP_DEVICES + 16; i++) {
|
||||
pmbus_page_config(pmdev, i, PB_HAS_TEMPERATURE | PB_HAS_VOUT_MODE);
|
||||
}
|
||||
|
||||
/* get and set the voltage in millivolts, max is 32767 mV */
|
||||
for (int i = 0; i < MAX34451_NUM_PWR_DEVICES; i++) {
|
||||
object_property_add(obj, "vout[*]", "uint16",
|
||||
max34451_get,
|
||||
max34451_set, NULL, &pmdev->pages[i].read_vout);
|
||||
}
|
||||
|
||||
/*
|
||||
* get and set the temperature of the internal temperature sensor in
|
||||
* centidegrees Celcius i.e.: 2500 -> 25.00 C, max is 327.67 C
|
||||
*/
|
||||
for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES; i++) {
|
||||
object_property_add(obj, "temperature[*]", "uint16",
|
||||
max34451_get,
|
||||
max34451_set,
|
||||
NULL,
|
||||
&pmdev->pages[i + 16].read_temperature_1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void max34451_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
|
||||
dc->desc = "Maxim MAX34451 16-Channel V/I monitor";
|
||||
dc->vmsd = &vmstate_max34451;
|
||||
k->write_data = max34451_write_data;
|
||||
k->receive_byte = max34451_read_byte;
|
||||
k->device_num_pages = MAX34451_NUM_PAGES;
|
||||
rc->phases.exit = max34451_exit_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo max34451_info = {
|
||||
.name = TYPE_MAX34451,
|
||||
.parent = TYPE_PMBUS_DEVICE,
|
||||
.instance_size = sizeof(MAX34451State),
|
||||
.instance_init = max34451_init,
|
||||
.class_init = max34451_class_init,
|
||||
};
|
||||
|
||||
static void max34451_register_types(void)
|
||||
{
|
||||
type_register_static(&max34451_info);
|
||||
}
|
||||
|
||||
type_init(max34451_register_types)
|
5
hw/sensor/meson.build
Normal file
5
hw/sensor/meson.build
Normal file
|
@ -0,0 +1,5 @@
|
|||
softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c'))
|
|
@ -22,7 +22,7 @@
|
|||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "tmp105.h"
|
||||
#include "hw/sensor/tmp105.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/module.h"
|
Loading…
Add table
Add a link
Reference in a new issue