Re: [PATCH v3 0/8] PMBus fixes and new functions
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
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
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
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
- 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
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
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
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
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
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
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
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
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
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,
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
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
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
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
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
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
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
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
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
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
- 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
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
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
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
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
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
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
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
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
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
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
- 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
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