Re: [PATCH v3 0/8] PMBus fixes and new functions

2023-10-24 Thread Titus Rwantare
On Tue, 24 Oct 2023 at 04:50, Philippe Mathieu-Daudé  wrote:
>
> On 24/10/23 12:06, Alex Bennée wrote:
>
> > A pull request is really just a GPG signed tag that you push to a repo.
> > You can use the existing git tooling to create the cover letter for it.
> >
> > I've included my exact steps at the end of the email but really it comes
> > down to:
> >
> >git tag --sign your-pr-tag
> >git push your-pr-tag
> >git format-patch 
> >git request-pull origin/master your_repo_details your-pr-tag
> >
> > and finally
> >
> >git send-email
> >
> > My personal exact steps are integrated with my editor but are:
>
>
> > 8 Preparing a QEMU Pull Request
> > ═══
>
> > 9 And send the pull request
> > ═══
>
> For these steps I just do:
>
> $ git publish -b origin/master \
>  --pull-request --sign-pull --keyid=0xMYKEY
>
> which uses .gitpublish from commit 08bb160e02,
> calling get_maintainer.pl for each patch.
>
> Using GSuite, I also have in ~/.gitconfig:
>
> [sendemail]
>  smtpServer = smtp.gmail.com
>  smtpBatchSize = 1
>  smtpReloginDelay = 3

Thanks all, I'll do some dry runs to walk through these approaches.

-Titus



Re: [PATCH v3 0/8] PMBus fixes and new functions

2023-10-23 Thread Titus Rwantare
On Mon, 23 Oct 2023 at 12:16, Alex Bennée  wrote:

> You seem to have missed a number of tags from previous postings:
>
>   
> https://qemu.readthedocs.io/en/master/devel/submitting-a-patch.html#proper-use-of-reviewed-by-tags-can-aid-review
>
> (although I notice we only mention Reviewed-by in the docs)
>
> You can use a tool like b4 to apply a series and collect the tags. It
> will also collect the Message-Id's which are also useful.
>
> Once you've fixed up your tags I think as the maintainer you can submit
> a pull request.
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro

Thanks for the tip about b4, I spent some time getting to grips with
it and it's a huge timesaver.
I haven't quite figured out how to get it to produce and send a signed
pull request, but I don't need that just yet.

-Titus



[PATCH v3 7/8] hw/i2c: pmbus: immediately clear faults on request

2023-10-23 Thread Titus Rwantare
The probing process of the generic pmbus driver generates
faults to determine if functions are available. These faults
were not always cleared resulting in probe failures.

Signed-off-by: Titus Rwantare 
Reviewed-by: Patrick Venture 
---
 hw/i2c/pmbus_device.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 3bce39e84e..481e158380 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -1244,6 +1244,11 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->in_buf = buf;
 
 pmdev->code = buf[0]; /* PMBus command code */
+
+if (pmdev->code == PMBUS_CLEAR_FAULTS) {
+pmbus_clear_faults(pmdev);
+}
+
 if (len == 1) { /* Single length writes are command codes only */
 return 0;
 }
-- 
2.42.0.758.gaed0368e0e-goog




[PATCH v3 4/8] hw/i2c: pmbus: add VCAP register

2023-10-23 Thread Titus Rwantare
VCAP is a register for devices with energy storage capacitors.

Reviewed-by: Benjamin Streb 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 8 
 include/hw/i2c/pmbus_device.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index c1d8c93056..3bce39e84e 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -906,6 +906,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_READ_VCAP: /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_VCAP) {
+pmbus_send16(pmdev, pmdev->pages[index].read_vcap);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_READ_VOUT: /* Read-Only word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index ad431bdc7c..f195c11384 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -243,6 +243,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 #define PB_HAS_VIN_RATING  BIT_ULL(13)
 #define PB_HAS_VOUT_RATING BIT_ULL(14)
 #define PB_HAS_VOUT_MODE   BIT_ULL(15)
+#define PB_HAS_VCAPBIT_ULL(16)
 #define PB_HAS_IOUTBIT_ULL(21)
 #define PB_HAS_IIN BIT_ULL(22)
 #define PB_HAS_IOUT_RATING BIT_ULL(23)
-- 
2.42.0.758.gaed0368e0e-goog




[PATCH v3 8/8] hw/i2c: pmbus: reset page register for out of range reads

2023-10-23 Thread Titus Rwantare
The linux pmbus driver scans all possible pages and does not reset the
current page after the scan, making all future page reads fail as out of range
on devices with a single page.

This change resets out of range pages immediately on write.

Also added a qtest for simultaneous writes to all pages.

Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/i2c/pmbus_device.c   | 18 +-
 tests/qtest/max34451-test.c | 24 
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 481e158380..1b978e588f 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -1255,6 +1255,15 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 
 if (pmdev->code == PMBUS_PAGE) {
 pmdev->page = pmbus_receive8(pmdev);
+
+if (pmdev->page > pmdev->num_pages - 1 && pmdev->page != PB_ALL_PAGES) 
{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: page %u is out of range\n",
+  __func__, pmdev->page);
+pmdev->page = 0; /* undefined behaviour - reset to page 0 */
+pmbus_cml_error(pmdev);
+return PMBUS_ERR_BYTE;
+}
 return 0;
 }
 
@@ -1268,15 +1277,6 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 return 0;
 }
 
-if (pmdev->page > pmdev->num_pages - 1) {
-qemu_log_mask(LOG_GUEST_ERROR,
-"%s: page %u is out of range\n",
-__func__, pmdev->page);
-pmdev->page = 0; /* undefined behaviour - reset to page 0 */
-pmbus_cml_error(pmdev);
-return PMBUS_ERR_BYTE;
-}
-
 index = pmdev->page;
 
 switch (pmdev->code) {
diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c
index 0c98d0764c..dbf6ddc829 100644
--- a/tests/qtest/max34451-test.c
+++ b/tests/qtest/max34451-test.c
@@ -18,6 +18,7 @@
 #define TEST_ID "max34451-test"
 #define TEST_ADDR (0x4e)
 
+#define MAX34451_MFR_MODE   0xD1
 #define MAX34451_MFR_VOUT_PEAK  0xD4
 #define MAX34451_MFR_IOUT_PEAK  0xD5
 #define MAX34451_MFR_TEMPERATURE_PEAK   0xD6
@@ -315,6 +316,28 @@ static void test_ot_faults(void *obj, void *data, 
QGuestAllocator *alloc)
 }
 }
 
+#define RAND_ON_OFF_CONFIG  0x12
+#define RAND_MFR_MODE   0x3456
+
+/* test writes to all pages */
+static void test_all_pages(void *obj, void *data, QGuestAllocator *alloc)
+{
+uint16_t i2c_value;
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+i2c_set8(i2cdev, PMBUS_PAGE, PB_ALL_PAGES);
+i2c_set8(i2cdev, PMBUS_ON_OFF_CONFIG, RAND_ON_OFF_CONFIG);
+max34451_i2c_set16(i2cdev, MAX34451_MFR_MODE, RAND_MFR_MODE);
+
+for (int i = 0; i < MAX34451_NUM_TEMP_DEVICES + MAX34451_NUM_PWR_DEVICES;
+ i++) {
+i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG);
+g_assert_cmphex(i2c_value, ==, RAND_ON_OFF_CONFIG);
+i2c_value = max34451_i2c_get16(i2cdev, MAX34451_MFR_MODE);
+g_assert_cmphex(i2c_value, ==, RAND_MFR_MODE);
+}
+}
+
 static void max34451_register_nodes(void)
 {
 QOSGraphEdgeOptions opts = {
@@ -332,5 +355,6 @@ static void max34451_register_nodes(void)
 qos_add_test("test_ro_regs", "max34451", test_ro_regs, NULL);
 qos_add_test("test_ov_faults", "max34451", test_ov_faults, NULL);
 qos_add_test("test_ot_faults", "max34451", test_ot_faults, NULL);
+qos_add_test("test_all_pages", "max34451", test_all_pages, NULL);
 }
 libqos_init(max34451_register_nodes);
-- 
2.42.0.758.gaed0368e0e-goog




[PATCH v3 2/8] hw/i2c: pmbus: add vout mode bitfields

2023-10-23 Thread Titus Rwantare
The VOUT_MODE command is described in the PMBus Specification,
Part II, Ver 1.3 Section 8.3

VOUT_MODE has a three bit mode and 4 bit parameter, the three bit
mode determines whether voltages are formatted as uint16, uint16,
VID, and Direct modes. VID and Direct modes use the remaining 5 bits
to scale the voltage readings.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 include/hw/i2c/pmbus_device.h | 8 
 1 file changed, 8 insertions(+)

diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 7dc00cc4d9..2e95164aa1 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -444,6 +444,14 @@ typedef struct PMBusCoefficients {
 int32_t R; /* exponent */
 } PMBusCoefficients;
 
+/**
+ * VOUT_Mode bit fields
+ */
+typedef struct PMBusVoutMode {
+uint8_t  mode:3;
+int8_t   exp:5;
+} PMBusVoutMode;
+
 /**
  * Convert sensor values to direct mode format
  *
-- 
2.42.0.758.gaed0368e0e-goog




[PATCH v3 5/8] hw/sensor: add ADM1266 device model

2023-10-23 Thread Titus Rwantare
  The ADM1266 is a cascadable super sequencer with margin control and
  fault recording.
  This commit adds basic support for its PMBus commands and models
  the identification registers that can be modified in a firmware
  update.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/arm/Kconfig|   1 +
 hw/sensor/Kconfig |   5 +
 hw/sensor/adm1266.c   | 254 ++
 hw/sensor/meson.build |   1 +
 4 files changed, 261 insertions(+)
 create mode 100644 hw/sensor/adm1266.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 7e68348440..b1e8c0e2ac 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -488,6 +488,7 @@ config NPCM7XX
 default y
 depends on TCG && ARM
 select A9MPCORE
+select ADM1266
 select ADM1272
 select ARM_GIC
 select SMBUS
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index e03bd09b50..bc6331b4ab 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -22,6 +22,11 @@ config ADM1272
 bool
 depends on I2C
 
+config ADM1266
+bool
+depends on PMBUS
+default y if PMBUS
+
 config MAX34451
 bool
 depends on I2C
diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c
new file mode 100644
index 00..5ae4f82ba1
--- /dev/null
+++ b/hw/sensor/adm1266.c
@@ -0,0 +1,254 @@
+/*
+ * 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_INFORMATION0xE6
+#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_LEN8
+
+#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)
+{
+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:  

[PATCH v3 6/8] tests/qtest: add tests for ADM1266

2023-10-23 Thread Titus Rwantare
  The ADM1266 can have string fields written by the driver, so
  it's worth specifically testing.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 tests/qtest/adm1266-test.c | 123 +
 tests/qtest/meson.build|   1 +
 2 files changed, 124 insertions(+)
 create mode 100644 tests/qtest/adm1266-test.c

diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c
new file mode 100644
index 00..6431a21de6
--- /dev/null
+++ b/tests/qtest/adm1266-test.c
@@ -0,0 +1,123 @@
+/*
+ * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and
+ * Fault Recording with PMBus
+ *
+ * Copyright 2022 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include 
+#include "hw/i2c/pmbus_device.h"
+#include "libqtest-single.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qnum.h"
+#include "qemu/bitops.h"
+
+#define TEST_ID "adm1266-test"
+#define TEST_ADDR (0x12)
+
+#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_INFORMATION0xE6
+#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_LEN8
+#define TEST_STRING_A   "a sample"
+#define TEST_STRING_B   "b sample"
+#define TEST_STRING_C   "rev c"
+
+static void compare_string(QI2CDevice *i2cdev, uint8_t reg,
+   const char *test_str)
+{
+uint8_t len = i2c_get8(i2cdev, reg);
+char i2c_str[SMBUS_DATA_MAX_LEN] = {0};
+
+i2c_read_block(i2cdev, reg, (uint8_t *)i2c_str, len);
+g_assert_cmpstr(i2c_str, ==, test_str);
+}
+
+static void write_and_compare_string(QI2CDevice *i2cdev, uint8_t reg,
+ const char *test_str, uint8_t len)
+{
+char buf[SMBUS_DATA_MAX_LEN] = {0};
+buf[0] = len;
+strncpy(buf + 1, test_str, len);
+i2c_write_block(i2cdev, reg, (uint8_t *)buf, len + 1);
+compare_string(i2cdev, reg, test_str);
+}
+
+static void test_defaults(void *obj, void *data, QGuestAllocator *alloc)
+{
+uint16_t i2c_value;
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
+g_assert_cmphex(i2c_value, ==, ADM1266_OPERATION_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_REVISION);
+g_assert_cmphex(i2c_value, ==, ADM1266_PMBUS_REVISION_DEFAULT);
+
+compare_string(i2cdev, PMBUS_MFR_ID, ADM1266_MFR_ID_DEFAULT);
+compare_string(i2cdev, PMBUS_MFR_MODEL, ADM1266_MFR_MODEL_DEFAULT);
+compare_string(i2cdev, PMBUS_MFR_REVISION, ADM1266_MFR_REVISION_DEFAULT);
+}
+
+/* test r/w registers */
+static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc)
+{
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+/* empty strings */
+i2c_set8(i2cdev, PMBUS_MFR_ID, 0);
+compare_string(i2cdev, PMBUS_MFR_ID, "");
+
+i2c_set8(i2cdev, PMBUS_MFR_MODEL, 0);
+compare_string(i2cdev, PMBUS_MFR_MODEL, "");
+
+i2c_set8(i2cdev, PMBUS_MFR_REVISION, 0);
+compare_string(i2cdev, PMBUS_MFR_REVISION, "");
+
+/* test strings */
+write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_A,
+ sizeof(TEST_STRING_A));
+write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_B,
+ sizeof(TEST_STRING_B));
+write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_C,
+ sizeof(TEST_STRING_C));
+}
+
+static void adm1266_register_nodes(void)
+{
+QOSGraphEdgeOptions opts = {
+.extra_device_opts = "id=" TEST_ID ",address=0x12"
+};
+add_qi2c_address(, &(QI2CAddress) { TEST_ADDR });
+
+qos_node_create_driver("adm1266", i2c_device_create);
+qos_node_consumes("adm1266", "i2c-bus", );
+
+qos_add_test("test_defaults", "adm1266&qu

[PATCH v3 3/8] hw/i2c: pmbus: add fan support

2023-10-23 Thread Titus Rwantare
PMBus devices may integrate fans whose operation is configurable
over PMBus. This commit allows the driver to read and write the
fan control registers but does not model the operation of fans.

Reviewed-by: Stephen Longfield 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 176 ++
 include/hw/i2c/pmbus_device.h |   1 +
 2 files changed, 177 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index ea15490720..c1d8c93056 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -500,6 +500,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_FAN_CONFIG_1_2:/* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_1: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_1);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_2: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_CONFIG_3_4:/* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_3: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_3);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_4: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_4);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_VOUT_OV_FAULT_LIMIT:   /* R/W word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
@@ -810,6 +858,22 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
 break;
 
+case PMBUS_STATUS_FANS_1_2:   /* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_STATUS_FANS_3_4:   /* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_READ_EIN:  /* Read-Only block 5 bytes */
 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
@@ -882,6 +946,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_READ_FAN_SPEED_1:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_2:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_3:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_4:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_DUTY_CYCLE:   /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FREQUENCY:   

[PATCH v3 1/8] hw/i2c: pmbus add support for block receive

2023-10-23 Thread Titus Rwantare
PMBus devices can send and receive variable length data using the
block read and write format, with the first byte in the payload
denoting the length.

This is mostly used for strings and on-device logs. Devices can
respond to a block read with an empty string.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 30 +-
 include/hw/i2c/pmbus_device.h |  7 +++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index cef51663d0..ea15490720 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -102,7 +102,6 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 }
 
 size_t len = strlen(data);
-g_assert(len > 0);
 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
 pmdev->out_buf[len + pmdev->out_buf_len] = len;
 
@@ -112,6 +111,35 @@ void pmbus_send_string(PMBusDevice *pmdev, const char 
*data)
 pmdev->out_buf_len += len + 1;
 }
 
+uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len)
+{
+/* dest may contain data from previous writes */
+memset(dest, 0, len);
+
+/* Exclude command code from return value */
+pmdev->in_buf++;
+pmdev->in_buf_len--;
+
+/* The byte after the command code denotes the length */
+uint8_t sent_len = pmdev->in_buf[0];
+
+if (sent_len != pmdev->in_buf_len - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: length mismatch. Expected %d bytes, got %d bytes\n",
+  __func__, sent_len, pmdev->in_buf_len - 1);
+}
+
+/* exclude length byte */
+pmdev->in_buf++;
+pmdev->in_buf_len--;
+
+if (pmdev->in_buf_len < len) {
+len = pmdev->in_buf_len;
+}
+memcpy(dest, pmdev->in_buf, len);
+return len;
+}
+
 
 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
 {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 93f5d57c9d..7dc00cc4d9 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -501,6 +501,13 @@ void pmbus_send64(PMBusDevice *state, uint64_t data);
  */
 void pmbus_send_string(PMBusDevice *state, const char *data);
 
+/**
+ * @brief Receive data sent with Block Write.
+ * @param dest - memory with enough capacity to receive the write
+ * @param len - the capacity of dest
+ */
+uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len);
+
 /**
  * @brief Receive data over PMBus
  * These methods help track how much data is being received over PMBus
-- 
2.42.0.758.gaed0368e0e-goog




[PATCH v3 0/8] PMBus fixes and new functions

2023-10-23 Thread Titus Rwantare
This patch series contains fixes and improvements to PMBus support in QEMU.

The following has been added:
   - Support for block receive
   - Support for devices with fans
   - Support for the VCAP register for devices with onboard energy storage
   - A bitfield struct for the vout mode register, whose bits determine the 
formatting of several read commands in PMBus
Fixes:
   - String read now handles now logs an error when passed an empty string

This series is in preparation for some additional sensors that exercise
this functionality that will be incoming shortly.

Thanks

Changes in v3:
   - Added fixes to PMBus: page resets and fault clearing

Changes in v2:
   - Expanded commit descriptions
   - Added the ADM1266 device model that uses new functions

Titus Rwantare (8):
  hw/i2c: pmbus add support for block receive
  hw/i2c: pmbus: add vout mode bitfields
  hw/i2c: pmbus: add fan support
  hw/i2c: pmbus: add VCAP register
  hw/sensor: add ADM1266 device model
  tests/qtest: add tests for ADM1266
  hw/i2c: pmbus: immediately clear faults on request
  hw/i2c: pmbus: reset page register for out of range reads

 hw/arm/Kconfig|   1 +
 hw/i2c/pmbus_device.c | 237 +--
 hw/sensor/Kconfig |   5 +
 hw/sensor/adm1266.c   | 254 ++
 hw/sensor/meson.build |   1 +
 include/hw/i2c/pmbus_device.h |  17 +++
 tests/qtest/adm1266-test.c| 123 
 tests/qtest/max34451-test.c   |  24 
 tests/qtest/meson.build   |   1 +
 9 files changed, 653 insertions(+), 10 deletions(-)
 create mode 100644 hw/sensor/adm1266.c
 create mode 100644 tests/qtest/adm1266-test.c

--
2.42.0.758.gaed0368e0e-goog




[PATCH 2/7] hw/i2c: pmbus: add vout mode bitfields

2023-03-30 Thread Titus Rwantare
The VOUT_MODE command is described in the PMBus Specification,
Part II, Ver 1.3 Section 8.3

VOUT_MODE has a three bit mode and 4 bit parameter, the three bit
mode determines whether voltages are formatted as uint16, uint16,
VID, and Direct modes. VID and Direct modes use the remaining 5 bits
to scale the voltage readings.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 include/hw/i2c/pmbus_device.h | 8 
 1 file changed, 8 insertions(+)

diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 7dc00cc4d9..2e95164aa1 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -444,6 +444,14 @@ typedef struct PMBusCoefficients {
 int32_t R; /* exponent */
 } PMBusCoefficients;
 
+/**
+ * VOUT_Mode bit fields
+ */
+typedef struct PMBusVoutMode {
+uint8_t  mode:3;
+int8_t   exp:5;
+} PMBusVoutMode;
+
 /**
  * Convert sensor values to direct mode format
  *
-- 
2.40.0.423.gd6c402a77b-goog




[PATCH 5/7] hw/i2c: pmbus: add VCAP register

2023-03-30 Thread Titus Rwantare
VCAP is a register for devices with energy storage capacitors.

Reviewed-by: Benjamin Streb 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 8 
 include/hw/i2c/pmbus_device.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 18e629eaac..ef0314a913 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -903,6 +903,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_READ_VCAP: /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_VCAP) {
+pmbus_send16(pmdev, pmdev->pages[index].read_vcap);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_READ_VOUT: /* Read-Only word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index ad431bdc7c..f195c11384 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -243,6 +243,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 #define PB_HAS_VIN_RATING  BIT_ULL(13)
 #define PB_HAS_VOUT_RATING BIT_ULL(14)
 #define PB_HAS_VOUT_MODE   BIT_ULL(15)
+#define PB_HAS_VCAPBIT_ULL(16)
 #define PB_HAS_IOUTBIT_ULL(21)
 #define PB_HAS_IIN BIT_ULL(22)
 #define PB_HAS_IOUT_RATING BIT_ULL(23)
-- 
2.40.0.423.gd6c402a77b-goog




[PATCH 1/7] hw/i2c: pmbus add support for block receive

2023-03-30 Thread Titus Rwantare
PMBus devices can send and receive variable length data using the
block read and write format, with the first byte in the payload
denoting the length.

This is mostly used for strings and on-device logs. Devices can
respond to a block read with an empty string.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 30 +-
 include/hw/i2c/pmbus_device.h |  7 +++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index c3d6046784..02647769cd 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -95,7 +95,6 @@ void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 {
 size_t len = strlen(data);
-g_assert(len > 0);
 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
 pmdev->out_buf[len + pmdev->out_buf_len] = len;
 
@@ -105,6 +104,35 @@ void pmbus_send_string(PMBusDevice *pmdev, const char 
*data)
 pmdev->out_buf_len += len + 1;
 }
 
+uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len)
+{
+/* dest may contain data from previous writes */
+memset(dest, 0, len);
+
+/* Exclude command code from return value */
+pmdev->in_buf++;
+pmdev->in_buf_len--;
+
+/* The byte after the command code denotes the length */
+uint8_t sent_len = pmdev->in_buf[0];
+
+if (sent_len != pmdev->in_buf_len - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: length mismatch. Expected %d bytes, got %d bytes\n",
+  __func__, sent_len, pmdev->in_buf_len - 1);
+}
+
+/* exclude length byte */
+pmdev->in_buf++;
+pmdev->in_buf_len--;
+
+if (pmdev->in_buf_len < len) {
+len = pmdev->in_buf_len;
+}
+memcpy(dest, pmdev->in_buf, len);
+return len;
+}
+
 
 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
 {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 93f5d57c9d..7dc00cc4d9 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -501,6 +501,13 @@ void pmbus_send64(PMBusDevice *state, uint64_t data);
  */
 void pmbus_send_string(PMBusDevice *state, const char *data);
 
+/**
+ * @brief Receive data sent with Block Write.
+ * @param dest - memory with enough capacity to receive the write
+ * @param len - the capacity of dest
+ */
+uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len);
+
 /**
  * @brief Receive data over PMBus
  * These methods help track how much data is being received over PMBus
-- 
2.40.0.423.gd6c402a77b-goog




[PATCH 4/7] hw/i2c: pmbus: block uninitialised string reads

2023-03-30 Thread Titus Rwantare
Devices models calling pmbus_send_string can't be relied upon to
send a non-zero pointer. This logs an error and doesn't segfault.

Reviewed-by: Patrick Venture 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index bb42e410b4..18e629eaac 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -94,6 +94,13 @@ void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
 
 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 {
+if (!data) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: %s: uninitialised read from 0x%02x\n",
+  __func__, DEVICE(pmdev)->canonical_path, pmdev->code);
+return;
+}
+
 size_t len = strlen(data);
 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
 pmdev->out_buf[len + pmdev->out_buf_len] = len;
-- 
2.40.0.423.gd6c402a77b-goog




[PATCH 3/7] hw/i2c: pmbus: add fan support

2023-03-30 Thread Titus Rwantare
PMBus devices may integrate fans whose operation is configurable
over PMBus. This commit allows the driver to read and write the
fan control registers but does not model the operation of fans.

Reviewed-by: Stephen Longfield 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 176 ++
 include/hw/i2c/pmbus_device.h |   1 +
 2 files changed, 177 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 02647769cd..bb42e410b4 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -490,6 +490,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_FAN_CONFIG_1_2:/* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_1: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_1);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_2: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_CONFIG_3_4:/* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_3: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_3);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_4: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_4);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_VOUT_OV_FAULT_LIMIT:   /* R/W word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
@@ -800,6 +848,22 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
 break;
 
+case PMBUS_STATUS_FANS_1_2:   /* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_STATUS_FANS_3_4:   /* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_READ_EIN:  /* Read-Only block 5 bytes */
 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
@@ -872,6 +936,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_READ_FAN_SPEED_1:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_2:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_3:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_4:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_DUTY_CYCLE:   /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FREQUENCY:   

[PATCH 7/7] tests/qtest: add tests for ADM1266

2023-03-30 Thread Titus Rwantare
  The ADM1266 can have string fields written by the driver, so
  it's worth specifically testing.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 tests/qtest/adm1266-test.c | 123 +
 tests/qtest/meson.build|   1 +
 2 files changed, 124 insertions(+)
 create mode 100644 tests/qtest/adm1266-test.c

diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c
new file mode 100644
index 00..6431a21de6
--- /dev/null
+++ b/tests/qtest/adm1266-test.c
@@ -0,0 +1,123 @@
+/*
+ * Analog Devices ADM1266 Cascadable Super Sequencer with Margin Control and
+ * Fault Recording with PMBus
+ *
+ * Copyright 2022 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include 
+#include "hw/i2c/pmbus_device.h"
+#include "libqtest-single.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qnum.h"
+#include "qemu/bitops.h"
+
+#define TEST_ID "adm1266-test"
+#define TEST_ADDR (0x12)
+
+#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_INFORMATION0xE6
+#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_LEN8
+#define TEST_STRING_A   "a sample"
+#define TEST_STRING_B   "b sample"
+#define TEST_STRING_C   "rev c"
+
+static void compare_string(QI2CDevice *i2cdev, uint8_t reg,
+   const char *test_str)
+{
+uint8_t len = i2c_get8(i2cdev, reg);
+char i2c_str[SMBUS_DATA_MAX_LEN] = {0};
+
+i2c_read_block(i2cdev, reg, (uint8_t *)i2c_str, len);
+g_assert_cmpstr(i2c_str, ==, test_str);
+}
+
+static void write_and_compare_string(QI2CDevice *i2cdev, uint8_t reg,
+ const char *test_str, uint8_t len)
+{
+char buf[SMBUS_DATA_MAX_LEN] = {0};
+buf[0] = len;
+strncpy(buf + 1, test_str, len);
+i2c_write_block(i2cdev, reg, (uint8_t *)buf, len + 1);
+compare_string(i2cdev, reg, test_str);
+}
+
+static void test_defaults(void *obj, void *data, QGuestAllocator *alloc)
+{
+uint16_t i2c_value;
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
+g_assert_cmphex(i2c_value, ==, ADM1266_OPERATION_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_REVISION);
+g_assert_cmphex(i2c_value, ==, ADM1266_PMBUS_REVISION_DEFAULT);
+
+compare_string(i2cdev, PMBUS_MFR_ID, ADM1266_MFR_ID_DEFAULT);
+compare_string(i2cdev, PMBUS_MFR_MODEL, ADM1266_MFR_MODEL_DEFAULT);
+compare_string(i2cdev, PMBUS_MFR_REVISION, ADM1266_MFR_REVISION_DEFAULT);
+}
+
+/* test r/w registers */
+static void test_rw_regs(void *obj, void *data, QGuestAllocator *alloc)
+{
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+/* empty strings */
+i2c_set8(i2cdev, PMBUS_MFR_ID, 0);
+compare_string(i2cdev, PMBUS_MFR_ID, "");
+
+i2c_set8(i2cdev, PMBUS_MFR_MODEL, 0);
+compare_string(i2cdev, PMBUS_MFR_MODEL, "");
+
+i2c_set8(i2cdev, PMBUS_MFR_REVISION, 0);
+compare_string(i2cdev, PMBUS_MFR_REVISION, "");
+
+/* test strings */
+write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_A,
+ sizeof(TEST_STRING_A));
+write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_B,
+ sizeof(TEST_STRING_B));
+write_and_compare_string(i2cdev, PMBUS_MFR_ID, TEST_STRING_C,
+ sizeof(TEST_STRING_C));
+}
+
+static void adm1266_register_nodes(void)
+{
+QOSGraphEdgeOptions opts = {
+.extra_device_opts = "id=" TEST_ID ",address=0x12"
+};
+add_qi2c_address(, &(QI2CAddress) { TEST_ADDR });
+
+qos_node_create_driver("adm1266", i2c_device_create);
+qos_node_consumes("adm1266", "i2c-bus", );
+
+qos_add_test("test_defaults", "adm1266&qu

[PATCH 6/7] hw/sensor: add ADM1266 device model

2023-03-30 Thread Titus Rwantare
  The ADM1266 is a cascadable super sequencer with margin control and
  fault recording.
  This commit adds basic support for its PMBus commands and models
  the identification registers that can be modified in a firmware
  update.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/arm/Kconfig|   1 +
 hw/sensor/Kconfig |   5 +
 hw/sensor/adm1266.c   | 255 ++
 hw/sensor/meson.build |   1 +
 4 files changed, 262 insertions(+)
 create mode 100644 hw/sensor/adm1266.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b5aed4aff5..4e44a7451d 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -407,6 +407,7 @@ config XLNX_VERSAL
 config NPCM7XX
 bool
 select A9MPCORE
+select ADM1266
 select ADM1272
 select ARM_GIC
 select SMBUS
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index e03bd09b50..bc6331b4ab 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -22,6 +22,11 @@ config ADM1272
 bool
 depends on I2C
 
+config ADM1266
+bool
+depends on PMBUS
+default y if PMBUS
+
 config MAX34451
 bool
 depends on I2C
diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c
new file mode 100644
index 00..0745b12b1d
--- /dev/null
+++ b/hw/sensor/adm1266.c
@@ -0,0 +1,255 @@
+/*
+ * 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 
+#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_INFORMATION0xE6
+#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_LEN8
+
+#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)
+{
+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

[PATCH 0/7] PMBus fixes and new functions

2023-03-30 Thread Titus Rwantare
This patch series contains fixes and improvements to PMBus support in QEMU.

The following has been added:
   - Support for block receive
   - Support for devices with fans
   - Support for the VCAP register for devices with onboard energy storage
   - A bitfield struct for the vout mode register, whose bits determine the 
formatting of several read commands in PMBus
Fixes:
   - String read now handles now logs an error when passed an empty string

This series is in preparation for some additional sensors that exercise
this functionality that will be incoming shortly.

Thanks

Changes in v2:
   - Expanded commit descriptions
   - Added the ADM1266 device model that uses new functions

Titus Rwantare (7):
  hw/i2c: pmbus add support for block receive
  hw/i2c: pmbus: add vout mode bitfields
  hw/i2c: pmbus: add fan support
  hw/i2c: pmbus: block uninitialised string reads
  hw/i2c: pmbus: add VCAP register
  hw/sensor: add ADM1266 device model
  tests/qtest: add tests for ADM1266

 hw/arm/Kconfig|   1 +
 hw/i2c/pmbus_device.c | 221 -
 hw/sensor/Kconfig |   5 +
 hw/sensor/adm1266.c   | 255 ++
 hw/sensor/meson.build |   1 +
 include/hw/i2c/pmbus_device.h |  17 +++
 tests/qtest/adm1266-test.c| 123 
 tests/qtest/meson.build   |   1 +
 8 files changed, 623 insertions(+), 1 deletion(-)
 create mode 100644 hw/sensor/adm1266.c
 create mode 100644 tests/qtest/adm1266-test.c

-- 
2.40.0.423.gd6c402a77b-goog




Re: [PATCH 1/5] hw/i2c: pmbus add support for block receive

2023-03-30 Thread Titus Rwantare
Apologies. I've updated the commit descriptions and added a sensor using
this code in v2.

While doing block receive I discovered that it is valid behaviour to erase
a field and have it be an empty string.

-Titus

On Thu, 30 Mar 2023 at 09:18, Corey Minyard  wrote:

> It's generally frowned upon to have empty descriptions, some rationale
> would be helpful.  For instance, you remove a length check from the send
> string, why did you do that?
>
> Any why is this being added?  What's it supporting?
>
> -corey
>
> On Wed, Mar 22, 2023 at 05:55:09PM +, Titus Rwantare wrote:
> > Reviewed-by: Hao Wu 
> > Signed-off-by: Titus Rwantare 
> > ---
> >  hw/i2c/pmbus_device.c | 30 +-
> >  include/hw/i2c/pmbus_device.h |  7 +++
> >  2 files changed, 36 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
> > index c3d6046784..02647769cd 100644
> > --- a/hw/i2c/pmbus_device.c
> > +++ b/hw/i2c/pmbus_device.c
> > @@ -95,7 +95,6 @@ void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
> >  void pmbus_send_string(PMBusDevice *pmdev, const char *data)
> >  {
> >  size_t len = strlen(data);
> > -g_assert(len > 0);
> >  g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
> >  pmdev->out_buf[len + pmdev->out_buf_len] = len;
> >
> > @@ -105,6 +104,35 @@ void pmbus_send_string(PMBusDevice *pmdev, const
> char *data)
> >  pmdev->out_buf_len += len + 1;
> >  }
> >
> > +uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t
> len)
> > +{
> > +/* dest may contain data from previous writes */
> > +memset(dest, 0, len);
> > +
> > +/* Exclude command code from return value */
> > +pmdev->in_buf++;
> > +pmdev->in_buf_len--;
> > +
> > +/* The byte after the command code denotes the length */
> > +uint8_t sent_len = pmdev->in_buf[0];
> > +
> > +if (sent_len != pmdev->in_buf_len - 1) {
> > +qemu_log_mask(LOG_GUEST_ERROR,
> > +  "%s: length mismatch. Expected %d bytes, got %d
> bytes\n",
> > +  __func__, sent_len, pmdev->in_buf_len - 1);
> > +}
> > +
> > +/* exclude length byte */
> > +pmdev->in_buf++;
> > +pmdev->in_buf_len--;
> > +
> > +if (pmdev->in_buf_len < len) {
> > +len = pmdev->in_buf_len;
> > +}
> > +memcpy(dest, pmdev->in_buf, len);
> > +return len;
> > +}
> > +
> >
> >  static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
> >  {
> > diff --git a/include/hw/i2c/pmbus_device.h
> b/include/hw/i2c/pmbus_device.h
> > index 93f5d57c9d..7dc00cc4d9 100644
> > --- a/include/hw/i2c/pmbus_device.h
> > +++ b/include/hw/i2c/pmbus_device.h
> > @@ -501,6 +501,13 @@ void pmbus_send64(PMBusDevice *state, uint64_t
> data);
> >   */
> >  void pmbus_send_string(PMBusDevice *state, const char *data);
> >
> > +/**
> > + * @brief Receive data sent with Block Write.
> > + * @param dest - memory with enough capacity to receive the write
> > + * @param len - the capacity of dest
> > + */
> > +uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t
> len);
> > +
> >  /**
> >   * @brief Receive data over PMBus
> >   * These methods help track how much data is being received over PMBus
> > --
> > 2.40.0.rc1.284.g88254d51c5-goog
> >
>


[PATCH 1/5] hw/i2c: pmbus add support for block receive

2023-03-22 Thread Titus Rwantare
Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 30 +-
 include/hw/i2c/pmbus_device.h |  7 +++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index c3d6046784..02647769cd 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -95,7 +95,6 @@ void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 {
 size_t len = strlen(data);
-g_assert(len > 0);
 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
 pmdev->out_buf[len + pmdev->out_buf_len] = len;
 
@@ -105,6 +104,35 @@ void pmbus_send_string(PMBusDevice *pmdev, const char 
*data)
 pmdev->out_buf_len += len + 1;
 }
 
+uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len)
+{
+/* dest may contain data from previous writes */
+memset(dest, 0, len);
+
+/* Exclude command code from return value */
+pmdev->in_buf++;
+pmdev->in_buf_len--;
+
+/* The byte after the command code denotes the length */
+uint8_t sent_len = pmdev->in_buf[0];
+
+if (sent_len != pmdev->in_buf_len - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: length mismatch. Expected %d bytes, got %d bytes\n",
+  __func__, sent_len, pmdev->in_buf_len - 1);
+}
+
+/* exclude length byte */
+pmdev->in_buf++;
+pmdev->in_buf_len--;
+
+if (pmdev->in_buf_len < len) {
+len = pmdev->in_buf_len;
+}
+memcpy(dest, pmdev->in_buf, len);
+return len;
+}
+
 
 static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
 {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 93f5d57c9d..7dc00cc4d9 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -501,6 +501,13 @@ void pmbus_send64(PMBusDevice *state, uint64_t data);
  */
 void pmbus_send_string(PMBusDevice *state, const char *data);
 
+/**
+ * @brief Receive data sent with Block Write.
+ * @param dest - memory with enough capacity to receive the write
+ * @param len - the capacity of dest
+ */
+uint8_t pmbus_receive_block(PMBusDevice *pmdev, uint8_t *dest, size_t len);
+
 /**
  * @brief Receive data over PMBus
  * These methods help track how much data is being received over PMBus
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH 4/5] hw/i2c: pmbus: block uninitialised string reads

2023-03-22 Thread Titus Rwantare
Devices models calling pmbus_send_string can't be relied upon to
send a non-zero pointer. This logs an error and doesn't segfault.

Reviewed-by: Patrick Venture 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index bb42e410b4..18e629eaac 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -94,6 +94,13 @@ void pmbus_send64(PMBusDevice *pmdev, uint64_t data)
 
 void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 {
+if (!data) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: %s: uninitialised read from 0x%02x\n",
+  __func__, DEVICE(pmdev)->canonical_path, pmdev->code);
+return;
+}
+
 size_t len = strlen(data);
 g_assert(len + pmdev->out_buf_len < SMBUS_DATA_MAX_LEN);
 pmdev->out_buf[len + pmdev->out_buf_len] = len;
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH 3/5] hw/i2c: pmbus: add fan support

2023-03-22 Thread Titus Rwantare
Reviewed-by: Stephen Longfield 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 176 ++
 include/hw/i2c/pmbus_device.h |   1 +
 2 files changed, 177 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 02647769cd..bb42e410b4 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -490,6 +490,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_FAN_CONFIG_1_2:/* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].fan_config_1_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_1: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_1);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_2: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_CONFIG_3_4:/* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].fan_config_3_4);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_3: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_3);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_FAN_COMMAND_4: /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].fan_command_4);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_VOUT_OV_FAULT_LIMIT:   /* R/W word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmbus_send16(pmdev, pmdev->pages[index].vout_ov_fault_limit);
@@ -800,6 +848,22 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
 break;
 
+case PMBUS_STATUS_FANS_1_2:   /* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].status_fans_1_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_STATUS_FANS_3_4:   /* R/W byte */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send8(pmdev, pmdev->pages[index].status_fans_3_4);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_READ_EIN:  /* Read-Only block 5 bytes */
 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
@@ -872,6 +936,54 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_READ_FAN_SPEED_1:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_1);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_2:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_2);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_3:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_3);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FAN_SPEED_4:  /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_fan_speed_4);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_DUTY_CYCLE:   /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_duty_cycle);
+} else {
+goto passthough;
+}
+break;
+
+case PMBUS_READ_FREQUENCY:/* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_FAN) {
+pmbus_send16(pmdev, pmdev->pages[index].read_frequency);
+} else {
+goto

[PATCH 5/5] hw/i2c: pmbus: add VCAP register

2023-03-22 Thread Titus Rwantare
VCAP is a register for devices with energy storage capacitors.

Reviewed-by: Benjamin Streb 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 8 
 include/hw/i2c/pmbus_device.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 18e629eaac..ef0314a913 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -903,6 +903,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_READ_VCAP: /* Read-Only word */
+if (pmdev->pages[index].page_flags & PB_HAS_VCAP) {
+pmbus_send16(pmdev, pmdev->pages[index].read_vcap);
+} else {
+goto passthough;
+}
+break;
+
 case PMBUS_READ_VOUT: /* Read-Only word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmbus_send16(pmdev, pmdev->pages[index].read_vout);
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index ad431bdc7c..f195c11384 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -243,6 +243,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 #define PB_HAS_VIN_RATING  BIT_ULL(13)
 #define PB_HAS_VOUT_RATING BIT_ULL(14)
 #define PB_HAS_VOUT_MODE   BIT_ULL(15)
+#define PB_HAS_VCAPBIT_ULL(16)
 #define PB_HAS_IOUTBIT_ULL(21)
 #define PB_HAS_IIN BIT_ULL(22)
 #define PB_HAS_IOUT_RATING BIT_ULL(23)
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH 0/5] PMBus fixes and new functions

2023-03-22 Thread Titus Rwantare
This patch series contains fixes and improvements to PMBus support in QEMU.

The following has been added:
   - Support for block receive
   - Support for devices with fans
   - Support for the VCAP register for devices with onboard energy storage
   - A bitfield struct for the vout mode register, whose bits determine the 
formatting of several read commands in PMBus
Fixes:
   - String read now handles now logs an error when passed an empty string

This series is in preparation for some additional sensors that exercise
this functionality that will be incoming shortly.

Thanks

Titus Rwantare (5):
  hw/i2c: pmbus add support for block receive
  hw/i2c: pmbus: add vout mode bitfields
  hw/i2c: pmbus: add fan support
  hw/i2c: pmbus: block uninitialised string reads
  hw/i2c: pmbus: add VCAP register

 hw/i2c/pmbus_device.c | 221 +-
 include/hw/i2c/pmbus_device.h |  17 +++
 2 files changed, 237 insertions(+), 1 deletion(-)

-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH 2/5] hw/i2c: pmbus: add vout mode bitfields

2023-03-22 Thread Titus Rwantare
Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 include/hw/i2c/pmbus_device.h | 8 
 1 file changed, 8 insertions(+)

diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 7dc00cc4d9..2e95164aa1 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -444,6 +444,14 @@ typedef struct PMBusCoefficients {
 int32_t R; /* exponent */
 } PMBusCoefficients;
 
+/**
+ * VOUT_Mode bit fields
+ */
+typedef struct PMBusVoutMode {
+uint8_t  mode:3;
+int8_t   exp:5;
+} PMBusVoutMode;
+
 /**
  * Convert sensor values to direct mode format
  *
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH v3 1/5] bitops.h: add deposit16 function

2023-03-20 Thread Titus Rwantare
Makes it more explicit that 16 bit values are being used

Signed-off-by: Titus Rwantare 
---
 include/qemu/bitops.h | 26 ++
 1 file changed, 26 insertions(+)

diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 03213ce952..887b8f8ce8 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -446,6 +446,32 @@ static inline int64_t sextract64(uint64_t value, int 
start, int length)
 return ((int64_t)(value << (64 - length - start))) >> (64 - length);
 }
 
+/**
+ * deposit16:
+ * @value: initial value to insert bit field into
+ * @start: the lowest bit in the bit field (numbered from 0)
+ * @length: the length of the bit field
+ * @fieldval: the value to insert into the bit field
+ *
+ * Deposit @fieldval into the 16 bit @value at the bit field specified
+ * by the @start and @length parameters, and return the modified
+ * @value. Bits of @value outside the bit field are not modified.
+ * Bits of @fieldval above the least significant @length bits are
+ * ignored. The bit field must lie entirely within the 16 bit word.
+ * It is valid to request that all 16 bits are modified (ie @length
+ * 16 and @start 0).
+ *
+ * Returns: the modified @value.
+ */
+static inline uint16_t deposit16(uint16_t value, int start, int length,
+ uint16_t fieldval)
+{
+uint16_t mask;
+assert(start >= 0 && length > 0 && length <= 16 - start);
+mask = (~0U >> (16 - length)) << start;
+return (value & ~mask) | ((fieldval << start) & mask);
+}
+
 /**
  * deposit32:
  * @value: initial value to insert bit field into
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH v3 3/5] hw/gpio: add PCA9536 i2c gpio expander

2023-03-20 Thread Titus Rwantare
This device has the same register layout as the pca9538, but 4 fewer
gpio pins. This commit lowers the number of pins initialised, and reuses
the pca9538 logic.

Reviewed-by: Philippe Mathieu-Daudé 
Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/gpio/pca_i2c_gpio.c | 18 ++
 include/hw/gpio/pca_i2c_gpio.h |  2 ++
 tests/lcitool/libvirt-ci   |  2 +-
 3 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
index 00ba343f95..14da58e5c4 100644
--- a/hw/gpio/pca_i2c_gpio.c
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -337,6 +337,19 @@ static void pca9538_gpio_class_init(ObjectClass *klass, 
void *data)
 k->send = pca953x_send;
 }
 
+static void pca9536_gpio_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
+
+dc->desc = "PCA9536 4-bit I/O expander";
+pc->num_pins = PCA9536_NUM_PINS;
+
+k->recv = pca953x_recv;
+k->send = pca953x_send;
+}
+
 static void pca_i2c_gpio_class_init(ObjectClass *klass, void *data)
 {
 DeviceClass *dc = DEVICE_CLASS(klass);
@@ -387,6 +400,11 @@ static const TypeInfo pca_gpio_types[] = {
 .parent = TYPE_PCA_I2C_GPIO,
 .class_init = pca9538_gpio_class_init,
 },
+{
+.name = TYPE_PCA9536_GPIO,
+.parent = TYPE_PCA_I2C_GPIO,
+.class_init = pca9536_gpio_class_init,
+},
 };
 
 DEFINE_TYPES(pca_gpio_types);
diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
index a4220105e8..deb4528065 100644
--- a/include/hw/gpio/pca_i2c_gpio.h
+++ b/include/hw/gpio/pca_i2c_gpio.h
@@ -20,6 +20,7 @@
 #define PCA_I2C_MAX_PINS 16
 #define PCA6416_NUM_PINS 16
 #define PCA9538_NUM_PINS 8
+#define PCA9536_NUM_PINS 4
 
 typedef struct PCAGPIOClass {
 I2CSlaveClass parent;
@@ -63,5 +64,6 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 
 #define TYPE_PCA6416_GPIO "pca6416"
 #define TYPE_PCA9538_GPIO "pca9538"
+#define TYPE_PCA9536_GPIO "pca9536"
 
 #endif
diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci
index e3eb28cf2e..232f41f160 16
--- a/tests/lcitool/libvirt-ci
+++ b/tests/lcitool/libvirt-ci
@@ -1 +1 @@
-Subproject commit e3eb28cf2e17fbcf7fe7e19505ee432b8ec5bbb5
+Subproject commit 232f41f160d4567b8c82dd52aa96c2bc3a5b75c1
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH v3 0/5] PCA I2C GPIO expanders

2023-03-20 Thread Titus Rwantare
This patch series contains a set of i2c GPIO expanders,
with support for 4, 8, and 16 GPIO connections.

The devices are configured as GPIO inputs by default, but can have pins
configured to be inputs with qmp commands.

For example, the following snippet in a board file for a system,
configures a 16 bit pca6416 to have pins 8-11 as inputs, then asserts
them.

dev = DEVICE(i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 3), 
"pca6416", 0x72));
object_property_set_uint(OBJECT(dev), "gpio_config", 0x0F00, _abort);
object_property_set_uint(OBJECT(dev), "gpio_input", 0x0F00, _abort);

We currently use these to test hardware presence and LEDs in simulation.

Thanks

Since v2:
- switched to extract / deposit API
- added deposit16 to bitops.h
- squashed PCA9538 patch into PCA6416 to use the same send and recv
  functions
- updated unit tests use asymmetric 16-bit test values
- add patch to imply I2C devices on NPCM7xx boards

Since v1:
- addressed comments
- fixed typos in commit messages

Titus Rwantare (5):
  bitops.h: add deposit16 function
  hw/gpio: add PCA953x i2c GPIO expanders
  hw/gpio: add PCA9536 i2c gpio expander
  hw/i2c: add canonical path to i2c event traces
  hw/arm: imply I2C_DEVICES on NPCM7xx

 hw/arm/Kconfig  |   1 +
 hw/gpio/Kconfig |   5 +
 hw/gpio/meson.build |   1 +
 hw/gpio/pca_i2c_gpio.c  | 410 
 hw/gpio/trace-events|   5 +
 hw/i2c/core.c   |   8 +-
 hw/i2c/trace-events |   2 +-
 include/hw/gpio/pca_i2c_gpio.h  |  69 ++
 include/qemu/bitops.h   |  26 ++
 roms/edk2   |   2 +-
 roms/openbios   |   2 +-
 roms/opensbi|   2 +-
 roms/seabios|   2 +-
 tests/qtest/meson.build |   1 +
 tests/qtest/pca_i2c_gpio-test.c | 188 +++
 15 files changed, 716 insertions(+), 8 deletions(-)
 create mode 100644 hw/gpio/pca_i2c_gpio.c
 create mode 100644 include/hw/gpio/pca_i2c_gpio.h
 create mode 100644 tests/qtest/pca_i2c_gpio-test.c

-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH v3 2/5] hw/gpio: add PCA953x i2c GPIO expanders

2023-03-20 Thread Titus Rwantare
The PCA6416 is an i2c device with 16 GPIO pins, the PCA9538 has 8 pins.

Reviewed-by: Philippe Mathieu-Daudé 
Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/gpio/Kconfig |   5 +
 hw/gpio/meson.build |   1 +
 hw/gpio/pca_i2c_gpio.c  | 392 
 hw/gpio/trace-events|   5 +
 include/hw/gpio/pca_i2c_gpio.h  |  67 ++
 roms/edk2   |   2 +-
 roms/openbios   |   2 +-
 roms/opensbi|   2 +-
 roms/seabios|   2 +-
 tests/lcitool/libvirt-ci|   2 +-
 tests/qtest/meson.build |   1 +
 tests/qtest/pca_i2c_gpio-test.c | 188 +++
 12 files changed, 664 insertions(+), 5 deletions(-)
 create mode 100644 hw/gpio/pca_i2c_gpio.c
 create mode 100644 include/hw/gpio/pca_i2c_gpio.h
 create mode 100644 tests/qtest/pca_i2c_gpio-test.c

diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index d2cf3accc8..80395af197 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -16,3 +16,8 @@ config GPIO_PWR
 
 config SIFIVE_GPIO
 bool
+
+config PCA_I2C_GPIO
+bool
+depends on I2C
+default y if I2C_DEVICES
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index b726e6d27a..1e5b602002 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: 
files('omap_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
+softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true: files('pca_i2c_gpio.c'))
diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
new file mode 100644
index 00..00ba343f95
--- /dev/null
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -0,0 +1,392 @@
+/*
+ * NXP PCA I2C GPIO Expanders
+ *
+ * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt 
output,
+ * reset, and configuration registers
+ *
+ * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
+ *
+ * Copyright 2023 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * To assert some input pins before boot, use the following in the board file 
of
+ * the machine:
+ *  object_property_set_uint(Object *obj, const char *name,
+ *   uint64_t value, Error **errp);
+ * specifying name as "gpio_config" and the value as a bitfield of the inputs
+ * e.g. for the pca6416, a value of 0xFFF0, configures pins 0-3 as outputs and
+ * 4-15 as inputs.
+ * Then using name "gpio_input" with value "0x0F00" would raise GPIOs 8-11.
+ *
+ * This value can also be set at runtime through qmp externally, or by
+ * writing to the config register using i2c. The guest driver should generally
+ * control the config register, but exposing it via qmp allows external 
testing.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/gpio/pca_i2c_gpio.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/visitor.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "trace.h"
+
+/*
+ * compare new_output to curr_output and update irq to match new_output
+ *
+ * The Input port registers (registers 0 and 1) reflect the incoming logic
+ * levels of the pins, regardless of whether the pin is defined as an input or
+ * an output by the Configuration register.
+ */
+static void pca_i2c_update_irqs(PCAGPIOState *ps)
+{
+PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(ps);
+uint16_t out_diff = ps->new_output ^ ps->curr_output;
+uint16_t in_diff = ps->new_input ^ ps->curr_input;
+uint16_t mask, pin_i;
+
+if (in_diff || out_diff) {
+for (int i = 0; i < pc->num_pins; i++) {
+mask = BIT(i);
+/* pin must be configured as an output to be set here */
+if (out_diff & ~ps->config & mask) {
+pin_i = mask & ps->new_output;
+qemu_set_irq(ps->output[i], pin_i > 0);
+ps->curr_output &= ~mask;
+ps->curr_output |= pin_i;
+}
+
+if (in_diff & mask) {
+ps->curr_input &= ~mask;
+ps->curr_input |= mask & ps->new_input;
+}
+}
+/* make diff = 0 */
+ps->new_input = ps->curr_input;
+}
+}
+
+static void pca_i2c_irq_handler(void *opaque, int n, int level)
+{
+PCAGPIOState *ps = opaque;
+PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(opaque);
+uint16_t mask = BIT(n);
+
+g_assert(n < pc->num_pins);
+g_assert(n >= 0);
+
+ps->new_input &= ~mask;
+
+if (level > 0) {
+ps->new_input

[PATCH v3 5/5] hw/arm: imply I2C_DEVICES on NPCM7xx

2023-03-20 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/arm/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b5aed4aff5..548c10d7fc 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -406,6 +406,7 @@ config XLNX_VERSAL
 
 config NPCM7XX
 bool
+imply I2C_DEVICES
 select A9MPCORE
 select ADM1272
 select ARM_GIC
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH v3 4/5] hw/i2c: add canonical path to i2c event traces

2023-03-20 Thread Titus Rwantare
Reviewed-by: Philippe Mathieu-Daudé 
Signed-off-by: Titus Rwantare 
---
 hw/i2c/core.c   | 8 +---
 hw/i2c/trace-events | 2 +-
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index bed594fe59..896da359f5 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -161,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t 
address,
start condition.  */
 
 if (sc->event) {
-trace_i2c_event(event == I2C_START_SEND ? "start" : "start_async",
+trace_i2c_event(DEVICE(s)->canonical_path,
+event == I2C_START_SEND ? "start" : "start_async",
 s->address);
 rv = sc->event(s, event);
 if (rv && !bus->broadcast) {
@@ -244,7 +245,7 @@ void i2c_end_transfer(I2CBus *bus)
 I2CSlave *s = node->elt;
 sc = I2C_SLAVE_GET_CLASS(s);
 if (sc->event) {
-trace_i2c_event("finish", s->address);
+trace_i2c_event(DEVICE(s)->canonical_path, "finish", s->address);
 sc->event(s, I2C_FINISH);
 }
 QLIST_REMOVE(node, next);
@@ -321,7 +322,8 @@ void i2c_nack(I2CBus *bus)
 QLIST_FOREACH(node, >current_devs, next) {
 sc = I2C_SLAVE_GET_CLASS(node->elt);
 if (sc->event) {
-trace_i2c_event("nack", node->elt->address);
+trace_i2c_event(DEVICE(node->elt)->canonical_path,
+"nack", node->elt->address);
 sc->event(node->elt, I2C_NACK);
 }
 }
diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
index 8e88aa24c1..f42a1ff539 100644
--- a/hw/i2c/trace-events
+++ b/hw/i2c/trace-events
@@ -9,7 +9,7 @@ bitbang_i2c_data(unsigned dat, unsigned clk, unsigned old_out, 
unsigned new_out)
 
 # core.c
 
-i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)"
+i2c_event(const char *id, const char *event, uint8_t address) "%s: 
%s(addr:0x%02x)"
 i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x"
 i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) 
data:0x%02x"
 i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x"
-- 
2.40.0.rc1.284.g88254d51c5-goog




[PATCH v2 1/4] hw/gpio: add PCA6416 i2c GPIO expander

2023-02-08 Thread Titus Rwantare
The PCA6416 is an i2c device with 16 GPIOs.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/arm/Kconfig  |   1 +
 hw/gpio/Kconfig |   4 +
 hw/gpio/meson.build |   1 +
 hw/gpio/pca_i2c_gpio.c  | 388 
 hw/gpio/trace-events|   5 +
 include/hw/gpio/pca_i2c_gpio.h  |  69 ++
 tests/lcitool/libvirt-ci|   2 +-
 tests/qtest/meson.build |   1 +
 tests/qtest/pca_i2c_gpio-test.c | 169 ++
 9 files changed, 639 insertions(+), 1 deletion(-)
 create mode 100644 hw/gpio/pca_i2c_gpio.c
 create mode 100644 include/hw/gpio/pca_i2c_gpio.h
 create mode 100644 tests/qtest/pca_i2c_gpio-test.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 2d157de9b8..1b533ddd76 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -418,6 +418,7 @@ config NPCM7XX
 select SSI
 select UNIMP
 select PCA954X
+select PCA_I2C_GPIO
 
 config FSL_IMX25
 bool
diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index d2cf3accc8..1c01d9744a 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -16,3 +16,7 @@ config GPIO_PWR
 
 config SIFIVE_GPIO
 bool
+
+config PCA_I2C_GPIO
+bool
+depends on I2C
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index b726e6d27a..1e5b602002 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: 
files('omap_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
+softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true: files('pca_i2c_gpio.c'))
diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
new file mode 100644
index 00..434a759453
--- /dev/null
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -0,0 +1,388 @@
+/*
+ * NXP PCA I2C GPIO Expanders
+ *
+ * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt 
output,
+ * reset, and configuration registers
+ *
+ * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
+ *
+ * Copyright 2023 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * To assert some input pins before boot, use the following in the board file 
of
+ * the machine:
+ *  object_property_set_uint(Object *obj, const char *name,
+ *   uint64_t value, Error **errp);
+ * specifying name as "gpio_config" and the value as a bitfield of the inputs
+ * e.g. for the pca6416, a value of 0xFFF0, configures pins 0-3 as outputs and
+ * 4-15 as inputs.
+ * Then using name "gpio_input" with value "0x0F00" would raise GPIOs 8-11.
+ *
+ * This value can also be set at runtime through qmp externally, or by
+ * writing to the config register using i2c. The guest driver should generally
+ * control the config register, but exposing it via qmp allows external 
testing.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/gpio/pca_i2c_gpio.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/visitor.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+/*
+ * compare new_output to curr_output and update irq to match new_output
+ *
+ * The Input port registers (registers 0 and 1) reflect the incoming logic
+ * levels of the pins, regardless of whether the pin is defined as an input or
+ * an output by the Configuration register.
+ */
+static void pca_i2c_update_irqs(PCAGPIOState *ps)
+{
+PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(ps);
+uint16_t out_diff = ps->new_output ^ ps->curr_output;
+uint16_t in_diff = ps->new_input ^ ps->curr_input;
+uint16_t mask, pin_i;
+
+if (in_diff || out_diff) {
+for (int i = 0; i < pc->num_pins; i++) {
+mask = BIT(i);
+/* pin must be configured as an output to be set here */
+if (out_diff & ~ps->config & mask) {
+pin_i = mask & ps->new_output;
+qemu_set_irq(ps->output[i], pin_i > 0);
+ps->curr_output &= ~mask;
+ps->curr_output |= pin_i;
+}
+
+if (in_diff & mask) {
+ps->curr_input &= ~mask;
+ps->curr_input |= mask & ps->new_input;
+}
+}
+/* make diff = 0 */
+ps->new_input = ps->curr_input;
+}
+}
+
+static void pca_i2c_irq_handler(void *opaque, int n, int level)
+{
+PCAGPIOState *ps = opaque;
+PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(opaque);
+uint16_t mask = BIT(n);
+
+g_assert(n < pc->num_pins);
+g_assert(n >= 0);
+
+ps->new_input &= ~mask;
+
+if (level > 0) {
+ps->ne

[PATCH v2 3/4] hw/gpio: add PCA9536 i2c gpio expander

2023-02-08 Thread Titus Rwantare
This device has the same register layout as the pca9538, but 4 fewer
gpio pins. This commit lowers the number of pins initialised, and reuses
the pca9538 logic.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/gpio/pca_i2c_gpio.c | 18 ++
 include/hw/gpio/pca_i2c_gpio.h |  2 ++
 2 files changed, 20 insertions(+)

diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
index fa69523556..8e30064990 100644
--- a/hw/gpio/pca_i2c_gpio.c
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -426,6 +426,19 @@ static void pca9538_gpio_class_init(ObjectClass *klass, 
void *data)
 k->send = pca9538_send;
 }
 
+static void pca9536_gpio_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
+
+dc->desc = "PCA9536 4-bit I/O expander";
+pc->num_pins = PCA9536_NUM_PINS;
+
+k->recv = pca9538_recv;
+k->send = pca9538_send;
+}
+
 static void pca_i2c_gpio_class_init(ObjectClass *klass, void *data)
 {
 DeviceClass *dc = DEVICE_CLASS(klass);
@@ -477,6 +490,11 @@ static const TypeInfo pca_gpio_types[] = {
 .parent = TYPE_PCA_I2C_GPIO,
 .class_init = pca9538_gpio_class_init,
 },
+{
+.name = TYPE_PCA9536_GPIO,
+.parent = TYPE_PCA_I2C_GPIO,
+.class_init = pca9536_gpio_class_init,
+},
 };
 
 DEFINE_TYPES(pca_gpio_types);
diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
index 3ab7d19a97..6f8260a8c6 100644
--- a/include/hw/gpio/pca_i2c_gpio.h
+++ b/include/hw/gpio/pca_i2c_gpio.h
@@ -20,6 +20,7 @@
 #define PCA_I2C_MAX_PINS 16
 #define PCA6416_NUM_PINS 16
 #define PCA9538_NUM_PINS 8
+#define PCA9536_NUM_PINS 4
 
 typedef struct PCAGPIOClass {
 I2CSlaveClass parent;
@@ -72,5 +73,6 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 
 #define TYPE_PCA6416_GPIO "pca6416"
 #define TYPE_PCA9538_GPIO "pca9538"
+#define TYPE_PCA9536_GPIO "pca9536"
 
 #endif
-- 
2.39.1.581.gbfd45094c4-goog




[PATCH v2 2/4] hw/gpio: add PCA9538 8-bit GPIO expander

2023-02-08 Thread Titus Rwantare
  The 8-bit expander has different register offsets than the 16-bit one,
  making them incompatible.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/gpio/pca_i2c_gpio.c | 94 ++
 include/hw/gpio/pca_i2c_gpio.h |  7 +++
 2 files changed, 101 insertions(+)

diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
index 434a759453..fa69523556 100644
--- a/hw/gpio/pca_i2c_gpio.c
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -143,6 +143,41 @@ static uint8_t pca6416_recv(I2CSlave *i2c)
 return data;
 }
 
+/* slave to master */
+static uint8_t pca9538_recv(I2CSlave *i2c)
+{
+PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+uint8_t data;
+
+switch (ps->command) {
+case PCA9538_INPUT_PORT:
+data = ps->curr_input;
+break;
+
+case PCA9538_OUTPUT_PORT:
+data = ps->new_output;
+break;
+
+case PCA9538_POLARITY_INVERSION_PORT:
+data = ps->polarity_inv;
+break;
+
+case PCA9538_CONFIGURATION_PORT:
+data = ps->config;
+break;
+
+default:
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: reading from unsupported register 0x%02x",
+  __func__, ps->command);
+data = 0xFF;
+break;
+}
+
+trace_pca_i2c_recv(DEVICE(ps)->canonical_path, ps->command, data);
+return data;
+}
+
 /* master to slave */
 static int pca6416_send(I2CSlave *i2c, uint8_t data)
 {
@@ -203,6 +238,47 @@ static int pca6416_send(I2CSlave *i2c, uint8_t data)
 return 0;
 }
 
+/* master to slave */
+static int pca9538_send(I2CSlave *i2c, uint8_t data)
+{
+PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+if (ps->i2c_cmd) {
+ps->command = data;
+ps->i2c_cmd = false;
+return 0;
+}
+
+trace_pca_i2c_send(DEVICE(ps)->canonical_path, ps->command, data);
+
+switch (ps->command) {
+case PCA9538_INPUT_PORT:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: writing to read only reg: 0x%02x",
+  __func__, ps->command);
+break;
+case PCA9538_OUTPUT_PORT:
+ps->new_output = data;
+break;
+
+case PCA9538_POLARITY_INVERSION_PORT:
+ps->polarity_inv = data;
+break;
+
+case PCA9538_CONFIGURATION_PORT:
+ps->config = data;
+break;
+
+default:
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: writing to unsupported register\n",
+  __func__);
+return -1;
+}
+
+pca_i2c_update_irqs(ps);
+
+return 0;
+}
+
 static int pca_i2c_event(I2CSlave *i2c, enum i2c_event event)
 {
 PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
@@ -337,6 +413,19 @@ static void pca6416_gpio_class_init(ObjectClass *klass, 
void *data)
 k->send = pca6416_send;
 }
 
+static void pca9538_gpio_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
+
+dc->desc = "PCA9538 8-bit I/O expander";
+pc->num_pins = PCA9538_NUM_PINS;
+
+k->recv = pca9538_recv;
+k->send = pca9538_send;
+}
+
 static void pca_i2c_gpio_class_init(ObjectClass *klass, void *data)
 {
 DeviceClass *dc = DEVICE_CLASS(klass);
@@ -383,6 +472,11 @@ static const TypeInfo pca_gpio_types[] = {
 .parent = TYPE_PCA_I2C_GPIO,
 .class_init = pca6416_gpio_class_init,
 },
+{
+.name = TYPE_PCA9538_GPIO,
+.parent = TYPE_PCA_I2C_GPIO,
+.class_init = pca9538_gpio_class_init,
+},
 };
 
 DEFINE_TYPES(pca_gpio_types);
diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
index 99322959e1..3ab7d19a97 100644
--- a/include/hw/gpio/pca_i2c_gpio.h
+++ b/include/hw/gpio/pca_i2c_gpio.h
@@ -19,6 +19,7 @@
 
 #define PCA_I2C_MAX_PINS 16
 #define PCA6416_NUM_PINS 16
+#define PCA9538_NUM_PINS 8
 
 typedef struct PCAGPIOClass {
 I2CSlaveClass parent;
@@ -62,8 +63,14 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 #define PCA6416_CONFIGURATION_PORT_0 0x06 /* read/write */
 #define PCA6416_CONFIGURATION_PORT_1 0x07 /* read/write */
 
+#define PCA9538_INPUT_PORT   0x00 /* read */
+#define PCA9538_OUTPUT_PORT  0x01 /* read/write */
+#define PCA9538_POLARITY_INVERSION_PORT  0x02 /* read/write */
+#define PCA9538_CONFIGURATION_PORT   0x03 /* read/write */
+
 #define PCA_I2C_CONFIG_DEFAULT   0
 
 #define TYPE_PCA6416_GPIO "pca6416"
+#define TYPE_PCA9538_GPIO "pca9538"
 
 #endif
-- 
2.39.1.581.gbfd45094c4-goog




[PATCH v2 0/4] PCA I2C GPIO expanders

2023-02-08 Thread Titus Rwantare
This patch series contains a set of i2c GPIO expanders,
with support for 4, 8, and 16 GPIO connections.

The devices are configured as GPIO *inputs by default, but can have pins
configured to be inputs with qmp commands.

For example, the following snippet in a board file for a system,
configures a 16 bit pca6416 to have pins 8-11 as inputs, then asserts
them.

dev = DEVICE(i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 3), 
"pca6416", 0x72));
object_property_set_uint(OBJECT(dev), "gpio_config", 0x0F00, _abort);
object_property_set_uint(OBJECT(dev), "gpio_input", 0x0F00, _abort);

We currently use these to test hardware presence and LEDs in simulation.

Thanks

Since v1:
- addressed comments
- fixed typos in commit messages

Titus Rwantare (4):
  hw/gpio: add PCA6416 i2c GPIO expander
  hw/gpio: add PCA9538 8-bit GPIO expander
  hw/gpio: add PCA9536 i2c gpio expander
  hw/i2c: add canonical path to i2c event traces

 hw/arm/Kconfig  |   1 +
 hw/gpio/Kconfig |   4 +
 hw/gpio/meson.build |   1 +
 hw/gpio/pca_i2c_gpio.c  | 500 
 hw/gpio/trace-events|   5 +
 hw/i2c/core.c   |   8 +-
 hw/i2c/trace-events |   2 +-
 include/hw/gpio/pca_i2c_gpio.h  |  78 +
 tests/lcitool/libvirt-ci|   2 +-
 tests/qtest/meson.build |   1 +
 tests/qtest/pca_i2c_gpio-test.c | 169 +++
 11 files changed, 766 insertions(+), 5 deletions(-)
 create mode 100644 hw/gpio/pca_i2c_gpio.c
 create mode 100644 include/hw/gpio/pca_i2c_gpio.h
 create mode 100644 tests/qtest/pca_i2c_gpio-test.c

-- 
2.39.1.581.gbfd45094c4-goog




[PATCH v2 4/4] hw/i2c: add canonical path to i2c event traces

2023-02-08 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/i2c/core.c   | 8 +---
 hw/i2c/trace-events | 2 +-
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index d4ba8146bf..c583c1497a 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -161,7 +161,8 @@ static int i2c_do_start_transfer(I2CBus *bus, uint8_t 
address,
start condition.  */
 
 if (sc->event) {
-trace_i2c_event(event == I2C_START_SEND ? "start" : "start_async",
+trace_i2c_event(DEVICE(s)->canonical_path,
+event == I2C_START_SEND ? "start" : "start_async",
 s->address);
 rv = sc->event(s, event);
 if (rv && !bus->broadcast) {
@@ -227,7 +228,7 @@ void i2c_end_transfer(I2CBus *bus)
 I2CSlave *s = node->elt;
 sc = I2C_SLAVE_GET_CLASS(s);
 if (sc->event) {
-trace_i2c_event("finish", s->address);
+trace_i2c_event(DEVICE(s)->canonical_path, "finish", s->address);
 sc->event(s, I2C_FINISH);
 }
 QLIST_REMOVE(node, next);
@@ -314,7 +315,8 @@ void i2c_nack(I2CBus *bus)
 QLIST_FOREACH(node, >current_devs, next) {
 sc = I2C_SLAVE_GET_CLASS(node->elt);
 if (sc->event) {
-trace_i2c_event("nack", node->elt->address);
+trace_i2c_event(DEVICE(node->elt)->canonical_path,
+"nack", node->elt->address);
 sc->event(node->elt, I2C_NACK);
 }
 }
diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
index 8e88aa24c1..f42a1ff539 100644
--- a/hw/i2c/trace-events
+++ b/hw/i2c/trace-events
@@ -9,7 +9,7 @@ bitbang_i2c_data(unsigned dat, unsigned clk, unsigned old_out, 
unsigned new_out)
 
 # core.c
 
-i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)"
+i2c_event(const char *id, const char *event, uint8_t address) "%s: 
%s(addr:0x%02x)"
 i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x"
 i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) 
data:0x%02x"
 i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x"
-- 
2.39.1.581.gbfd45094c4-goog




Re: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander

2023-02-08 Thread Titus Rwantare
On Mon, 6 Feb 2023 at 14:27, Corey Minyard  wrote:
>
> On Mon, Feb 06, 2023 at 07:49:34PM +, Titus Rwantare wrote:
> > This is a simple i2c device that allows i2c capable devices to have
> > GPIOs.
> >
> > Reviewed-by: Hao Wu 
> > Signed-off-by: Titus Rwantare 
> > ---
> >  hw/arm/Kconfig  |   1 +
> >  hw/gpio/meson.build |   1 +
> >  hw/gpio/pca_i2c_gpio.c  | 362 
> >  hw/gpio/trace-events|   5 +
> >  hw/i2c/Kconfig  |   4 +
> >  include/hw/gpio/pca_i2c_gpio.h  |  72 +++
> >  tests/qtest/meson.build |   1 +
> >  tests/qtest/pca_i2c_gpio-test.c | 169 +++
> >  8 files changed, 615 insertions(+)
> >  create mode 100644 hw/gpio/pca_i2c_gpio.c
> >  create mode 100644 include/hw/gpio/pca_i2c_gpio.h
> >  create mode 100644 tests/qtest/pca_i2c_gpio-test.c
> >
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index 2d157de9b8..1b533ddd76 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -418,6 +418,7 @@ config NPCM7XX
> >  select SSI
> >  select UNIMP
> >  select PCA954X
> > +select PCA_I2C_GPIO
> >
> >  config FSL_IMX25
> >  bool
> > diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
> > index b726e6d27a..1e5b602002 100644
> > --- a/hw/gpio/meson.build
> > +++ b/hw/gpio/meson.build
> > @@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: 
> > files('omap_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
> > +softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true: 
> > files('pca_i2c_gpio.c'))
> > diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
> > new file mode 100644
> > index 00..afae497a22
> > --- /dev/null
> > +++ b/hw/gpio/pca_i2c_gpio.c
> > @@ -0,0 +1,362 @@
> > +/*
> > + * NXP PCA I2C GPIO Expanders
> > + *
> > + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt 
> > output,
> > + * reset, and configuration registers
> > + *
> > + * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
> > + *
> > + * Copyright 2023 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * These devices, by default, are configured to input only. The 
> > configuration is
>
> Yout initial email set they are output only by default


Oops, over the course of writing this I changed my mind on what the
default should be.
I've now checked it's all consistent.

>
>
> > + * settable through qom/qmp, or i2c.To set some pins as inputs before 
> > boot, use
> > + * the following in the board file of the machine:
> > + *  object_property_set_uint(Object *obj, const char *name,
> > + *   uint64_t value, Error **errp);
> > + * specifying name as "gpio_config" and the value as a bitfield of the 
> > inputs
> > + * e.g. for the pca6416, a value of 0xFFF0, sets pins 0-3 as outputs and
> > + * 4-15 as inputs.
> > + * This value can also be set at runtime through qmp externally, or by
> > + * writing to the config register using i2c.
>
> When the real hardware comes up, can it be configured in some way
> before the software access it through I2C?  It seems odd to me that you
> have a qemu configuration for something that wouldn't normally be
> configurable by something at power up time.
>
> Philippe mentioned some things, too, but this looks good to me beyond
> that.
>
> -corey


Right, my intention is to not have an implicit dependency on the default
configuration, I've re-written this section to be less confusing.

Thanks for the review, I've sent v2.

-Titus



Re: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander

2023-02-08 Thread Titus Rwantare
On Mon, 6 Feb 2023 at 13:38, Philippe Mathieu-Daudé  wrote:
>
> Hi Titus,
>
> On 6/2/23 20:49, Titus Rwantare wrote:
> > This is a simple i2c device that allows i2c capable devices to have
> > GPIOs.
> >
> > Reviewed-by: Hao Wu 
> > Signed-off-by: Titus Rwantare 
> > ---
> >   hw/arm/Kconfig  |   1 +
> >   hw/gpio/meson.build |   1 +
> >   hw/gpio/pca_i2c_gpio.c  | 362 
> >   hw/gpio/trace-events|   5 +
> >   hw/i2c/Kconfig  |   4 +
> >   include/hw/gpio/pca_i2c_gpio.h  |  72 +++
> >   tests/qtest/meson.build |   1 +
> >   tests/qtest/pca_i2c_gpio-test.c | 169 +++
> >   8 files changed, 615 insertions(+)
> >   create mode 100644 hw/gpio/pca_i2c_gpio.c
> >   create mode 100644 include/hw/gpio/pca_i2c_gpio.h
> >   create mode 100644 tests/qtest/pca_i2c_gpio-test.c
>
>
> > +/* slave to master */
> > +static uint8_t pca6416_recv(I2CSlave *i2c)
> > +{
> > +PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
> > +uint8_t data;

> > +default:
> > +qemu_log_mask(LOG_GUEST_ERROR,
> > +  "%s: reading from unsupported register 0x%02x",
>
> Some of your qemu_log_mask() calls miss the trailing newline.


Fixed.

>
>
> > +static int pca_i2c_event(I2CSlave *i2c, enum i2c_event event)
> > +{
> > +PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
> > +
> > +switch (event) {
> > +case I2C_START_RECV:
> > +trace_pca_i2c_event(DEVICE(ps)->canonical_path, "START_RECV");
>
> Maybe add the canonical_path to trace_i2c_event() so this is useful
> for all I2C devices.

I've added a patch that does this.

>
>
> > +static void pca_i2c_realize(DeviceState *dev, Error **errp)
> > +{
> > +PCAGPIOState *ps = PCA_I2C_GPIO(dev);
> > +pca_i2c_update_irqs(ps);
>
> There is no reset() handler, is that on purpose?

No, I've added one.

>
> > +static void pca6416_gpio_class_init(ObjectClass *klass, void *data)
> > +{
> > +DeviceClass *dc = DEVICE_CLASS(klass);
> > +I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
> > +PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
> > +
> > +dc->desc = "PCA6416 16-bit I/O expander";
>
> Why not set these 3 ...:
>
> > +dc->realize = pca_i2c_realize;
> > +dc->vmsd = _pca_i2c_gpio;
> > +
> > +k->event = pca_i2c_event;
>
> ... in a base abstract class pca_i2c_gpio_class_init()?
>

Right, the code evolved. This makes more sense to do now.


>
> > diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
> > index 14886b35da..b59a79fddf 100644
> > --- a/hw/i2c/Kconfig
> > +++ b/hw/i2c/Kconfig
> > @@ -42,6 +42,10 @@ config PCA954X
> >   bool
> >   select I2C
> >
> > +config PCA_I2C_GPIO
> > +bool
> > +select I2C
>
> This should be 'depends on I2C'.
>
> Apparently various entries are incorrect in this file.
>
> Per docs/devel/kconfig.rst:
>
>Devices *depend on* the bus that they lie on, for example a PCI
>device would specify ``depends on PCI``.  An MMIO device will likely
>have no ``depends on`` directive.  Devices also *select* the buses
>that the device provides, for example a SCSI adapter would specify
>``select SCSI``.

I've moved the entry to hw/gpio and fixed it.

> > diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
> > new file mode 100644
> > index 00..a10897c0e0
> > --- /dev/null
> > +++ b/include/hw/gpio/pca_i2c_gpio.h
> > @@ -0,0 +1,72 @@
> > +/*
> > + * NXP PCA6416A
> > + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt 
> > output,
> > + * reset, and configuration registers
> > + *
> > + * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
> > + *
> > + * Note: Polarity inversion emulation not implemented
> > + *
> > + * Copyright 2021 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +#ifndef PCA_I2C_GPIO_H
> > +#define PCA_I2C_GPIO_H
> > +
> > +#include "hw/i2c/i2c.h"
> > +#include "qom/object.h"
> > +
> > +#define PCA6416_NUM_PINS 16
>
> ^ This is specific to TYPE_PCA6416_GPIO, and you set
> PCAGPIOClass::num_pins to it in pca6416_gpio_class_init(), ...
>
> > +
> > +typedef struct PCAGPIOClass {
> > +I2CSlaveClass parent;
> > +
> > +uint8_t num_pins;
> > 

Re: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander

2023-02-08 Thread Titus Rwantare
On Mon, 6 Feb 2023 at 17:29, Dong, Eddie  wrote:
> > -Original Message-
> > From: qemu-devel-bounces+eddie.dong=intel@nongnu.org  > devel-bounces+eddie.dong=intel@nongnu.org> On Behalf Of Titus
> > Rwantare
> > Sent: Monday, February 6, 2023 11:50 AM
> > To: peter.mayd...@linaro.org
> > Cc: qemu-...@nongnu.org; qemu-devel@nongnu.org; Titus Rwantare
> > ; Hao Wu 
> > Subject: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander
> >
> > This is a simple i2c device that allows i2c capable devices to have GPIOs.
>
> This patch is to implement a device model of I2C to GPIO for PCA_xxx, right?
> Or do you implement a general/common I2C to GPIO device?
> I think it is for the former one. In this case, the commit message is not 
> clear.

I've redone the commit message.

>
> >
> > Reviewed-by: Hao Wu 
> > Signed-off-by: Titus Rwantare 
> > ---
> >  hw/arm/Kconfig  |   1 +
> >  hw/gpio/meson.build |   1 +
> >  hw/gpio/pca_i2c_gpio.c  | 362 
> >  hw/gpio/trace-events|   5 +
> >  hw/i2c/Kconfig  |   4 +
> >  include/hw/gpio/pca_i2c_gpio.h  |  72 +++
> >  tests/qtest/meson.build |   1 +
> >  tests/qtest/pca_i2c_gpio-test.c | 169 +++
> >  8 files changed, 615 insertions(+)
> >  create mode 100644 hw/gpio/pca_i2c_gpio.c  create mode 100644
> > include/hw/gpio/pca_i2c_gpio.h  create mode 100644
> > tests/qtest/pca_i2c_gpio-test.c
> >
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index
> > 2d157de9b8..1b533ddd76 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -418,6 +418,7 @@ config NPCM7XX
> >  select SSI
> >  select UNIMP
> >  select PCA954X
> > +select PCA_I2C_GPIO
> >
> >  config FSL_IMX25
> >  bool
> > diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index
> > b726e6d27a..1e5b602002 100644
> > --- a/hw/gpio/meson.build
> > +++ b/hw/gpio/meson.build
> > @@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true:
> > files('omap_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true:
> > files('aspeed_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
> > +softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true:
> > +files('pca_i2c_gpio.c'))
> > diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c new file
> > mode 100644 index 00..afae497a22
> > --- /dev/null
> > +++ b/hw/gpio/pca_i2c_gpio.c
>
> Should this file be located under hw/i2c ?

I think there's a case to be made for either location. However, there
already exists an i2c device in hw/gpio.

> > @@ -0,0 +1,362 @@
> > +/*
> > + * NXP PCA I2C GPIO Expanders
> > + *
> > + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with
> > +interrupt output,
> > + * reset, and configuration registers
> > + *
> > + * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
> > + *
> > + * Copyright 2023 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * These devices, by default, are configured to input only. The
> > +configuration is
> > + * settable through qom/qmp, or i2c.To set some pins as inputs before
> > +boot, use
> > + * the following in the board file of the machine:
> > + *  object_property_set_uint(Object *obj, const char *name,
> > + *   uint64_t value, Error **errp);
> > + * specifying name as "gpio_config" and the value as a bitfield of the
> > +inputs
> > + * e.g. for the pca6416, a value of 0xFFF0, sets pins 0-3 as outputs
> > +and
> > + * 4-15 as inputs.
> > + * This value can also be set at runtime through qmp externally, or by
> > + * writing to the config register using i2c.
> > + *
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "hw/gpio/pca_i2c_gpio.h"
> > +#include "hw/irq.h"
> > +#include "hw/qdev-properties.h"
> > +#include "migration/vmstate.h"
> > +#include "qapi/visitor.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "trace.h"
> > +
> > +/*
> > + * compare new_output to curr_output and update irq to match
> > new_output
> > + *
> > + * The Input port registers (registers 0 and 1) reflect the inc

Re: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander

2023-02-08 Thread Titus Rwantare
On Mon, 6 Feb 2023 at 19:43, Joel Stanley  wrote:
>
> On Mon, 6 Feb 2023 at 19:51, Titus Rwantare  wrote:
> >
> > This is a simple i2c device that allows i2c capable devices to have
> > GPIOs.
>
> Nice.
>
> In Linux this is supported by a driver called pca953x.  Would it make
> sense to name your model similarly (both the file and the prefixes you
> use)?
>
> If we do that then it looks like other devices from the same family
> could be easily supported. (I'm not suggesting you do that work up
> front)

Thanks to NXP's naming, pca could mean anything, so I opted
for i2c_gpio.
For example, we use pca954{3,6,8} i2c muxes that are entirely unrelated.
Looking at the driver, most of the devices there seem like they would
work with this qemu model. Someone more familiar would need to
validate them.

> >  hw/gpio/pca_i2c_gpio.c  | 362 
> >  hw/gpio/trace-events|   5 +
> >  hw/i2c/Kconfig  |   4 +
> >  include/hw/gpio/pca_i2c_gpio.h  |  72 +++
> >  tests/qtest/meson.build |   1 +
> >  tests/qtest/pca_i2c_gpio-test.c | 169 +++
> >  8 files changed, 615 insertions(+)
> >  create mode 100644 hw/gpio/pca_i2c_gpio.c
> >  create mode 100644 include/hw/gpio/pca_i2c_gpio.h
> >  create mode 100644 tests/qtest/pca_i2c_gpio-test.c
> >
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index 2d157de9b8..1b533ddd76 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -418,6 +418,7 @@ config NPCM7XX
> >  select SSI
> >  select UNIMP
> >  select PCA954X
> > +select PCA_I2C_GPIO
> >
> >  config FSL_IMX25
> >  bool
> > diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
> > index b726e6d27a..1e5b602002 100644
> > --- a/hw/gpio/meson.build
> > +++ b/hw/gpio/meson.build
> > @@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: 
> > files('omap_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
> >  softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
> > +softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true: 
> > files('pca_i2c_gpio.c'))
> > diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
> > new file mode 100644
> > index 00..afae497a22
> > --- /dev/null
> > +++ b/hw/gpio/pca_i2c_gpio.c
> > @@ -0,0 +1,362 @@
> > +/*
> > + * NXP PCA I2C GPIO Expanders
> > + *
> > + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt 
> > output,
> > + * reset, and configuration registers
> > + *
> > + * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
>
> +1
>
> > + *
> > + * Copyright 2023 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * These devices, by default, are configured to input only. The 
> > configuration is
> > + * settable through qom/qmp, or i2c.To set some pins as inputs before 
> > boot, use
> > + * the following in the board file of the machine:
> > + *  object_property_set_uint(Object *obj, const char *name,
> > + *   uint64_t value, Error **errp);
> > + * specifying name as "gpio_config" and the value as a bitfield of the 
> > inputs
> > + * e.g. for the pca6416, a value of 0xFFF0, sets pins 0-3 as outputs and
> > + * 4-15 as inputs.
> > + * This value can also be set at runtime through qmp externally, or by
> > + * writing to the config register using i2c.
>
> Nice docs. I'm sure someone else will tell you if there's a better
> spot, but I like that you've written this down.

Thanks,
-Titus



[PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander

2023-02-06 Thread Titus Rwantare
This is a simple i2c device that allows i2c capable devices to have
GPIOs.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/arm/Kconfig  |   1 +
 hw/gpio/meson.build |   1 +
 hw/gpio/pca_i2c_gpio.c  | 362 
 hw/gpio/trace-events|   5 +
 hw/i2c/Kconfig  |   4 +
 include/hw/gpio/pca_i2c_gpio.h  |  72 +++
 tests/qtest/meson.build |   1 +
 tests/qtest/pca_i2c_gpio-test.c | 169 +++
 8 files changed, 615 insertions(+)
 create mode 100644 hw/gpio/pca_i2c_gpio.c
 create mode 100644 include/hw/gpio/pca_i2c_gpio.h
 create mode 100644 tests/qtest/pca_i2c_gpio-test.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 2d157de9b8..1b533ddd76 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -418,6 +418,7 @@ config NPCM7XX
 select SSI
 select UNIMP
 select PCA954X
+select PCA_I2C_GPIO
 
 config FSL_IMX25
 bool
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index b726e6d27a..1e5b602002 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true: 
files('omap_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
 softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
+softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true: files('pca_i2c_gpio.c'))
diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
new file mode 100644
index 00..afae497a22
--- /dev/null
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -0,0 +1,362 @@
+/*
+ * NXP PCA I2C GPIO Expanders
+ *
+ * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt 
output,
+ * reset, and configuration registers
+ *
+ * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
+ *
+ * Copyright 2023 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * These devices, by default, are configured to input only. The configuration 
is
+ * settable through qom/qmp, or i2c.To set some pins as inputs before boot, use
+ * the following in the board file of the machine:
+ *  object_property_set_uint(Object *obj, const char *name,
+ *   uint64_t value, Error **errp);
+ * specifying name as "gpio_config" and the value as a bitfield of the inputs
+ * e.g. for the pca6416, a value of 0xFFF0, sets pins 0-3 as outputs and
+ * 4-15 as inputs.
+ * This value can also be set at runtime through qmp externally, or by
+ * writing to the config register using i2c.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/gpio/pca_i2c_gpio.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/visitor.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+/*
+ * compare new_output to curr_output and update irq to match new_output
+ *
+ * The Input port registers (registers 0 and 1) reflect the incoming logic
+ * levels of the pins, regardless of whether the pin is defined as an input or
+ * an output by the Configuration register.
+ */
+static void pca_i2c_update_irqs(PCAGPIOState *ps)
+{
+PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(ps);
+uint16_t out_diff = ps->new_output ^ ps->curr_output;
+uint16_t in_diff = ps->new_input ^ ps->curr_input;
+uint16_t mask, pin_i;
+
+if (in_diff || out_diff) {
+for (int i = 0; i < pc->num_pins; i++) {
+mask = BIT(i);
+/* pin must be configured as an output to be set here */
+if (out_diff & ~ps->config & mask) {
+pin_i = mask & ps->new_output;
+qemu_set_irq(ps->output[i], pin_i > 0);
+ps->curr_output &= ~mask;
+ps->curr_output |= pin_i;
+}
+
+if (in_diff & mask) {
+ps->curr_input &= ~mask;
+ps->curr_input |= mask & ps->new_input;
+}
+}
+/* make diff = 0 */
+ps->new_input = ps->curr_input;
+}
+}
+
+static void pca_i2c_irq_handler(void *opaque, int n, int level)
+{
+PCAGPIOState *ps = opaque;
+PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(opaque);
+uint16_t mask = BIT(n);
+
+g_assert(n < pc->num_pins);
+g_assert(n >= 0);
+
+ps->new_input &= ~mask;
+
+if (level > 0) {
+ps->new_input |= BIT(n);
+}
+
+pca_i2c_update_irqs(ps);
+}
+
+/* slave to master */
+static uint8_t pca6416_recv(I2CSlave *i2c)
+{
+PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+uint8_t data;
+
+switch (ps->command) {
+case PCA6416_INPUT_PORT_0:
+data = ps->curr_input;
+break;
+
+case PCA6416_INPUT_PORT_1:
+data = ps->curr_input >&g

[PATCH 2/3] hw/gpio: add PCA9538 8-bit GPIO expander

2023-02-06 Thread Titus Rwantare
  The 8-bit expander has different register offsets than the 16-bit one,
  making them incompatible.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/gpio/pca_i2c_gpio.c | 98 ++
 include/hw/gpio/pca_i2c_gpio.h |  7 +++
 2 files changed, 105 insertions(+)

diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
index afae497a22..f77d8d7e84 100644
--- a/hw/gpio/pca_i2c_gpio.c
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -141,6 +141,41 @@ static uint8_t pca6416_recv(I2CSlave *i2c)
 return data;
 }
 
+/* slave to master */
+static uint8_t pca9538_recv(I2CSlave *i2c)
+{
+PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+uint8_t data;
+
+switch (ps->command) {
+case PCA9538_INPUT_PORT:
+data = ps->curr_input;
+break;
+
+case PCA9538_OUTPUT_PORT:
+data = ps->new_output;
+break;
+
+case PCA9538_POLARITY_INVERSION_PORT:
+data = ps->polarity_inv;
+break;
+
+case PCA9538_CONFIGURATION_PORT:
+data = ps->config;
+break;
+
+default:
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: reading from unsupported register 0x%02x",
+  __func__, ps->command);
+data = 0xFF;
+break;
+}
+
+trace_pca_i2c_recv(DEVICE(ps)->canonical_path, ps->command, data);
+return data;
+}
+
 /* master to slave */
 static int pca6416_send(I2CSlave *i2c, uint8_t data)
 {
@@ -201,6 +236,47 @@ static int pca6416_send(I2CSlave *i2c, uint8_t data)
 return 0;
 }
 
+/* master to slave */
+static int pca9538_send(I2CSlave *i2c, uint8_t data)
+{
+PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+if (ps->i2c_cmd) {
+ps->command = data;
+ps->i2c_cmd = false;
+return 0;
+}
+
+trace_pca_i2c_send(DEVICE(ps)->canonical_path, ps->command, data);
+
+switch (ps->command) {
+case PCA9538_INPUT_PORT:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: writing to read only reg: 0x%02x",
+  __func__, ps->command);
+break;
+case PCA9538_OUTPUT_PORT:
+ps->new_output = data;
+break;
+
+case PCA9538_POLARITY_INVERSION_PORT:
+ps->polarity_inv = data;
+break;
+
+case PCA9538_CONFIGURATION_PORT:
+ps->config = data;
+break;
+
+default:
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: writing to unsupported register\n",
+  __func__);
+return -1;
+}
+
+pca_i2c_update_irqs(ps);
+
+return 0;
+}
+
 static int pca_i2c_event(I2CSlave *i2c, enum i2c_event event)
 {
 PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
@@ -321,6 +397,23 @@ static void pca6416_gpio_class_init(ObjectClass *klass, 
void *data)
 pc->num_pins = PCA6416_NUM_PINS;
 }
 
+static void pca9538_gpio_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
+
+dc->desc = "PCA9538 8-bit I/O expander";
+dc->realize = pca_i2c_realize;
+dc->vmsd = _pca_i2c_gpio;
+
+k->event = pca_i2c_event;
+k->recv = pca9538_recv;
+k->send = pca9538_send;
+
+pc->num_pins = PCA9538_NUM_PINS;
+}
+
 static void pca_i2c_gpio_init(Object *obj)
 {
 PCAGPIOState *ps = PCA_I2C_GPIO(obj);
@@ -357,6 +450,11 @@ static const TypeInfo pca_gpio_types[] = {
 .parent = TYPE_PCA_I2C_GPIO,
 .class_init = pca6416_gpio_class_init,
 },
+{
+.name = TYPE_PCA9538_GPIO,
+.parent = TYPE_PCA_I2C_GPIO,
+.class_init = pca9538_gpio_class_init,
+},
 };
 
 DEFINE_TYPES(pca_gpio_types);
diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
index a10897c0e0..8cd268e8f0 100644
--- a/include/hw/gpio/pca_i2c_gpio.h
+++ b/include/hw/gpio/pca_i2c_gpio.h
@@ -18,6 +18,7 @@
 #include "qom/object.h"
 
 #define PCA6416_NUM_PINS 16
+#define PCA9538_NUM_PINS 8
 
 typedef struct PCAGPIOClass {
 I2CSlaveClass parent;
@@ -61,6 +62,11 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 #define PCA6416_CONFIGURATION_PORT_0 0x06 /* read/write */
 #define PCA6416_CONFIGURATION_PORT_1 0x07 /* read/write */
 
+#define PCA9538_INPUT_PORT   0x00 /* read */
+#define PCA9538_OUTPUT_PORT  0x01 /* read/write */
+#define PCA9538_POLARITY_INVERSION_PORT  0x02 /* read/write */
+#define PCA9538_CONFIGURATION_PORT   0x03 /* read/write */
+
 #define PCA6416_OUTPUT_DEFAULT   0x
 #define PCA6416_CONFIG_DEFAULT   0x
 
@@ -68,5 +74,6 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 #define PCA_I2C_CONFIG_DEFAULT   0x
 
 #define TYPE_PCA6416_GPIO "pca6416"
+#define TYPE_PCA9538_GPIO "pca9538"
 
 #endif
-- 
2.39.1.519.gcb327c4b5f-goog




[PATCH 3/3] hw/gpio: add PCA9536 i2c gpio expander

2023-02-06 Thread Titus Rwantare
This device has the same register layout as the pca9538, but 4 fewer
gpio pins. This commit lowers the number of pins intialised, and reuses
the pca9539 logic.

Reviewed-by: Hao Wu 
Signed-off-by: Titus Rwantare 
---
 hw/gpio/pca_i2c_gpio.c | 22 ++
 include/hw/gpio/pca_i2c_gpio.h |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
index f77d8d7e84..66dd1d3369 100644
--- a/hw/gpio/pca_i2c_gpio.c
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -414,6 +414,23 @@ static void pca9538_gpio_class_init(ObjectClass *klass, 
void *data)
 pc->num_pins = PCA9538_NUM_PINS;
 }
 
+static void pca9536_gpio_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
+
+dc->desc = "PCA9536 4-bit I/O expander";
+dc->realize = pca_i2c_realize;
+dc->vmsd = _pca_i2c_gpio;
+
+k->event = pca_i2c_event;
+k->recv = pca9538_recv;
+k->send = pca9538_send;
+
+pc->num_pins = PCA9536_NUM_PINS;
+}
+
 static void pca_i2c_gpio_init(Object *obj)
 {
 PCAGPIOState *ps = PCA_I2C_GPIO(obj);
@@ -455,6 +472,11 @@ static const TypeInfo pca_gpio_types[] = {
 .parent = TYPE_PCA_I2C_GPIO,
 .class_init = pca9538_gpio_class_init,
 },
+{
+.name = TYPE_PCA9536_GPIO,
+.parent = TYPE_PCA_I2C_GPIO,
+.class_init = pca9536_gpio_class_init,
+},
 };
 
 DEFINE_TYPES(pca_gpio_types);
diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
index 8cd268e8f0..3d2a88ba7b 100644
--- a/include/hw/gpio/pca_i2c_gpio.h
+++ b/include/hw/gpio/pca_i2c_gpio.h
@@ -19,6 +19,7 @@
 
 #define PCA6416_NUM_PINS 16
 #define PCA9538_NUM_PINS 8
+#define PCA9536_NUM_PINS 4
 
 typedef struct PCAGPIOClass {
 I2CSlaveClass parent;
@@ -75,5 +76,6 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 
 #define TYPE_PCA6416_GPIO "pca6416"
 #define TYPE_PCA9538_GPIO "pca9538"
+#define TYPE_PCA9536_GPIO "pca9536"
 
 #endif
-- 
2.39.1.519.gcb327c4b5f-goog




[PATCH 0/3] PCA I2C GPIO expanders

2023-02-06 Thread Titus Rwantare
This patch series contains a set of i2c GPIO expanders,
with support for 4, 8, and 16 GPIO connections.

The devices are configured as GPIO outputs by default, but can have pins
configured to be inputs with qmp commands.

For example, the following snippet in a board file for a system,
configures a 16 bit pca6416 to have pins 8-11 as inputs, then asserts
them.

dev = DEVICE(i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 3), 
"pca6416", 0x72));
object_property_set_uint(OBJECT(dev), "gpio_config", 0x0F00, _abort);
object_property_set_uint(OBJECT(dev), "gpio_input", 0x0F00, _abort);

We currently use these to test hardware presence and LEDs in simulation.

Thanks

Titus Rwantare (3):
  hw/gpio: add PCA6414 i2c GPIO expander
  hw/gpio: add PCA9538 8-bit GPIO expander
  hw/gpio: add PCA9536 i2c gpio expander

 hw/arm/Kconfig  |   1 +
 hw/gpio/meson.build |   1 +
 hw/gpio/pca_i2c_gpio.c  | 482 
 hw/gpio/trace-events|   5 +
 hw/i2c/Kconfig  |   4 +
 include/hw/gpio/pca_i2c_gpio.h  |  81 ++
 tests/qtest/meson.build |   1 +
 tests/qtest/pca_i2c_gpio-test.c | 169 +++
 8 files changed, 744 insertions(+)
 create mode 100644 hw/gpio/pca_i2c_gpio.c
 create mode 100644 include/hw/gpio/pca_i2c_gpio.h
 create mode 100644 tests/qtest/pca_i2c_gpio-test.c

-- 
2.39.1.519.gcb327c4b5f-goog




[RFC PATCH v2 3/3] hw/peci: add support for EndPointConfig reads

2022-09-13 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/peci/peci-client.c  | 63 ++
 hw/peci/peci-core.c| 44 +++--
 include/hw/peci/peci.h | 23 +++
 3 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/hw/peci/peci-client.c b/hw/peci/peci-client.c
index 2aa797b5f6..e54735bb53 100644
--- a/hw/peci/peci-client.c
+++ b/hw/peci/peci-client.c
@@ -23,6 +23,64 @@
 
 #define PECI_CLIENT_DEFAULT_TEMP 30
 
+/* TODO: move this out into a config */
+static const PECIEndPointConfig spr_config[] = {
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 0,
+.hdr.func = 2,
+.hdr.reg = 0xD4,
+.data = BIT(31)
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 0,
+.hdr.func = 2,
+.hdr.reg = 0xD0,
+.data = BIT(31) | BIT(30)
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x84,
+.data = 0x03FF
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x80,
+.data = 0x
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x84,
+.data = 0x03FF
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x80,
+.data = 0x
+},
+};
+
 static void peci_client_update_temps(PECIClientDevice *client)
 {
 uint8_t temp_cpu = 0;
@@ -115,7 +173,12 @@ PECIClientDevice *peci_add_client(PECIBus *bus,
 break;
 
 case FAM6_ICELAKE_X:
+client->revision = 0x40;
+break;
+
 case FAM6_SAPPHIRE_RAPIDS_X:
+client->endpoint_conf = spr_config;
+client->num_entries = sizeof(spr_config) / sizeof(spr_config[0]);
 client->revision = 0x40;
 client->ucode = 0x8c0004a0;
 break;
diff --git a/hw/peci/peci-core.c b/hw/peci/peci-core.c
index 8210bfa198..0650a03e2d 100644
--- a/hw/peci/peci-core.c
+++ b/hw/peci/peci-core.c
@@ -22,6 +22,47 @@
 #define PECI_FCS_OK 0
 #define PECI_FCS_ERR1
 
+static PECIEndPointHeader peci_fmt_end_pt_header(PECICmd *pcmd)
+{
+uint32_t val = pcmd->rx[7] | (pcmd->rx[8] << 8) | (pcmd->rx[9] << 16) |
+  (pcmd->rx[10] << 24);
+
+PECIEndPointHeader header = {
+.msg_type = pcmd->rx[1],
+.addr_type = pcmd->rx[5],
+.bus = (val >> 20) & 0xFF,
+.dev = (val >> 15) & 0x1F,
+.func = (val >> 12) & 0x7,
+.reg = val & 0xFFF,
+};
+
+return header;
+}
+
+static void peci_rd_endpoint_cfg(PECIClientDevice *client, PECICmd *pcmd)
+{
+PECIPkgCfg *resp = (PECIPkgCfg *)pcmd->tx;
+PECIEndPointHeader req = peci_fmt_end_pt_header(pcmd);
+PECIEndPointConfig const *c;
+
+if (client->endpoint_conf) {
+for (size_t i = 0; i < client->num_entries; i++) {
+c = >endpoint_conf[i];
+
+if (!memcmp(, >hdr, sizeof(PECIEndPointHeader))) {
+resp->data = c->data;
+resp->cc = PECI_DEV_CC_SUCCESS;
+return;
+}
+}
+}
+
+qemu_log_mask(LOG_UNIMP,
+  "%s: msg_type: 0x%x bus: %u, dev: %u, func: %u, reg: 0x%x\n",
+  __func__, req.msg_type, req.bus, req.dev, req.func, req.reg);
+
+}
+
 static void peci_rd_pkg_cfg(PECIClientDevice *client, PECICmd *pcmd)
 {
 PECIPkgCfg *resp = (PECIPkgCfg *)pcmd->tx;
@@ -153,8 +194,7 @@ int peci_handle_cmd(PECIBus *bus, PECICmd *pcmd)
 break;
 
 case PECI_CMD_RD_END_PT_CFG:
-qemu_log_mask(LOG_UNIMP, "%s: unimplemented CMD_RD_END_PT_CFG\n",
-  __func__);
+peci_rd_endpoint_cfg(client, pcmd);
 break;
 
 default:
diff --git a/include/hw/peci/peci.h b/include/hw/peci/peci.h
index 1a0abe65cd..3dcfe82245 100644
--- a/include/hw/peci/peci.h
+++ b/include/hw/peci/peci.h
@@ -112,6 +112,26 @@ typedef struct PECITempTarget {
 uint8_t tjmax;
 } PECITempTarget;
 
+typedef enum PECIEndPointType {
+LOCAL_PCI_CFG = 3,
+PCI_CFG,
+MMIO_BDF,
+} PECIEndPointType;
+
+typedef struct __attribute__ ((__packed__)) {
+PECIEndPointType msg_type;
+uint8_t addr_type;
+uint8_t bus;
+uint8_t dev;
+uint8_t func;
+uint16_t reg;
+} PECIEndPointHeader;
+
+typedef struct {
+PECIEndPointHeader

[RFC PATCH v2 2/3] hw/peci: add PECI support for NPCM7xx BMCs

2022-09-13 Thread Titus Rwantare
This allows BMC firmware for npcm7xx BMCs to talk to a PECI client
in qemu.

Signed-off-by: Titus Rwantare 
Reviewed-by: Patrick Venture 
Reviewed-by: Peter Delevoryas 
---
 MAINTAINERS|   1 +
 hw/arm/Kconfig |   1 +
 hw/arm/npcm7xx.c   |   9 ++
 hw/peci/meson.build|   1 +
 hw/peci/npcm7xx_peci.c | 204 +
 hw/peci/trace-events   |   5 +
 include/hw/arm/npcm7xx.h   |   2 +
 include/hw/peci/npcm7xx_peci.h |  37 ++
 8 files changed, 260 insertions(+)
 create mode 100644 hw/peci/npcm7xx_peci.c
 create mode 100644 include/hw/peci/npcm7xx_peci.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 14ab29679d..f11a31cf57 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3218,6 +3218,7 @@ S: Maintained
 F: hw/peci/peci-core.c
 F: hw/peci/peci-client.c
 F: include/hw/peci/peci.h
+F: hw/peci/npcm7xx_peci.c
 
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 15fa79afd3..cb38c6c88f 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -408,6 +408,7 @@ config NPCM7XX
 select SSI
 select UNIMP
 select PCA954X
+select PECI
 
 config FSL_IMX25
 bool
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index d85cc02765..d408dd7eb4 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -45,6 +45,7 @@
 #define NPCM7XX_CLK_BA  (0xf0801000)
 #define NPCM7XX_MC_BA   (0xf0824000)
 #define NPCM7XX_RNG_BA  (0xf000b000)
+#define NPCM7XX_PECI_BA (0xf010)
 
 /* USB Host modules */
 #define NPCM7XX_EHCI_BA (0xf0806000)
@@ -83,6 +84,7 @@ enum NPCM7xxInterrupt {
 NPCM7XX_UART1_IRQ,
 NPCM7XX_UART2_IRQ,
 NPCM7XX_UART3_IRQ,
+NPCM7XX_PECI_IRQ= 6,
 NPCM7XX_EMC1RX_IRQ  = 15,
 NPCM7XX_EMC1TX_IRQ,
 NPCM7XX_MMC_IRQ = 26,
@@ -445,6 +447,7 @@ static void npcm7xx_init(Object *obj)
 }
 
 object_initialize_child(obj, "mmc", >mmc, TYPE_NPCM7XX_SDHCI);
+object_initialize_child(obj, "peci", >peci, TYPE_NPCM7XX_PECI);
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -715,6 +718,12 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>mmc), 0,
 npcm7xx_irq(s, NPCM7XX_MMC_IRQ));
 
+ /* PECI */
+sysbus_realize(SYS_BUS_DEVICE(>peci), _abort);
+sysbus_mmio_map(SYS_BUS_DEVICE(>peci), 0, NPCM7XX_PECI_BA);
+sysbus_connect_irq(SYS_BUS_DEVICE(>peci), 0,
+   npcm7xx_irq(s, NPCM7XX_PECI_IRQ));
+
 create_unimplemented_device("npcm7xx.shm",  0xc0001000,   4 * KiB);
 create_unimplemented_device("npcm7xx.vdmx", 0xe080,   4 * KiB);
 create_unimplemented_device("npcm7xx.pcierc",   0xe100,  64 * KiB);
diff --git a/hw/peci/meson.build b/hw/peci/meson.build
index 01cfa95abe..ee033eb915 100644
--- a/hw/peci/meson.build
+++ b/hw/peci/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_PECI', if_true: files('peci-core.c', 
'peci-client.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_peci.c'))
diff --git a/hw/peci/npcm7xx_peci.c b/hw/peci/npcm7xx_peci.c
new file mode 100644
index 00..17a2642898
--- /dev/null
+++ b/hw/peci/npcm7xx_peci.c
@@ -0,0 +1,204 @@
+/*
+ * Nuvoton NPCM7xx PECI Module
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/peci/npcm7xx_peci.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+#define PECI_CTL_STS0
+#define PECI_CTL_STS_DONE_EN  BIT(6)
+#define PECI_CTL_STS_ABRT_ERR BIT(4)
+#define PECI_CTL_STS_CRC_ERR  BIT(3)
+#define PECI_CTL_STS_DONE BIT(1)
+#define PECI_CTL_STS_START_BUSY   BIT(0)
+#define PECI_RD_LENGTH  0x4
+#define PECI_ADDR   0x8
+#define PECI_CMD0xC
+#define PECI_CTL2   0x10
+#define PECI_WR_LENGTH  0x1C
+#define PECI_PDDR   0x2C
+#define PECI_DAT_INOUT(reg)(0x100 + (reg) * 4)
+
+static uint64_t npcm7xx_peci_read(void *opaque, hwaddr offset, unsigned size)
+{
+NPCM7xxPECIState *ps = NPCM7XX_PECI(opaque);
+uint8_t ret = 0;
+
+if (!ps->bus->num_clients) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: no peci clients added to board\n",
+  __func__);
+return 0;
+}
+
+qemu_irq_lower(ps->irq);
+
+switch (offset) {
+case PECI_CTL_STS:
+ret = ps->status;
+break;
+
+case PECI_RD_LENGTH:
+ret = ps->pcmd.rd_length;
+break;
+
+case PECI_ADDR:
+ret = ps->pcmd.addr;
+break;
+
+case PECI_CMD:
+ret = ps->pcmd.cmd;
+break;
+
+  

Re: [RFC PATCH 3/3] hw/peci: add support for EndPointConfig reads

2022-09-13 Thread Titus Rwantare
On Fri, 9 Sept 2022 at 12:48, Peter Delevoryas  wrote:
>
> On Tue, Sep 06, 2022 at 10:05:52PM +, Titus Rwantare wrote:
> > Signed-off-by: Titus Rwantare 
> > Reviewed-by: Hao Wu 
> > ---
...
> > +++ b/include/hw/peci/peci.h
> > @@ -112,6 +112,26 @@ typedef struct PECITempTarget {
> >  uint8_t tjmax;
> >  } PECITempTarget;
> >
> > +typedef enum PECIEndPtType {
> > +LOCAL_PCI_CFG = 3,
> > +PCI_CFG,
> > +MMIO_BDF,
> > +} PECIEndPtType;
> > +
> > +typedef struct __attribute__ ((__packed__)) {
> > +PECIEndPtType msg_type;
> > +uint8_t addr_type;
> > +uint8_t bus;
> > +uint8_t dev;
> > +uint8_t func;
> > +uint16_t reg;
> > +} PECIEndPtHeader;
> > +
> > +typedef struct {
> > +PECIEndPtHeader hdr;
> > +uint32_t data;
> > +} PECIEndPtConfig;
>
> I noticed the summary is "hw/peci: add support for EndPointConfig reads"
> but type definitions here use "EndPt", maybe they should be
> "PECIEndPoint*"? I don't think extending to Point is too long.

Fair, fixed in v2.


Titus



Re: [RFC PATCH 1/3] hw/peci: add initial support for PECI

2022-09-13 Thread Titus Rwantare
On Fri, 9 Sept 2022 at 12:58, Peter Delevoryas  wrote:

> > +/*
> > + * PECI Client device
> > + * Copyright 2021 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
>
> Not sure, but I think the SPDX license identifier is supposed to be in
> the first line? Maybe not though. I would have expected:
>

That's a Linux thing as far as I can tell. QEMU has it in the top comment.

>
> I'm curious if we really need the CPU family here, or if we could just
> base everything off the PECI version.
>
> The PECI specification doesn't mention the CPU family, does it? Or maybe
> it does.
>

I needed the family info anyway for RdPkgConfig() CPU ID, and thought it
would be more readable to specify that in the board file than the PECI version.
We tend to add new machines to QEMU by copying the config of an existing
machine, I think this way makes it more obvious that this is a field
that is changing.


Titus



Re: [RFC PATCH 0/3] Initial PECI bus support

2022-09-13 Thread Titus Rwantare
On Fri, 9 Sept 2022 at 12:54, Peter Delevoryas  wrote:
>
> On Tue, Sep 06, 2022 at 10:05:49PM +, Titus Rwantare wrote:
...
> >
> > This is something that can also be extended as other parameters arise that 
> > need
> > to differ between platforms. So far you can have have different CPUs, DIMM 
> > counts,
> > DIMM temperatures here. These fields can also be adjusted at runtime 
> > through qmp.
>
> That looks good to me, seems like the standard way to do it in QEMU.
>
> >
> > A lot of the registers are hard coded, see hw/peci/peci-client.c. I'd like 
> > to
> > gauge interest in what potential users would like to be adjustable at 
> > runtime.
> > I've not written QEMU models that read config files at runtime, something 
> > I'd
> > appreciate guidance on.
>
> This part I don't totally understand. I also barely know anything about
> PECI.
>
> Is the register location for things different between CPU generations?

Some things seem to move between generations and others don't move, someone at
Intel would know better than I do.



> If so (and I expect it probably is), why is there only a configuration
> for Sapphire Rapids, and not for the other ones?
>
> Is that because of PECI protocol changes between generations?

I haven't dug into the other machines because of internal demand, but
I've found that
with newer generations, more features get used in addition to existing
ones. It's
possible these features existed on older machines.



> In which case, maybe there needs to be a notion of PECI version
> somewhere?
>
> Also, I don't understand why it would be adjustable at runtime, do we
> change register locations during execution?
>
> I would expect it to be part of the board definition.
>
> You could provide a bunch of sample configs for the CPU's that you're
> testing for, and the board configuration could just select the sample
> config it is using (corresponding to the CPU model).
>
> That's the model I would imagine, but I might be missing some important
> context here.

I think it would be nice to have additional registers at runtime, at
the time of writing,
I don't know how much of the internal workings of Sapphire Rapids
Intel is willing to
share publicly. If users are free to separately define registers, I
don't then get to
worry about this. e.g. I'd like to simulate errors from the memory controllers.



Titus



[RFC PATCH v2 0/3] Initial PECI bus support

2022-09-13 Thread Titus Rwantare
The Platform Environment Control Interface (PECI), is a way for Intel
processors to communicate with management controllers.

This series of patches simulate some PECI subsystem functionality. This
work is currently used against Nuvoton 7xx BMC, but it can easily be
extended to support Aspeed BMCs. Most of the functionality is derived
from PECI support in openbmc. See https://github.com/openbmc/libpeci

The main consumer of this work is openbmc, so functionality not
exercised by the openbmc/libpeci is unlikely to be present here.

peci-core.c is an attempt to split out functionality defined by the
spec. Anything that is not expected to change between BMC vendors.

The following commands have some support:
Ping()
GetDIB()
GetTemp()
~RdPkgConfig()
~RdEndPtConfig()

To be implemented:
RdIAMSR()
RdPCIConfig()
RdPCIConfigLocal()

Currently, in the board file during bmc_init() one may specify defaults
as follows:

static void my_machine_peci_init(NPCM7xxState *soc)
{
PECIBus *peci_bus = npcm7xx_peci_get_bus(soc);
DeviceState *dev;

/* per socket properties - both sockets are identical in this case */
PECIClientProperties peci_props = {
.cpu_family = FAM6_SAPPHIRE_RAPIDS_X,
.cpus = 56,
.dimms = 16
};

/* socket 0 - with example setting a few of the cpu and dimm temperatures 
in millidegrees */
dev = DEVICE(peci_add_client(peci_bus, 0x30, _props));
object_property_set_uint(OBJECT(dev), "cpu_temp[0]", 3, _abort);
object_property_set_uint(OBJECT(dev), "cpu_temp[2]", 35000, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[1]", 4, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[8]", 36000, _abort);

/* socket 1 */
dev = DEVICE(peci_add_client(peci_bus, 0x31, _props));
object_property_set_uint(OBJECT(dev), "cpu_temp[9]", 5, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[0]", 31000, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[14]", 36000, _abort);
...
}

This is something that can also be extended as other parameters arise that need
to differ between platforms. So far you can have have different CPUs, DIMM 
counts,
DIMM temperatures here. These fields can also be adjusted at runtime through 
qmp.

A lot of the registers are hard coded, see hw/peci/peci-client.c. I'd like to
gauge interest in what potential users would like to be adjustable at runtime.
I've not written QEMU models that read config files at runtime, something I'd
appreciate guidance on.

Thanks all

Changes in v2:
- rename EndPt to EndPoint

Titus Rwantare (3):
  hw/peci: add initial support for PECI
  hw/peci: add PECI support for NPCM7xx BMCs
  hw/peci: add support for EndPointConfig reads

 MAINTAINERS|   8 +
 hw/Kconfig |   1 +
 hw/arm/Kconfig |   1 +
 hw/arm/npcm7xx.c   |   9 +
 hw/meson.build |   1 +
 hw/peci/Kconfig|   2 +
 hw/peci/meson.build|   2 +
 hw/peci/npcm7xx_peci.c | 204 +++
 hw/peci/peci-client.c  | 293 +
 hw/peci/peci-core.c| 222 +
 hw/peci/trace-events   |  10 ++
 hw/peci/trace.h|   1 +
 include/hw/arm/npcm7xx.h   |   2 +
 include/hw/peci/npcm7xx_peci.h |  37 +
 include/hw/peci/peci.h | 217 
 meson.build|   1 +
 16 files changed, 1011 insertions(+)
 create mode 100644 hw/peci/Kconfig
 create mode 100644 hw/peci/meson.build
 create mode 100644 hw/peci/npcm7xx_peci.c
 create mode 100644 hw/peci/peci-client.c
 create mode 100644 hw/peci/peci-core.c
 create mode 100644 hw/peci/trace-events
 create mode 100644 hw/peci/trace.h
 create mode 100644 include/hw/peci/npcm7xx_peci.h
 create mode 100644 include/hw/peci/peci.h

-- 
2.37.3.968.ga6b4b080e4-goog




[RFC PATCH v2 1/3] hw/peci: add initial support for PECI

2022-09-13 Thread Titus Rwantare
PECI - Platform Environment Control Interface

This commit adds support for reading basic sensor values from a client
on the PECI bus.
BMCs can use the PECI wire to get thermal information out of an Intel
cpu. Additionally, on hardware, various MSRs are exposed over the
PECI bus. Part of PCI config space is exposed due to Intel posting
various platform configuration in PCI config space.

Commands implemented:
- Ping
- GetDIB
- GetTemp
- GetPkgConfig (partial)

Commands not implemented:
- RdIAMSR
- RdPCIConfig
- RdPCIConfigLocal

Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
Reviewed-by: Patrick Venture 
---
 MAINTAINERS|   7 ++
 hw/Kconfig |   1 +
 hw/meson.build |   1 +
 hw/peci/Kconfig|   2 +
 hw/peci/meson.build|   1 +
 hw/peci/peci-client.c  | 230 +
 hw/peci/peci-core.c| 182 
 hw/peci/trace-events   |   5 +
 hw/peci/trace.h|   1 +
 include/hw/peci/peci.h | 194 ++
 meson.build|   1 +
 11 files changed, 625 insertions(+)
 create mode 100644 hw/peci/Kconfig
 create mode 100644 hw/peci/meson.build
 create mode 100644 hw/peci/peci-client.c
 create mode 100644 hw/peci/peci-core.c
 create mode 100644 hw/peci/trace-events
 create mode 100644 hw/peci/trace.h
 create mode 100644 include/hw/peci/peci.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5ce4227ff6..14ab29679d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3212,6 +3212,13 @@ F: tests/qtest/adm1272-test.c
 F: tests/qtest/max34451-test.c
 F: tests/qtest/isl_pmbus_vr-test.c
 
+PECI
+M: Titus Rwantare 
+S: Maintained
+F: hw/peci/peci-core.c
+F: hw/peci/peci-client.c
+F: include/hw/peci/peci.h
+
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
 R: Daniel P. Berrange 
diff --git a/hw/Kconfig b/hw/Kconfig
index 38233bbb0f..300ab48127 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -24,6 +24,7 @@ source net/Kconfig
 source nubus/Kconfig
 source nvme/Kconfig
 source nvram/Kconfig
+source peci/Kconfig
 source pci-bridge/Kconfig
 source pci-host/Kconfig
 source pcmcia/Kconfig
diff --git a/hw/meson.build b/hw/meson.build
index c7ac7d3d75..340cc88a52 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -28,6 +28,7 @@ subdir('pci')
 subdir('pci-bridge')
 subdir('pci-host')
 subdir('pcmcia')
+subdir('peci')
 subdir('rdma')
 subdir('rtc')
 subdir('scsi')
diff --git a/hw/peci/Kconfig b/hw/peci/Kconfig
new file mode 100644
index 00..fe4f665d21
--- /dev/null
+++ b/hw/peci/Kconfig
@@ -0,0 +1,2 @@
+config PECI
+bool
diff --git a/hw/peci/meson.build b/hw/peci/meson.build
new file mode 100644
index 00..01cfa95abe
--- /dev/null
+++ b/hw/peci/meson.build
@@ -0,0 +1 @@
+softmmu_ss.add(when: 'CONFIG_PECI', if_true: files('peci-core.c', 
'peci-client.c'))
diff --git a/hw/peci/peci-client.c b/hw/peci/peci-client.c
new file mode 100644
index 00..2aa797b5f6
--- /dev/null
+++ b/hw/peci/peci-client.c
@@ -0,0 +1,230 @@
+/*
+ * PECI Client device
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/peci/peci.h"
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "qom/object.h"
+
+/*
+ * A PECI client represents an Intel socket and the peripherals attached to it
+ * that are accessible over the PECI bus.
+ */
+
+#define PECI_CLIENT_DEFAULT_TEMP 30
+
+static void peci_client_update_temps(PECIClientDevice *client)
+{
+uint8_t temp_cpu = 0;
+for (size_t i = 0; i < client->cpu_cores; i++) {
+if (temp_cpu < client->core_temp[i]) {
+temp_cpu = client->core_temp[i];
+}
+}
+client->core_temp_max = -1 * (client->tjmax - temp_cpu);
+
+uint8_t temp_dimm = 0;
+for (size_t i = 0; i < client->dimms; i++) {
+if (temp_dimm < client->dimm_temp[i]) {
+temp_dimm = client->dimm_temp[i];
+}
+}
+client->dimm_temp_max = temp_dimm;
+}
+
+PECIClientDevice *peci_get_client(PECIBus *bus, uint8_t addr)
+{
+PECIClientDevice *client;
+BusChild *var;
+
+QTAILQ_FOREACH(var, >qbus.children, sibling) {
+DeviceState *dev = var->child;
+client = PECI_CLIENT(dev);
+
+if (client->address == addr) {
+return client;
+}
+}
+return 0;
+}
+
+
+PECIClientDevice *peci_add_client(PECIBus *bus,
+  uint8_t address,
+  PECIClientProperties *props)
+{
+DeviceState *dev = qdev_new("peci-client");
+PECIClientDevice *client;
+
+/* Only 8 addresses supported as of rev 4.1, 0x30 to 0x37 */
+if (address < PECI_BA

[RFC PATCH 2/3] hw/peci: add PECI support for NPCM7xx BMCs

2022-09-06 Thread Titus Rwantare
This allows BMC firmware for npcm7xx BMCs to talk to a PECI client
in qemu.

Signed-off-by: Titus Rwantare 
Reviewed-by: Patrick Venture 
---
 MAINTAINERS|   3 +-
 hw/arm/Kconfig |   1 +
 hw/arm/npcm7xx.c   |   9 ++
 hw/peci/meson.build|   1 +
 hw/peci/npcm7xx_peci.c | 204 +
 hw/peci/trace-events   |   5 +
 include/hw/arm/npcm7xx.h   |   2 +
 include/hw/peci/npcm7xx_peci.h |  37 ++
 8 files changed, 261 insertions(+), 1 deletion(-)
 create mode 100644 hw/peci/npcm7xx_peci.c
 create mode 100644 include/hw/peci/npcm7xx_peci.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 14ab29679d..f87dfe5bfa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2959,7 +2959,7 @@ R: Paolo Bonzini 
 R: Bandan Das 
 R: Stefan Hajnoczi 
 R: Thomas Huth 
-R: Darren Kenny  
+R: Darren Kenny 
 R: Qiuhao Li 
 S: Maintained
 F: tests/qtest/fuzz/
@@ -3218,6 +3218,7 @@ S: Maintained
 F: hw/peci/peci-core.c
 F: hw/peci/peci-client.c
 F: include/hw/peci/peci.h
+F: hw/peci/npcm7xx_peci.c
 
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 15fa79afd3..cb38c6c88f 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -408,6 +408,7 @@ config NPCM7XX
 select SSI
 select UNIMP
 select PCA954X
+select PECI
 
 config FSL_IMX25
 bool
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index d85cc02765..d408dd7eb4 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -45,6 +45,7 @@
 #define NPCM7XX_CLK_BA  (0xf0801000)
 #define NPCM7XX_MC_BA   (0xf0824000)
 #define NPCM7XX_RNG_BA  (0xf000b000)
+#define NPCM7XX_PECI_BA (0xf010)
 
 /* USB Host modules */
 #define NPCM7XX_EHCI_BA (0xf0806000)
@@ -83,6 +84,7 @@ enum NPCM7xxInterrupt {
 NPCM7XX_UART1_IRQ,
 NPCM7XX_UART2_IRQ,
 NPCM7XX_UART3_IRQ,
+NPCM7XX_PECI_IRQ= 6,
 NPCM7XX_EMC1RX_IRQ  = 15,
 NPCM7XX_EMC1TX_IRQ,
 NPCM7XX_MMC_IRQ = 26,
@@ -445,6 +447,7 @@ static void npcm7xx_init(Object *obj)
 }
 
 object_initialize_child(obj, "mmc", >mmc, TYPE_NPCM7XX_SDHCI);
+object_initialize_child(obj, "peci", >peci, TYPE_NPCM7XX_PECI);
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -715,6 +718,12 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>mmc), 0,
 npcm7xx_irq(s, NPCM7XX_MMC_IRQ));
 
+ /* PECI */
+sysbus_realize(SYS_BUS_DEVICE(>peci), _abort);
+sysbus_mmio_map(SYS_BUS_DEVICE(>peci), 0, NPCM7XX_PECI_BA);
+sysbus_connect_irq(SYS_BUS_DEVICE(>peci), 0,
+   npcm7xx_irq(s, NPCM7XX_PECI_IRQ));
+
 create_unimplemented_device("npcm7xx.shm",  0xc0001000,   4 * KiB);
 create_unimplemented_device("npcm7xx.vdmx", 0xe080,   4 * KiB);
 create_unimplemented_device("npcm7xx.pcierc",   0xe100,  64 * KiB);
diff --git a/hw/peci/meson.build b/hw/peci/meson.build
index 01cfa95abe..ee033eb915 100644
--- a/hw/peci/meson.build
+++ b/hw/peci/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_PECI', if_true: files('peci-core.c', 
'peci-client.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_peci.c'))
diff --git a/hw/peci/npcm7xx_peci.c b/hw/peci/npcm7xx_peci.c
new file mode 100644
index 00..17a2642898
--- /dev/null
+++ b/hw/peci/npcm7xx_peci.c
@@ -0,0 +1,204 @@
+/*
+ * Nuvoton NPCM7xx PECI Module
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/peci/npcm7xx_peci.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+#define PECI_CTL_STS0
+#define PECI_CTL_STS_DONE_EN  BIT(6)
+#define PECI_CTL_STS_ABRT_ERR BIT(4)
+#define PECI_CTL_STS_CRC_ERR  BIT(3)
+#define PECI_CTL_STS_DONE BIT(1)
+#define PECI_CTL_STS_START_BUSY   BIT(0)
+#define PECI_RD_LENGTH  0x4
+#define PECI_ADDR   0x8
+#define PECI_CMD0xC
+#define PECI_CTL2   0x10
+#define PECI_WR_LENGTH  0x1C
+#define PECI_PDDR   0x2C
+#define PECI_DAT_INOUT(reg)(0x100 + (reg) * 4)
+
+static uint64_t npcm7xx_peci_read(void *opaque, hwaddr offset, unsigned size)
+{
+NPCM7xxPECIState *ps = NPCM7XX_PECI(opaque);
+uint8_t ret = 0;
+
+if (!ps->bus->num_clients) {
+qemu_log_mask(LOG_GUEST_ERROR, "%s: no peci clients added to board\n",
+  __func__);
+return 0;
+}
+
+qemu_irq_lower(ps->irq);
+
+switch (offset) {
+case PECI_CTL_STS:
+ret = ps->status;
+break;
+
+case PECI_RD_LENGTH:
+ret = ps->pcmd.rd_length;
+b

[RFC PATCH 3/3] hw/peci: add support for EndPointConfig reads

2022-09-06 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/peci/peci-client.c  | 63 ++
 hw/peci/peci-core.c| 44 +++--
 include/hw/peci/peci.h | 23 +++
 3 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/hw/peci/peci-client.c b/hw/peci/peci-client.c
index 2aa797b5f6..8d9248 100644
--- a/hw/peci/peci-client.c
+++ b/hw/peci/peci-client.c
@@ -23,6 +23,64 @@
 
 #define PECI_CLIENT_DEFAULT_TEMP 30
 
+/* TODO: move this out into a config */
+static const PECIEndPtConfig spr_config[] = {
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 0,
+.hdr.func = 2,
+.hdr.reg = 0xD4,
+.data = BIT(31)
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 0,
+.hdr.func = 2,
+.hdr.reg = 0xD0,
+.data = BIT(31) | BIT(30)
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x84,
+.data = 0x03FF
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x80,
+.data = 0x
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x84,
+.data = 0x03FF
+},
+{
+.hdr.msg_type = LOCAL_PCI_CFG,
+.hdr.addr_type = 0x4,
+.hdr.bus = 31,
+.hdr.dev = 30,
+.hdr.func = 6,
+.hdr.reg = 0x80,
+.data = 0x
+},
+};
+
 static void peci_client_update_temps(PECIClientDevice *client)
 {
 uint8_t temp_cpu = 0;
@@ -115,7 +173,12 @@ PECIClientDevice *peci_add_client(PECIBus *bus,
 break;
 
 case FAM6_ICELAKE_X:
+client->revision = 0x40;
+break;
+
 case FAM6_SAPPHIRE_RAPIDS_X:
+client->endpt_conf = spr_config;
+client->num_entries = sizeof(spr_config) / sizeof(spr_config[0]);
 client->revision = 0x40;
 client->ucode = 0x8c0004a0;
 break;
diff --git a/hw/peci/peci-core.c b/hw/peci/peci-core.c
index 8210bfa198..a961ae51f3 100644
--- a/hw/peci/peci-core.c
+++ b/hw/peci/peci-core.c
@@ -22,6 +22,47 @@
 #define PECI_FCS_OK 0
 #define PECI_FCS_ERR1
 
+static PECIEndPtHeader peci_fmt_end_pt_header(PECICmd *pcmd)
+{
+uint32_t val = pcmd->rx[7] | (pcmd->rx[8] << 8) | (pcmd->rx[9] << 16) |
+  (pcmd->rx[10] << 24);
+
+PECIEndPtHeader header = {
+.msg_type = pcmd->rx[1],
+.addr_type = pcmd->rx[5],
+.bus = (val >> 20) & 0xFF,
+.dev = (val >> 15) & 0x1F,
+.func = (val >> 12) & 0x7,
+.reg = val & 0xFFF,
+};
+
+return header;
+}
+
+static void peci_rd_endpt_cfg(PECIClientDevice *client, PECICmd *pcmd)
+{
+PECIPkgCfg *resp = (PECIPkgCfg *)pcmd->tx;
+PECIEndPtHeader req = peci_fmt_end_pt_header(pcmd);
+PECIEndPtConfig const *c;
+
+if (client->endpt_conf) {
+for (size_t i = 0; i < client->num_entries; i++) {
+c = >endpt_conf[i];
+
+if (!memcmp(, >hdr, sizeof(PECIEndPtHeader))) {
+resp->data = c->data;
+resp->cc = PECI_DEV_CC_SUCCESS;
+return;
+}
+}
+}
+
+qemu_log_mask(LOG_UNIMP,
+  "%s: msg_type: 0x%x bus: %u, dev: %u, func: %u, reg: 0x%x\n",
+  __func__, req.msg_type, req.bus, req.dev, req.func, req.reg);
+
+}
+
 static void peci_rd_pkg_cfg(PECIClientDevice *client, PECICmd *pcmd)
 {
 PECIPkgCfg *resp = (PECIPkgCfg *)pcmd->tx;
@@ -153,8 +194,7 @@ int peci_handle_cmd(PECIBus *bus, PECICmd *pcmd)
 break;
 
 case PECI_CMD_RD_END_PT_CFG:
-qemu_log_mask(LOG_UNIMP, "%s: unimplemented CMD_RD_END_PT_CFG\n",
-  __func__);
+peci_rd_endpt_cfg(client, pcmd);
 break;
 
 default:
diff --git a/include/hw/peci/peci.h b/include/hw/peci/peci.h
index 1a0abe65cd..4fb2fc236e 100644
--- a/include/hw/peci/peci.h
+++ b/include/hw/peci/peci.h
@@ -112,6 +112,26 @@ typedef struct PECITempTarget {
 uint8_t tjmax;
 } PECITempTarget;
 
+typedef enum PECIEndPtType {
+LOCAL_PCI_CFG = 3,
+PCI_CFG,
+MMIO_BDF,
+} PECIEndPtType;
+
+typedef struct __attribute__ ((__packed__)) {
+PECIEndPtType msg_type;
+uint8_t addr_type;
+uint8_t bus;
+uint8_t dev;
+uint8_t func;
+uint16_t reg;
+} PECIEndPtHeader;
+
+typedef struct {
+PECIEndPtHeader hdr;
+uint32_t data;
+} PECIEndPtConfig;
+

[RFC PATCH 0/3] Initial PECI bus support

2022-09-06 Thread Titus Rwantare
The Platform Environment Control Interface (PECI), is a way for Intel
processors to communicate with management controllers.

This series of patches simulate some PECI subsystem functionality. This
work is currently used against Nuvoton 7xx BMC, but it can easily be
extended to support Aspeed BMCs. Most of the functionality is derived
from PECI support in openbmc. See https://github.com/openbmc/libpeci

The main consumer of this work is openbmc, so functionality not
exercised by the openbmc/libpeci is unlikely to be present here.

peci-core.c is an attempt to split out functionality defined by the
spec. Anything that is not expected to change between BMC vendors.

The following commands have some support:
Ping()
GetDIB()
GetTemp()
~RdPkgConfig()
~RdEndPtConfig()

To be implemented:
RdIAMSR()
RdPCIConfig()
RdPCIConfigLocal()

Currently, in the board file during bmc_init() one may specify defaults
as follows:

static void my_machine_peci_init(NPCM7xxState *soc)
{
PECIBus *peci_bus = npcm7xx_peci_get_bus(soc);
DeviceState *dev;

/* per socket properties - both sockets are identical in this case */
PECIClientProperties peci_props = {
.cpu_family = FAM6_SAPPHIRE_RAPIDS_X,
.cpus = 56,
.dimms = 16
};

/* socket 0 - with example setting a few of the cpu and dimm temperatures 
in millidegrees */
dev = DEVICE(peci_add_client(peci_bus, 0x30, _props));
object_property_set_uint(OBJECT(dev), "cpu_temp[0]", 3, _abort);
object_property_set_uint(OBJECT(dev), "cpu_temp[2]", 35000, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[1]", 4, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[8]", 36000, _abort);

/* socket 1 */
dev = DEVICE(peci_add_client(peci_bus, 0x31, _props));
object_property_set_uint(OBJECT(dev), "cpu_temp[9]", 5, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[0]", 31000, _abort);
object_property_set_uint(OBJECT(dev), "dimm_temp[14]", 36000, _abort);
...
}

This is something that can also be extended as other parameters arise that need
to differ between platforms. So far you can have have different CPUs, DIMM 
counts,
DIMM temperatures here. These fields can also be adjusted at runtime through 
qmp.

A lot of the registers are hard coded, see hw/peci/peci-client.c. I'd like to
gauge interest in what potential users would like to be adjustable at runtime.
I've not written QEMU models that read config files at runtime, something I'd
appreciate guidance on.

Thanks all

Titus Rwantare (3):
  hw/peci: add initial support for PECI
  hw/peci: add PECI support for NPCM7xx BMCs
  hw/peci: add support for EndPointConfig reads

 MAINTAINERS|  10 +-
 hw/Kconfig |   1 +
 hw/arm/Kconfig |   1 +
 hw/arm/npcm7xx.c   |   9 +
 hw/meson.build |   1 +
 hw/peci/Kconfig|   2 +
 hw/peci/meson.build|   2 +
 hw/peci/npcm7xx_peci.c | 204 +++
 hw/peci/peci-client.c  | 293 +
 hw/peci/peci-core.c| 222 +
 hw/peci/trace-events   |  10 ++
 hw/peci/trace.h|   1 +
 include/hw/arm/npcm7xx.h   |   2 +
 include/hw/peci/npcm7xx_peci.h |  37 +
 include/hw/peci/peci.h | 217 
 meson.build|   1 +
 16 files changed, 1012 insertions(+), 1 deletion(-)
 create mode 100644 hw/peci/Kconfig
 create mode 100644 hw/peci/meson.build
 create mode 100644 hw/peci/npcm7xx_peci.c
 create mode 100644 hw/peci/peci-client.c
 create mode 100644 hw/peci/peci-core.c
 create mode 100644 hw/peci/trace-events
 create mode 100644 hw/peci/trace.h
 create mode 100644 include/hw/peci/npcm7xx_peci.h
 create mode 100644 include/hw/peci/peci.h

-- 
2.37.2.789.g6183377224-goog




[RFC PATCH 1/3] hw/peci: add initial support for PECI

2022-09-06 Thread Titus Rwantare
PECI - Platform Environment Control Interface

This commit adds support for reading basic sensor values from a client
on the PECI bus.
BMCs can use the PECI wire to get thermal information out of an Intel
cpu. Additionally, on hardware, various MSRs are exposed over the
PECI bus. Part of PCI config space is exposed due to Intel posting
various platform configuration in PCI config space.

Commands implemented:
- Ping
- GetDIB
- GetTemp
- GetPkgConfig (partial)

Commands not implemented:
- RdIAMSR
- RdPCIConfig
- RdPCIConfigLocal

Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
Reviewed-by: Patrick Venture 
---
 MAINTAINERS|   7 ++
 hw/Kconfig |   1 +
 hw/meson.build |   1 +
 hw/peci/Kconfig|   2 +
 hw/peci/meson.build|   1 +
 hw/peci/peci-client.c  | 230 +
 hw/peci/peci-core.c| 182 
 hw/peci/trace-events   |   5 +
 hw/peci/trace.h|   1 +
 include/hw/peci/peci.h | 194 ++
 meson.build|   1 +
 11 files changed, 625 insertions(+)
 create mode 100644 hw/peci/Kconfig
 create mode 100644 hw/peci/meson.build
 create mode 100644 hw/peci/peci-client.c
 create mode 100644 hw/peci/peci-core.c
 create mode 100644 hw/peci/trace-events
 create mode 100644 hw/peci/trace.h
 create mode 100644 include/hw/peci/peci.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 5ce4227ff6..14ab29679d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3212,6 +3212,13 @@ F: tests/qtest/adm1272-test.c
 F: tests/qtest/max34451-test.c
 F: tests/qtest/isl_pmbus_vr-test.c
 
+PECI
+M: Titus Rwantare 
+S: Maintained
+F: hw/peci/peci-core.c
+F: hw/peci/peci-client.c
+F: include/hw/peci/peci.h
+
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
 R: Daniel P. Berrange 
diff --git a/hw/Kconfig b/hw/Kconfig
index 38233bbb0f..300ab48127 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -24,6 +24,7 @@ source net/Kconfig
 source nubus/Kconfig
 source nvme/Kconfig
 source nvram/Kconfig
+source peci/Kconfig
 source pci-bridge/Kconfig
 source pci-host/Kconfig
 source pcmcia/Kconfig
diff --git a/hw/meson.build b/hw/meson.build
index c7ac7d3d75..340cc88a52 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -28,6 +28,7 @@ subdir('pci')
 subdir('pci-bridge')
 subdir('pci-host')
 subdir('pcmcia')
+subdir('peci')
 subdir('rdma')
 subdir('rtc')
 subdir('scsi')
diff --git a/hw/peci/Kconfig b/hw/peci/Kconfig
new file mode 100644
index 00..fe4f665d21
--- /dev/null
+++ b/hw/peci/Kconfig
@@ -0,0 +1,2 @@
+config PECI
+bool
diff --git a/hw/peci/meson.build b/hw/peci/meson.build
new file mode 100644
index 00..01cfa95abe
--- /dev/null
+++ b/hw/peci/meson.build
@@ -0,0 +1 @@
+softmmu_ss.add(when: 'CONFIG_PECI', if_true: files('peci-core.c', 
'peci-client.c'))
diff --git a/hw/peci/peci-client.c b/hw/peci/peci-client.c
new file mode 100644
index 00..2aa797b5f6
--- /dev/null
+++ b/hw/peci/peci-client.c
@@ -0,0 +1,230 @@
+/*
+ * PECI Client device
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/peci/peci.h"
+#include "hw/qdev-core.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "qom/object.h"
+
+/*
+ * A PECI client represents an Intel socket and the peripherals attached to it
+ * that are accessible over the PECI bus.
+ */
+
+#define PECI_CLIENT_DEFAULT_TEMP 30
+
+static void peci_client_update_temps(PECIClientDevice *client)
+{
+uint8_t temp_cpu = 0;
+for (size_t i = 0; i < client->cpu_cores; i++) {
+if (temp_cpu < client->core_temp[i]) {
+temp_cpu = client->core_temp[i];
+}
+}
+client->core_temp_max = -1 * (client->tjmax - temp_cpu);
+
+uint8_t temp_dimm = 0;
+for (size_t i = 0; i < client->dimms; i++) {
+if (temp_dimm < client->dimm_temp[i]) {
+temp_dimm = client->dimm_temp[i];
+}
+}
+client->dimm_temp_max = temp_dimm;
+}
+
+PECIClientDevice *peci_get_client(PECIBus *bus, uint8_t addr)
+{
+PECIClientDevice *client;
+BusChild *var;
+
+QTAILQ_FOREACH(var, >qbus.children, sibling) {
+DeviceState *dev = var->child;
+client = PECI_CLIENT(dev);
+
+if (client->address == addr) {
+return client;
+}
+}
+return 0;
+}
+
+
+PECIClientDevice *peci_add_client(PECIBus *bus,
+  uint8_t address,
+  PECIClientProperties *props)
+{
+DeviceState *dev = qdev_new("peci-client");
+PECIClientDevice *client;
+
+/* Only 8 addresses supported as of rev 4.1, 0x30 to 0x37 */
+if (address < PECI_BA

Re: [PATCH v3 09/14] hw/sensor: Add IC_DEVICE_ID to ISL voltage regulators

2022-06-30 Thread Titus Rwantare
On Wed, 29 Jun 2022 at 21:52, Peter Delevoryas  wrote:
>
> From: Peter Delevoryas 
>
> This commit adds a passthrough for PMBUS_IC_DEVICE_ID to allow Renesas
> voltage regulators to return the integrated circuit device ID if they
> would like to.
>
> The behavior is very device specific, so it hasn't been added to the
> general PMBUS model. Additionally, if the device ID hasn't been set,
> then the voltage regulator will respond with the error byte value.  The
> guest error message will change slightly for IC_DEVICE_ID with this
> commit.
>
> Signed-off-by: Peter Delevoryas 
> ---
>  hw/sensor/isl_pmbus_vr.c | 12 
>  include/hw/sensor/isl_pmbus_vr.h |  5 +
>  2 files changed, 17 insertions(+)
>
> diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
> index e11e028884..799ea9d89e 100644
> --- a/hw/sensor/isl_pmbus_vr.c
> +++ b/hw/sensor/isl_pmbus_vr.c
> @@ -15,6 +15,18 @@
>
>  static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev)
>  {
> +ISLState *s = ISL69260(pmdev);
> +
> +switch (pmdev->code) {
> +case PMBUS_IC_DEVICE_ID:
> +if (!s->ic_device_id_len) {
> +break;
> +}
> +pmbus_send(pmdev, s->ic_device_id, s->ic_device_id_len);
> +pmbus_idle(pmdev);
> +return 0;
> +}
> +
>  qemu_log_mask(LOG_GUEST_ERROR,
>"%s: reading from unsupported register: 0x%02x\n",
>__func__, pmdev->code);
> diff --git a/include/hw/sensor/isl_pmbus_vr.h 
> b/include/hw/sensor/isl_pmbus_vr.h
> index 3e47ff7e48..aa2c2767df 100644
> --- a/include/hw/sensor/isl_pmbus_vr.h
> +++ b/include/hw/sensor/isl_pmbus_vr.h
> @@ -12,12 +12,17 @@
>  #include "hw/i2c/pmbus_device.h"
>  #include "qom/object.h"
>
> +#define TYPE_ISL69259   "isl69259"
>  #define TYPE_ISL69260   "isl69260"
>  #define TYPE_RAA228000  "raa228000"
>  #define TYPE_RAA229004  "raa229004"
> +#define ISL_MAX_IC_DEVICE_ID_LEN 16
>
>  struct ISLState {
>  PMBusDevice parent;
> +
> +uint8_t ic_device_id[ISL_MAX_IC_DEVICE_ID_LEN];
> +uint8_t ic_device_id_len;
>  };
>
>  OBJECT_DECLARE_SIMPLE_TYPE(ISLState, ISL69260)
> --
> 2.37.0
>
Reviewed-by: Titus Rwantare 



Re: [PATCH v3 08/14] hw/i2c/pmbus: Add idle state to return 0xff's

2022-06-30 Thread Titus Rwantare
On Wed, 29 Jun 2022 at 21:52, Peter Delevoryas  wrote:
>
> From: Peter Delevoryas 
>
> Signed-off-by: Peter Delevoryas 
> ---
>  hw/i2c/pmbus_device.c | 9 +
>  include/hw/i2c/pmbus_device.h | 7 +++
>  2 files changed, 16 insertions(+)
>
> diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
> index 62885fa6a1..f89fea65f3 100644
> --- a/hw/i2c/pmbus_device.c
> +++ b/hw/i2c/pmbus_device.c
> @@ -261,6 +261,11 @@ void pmbus_check_limits(PMBusDevice *pmdev)
>  }
>  }
>
> +void pmbus_idle(PMBusDevice *pmdev)
> +{
> +pmdev->code = PMBUS_IDLE_STATE;
> +}
> +
>  /* assert the status_cml error upon receipt of malformed command */
>  static void pmbus_cml_error(PMBusDevice *pmdev)
>  {
> @@ -984,6 +989,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
>  }
>  break;
>
> +case PMBUS_IDLE_STATE:
> +pmbus_send8(pmdev, PMBUS_ERR_BYTE);
> +break;
> +
>  case PMBUS_CLEAR_FAULTS:  /* Send Byte */
>  case PMBUS_PAGE_PLUS_WRITE:   /* Block Write-only */
>  case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
> diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
> index 0f4d6b3fad..93f5d57c9d 100644
> --- a/include/hw/i2c/pmbus_device.h
> +++ b/include/hw/i2c/pmbus_device.h
> @@ -155,6 +155,7 @@ enum pmbus_registers {
>  PMBUS_MFR_MAX_TEMP_1= 0xC0, /* R/W word */
>  PMBUS_MFR_MAX_TEMP_2= 0xC1, /* R/W word */
>  PMBUS_MFR_MAX_TEMP_3= 0xC2, /* R/W word */
> +PMBUS_IDLE_STATE= 0xFF,
>  };
>
>  /* STATUS_WORD */
> @@ -527,6 +528,12 @@ int pmbus_page_config(PMBusDevice *pmdev, uint8_t 
> page_index, uint64_t flags);
>   */
>  void pmbus_check_limits(PMBusDevice *pmdev);
>
> +/**
> + * Enter an idle state where only the PMBUS_ERR_BYTE will be returned
> + * indefinitely until a new command is issued.
> + */
> +void pmbus_idle(PMBusDevice *pmdev);
> +
>  extern const VMStateDescription vmstate_pmbus_device;
>
>  #define VMSTATE_PMBUS_DEVICE(_field, _state) {   \
> --
> 2.37.0
>

Reviewed-by: Titus Rwantare 



Re: [PATCH v3 10/14] hw/sensor: Add Renesas ISL69259 device model

2022-06-30 Thread Titus Rwantare
On Wed, 29 Jun 2022 at 21:52, Peter Delevoryas  wrote:
>
> From: Peter Delevoryas 
>
> This adds the ISL69259, using all the same functionality as the existing
> ISL69260 but overriding the IC_DEVICE_ID.
>
> Signed-off-by: Peter Delevoryas 
> ---
>  hw/sensor/isl_pmbus_vr.c | 28 
>  1 file changed, 28 insertions(+)
>
> diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
> index 799ea9d89e..853d70536f 100644
> --- a/hw/sensor/isl_pmbus_vr.c
> +++ b/hw/sensor/isl_pmbus_vr.c
> @@ -119,6 +119,18 @@ static void raa228000_exit_reset(Object *obj)
>  pmdev->pages[0].read_temperature_3 = 0;
>  }
>
> +static void isl69259_exit_reset(Object *obj)
> +{
> +ISLState *s = ISL69260(obj);
> +static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 
> 0x3c};
> +g_assert_cmphex(sizeof(ic_device_id), <=, sizeof(s->ic_device_id));
> +

This generates an error from the checkpatch script:
Checking 0010-hw-sensor-Add-Renesas-ISL69259-device-model.patch...
ERROR: Use g_assert or g_assert_not_reached
#27: FILE: hw/sensor/isl_pmbus_vr.c:126:
+g_assert_cmphex(sizeof(ic_device_id), <=, sizeof(s->ic_device_id));

otherwise, LGTM.


Titus



Re: [PATCH v3 10/14] hw/sensor: Add Renesas ISL69259 device model

2022-06-30 Thread Titus Rwantare
On Wed, 29 Jun 2022 at 23:30, Cédric Le Goater  wrote:
>
> On 6/30/22 06:51, Peter Delevoryas wrote:
> > From: Peter Delevoryas 
> >
> > This adds the ISL69259, using all the same functionality as the existing
> > ISL69260 but overriding the IC_DEVICE_ID.
> >
> > Signed-off-by: Peter Delevoryas 
> > ---
> >   hw/sensor/isl_pmbus_vr.c | 28 
> >   1 file changed, 28 insertions(+)
> >
> > diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
> > index 799ea9d89e..853d70536f 100644
> > --- a/hw/sensor/isl_pmbus_vr.c
> > +++ b/hw/sensor/isl_pmbus_vr.c
> > @@ -119,6 +119,18 @@ static void raa228000_exit_reset(Object *obj)
> >   pmdev->pages[0].read_temperature_3 = 0;
> >   }
> >
> > +static void isl69259_exit_reset(Object *obj)
> > +{
> > +ISLState *s = ISL69260(obj);
> > +static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49, 
> > 0x3c};
>
> This looks like an ISLClass attribute to me. In which case, you wouldn't need 
> the
> reset handler nor the 'ic_device_id_len' field.
>
> Thanks,
>
> C.

I asked for this because, so far, I've been doing all the register
defaults in reset handlers, including read-only registers.
I don't mind either way, but it seemed preferable to have the devices
consistent.

Titus



Re: [PATCH v2 08/13] hw/i2c/pmbus: Reset out buf after switching pages

2022-06-29 Thread Titus Rwantare
On Tue, 28 Jun 2022 at 20:36, Peter Delevoryas
 wrote:
>
> When a pmbus device switches pages, it should clears its output buffer so
> that the next transaction doesn't emit data from the previous page.
>
> Fixes: 3746d5c15e70570b ("hw/i2c: add support for PMBus”)
> Signed-off-by: Peter Delevoryas 
> ---
>  hw/i2c/pmbus_device.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
> index 62885fa6a1..efddc36fd9 100644
> --- a/hw/i2c/pmbus_device.c
> +++ b/hw/i2c/pmbus_device.c
> @@ -1088,6 +1088,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
> *buf, uint8_t len)
>
>  if (pmdev->code == PMBUS_PAGE) {
>  pmdev->page = pmbus_receive8(pmdev);
> +pmdev->out_buf_len = 0;
>  return 0;
>  }
>

I suspect you were running into this because ic_device_id was putting
too much data in the output buffer. Still, I wouldn't want the buffer
cleared if the page hasn't changed. Some drivers write the same page
before every read.

Titus



Re: [PATCH v2 09/13] hw/i2c/pmbus: Add read-only IC_DEVICE_ID support

2022-06-29 Thread Titus Rwantare
On Tue, 28 Jun 2022 at 20:36, Peter Delevoryas
 wrote:
>
> Signed-off-by: Peter Delevoryas 
> ---

> --- a/hw/i2c/pmbus_device.c
> +++ b/hw/i2c/pmbus_device.c
> @@ -984,6 +984,11 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
>  }
>  break;
>
> +case PMBUS_IC_DEVICE_ID:
> +pmbus_send(pmdev, pmdev->pages[index].ic_device_id,
> +   sizeof(pmdev->pages[index].ic_device_id));
> +break;
> +

I don't think it's a good idea to add this here because this sends 16
bytes for all PMBus devices. I have at least one device that formats
IC_DEVICE_ID differently that I've not got permission to upstream.
The spec leaves the size and format up to the manufacturer, so this is
best done in isl_pmbus_vr.c in isl_pmbus_vr_read_byte().
Look at the adm1272_read_byte() which is more interesting than
isl_pmbus_vr one as an example.


>  case PMBUS_CLEAR_FAULTS:  /* Send Byte */
>  case PMBUS_PAGE_PLUS_WRITE:   /* Block Write-only */
>  case PMBUS_STORE_DEFAULT_ALL: /* Send Byte */
> diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
> index e11e028884..b12c46ab6d 100644
> --- a/hw/sensor/isl_pmbus_vr.c
> +++ b/hw/sensor/isl_pmbus_vr.c
> @@ -218,6 +218,28 @@ static void isl_pmbus_vr_class_init(ObjectClass *klass, 
> void *data,
>  k->device_num_pages = pages;
>  }
>
> +static void isl69259_init(Object *obj)
> +{
> +static const uint8_t ic_device_id[] = {0x04, 0x00, 0x81, 0xD2, 0x49};
> +PMBusDevice *pmdev = PMBUS_DEVICE(obj);
> +int i;
> +
> +raa22xx_init(obj);
> +for (i = 0; i < pmdev->num_pages; i++) {
> +memcpy(pmdev->pages[i].ic_device_id, ic_device_id,
> +   sizeof(ic_device_id));
> +}
> +}
> +

We tend to set default register values in exit_reset() calls. You can
do something like in raa228000_exit_reset()


> diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
> index 0f4d6b3fad..aed7809841 100644
> --- a/include/hw/i2c/pmbus_device.h
> +++ b/include/hw/i2c/pmbus_device.h
> @@ -407,6 +407,7 @@ typedef struct PMBusPage {
>  uint16_t mfr_max_temp_1;   /* R/W word */
>  uint16_t mfr_max_temp_2;   /* R/W word */
>  uint16_t mfr_max_temp_3;   /* R/W word */
> +uint8_t ic_device_id[16];  /* Read-Only block-read */

You wouldn't be able to do this here either, since the size could be
anything for other devices.

Thanks for the new device. It helps me see where to expand on PMBus.


Titus



Re: [PATCH v2 0/7] Add Qualcomm BMC machines

2022-06-27 Thread Titus Rwantare
You can take them through the aspeed branch.

Thanks.

-Titus

On Mon, 27 Jun 2022 at 09:33, Cédric Le Goater  wrote:
>
> Hello Titus,
>
> On 6/27/22 17:46, Jae Hyun Yoo wrote:
> > Hello,
> >
> > I'm sending a series to add Qualcomm BMC machines that are equipped with
> > Aspeed AST2600 SoC. Also, this series adds MAX31785 fan controller device
> > emulation. Please help to review.
> >
> > Thanks,
> >
> > Jae
> >
> > Changes in v2:
> > * Fixed a typo in QCOM DC-SCM V1 HW strap value comment. (Rebecca)
> > * Removed a useless change which is reverted by the next patch. (Joel)
> > * Changed machine name to 'qcom-firework-bmc'. (Cedric)
> > * Dropped FRU eeprom initialization part. (Patrick)
> > * Fixed comment for a case of PB_ALL_PAGES. (Titus)
> > * Removed an error log printing when it handles PB_ALL_PAGES. (Jae)
> > * Fixed a typo in copyright in max31785.c. (Rebecca)
> > * Fixed indentation issues in max31785.c. (Titus)
> > * Fixed license identifier style and refined indentation of defines. (Jae)
> > * Added PMBUS and MAX31785 config selection under ASPEED_SOC. (Titus)
> > * Moved machine updating part from the previous patch. (Cedric)
> > * Refined code to avoid retouching by the next patch. (Joel)
> >
> > Graeme Gregory (1):
> >hw/arm/aspeed: add Qualcomm Firework BMC machine
> >
> > Jae Hyun Yoo (2):
> >hw/arm/aspeed: add support for the Qualcomm DC-SCM v1 board
> >hw/arm/aspeed: firework: add I2C MUXes for VR channels
> >
> > Maheswara Kurapati (4):
> >hw/i2c: pmbus: Page #255 is valid page for read requests.
> >hw/sensor: add Maxim MAX31785 device
> >hw/arm/aspeed: Add MAX31785 Fan controllers
> >hw/arm/aspeed: firework: Add Thermal Diodes
> >
> >   hw/arm/Kconfig|   2 +
> >   hw/arm/aspeed.c   |  95 ++-
> >   hw/i2c/pmbus_device.c |   6 +-
> >   hw/sensor/Kconfig |   4 +
> >   hw/sensor/max31785.c  | 573 ++
> >   hw/sensor/meson.build |   1 +
> >   6 files changed, 674 insertions(+), 7 deletions(-)
> >   create mode 100644 hw/sensor/max31785.c
>
> Will you handle the pmbus/sensor patches or would you mind if I took them
> through the aspeed branch ?
>
> Thanks,
>
> C.
>
>



Re: [PATCH 6/9] hw/sensor: add Maxim MAX31785 device

2022-06-22 Thread Titus Rwantare
On Wed, 22 Jun 2022 at 10:29, Jae Hyun Yoo  wrote:
>
> From: Maheswara Kurapati 
>
> MAX31785 is a PMBus compliant 6-Channel fan controller. It supports 6 fan
> channels, 11 temperature sensors, and 6-Channel ADC to measure the remote
> voltages. Datasheet can be found here:
> https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
>
> This initial version of the driver has skeleton and support for the
> fan channels. Requests for temperature sensors, and ADC Channels the
> are serviced with the default values as per the datasheet.  No additional
> instrumentation is done.  NV Log feature is not supported.
>
> Signed-off-by: Maheswara Kurapati 
> ---
>  hw/arm/Kconfig|   1 +
>  hw/arm/aspeed.c   |   6 +-
>  hw/sensor/Kconfig |   4 +
>  hw/sensor/max31785.c  | 580 ++
>  hw/sensor/meson.build |   1 +
>  5 files changed, 590 insertions(+), 2 deletions(-)
>  create mode 100644 hw/sensor/max31785.c
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 219262a8da36..77ef0fa967b2 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -408,6 +408,7 @@ config NPCM7XX
>  select SSI
>  select UNIMP
>  select PCA954X
> +select MAX31785
>
>  config FSL_IMX25
>  bool

As this is being used with the Aspeed 2600, you may need to select
PMBUS and MAX31785 under config ASPEED_SOC in this same file.


> diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c
> new file mode 100644
> index ..11bf9977b6fd
> --- /dev/null
> +++ b/hw/sensor/max31785.c


Also, style nit, the checkpatch.pl script doesn't check whitespace
alignment. But the style guide
https://qemu-project.gitlab.io/qemu/devel/style.html#multiline-indent
specifies the variants we should use.

> +pmdev->pages[i].vout_scale_monitor =
> +MAX31785_DEFAULT_VOUT_SCALE_MONITOR;
> +pmdev->pages[i].vout_ov_fault_limit =
> +MAX31785_DEFAULT_OV_FAULT_LIMIT;
> +pmdev->pages[i].vout_ov_warn_limit =
> +MAX31785_DEFAULT_OV_WARN_LIMIT;
> +}
> +
> +}
> +
> +static const VMStateDescription vmstate_max31785 = {
> +.name = TYPE_MAX31785,
> +.version_id = 0,
> +.minimum_version_id = 0,
> +.fields = (VMStateField[]){
> +VMSTATE_PMBUS_DEVICE(parent, MAX31785State),
> +VMSTATE_UINT16_ARRAY(mfr_mode, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(vout_peak, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(temperature_peak, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(vout_min, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT8_ARRAY(fault_response, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT32_ARRAY(time_count, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(temp_sensor_config, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(fan_config, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(read_fan_pwm, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(fan_fault_limit, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(fan_warn_limit, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(fan_run_time, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT16_ARRAY(fan_pwm_avg, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +VMSTATE_UINT64_ARRAY(fan_pwm2rpm, MAX31785State,
> +MAX31785_TOTAL_NUM_PAGES),
> +    VMSTATE_UINT64(mfr_location, MAX31785State),
> +VMSTATE_UINT64(mfr_date, MAX31785State),
> +VMSTATE_UINT64(mfr_serial, MAX31785State),
> +VMSTATE_END_OF_LIST()
> +}
> +};
> +
There's missing indentation here for example.


Thanks,
Titus Rwantare



Re: [PATCH 5/9] hw/i2c: pmbus: Page #255 is valid page for read requests.

2022-06-22 Thread Titus Rwantare
On Wed, 22 Jun 2022 at 10:29, Jae Hyun Yoo  wrote:
>
> From: Maheswara Kurapati 
>
> Current implementation of the pmbus core driver treats the read request
> for page 255 as invalid request and sets the invalid command bit (bit 7) in 
> the
> STATUS_CML register. As per the PMBus specification it is a valid request.
>
> Refer to the PMBus specification, revision 1.3.1, section 11.10 PAGE, on the 
> page 58:
> "Setting the PAGE to FFh means that all subsequent comands are to be applied 
> to
>  all outputs.
>
>  Some commands, such as READ_TEMPERATURE, may use a common sensor but be
>  available on all pages of a device.  Such implementations are the decision of
>  each device manufacturer or are specified in a PMBus Application Profile. 
> Consult
>  the manufacturer's socuments or the Applicatin Profile Specification as 
> needed."
>
Thanks for this, the copy of the spec I used was older.


> For e.g.,
> The VOUT_MODE is a valid command for page 255 for maxim 31785 device.
> refer to Table 1. PMBus Command Codes on page 14 in the datasheet.
> https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
>
> Fixes: 38870253f1d1 ("hw/i2c: pmbus: fix error returns and guard against out 
> of range accesses")
>
> Signed-off-by: Maheswara Kurapati 
> ---
>  hw/i2c/pmbus_device.c | 1 -
>  1 file changed, 1 deletion(-)
>
> diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
> index 62885fa6a15e..7db3343a83b6 100644
> --- a/hw/i2c/pmbus_device.c
> +++ b/hw/i2c/pmbus_device.c
> @@ -291,7 +291,6 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
>  qemu_log_mask(LOG_GUEST_ERROR,
>"%s: tried to read from all pages\n",
>__func__);
> -pmbus_cml_error(pmdev);
>  } else if (pmdev->page > pmdev->num_pages - 1) {
>  qemu_log_mask(LOG_GUEST_ERROR,
>    "%s: page %d is out of range\n",
> --
> 2.25.1
>

Please also update the stale comment just above, since this is now
specified behaviour.

Reviewed-by: Titus Rwantare 



[PATCH v4 0/9] Fixups for PMBus and new sensors

2022-03-07 Thread Titus Rwantare
This patch series contains updates to PMBus in QEMU along with some PMBus
device models for Renesas regulators.
I have also added myself to MAINTAINERS as this code is in use daily,
where I am responsible for it.

v2:
  - split PMBus commit with updates into individual fixes
  - renamed isl_pmbus[.ch] adding _vr for voltage regulators

v3:
  - split uint refactor commit and removed commit renaming files
  - rename rolled into preceding commits
  - update commit description for uint refactoring change

v4:
  - responding to reviewer suggestions
  - added PMBUS_ERR_BYTE for consistent error returns

Shengtan Mao (1):
  hw/i2c: Added linear mode translation for pmbus devices

Titus Rwantare (8):
  hw/i2c: pmbus: add registers
  hw/i2c: pmbus: fix error returns and guard against out of range
accesses
  hw/i2c: pmbus: add PEC unsupported warning
  hw/i2c: pmbus: refactor uint handling
  hw/i2c: pmbus: update MAINTAINERS
  hw/sensor: add Intersil ISL69260 device model
  hw/sensor: add Renesas raa229004 PMBus device
  hw/sensor: add Renesas raa228000 device

 MAINTAINERS  |  13 +
 hw/arm/Kconfig   |   1 +
 hw/i2c/pmbus_device.c| 112 +++-
 hw/sensor/Kconfig|   5 +
 hw/sensor/isl_pmbus_vr.c | 279 ++
 hw/sensor/meson.build|   1 +
 include/hw/i2c/pmbus_device.h|  25 +-
 include/hw/sensor/isl_pmbus_vr.h |  52 
 tests/qtest/isl_pmbus_vr-test.c  | 474 +++
 tests/qtest/meson.build  |   1 +
 10 files changed, 949 insertions(+), 14 deletions(-)
 create mode 100644 hw/sensor/isl_pmbus_vr.c
 create mode 100644 include/hw/sensor/isl_pmbus_vr.h
 create mode 100644 tests/qtest/isl_pmbus_vr-test.c

-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 3/9] hw/i2c: pmbus: add PEC unsupported warning

2022-03-07 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index c7ec8e5499..ff644c1d4a 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -307,6 +307,11 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 
 case PMBUS_CAPABILITY:
 pmbus_send8(pmdev, pmdev->capability);
+if (pmdev->capability & BIT(7)) {
+qemu_log_mask(LOG_UNIMP,
+  "%s: PEC is enabled but not yet supported.\n",
+  __func__);
+}
 break;
 
 case PMBUS_VOUT_MODE: /* R/W byte */
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 1/9] hw/i2c: pmbus: add registers

2022-03-07 Thread Titus Rwantare
   - add the VOUT_MIN and STATUS_MFR registers

Signed-off-by: Titus Rwantare 
Reviewed-by: Philippe Mathieu-Daudé 
---
 hw/i2c/pmbus_device.c | 24 
 include/hw/i2c/pmbus_device.h |  3 +++
 2 files changed, 27 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 24f8f522d9..07a45c99f9 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -368,6 +368,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_VOUT_MIN:/* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
+pmbus_send16(pmdev, pmdev->pages[index].vout_min);
+} else {
+goto passthough;
+}
+break;
+
 /* TODO: implement coefficients support */
 
 case PMBUS_POUT_MAX:  /* R/W word */
@@ -708,6 +716,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 pmbus_send8(pmdev, pmdev->pages[index].status_other);
 break;
 
+case PMBUS_STATUS_MFR_SPECIFIC:   /* R/W byte */
+pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
+break;
+
 case PMBUS_READ_EIN:  /* Read-Only block 5 bytes */
 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
@@ -1149,6 +1161,14 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 }
 break;
 
+case PMBUS_VOUT_MIN:  /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
+pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
+} else {
+goto passthrough;
+}
+break;
+
 case PMBUS_POUT_MAX:  /* R/W word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
@@ -1482,6 +1502,10 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
 break;
 
+case PMBUS_STATUS_MFR_SPECIFIC:/* R/W byte */
+pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
+break;
+
 case PMBUS_PAGE_PLUS_READ:/* Block Read-only */
 case PMBUS_CAPABILITY:/* Read-Only byte */
 case PMBUS_COEFFICIENTS:  /* Read-only block 5 bytes */
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 62bd38c83f..72c0483149 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -43,6 +43,7 @@ enum pmbus_registers {
 PMBUS_VOUT_DROOP= 0x28, /* R/W word */
 PMBUS_VOUT_SCALE_LOOP   = 0x29, /* R/W word */
 PMBUS_VOUT_SCALE_MONITOR= 0x2A, /* R/W word */
+PMBUS_VOUT_MIN  = 0x2B, /* R/W word */
 PMBUS_COEFFICIENTS  = 0x30, /* Read-only block 5 bytes */
 PMBUS_POUT_MAX  = 0x31, /* R/W word */
 PMBUS_MAX_DUTY  = 0x32, /* R/W word */
@@ -255,6 +256,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 #define PB_HAS_TEMP3   BIT_ULL(42)
 #define PB_HAS_TEMP_RATING BIT_ULL(43)
 #define PB_HAS_MFR_INFOBIT_ULL(50)
+#define PB_HAS_STATUS_MFR_SPECIFIC BIT_ULL(51)
 
 struct PMBusDeviceClass {
 SMBusDeviceClass parent_class;
@@ -295,6 +297,7 @@ typedef struct PMBusPage {
 uint16_t vout_droop;   /* R/W word */
 uint16_t vout_scale_loop;  /* R/W word */
 uint16_t vout_scale_monitor;   /* R/W word */
+uint16_t vout_min; /* R/W word */
 uint8_t coefficients[5];   /* Read-only block 5 bytes */
 uint16_t pout_max; /* R/W word */
 uint16_t max_duty; /* R/W word */
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 7/9] hw/sensor: add Intersil ISL69260 device model

2022-03-07 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
Reviewed-by: Philippe Mathieu-Daudé 
---
 MAINTAINERS  |   3 +
 hw/arm/Kconfig   |   1 +
 hw/sensor/Kconfig|   5 +
 hw/sensor/isl_pmbus_vr.c | 211 +
 hw/sensor/meson.build|   1 +
 include/hw/sensor/isl_pmbus_vr.h |  50 
 tests/qtest/isl_pmbus_vr-test.c  | 394 +++
 tests/qtest/meson.build  |   1 +
 8 files changed, 666 insertions(+)
 create mode 100644 hw/sensor/isl_pmbus_vr.c
 create mode 100644 include/hw/sensor/isl_pmbus_vr.h
 create mode 100644 tests/qtest/isl_pmbus_vr-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3601984b5d..364a844045 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3140,10 +3140,13 @@ M: Titus Rwantare 
 S: Maintained
 F: hw/i2c/pmbus_device.c
 F: hw/sensor/adm1272.c
+F: hw/sensor/isl_pmbus_vr.c
 F: hw/sensor/max34451.c
 F: include/hw/i2c/pmbus_device.h
+F: include/hw/sensor/isl_pmbus_vr.h
 F: tests/qtest/adm1272-test.c
 F: tests/qtest/max34451-test.c
+F: tests/qtest/isl_pmbus_vr-test.c
 
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 6945330030..97f3b38019 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -400,6 +400,7 @@ config NPCM7XX
 select SMBUS
 select AT24C  # EEPROM
 select MAX34451
+select ISL_PMBUS_VR
 select PL310  # cache controller
 select PMBUS
 select SERIAL
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index 215944decc..4184fc2b08 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -30,3 +30,8 @@ config LSM303DLHC_MAG
 bool
 depends on I2C
 default y if I2C_DEVICES
+
+config ISL_PMBUS_VR
+bool
+depends on PMBUS
+
diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
new file mode 100644
index 00..f8cc75fc31
--- /dev/null
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -0,0 +1,211 @@
+/*
+ * PMBus device for Renesas Digital Multiphase Voltage Regulators
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sensor/isl_pmbus_vr.h"
+#include "hw/qdev-properties.h"
+#include "qapi/visitor.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev)
+{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: reading from unsupported register: 0x%02x\n",
+  __func__, pmdev->code);
+return PMBUS_ERR_BYTE;
+}
+
+static int isl_pmbus_vr_write_data(PMBusDevice *pmdev, const uint8_t *buf,
+   uint8_t len)
+{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: write to unsupported register: 0x%02x\n",
+  __func__, pmdev->code);
+return PMBUS_ERR_BYTE;
+}
+
+/* TODO: Implement coefficients support in pmbus_device.c for qmp */
+static void isl_pmbus_vr_get(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+visit_type_uint16(v, name, (uint16_t *)opaque, errp);
+}
+
+static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+uint16_t *internal = opaque;
+uint16_t value;
+if (!visit_type_uint16(v, name, , errp)) {
+return;
+}
+
+*internal = value;
+pmbus_check_limits(pmdev);
+}
+
+static void isl_pmbus_vr_exit_reset(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+
+pmdev->page = 0;
+pmdev->capability = ISL_CAPABILITY_DEFAULT;
+for (int i = 0; i < pmdev->num_pages; i++) {
+pmdev->pages[i].operation = ISL_OPERATION_DEFAULT;
+pmdev->pages[i].on_off_config = ISL_ON_OFF_CONFIG_DEFAULT;
+pmdev->pages[i].vout_mode = ISL_VOUT_MODE_DEFAULT;
+pmdev->pages[i].vout_command = ISL_VOUT_COMMAND_DEFAULT;
+pmdev->pages[i].vout_max = ISL_VOUT_MAX_DEFAULT;
+pmdev->pages[i].vout_margin_high = ISL_VOUT_MARGIN_HIGH_DEFAULT;
+pmdev->pages[i].vout_margin_low = ISL_VOUT_MARGIN_LOW_DEFAULT;
+pmdev->pages[i].vout_transition_rate = 
ISL_VOUT_TRANSITION_RATE_DEFAULT;
+pmdev->pages[i].vout_ov_fault_limit = ISL_VOUT_OV_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ot_fault_limit = ISL_OT_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ot_warn_limit = ISL_OT_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].vin_ov_warn_limit = ISL_VIN_OV_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].vin_uv_warn_limit = ISL_VIN_UV_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].iin_oc_fault_limit = ISL_IIN_OC_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ton_delay = ISL_TON_DELAY_DEFAULT;
+pmdev->pages[i].ton_rise = ISL_TON_RISE_DEFAULT;
+pmdev->pages[i].toff

[PATCH v4 9/9] hw/sensor: add Renesas raa228000 device

2022-03-07 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
Reviewed-by: Philippe Mathieu-Daudé 
---
 hw/sensor/isl_pmbus_vr.c | 50 ++
 include/hw/sensor/isl_pmbus_vr.h |  1 +
 tests/qtest/isl_pmbus_vr-test.c  | 72 
 3 files changed, 123 insertions(+)

diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
index 53187d619a..e11e028884 100644
--- a/hw/sensor/isl_pmbus_vr.c
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -89,6 +89,24 @@ static void isl_pmbus_vr_exit_reset(Object *obj)
 }
 }
 
+/* The raa228000 uses different direct mode coefficents from most isl devices 
*/
+static void raa228000_exit_reset(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+
+isl_pmbus_vr_exit_reset(obj);
+
+pmdev->pages[0].read_iout = 0;
+pmdev->pages[0].read_pout = 0;
+pmdev->pages[0].read_vout = 0;
+pmdev->pages[0].read_vin = 0;
+pmdev->pages[0].read_iin = 0;
+pmdev->pages[0].read_pin = 0;
+pmdev->pages[0].read_temperature_1 = 0;
+pmdev->pages[0].read_temperature_2 = 0;
+pmdev->pages[0].read_temperature_3 = 0;
+}
+
 static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
@@ -177,6 +195,20 @@ static void raa22xx_init(Object *obj)
 isl_pmbus_vr_add_props(obj, flags, ARRAY_SIZE(flags));
 }
 
+static void raa228000_init(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+uint64_t flags[1];
+
+flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE |
+   PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN |
+   PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE |
+   PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC;
+
+pmbus_page_config(pmdev, 0, flags[0]);
+isl_pmbus_vr_add_props(obj, flags, 1);
+}
+
 static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data,
 uint8_t pages)
 {
@@ -195,6 +227,15 @@ static void isl69260_class_init(ObjectClass *klass, void 
*data)
 isl_pmbus_vr_class_init(klass, data, 2);
 }
 
+static void raa228000_class_init(ObjectClass *klass, void *data)
+{
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+DeviceClass *dc = DEVICE_CLASS(klass);
+dc->desc = "Renesas 228000 Digital Multiphase Voltage Regulator";
+rc->phases.exit = raa228000_exit_reset;
+isl_pmbus_vr_class_init(klass, data, 1);
+}
+
 static void raa229004_class_init(ObjectClass *klass, void *data)
 {
 ResettableClass *rc = RESETTABLE_CLASS(klass);
@@ -220,9 +261,18 @@ static const TypeInfo raa229004_info = {
 .class_init = raa229004_class_init,
 };
 
+static const TypeInfo raa228000_info = {
+.name = TYPE_RAA228000,
+.parent = TYPE_PMBUS_DEVICE,
+.instance_size = sizeof(ISLState),
+.instance_init = raa228000_init,
+.class_init = raa228000_class_init,
+};
+
 static void isl_pmbus_vr_register_types(void)
 {
 type_register_static(_info);
+type_register_static(_info);
 type_register_static(_info);
 }
 
diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h
index 233916f70a..3e47ff7e48 100644
--- a/include/hw/sensor/isl_pmbus_vr.h
+++ b/include/hw/sensor/isl_pmbus_vr.h
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 
 #define TYPE_ISL69260   "isl69260"
+#define TYPE_RAA228000  "raa228000"
 #define TYPE_RAA229004  "raa229004"
 
 struct ISLState {
diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c
index dc0ccae2aa..5553ea410a 100644
--- a/tests/qtest/isl_pmbus_vr-test.c
+++ b/tests/qtest/isl_pmbus_vr-test.c
@@ -150,6 +150,70 @@ static void test_defaults(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(i2c_value, ==, ISL_REVISION_DEFAULT);
 }
 
+static void raa228000_test_defaults(void *obj, void *data,
+QGuestAllocator *alloc)
+{
+uint16_t value, i2c_value;
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+value = qmp_isl_pmbus_vr_get(TEST_ID, "vout[0]");
+g_assert_cmpuint(value, ==, 0);
+
+i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT);
+g_assert_cmpuint(i2c_value, ==, 0);
+
+value = qmp_isl_pmbus_vr_get(TEST_ID, "pout[0]");
+g_assert_cmpuint(value, ==, 0);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY);
+g_assert_cmphex(i2c_value, ==, ISL_CAPABILITY_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
+g_assert_cmphex(i2c_value, ==, ISL_OPERATION_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG);
+g_assert_cmphex(i2c_value, ==, ISL_ON_OFF_CONFIG_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
+g_assert_cmphex(i2c_value, ==, ISL_VOUT_MODE_DEFAULT);
+
+i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_COMMAND);
+g_assert_cmphex(i2c_value, ==, ISL_VOUT_COMMAND_DEFA

[PATCH v4 6/9] hw/i2c: Added linear mode translation for pmbus devices

2022-03-07 Thread Titus Rwantare
From: Shengtan Mao 

Signed-off-by: Shengtan Mao 
Reviewed-by: Titus Rwantare 
Reviewed-by: Philippe Mathieu-Daudé 
---
 hw/i2c/pmbus_device.c | 18 ++
 include/hw/i2c/pmbus_device.h | 20 +++-
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 8cb9db0f80..62885fa6a1 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -28,6 +28,24 @@ uint32_t pmbus_direct_mode2data(PMBusCoefficients c, 
uint16_t value)
 return x;
 }
 
+uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
+{
+/* L = D * 2^(-e) */
+if (exp < 0) {
+return value << (-exp);
+}
+return value >> exp;
+}
+
+uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
+{
+/* D = L * 2^e */
+if (exp < 0) {
+return value >> (-exp);
+}
+return value << exp;
+}
+
 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
 {
 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index bab4526734..0f4d6b3fad 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -448,7 +448,7 @@ typedef struct PMBusCoefficients {
  *
  * Y = (m * x - b) * 10^R
  *
- * @return uint32_t
+ * @return uint16_t
  */
 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value);
 
@@ -461,6 +461,24 @@ uint16_t pmbus_data2direct_mode(PMBusCoefficients c, 
uint32_t value);
  */
 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value);
 
+/**
+ * Convert sensor values to linear mode format
+ *
+ * L = D * 2^(-e)
+ *
+ * @return uint16
+ */
+uint16_t pmbus_data2linear_mode(uint16_t value, int exp);
+
+/**
+ * Convert linear mode formatted data into sensor reading
+ *
+ * D = L * 2^e
+ *
+ * @return uint16
+ */
+uint16_t pmbus_linear_mode2data(uint16_t value, int exp);
+
 /**
  * @brief Send a block of data over PMBus
  * Assumes that the bytes in the block are already ordered correctly,
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 4/9] hw/i2c: pmbus: refactor uint handling

2022-03-07 Thread Titus Rwantare
This change cleans up the inputs to pmbus_receive uint, the length of
received data is contained in PMBusDevice state and doesn't need to be
passed around.

Signed-off-by: Titus Rwantare 
Reviewed-by: Philippe Mathieu-Daudé 
---
 hw/i2c/pmbus_device.c | 18 +-
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index ff644c1d4a..8cb9db0f80 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -89,16 +89,16 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 }
 
 
-static uint64_t pmbus_receive_uint(const uint8_t *buf, uint8_t len)
+static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
 {
 uint64_t ret = 0;
 
 /* Exclude command code from return value */
-buf++;
-len--;
+pmdev->in_buf++;
+pmdev->in_buf_len--;
 
-for (int i = len - 1; i >= 0; i--) {
-ret = ret << 8 | buf[i];
+for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
+ret = ret << 8 | pmdev->in_buf[i];
 }
 return ret;
 }
@@ -110,7 +110,7 @@ uint8_t pmbus_receive8(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 1 byte, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint16_t pmbus_receive16(PMBusDevice *pmdev)
@@ -120,7 +120,7 @@ uint16_t pmbus_receive16(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint32_t pmbus_receive32(PMBusDevice *pmdev)
@@ -130,7 +130,7 @@ uint32_t pmbus_receive32(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint64_t pmbus_receive64(PMBusDevice *pmdev)
@@ -140,7 +140,7 @@ uint64_t pmbus_receive64(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 8/9] hw/sensor: add Renesas raa229004 PMBus device

2022-03-07 Thread Titus Rwantare
The Renesas RAA229004 is a PMBus Multiphase Voltage Regulator

Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
Reviewed-by: Philippe Mathieu-Daudé 
---
 hw/sensor/isl_pmbus_vr.c | 18 ++
 include/hw/sensor/isl_pmbus_vr.h |  1 +
 tests/qtest/isl_pmbus_vr-test.c  |  8 
 3 files changed, 27 insertions(+)

diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
index f8cc75fc31..53187d619a 100644
--- a/hw/sensor/isl_pmbus_vr.c
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -195,6 +195,15 @@ static void isl69260_class_init(ObjectClass *klass, void 
*data)
 isl_pmbus_vr_class_init(klass, data, 2);
 }
 
+static void raa229004_class_init(ObjectClass *klass, void *data)
+{
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+DeviceClass *dc = DEVICE_CLASS(klass);
+dc->desc = "Renesas 229004 Digital Multiphase Voltage Regulator";
+rc->phases.exit = isl_pmbus_vr_exit_reset;
+isl_pmbus_vr_class_init(klass, data, 2);
+}
+
 static const TypeInfo isl69260_info = {
 .name = TYPE_ISL69260,
 .parent = TYPE_PMBUS_DEVICE,
@@ -203,9 +212,18 @@ static const TypeInfo isl69260_info = {
 .class_init = isl69260_class_init,
 };
 
+static const TypeInfo raa229004_info = {
+.name = TYPE_RAA229004,
+.parent = TYPE_PMBUS_DEVICE,
+.instance_size = sizeof(ISLState),
+.instance_init = raa22xx_init,
+.class_init = raa229004_class_init,
+};
+
 static void isl_pmbus_vr_register_types(void)
 {
 type_register_static(_info);
+type_register_static(_info);
 }
 
 type_init(isl_pmbus_vr_register_types)
diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h
index 4e12e95efb..233916f70a 100644
--- a/include/hw/sensor/isl_pmbus_vr.h
+++ b/include/hw/sensor/isl_pmbus_vr.h
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 
 #define TYPE_ISL69260   "isl69260"
+#define TYPE_RAA229004  "raa229004"
 
 struct ISLState {
 PMBusDevice parent;
diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c
index f77732ae96..dc0ccae2aa 100644
--- a/tests/qtest/isl_pmbus_vr-test.c
+++ b/tests/qtest/isl_pmbus_vr-test.c
@@ -390,5 +390,13 @@ static void isl_pmbus_vr_register_nodes(void)
 qos_add_test("test_pages_rw", "isl69260", test_pages_rw, NULL);
 qos_add_test("test_ro_regs", "isl69260", test_ro_regs, NULL);
 qos_add_test("test_ov_faults", "isl69260", test_voltage_faults, NULL);
+
+qos_node_create_driver("raa229004", i2c_device_create);
+qos_node_consumes("raa229004", "i2c-bus", );
+
+qos_add_test("test_tx_rx", "raa229004", test_tx_rx, NULL);
+qos_add_test("test_rw_regs", "raa229004", test_rw_regs, NULL);
+qos_add_test("test_pages_rw", "raa229004", test_pages_rw, NULL);
+qos_add_test("test_ov_faults", "raa229004", test_voltage_faults, NULL);
 }
 libqos_init(isl_pmbus_vr_register_nodes);
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 5/9] hw/i2c: pmbus: update MAINTAINERS

2022-03-07 Thread Titus Rwantare
add self to MAINTAINERS for the PMBus subsystem and related sensors,
and set PMBus as maintained.

Signed-off-by: Titus Rwantare 
Reviewed-by: Philippe Mathieu-Daudé 
---
 MAINTAINERS | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fa8adc2618..3601984b5d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3135,6 +3135,16 @@ F: include/hw/i2c/smbus_master.h
 F: include/hw/i2c/smbus_slave.h
 F: include/hw/i2c/smbus_eeprom.h
 
+PMBus
+M: Titus Rwantare 
+S: Maintained
+F: hw/i2c/pmbus_device.c
+F: hw/sensor/adm1272.c
+F: hw/sensor/max34451.c
+F: include/hw/i2c/pmbus_device.h
+F: tests/qtest/adm1272-test.c
+F: tests/qtest/max34451-test.c
+
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
 R: Daniel P. Berrange 
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v4 2/9] hw/i2c: pmbus: fix error returns and guard against out of range accesses

2022-03-07 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Philippe Mathieu-Daudé 
---
 hw/i2c/pmbus_device.c | 47 ---
 include/hw/i2c/pmbus_device.h |  2 ++
 2 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 07a45c99f9..c7ec8e5499 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -149,7 +149,7 @@ static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
 qemu_log_mask(LOG_GUEST_ERROR,
   "%s: tried to read from empty buffer",
   __func__);
-return 0xFF;
+return PMBUS_ERR_BYTE;
 }
 uint8_t data = pmdev->out_buf[pmdev->out_buf_len - 1];
 pmdev->out_buf_len--;
@@ -243,18 +243,47 @@ void pmbus_check_limits(PMBusDevice *pmdev)
 }
 }
 
+/* assert the status_cml error upon receipt of malformed command */
+static void pmbus_cml_error(PMBusDevice *pmdev)
+{
+for (int i = 0; i < pmdev->num_pages; i++) {
+pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
+pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
+}
+}
+
 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
-uint8_t ret = 0xFF;
-uint8_t index = pmdev->page;
+uint8_t ret = PMBUS_ERR_BYTE;
+uint8_t index;
 
 if (pmdev->out_buf_len != 0) {
 ret = pmbus_out_buf_pop(pmdev);
 return ret;
 }
 
+/*
+ * Reading from all pages will return the value from page 0,
+ * this is unspecified behaviour in general.
+ */
+if (pmdev->page == PB_ALL_PAGES) {
+index = 0;
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: tried to read from all pages\n",
+  __func__);
+pmbus_cml_error(pmdev);
+} else if (pmdev->page > pmdev->num_pages - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: page %d is out of range\n",
+  __func__, pmdev->page);
+pmbus_cml_error(pmdev);
+return PMBUS_ERR_BYTE;
+} else {
+index = pmdev->page;
+}
+
 switch (pmdev->code) {
 case PMBUS_PAGE:
 pmbus_send8(pmdev, pmdev->page);
@@ -1019,7 +1048,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 
 if (len == 0) {
 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
-return -1;
+return PMBUS_ERR_BYTE;
 }
 
 if (!pmdev->pages) { /* allocate memory for pages on first use */
@@ -1038,6 +1067,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->page = pmbus_receive8(pmdev);
 return 0;
 }
+
 /* loop through all the pages when 0xFF is received */
 if (pmdev->page == PB_ALL_PAGES) {
 for (int i = 0; i < pmdev->num_pages; i++) {
@@ -1048,6 +1078,15 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 return 0;
 }
 
+if (pmdev->page > pmdev->num_pages - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+"%s: page %u is out of range\n",
+__func__, pmdev->page);
+pmdev->page = 0; /* undefined behaviour - reset to page 0 */
+pmbus_cml_error(pmdev);
+return PMBUS_ERR_BYTE;
+}
+
 index = pmdev->page;
 
 switch (pmdev->code) {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 72c0483149..bab4526734 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -228,6 +228,8 @@ enum pmbus_registers {
 #define PB_MAX_PAGES0x1F
 #define PB_ALL_PAGES0xFF
 
+#define PMBUS_ERR_BYTE  0xFF
+
 #define TYPE_PMBUS_DEVICE "pmbus-device"
 OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 PMBUS_DEVICE)
-- 
2.35.1.616.g0bdcbb4464-goog




Re: [PATCH v3 3/9] hw/i2c: pmbus: add PEC unsupported warning

2022-03-07 Thread Titus Rwantare
Yes, fixed.

On Fri, 4 Mar 2022 at 16:02, Philippe Mathieu-Daudé
 wrote:
>
> On 2/3/22 02:50, Titus Rwantare wrote:
> > Signed-off-by: Titus Rwantare 
> > ---
> >   hw/i2c/pmbus_device.c | 5 +
> >   1 file changed, 5 insertions(+)
> >
> > diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
> > index 93c746bab3..6eeb0731d7 100644
> > --- a/hw/i2c/pmbus_device.c
> > +++ b/hw/i2c/pmbus_device.c
> > @@ -307,6 +307,11 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
> >
> >   case PMBUS_CAPABILITY:
> >   pmbus_send8(pmdev, pmdev->capability);
> > +if (pmdev->capability & BIT(7)) {
> > +qemu_log_mask(LOG_GUEST_ERROR,
>
> That would be LOG_UNIMP?
>
> > +  "%s: PEC is enabled but not yet supported.\n",
> > +  __func__);
> > +}
> >   break;
> >
> >   case PMBUS_VOUT_MODE: /* R/W byte */
>



Re: [PATCH v3 2/9] hw/i2c: pmbus: guard against out of range accesses

2022-03-07 Thread Titus Rwantare
Ack. All errors for PMBus should
ideally be reflected in status and status_cml registers instead of
carrying meaning in return values. I'll have to separately go through
the existing code to make it consistent.


On Fri, 4 Mar 2022 at 16:08, Philippe Mathieu-Daudé
 wrote:
>
> On 2/3/22 02:50, Titus Rwantare wrote:
> > Signed-off-by: Titus Rwantare 
> > ---
> >   hw/i2c/pmbus_device.c | 41 -
> >   1 file changed, 40 insertions(+), 1 deletion(-)
>
> >   static uint8_t pmbus_receive_byte(SMBusDevice *smd)
> >   {
> >   PMBusDevice *pmdev = PMBUS_DEVICE(smd);
> >   PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
> >   uint8_t ret = 0xFF;
> > -uint8_t index = pmdev->page;
> > +uint8_t index;
> >
> >   if (pmdev->out_buf_len != 0) {
> >   ret = pmbus_out_buf_pop(pmdev);
> >   return ret;
> >   }
> >
> > +/*
> > + * Reading from all pages will return the value from page 0,
> > + * this is unspecified behaviour in general.
> > + */
> > +if (pmdev->page == PB_ALL_PAGES) {
> > +index = 0;
> > +qemu_log_mask(LOG_GUEST_ERROR,
> > +  "%s: tried to read from all pages\n",
> > +  __func__);
> > +pmbus_cml_error(pmdev);
> > +} else if (pmdev->page > pmdev->num_pages - 1) {
> > +qemu_log_mask(LOG_GUEST_ERROR,
> > +  "%s: page %d is out of range\n",
> > +  __func__, pmdev->page);
> > +pmbus_cml_error(pmdev);
> > +return -1;
>
> This file returns a mix of 0xFF/-1 for error. It would be nice
> to pick one. Adding a definition (PMBUS_ERR_BYTE?) could help.
>
> Preferably with error unified:
> Reviewed-by: Philippe Mathieu-Daudé 



Re: [PATCH v3 0/9] This patch series contains updates to PMBus in QEMU along with some PMBus device models for Renesas regulators. I have also added myself to MAINTAINERS as this code is in use daily,

2022-03-04 Thread Titus Rwantare
On Fri, 4 Mar 2022 at 13:43, Corey Minyard  wrote:
>
> On Tue, Mar 01, 2022 at 05:50:44PM -0800, Titus Rwantare wrote:
> > v2:
> >   - split PMBus commit with updates into individual fixes
> >   - renamed isl_pmbus[.ch] adding _vr for voltage regulators
> >
> > v3:
> >   - split uint refactor commit and removed commit renaming files
> >   - rename rolled into preceding commits
> >   - update commit description for uint refactoring change
>
> This all looks good to me:
>
> Acked-by: Corey Minyard 
>
> Do you have a plan for getting this in to qemu?  Like through the ARM
> tree?  I could take it into an I2C tree, but there's really not much
> activity or work there.
>
> -corey

In general PMBus is more specific to i2c than ARM, but I'm not sure of
the QEMU implications.

Titus



[PATCH v3 7/9] hw/sensor: add Intersil ISL69260 device model

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 MAINTAINERS  |   3 +
 hw/arm/Kconfig   |   1 +
 hw/sensor/Kconfig|   5 +
 hw/sensor/isl_pmbus_vr.c | 211 +
 hw/sensor/meson.build|   1 +
 include/hw/sensor/isl_pmbus_vr.h |  50 
 tests/qtest/isl_pmbus_vr-test.c  | 394 +++
 tests/qtest/meson.build  |   1 +
 8 files changed, 666 insertions(+)
 create mode 100644 hw/sensor/isl_pmbus_vr.c
 create mode 100644 include/hw/sensor/isl_pmbus_vr.h
 create mode 100644 tests/qtest/isl_pmbus_vr-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3601984b5d..364a844045 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3140,10 +3140,13 @@ M: Titus Rwantare 
 S: Maintained
 F: hw/i2c/pmbus_device.c
 F: hw/sensor/adm1272.c
+F: hw/sensor/isl_pmbus_vr.c
 F: hw/sensor/max34451.c
 F: include/hw/i2c/pmbus_device.h
+F: include/hw/sensor/isl_pmbus_vr.h
 F: tests/qtest/adm1272-test.c
 F: tests/qtest/max34451-test.c
+F: tests/qtest/isl_pmbus_vr-test.c
 
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 6945330030..97f3b38019 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -400,6 +400,7 @@ config NPCM7XX
 select SMBUS
 select AT24C  # EEPROM
 select MAX34451
+select ISL_PMBUS_VR
 select PL310  # cache controller
 select PMBUS
 select SERIAL
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index 215944decc..a834d2f814 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -30,3 +30,8 @@ config LSM303DLHC_MAG
 bool
 depends on I2C
 default y if I2C_DEVICES
+
+config ISL_PMBUS_VR
+bool
+depends on I2C
+
diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
new file mode 100644
index 00..b3d24e40ab
--- /dev/null
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -0,0 +1,211 @@
+/*
+ * PMBus device for Renesas Digital Multiphase Voltage Regulators
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sensor/isl_pmbus_vr.h"
+#include "hw/qdev-properties.h"
+#include "qapi/visitor.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev)
+{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: reading from unsupported register: 0x%02x\n",
+  __func__, pmdev->code);
+return 0xFF;
+}
+
+static int isl_pmbus_vr_write_data(PMBusDevice *pmdev, const uint8_t *buf,
+  uint8_t len)
+{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: write to unsupported register: 0x%02x\n",
+  __func__, pmdev->code);
+return 0xFF;
+}
+
+/* TODO: Implement coefficients support in pmbus_device.c for qmp */
+static void isl_pmbus_vr_get(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+visit_type_uint16(v, name, (uint16_t *)opaque, errp);
+}
+
+static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+uint16_t *internal = opaque;
+uint16_t value;
+if (!visit_type_uint16(v, name, , errp)) {
+return;
+}
+
+*internal = value;
+pmbus_check_limits(pmdev);
+}
+
+static void isl_pmbus_vr_exit_reset(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+
+pmdev->page = 0;
+pmdev->capability = ISL_CAPABILITY_DEFAULT;
+for (int i = 0; i < pmdev->num_pages; i++) {
+pmdev->pages[i].operation = ISL_OPERATION_DEFAULT;
+pmdev->pages[i].on_off_config = ISL_ON_OFF_CONFIG_DEFAULT;
+pmdev->pages[i].vout_mode = ISL_VOUT_MODE_DEFAULT;
+pmdev->pages[i].vout_command = ISL_VOUT_COMMAND_DEFAULT;
+pmdev->pages[i].vout_max = ISL_VOUT_MAX_DEFAULT;
+pmdev->pages[i].vout_margin_high = ISL_VOUT_MARGIN_HIGH_DEFAULT;
+pmdev->pages[i].vout_margin_low = ISL_VOUT_MARGIN_LOW_DEFAULT;
+pmdev->pages[i].vout_transition_rate = 
ISL_VOUT_TRANSITION_RATE_DEFAULT;
+pmdev->pages[i].vout_ov_fault_limit = ISL_VOUT_OV_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ot_fault_limit = ISL_OT_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ot_warn_limit = ISL_OT_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].vin_ov_warn_limit = ISL_VIN_OV_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].vin_uv_warn_limit = ISL_VIN_UV_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].iin_oc_fault_limit = ISL_IIN_OC_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ton_delay = ISL_TON_DELAY_DEFAULT;
+pmdev->pages[i].ton_rise = ISL_TON_RISE_DEFAULT;
+pmdev->pages[i].toff_fall = ISL_TOFF_FALL_DEFAULT;
+

[PATCH v3 5/9] hw/i2c: pmbus: update MAINTAINERS

2022-03-01 Thread Titus Rwantare
add self to MAINTAINERS for the PMBus subsystem and related sensors,
and set PMBus as maintained.

Signed-off-by: Titus Rwantare 
---
 MAINTAINERS | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fa8adc2618..3601984b5d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3135,6 +3135,16 @@ F: include/hw/i2c/smbus_master.h
 F: include/hw/i2c/smbus_slave.h
 F: include/hw/i2c/smbus_eeprom.h
 
+PMBus
+M: Titus Rwantare 
+S: Maintained
+F: hw/i2c/pmbus_device.c
+F: hw/sensor/adm1272.c
+F: hw/sensor/max34451.c
+F: include/hw/i2c/pmbus_device.h
+F: tests/qtest/adm1272-test.c
+F: tests/qtest/max34451-test.c
+
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
 R: Daniel P. Berrange 
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 0/9] This patch series contains updates to PMBus in QEMU along with some PMBus device models for Renesas regulators. I have also added myself to MAINTAINERS as this code is in use daily, whe

2022-03-01 Thread Titus Rwantare
v2:
  - split PMBus commit with updates into individual fixes
  - renamed isl_pmbus[.ch] adding _vr for voltage regulators

v3:
  - split uint refactor commit and removed commit renaming files
  - rename rolled into preceding commits
  - update commit description for uint refactoring change

Shengtan Mao (1):
  hw/i2c: Added linear mode translation for pmbus devices

Titus Rwantare (8):
  hw/i2c: pmbus: add registers
  hw/i2c: pmbus: guard against out of range accesses
  hw/i2c: pmbus: add PEC unsupported warning
  hw/i2c: pmbus: refactor uint handling
  hw/i2c: pmbus: update MAINTAINERS
  hw/sensor: add Intersil ISL69260 device model
  hw/sensor: add Renesas raa229004 PMBus device
  hw/sensor: add Renesas raa228000 device

 MAINTAINERS  |  13 +
 hw/arm/Kconfig   |   1 +
 hw/i2c/pmbus_device.c| 106 ++-
 hw/sensor/Kconfig|   5 +
 hw/sensor/isl_pmbus_vr.c | 279 ++
 hw/sensor/meson.build|   1 +
 include/hw/i2c/pmbus_device.h|  23 +-
 include/hw/sensor/isl_pmbus_vr.h |  52 
 tests/qtest/isl_pmbus_vr-test.c  | 474 +++
 tests/qtest/meson.build  |   1 +
 10 files changed, 944 insertions(+), 11 deletions(-)
 create mode 100644 hw/sensor/isl_pmbus_vr.c
 create mode 100644 include/hw/sensor/isl_pmbus_vr.h
 create mode 100644 tests/qtest/isl_pmbus_vr-test.c

-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 9/9] hw/sensor: add Renesas raa228000 device

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/sensor/isl_pmbus_vr.c | 50 
 include/hw/sensor/isl_pmbus_vr.h |  1 +
 tests/qtest/isl_pmbus_vr-test.c  | 78 ++--
 3 files changed, 126 insertions(+), 3 deletions(-)

diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
index e260faeac3..df7c003ea6 100644
--- a/hw/sensor/isl_pmbus_vr.c
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -89,6 +89,24 @@ static void isl_pmbus_vr_exit_reset(Object *obj)
 }
 }
 
+/* The raa228000 uses different direct mode coefficents from most isl devices 
*/
+static void raa228000_exit_reset(Object *obj)
+{
+isl_pmbus_vr_exit_reset(obj);
+
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+
+pmdev->pages[0].read_vout = 0;
+pmdev->pages[0].read_iout = 0;
+pmdev->pages[0].read_pout = 0;
+pmdev->pages[0].read_vin = 0;
+pmdev->pages[0].read_iin = 0;
+pmdev->pages[0].read_pin = 0;
+pmdev->pages[0].read_temperature_1 = 0;
+pmdev->pages[0].read_temperature_2 = 0;
+pmdev->pages[0].read_temperature_3 = 0;
+}
+
 static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
@@ -177,6 +195,20 @@ static void raa22xx_init(Object *obj)
 isl_pmbus_vr_add_props(obj, flags, 2);
 }
 
+static void raa228000_init(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+uint64_t flags[1];
+
+flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE |
+   PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN |
+   PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE |
+   PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC;
+
+pmbus_page_config(pmdev, 0, flags[0]);
+isl_pmbus_vr_add_props(obj, flags, 1);
+}
+
 static void isl_pmbus_vr_class_init(ObjectClass *klass, void *data,
 uint8_t pages)
 {
@@ -195,6 +227,15 @@ static void isl69260_class_init(ObjectClass *klass, void 
*data)
 isl_pmbus_vr_class_init(klass, data, 2);
 }
 
+static void raa228000_class_init(ObjectClass *klass, void *data)
+{
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+DeviceClass *dc = DEVICE_CLASS(klass);
+dc->desc = "Renesas 228000 Digital Multiphase Voltage Regulator";
+rc->phases.exit = raa228000_exit_reset;
+isl_pmbus_vr_class_init(klass, data, 1);
+}
+
 static void raa229004_class_init(ObjectClass *klass, void *data)
 {
 ResettableClass *rc = RESETTABLE_CLASS(klass);
@@ -220,9 +261,18 @@ static const TypeInfo raa229004_info = {
 .class_init = raa229004_class_init,
 };
 
+static const TypeInfo raa228000_info = {
+.name = TYPE_RAA228000,
+.parent = TYPE_PMBUS_DEVICE,
+.instance_size = sizeof(ISLState),
+.instance_init = raa228000_init,
+.class_init = raa228000_class_init,
+};
+
 static void isl_pmbus_vr_register_types(void)
 {
 type_register_static(_info);
+type_register_static(_info);
 type_register_static(_info);
 }
 
diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h
index 233916f70a..3e47ff7e48 100644
--- a/include/hw/sensor/isl_pmbus_vr.h
+++ b/include/hw/sensor/isl_pmbus_vr.h
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 
 #define TYPE_ISL69260   "isl69260"
+#define TYPE_RAA228000  "raa228000"
 #define TYPE_RAA229004  "raa229004"
 
 struct ISLState {
diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c
index a33dfb6135..5553ea410a 100644
--- a/tests/qtest/isl_pmbus_vr-test.c
+++ b/tests/qtest/isl_pmbus_vr-test.c
@@ -150,6 +150,70 @@ static void test_defaults(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(i2c_value, ==, ISL_REVISION_DEFAULT);
 }
 
+static void raa228000_test_defaults(void *obj, void *data,
+QGuestAllocator *alloc)
+{
+uint16_t value, i2c_value;
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+value = qmp_isl_pmbus_vr_get(TEST_ID, "vout[0]");
+g_assert_cmpuint(value, ==, 0);
+
+i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_READ_IOUT);
+g_assert_cmpuint(i2c_value, ==, 0);
+
+value = qmp_isl_pmbus_vr_get(TEST_ID, "pout[0]");
+g_assert_cmpuint(value, ==, 0);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY);
+g_assert_cmphex(i2c_value, ==, ISL_CAPABILITY_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
+g_assert_cmphex(i2c_value, ==, ISL_OPERATION_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG);
+g_assert_cmphex(i2c_value, ==, ISL_ON_OFF_CONFIG_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
+g_assert_cmphex(i2c_value, ==, ISL_VOUT_MODE_DEFAULT);
+
+i2c_value = isl_pmbus_vr_i2c_get16(i2cdev, PMBUS_VOUT_COMMAND);
+g_assert_cmphex(i2c_value, ==, ISL_VOUT_COMMAND_DEFAULT);
+
+i2c_value = isl_pmbus_vr_i2c_get

[PATCH v3 2/9] hw/i2c: pmbus: guard against out of range accesses

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 41 -
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 07a45c99f9..93c746bab3 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -243,18 +243,47 @@ void pmbus_check_limits(PMBusDevice *pmdev)
 }
 }
 
+/* assert the status_cml error upon receipt of malformed command */
+static void pmbus_cml_error(PMBusDevice *pmdev)
+{
+for (int i = 0; i < pmdev->num_pages; i++) {
+pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
+pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
+}
+}
+
 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
 uint8_t ret = 0xFF;
-uint8_t index = pmdev->page;
+uint8_t index;
 
 if (pmdev->out_buf_len != 0) {
 ret = pmbus_out_buf_pop(pmdev);
 return ret;
 }
 
+/*
+ * Reading from all pages will return the value from page 0,
+ * this is unspecified behaviour in general.
+ */
+if (pmdev->page == PB_ALL_PAGES) {
+index = 0;
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: tried to read from all pages\n",
+  __func__);
+pmbus_cml_error(pmdev);
+} else if (pmdev->page > pmdev->num_pages - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: page %d is out of range\n",
+  __func__, pmdev->page);
+pmbus_cml_error(pmdev);
+return -1;
+} else {
+index = pmdev->page;
+}
+
 switch (pmdev->code) {
 case PMBUS_PAGE:
 pmbus_send8(pmdev, pmdev->page);
@@ -1038,6 +1067,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->page = pmbus_receive8(pmdev);
 return 0;
 }
+
 /* loop through all the pages when 0xFF is received */
 if (pmdev->page == PB_ALL_PAGES) {
 for (int i = 0; i < pmdev->num_pages; i++) {
@@ -1048,6 +1078,15 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 return 0;
 }
 
+if (pmdev->page > pmdev->num_pages - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+"%s: page %u is out of range\n",
+__func__, pmdev->page);
+pmdev->page = 0; /* undefined behaviour - reset to page 0 */
+pmbus_cml_error(pmdev);
+return -1;
+}
+
 index = pmdev->page;
 
 switch (pmdev->code) {
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 4/9] hw/i2c: pmbus: refactor uint handling

2022-03-01 Thread Titus Rwantare
This change cleans up the inputs to pmbus_receive uint, the length of
received data is contained in PMBusDevice state and doesn't need to be
passed around.

Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 18 +-
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 6eeb0731d7..3beb02afad 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -89,16 +89,16 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 }
 
 
-static uint64_t pmbus_receive_uint(const uint8_t *buf, uint8_t len)
+static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
 {
 uint64_t ret = 0;
 
 /* Exclude command code from return value */
-buf++;
-len--;
+pmdev->in_buf++;
+pmdev->in_buf_len--;
 
-for (int i = len - 1; i >= 0; i--) {
-ret = ret << 8 | buf[i];
+for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
+ret = ret << 8 | pmdev->in_buf[i];
 }
 return ret;
 }
@@ -110,7 +110,7 @@ uint8_t pmbus_receive8(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 1 byte, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint16_t pmbus_receive16(PMBusDevice *pmdev)
@@ -120,7 +120,7 @@ uint16_t pmbus_receive16(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint32_t pmbus_receive32(PMBusDevice *pmdev)
@@ -130,7 +130,7 @@ uint32_t pmbus_receive32(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint64_t pmbus_receive64(PMBusDevice *pmdev)
@@ -140,7 +140,7 @@ uint64_t pmbus_receive64(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 3/9] hw/i2c: pmbus: add PEC unsupported warning

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 93c746bab3..6eeb0731d7 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -307,6 +307,11 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 
 case PMBUS_CAPABILITY:
 pmbus_send8(pmdev, pmdev->capability);
+if (pmdev->capability & BIT(7)) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: PEC is enabled but not yet supported.\n",
+  __func__);
+}
 break;
 
 case PMBUS_VOUT_MODE: /* R/W byte */
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 8/9] hw/sensor: add Renesas raa229004 PMBus device

2022-03-01 Thread Titus Rwantare
The Renesas RAA229004 is a PMBus Multiphase Voltage Regulator

Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/sensor/isl_pmbus_vr.c | 18 ++
 include/hw/sensor/isl_pmbus_vr.h |  1 +
 tests/qtest/isl_pmbus_vr-test.c  |  8 
 3 files changed, 27 insertions(+)

diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c
index b3d24e40ab..e260faeac3 100644
--- a/hw/sensor/isl_pmbus_vr.c
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -195,6 +195,15 @@ static void isl69260_class_init(ObjectClass *klass, void 
*data)
 isl_pmbus_vr_class_init(klass, data, 2);
 }
 
+static void raa229004_class_init(ObjectClass *klass, void *data)
+{
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+DeviceClass *dc = DEVICE_CLASS(klass);
+dc->desc = "Renesas 229004 Digital Multiphase Voltage Regulator";
+rc->phases.exit = isl_pmbus_vr_exit_reset;
+isl_pmbus_vr_class_init(klass, data, 2);
+}
+
 static const TypeInfo isl69260_info = {
 .name = TYPE_ISL69260,
 .parent = TYPE_PMBUS_DEVICE,
@@ -203,9 +212,18 @@ static const TypeInfo isl69260_info = {
 .class_init = isl69260_class_init,
 };
 
+static const TypeInfo raa229004_info = {
+.name = TYPE_RAA229004,
+.parent = TYPE_PMBUS_DEVICE,
+.instance_size = sizeof(ISLState),
+.instance_init = raa22xx_init,
+.class_init = raa229004_class_init,
+};
+
 static void isl_pmbus_vr_register_types(void)
 {
 type_register_static(_info);
+type_register_static(_info);
 }
 
 type_init(isl_pmbus_vr_register_types)
diff --git a/include/hw/sensor/isl_pmbus_vr.h b/include/hw/sensor/isl_pmbus_vr.h
index 4e12e95efb..233916f70a 100644
--- a/include/hw/sensor/isl_pmbus_vr.h
+++ b/include/hw/sensor/isl_pmbus_vr.h
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 
 #define TYPE_ISL69260   "isl69260"
+#define TYPE_RAA229004  "raa229004"
 
 struct ISLState {
 PMBusDevice parent;
diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c
index f77732ae96..a33dfb6135 100644
--- a/tests/qtest/isl_pmbus_vr-test.c
+++ b/tests/qtest/isl_pmbus_vr-test.c
@@ -384,11 +384,19 @@ static void isl_pmbus_vr_register_nodes(void)
 qos_node_create_driver("isl69260", i2c_device_create);
 qos_node_consumes("isl69260", "i2c-bus", );
 
+qos_node_create_driver("raa229004", i2c_device_create);
+qos_node_consumes("raa229004", "i2c-bus", );
+
 qos_add_test("test_defaults", "isl69260", test_defaults, NULL);
 qos_add_test("test_tx_rx", "isl69260", test_tx_rx, NULL);
 qos_add_test("test_rw_regs", "isl69260", test_rw_regs, NULL);
 qos_add_test("test_pages_rw", "isl69260", test_pages_rw, NULL);
 qos_add_test("test_ro_regs", "isl69260", test_ro_regs, NULL);
 qos_add_test("test_ov_faults", "isl69260", test_voltage_faults, NULL);
+
+qos_add_test("test_tx_rx", "raa229004", test_tx_rx, NULL);
+qos_add_test("test_rw_regs", "raa229004", test_rw_regs, NULL);
+qos_add_test("test_pages_rw", "raa229004", test_pages_rw, NULL);
+qos_add_test("test_ov_faults", "raa229004", test_voltage_faults, NULL);
 }
 libqos_init(isl_pmbus_vr_register_nodes);
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 6/9] hw/i2c: Added linear mode translation for pmbus devices

2022-03-01 Thread Titus Rwantare
From: Shengtan Mao 

Signed-off-by: Shengtan Mao 
Reviewed-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 18 ++
 include/hw/i2c/pmbus_device.h | 20 +++-
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 3beb02afad..1036c41c49 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -28,6 +28,24 @@ uint32_t pmbus_direct_mode2data(PMBusCoefficients c, 
uint16_t value)
 return x;
 }
 
+uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
+{
+/* L = D * 2^(-e) */
+if (exp < 0) {
+return value << (-exp);
+}
+return value >> exp;
+}
+
+uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
+{
+/* D = L * 2^e */
+if (exp < 0) {
+return value >> (-exp);
+}
+return value << exp;
+}
+
 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
 {
 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 72c0483149..9a274247ab 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -446,7 +446,7 @@ typedef struct PMBusCoefficients {
  *
  * Y = (m * x - b) * 10^R
  *
- * @return uint32_t
+ * @return uint16_t
  */
 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value);
 
@@ -459,6 +459,24 @@ uint16_t pmbus_data2direct_mode(PMBusCoefficients c, 
uint32_t value);
  */
 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value);
 
+/**
+ * Convert sensor values to linear mode format
+ *
+ * L = D * 2^(-e)
+ *
+ * @return uint16
+ */
+uint16_t pmbus_data2linear_mode(uint16_t value, int exp);
+
+/**
+ * Convert linear mode formatted data into sensor reading
+ *
+ * D = L * 2^e
+ *
+ * @return uint16
+ */
+uint16_t pmbus_linear_mode2data(uint16_t value, int exp);
+
 /**
  * @brief Send a block of data over PMBus
  * Assumes that the bytes in the block are already ordered correctly,
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v3 1/9] hw/i2c: pmbus: add registers

2022-03-01 Thread Titus Rwantare
   - add the VOUT_MIN and STATUS_MFR registers

Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 24 
 include/hw/i2c/pmbus_device.h |  3 +++
 2 files changed, 27 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 24f8f522d9..07a45c99f9 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -368,6 +368,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_VOUT_MIN:/* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
+pmbus_send16(pmdev, pmdev->pages[index].vout_min);
+} else {
+goto passthough;
+}
+break;
+
 /* TODO: implement coefficients support */
 
 case PMBUS_POUT_MAX:  /* R/W word */
@@ -708,6 +716,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 pmbus_send8(pmdev, pmdev->pages[index].status_other);
 break;
 
+case PMBUS_STATUS_MFR_SPECIFIC:   /* R/W byte */
+pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
+break;
+
 case PMBUS_READ_EIN:  /* Read-Only block 5 bytes */
 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
@@ -1149,6 +1161,14 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 }
 break;
 
+case PMBUS_VOUT_MIN:  /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
+pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
+} else {
+goto passthrough;
+}
+break;
+
 case PMBUS_POUT_MAX:  /* R/W word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
@@ -1482,6 +1502,10 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
 break;
 
+case PMBUS_STATUS_MFR_SPECIFIC:/* R/W byte */
+pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
+break;
+
 case PMBUS_PAGE_PLUS_READ:/* Block Read-only */
 case PMBUS_CAPABILITY:/* Read-Only byte */
 case PMBUS_COEFFICIENTS:  /* Read-only block 5 bytes */
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 62bd38c83f..72c0483149 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -43,6 +43,7 @@ enum pmbus_registers {
 PMBUS_VOUT_DROOP= 0x28, /* R/W word */
 PMBUS_VOUT_SCALE_LOOP   = 0x29, /* R/W word */
 PMBUS_VOUT_SCALE_MONITOR= 0x2A, /* R/W word */
+PMBUS_VOUT_MIN  = 0x2B, /* R/W word */
 PMBUS_COEFFICIENTS  = 0x30, /* Read-only block 5 bytes */
 PMBUS_POUT_MAX  = 0x31, /* R/W word */
 PMBUS_MAX_DUTY  = 0x32, /* R/W word */
@@ -255,6 +256,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 #define PB_HAS_TEMP3   BIT_ULL(42)
 #define PB_HAS_TEMP_RATING BIT_ULL(43)
 #define PB_HAS_MFR_INFOBIT_ULL(50)
+#define PB_HAS_STATUS_MFR_SPECIFIC BIT_ULL(51)
 
 struct PMBusDeviceClass {
 SMBusDeviceClass parent_class;
@@ -295,6 +297,7 @@ typedef struct PMBusPage {
 uint16_t vout_droop;   /* R/W word */
 uint16_t vout_scale_loop;  /* R/W word */
 uint16_t vout_scale_monitor;   /* R/W word */
+uint16_t vout_min; /* R/W word */
 uint8_t coefficients[5];   /* Read-only block 5 bytes */
 uint16_t pout_max; /* R/W word */
 uint16_t max_duty; /* R/W word */
-- 
2.35.1.616.g0bdcbb4464-goog




Re: [PATCH v2 9/9] hw/sensor: rename isl_pmbus to isl_pmbus_vr

2022-03-01 Thread Titus Rwantare
The rename is from feedback in v1 by Peter. I did this in a separate
patch as it's easier to merge, compared to editing the 4 commits
affected by this.

Titus

On Tue, 1 Mar 2022 at 16:43, Corey Minyard  wrote:
>
> On Tue, Mar 01, 2022 at 04:23:07PM -0800, Titus Rwantare wrote:
> > Signed-off-by: Titus Rwantare 
>
> Two things:
>
> * Why do you want to rename this?
>
> * This patch doesn't really add anything, it just renames things in a
>   previous patch.  Can it be folded in to the patch that added these
>   fields?
>
> -corey
>
> > ---
> >  MAINTAINERS   |   6 +-
> >  hw/arm/Kconfig|   2 +-
> >  hw/sensor/Kconfig |   2 +-
> >  hw/sensor/{isl_pmbus.c => isl_pmbus_vr.c} |  77 ++---
> >  hw/sensor/meson.build |   2 +-
> >  .../hw/sensor/{isl_pmbus.h => isl_pmbus_vr.h} |   4 +-
> >  .../{isl_pmbus-test.c => isl_pmbus_vr-test.c} | 263 +-
> >  tests/qtest/meson.build   |   2 +-
> >  8 files changed, 180 insertions(+), 178 deletions(-)
> >  rename hw/sensor/{isl_pmbus.c => isl_pmbus_vr.c} (80%)
> >  rename include/hw/sensor/{isl_pmbus.h => isl_pmbus_vr.h} (96%)
> >  rename tests/qtest/{isl_pmbus-test.c => isl_pmbus_vr-test.c} (55%)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 3df8e938e0..364a844045 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -3140,13 +3140,13 @@ M: Titus Rwantare 
> >  S: Maintained
> >  F: hw/i2c/pmbus_device.c
> >  F: hw/sensor/adm1272.c
> > -F: hw/sensor/isl_pmbus.c
> > +F: hw/sensor/isl_pmbus_vr.c
> >  F: hw/sensor/max34451.c
> >  F: include/hw/i2c/pmbus_device.h
> > -F: include/hw/sensor/isl_pmbus.h
> > +F: include/hw/sensor/isl_pmbus_vr.h
> >  F: tests/qtest/adm1272-test.c
> >  F: tests/qtest/max34451-test.c
> > -F: tests/qtest/isl_pmbus-test.c
> > +F: tests/qtest/isl_pmbus_vr-test.c
> >
> >  Firmware schema specifications
> >  M: Philippe Mathieu-Daudé 
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index 8047ad6378..97f3b38019 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -400,7 +400,7 @@ config NPCM7XX
> >  select SMBUS
> >  select AT24C  # EEPROM
> >  select MAX34451
> > -select ISL_PMBUS
> > +select ISL_PMBUS_VR
> >  select PL310  # cache controller
> >  select PMBUS
> >  select SERIAL
> > diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
> > index 70fb335789..a834d2f814 100644
> > --- a/hw/sensor/Kconfig
> > +++ b/hw/sensor/Kconfig
> > @@ -31,7 +31,7 @@ config LSM303DLHC_MAG
> >  depends on I2C
> >  default y if I2C_DEVICES
> >
> > -config ISL_PMBUS
> > +config ISL_PMBUS_VR
> >  bool
> >  depends on I2C
> >
> > diff --git a/hw/sensor/isl_pmbus.c b/hw/sensor/isl_pmbus_vr.c
> > similarity index 80%
> > rename from hw/sensor/isl_pmbus.c
> > rename to hw/sensor/isl_pmbus_vr.c
> > index e3b42b119e..df7c003ea6 100644
> > --- a/hw/sensor/isl_pmbus.c
> > +++ b/hw/sensor/isl_pmbus_vr.c
> > @@ -7,13 +7,13 @@
> >   */
> >
> >  #include "qemu/osdep.h"
> > -#include "hw/sensor/isl_pmbus.h"
> > +#include "hw/sensor/isl_pmbus_vr.h"
> >  #include "hw/qdev-properties.h"
> >  #include "qapi/visitor.h"
> >  #include "qemu/log.h"
> >  #include "qemu/module.h"
> >
> > -static uint8_t isl_pmbus_read_byte(PMBusDevice *pmdev)
> > +static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev)
> >  {
> >  qemu_log_mask(LOG_GUEST_ERROR,
> >"%s: reading from unsupported register: 0x%02x\n",
> > @@ -21,7 +21,7 @@ static uint8_t isl_pmbus_read_byte(PMBusDevice *pmdev)
> >  return 0xFF;
> >  }
> >
> > -static int isl_pmbus_write_data(PMBusDevice *pmdev, const uint8_t *buf,
> > +static int isl_pmbus_vr_write_data(PMBusDevice *pmdev, const uint8_t *buf,
> >uint8_t len)
> >  {
> >  qemu_log_mask(LOG_GUEST_ERROR,
> > @@ -31,13 +31,13 @@ static int isl_pmbus_write_data(PMBusDevice *pmdev, 
> > const uint8_t *buf,
> >  }
> >
> >  /* TODO: Implement coefficients support in pmbus_device.c for qmp */
> > -static void isl_pmbus_get(Object *obj, Visitor *v, const char *name,
> > +static void isl_pmbus_vr_get(Object *obj, Visitor *v, const char *name,
> > 

[PATCH v2 9/9] hw/sensor: rename isl_pmbus to isl_pmbus_vr

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 MAINTAINERS   |   6 +-
 hw/arm/Kconfig|   2 +-
 hw/sensor/Kconfig |   2 +-
 hw/sensor/{isl_pmbus.c => isl_pmbus_vr.c} |  77 ++---
 hw/sensor/meson.build |   2 +-
 .../hw/sensor/{isl_pmbus.h => isl_pmbus_vr.h} |   4 +-
 .../{isl_pmbus-test.c => isl_pmbus_vr-test.c} | 263 +-
 tests/qtest/meson.build   |   2 +-
 8 files changed, 180 insertions(+), 178 deletions(-)
 rename hw/sensor/{isl_pmbus.c => isl_pmbus_vr.c} (80%)
 rename include/hw/sensor/{isl_pmbus.h => isl_pmbus_vr.h} (96%)
 rename tests/qtest/{isl_pmbus-test.c => isl_pmbus_vr-test.c} (55%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3df8e938e0..364a844045 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3140,13 +3140,13 @@ M: Titus Rwantare 
 S: Maintained
 F: hw/i2c/pmbus_device.c
 F: hw/sensor/adm1272.c
-F: hw/sensor/isl_pmbus.c
+F: hw/sensor/isl_pmbus_vr.c
 F: hw/sensor/max34451.c
 F: include/hw/i2c/pmbus_device.h
-F: include/hw/sensor/isl_pmbus.h
+F: include/hw/sensor/isl_pmbus_vr.h
 F: tests/qtest/adm1272-test.c
 F: tests/qtest/max34451-test.c
-F: tests/qtest/isl_pmbus-test.c
+F: tests/qtest/isl_pmbus_vr-test.c
 
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 8047ad6378..97f3b38019 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -400,7 +400,7 @@ config NPCM7XX
 select SMBUS
 select AT24C  # EEPROM
 select MAX34451
-select ISL_PMBUS
+select ISL_PMBUS_VR
 select PL310  # cache controller
 select PMBUS
 select SERIAL
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index 70fb335789..a834d2f814 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -31,7 +31,7 @@ config LSM303DLHC_MAG
 depends on I2C
 default y if I2C_DEVICES
 
-config ISL_PMBUS
+config ISL_PMBUS_VR
 bool
 depends on I2C
 
diff --git a/hw/sensor/isl_pmbus.c b/hw/sensor/isl_pmbus_vr.c
similarity index 80%
rename from hw/sensor/isl_pmbus.c
rename to hw/sensor/isl_pmbus_vr.c
index e3b42b119e..df7c003ea6 100644
--- a/hw/sensor/isl_pmbus.c
+++ b/hw/sensor/isl_pmbus_vr.c
@@ -7,13 +7,13 @@
  */
 
 #include "qemu/osdep.h"
-#include "hw/sensor/isl_pmbus.h"
+#include "hw/sensor/isl_pmbus_vr.h"
 #include "hw/qdev-properties.h"
 #include "qapi/visitor.h"
 #include "qemu/log.h"
 #include "qemu/module.h"
 
-static uint8_t isl_pmbus_read_byte(PMBusDevice *pmdev)
+static uint8_t isl_pmbus_vr_read_byte(PMBusDevice *pmdev)
 {
 qemu_log_mask(LOG_GUEST_ERROR,
   "%s: reading from unsupported register: 0x%02x\n",
@@ -21,7 +21,7 @@ static uint8_t isl_pmbus_read_byte(PMBusDevice *pmdev)
 return 0xFF;
 }
 
-static int isl_pmbus_write_data(PMBusDevice *pmdev, const uint8_t *buf,
+static int isl_pmbus_vr_write_data(PMBusDevice *pmdev, const uint8_t *buf,
   uint8_t len)
 {
 qemu_log_mask(LOG_GUEST_ERROR,
@@ -31,13 +31,13 @@ static int isl_pmbus_write_data(PMBusDevice *pmdev, const 
uint8_t *buf,
 }
 
 /* TODO: Implement coefficients support in pmbus_device.c for qmp */
-static void isl_pmbus_get(Object *obj, Visitor *v, const char *name,
+static void isl_pmbus_vr_get(Object *obj, Visitor *v, const char *name,
  void *opaque, Error **errp)
 {
 visit_type_uint16(v, name, (uint16_t *)opaque, errp);
 }
 
-static void isl_pmbus_set(Object *obj, Visitor *v, const char *name,
+static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name,
  void *opaque, Error **errp)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
@@ -51,7 +51,7 @@ static void isl_pmbus_set(Object *obj, Visitor *v, const char 
*name,
 pmbus_check_limits(pmdev);
 }
 
-static void isl_pmbus_exit_reset(Object *obj)
+static void isl_pmbus_vr_exit_reset(Object *obj)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 
@@ -92,7 +92,7 @@ static void isl_pmbus_exit_reset(Object *obj)
 /* The raa228000 uses different direct mode coefficents from most isl devices 
*/
 static void raa228000_exit_reset(Object *obj)
 {
-isl_pmbus_exit_reset(obj);
+isl_pmbus_vr_exit_reset(obj);
 
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 
@@ -107,70 +107,70 @@ static void raa228000_exit_reset(Object *obj)
 pmdev->pages[0].read_temperature_3 = 0;
 }
 
-static void isl_pmbus_add_props(Object *obj, uint64_t *flags, uint8_t pages)
+static void isl_pmbus_vr_add_props(Object *obj, uint64_t *flags, uint8_t pages)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
 for (int i = 0; i < pages; i++) {
 if (flags[i] & PB_HAS_VIN) {
 object_property_add(obj, "vin[*]", "uint16",
-isl_pmbus_get,
-isl_pmbus_set,
+  

[PATCH v2 0/9] Fixups for PMBus and new sensors

2022-03-01 Thread Titus Rwantare
This patch series contains updates to PMBus in QEMU along with some PMBus
device models for Renesas regulators.
I have also added myself to MAINTAINERS as this code is in use daily,
where I am responsible for it.

v2:
  - split PMBus commit with updates into individual fixes
  - renamed isl_pmbus[.ch] adding _vr for voltage regulators

Shengtan Mao (1):
  hw/i2c: Added linear mode translation for pmbus devices

Titus Rwantare (8):
  hw/i2c: pmbus: add registers
  hw/i2c: pmbus: guard against out of range accesses
  hw/i2c: pmbus: add PEC unsupported warning
  hw/i2c: pmbus: refactor uint handling and update MAINTAINERS
  hw/sensor: add Intersil ISL69260 device model
  hw/sensor: add Renesas raa229004 PMBus device
  hw/sensor: add Renesas raa228000 device
  hw/sensor: rename isl_pmbus to isl_pmbus_vr

 MAINTAINERS  |  13 +
 hw/arm/Kconfig   |   1 +
 hw/i2c/pmbus_device.c| 106 ++-
 hw/sensor/Kconfig|   5 +
 hw/sensor/isl_pmbus_vr.c | 279 ++
 hw/sensor/meson.build|   1 +
 include/hw/i2c/pmbus_device.h|  23 +-
 include/hw/sensor/isl_pmbus_vr.h |  52 
 tests/qtest/isl_pmbus_vr-test.c  | 474 +++
 tests/qtest/meson.build  |   1 +
 10 files changed, 944 insertions(+), 11 deletions(-)
 create mode 100644 hw/sensor/isl_pmbus_vr.c
 create mode 100644 include/hw/sensor/isl_pmbus_vr.h
 create mode 100644 tests/qtest/isl_pmbus_vr-test.c

-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v2 7/9] hw/sensor: add Renesas raa229004 PMBus device

2022-03-01 Thread Titus Rwantare
The Renesas RAA229004 is a PMBus Multiphase Voltage Regulator

Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/sensor/isl_pmbus.c | 18 ++
 include/hw/sensor/isl_pmbus.h |  1 +
 tests/qtest/isl_pmbus-test.c  |  8 
 3 files changed, 27 insertions(+)

diff --git a/hw/sensor/isl_pmbus.c b/hw/sensor/isl_pmbus.c
index 8cc7220a57..4ff848f663 100644
--- a/hw/sensor/isl_pmbus.c
+++ b/hw/sensor/isl_pmbus.c
@@ -194,6 +194,15 @@ static void isl69260_class_init(ObjectClass *klass, void 
*data)
 isl_pmbus_class_init(klass, data, 2);
 }
 
+static void raa229004_class_init(ObjectClass *klass, void *data)
+{
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+DeviceClass *dc = DEVICE_CLASS(klass);
+dc->desc = "Renesas 229004 Digital Multiphase Voltage Regulator";
+rc->phases.exit = isl_pmbus_exit_reset;
+isl_pmbus_class_init(klass, data, 2);
+}
+
 static const TypeInfo isl69260_info = {
 .name = TYPE_ISL69260,
 .parent = TYPE_PMBUS_DEVICE,
@@ -202,9 +211,18 @@ static const TypeInfo isl69260_info = {
 .class_init = isl69260_class_init,
 };
 
+static const TypeInfo raa229004_info = {
+.name = TYPE_RAA229004,
+.parent = TYPE_PMBUS_DEVICE,
+.instance_size = sizeof(ISLState),
+.instance_init = raa22xx_init,
+.class_init = raa229004_class_init,
+};
+
 static void isl_pmbus_register_types(void)
 {
 type_register_static(_info);
+type_register_static(_info);
 }
 
 type_init(isl_pmbus_register_types)
diff --git a/include/hw/sensor/isl_pmbus.h b/include/hw/sensor/isl_pmbus.h
index 8115aaa698..a947fd3903 100644
--- a/include/hw/sensor/isl_pmbus.h
+++ b/include/hw/sensor/isl_pmbus.h
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 
 #define TYPE_ISL69260   "isl69260"
+#define TYPE_RAA229004  "raa229004"
 
 struct ISLState {
 PMBusDevice parent;
diff --git a/tests/qtest/isl_pmbus-test.c b/tests/qtest/isl_pmbus-test.c
index 59fa67f110..80d6c24ec7 100644
--- a/tests/qtest/isl_pmbus-test.c
+++ b/tests/qtest/isl_pmbus-test.c
@@ -383,11 +383,19 @@ static void isl_pmbus_register_nodes(void)
 qos_node_create_driver("isl69260", i2c_device_create);
 qos_node_consumes("isl69260", "i2c-bus", );
 
+qos_node_create_driver("raa229004", i2c_device_create);
+qos_node_consumes("raa229004", "i2c-bus", );
+
 qos_add_test("test_defaults", "isl69260", test_defaults, NULL);
 qos_add_test("test_tx_rx", "isl69260", test_tx_rx, NULL);
 qos_add_test("test_rw_regs", "isl69260", test_rw_regs, NULL);
 qos_add_test("test_pages_rw", "isl69260", test_pages_rw, NULL);
 qos_add_test("test_ro_regs", "isl69260", test_ro_regs, NULL);
 qos_add_test("test_ov_faults", "isl69260", test_voltage_faults, NULL);
+
+qos_add_test("test_tx_rx", "raa229004", test_tx_rx, NULL);
+qos_add_test("test_rw_regs", "raa229004", test_rw_regs, NULL);
+qos_add_test("test_pages_rw", "raa229004", test_pages_rw, NULL);
+qos_add_test("test_ov_faults", "raa229004", test_voltage_faults, NULL);
 }
 libqos_init(isl_pmbus_register_nodes);
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v2 6/9] hw/sensor: add Intersil ISL69260 device model

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 MAINTAINERS   |   3 +
 hw/arm/Kconfig|   1 +
 hw/sensor/Kconfig |   5 +
 hw/sensor/isl_pmbus.c | 210 ++
 hw/sensor/meson.build |   1 +
 include/hw/sensor/isl_pmbus.h |  50 +
 tests/qtest/isl_pmbus-test.c  | 393 ++
 tests/qtest/meson.build   |   1 +
 8 files changed, 664 insertions(+)
 create mode 100644 hw/sensor/isl_pmbus.c
 create mode 100644 include/hw/sensor/isl_pmbus.h
 create mode 100644 tests/qtest/isl_pmbus-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3601984b5d..3df8e938e0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3140,10 +3140,13 @@ M: Titus Rwantare 
 S: Maintained
 F: hw/i2c/pmbus_device.c
 F: hw/sensor/adm1272.c
+F: hw/sensor/isl_pmbus.c
 F: hw/sensor/max34451.c
 F: include/hw/i2c/pmbus_device.h
+F: include/hw/sensor/isl_pmbus.h
 F: tests/qtest/adm1272-test.c
 F: tests/qtest/max34451-test.c
+F: tests/qtest/isl_pmbus-test.c
 
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 6945330030..8047ad6378 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -400,6 +400,7 @@ config NPCM7XX
 select SMBUS
 select AT24C  # EEPROM
 select MAX34451
+select ISL_PMBUS
 select PL310  # cache controller
 select PMBUS
 select SERIAL
diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig
index 215944decc..70fb335789 100644
--- a/hw/sensor/Kconfig
+++ b/hw/sensor/Kconfig
@@ -30,3 +30,8 @@ config LSM303DLHC_MAG
 bool
 depends on I2C
 default y if I2C_DEVICES
+
+config ISL_PMBUS
+bool
+depends on I2C
+
diff --git a/hw/sensor/isl_pmbus.c b/hw/sensor/isl_pmbus.c
new file mode 100644
index 00..8cc7220a57
--- /dev/null
+++ b/hw/sensor/isl_pmbus.c
@@ -0,0 +1,210 @@
+/*
+ * PMBus device for Renesas Digital Multiphase Voltage Regulators
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sensor/isl_pmbus.h"
+#include "hw/qdev-properties.h"
+#include "qapi/visitor.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+static uint8_t isl_pmbus_read_byte(PMBusDevice *pmdev)
+{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: reading from unsupported register: 0x%02x\n",
+  __func__, pmdev->code);
+return 0xFF;
+}
+
+static int isl_pmbus_write_data(PMBusDevice *pmdev, const uint8_t *buf,
+  uint8_t len)
+{
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: write to unsupported register: 0x%02x\n",
+  __func__, pmdev->code);
+return 0xFF;
+}
+
+/* TODO: Implement coefficients support in pmbus_device.c for qmp */
+static void isl_pmbus_get(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+visit_type_uint16(v, name, (uint16_t *)opaque, errp);
+}
+
+static void isl_pmbus_set(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+uint16_t *internal = opaque;
+uint16_t value;
+if (!visit_type_uint16(v, name, , errp)) {
+return;
+}
+
+*internal = value;
+pmbus_check_limits(pmdev);
+}
+
+static void isl_pmbus_exit_reset(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+
+pmdev->page = 0;
+pmdev->capability = ISL_CAPABILITY_DEFAULT;
+for (int i = 0; i < pmdev->num_pages; i++) {
+pmdev->pages[i].operation = ISL_OPERATION_DEFAULT;
+pmdev->pages[i].on_off_config = ISL_ON_OFF_CONFIG_DEFAULT;
+pmdev->pages[i].vout_mode = ISL_VOUT_MODE_DEFAULT;
+pmdev->pages[i].vout_command = ISL_VOUT_COMMAND_DEFAULT;
+pmdev->pages[i].vout_max = ISL_VOUT_MAX_DEFAULT;
+pmdev->pages[i].vout_margin_high = ISL_VOUT_MARGIN_HIGH_DEFAULT;
+pmdev->pages[i].vout_margin_low = ISL_VOUT_MARGIN_LOW_DEFAULT;
+pmdev->pages[i].vout_transition_rate = 
ISL_VOUT_TRANSITION_RATE_DEFAULT;
+pmdev->pages[i].vout_ov_fault_limit = ISL_VOUT_OV_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ot_fault_limit = ISL_OT_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ot_warn_limit = ISL_OT_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].vin_ov_warn_limit = ISL_VIN_OV_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].vin_uv_warn_limit = ISL_VIN_UV_WARN_LIMIT_DEFAULT;
+pmdev->pages[i].iin_oc_fault_limit = ISL_IIN_OC_FAULT_LIMIT_DEFAULT;
+pmdev->pages[i].ton_delay = ISL_TON_DELAY_DEFAULT;
+pmdev->pages[i].ton_rise = ISL_TON_RISE_DEFAULT;
+pmdev->pages[i].toff_fall = ISL_TOFF_FALL_DEFAULT;
+pmdev->pages[i].revision = ISL_REVISION_DEFAULT;
+
+pmdev->pages[i

[PATCH v2 8/9] hw/sensor: add Renesas raa228000 device

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
Reviewed-by: Hao Wu 
---
 hw/sensor/isl_pmbus.c | 50 ++
 include/hw/sensor/isl_pmbus.h |  1 +
 tests/qtest/isl_pmbus-test.c  | 78 +--
 3 files changed, 126 insertions(+), 3 deletions(-)

diff --git a/hw/sensor/isl_pmbus.c b/hw/sensor/isl_pmbus.c
index 4ff848f663..e3b42b119e 100644
--- a/hw/sensor/isl_pmbus.c
+++ b/hw/sensor/isl_pmbus.c
@@ -89,6 +89,24 @@ static void isl_pmbus_exit_reset(Object *obj)
 }
 }
 
+/* The raa228000 uses different direct mode coefficents from most isl devices 
*/
+static void raa228000_exit_reset(Object *obj)
+{
+isl_pmbus_exit_reset(obj);
+
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+
+pmdev->pages[0].read_vout = 0;
+pmdev->pages[0].read_iout = 0;
+pmdev->pages[0].read_pout = 0;
+pmdev->pages[0].read_vin = 0;
+pmdev->pages[0].read_iin = 0;
+pmdev->pages[0].read_pin = 0;
+pmdev->pages[0].read_temperature_1 = 0;
+pmdev->pages[0].read_temperature_2 = 0;
+pmdev->pages[0].read_temperature_3 = 0;
+}
+
 static void isl_pmbus_add_props(Object *obj, uint64_t *flags, uint8_t pages)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(obj);
@@ -177,6 +195,20 @@ static void raa22xx_init(Object *obj)
 isl_pmbus_add_props(obj, flags, 2);
 }
 
+static void raa228000_init(Object *obj)
+{
+PMBusDevice *pmdev = PMBUS_DEVICE(obj);
+uint64_t flags[1];
+
+flags[0] = PB_HAS_VIN | PB_HAS_VOUT | PB_HAS_VOUT_MODE |
+   PB_HAS_VOUT_RATING | PB_HAS_VOUT_MARGIN | PB_HAS_IIN |
+   PB_HAS_IOUT | PB_HAS_PIN | PB_HAS_POUT | PB_HAS_TEMPERATURE |
+   PB_HAS_TEMP2 | PB_HAS_TEMP3 | PB_HAS_STATUS_MFR_SPECIFIC;
+
+pmbus_page_config(pmdev, 0, flags[0]);
+isl_pmbus_add_props(obj, flags, 1);
+}
+
 static void isl_pmbus_class_init(ObjectClass *klass, void *data, uint8_t pages)
 {
 PMBusDeviceClass *k = PMBUS_DEVICE_CLASS(klass);
@@ -194,6 +226,15 @@ static void isl69260_class_init(ObjectClass *klass, void 
*data)
 isl_pmbus_class_init(klass, data, 2);
 }
 
+static void raa228000_class_init(ObjectClass *klass, void *data)
+{
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+DeviceClass *dc = DEVICE_CLASS(klass);
+dc->desc = "Renesas 228000 Digital Multiphase Voltage Regulator";
+rc->phases.exit = raa228000_exit_reset;
+isl_pmbus_class_init(klass, data, 1);
+}
+
 static void raa229004_class_init(ObjectClass *klass, void *data)
 {
 ResettableClass *rc = RESETTABLE_CLASS(klass);
@@ -219,9 +260,18 @@ static const TypeInfo raa229004_info = {
 .class_init = raa229004_class_init,
 };
 
+static const TypeInfo raa228000_info = {
+.name = TYPE_RAA228000,
+.parent = TYPE_PMBUS_DEVICE,
+.instance_size = sizeof(ISLState),
+.instance_init = raa228000_init,
+.class_init = raa228000_class_init,
+};
+
 static void isl_pmbus_register_types(void)
 {
 type_register_static(_info);
+type_register_static(_info);
 type_register_static(_info);
 }
 
diff --git a/include/hw/sensor/isl_pmbus.h b/include/hw/sensor/isl_pmbus.h
index a947fd3903..7ead1dc4a2 100644
--- a/include/hw/sensor/isl_pmbus.h
+++ b/include/hw/sensor/isl_pmbus.h
@@ -13,6 +13,7 @@
 #include "qom/object.h"
 
 #define TYPE_ISL69260   "isl69260"
+#define TYPE_RAA228000  "raa228000"
 #define TYPE_RAA229004  "raa229004"
 
 struct ISLState {
diff --git a/tests/qtest/isl_pmbus-test.c b/tests/qtest/isl_pmbus-test.c
index 80d6c24ec7..ea62cd4e07 100644
--- a/tests/qtest/isl_pmbus-test.c
+++ b/tests/qtest/isl_pmbus-test.c
@@ -149,6 +149,70 @@ static void test_defaults(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(i2c_value, ==, ISL_REVISION_DEFAULT);
 }
 
+static void raa228000_test_defaults(void *obj, void *data,
+QGuestAllocator *alloc)
+{
+uint16_t value, i2c_value;
+QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+value = qmp_isl_pmbus_get(TEST_ID, "vout[0]");
+g_assert_cmpuint(value, ==, 0);
+
+i2c_value = isl_pmbus_i2c_get16(i2cdev, PMBUS_READ_IOUT);
+g_assert_cmpuint(i2c_value, ==, 0);
+
+value = qmp_isl_pmbus_get(TEST_ID, "pout[0]");
+g_assert_cmpuint(value, ==, 0);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_CAPABILITY);
+g_assert_cmphex(i2c_value, ==, ISL_CAPABILITY_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_OPERATION);
+g_assert_cmphex(i2c_value, ==, ISL_OPERATION_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_ON_OFF_CONFIG);
+g_assert_cmphex(i2c_value, ==, ISL_ON_OFF_CONFIG_DEFAULT);
+
+i2c_value = i2c_get8(i2cdev, PMBUS_VOUT_MODE);
+g_assert_cmphex(i2c_value, ==, ISL_VOUT_MODE_DEFAULT);
+
+i2c_value = isl_pmbus_i2c_get16(i2cdev, PMBUS_VOUT_COMMAND);
+g_assert_cmphex(i2c_value, ==, ISL_VOUT_COMMAND_DEFAULT);
+
+i2c_value = isl_pmbus_i2c_get16(i2cdev, PMBUS_VOUT_MAX);
+g_assert_cmphex(i2c_

[PATCH v2 2/9] hw/i2c: pmbus: guard against out of range accesses

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 41 -
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 07a45c99f9..93c746bab3 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -243,18 +243,47 @@ void pmbus_check_limits(PMBusDevice *pmdev)
 }
 }
 
+/* assert the status_cml error upon receipt of malformed command */
+static void pmbus_cml_error(PMBusDevice *pmdev)
+{
+for (int i = 0; i < pmdev->num_pages; i++) {
+pmdev->pages[i].status_word |= PMBUS_STATUS_CML;
+pmdev->pages[i].status_cml |= PB_CML_FAULT_INVALID_CMD;
+}
+}
+
 static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 {
 PMBusDevice *pmdev = PMBUS_DEVICE(smd);
 PMBusDeviceClass *pmdc = PMBUS_DEVICE_GET_CLASS(pmdev);
 uint8_t ret = 0xFF;
-uint8_t index = pmdev->page;
+uint8_t index;
 
 if (pmdev->out_buf_len != 0) {
 ret = pmbus_out_buf_pop(pmdev);
 return ret;
 }
 
+/*
+ * Reading from all pages will return the value from page 0,
+ * this is unspecified behaviour in general.
+ */
+if (pmdev->page == PB_ALL_PAGES) {
+index = 0;
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: tried to read from all pages\n",
+  __func__);
+pmbus_cml_error(pmdev);
+} else if (pmdev->page > pmdev->num_pages - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: page %d is out of range\n",
+  __func__, pmdev->page);
+pmbus_cml_error(pmdev);
+return -1;
+} else {
+index = pmdev->page;
+}
+
 switch (pmdev->code) {
 case PMBUS_PAGE:
 pmbus_send8(pmdev, pmdev->page);
@@ -1038,6 +1067,7 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->page = pmbus_receive8(pmdev);
 return 0;
 }
+
 /* loop through all the pages when 0xFF is received */
 if (pmdev->page == PB_ALL_PAGES) {
 for (int i = 0; i < pmdev->num_pages; i++) {
@@ -1048,6 +1078,15 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 return 0;
 }
 
+if (pmdev->page > pmdev->num_pages - 1) {
+qemu_log_mask(LOG_GUEST_ERROR,
+"%s: page %u is out of range\n",
+__func__, pmdev->page);
+pmdev->page = 0; /* undefined behaviour - reset to page 0 */
+pmbus_cml_error(pmdev);
+return -1;
+}
+
 index = pmdev->page;
 
 switch (pmdev->code) {
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v2 5/9] hw/i2c: Added linear mode translation for pmbus devices

2022-03-01 Thread Titus Rwantare
From: Shengtan Mao 

Signed-off-by: Shengtan Mao 
Reviewed-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 18 ++
 include/hw/i2c/pmbus_device.h | 20 +++-
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 3beb02afad..1036c41c49 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -28,6 +28,24 @@ uint32_t pmbus_direct_mode2data(PMBusCoefficients c, 
uint16_t value)
 return x;
 }
 
+uint16_t pmbus_data2linear_mode(uint16_t value, int exp)
+{
+/* L = D * 2^(-e) */
+if (exp < 0) {
+return value << (-exp);
+}
+return value >> exp;
+}
+
+uint16_t pmbus_linear_mode2data(uint16_t value, int exp)
+{
+/* D = L * 2^e */
+if (exp < 0) {
+return value >> (-exp);
+}
+return value << exp;
+}
+
 void pmbus_send(PMBusDevice *pmdev, const uint8_t *data, uint16_t len)
 {
 if (pmdev->out_buf_len + len > SMBUS_DATA_MAX_LEN) {
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 72c0483149..9a274247ab 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -446,7 +446,7 @@ typedef struct PMBusCoefficients {
  *
  * Y = (m * x - b) * 10^R
  *
- * @return uint32_t
+ * @return uint16_t
  */
 uint16_t pmbus_data2direct_mode(PMBusCoefficients c, uint32_t value);
 
@@ -459,6 +459,24 @@ uint16_t pmbus_data2direct_mode(PMBusCoefficients c, 
uint32_t value);
  */
 uint32_t pmbus_direct_mode2data(PMBusCoefficients c, uint16_t value);
 
+/**
+ * Convert sensor values to linear mode format
+ *
+ * L = D * 2^(-e)
+ *
+ * @return uint16
+ */
+uint16_t pmbus_data2linear_mode(uint16_t value, int exp);
+
+/**
+ * Convert linear mode formatted data into sensor reading
+ *
+ * D = L * 2^e
+ *
+ * @return uint16
+ */
+uint16_t pmbus_linear_mode2data(uint16_t value, int exp);
+
 /**
  * @brief Send a block of data over PMBus
  * Assumes that the bytes in the block are already ordered correctly,
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v2 4/9] hw/i2c: pmbus: refactor uint handling and update MAINTAINERS

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 MAINTAINERS   | 10 ++
 hw/i2c/pmbus_device.c | 18 +-
 2 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index fa8adc2618..3601984b5d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3135,6 +3135,16 @@ F: include/hw/i2c/smbus_master.h
 F: include/hw/i2c/smbus_slave.h
 F: include/hw/i2c/smbus_eeprom.h
 
+PMBus
+M: Titus Rwantare 
+S: Maintained
+F: hw/i2c/pmbus_device.c
+F: hw/sensor/adm1272.c
+F: hw/sensor/max34451.c
+F: include/hw/i2c/pmbus_device.h
+F: tests/qtest/adm1272-test.c
+F: tests/qtest/max34451-test.c
+
 Firmware schema specifications
 M: Philippe Mathieu-Daudé 
 R: Daniel P. Berrange 
diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 6eeb0731d7..3beb02afad 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -89,16 +89,16 @@ void pmbus_send_string(PMBusDevice *pmdev, const char *data)
 }
 
 
-static uint64_t pmbus_receive_uint(const uint8_t *buf, uint8_t len)
+static uint64_t pmbus_receive_uint(PMBusDevice *pmdev)
 {
 uint64_t ret = 0;
 
 /* Exclude command code from return value */
-buf++;
-len--;
+pmdev->in_buf++;
+pmdev->in_buf_len--;
 
-for (int i = len - 1; i >= 0; i--) {
-ret = ret << 8 | buf[i];
+for (int i = pmdev->in_buf_len - 1; i >= 0; i--) {
+ret = ret << 8 | pmdev->in_buf[i];
 }
 return ret;
 }
@@ -110,7 +110,7 @@ uint8_t pmbus_receive8(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 1 byte, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint16_t pmbus_receive16(PMBusDevice *pmdev)
@@ -120,7 +120,7 @@ uint16_t pmbus_receive16(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 2 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint32_t pmbus_receive32(PMBusDevice *pmdev)
@@ -130,7 +130,7 @@ uint32_t pmbus_receive32(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 4 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 uint64_t pmbus_receive64(PMBusDevice *pmdev)
@@ -140,7 +140,7 @@ uint64_t pmbus_receive64(PMBusDevice *pmdev)
   "%s: length mismatch. Expected 8 bytes, got %d bytes\n",
   __func__, pmdev->in_buf_len - 1);
 }
-return pmbus_receive_uint(pmdev->in_buf, pmdev->in_buf_len);
+return pmbus_receive_uint(pmdev);
 }
 
 static uint8_t pmbus_out_buf_pop(PMBusDevice *pmdev)
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v2 3/9] hw/i2c: pmbus: add PEC unsupported warning

2022-03-01 Thread Titus Rwantare
Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 93c746bab3..6eeb0731d7 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -307,6 +307,11 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 
 case PMBUS_CAPABILITY:
 pmbus_send8(pmdev, pmdev->capability);
+if (pmdev->capability & BIT(7)) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: PEC is enabled but not yet supported.\n",
+  __func__);
+}
 break;
 
 case PMBUS_VOUT_MODE: /* R/W byte */
-- 
2.35.1.616.g0bdcbb4464-goog




[PATCH v2 1/9] hw/i2c: pmbus: add registers

2022-03-01 Thread Titus Rwantare
   - add the VOUT_MIN and STATUS_MFR registers

Signed-off-by: Titus Rwantare 
---
 hw/i2c/pmbus_device.c | 24 
 include/hw/i2c/pmbus_device.h |  3 +++
 2 files changed, 27 insertions(+)

diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c
index 24f8f522d9..07a45c99f9 100644
--- a/hw/i2c/pmbus_device.c
+++ b/hw/i2c/pmbus_device.c
@@ -368,6 +368,14 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 }
 break;
 
+case PMBUS_VOUT_MIN:/* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
+pmbus_send16(pmdev, pmdev->pages[index].vout_min);
+} else {
+goto passthough;
+}
+break;
+
 /* TODO: implement coefficients support */
 
 case PMBUS_POUT_MAX:  /* R/W word */
@@ -708,6 +716,10 @@ static uint8_t pmbus_receive_byte(SMBusDevice *smd)
 pmbus_send8(pmdev, pmdev->pages[index].status_other);
 break;
 
+case PMBUS_STATUS_MFR_SPECIFIC:   /* R/W byte */
+pmbus_send8(pmdev, pmdev->pages[index].status_mfr_specific);
+break;
+
 case PMBUS_READ_EIN:  /* Read-Only block 5 bytes */
 if (pmdev->pages[index].page_flags & PB_HAS_EIN) {
 pmbus_send(pmdev, pmdev->pages[index].read_ein, 5);
@@ -1149,6 +1161,14 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 }
 break;
 
+case PMBUS_VOUT_MIN:  /* R/W word */
+if (pmdev->pages[index].page_flags & PB_HAS_VOUT_RATING) {
+pmdev->pages[index].vout_min = pmbus_receive16(pmdev);
+} else {
+goto passthrough;
+}
+break;
+
 case PMBUS_POUT_MAX:  /* R/W word */
 if (pmdev->pages[index].page_flags & PB_HAS_VOUT) {
 pmdev->pages[index].pout_max = pmbus_receive16(pmdev);
@@ -1482,6 +1502,10 @@ static int pmbus_write_data(SMBusDevice *smd, uint8_t 
*buf, uint8_t len)
 pmdev->pages[index].status_other = pmbus_receive8(pmdev);
 break;
 
+case PMBUS_STATUS_MFR_SPECIFIC:/* R/W byte */
+pmdev->pages[index].status_mfr_specific = pmbus_receive8(pmdev);
+break;
+
 case PMBUS_PAGE_PLUS_READ:/* Block Read-only */
 case PMBUS_CAPABILITY:/* Read-Only byte */
 case PMBUS_COEFFICIENTS:  /* Read-only block 5 bytes */
diff --git a/include/hw/i2c/pmbus_device.h b/include/hw/i2c/pmbus_device.h
index 62bd38c83f..72c0483149 100644
--- a/include/hw/i2c/pmbus_device.h
+++ b/include/hw/i2c/pmbus_device.h
@@ -43,6 +43,7 @@ enum pmbus_registers {
 PMBUS_VOUT_DROOP= 0x28, /* R/W word */
 PMBUS_VOUT_SCALE_LOOP   = 0x29, /* R/W word */
 PMBUS_VOUT_SCALE_MONITOR= 0x2A, /* R/W word */
+PMBUS_VOUT_MIN  = 0x2B, /* R/W word */
 PMBUS_COEFFICIENTS  = 0x30, /* Read-only block 5 bytes */
 PMBUS_POUT_MAX  = 0x31, /* R/W word */
 PMBUS_MAX_DUTY  = 0x32, /* R/W word */
@@ -255,6 +256,7 @@ OBJECT_DECLARE_TYPE(PMBusDevice, PMBusDeviceClass,
 #define PB_HAS_TEMP3   BIT_ULL(42)
 #define PB_HAS_TEMP_RATING BIT_ULL(43)
 #define PB_HAS_MFR_INFOBIT_ULL(50)
+#define PB_HAS_STATUS_MFR_SPECIFIC BIT_ULL(51)
 
 struct PMBusDeviceClass {
 SMBusDeviceClass parent_class;
@@ -295,6 +297,7 @@ typedef struct PMBusPage {
 uint16_t vout_droop;   /* R/W word */
 uint16_t vout_scale_loop;  /* R/W word */
 uint16_t vout_scale_monitor;   /* R/W word */
+uint16_t vout_min; /* R/W word */
 uint8_t coefficients[5];   /* Read-only block 5 bytes */
 uint16_t pout_max; /* R/W word */
 uint16_t max_duty; /* R/W word */
-- 
2.35.1.616.g0bdcbb4464-goog




Re: [PATCH 3/5] hw/sensor: add Intersil ISL69260 device model

2022-01-27 Thread Titus Rwantare
On Thu, 27 Jan 2022 at 11:39, Peter Maydell  wrote:
>
> On Thu, 6 Jan 2022 at 23:19, Titus Rwantare  wrote:
> >

> > +static uint8_t isl_pmbus_read_byte(PMBusDevice *pmdev)
> > +{
> > +qemu_log_mask(LOG_GUEST_ERROR,
> > +  "%s: reading from unsupported register: 0x%02x\n",
> > +  __func__, pmdev->code);
> > +return 0xFF;
> > +}
> > +
> > +static int isl_pmbus_write_data(PMBusDevice *pmdev, const uint8_t *buf,
> > +  uint8_t len)
> > +{
> > +qemu_log_mask(LOG_GUEST_ERROR,
> > +  "%s: write to unsupported register: 0x%02x\n",
> > +  __func__, pmdev->code);
> > +return 0xFF;
> > +}
>
> This device appears to have no implemented guest visible
> interface at all, and yet it has a lot of object properties.
> What's going on here ?
>
> thanks
> -- PMM

This device relies on read_byte and write_data implemented in
pmbus_device.c. Those generic implementations fall through to the
device specific implementations for registers not in the standard.
This qemu model happens not to include additional registers. However,
I must change these to LOG_UNIMP which is more appropriate to what's
going on.

Thanks,
Titus



  1   2   >