xref: /qemu/hw/sensor/isl_pmbus_vr.c (revision a06d9eddb015a9f5895161b0a3958a2e4be21579)
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  
16  static 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  
36  static 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 */
46  static 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  
52  static 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  
66  static void isl_pmbus_vr_exit_reset(Object *obj, ResetType type)
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 coefficients from most isl devices */
105  static void raa228000_exit_reset(Object *obj, ResetType type)
106  {
107      PMBusDevice *pmdev = PMBUS_DEVICE(obj);
108  
109      isl_pmbus_vr_exit_reset(obj, type);
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  
122  static void isl69259_exit_reset(Object *obj, ResetType type)
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, type);
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  
134  static 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  
203  static 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  
222  static 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  
236  static 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  
245  static 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  
254  static 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  
263  static 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  
272  static 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  
281  static const TypeInfo isl69259_info = {
282      .name = TYPE_ISL69259,
283      .parent = TYPE_ISL69260,
284      .class_init = isl69259_class_init,
285  };
286  
287  static 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  
295  static 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  
303  static 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  
311  static 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  
319  type_init(isl_pmbus_vr_register_types)
320