ad80e36744
We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Luc Michel <luc.michel@amd.com> Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org
255 lines
7.4 KiB
C
255 lines
7.4 KiB
C
/*
|
|
* Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and
|
|
* Fault Recording with PMBus
|
|
*
|
|
* https://www.analog.com/media/en/technical-documentation/data-sheets/adm1266.pdf
|
|
*
|
|
* Copyright 2023 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_ADM1266 "adm1266"
|
|
OBJECT_DECLARE_SIMPLE_TYPE(ADM1266State, ADM1266)
|
|
|
|
#define ADM1266_BLACKBOX_CONFIG 0xD3
|
|
#define ADM1266_PDIO_CONFIG 0xD4
|
|
#define ADM1266_READ_STATE 0xD9
|
|
#define ADM1266_READ_BLACKBOX 0xDE
|
|
#define ADM1266_SET_RTC 0xDF
|
|
#define ADM1266_GPIO_SYNC_CONFIGURATION 0xE1
|
|
#define ADM1266_BLACKBOX_INFORMATION 0xE6
|
|
#define ADM1266_PDIO_STATUS 0xE9
|
|
#define ADM1266_GPIO_STATUS 0xEA
|
|
|
|
/* Defaults */
|
|
#define ADM1266_OPERATION_DEFAULT 0x80
|
|
#define ADM1266_CAPABILITY_DEFAULT 0xA0
|
|
#define ADM1266_CAPABILITY_NO_PEC 0x20
|
|
#define ADM1266_PMBUS_REVISION_DEFAULT 0x22
|
|
#define ADM1266_MFR_ID_DEFAULT "ADI"
|
|
#define ADM1266_MFR_ID_DEFAULT_LEN 32
|
|
#define ADM1266_MFR_MODEL_DEFAULT "ADM1266-A1"
|
|
#define ADM1266_MFR_MODEL_DEFAULT_LEN 32
|
|
#define ADM1266_MFR_REVISION_DEFAULT "25"
|
|
#define ADM1266_MFR_REVISION_DEFAULT_LEN 8
|
|
|
|
#define ADM1266_NUM_PAGES 17
|
|
/**
|
|
* PAGE Index
|
|
* Page 0 VH1.
|
|
* Page 1 VH2.
|
|
* Page 2 VH3.
|
|
* Page 3 VH4.
|
|
* Page 4 VP1.
|
|
* Page 5 VP2.
|
|
* Page 6 VP3.
|
|
* Page 7 VP4.
|
|
* Page 8 VP5.
|
|
* Page 9 VP6.
|
|
* Page 10 VP7.
|
|
* Page 11 VP8.
|
|
* Page 12 VP9.
|
|
* Page 13 VP10.
|
|
* Page 14 VP11.
|
|
* Page 15 VP12.
|
|
* Page 16 VP13.
|
|
*/
|
|
typedef struct ADM1266State {
|
|
PMBusDevice parent;
|
|
|
|
char mfr_id[32];
|
|
char mfr_model[32];
|
|
char mfr_rev[8];
|
|
} ADM1266State;
|
|
|
|
static const uint8_t adm1266_ic_device_id[] = {0x03, 0x41, 0x12, 0x66};
|
|
static const uint8_t adm1266_ic_device_rev[] = {0x08, 0x01, 0x08, 0x07, 0x0,
|
|
0x0, 0x07, 0x41, 0x30};
|
|
|
|
static void adm1266_exit_reset(Object *obj, ResetType type)
|
|
{
|
|
ADM1266State *s = ADM1266(obj);
|
|
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
|
|
|
pmdev->page = 0;
|
|
pmdev->capability = ADM1266_CAPABILITY_NO_PEC;
|
|
|
|
for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
|
|
pmdev->pages[i].operation = ADM1266_OPERATION_DEFAULT;
|
|
pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT;
|
|
pmdev->pages[i].vout_mode = 0;
|
|
pmdev->pages[i].read_vout = pmbus_data2linear_mode(12, 0);
|
|
pmdev->pages[i].vout_margin_high = pmbus_data2linear_mode(15, 0);
|
|
pmdev->pages[i].vout_margin_low = pmbus_data2linear_mode(3, 0);
|
|
pmdev->pages[i].vout_ov_fault_limit = pmbus_data2linear_mode(16, 0);
|
|
pmdev->pages[i].revision = ADM1266_PMBUS_REVISION_DEFAULT;
|
|
}
|
|
|
|
strncpy(s->mfr_id, ADM1266_MFR_ID_DEFAULT, 4);
|
|
strncpy(s->mfr_model, ADM1266_MFR_MODEL_DEFAULT, 11);
|
|
strncpy(s->mfr_rev, ADM1266_MFR_REVISION_DEFAULT, 3);
|
|
}
|
|
|
|
static uint8_t adm1266_read_byte(PMBusDevice *pmdev)
|
|
{
|
|
ADM1266State *s = ADM1266(pmdev);
|
|
|
|
switch (pmdev->code) {
|
|
case PMBUS_MFR_ID: /* R/W block */
|
|
pmbus_send_string(pmdev, s->mfr_id);
|
|
break;
|
|
|
|
case PMBUS_MFR_MODEL: /* R/W block */
|
|
pmbus_send_string(pmdev, s->mfr_model);
|
|
break;
|
|
|
|
case PMBUS_MFR_REVISION: /* R/W block */
|
|
pmbus_send_string(pmdev, s->mfr_rev);
|
|
break;
|
|
|
|
case PMBUS_IC_DEVICE_ID:
|
|
pmbus_send(pmdev, adm1266_ic_device_id, sizeof(adm1266_ic_device_id));
|
|
break;
|
|
|
|
case PMBUS_IC_DEVICE_REV:
|
|
pmbus_send(pmdev, adm1266_ic_device_rev, sizeof(adm1266_ic_device_rev));
|
|
break;
|
|
|
|
default:
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"%s: reading from unimplemented register: 0x%02x\n",
|
|
__func__, pmdev->code);
|
|
return 0xFF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adm1266_write_data(PMBusDevice *pmdev, const uint8_t *buf,
|
|
uint8_t len)
|
|
{
|
|
ADM1266State *s = ADM1266(pmdev);
|
|
|
|
switch (pmdev->code) {
|
|
case PMBUS_MFR_ID: /* R/W block */
|
|
pmbus_receive_block(pmdev, (uint8_t *)s->mfr_id, sizeof(s->mfr_id));
|
|
break;
|
|
|
|
case PMBUS_MFR_MODEL: /* R/W block */
|
|
pmbus_receive_block(pmdev, (uint8_t *)s->mfr_model,
|
|
sizeof(s->mfr_model));
|
|
break;
|
|
|
|
case PMBUS_MFR_REVISION: /* R/W block*/
|
|
pmbus_receive_block(pmdev, (uint8_t *)s->mfr_rev, sizeof(s->mfr_rev));
|
|
break;
|
|
|
|
case ADM1266_SET_RTC: /* do nothing */
|
|
break;
|
|
|
|
default:
|
|
qemu_log_mask(LOG_UNIMP,
|
|
"%s: writing to unimplemented register: 0x%02x\n",
|
|
__func__, pmdev->code);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void adm1266_get(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
uint16_t value;
|
|
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
|
PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode;
|
|
|
|
if (strcmp(name, "vout") == 0) {
|
|
value = pmbus_linear_mode2data(*(uint16_t *)opaque, mode->exp);
|
|
} else {
|
|
value = *(uint16_t *)opaque;
|
|
}
|
|
|
|
visit_type_uint16(v, name, &value, errp);
|
|
}
|
|
|
|
static void adm1266_set(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
uint16_t *internal = opaque;
|
|
uint16_t value;
|
|
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
|
PMBusVoutMode *mode = (PMBusVoutMode *)&pmdev->pages[0].vout_mode;
|
|
|
|
if (!visit_type_uint16(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
|
|
*internal = pmbus_data2linear_mode(value, mode->exp);
|
|
pmbus_check_limits(pmdev);
|
|
}
|
|
|
|
static const VMStateDescription vmstate_adm1266 = {
|
|
.name = "ADM1266",
|
|
.version_id = 0,
|
|
.minimum_version_id = 0,
|
|
.fields = (const VMStateField[]){
|
|
VMSTATE_PMBUS_DEVICE(parent, ADM1266State),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
static void adm1266_init(Object *obj)
|
|
{
|
|
PMBusDevice *pmdev = PMBUS_DEVICE(obj);
|
|
uint64_t flags = PB_HAS_VOUT_MODE | PB_HAS_VOUT | PB_HAS_VOUT_MARGIN |
|
|
PB_HAS_VOUT_RATING | PB_HAS_STATUS_MFR_SPECIFIC;
|
|
|
|
for (int i = 0; i < ADM1266_NUM_PAGES; i++) {
|
|
pmbus_page_config(pmdev, i, flags);
|
|
|
|
object_property_add(obj, "vout[*]", "uint16",
|
|
adm1266_get,
|
|
adm1266_set, NULL, &pmdev->pages[i].read_vout);
|
|
}
|
|
}
|
|
|
|
static void adm1266_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 ADM1266 Hot Swap controller";
|
|
dc->vmsd = &vmstate_adm1266;
|
|
k->write_data = adm1266_write_data;
|
|
k->receive_byte = adm1266_read_byte;
|
|
k->device_num_pages = 17;
|
|
|
|
rc->phases.exit = adm1266_exit_reset;
|
|
}
|
|
|
|
static const TypeInfo adm1266_info = {
|
|
.name = TYPE_ADM1266,
|
|
.parent = TYPE_PMBUS_DEVICE,
|
|
.instance_size = sizeof(ADM1266State),
|
|
.instance_init = adm1266_init,
|
|
.class_init = adm1266_class_init,
|
|
};
|
|
|
|
static void adm1266_register_types(void)
|
|
{
|
|
type_register_static(&adm1266_info);
|
|
}
|
|
|
|
type_init(adm1266_register_types)
|