qemu/hw/sensor/isl_pmbus_vr.c
<<
>>
Prefs
   1/*
   2 * PMBus device for Renesas Digital Multiphase Voltage Regulators
   3 *
   4 * Copyright 2021 Google LLC
   5 *
   6 * SPDX-License-Identifier: GPL-2.0-or-later
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "hw/sensor/isl_pmbus_vr.h"
  11#include "hw/qdev-properties.h"
  12#include "qapi/visitor.h"
  13#include "qemu/log.h"
  14#include "qemu/module.h"
  15
  16static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev)
  17{
  18    ISLState *s = ISL69260(pmdev);
  19
  20    switch (pmdev->code) {
  21    case PMBUS_IC_DEVICE_ID:
  22        if (!s->ic_device_id_len) {
  23            break;
  24        }
  25        pmbus_send(pmdev, s->ic_device_id, s->ic_device_id_len);
  26        pmbus_idle(pmdev);
  27        return 0;
  28    }
  29
  30    qemu_log_mask(LOG_GUEST_ERROR,
  31                  "%s: reading from unsupported register: 0x%02x\n",
  32                  __func__, pmdev->code);
  33    return PMBUS_ERR_BYTE;
  34}
  35
  36static int isl_pmbus_vr_write_data(PMBusDevice *pmdev, const uint8_t *buf,
  37                                   uint8_t len)
  38{
  39    qemu_log_mask(LOG_GUEST_ERROR,
  40                  "%s: write to unsupported register: 0x%02x\n",
  41                  __func__, pmdev->code);
  42    return PMBUS_ERR_BYTE;
  43}
  44
  45/* TODO: Implement coefficients support in pmbus_device.c for qmp */
  46static void isl_pmbus_vr_get(Object *obj, Visitor *v, const char *name,
  47                             void *opaque, Error **errp)
  48{
  49    visit_type_uint16(v, name, (uint16_t *)opaque, errp);
  50}
  51
  52static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name,
  53                             void *opaque, Error **errp)
  54{
  55    PMBusDevice *pmdev = PMBUS_DEVICE(obj);
  56    uint16_t *internal = opaque;
  57    uint16_t value;
  58    if (!visit_type_uint16(v, name, &value, errp)) {
  59        return;
  60    }
  61
  62    *internal = value;
  63    pmbus_check_limits(pmdev);
  64}
  65
  66static void isl_pmbus_vr_exit_reset(Object *obj)
  67{
  68    PMBusDevice *pmdev = PMBUS_DEVICE(obj);
  69
  70    pmdev->page = 0;
  71    pmdev->capability = ISL_CAPABILITY_DEFAULT;
  72    for (int i = 0; i < pmdev->num_pages; i++) {
  73        pmdev->pages[i].operation = ISL_OPERATION_DEFAULT;
  74        pmdev->pages[i].on_off_config = ISL_ON_OFF_CONFIG_DEFAULT;
  75        pmdev->pages[i].vout_mode = ISL_VOUT_MODE_DEFAULT;
  76        pmdev->pages[i].vout_command = ISL_VOUT_COMMAND_DEFAULT;
  77        pmdev->pages[i].vout_max = ISL_VOUT_MAX_DEFAULT;
  78        pmdev->pages[i].vout_margin_high = ISL_VOUT_MARGIN_HIGH_DEFAULT;
  79        pmdev->pages[i].vout_margin_low = ISL_VOUT_MARGIN_LOW_DEFAULT;
  80        pmdev->pages[i].vout_transition_rate = ISL_VOUT_TRANSITION_RATE_DEFAULT;
  81        pmdev->pages[i].vout_ov_fault_limit = ISL_VOUT_OV_FAULT_LIMIT_DEFAULT;
  82        pmdev->pages[i].ot_fault_limit = ISL_OT_FAULT_LIMIT_DEFAULT;
  83        pmdev->pages[i].ot_warn_limit = ISL_OT_WARN_LIMIT_DEFAULT;
  84        pmdev->pages[i].vin_ov_warn_limit = ISL_VIN_OV_WARN_LIMIT_DEFAULT;
  85        pmdev->pages[i].vin_uv_warn_limit = ISL_VIN_UV_WARN_LIMIT_DEFAULT;
  86        pmdev->pages[i].iin_oc_fault_limit = ISL_IIN_OC_FAULT_LIMIT_DEFAULT;
  87        pmdev->pages[i].ton_delay = ISL_TON_DELAY_DEFAULT;
  88        pmdev->pages[i].ton_rise = ISL_TON_RISE_DEFAULT;
  89        pmdev->pages[i].toff_fall = ISL_TOFF_FALL_DEFAULT;
  90        pmdev->pages[i].revision = ISL_REVISION_DEFAULT;
  91
  92        pmdev->pages[i].read_vout = ISL_READ_VOUT_DEFAULT;
  93        pmdev->pages[i].read_iout = ISL_READ_IOUT_DEFAULT;
  94        pmdev->pages[i].read_pout = ISL_READ_POUT_DEFAULT;
  95        pmdev->pages[i].read_vin = ISL_READ_VIN_DEFAULT;
  96        pmdev->pages[i].read_iin = ISL_READ_IIN_DEFAULT;
  97        pmdev->pages[i].read_pin = ISL_READ_PIN_DEFAULT;
  98        pmdev->pages[i].read_temperature_1 = ISL_READ_TEMP_DEFAULT;
  99        pmdev->pages[i].read_temperature_2 = ISL_READ_TEMP_DEFAULT;
 100        pmdev->pages[i].read_temperature_3 = ISL_READ_TEMP_DEFAULT;
 101    }
 102}
 103
 104/* The raa228000 uses different direct mode coefficents from most isl devices */
 105static void raa228000_exit_reset(Object *obj)
 106{
 107    PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 108
 109    isl_pmbus_vr_exit_reset(obj);
 110
 111    pmdev->pages[0].read_iout = 0;
 112    pmdev->pages[0].read_pout = 0;
 113    pmdev->pages[0].read_vout = 0;
 114    pmdev->pages[0].read_vin = 0;
 115    pmdev->pages[0].read_iin = 0;
 116    pmdev->pages[0].read_pin = 0;
 117    pmdev->pages[0].read_temperature_1 = 0;
 118    pmdev->pages[0].read_temperature_2 = 0;
 119    pmdev->pages[0].read_temperature_3 = 0;
 120}
 121
 122static void isl69259_exit_reset(Object *obj)
 123{
 124    ISLState *s = ISL69260(obj);
 125    static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 0x3c};
 126    g_assert(sizeof(ic_device_id) <= sizeof(s->ic_device_id));
 127
 128    isl_pmbus_vr_exit_reset(obj);
 129
 130    s->ic_device_id_len = sizeof(ic_device_id);
 131    memcpy(s->ic_device_id, ic_device_id, sizeof(ic_device_id));
 132}
 133
 134static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages)
 135{
 136    PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 137    for (int i = 0; i < pages; i++) {
 138        if (flags[i] & PB_HAS_VIN) {
 139            object_property_add(obj, "vin[*]", "uint16",
 140                                isl_pmbus_vr_get,
 141                                isl_pmbus_vr_set,
 142                                NULL, &pmdev->pages[i].read_vin);
 143        }
 144
 145        if (flags[i] & PB_HAS_VOUT) {
 146            object_property_add(obj, "vout[*]", "uint16",
 147                                isl_pmbus_vr_get,
 148                                isl_pmbus_vr_set,
 149                                NULL, &pmdev->pages[i].read_vout);
 150        }
 151
 152        if (flags[i] & PB_HAS_IIN) {
 153            object_property_add(obj, "iin[*]", "uint16",
 154                                isl_pmbus_vr_get,
 155                                isl_pmbus_vr_set,
 156                                NULL, &pmdev->pages[i].read_iin);
 157        }
 158
 159        if (flags[i] & PB_HAS_IOUT) {
 160            object_property_add(obj, "iout[*]", "uint16",
 161                                isl_pmbus_vr_get,
 162                                isl_pmbus_vr_set,
 163                                NULL, &pmdev->pages[i].read_iout);
 164        }
 165
 166        if (flags[i] & PB_HAS_PIN) {
 167            object_property_add(obj, "pin[*]", "uint16",
 168                                isl_pmbus_vr_get,
 169                                isl_pmbus_vr_set,
 170                                NULL, &pmdev->pages[i].read_pin);
 171        }
 172
 173        if (flags[i] & PB_HAS_POUT) {
 174            object_property_add(obj, "pout[*]", "uint16",
 175                                isl_pmbus_vr_get,
 176                                isl_pmbus_vr_set,
 177                                NULL, &pmdev->pages[i].read_pout);
 178        }
 179
 180        if (flags[i] & PB_HAS_TEMPERATURE) {
 181            object_property_add(obj, "temp1[*]", "uint16",
 182                                isl_pmbus_vr_get,
 183                                isl_pmbus_vr_set,
 184                                NULL, &pmdev->pages[i].read_temperature_1);
 185        }
 186
 187        if (flags[i] & PB_HAS_TEMP2) {
 188            object_property_add(obj, "temp2[*]", "uint16",
 189                                isl_pmbus_vr_get,
 190                                isl_pmbus_vr_set,
 191                                NULL, &pmdev->pages[i].read_temperature_2);
 192        }
 193
 194        if (flags[i] & PB_HAS_TEMP3) {
 195            object_property_add(obj, "temp3[*]", "uint16",
 196                                isl_pmbus_vr_get,
 197                                isl_pmbus_vr_set,
 198                                NULL, &pmdev->pages[i].read_temperature_3);
 199        }
 200    }
 201}
 202
 203static void raa22xx_init(Object *obj)
 204{
 205    PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 206    uint64_t flags[2];
 207
 208    flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE |
 209               PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN |
 210               PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE |
 211               PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC;
 212    flags[1] = PB_HAS_IIN | PB_HAS_PIN | PB_HAS_TEMPERATURE | PB_HAS_TEMP3 |
 213               PB_HAS_VOUT | PB_HAS_VOUT_MODE | PB_HAS_VOUT_MARGIN |
 214               PB_HAS_VOUT_RATING | PB_HAS_IOUT | PB_HAS_POUT |
 215               PB_HAS_STATUS_MFR_SPECIFIC;
 216
 217    pmbus_page_config(pmdev, 0, flags[0]);
 218    pmbus_page_config(pmdev, 1, flags[1]);
 219    isl_pmbus_vr_add_props(obj, flags, ARRAY_SIZE(flags));
 220}
 221
 222static void raa228000_init(Object *obj)
 223{
 224    PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 225    uint64_t flags[1];
 226
 227    flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE |
 228               PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN |
 229               PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE |
 230               PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC;
 231
 232    pmbus_page_config(pmdev, 0, flags[0]);
 233    isl_pmbus_vr_add_props(obj, flags, 1);
 234}
 235
 236static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data,
 237                                    uint8_t pages)
 238{
 239    PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
 240    k->write_data = isl_pmbus_vr_write_data;
 241    k->receive_byte = isl_pmbus_vr_read_byte;
 242    k->device_num_pages = pages;
 243}
 244
 245static void isl69260_class_init(ObjectClass *klass, void *data)
 246{
 247    ResettableClass *rc = RESETTABLE_CLASS(klass);
 248    DeviceClass *dc = DEVICE_CLASS(klass);
 249    dc->desc = "Renesas ISL69260 Digital Multiphase Voltage Regulator";
 250    rc->phases.exit = isl_pmbus_vr_exit_reset;
 251    isl_pmbus_vr_class_init(klass, data, 2);
 252}
 253
 254static void raa228000_class_init(ObjectClass *klass, void *data)
 255{
 256    ResettableClass *rc = RESETTABLE_CLASS(klass);
 257    DeviceClass *dc = DEVICE_CLASS(klass);
 258    dc->desc = "Renesas 228000 Digital Multiphase Voltage Regulator";
 259    rc->phases.exit = raa228000_exit_reset;
 260    isl_pmbus_vr_class_init(klass, data, 1);
 261}
 262
 263static void raa229004_class_init(ObjectClass *klass, void *data)
 264{
 265    ResettableClass *rc = RESETTABLE_CLASS(klass);
 266    DeviceClass *dc = DEVICE_CLASS(klass);
 267    dc->desc = "Renesas 229004 Digital Multiphase Voltage Regulator";
 268    rc->phases.exit = isl_pmbus_vr_exit_reset;
 269    isl_pmbus_vr_class_init(klass, data, 2);
 270}
 271
 272static void isl69259_class_init(ObjectClass *klass, void *data)
 273{
 274    ResettableClass *rc = RESETTABLE_CLASS(klass);
 275    DeviceClass *dc = DEVICE_CLASS(klass);
 276    dc->desc = "Renesas ISL69259 Digital Multiphase Voltage Regulator";
 277    rc->phases.exit = isl69259_exit_reset;
 278    isl_pmbus_vr_class_init(klass, data, 2);
 279}
 280
 281static const TypeInfo isl69259_info = {
 282    .name = TYPE_ISL69259,
 283    .parent = TYPE_ISL69260,
 284    .class_init = isl69259_class_init,
 285};
 286
 287static const TypeInfo isl69260_info = {
 288    .name = TYPE_ISL69260,
 289    .parent = TYPE_PMBUS_DEVICE,
 290    .instance_size = sizeof(ISLState),
 291    .instance_init = raa22xx_init,
 292    .class_init = isl69260_class_init,
 293};
 294
 295static const TypeInfo raa229004_info = {
 296    .name = TYPE_RAA229004,
 297    .parent = TYPE_PMBUS_DEVICE,
 298    .instance_size = sizeof(ISLState),
 299    .instance_init = raa22xx_init,
 300    .class_init = raa229004_class_init,
 301};
 302
 303static const TypeInfo raa228000_info = {
 304    .name = TYPE_RAA228000,
 305    .parent = TYPE_PMBUS_DEVICE,
 306    .instance_size = sizeof(ISLState),
 307    .instance_init = raa228000_init,
 308    .class_init = raa228000_class_init,
 309};
 310
 311static void isl_pmbus_vr_register_types(void)
 312{
 313    type_register_static(&isl69259_info);
 314    type_register_static(&isl69260_info);
 315    type_register_static(&raa228000_info);
 316    type_register_static(&raa229004_info);
 317}
 318
 319type_init(isl_pmbus_vr_register_types)
 320