[PATCH] gpio/pca955x: Update maintainer email address

2024-03-28 Thread Glenn Miles
It was noticed that my linux.vnet.ibm.com address does not
always work so dropping the vnet to see if that works better.

Signed-off-by: Glenn Miles 
---
 MAINTAINERS | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index a07af6b9d4..575ac2e05d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1545,7 +1545,7 @@ F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
 pca955x
-M: Glenn Miles 
+M: Glenn Miles 
 L: qemu-...@nongnu.org
 L: qemu-...@nongnu.org
 S: Odd Fixes
-- 
2.31.8




[PATCH v4 4/4] target/ppc: Add migration support for BHRB

2024-03-28 Thread Glenn Miles
Adds migration support for Branch History Rolling
Buffer (BHRB) internal state.

Signed-off-by: Glenn Miles 
Reviewed-by: Nicholas Piggin 
---

Changes from v3:
  - Rebased onto latest master branch

 target/ppc/machine.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/target/ppc/machine.c b/target/ppc/machine.c
index 6b6c31d903..731dd8df35 100644
--- a/target/ppc/machine.c
+++ b/target/ppc/machine.c
@@ -711,6 +711,26 @@ static const VMStateDescription vmstate_reservation = {
 }
 };
 
+#ifdef TARGET_PPC64
+static bool bhrb_needed(void *opaque)
+{
+PowerPCCPU *cpu = opaque;
+return (cpu->env.flags & POWERPC_FLAG_BHRB) != 0;
+}
+
+static const VMStateDescription vmstate_bhrb = {
+.name = "cpu/bhrb",
+.version_id = 1,
+.minimum_version_id = 1,
+.needed = bhrb_needed,
+.fields = (VMStateField[]) {
+VMSTATE_UINTTL(env.bhrb_offset, PowerPCCPU),
+VMSTATE_UINT64_ARRAY(env.bhrb, PowerPCCPU, BHRB_MAX_NUM_ENTRIES),
+VMSTATE_END_OF_LIST()
+}
+};
+#endif
+
 const VMStateDescription vmstate_ppc_cpu = {
 .name = "cpu",
 .version_id = 5,
@@ -756,6 +776,7 @@ const VMStateDescription vmstate_ppc_cpu = {
 #ifdef TARGET_PPC64
 _tm,
 _slb,
+_bhrb,
 #endif /* TARGET_PPC64 */
 _tlb6xx,
 _tlbemb,
-- 
2.31.8




[PATCH v4 2/4] target/ppc: Add recording of taken branches to BHRB

2024-03-28 Thread Glenn Miles
This commit continues adding support for the Branch History
Rolling Buffer (BHRB) as is provided starting with the P8
processor and continuing with its successors.  This commit
is limited to the recording and filtering of taken branches.

The following changes were made:

  - Enabled functionality on P10 processors only due to
performance impact seen with P8 and P9 where it is not
disabled for non problem state branches.
  - Added a BHRB buffer for storing branch instruction and
target addresses for taken branches
  - Renamed gen_update_cfar to gen_update_branch_history and
added a 'target' parameter to hold the branch target
address and 'inst_type' parameter to use for filtering
  - Added TCG code to gen_update_branch_history that stores
data to the BHRB and updates the BHRB offset.
  - Added BHRB resource initialization and reset functions

Signed-off-by: Glenn Miles 
Reviewed-by: Nicholas Piggin 
---

Changes from v3:
  - Rebased on latest master branch
  - Fixed compile errors for non ppc64-softmmu targets
  - Fixed compile errors from compiling on 32 bit hosts

 target/ppc/cpu.h   | 17 +
 target/ppc/cpu_init.c  | 37 +-
 target/ppc/power8-pmu.c| 33 +
 target/ppc/power8-pmu.h|  7 ++
 target/ppc/translate.c | 98 --
 target/ppc/translate/branch-impl.c.inc |  2 +-
 6 files changed, 186 insertions(+), 8 deletions(-)

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 3f6b93ca8f..7a62a82d03 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -550,6 +550,8 @@ FIELD(MSR, LE, MSR_LE, 1)
  MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0)
 
 #define MMCRA_BHRBRDPPC_BIT(26) /* BHRB Recording Disable */
+#define MMCRA_IFM_MASK  PPC_BITMASK(32, 33) /* BHRB Instruction Filtering */
+#define MMCRA_IFM_SHIFT PPC_BIT_NR(33)
 
 #define MMCR1_EVT_SIZE 8
 /* extract64() does a right shift before extracting */
@@ -776,6 +778,8 @@ enum {
 POWERPC_FLAG_SMT  = 0x0040,
 /* Using "LPAR per core" mode  (as opposed to per-thread)*/
 POWERPC_FLAG_SMT_1LPAR = 0x0080,
+/* Has BHRB */
+POWERPC_FLAG_BHRB  = 0x0100,
 };
 
 /*
@@ -1217,6 +1221,9 @@ struct pnv_tod_tbst {
 #define PPC_CPU_OPCODES_LEN  0x40
 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20
 
+#define BHRB_MAX_NUM_ENTRIES_LOG2 (5)
+#define BHRB_MAX_NUM_ENTRIES  (1 << BHRB_MAX_NUM_ENTRIES_LOG2)
+
 struct CPUArchState {
 /* Most commonly used resources during translated code execution first */
 target_ulong gpr[32];  /* general purpose registers */
@@ -1313,6 +1320,16 @@ struct CPUArchState {
 int dcache_line_size;
 int icache_line_size;
 
+#ifdef TARGET_PPC64
+/* Branch History Rolling Buffer (BHRB) resources */
+target_ulong bhrb_num_entries;
+intptr_t bhrb_base;
+target_ulong bhrb_filter;
+target_ulong bhrb_offset;
+target_ulong bhrb_offset_mask;
+uint64_t bhrb[BHRB_MAX_NUM_ENTRIES];
+#endif
+
 /* These resources are used during exception processing */
 /* CPU model definition */
 target_ulong msr_mask;
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 4e65335669..907cdde5a8 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -6142,6 +6142,28 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
 pcc->l1_icache_size = 0x8000;
 }
 
+static void bhrb_init_state(CPUPPCState *env, target_long num_entries_log2)
+{
+if (env->flags & POWERPC_FLAG_BHRB) {
+if (num_entries_log2 > BHRB_MAX_NUM_ENTRIES_LOG2) {
+num_entries_log2 = BHRB_MAX_NUM_ENTRIES_LOG2;
+}
+env->bhrb_num_entries = 1 << num_entries_log2;
+env->bhrb_base = (intptr_t)>bhrb[0];
+env->bhrb_offset_mask = (env->bhrb_num_entries * sizeof(uint64_t)) - 1;
+}
+}
+
+static void bhrb_reset_state(CPUPPCState *env)
+{
+if (env->flags & POWERPC_FLAG_BHRB) {
+env->bhrb_offset = 0;
+env->bhrb_filter = 0;
+memset(env->bhrb, 0, sizeof(env->bhrb));
+}
+}
+
+#define POWER8_BHRB_ENTRIES_LOG2 5
 static void init_proc_POWER8(CPUPPCState *env)
 {
 /* Common Registers */
@@ -6183,6 +6205,8 @@ static void init_proc_POWER8(CPUPPCState *env)
 env->dcache_line_size = 128;
 env->icache_line_size = 128;
 
+bhrb_init_state(env, POWER8_BHRB_ENTRIES_LOG2);
+
 /* Allocate hardware IRQ controller */
 init_excp_POWER8(env);
 ppcPOWER7_irq_init(env_archcpu(env));
@@ -6307,6 +6331,7 @@ static struct ppc_radix_page_info POWER9_radix_page_info 
= {
 };
 #endif /* CONFIG_USER_ONLY */
 
+#define POWER9_BHRB_ENTRIES_LOG2 5
 static void init_proc_POWER9(CPUPPCState *env)
 {
 /* Common Registers */
@@ -6357,6 +6382,8 @@ static void init_proc_POWER9(CPUPPCState *env)
 env->dcache_line_size = 128;
 env

[PATCH v4 1/4] target/ppc: Add new hflags to support BHRB

2024-03-28 Thread Glenn Miles
This commit is preparatory to the addition of Branch History
Rolling Buffer (BHRB) functionality, which is being provided
today starting with the P8 processor.

BHRB uses several SPR register fields to control whether or not
a branch instruction's address (and sometimes target address)
should be recorded.  Checking each of these fields with each
branch instruction using jitted code would lead to a significant
decrease in performance.

Therefore, it was decided that BHRB configuration bits that are
not expected to change frequently should have their state summarized
in an hflag so that the amount of checking done by jitted code can
be reduced.

This commit contains the changes for summarizing the state of the
following register fields in the HFLAGS_BHRB_ENABLE hflag:

MMCR0[FCP] - Determines if BHRB recording is frozen in the
 problem state

MMCR0[FCPC] - A modifier for MMCR0[FCP]

MMCRA[BHRBRD] - Disables all BHRB recording for a thread

Signed-off-by: Glenn Miles 
Reviewed-by: Nicholas Piggin 
---

Changes from v3:
  - Rebased on latest master branch
  - Fixed compile errors from non ppc64-softmmu targets

 target/ppc/cpu.h |  5 +
 target/ppc/cpu_init.c|  4 ++--
 target/ppc/helper.h  |  1 +
 target/ppc/helper_regs.c | 37 
 target/ppc/machine.c |  2 +-
 target/ppc/power8-pmu-regs.c.inc |  5 +
 target/ppc/power8-pmu.c  | 15 +
 target/ppc/power8-pmu.h  |  4 ++--
 target/ppc/spr_common.h  |  1 +
 target/ppc/translate.c   |  2 ++
 10 files changed, 67 insertions(+), 9 deletions(-)

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 67e6b2effd..3f6b93ca8f 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -535,6 +535,8 @@ FIELD(MSR, LE, MSR_LE, 1)
 #define MMCR0_FC56   PPC_BIT(59) /* PMC Freeze Counters 5-6 bit */
 #define MMCR0_PMC1CE PPC_BIT(48) /* MMCR0 PMC1 Condition Enabled */
 #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */
+#define MMCR0_FCPPPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */
+#define MMCR0_FCPC   PPC_BIT(51) /* Condition for FCP bit */
 /* MMCR0 userspace r/w mask */
 #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE)
 /* MMCR2 userspace r/w mask */
@@ -547,6 +549,8 @@ FIELD(MSR, LE, MSR_LE, 1)
 #define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \
  MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0)
 
+#define MMCRA_BHRBRDPPC_BIT(26) /* BHRB Recording Disable */
+
 #define MMCR1_EVT_SIZE 8
 /* extract64() does a right shift before extracting */
 #define MMCR1_PMC1SEL_START 32
@@ -799,6 +803,7 @@ enum {
 HFLAGS_PMCJCE = 17, /* MMCR0 PMCjCE bit */
 HFLAGS_PMC_OTHER = 18, /* PMC other than PMC5-6 is enabled */
 HFLAGS_INSN_CNT = 19, /* PMU instruction count enabled */
+HFLAGS_BHRB_ENABLE = 20, /* Summary flag for enabling BHRB */
 HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
 HFLAGS_VR = 25,  /* MSR_VR if cpu has VRE */
 
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 7e65f08147..4e65335669 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -5152,7 +5152,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env)
  KVM_REG_PPC_MMCR1, 0x);
 spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA",
  SPR_NOACCESS, SPR_NOACCESS,
- _read_generic, _write_generic,
+ _read_generic, _write_MMCRA,
  KVM_REG_PPC_MMCRA, 0x);
 spr_register_kvm(env, SPR_POWER_PMC1, "PMC1",
  SPR_NOACCESS, SPR_NOACCESS,
@@ -7194,7 +7194,7 @@ static void ppc_cpu_reset_hold(Object *obj)
 if (env->mmu_model != POWERPC_MMU_REAL) {
 ppc_tlb_invalidate_all(env);
 }
-pmu_mmcr01_updated(env);
+pmu_mmcr01a_updated(env);
 }
 
 /* clean any pending stop state */
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 86f97ee1e7..3df360efe9 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -30,6 +30,7 @@ DEF_HELPER_2(store_dawr0, void, env, tl)
 DEF_HELPER_2(store_dawrx0, void, env, tl)
 DEF_HELPER_2(store_mmcr0, void, env, tl)
 DEF_HELPER_2(store_mmcr1, void, env, tl)
+DEF_HELPER_2(store_mmcrA, void, env, tl)
 DEF_HELPER_3(store_pmc, void, env, i32, i64)
 DEF_HELPER_2(read_pmc, tl, env, i32)
 DEF_HELPER_2(insns_inc, void, env, i32)
diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c
index 25258986e3..07a07ae720 100644
--- a/target/ppc/helper_regs.c
+++ b/target/ppc/helper_regs.c
@@ -47,6 +47,39 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env)
 env->tgpr[3] = tmp;
 }
 
+#if defined(TARGET_PPC64)
+static bool hreg_check_bhrb_enable(CPUPPCState *env)
+{
+bool pr = !!(env->msr & (1 << MSR_PR));
+target_

[PATCH v4 3/4] target/ppc: Add clrbhrb and mfbhrbe instructions

2024-03-28 Thread Glenn Miles
Add support for the clrbhrb and mfbhrbe instructions.

Since neither instruction is believed to be critical to
performance, both instructions were implemented using helper
functions.

Access to both instructions is controlled by bits in the
HFSCR (for privileged state) and MMCR0 (for problem state).
A new function, helper_mmcr0_facility_check, was added for
checking MMCR0[BHRBA] and raising a facility_unavailable exception
if required.

NOTE: For P8 and P9, due to a performance issue, branch history will
not be kept, but the instructions will be allowed to execute
as normal with the exception that the mfbhrbe instruction will
always return a zero value.

Signed-off-by: Glenn Miles 
Reviewed-by: Nicholas Piggin 
---

Changes from v3:
  - Rebased on latest master branch
  - Fixed compile errors for non ppc64-softmmu targets

 target/ppc/cpu.h |  2 ++
 target/ppc/helper.h  |  7 
 target/ppc/insn32.decode |  8 +
 target/ppc/misc_helper.c | 50 
 target/ppc/translate.c   |  2 ++
 target/ppc/translate/bhrb-impl.c.inc | 43 
 6 files changed, 112 insertions(+)
 create mode 100644 target/ppc/translate/bhrb-impl.c.inc

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 7a62a82d03..76e896fdda 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -537,6 +537,7 @@ FIELD(MSR, LE, MSR_LE, 1)
 #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */
 #define MMCR0_FCPPPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */
 #define MMCR0_FCPC   PPC_BIT(51) /* Condition for FCP bit */
+#define MMCR0_BHRBA_NR PPC_BIT_NR(42)/* BHRB Available */
 /* MMCR0 userspace r/w mask */
 #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE)
 /* MMCR2 userspace r/w mask */
@@ -636,6 +637,7 @@ FIELD(MSR, LE, MSR_LE, 1)
 
 /* HFSCR bits */
 #define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */
+#define HFSCR_BHRB PPC_BIT(59) /* BHRB Instructions */
 #define HFSCR_IC_MSGP  0xA
 
 #define DBCR0_ICMP (1 << 27)
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 3df360efe9..8cdb322ed6 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -820,3 +820,10 @@ DEF_HELPER_4(DSCLIQ, void, env, fprp, fprp, i32)
 
 DEF_HELPER_1(tbegin, void, env)
 DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env)
+
+#if !defined(CONFIG_USER_ONLY)
+#if defined(TARGET_PPC64)
+DEF_HELPER_1(clrbhrb, void, env)
+DEF_HELPER_FLAGS_2(mfbhrbe, TCG_CALL_NO_WG, i64, env, i32)
+#endif
+#endif
diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode
index eada59f59f..a343621cdd 100644
--- a/target/ppc/insn32.decode
+++ b/target/ppc/insn32.decode
@@ -998,3 +998,11 @@ MSGSND  01 - - . 0011001110 -   
@X_rb
 MSGCLRP 01 - - . 0010101110 -   @X_rb
 MSGSNDP 01 - - . 0010001110 -   @X_rb
 MSGSYNC 01 - - - 1101110110 -
+
+# Branch History Rolling Buffer (BHRB) Instructions
+
+_bhrbe  rt bhrbe
+@XFX_bhrbe  .. rt:5 bhrbe:10 .. -   _bhrbe
+
+MFBHRBE 01 . . . 0100101110 -   @XFX_bhrbe
+CLRBHRB 01 - - - 0110101110 -
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index 58e808dc96..6f419c9346 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -150,6 +150,17 @@ void helper_msr_facility_check(CPUPPCState *env, uint32_t 
bit,
 
 #if !defined(CONFIG_USER_ONLY)
 
+#ifdef TARGET_PPC64
+static void helper_mmcr0_facility_check(CPUPPCState *env, uint32_t bit,
+ uint32_t sprn, uint32_t cause)
+{
+if (FIELD_EX64(env->msr, MSR, PR) &&
+!(env->spr[SPR_POWER_MMCR0] & (1ULL << bit))) {
+raise_fu_exception(env, bit, sprn, cause, GETPC());
+}
+}
+#endif
+
 void helper_store_sdr1(CPUPPCState *env, target_ulong val)
 {
 if (env->spr[SPR_SDR1] != val) {
@@ -363,3 +374,42 @@ void helper_fixup_thrm(CPUPPCState *env)
 env->spr[i] = v;
 }
 }
+
+#if !defined(CONFIG_USER_ONLY)
+#if defined(TARGET_PPC64)
+void helper_clrbhrb(CPUPPCState *env)
+{
+helper_hfscr_facility_check(env, HFSCR_BHRB, "clrbhrb", FSCR_IC_BHRB);
+
+helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB);
+
+if (env->flags & POWERPC_FLAG_BHRB) {
+memset(env->bhrb, 0, sizeof(env->bhrb));
+}
+}
+
+uint64_t helper_mfbhrbe(CPUPPCState *env, uint32_t bhrbe)
+{
+unsigned int index;
+
+helper_hfscr_facility_check(env, HFSCR_BHRB, "mfbhrbe", FSCR_IC_BHRB);
+
+helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB);
+
+if (!(env->flags & POWERPC_FLAG_BHRB) ||
+ (bhrbe >= env->bhrb_num_entries) ||
+ (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) {
+return 0;
+}
+
+/*
+ * Note:

[PATCH v4 0/4] Add BHRB Facility Support

2024-03-28 Thread Glenn Miles
This is a series of patches for adding support for the Branch History
Rolling Buffer (BHRB) facility.  This was added to the Power ISA
starting with version 2.07.  Changes were subsequently made in version
3.1 to limit BHRB recording to instructions run in problem state only
and to add a control bit to disable recording (MMCRA[BHRBRD]).

Changes from previous version:
 - Rebased on latest master head (req'd changing cpu_env to tcg_env)
 - Fixed compiler errors for non ppc64-softmmu targets
 - Fixed compiler errors from compiling on 32-bit platforms

Glenn Miles (4):
  target/ppc: Add new hflags to support BHRB
  target/ppc: Add recording of taken branches to BHRB
  target/ppc: Add clrbhrb and mfbhrbe instructions
  target/ppc: Add migration support for BHRB

 target/ppc/cpu.h   |  24 ++
 target/ppc/cpu_init.c  |  41 +-
 target/ppc/helper.h|   8 ++
 target/ppc/helper_regs.c   |  37 +
 target/ppc/insn32.decode   |   8 ++
 target/ppc/machine.c   |  23 +-
 target/ppc/misc_helper.c   |  50 
 target/ppc/power8-pmu-regs.c.inc   |   5 ++
 target/ppc/power8-pmu.c|  48 +++-
 target/ppc/power8-pmu.h|  11 ++-
 target/ppc/spr_common.h|   1 +
 target/ppc/translate.c | 102 +++--
 target/ppc/translate/bhrb-impl.c.inc   |  43 +++
 target/ppc/translate/branch-impl.c.inc |   2 +-
 14 files changed, 386 insertions(+), 17 deletions(-)
 create mode 100644 target/ppc/translate/bhrb-impl.c.inc

-- 
2.31.8




[PATCH v7 8/9] ppc/pnv: Add a pca9554 I2C device to powernv10-rainier

2024-01-25 Thread Glenn Miles
For powernv10-rainier, the Power Hypervisor code expects to see a
pca9554 device connected to the 3rd PNV I2C engine on port 1 at I2C
address 0x25 (or left-justified address of 0x4A).  This is used by
the hypervisor code to detect if a "Cable Card" is present.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 6 ++
 4 files changed, 12 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 4fc6b29b43..83ad849b62 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 2ca2ce4b62..8bf7a3b8da 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 8e592e4307..d97743d02f 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 42105211f5..40f6b8fea0 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1913,6 +1913,12 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
 qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 
7));
 qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 
8));
 qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 
9));
+
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 }
 
-- 
2.31.1




[PATCH v7 4/9] ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power control

2024-01-25 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/Kconfig   |  1 +
 hw/ppc/pnv.c | 25 +
 include/hw/ppc/pnv.h |  1 +
 3 files changed, 27 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 44263a58c4..8e592e4307 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 08704ce695..d8d19fb065 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -790,6 +790,7 @@ static void pnv_init(MachineState *machine)
 const char *bios_name = machine->firmware ?: FW_FILE_NAME;
 PnvMachineState *pnv = PNV_MACHINE(machine);
 MachineClass *mc = MACHINE_GET_CLASS(machine);
+PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
 char *fw_filename;
 long fw_size;
 uint64_t chip_ram_start = 0;
@@ -979,6 +980,13 @@ static void pnv_init(MachineState *machine)
  */
 pnv->powerdown_notifier.notify = pnv_powerdown_notify;
 qemu_register_powerdown_notifier(>powerdown_notifier);
+
+/*
+ * Create/Connect any machine-specific I2C devices
+ */
+if (pmc->i2c_init) {
+pmc->i2c_init(pnv);
+}
 }
 
 /*
@@ -1879,6 +1887,21 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+}
+
+static void pnv_rainier_i2c_init(PnvMachineState *pnv)
+{
+int i;
+for (i = 0; i < pnv->num_chips; i++) {
+Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+}
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -2286,9 +2309,11 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
+PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
 
 pnv_machine_p10_common_class_init(oc, data);
 mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+pmc->i2c_init = pnv_rainier_i2c_init;
 }
 
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 7e5fef7c43..110ac9aace 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -76,6 +76,7 @@ struct PnvMachineClass {
 int compat_size;
 
 void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt);
+void (*i2c_init)(PnvMachineState *pnv);
 };
 
 struct PnvMachineState {
-- 
2.31.1




[PATCH v7 3/9] ppc/pnv: New powernv10-rainier machine type

2024-01-25 Thread Glenn Miles
Create a new powernv machine type, powernv10-rainier, that
will contain rainier-specific devices.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/pnv.c | 24 ++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0297871bdd..08704ce695 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -2251,7 +2251,7 @@ static void pnv_machine_power9_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
-static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
 PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
@@ -2263,7 +2263,6 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 { TYPE_PNV_PHB_ROOT_PORT, "version", "5" },
 };
 
-mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
 compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
 
@@ -2276,6 +2275,22 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
+static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
+}
+
+static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+}
+
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
 {
 PnvMachineState *pnv = PNV_MACHINE(obj);
@@ -2381,6 +2396,11 @@ static void pnv_machine_class_init(ObjectClass *oc, void 
*data)
 }
 
 static const TypeInfo types[] = {
+{
+.name  = MACHINE_TYPE_NAME("powernv10-rainier"),
+.parent= MACHINE_TYPE_NAME("powernv10"),
+.class_init= pnv_machine_p10_rainier_class_init,
+},
 {
 .name  = MACHINE_TYPE_NAME("powernv10"),
 .parent= TYPE_PNV_MACHINE,
-- 
2.31.1




[PATCH v7 2/9] misc/pca9552: Let external devices set pca9552 inputs

2024-01-25 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/misc/pca9552.c | 50 +--
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index f00a149d61..2ae13af35e 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -110,22 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 for (i = 0; i < k->pin_count; i++) {
 uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-uint8_t input_shift = (i % 8);
+uint8_t bit_mask = 1 << (i % 8);
 uint8_t config = pca955x_pin_get_config(s, i);
+uint8_t old_value = s->regs[input_reg] & bit_mask;
+uint8_t new_value;
 
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+s->regs[input_reg] &= ~bit_mask;
+} else {
+s->regs[input_reg] |=  bit_mask;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -133,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
 default:
 break;
 }
+
+/* update irq state only if pin state changed */
+new_value = s->regs[input_reg] & bit_mask;
+if (new_value != old_value) {
+qemu_set_irq(s->gpio_out[i], !!new_value);
+}
 }
 }
 
@@ -340,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +395,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+qdev_init_gpio_out(dev, s->gpio_out, k->pin_co

[PATCH v7 9/9] ppc/pnv: Test pnv i2c master and connected devices

2024-01-25 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for powernv10-ranier only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Moved PNV I2C register definitions from pnv-host-i2c-test.c and
pnv_i2c.c into pnv_i2c_regs.h.
  - Moved PNV XSCOM definitions from pnv-host-i2c-test.c and
pnv-xscom-test.c into pnv-xscom.h.
  - Renamed pnv_i2c_dev_t to PnvI2cDev.
  - Added PnvI2cCtlr structure for conveniece in passing parameters.

 hw/ppc/pnv_i2c.c| 131 +
 include/hw/i2c/pnv_i2c_regs.h   | 143 ++
 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 491 
 tests/qtest/pnv-xscom-test.c|  61 +---
 tests/qtest/pnv-xscom.h |  80 ++
 6 files changed, 717 insertions(+), 190 deletions(-)
 create mode 100644 include/hw/i2c/pnv_i2c_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c
 create mode 100644 tests/qtest/pnv-xscom.h

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index 774946d6b2..4581cc5e5d 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -22,136 +22,7 @@
 
 #include 
 
-/* I2C FIFO register */
-#define I2C_FIFO_REG0x4
-#define I2C_FIFOPPC_BITMASK(0, 7)
-
-/* I2C command register */
-#define I2C_CMD_REG 0x5
-#define I2C_CMD_WITH_START  PPC_BIT(0)
-#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
-#define I2C_CMD_READ_CONT   PPC_BIT(2)
-#define I2C_CMD_WITH_STOP   PPC_BIT(3)
-#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
-#define   I2C_CMD_INTR_STEER_HOST   1
-#define   I2C_CMD_INTR_STEER_OCC2
-#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
-#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
-#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
-#define I2C_MAX_TFR_LEN 0xfff0ull
-
-/* I2C mode register */
-#define I2C_MODE_REG0x6
-#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
-#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
-#define I2C_MODE_ENHANCED   PPC_BIT(28)
-#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
-#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
-#define I2C_MODE_WRAP   PPC_BIT(31)
-
-/* I2C watermark register */
-#define I2C_WATERMARK_REG   0x7
-#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
-#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
-
-/*
- * I2C interrupt mask and condition registers
- *
- * NB: The function of 0x9 and 0xa changes depending on whether you're reading
- * or writing to them. When read they return the interrupt condition bits
- * and on writes they update the interrupt mask register.
- *
- *  The bit definitions are the same for all the interrupt registers.
- */
-#define I2C_INTR_MASK_REG   0x8
-
-#define I2C_INTR_RAW_COND_REG   0x9 /* read */
-#define I2C_INTR_MASK_OR_REG0x9 /* write*/
-
-#define I2C_INTR_COND_REG   0xa /* read */
-#define I2C_INTR_MASK_AND_REG   0xa /* write */
-
-#define I2C_INTR_ALLPPC_BITMASK(16, 31)
-#define I2C_INTR_INVALID_CMDPPC_BIT(16)
-#define I2C_INTR_LBUS_PARITY_ERRPPC_BIT(17)
-#define I2C_INTR_BKEND_OVERRUN_ERR  PPC_BIT(18)
-#define I2C_INTR_BKEND_ACCESS_ERR   PPC_BIT(19)
-#define I2C_INTR_ARBT_LOST_ERR  PPC_BIT(20)
-#define I2C_INTR_NACK_RCVD_ERR  PPC_BIT(21)
-#define I2C_INTR_DATA_REQ   PPC_BIT(22)
-#define I2C_INTR_CMD_COMP   PPC_BIT(23)
-#define I2C_INTR_STOP_ERR   PPC_BIT(24)
-#define I2C_INTR_I2C_BUSY   PPC_BIT(25)
-#define I2C_INTR_NOT_I2C_BUSY   PPC_BIT(26)
-#define I2C_INTR_SCL_EQ_1   PPC_BIT(28)
-#define I2C_INTR_SCL_EQ_0   PPC_BIT(29)
-#define I2C_INTR_SDA_EQ_1   PPC_BIT(30)
-#define I2C_INTR_SDA_EQ_0   PPC_BIT(31)
-
-/* I2C status register */
-#define I2C_RESET_I2C_REG   0xb /* write */
-#define I2C_RESET_ERRORS0xc
-#define I2C_STAT_REG0xb /* read */
-#define I2C_STAT_INVALID_CMDPPC_BIT(0)
-#define I2C_STAT_LBUS_PARITY_ERRPPC_BIT(1)
-#define I2C_STAT_BKEND_OVERRUN_ERR  PPC_BIT(2)
-#define I2C_STAT_BKEND_ACCESS_ERR   PPC_BIT(3)
-#define I2C_STAT_ARBT_LOST_ERR  PPC_BIT(4)
-#define I2C_STAT_NACK_RCVD_ERR  PPC_BIT(5)
-#define I2C_STAT_DATA_REQ   PPC_BIT(6)
-#define

[PATCH v7 7/9] misc: Add a pca9554 GPIO device model

2024-01-25 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 MAINTAINERS|  10 +-
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 391 insertions(+), 2 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index dfaca8323e..51861e3c7d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1169,9 +1169,7 @@ R: Joel Stanley 
 L: qemu-...@nongnu.org
 S: Maintained
 F: hw/*/*aspeed*
-F: hw/misc/pca9552.c
 F: include/hw/*/*aspeed*
-F: include/hw/misc/pca9552*.h
 F: hw/net/ftgmac100.c
 F: include/hw/net/ftgmac100.h
 F: docs/system/arm/aspeed.rst
@@ -1540,6 +1538,14 @@ F: include/hw/pci-host/pnv*
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
+pca955x
+M: Glenn Miles 
+L: qemu-...@nongnu.org
+L: qemu-...@nongnu.org
+S: Odd Fixes
+F: hw/misc/pca955*.c
+F: include/hw/misc/pca955*.h
+
 virtex_ml507
 M: Edgar E. Iglesias 
 L: qemu-...@nongnu.org
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+ 

[PATCH v7 1/9] misc/pca9552: Fix inverted input status

2024-01-25 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 72b653463f..f00a149d61 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH v7 5/9] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2024-01-25 Thread Glenn Miles
For power10-rainier, a pca9552 device is used for PCIe slot hotplug
power control by the Power Hypervisor code.  The code expects that
some time after it enables power to a PCIe slot by asserting one of
the pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/pnv.c | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index d8d19fb065..42105211f5 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1900,7 +1900,19 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+I2CSlave *dev = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots
+ * after hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(dev), 
5));
+qdev_connect_gpio_out(DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(dev), 
6));
+qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 
7));
+qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 
8));
+qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 
9));
 }
 }
 
-- 
2.31.1




[PATCH v7 6/9] ppc/pnv: Use resettable interface to reset child I2C buses

2024-01-25 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index 656a48eebe..774946d6b2 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -629,6 +629,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -654,7 +667,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH v7 0/9] Add powernv10 I2C devices and tests

2024-01-25 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on the Power10 Ranier platform.

Changes from previous version:
  - Made several changes to commit 9/9 to address comments from
Cédric.
  - Added new PnvI2cCtlr struct to commit 9/9 for convenience in
passing parameters.

Glenn Miles (9):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs
  ppc/pnv: New powernv10-rainier machine type
  ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power
control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10-rainier
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |  10 +-
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9552.c   |  58 +++-
 hw/misc/pca9554.c   | 328 +
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  67 -
 hw/ppc/pnv_i2c.c| 146 +-
 include/hw/i2c/pnv_i2c_regs.h   | 143 ++
 include/hw/misc/pca9552.h   |   3 +-
 include/hw/misc/pca9554.h   |  36 +++
 include/hw/misc/pca9554_regs.h  |  19 ++
 include/hw/ppc/pnv.h|   1 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pca9552-test.c  |   6 +-
 tests/qtest/pnv-host-i2c-test.c | 491 
 tests/qtest/pnv-xscom-test.c|  61 +---
 tests/qtest/pnv-xscom.h |  80 ++
 18 files changed, 1251 insertions(+), 206 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/i2c/pnv_i2c_regs.h
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c
 create mode 100644 tests/qtest/pnv-xscom.h

-- 
2.31.1




[PATCH v6 9/9] ppc/pnv: Test pnv i2c master and connected devices

2023-11-27 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for powernv10-ranier only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Signed-off-by: Glenn Miles 
---

No change from previous version

 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 650 
 2 files changed, 651 insertions(+)
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 47dabf91d0..fbb0bd204c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -163,6 +163,7 @@ qtests_ppc64 = \
   qtests_ppc + \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + 
  \
   (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) +   
  \
+  (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) 
+  \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) +
  \
   (slirp.found() ? ['pxe-test'] : []) +  \
   (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) 
+ \
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
new file mode 100644
index 00..377525e458
--- /dev/null
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -0,0 +1,650 @@
+/*
+ * QTest testcase for PowerNV 10 Host I2C Communications
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/misc/pca9552_regs.h"
+
+#define PPC_BIT(bit)(0x8000ULL >> (bit))
+#define PPC_BIT32(bit)  (0x8000 >> (bit))
+#define PPC_BIT8(bit)   (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+
+#define MASK_TO_LSH(m)  (__builtin_ffsll(m) - 1)
+#define GETFIELD(m, v)  (((v) & (m)) >> MASK_TO_LSH(m))
+#define SETFIELD(m, v, val) \
+(((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
+
+#define P10_XSCOM_BASE  0x000603fcull
+#define PNV10_CHIP_MAX_I2C  5
+#define PNV10_XSCOM_I2CM_BASE   0xa
+#define PNV10_XSCOM_I2CM_SIZE   0x1000
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALLPPC_BITMASK(16, 31)
+#de

[PATCH v6 7/9] misc: Add a pca9554 GPIO device model

2023-11-27 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 MAINTAINERS|  10 +-
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 391 insertions(+), 2 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 695e0bd34f..4d1c991691 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1155,9 +1155,7 @@ R: Joel Stanley 
 L: qemu-...@nongnu.org
 S: Maintained
 F: hw/*/*aspeed*
-F: hw/misc/pca9552.c
 F: include/hw/*/*aspeed*
-F: include/hw/misc/pca9552*.h
 F: hw/net/ftgmac100.c
 F: include/hw/net/ftgmac100.h
 F: docs/system/arm/aspeed.rst
@@ -1526,6 +1524,14 @@ F: include/hw/pci-host/pnv*
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
+pca955x
+M: Glenn Miles 
+L: qemu-...@nongnu.org
+L: qemu-...@nongnu.org
+S: Odd Fixes
+F: hw/misc/pca955*.c
+F: include/hw/misc/pca955*.h
+
 virtex_ml507
 M: Edgar E. Iglesias 
 L: qemu-...@nongnu.org
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+ 

[PATCH v6 2/9] misc/pca9552: Let external devices set pca9552 inputs

2023-11-27 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/misc/pca9552.c | 50 +--
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..fe876471c8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -110,22 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 for (i = 0; i < k->pin_count; i++) {
 uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-uint8_t input_shift = (i % 8);
+uint8_t bit_mask = 1 << (i % 8);
 uint8_t config = pca955x_pin_get_config(s, i);
+uint8_t old_value = s->regs[input_reg] & bit_mask;
+uint8_t new_value;
 
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+s->regs[input_reg] &= ~bit_mask;
+} else {
+s->regs[input_reg] |=  bit_mask;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -133,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
 default:
 break;
 }
+
+/* update irq state only if pin state changed */
+new_value = s->regs[input_reg] & bit_mask;
+if (new_value != old_value) {
+qemu_set_irq(s->gpio_out[i], !!new_value);
+}
 }
 }
 
@@ -340,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +395,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+qdev_init_gpio_out(dev, s->gpio_out, k->pin_co

[PATCH v6 6/9] ppc/pnv: Use resettable interface to reset child I2C buses

2023-11-27 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index 656a48eebe..774946d6b2 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -629,6 +629,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -654,7 +667,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH v6 3/9] ppc/pnv: New powernv10-rainier machine type

2023-11-27 Thread Glenn Miles
Create a new powernv machine type, powernv10-rainier, that
will contain rainier-specific devices.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/ppc/pnv.c | 24 ++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0297871bdd..08704ce695 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -2251,7 +2251,7 @@ static void pnv_machine_power9_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
-static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
 PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
@@ -2263,7 +2263,6 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 { TYPE_PNV_PHB_ROOT_PORT, "version", "5" },
 };
 
-mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
 compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
 
@@ -2276,6 +2275,22 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
+static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
+}
+
+static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+}
+
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
 {
 PnvMachineState *pnv = PNV_MACHINE(obj);
@@ -2381,6 +2396,11 @@ static void pnv_machine_class_init(ObjectClass *oc, void 
*data)
 }
 
 static const TypeInfo types[] = {
+{
+.name  = MACHINE_TYPE_NAME("powernv10-rainier"),
+.parent= MACHINE_TYPE_NAME("powernv10"),
+.class_init= pnv_machine_p10_rainier_class_init,
+},
 {
 .name  = MACHINE_TYPE_NAME("powernv10"),
 .parent= TYPE_PNV_MACHINE,
-- 
2.31.1




[PATCH v6 8/9] ppc/pnv: Add a pca9554 I2C device to powernv10-rainier

2023-11-27 Thread Glenn Miles
For powernv10-rainier, the Power Hypervisor code expects to see a
pca9554 device connected to the 3rd PNV I2C engine on port 1 at I2C
address 0x25 (or left-justified address of 0x4A).  This is used by
the hypervisor code to detect if a "Cable Card" is present.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 6 ++
 4 files changed, 12 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..c347a132c2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..c39410e4a7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f77ca773cf..2302778265 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 42105211f5..40f6b8fea0 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1913,6 +1913,12 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
 qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 
7));
 qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 
8));
 qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 
9));
+
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 }
 
-- 
2.31.1




[PATCH v6 5/9] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2023-11-27 Thread Glenn Miles
For power10-rainier, a pca9552 device is used for PCIe slot hotplug
power control by the Power Hypervisor code.  The code expects that
some time after it enables power to a PCIe slot by asserting one of
the pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Changed 'hotplug' variable name to 'dev'

 hw/ppc/pnv.c | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index d8d19fb065..42105211f5 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1900,7 +1900,19 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+I2CSlave *dev = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots
+ * after hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(dev), 
5));
+qdev_connect_gpio_out(DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(dev), 
6));
+qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 
7));
+qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 
8));
+qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 
9));
 }
 }
 
-- 
2.31.1




[PATCH v6 4/9] ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power control

2023-11-27 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/ppc/Kconfig   |  1 +
 hw/ppc/pnv.c | 25 +
 include/hw/ppc/pnv.h |  1 +
 3 files changed, 27 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 56f0475a8e..f77ca773cf 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 08704ce695..d8d19fb065 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -790,6 +790,7 @@ static void pnv_init(MachineState *machine)
 const char *bios_name = machine->firmware ?: FW_FILE_NAME;
 PnvMachineState *pnv = PNV_MACHINE(machine);
 MachineClass *mc = MACHINE_GET_CLASS(machine);
+PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
 char *fw_filename;
 long fw_size;
 uint64_t chip_ram_start = 0;
@@ -979,6 +980,13 @@ static void pnv_init(MachineState *machine)
  */
 pnv->powerdown_notifier.notify = pnv_powerdown_notify;
 qemu_register_powerdown_notifier(>powerdown_notifier);
+
+/*
+ * Create/Connect any machine-specific I2C devices
+ */
+if (pmc->i2c_init) {
+pmc->i2c_init(pnv);
+}
 }
 
 /*
@@ -1879,6 +1887,21 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+}
+
+static void pnv_rainier_i2c_init(PnvMachineState *pnv)
+{
+int i;
+for (i = 0; i < pnv->num_chips; i++) {
+Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+}
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -2286,9 +2309,11 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
+PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
 
 pnv_machine_p10_common_class_init(oc, data);
 mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+pmc->i2c_init = pnv_rainier_i2c_init;
 }
 
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 7e5fef7c43..110ac9aace 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -76,6 +76,7 @@ struct PnvMachineClass {
 int compat_size;
 
 void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt);
+void (*i2c_init)(PnvMachineState *pnv);
 };
 
 struct PnvMachineState {
-- 
2.31.1




[PATCH v6 0/9] Add powernv10 I2C devices and tests

2023-11-27 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on the Power10 Ranier platform.

Changes from previous version:
  - Changed variable name, 'hotplug', to 'dev' in 5/9

Glenn Miles (9):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs
  ppc/pnv: New powernv10-rainier machine type
  ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power
control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10-rainier
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |  10 +-
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9552.c   |  58 ++-
 hw/misc/pca9554.c   | 328 
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  67 +++-
 hw/ppc/pnv_i2c.c|  15 +-
 include/hw/misc/pca9552.h   |   3 +-
 include/hw/misc/pca9554.h   |  36 ++
 include/hw/misc/pca9554_regs.h  |  19 +
 include/hw/ppc/pnv.h|   1 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pca9552-test.c  |   6 +-
 tests/qtest/pnv-host-i2c-test.c | 650 
 15 files changed, 1185 insertions(+), 16 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

-- 
2.31.1




[PATCH v6 1/9] misc/pca9552: Fix inverted input status

2023-11-27 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH v5 5/9] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2023-11-21 Thread Glenn Miles
For power10-rainier, a pca9552 device is used for PCIe slot hotplug
power control by the Power Hypervisor code.  The code expects that
some time after it enables power to a PCIe slot by asserting one of
the pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/ppc/pnv.c | 19 ++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index d8d19fb065..088824fd9f 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1900,7 +1900,24 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+I2CSlave *hotplug = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots
+ * after hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(hotplug), 0,
+  qdev_get_gpio_in(DEVICE(hotplug), 5));
+qdev_connect_gpio_out(DEVICE(hotplug), 1,
+  qdev_get_gpio_in(DEVICE(hotplug), 6));
+qdev_connect_gpio_out(DEVICE(hotplug), 2,
+  qdev_get_gpio_in(DEVICE(hotplug), 7));
+qdev_connect_gpio_out(DEVICE(hotplug), 3,
+  qdev_get_gpio_in(DEVICE(hotplug), 8));
+qdev_connect_gpio_out(DEVICE(hotplug), 4,
+  qdev_get_gpio_in(DEVICE(hotplug), 9));
 }
 }
 
-- 
2.31.1




[PATCH v5 9/9] ppc/pnv: Test pnv i2c master and connected devices

2023-11-21 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for powernv10-ranier only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Signed-off-by: Glenn Miles 
---

No change from previous version

 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 650 
 2 files changed, 651 insertions(+)
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 47dabf91d0..fbb0bd204c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -163,6 +163,7 @@ qtests_ppc64 = \
   qtests_ppc + \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + 
  \
   (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) +   
  \
+  (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) 
+  \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) +
  \
   (slirp.found() ? ['pxe-test'] : []) +  \
   (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) 
+ \
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
new file mode 100644
index 00..377525e458
--- /dev/null
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -0,0 +1,650 @@
+/*
+ * QTest testcase for PowerNV 10 Host I2C Communications
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/misc/pca9552_regs.h"
+
+#define PPC_BIT(bit)(0x8000ULL >> (bit))
+#define PPC_BIT32(bit)  (0x8000 >> (bit))
+#define PPC_BIT8(bit)   (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+
+#define MASK_TO_LSH(m)  (__builtin_ffsll(m) - 1)
+#define GETFIELD(m, v)  (((v) & (m)) >> MASK_TO_LSH(m))
+#define SETFIELD(m, v, val) \
+(((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
+
+#define P10_XSCOM_BASE  0x000603fcull
+#define PNV10_CHIP_MAX_I2C  5
+#define PNV10_XSCOM_I2CM_BASE   0xa
+#define PNV10_XSCOM_I2CM_SIZE   0x1000
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALLPPC_BITMASK(16, 31)
+#de

[PATCH v5 3/9] ppc/pnv: New powernv10-rainier machine type

2023-11-21 Thread Glenn Miles
Create a new powernv machine type, powernv10-rainier, that
will contain rainier-specific devices.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Formatting changes
  - Capitalized "Rainier" in machine description string
  - Changed powernv10-rainier parent to MACHINE_TYPE_NAME("powernv10")

 hw/ppc/pnv.c | 24 ++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0297871bdd..08704ce695 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -2251,7 +2251,7 @@ static void pnv_machine_power9_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
-static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
 PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
@@ -2263,7 +2263,6 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 { TYPE_PNV_PHB_ROOT_PORT, "version", "5" },
 };
 
-mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
 compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
 
@@ -2276,6 +2275,22 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
+static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
+}
+
+static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+}
+
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
 {
 PnvMachineState *pnv = PNV_MACHINE(obj);
@@ -2381,6 +2396,11 @@ static void pnv_machine_class_init(ObjectClass *oc, void 
*data)
 }
 
 static const TypeInfo types[] = {
+{
+.name  = MACHINE_TYPE_NAME("powernv10-rainier"),
+.parent= MACHINE_TYPE_NAME("powernv10"),
+.class_init= pnv_machine_p10_rainier_class_init,
+},
 {
 .name  = MACHINE_TYPE_NAME("powernv10"),
 .parent= TYPE_PNV_MACHINE,
-- 
2.31.1




[PATCH v5 7/9] misc: Add a pca9554 GPIO device model

2023-11-21 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 MAINTAINERS|  10 +-
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 391 insertions(+), 2 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 695e0bd34f..4d1c991691 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1155,9 +1155,7 @@ R: Joel Stanley 
 L: qemu-...@nongnu.org
 S: Maintained
 F: hw/*/*aspeed*
-F: hw/misc/pca9552.c
 F: include/hw/*/*aspeed*
-F: include/hw/misc/pca9552*.h
 F: hw/net/ftgmac100.c
 F: include/hw/net/ftgmac100.h
 F: docs/system/arm/aspeed.rst
@@ -1526,6 +1524,14 @@ F: include/hw/pci-host/pnv*
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
+pca955x
+M: Glenn Miles 
+L: qemu-...@nongnu.org
+L: qemu-...@nongnu.org
+S: Odd Fixes
+F: hw/misc/pca955*.c
+F: include/hw/misc/pca955*.h
+
 virtex_ml507
 M: Edgar E. Iglesias 
 L: qemu-...@nongnu.org
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+ 

[PATCH v5 6/9] ppc/pnv: Use resettable interface to reset child I2C buses

2023-11-21 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index 656a48eebe..774946d6b2 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -629,6 +629,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -654,7 +667,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH v5 4/9] ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power control

2023-11-21 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Formatting change

 hw/ppc/Kconfig   |  1 +
 hw/ppc/pnv.c | 25 +
 include/hw/ppc/pnv.h |  1 +
 3 files changed, 27 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 56f0475a8e..f77ca773cf 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 08704ce695..d8d19fb065 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -790,6 +790,7 @@ static void pnv_init(MachineState *machine)
 const char *bios_name = machine->firmware ?: FW_FILE_NAME;
 PnvMachineState *pnv = PNV_MACHINE(machine);
 MachineClass *mc = MACHINE_GET_CLASS(machine);
+PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
 char *fw_filename;
 long fw_size;
 uint64_t chip_ram_start = 0;
@@ -979,6 +980,13 @@ static void pnv_init(MachineState *machine)
  */
 pnv->powerdown_notifier.notify = pnv_powerdown_notify;
 qemu_register_powerdown_notifier(>powerdown_notifier);
+
+/*
+ * Create/Connect any machine-specific I2C devices
+ */
+if (pmc->i2c_init) {
+pmc->i2c_init(pnv);
+}
 }
 
 /*
@@ -1879,6 +1887,21 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+}
+
+static void pnv_rainier_i2c_init(PnvMachineState *pnv)
+{
+int i;
+for (i = 0; i < pnv->num_chips; i++) {
+Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+}
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -2286,9 +2309,11 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
+PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
 
 pnv_machine_p10_common_class_init(oc, data);
 mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+pmc->i2c_init = pnv_rainier_i2c_init;
 }
 
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 7e5fef7c43..110ac9aace 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -76,6 +76,7 @@ struct PnvMachineClass {
 int compat_size;
 
 void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt);
+void (*i2c_init)(PnvMachineState *pnv);
 };
 
 struct PnvMachineState {
-- 
2.31.1




[PATCH v5 2/9] misc/pca9552: Let external devices set pca9552 inputs

2023-11-21 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/misc/pca9552.c | 50 +--
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..fe876471c8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -110,22 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 for (i = 0; i < k->pin_count; i++) {
 uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-uint8_t input_shift = (i % 8);
+uint8_t bit_mask = 1 << (i % 8);
 uint8_t config = pca955x_pin_get_config(s, i);
+uint8_t old_value = s->regs[input_reg] & bit_mask;
+uint8_t new_value;
 
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+s->regs[input_reg] &= ~bit_mask;
+} else {
+s->regs[input_reg] |=  bit_mask;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -133,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
 default:
 break;
 }
+
+/* update irq state only if pin state changed */
+new_value = s->regs[input_reg] & bit_mask;
+if (new_value != old_value) {
+qemu_set_irq(s->gpio_out[i], !!new_value);
+}
 }
 }
 
@@ -340,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +395,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+qdev_init_gpio_out(dev, s->gpio_out, k->pin_co

[PATCH v5 1/9] misc/pca9552: Fix inverted input status

2023-11-21 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH v5 8/9] ppc/pnv: Add a pca9554 I2C device to powernv10-rainier

2023-11-21 Thread Glenn Miles
For powernv10-rainier, the Power Hypervisor code expects to see a
pca9554 device connected to the 3rd PNV I2C engine on port 1 at I2C
address 0x25 (or left-justified address of 0x4A).  This is used by
the hypervisor code to detect if a "Cable Card" is present.

Signed-off-by: Glenn Miles 
---

No change from previous version

 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 6 ++
 4 files changed, 12 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..c347a132c2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..c39410e4a7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f77ca773cf..2302778265 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 088824fd9f..1dab7c57e8 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1918,6 +1918,12 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
   qdev_get_gpio_in(DEVICE(hotplug), 8));
 qdev_connect_gpio_out(DEVICE(hotplug), 4,
   qdev_get_gpio_in(DEVICE(hotplug), 9));
+
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 }
 
-- 
2.31.1




[PATCH v5 0/9] Add powernv10 I2C devices and tests

2023-11-21 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on the Power10 Ranier platform.

Changes from previous version:
  - Removed two already merged patches
  - Various formatting changes
  - Capitalized "Rainier" in machine description string
  - Changed powernv10-rainier parent to MACHINE_TYPE_NAME("powernv10")


Glenn Miles (9):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs
  ppc/pnv: New powernv10-rainier machine type
  ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power
control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10-rainier
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |  10 +-
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9552.c   |  58 ++-
 hw/misc/pca9554.c   | 328 
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  72 +++-
 hw/ppc/pnv_i2c.c|  15 +-
 include/hw/misc/pca9552.h   |   3 +-
 include/hw/misc/pca9554.h   |  36 ++
 include/hw/misc/pca9554_regs.h  |  19 +
 include/hw/ppc/pnv.h|   1 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pca9552-test.c  |   6 +-
 tests/qtest/pnv-host-i2c-test.c | 650 
 15 files changed, 1190 insertions(+), 16 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

-- 
2.31.1




[PATCH v4 11/11] ppc/pnv: Test pnv i2c master and connected devices

2023-11-20 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for powernv10-ranier only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Changed test to target powernv10-rainier for power10

 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 650 
 2 files changed, 651 insertions(+)
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 47dabf91d0..fbb0bd204c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -163,6 +163,7 @@ qtests_ppc64 = \
   qtests_ppc + \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + 
  \
   (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) +   
  \
+  (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) 
+  \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) +
  \
   (slirp.found() ? ['pxe-test'] : []) +  \
   (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) 
+ \
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
new file mode 100644
index 00..377525e458
--- /dev/null
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -0,0 +1,650 @@
+/*
+ * QTest testcase for PowerNV 10 Host I2C Communications
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/misc/pca9552_regs.h"
+
+#define PPC_BIT(bit)(0x8000ULL >> (bit))
+#define PPC_BIT32(bit)  (0x8000 >> (bit))
+#define PPC_BIT8(bit)   (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+
+#define MASK_TO_LSH(m)  (__builtin_ffsll(m) - 1)
+#define GETFIELD(m, v)  (((v) & (m)) >> MASK_TO_LSH(m))
+#define SETFIELD(m, v, val) \
+(((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
+
+#define P10_XSCOM_BASE  0x000603fcull
+#define PNV10_CHIP_MAX_I2C  5
+#define PNV10_XSCOM_I2CM_BASE   0xa
+#define PNV10_XSCOM_I2CM_SIZE   0x1000
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define

[PATCH v4 10/11] ppc/pnv: Add a pca9554 I2C device to powernv10-rainier

2023-11-20 Thread Glenn Miles
For powernv10-rainier, the Power Hypervisor code expects to see a
pca9554 device connected to the 3rd PNV I2C engine on port 1 at I2C
address 0x25 (or left-justified address of 0x4A).  This is used by
the hypervisor code to detect if a "Cable Card" is present.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Code moved from pnv_chip_power10_realize to pnv_rainier_i2c_init

 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 6 ++
 4 files changed, 12 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..c347a132c2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..c39410e4a7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f77ca773cf..2302778265 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index c29a136465..54ebef789e 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1918,6 +1918,12 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
   qdev_get_gpio_in(DEVICE(hotplug), 8));
 qdev_connect_gpio_out(DEVICE(hotplug), 4,
   qdev_get_gpio_in(DEVICE(hotplug), 9));
+
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 }
 
-- 
2.31.1




[PATCH v4 06/11] ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses

2023-11-20 Thread Glenn Miles
The PNV I2C engines for power9 and power10 were being assigned a base
XSCOM address that was off by one I2C engine's address range such
that engine 0 had engine 1's address and so on.  The xscom address
assignment was being based on the device tree engine numbering, which
starts at 1.  Rather than changing the device tree numbering to start
with 0, the addressing was changed to be based on the existing device
tree numbers minus one.

Reviewed-by: Cédric Le Goater 
Fixes: 1ceda19c28a1 ("ppc/pnv: Connect PNV I2C controller to powernv10)
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/pnv.c | 6 --
 hw/ppc/pnv_i2c.c | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 80d25fc1bd..c29a136465 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1631,7 +1631,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE +
-   chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE,
+(chip9->i2c[i].engine - 1) *
+PNV9_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
@@ -1879,7 +1880,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE +
-chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE,
+(chip10->i2c[i].engine - 1) *
+PNV10_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f75e59e709..b2c738da50 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -593,7 +593,7 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 int i2c_offset;
 const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm";
 uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE +
-i2c->engine * PNV9_XSCOM_I2CM_SIZE;
+(i2c->engine - 1) * PNV9_XSCOM_I2CM_SIZE;
 uint32_t reg[2] = {
 cpu_to_be32(i2c_pcba),
 cpu_to_be32(PNV9_XSCOM_I2CM_SIZE)
-- 
2.31.1




[PATCH v4 01/11] misc/pca9552: Fix inverted input status

2023-11-20 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH v4 04/11] ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power control

2023-11-20 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
  -  Code moved from pnv_chip_power10_realize to pnv_rainier_i2c_init

 hw/ppc/Kconfig   |  1 +
 hw/ppc/pnv.c | 26 ++
 include/hw/ppc/pnv.h |  1 +
 3 files changed, 28 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 56f0475a8e..f77ca773cf 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 3481a1220e..9cefcd0fd6 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -790,6 +790,7 @@ static void pnv_init(MachineState *machine)
 const char *bios_name = machine->firmware ?: FW_FILE_NAME;
 PnvMachineState *pnv = PNV_MACHINE(machine);
 MachineClass *mc = MACHINE_GET_CLASS(machine);
+PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
 char *fw_filename;
 long fw_size;
 uint64_t chip_ram_start = 0;
@@ -979,6 +980,13 @@ static void pnv_init(MachineState *machine)
  */
 pnv->powerdown_notifier.notify = pnv_powerdown_notify;
 qemu_register_powerdown_notifier(>powerdown_notifier);
+
+/*
+ * Create/Connect any machine-specific I2C devices
+ */
+if (pmc->i2c_init) {
+pmc->i2c_init(pnv);
+}
 }
 
 /*
@@ -1877,6 +1885,22 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+}
+
+static void pnv_rainier_i2c_init(PnvMachineState *pnv)
+{
+int i;
+for (i = 0; i < pnv->num_chips; i++) {
+Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+}
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -2285,9 +2309,11 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
+PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
 
 pnv_machine_p10_common_class_init(oc, data);
 mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 rainier";
+pmc->i2c_init = pnv_rainier_i2c_init;
 }
 
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index 7e5fef7c43..110ac9aace 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -76,6 +76,7 @@ struct PnvMachineClass {
 int compat_size;
 
 void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt);
+void (*i2c_init)(PnvMachineState *pnv);
 };
 
 struct PnvMachineState {
-- 
2.31.1




[PATCH v4 09/11] misc: Add a pca9554 GPIO device model

2023-11-20 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Makes myself maintainer of the pca955* models

 MAINTAINERS|  10 +-
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 391 insertions(+), 2 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index e73a3ff544..8d8bda24fc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1138,9 +1138,7 @@ R: Joel Stanley 
 L: qemu-...@nongnu.org
 S: Maintained
 F: hw/*/*aspeed*
-F: hw/misc/pca9552.c
 F: include/hw/*/*aspeed*
-F: include/hw/misc/pca9552*.h
 F: hw/net/ftgmac100.c
 F: include/hw/net/ftgmac100.h
 F: docs/system/arm/aspeed.rst
@@ -1509,6 +1507,14 @@ F: include/hw/pci-host/pnv*
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
+pca955x
+M: Glenn Miles 
+L: qemu-...@nongnu.org
+L: qemu-...@nongnu.org
+S: Odd Fixes
+F: hw/misc/pca955*.c
+F: include/hw/misc/pca955*.h
+
 virtex_ml507
 M: Edgar E. Iglesias 
 L: qemu-...@nongnu.org
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register

[PATCH v4 05/11] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2023-11-20 Thread Glenn Miles
For power10-rainier, a pca9552 device is used for PCIe slot hotplug
power control by the Power Hypervisor code.  The code expects that
some time after it enables power to a PCIe slot by asserting one of
the pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
  - Code moved from pnv_chip_power10_realize to pnv_rainier_i2c_init

 hw/ppc/pnv.c | 20 ++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 9cefcd0fd6..80d25fc1bd 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1898,8 +1898,24 @@ static void pnv_rainier_i2c_init(PnvMachineState *pnv)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1],
-"pca9552", 0x63);
+I2CSlave *hotplug = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots
+ * after hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(hotplug), 0,
+  qdev_get_gpio_in(DEVICE(hotplug), 5));
+qdev_connect_gpio_out(DEVICE(hotplug), 1,
+  qdev_get_gpio_in(DEVICE(hotplug), 6));
+qdev_connect_gpio_out(DEVICE(hotplug), 2,
+  qdev_get_gpio_in(DEVICE(hotplug), 7));
+qdev_connect_gpio_out(DEVICE(hotplug), 3,
+  qdev_get_gpio_in(DEVICE(hotplug), 8));
+qdev_connect_gpio_out(DEVICE(hotplug), 4,
+  qdev_get_gpio_in(DEVICE(hotplug), 9));
 }
 }
 
-- 
2.31.1




[PATCH v4 08/11] ppc/pnv: Use resettable interface to reset child I2C buses

2023-11-20 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f80589157b..9ced596b98 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -628,6 +628,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -648,7 +661,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH v4 03/11] ppc/pnv: New powernv10-rainier machine type

2023-11-20 Thread Glenn Miles
Create a new powernv machine type, powernv10-rainier, that
will contain rainier-specific devices.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv.c | 29 +++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 9c29727337..3481a1220e 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -2249,7 +2249,7 @@ static void pnv_machine_power9_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
-static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
 {
 MachineClass *mc = MACHINE_CLASS(oc);
 PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
@@ -2261,7 +2261,6 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 { TYPE_PNV_PHB_ROOT_PORT, "version", "5" },
 };
 
-mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
 compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
 
@@ -2274,6 +2273,23 @@ static void pnv_machine_power10_class_init(ObjectClass 
*oc, void *data)
 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
 }
 
+static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
+
+}
+
+static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
+{
+MachineClass *mc = MACHINE_CLASS(oc);
+
+pnv_machine_p10_common_class_init(oc, data);
+mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 rainier";
+}
+
 static bool pnv_machine_get_hb(Object *obj, Error **errp)
 {
 PnvMachineState *pnv = PNV_MACHINE(obj);
@@ -2379,6 +2395,15 @@ static void pnv_machine_class_init(ObjectClass *oc, void 
*data)
 }
 
 static const TypeInfo types[] = {
+{
+.name  = MACHINE_TYPE_NAME("powernv10-rainier"),
+.parent= TYPE_PNV_MACHINE,
+.class_init= pnv_machine_p10_rainier_class_init,
+.interfaces = (InterfaceInfo[]) {
+{ TYPE_XIVE_FABRIC },
+{ },
+},
+},
 {
 .name  = MACHINE_TYPE_NAME("powernv10"),
 .parent= TYPE_PNV_MACHINE,
-- 
2.31.1




[PATCH v4 07/11] ppc/pnv: Fix PNV I2C invalid status after reset

2023-11-20 Thread Glenn Miles
The PNV I2C Controller was clearing the status register
after a reset without repopulating the "upper threshold
for I2C ports", "Command Complete" and the SCL/SDA input
level fields.

Fixed this for resets caused by a system reset as well
as from writing to the "Immediate Reset" register.

Reviewed-by: Cédric Le Goater 
Fixes: 263b81ee15af ("ppc/pnv: Add an I2C controller model")
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/ppc/pnv_i2c.c | 42 ++
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index b2c738da50..f80589157b 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -462,6 +462,23 @@ static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr 
addr,
 return val;
 }
 
+static void pnv_i2c_reset(void *dev)
+{
+PnvI2C *i2c = PNV_I2C(dev);
+
+memset(i2c->regs, 0, sizeof(i2c->regs));
+
+i2c->regs[I2C_STAT_REG] =
+SETFIELD(I2C_STAT_UPPER_THRS, 0ull, i2c->num_busses - 1) |
+I2C_STAT_CMD_COMP | I2C_STAT_SCL_INPUT_LEVEL |
+I2C_STAT_SDA_INPUT_LEVEL;
+i2c->regs[I2C_EXTD_STAT_REG] =
+SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
+SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
+
+fifo8_reset(>fifo);
+}
+
 static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 uint64_t val, unsigned size)
 {
@@ -499,16 +516,7 @@ static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 break;
 
 case I2C_RESET_I2C_REG:
-i2c->regs[I2C_MODE_REG] = 0;
-i2c->regs[I2C_CMD_REG] = 0;
-i2c->regs[I2C_WATERMARK_REG] = 0;
-i2c->regs[I2C_INTR_MASK_REG] = 0;
-i2c->regs[I2C_INTR_COND_REG] = 0;
-i2c->regs[I2C_INTR_RAW_COND_REG] = 0;
-i2c->regs[I2C_STAT_REG] = 0;
-i2c->regs[I2C_RESIDUAL_LEN_REG] = 0;
-i2c->regs[I2C_EXTD_STAT_REG] &=
-(I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION);
+pnv_i2c_reset(i2c);
 break;
 
 case I2C_RESET_ERRORS:
@@ -620,20 +628,6 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
-static void pnv_i2c_reset(void *dev)
-{
-PnvI2C *i2c = PNV_I2C(dev);
-
-memset(i2c->regs, 0, sizeof(i2c->regs));
-
-i2c->regs[I2C_STAT_REG] = I2C_STAT_CMD_COMP;
-i2c->regs[I2C_EXTD_STAT_REG] =
-SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
-SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
-
-fifo8_reset(>fifo);
-}
-
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
-- 
2.31.1




[PATCH v4 02/11] misc/pca9552: Let external devices set pca9552 inputs

2023-11-20 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No changes from previous version

 hw/misc/pca9552.c | 50 +--
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..fe876471c8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -110,22 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 for (i = 0; i < k->pin_count; i++) {
 uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-uint8_t input_shift = (i % 8);
+uint8_t bit_mask = 1 << (i % 8);
 uint8_t config = pca955x_pin_get_config(s, i);
+uint8_t old_value = s->regs[input_reg] & bit_mask;
+uint8_t new_value;
 
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+s->regs[input_reg] &= ~bit_mask;
+} else {
+s->regs[input_reg] |=  bit_mask;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -133,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
 default:
 break;
 }
+
+/* update irq state only if pin state changed */
+new_value = s->regs[input_reg] & bit_mask;
+if (new_value != old_value) {
+qemu_set_irq(s->gpio_out[i], !!new_value);
+}
 }
 }
 
@@ -340,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +395,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+qdev_init_gpio_out(dev, s->gpio_out, k->pin_co

[PATCH v4 00/11] Add powernv10 I2C devices and tests

2023-11-20 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on Power10 platforms.

Changes from previous version:
  - Pulled in fixes/changes for pca9552 devices
  - Created new powernv10-rainier machine type
  - Adds pca9552/pca9554 devices to powernv10-rainier machine
  - Modified MAINTAINERS to make myself maintainer of
the pca955x devices


Glenn Miles (11):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs
  ppc/pnv: New powernv10-rainier machine type
  ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power
control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses
  ppc/pnv: Fix PNV I2C invalid status after reset
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10-rainier
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |  10 +-
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9552.c   |  58 ++-
 hw/misc/pca9554.c   | 328 
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  83 +++-
 hw/ppc/pnv_i2c.c|  47 ++-
 include/hw/misc/pca9552.h   |   3 +-
 include/hw/misc/pca9554.h   |  36 ++
 include/hw/misc/pca9554_regs.h  |  19 +
 include/hw/ppc/pnv.h|   1 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pca9552-test.c  |   6 +-
 tests/qtest/pnv-host-i2c-test.c | 650 
 15 files changed, 1212 insertions(+), 37 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

-- 
2.31.1




[PATCH v3 6/8] misc: Add a pca9554 GPIO device model

2023-11-14 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Signed-off-by: Glenn Miles 
---

No changes from v2

 MAINTAINERS|   2 +
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 385 insertions(+)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index e73a3ff544..209080e1a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1504,8 +1504,10 @@ F: hw/ppc/pnv*
 F: hw/intc/pnv*
 F: hw/intc/xics_pnv.c
 F: hw/pci-host/pnv*
+F: hw/misc/pca9554.c
 F: include/hw/ppc/pnv*
 F: include/hw/pci-host/pnv*
+F: include/hw/misc/pca9554*.h
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+  __func__, reg);
+}
+}
+
+static uint8_t pca9554_recv(I2CSlave *i2c)
+{
+PCA9554State *s = PCA9554(i2c);
+uint8_t ret;
+
+ret = pca9554_read(s, s->pointer & 0x3);
+
+return ret;
+}
+
+static int pca9554_send(I2CSlave *i2c, uint8_t data)
+{
+PCA9554State *s = PCA9554(i2c);
+
+/* First byte sent by is the register address

[PATCH v3 3/8] ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses

2023-11-14 Thread Glenn Miles
The PNV I2C engines for power9 and power10 were being assigned a base
XSCOM address that was off by one I2C engine's address range such
that engine 0 had engine 1's address and so on.  The xscom address
assignment was being based on the device tree engine numbering, which
starts at 1.  Rather than changing the device tree numbering to start
with 0, the addressing was changed to be based on the existing device
tree numbers minus one.

Fixes: 1ceda19c28a1 ("ppc/pnv: Connect PNV I2C controller to powernv10)
Signed-off-by: Glenn Miles 
---

Changes from v2:
- Added Fixes: tag

 hw/ppc/pnv.c | 6 --
 hw/ppc/pnv_i2c.c | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 645379d5bf..e82e9b30ec 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1623,7 +1623,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE +
-   chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE,
+(chip9->i2c[i].engine - 1) *
+PNV9_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
@@ -1871,7 +1872,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE +
-chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE,
+(chip10->i2c[i].engine - 1) *
+PNV10_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f75e59e709..b2c738da50 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -593,7 +593,7 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 int i2c_offset;
 const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm";
 uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE +
-i2c->engine * PNV9_XSCOM_I2CM_SIZE;
+(i2c->engine - 1) * PNV9_XSCOM_I2CM_SIZE;
 uint32_t reg[2] = {
 cpu_to_be32(i2c_pcba),
 cpu_to_be32(PNV9_XSCOM_I2CM_SIZE)
-- 
2.31.1




[PATCH v3 8/8] ppc/pnv: Test pnv i2c master and connected devices

2023-11-14 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for P10 only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
- Fixed formatting errors

 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 650 
 2 files changed, 651 insertions(+)
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 47dabf91d0..fbb0bd204c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -163,6 +163,7 @@ qtests_ppc64 = \
   qtests_ppc + \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + 
  \
   (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) +   
  \
+  (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) 
+  \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) +
  \
   (slirp.found() ? ['pxe-test'] : []) +  \
   (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) 
+ \
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
new file mode 100644
index 00..52639fc8f7
--- /dev/null
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -0,0 +1,650 @@
+/*
+ * QTest testcase for PowerNV 10 Host I2C Communications
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/misc/pca9552_regs.h"
+
+#define PPC_BIT(bit)(0x8000ULL >> (bit))
+#define PPC_BIT32(bit)  (0x8000 >> (bit))
+#define PPC_BIT8(bit)   (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+
+#define MASK_TO_LSH(m)  (__builtin_ffsll(m) - 1)
+#define GETFIELD(m, v)  (((v) & (m)) >> MASK_TO_LSH(m))
+#define SETFIELD(m, v, val) \
+(((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
+
+#define P10_XSCOM_BASE  0x000603fcull
+#define PNV10_CHIP_MAX_I2C  5
+#define PNV10_XSCOM_I2CM_BASE   0xa
+#define PNV10_XSCOM_I2CM_SIZE   0x1000
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALL   

[PATCH v3 7/8] ppc/pnv: Add a pca9554 I2C device to powernv10

2023-11-14 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9554 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x25 (or left-
justified address of 0x4A).  This is used by the hypervisor code to
detect if a "Cable Card" is present.

Signed-off-by: Glenn Miles 
---

No changes from v2

 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 5 +
 4 files changed, 11 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..c347a132c2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..c39410e4a7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f77ca773cf..2302778265 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index e82e9b30ec..bbbc4ca868 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1903,6 +1903,11 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 qdev_connect_gpio_out(DEVICE(hotplug), 4,
   qdev_get_gpio_in(DEVICE(hotplug), 9));
 
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
-- 
2.31.1




[PATCH v3 5/8] ppc/pnv: Use resettable interface to reset child I2C buses

2023-11-14 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Signed-off-by: Glenn Miles 
---

No changes from v2

 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f80589157b..9ced596b98 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -628,6 +628,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -648,7 +661,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH v3 1/8] ppc/pnv: Add pca9552 to powernv10 for PCIe hotplug power control

2023-11-14 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Signed-off-by: Glenn Miles 
---
Based-on: <20231024181144.4045056-3-mil...@linux.vnet.ibm.com>
[PATCH v3 2/2] misc/pca9552: Let external devices set pca9552 inputs

No changes from v2

 hw/ppc/Kconfig | 1 +
 hw/ppc/pnv.c   | 7 +++
 2 files changed, 8 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 56f0475a8e..f77ca773cf 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 9c29727337..7afaf1008f 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1877,6 +1877,13 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
-- 
2.31.1




[PATCH v3 0/8] Add powernv10 I2C devices and tests

2023-11-14 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on Power10 platforms.

Changes from previous version:
  - Added Fixes: tag to commits 3 and 4
  - Fixed formatting errors in commits 2 and 8

Glenn Miles (8):
  ppc/pnv: Add pca9552 to powernv10 for PCIe hotplug power control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses
  ppc/pnv: Fix PNV I2C invalid status after reset
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |   2 +
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9554.c   | 328 
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  35 +-
 hw/ppc/pnv_i2c.c|  47 ++-
 include/hw/misc/pca9554.h   |  36 ++
 include/hw/misc/pca9554_regs.h  |  19 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 650 
 11 files changed, 1103 insertions(+), 22 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

-- 
2.31.1




[PATCH v3 2/8] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2023-11-14 Thread Glenn Miles
For power10, a pca9552 device is used for PCIe slot hotplug power
control by the Power Hypervisor code.  The code expects that some
time after it enables power to a PCIe slot by asserting one of the
pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Signed-off-by: Glenn Miles 
---

Changes from previous version:
- Fixed formatting errors

 hw/ppc/pnv.c | 19 ++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7afaf1008f..645379d5bf 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1882,7 +1882,24 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+I2CSlave *hotplug = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots after
+ * hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(hotplug), 0,
+  qdev_get_gpio_in(DEVICE(hotplug), 5));
+qdev_connect_gpio_out(DEVICE(hotplug), 1,
+  qdev_get_gpio_in(DEVICE(hotplug), 6));
+qdev_connect_gpio_out(DEVICE(hotplug), 2,
+  qdev_get_gpio_in(DEVICE(hotplug), 7));
+qdev_connect_gpio_out(DEVICE(hotplug), 3,
+  qdev_get_gpio_in(DEVICE(hotplug), 8));
+qdev_connect_gpio_out(DEVICE(hotplug), 4,
+  qdev_get_gpio_in(DEVICE(hotplug), 9));
 
 }
 
-- 
2.31.1




[PATCH v3 4/8] ppc/pnv: Fix PNV I2C invalid status after reset

2023-11-14 Thread Glenn Miles
The PNV I2C Controller was clearing the status register
after a reset without repopulating the "upper threshold
for I2C ports", "Command Complete" and the SCL/SDA input
level fields.

Fixed this for resets caused by a system reset as well
as from writing to the "Immediate Reset" register.

Fixes: 263b81ee15af ("ppc/pnv: Add an I2C controller model")
Signed-off-by: Glenn Miles 
---

Changes from v2:
-Added Fixes: tag

 hw/ppc/pnv_i2c.c | 42 ++
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index b2c738da50..f80589157b 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -462,6 +462,23 @@ static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr 
addr,
 return val;
 }
 
+static void pnv_i2c_reset(void *dev)
+{
+PnvI2C *i2c = PNV_I2C(dev);
+
+memset(i2c->regs, 0, sizeof(i2c->regs));
+
+i2c->regs[I2C_STAT_REG] =
+SETFIELD(I2C_STAT_UPPER_THRS, 0ull, i2c->num_busses - 1) |
+I2C_STAT_CMD_COMP | I2C_STAT_SCL_INPUT_LEVEL |
+I2C_STAT_SDA_INPUT_LEVEL;
+i2c->regs[I2C_EXTD_STAT_REG] =
+SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
+SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
+
+fifo8_reset(>fifo);
+}
+
 static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 uint64_t val, unsigned size)
 {
@@ -499,16 +516,7 @@ static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 break;
 
 case I2C_RESET_I2C_REG:
-i2c->regs[I2C_MODE_REG] = 0;
-i2c->regs[I2C_CMD_REG] = 0;
-i2c->regs[I2C_WATERMARK_REG] = 0;
-i2c->regs[I2C_INTR_MASK_REG] = 0;
-i2c->regs[I2C_INTR_COND_REG] = 0;
-i2c->regs[I2C_INTR_RAW_COND_REG] = 0;
-i2c->regs[I2C_STAT_REG] = 0;
-i2c->regs[I2C_RESIDUAL_LEN_REG] = 0;
-i2c->regs[I2C_EXTD_STAT_REG] &=
-(I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION);
+pnv_i2c_reset(i2c);
 break;
 
 case I2C_RESET_ERRORS:
@@ -620,20 +628,6 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
-static void pnv_i2c_reset(void *dev)
-{
-PnvI2C *i2c = PNV_I2C(dev);
-
-memset(i2c->regs, 0, sizeof(i2c->regs));
-
-i2c->regs[I2C_STAT_REG] = I2C_STAT_CMD_COMP;
-i2c->regs[I2C_EXTD_STAT_REG] =
-SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
-SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
-
-fifo8_reset(>fifo);
-}
-
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
-- 
2.31.1




[PATCH v2 6/8] misc: Add a pca9554 GPIO device model

2023-11-10 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Signed-off-by: Glenn Miles 
---
 MAINTAINERS|   2 +
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 385 insertions(+)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index e73a3ff544..209080e1a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1504,8 +1504,10 @@ F: hw/ppc/pnv*
 F: hw/intc/pnv*
 F: hw/intc/xics_pnv.c
 F: hw/pci-host/pnv*
+F: hw/misc/pca9554.c
 F: include/hw/ppc/pnv*
 F: include/hw/pci-host/pnv*
+F: include/hw/misc/pca9554*.h
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+  __func__, reg);
+}
+}
+
+static uint8_t pca9554_recv(I2CSlave *i2c)
+{
+PCA9554State *s = PCA9554(i2c);
+uint8_t ret;
+
+ret = pca9554_read(s, s->pointer & 0x3);
+
+return ret;
+}
+
+static int pca9554_send(I2CSlave *i2c, uint8_t data)
+{
+PCA9554State *s = PCA9554(i2c);
+
+/* First byte sent by is the register address */
+if (s->l

[PATCH v2 8/8] ppc/pnv: Test pnv i2c master and connected devices

2023-11-10 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for P10 only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Signed-off-by: Glenn Miles 
---
 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 653 
 2 files changed, 654 insertions(+)
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 47dabf91d0..fbb0bd204c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -163,6 +163,7 @@ qtests_ppc64 = \
   qtests_ppc + \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + 
  \
   (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) +   
  \
+  (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) 
+  \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) +
  \
   (slirp.found() ? ['pxe-test'] : []) +  \
   (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) 
+ \
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
new file mode 100644
index 00..8002dfcc0c
--- /dev/null
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -0,0 +1,653 @@
+/*
+ * QTest testcase for PowerNV 10 Host I2C Communications
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/misc/pca9552_regs.h"
+
+#define PPC_BIT(bit)(0x8000ULL >> (bit))
+#define PPC_BIT32(bit)  (0x8000 >> (bit))
+#define PPC_BIT8(bit)   (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+
+#define MASK_TO_LSH(m)  (__builtin_ffsll(m) - 1)
+#define GETFIELD(m, v)  (((v) & (m)) >> MASK_TO_LSH(m))
+#define SETFIELD(m, v, val) \
+(((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
+
+#define P10_XSCOM_BASE  0x000603fcull
+#define PNV10_CHIP_MAX_I2C  5
+#define PNV10_XSCOM_I2CM_BASE   0xa
+#define PNV10_XSCOM_I2CM_SIZE   0x1000
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALLPPC_BITMASK(16, 31)
+#define I2C_INTR_INVALID_CMD   

[PATCH v2 4/8] ppc/pnv: Fix PNV I2C invalid status after reset

2023-11-10 Thread Glenn Miles
The PNV I2C Controller was clearing the status register
after a reset without repopulating the "upper threshold
for I2C ports", "Command Complete" and the SCL/SDA input
level fields.

Fixed this for resets caused by a system reset as well
as from writing to the "Immediate Reset" register.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv_i2c.c | 42 ++
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index b2c738da50..f80589157b 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -462,6 +462,23 @@ static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr 
addr,
 return val;
 }
 
+static void pnv_i2c_reset(void *dev)
+{
+PnvI2C *i2c = PNV_I2C(dev);
+
+memset(i2c->regs, 0, sizeof(i2c->regs));
+
+i2c->regs[I2C_STAT_REG] =
+SETFIELD(I2C_STAT_UPPER_THRS, 0ull, i2c->num_busses - 1) |
+I2C_STAT_CMD_COMP | I2C_STAT_SCL_INPUT_LEVEL |
+I2C_STAT_SDA_INPUT_LEVEL;
+i2c->regs[I2C_EXTD_STAT_REG] =
+SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
+SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
+
+fifo8_reset(>fifo);
+}
+
 static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 uint64_t val, unsigned size)
 {
@@ -499,16 +516,7 @@ static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 break;
 
 case I2C_RESET_I2C_REG:
-i2c->regs[I2C_MODE_REG] = 0;
-i2c->regs[I2C_CMD_REG] = 0;
-i2c->regs[I2C_WATERMARK_REG] = 0;
-i2c->regs[I2C_INTR_MASK_REG] = 0;
-i2c->regs[I2C_INTR_COND_REG] = 0;
-i2c->regs[I2C_INTR_RAW_COND_REG] = 0;
-i2c->regs[I2C_STAT_REG] = 0;
-i2c->regs[I2C_RESIDUAL_LEN_REG] = 0;
-i2c->regs[I2C_EXTD_STAT_REG] &=
-(I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION);
+pnv_i2c_reset(i2c);
 break;
 
 case I2C_RESET_ERRORS:
@@ -620,20 +628,6 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
-static void pnv_i2c_reset(void *dev)
-{
-PnvI2C *i2c = PNV_I2C(dev);
-
-memset(i2c->regs, 0, sizeof(i2c->regs));
-
-i2c->regs[I2C_STAT_REG] = I2C_STAT_CMD_COMP;
-i2c->regs[I2C_EXTD_STAT_REG] =
-SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
-SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
-
-fifo8_reset(>fifo);
-}
-
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
-- 
2.31.1




[PATCH v2 7/8] ppc/pnv: Add a pca9554 I2C device to powernv10

2023-11-10 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9554 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x25 (or left-
justified address of 0x4A).  This is used by the hypervisor code to
detect if a "Cable Card" is present.

Signed-off-by: Glenn Miles 
---
 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 5 +
 4 files changed, 11 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..c347a132c2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..c39410e4a7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f77ca773cf..2302778265 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 516434bc9c..b6b4164d78 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1903,6 +1903,11 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 qdev_connect_gpio_out(DEVICE(hotplug), 4,
   qdev_get_gpio_in(DEVICE(hotplug), 9));
 
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
-- 
2.31.1




[PATCH v2 3/8] ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses

2023-11-10 Thread Glenn Miles
The PNV I2C engines for power9 and power10 were being assigned a base
XSCOM address that was off by one I2C engine's address range such
that engine 0 had engine 1's address and so on.  The xscom address
assignment was being based on the device tree engine numbering, which
starts at 1.  Rather than changing the device tree numbering to start
with 0, the addressing was changed to be based on the existing device
tree numbers minus one.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv.c | 6 --
 hw/ppc/pnv_i2c.c | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0b24d7d8ed..516434bc9c 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1623,7 +1623,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE +
-   chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE,
+(chip9->i2c[i].engine - 1) *
+PNV9_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
@@ -1871,7 +1872,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE +
-chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE,
+(chip10->i2c[i].engine - 1) *
+PNV10_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f75e59e709..b2c738da50 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -593,7 +593,7 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 int i2c_offset;
 const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm";
 uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE +
-i2c->engine * PNV9_XSCOM_I2CM_SIZE;
+(i2c->engine - 1) * PNV9_XSCOM_I2CM_SIZE;
 uint32_t reg[2] = {
 cpu_to_be32(i2c_pcba),
 cpu_to_be32(PNV9_XSCOM_I2CM_SIZE)
-- 
2.31.1




[PATCH v2 1/8] ppc/pnv: Add pca9552 to powernv10 for PCIe hotplug power control

2023-11-10 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Signed-off-by: Glenn Miles 
---
Based-on: <20231024181144.4045056-3-mil...@linux.vnet.ibm.com>
[PATCH v3 2/2] misc/pca9552: Let external devices set pca9552 inputs

 hw/ppc/Kconfig | 1 +
 hw/ppc/pnv.c   | 7 +++
 2 files changed, 8 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 56f0475a8e..f77ca773cf 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 9c29727337..7afaf1008f 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1877,6 +1877,13 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
-- 
2.31.1




[PATCH v2 0/8] Add powernv10 I2C devices and tests

2023-11-10 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on Power10 platforms.

Changes from previous version:
  - Added prerequisite patch that hasn't merged into master yet.
(see patch 1/8 for details).  Without this patch, the code
compiles but "make check" fails.

Glenn Miles (8):
  ppc/pnv: Add pca9552 to powernv10 for PCIe hotplug power control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses
  ppc/pnv: Fix PNV I2C invalid status after reset
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |   2 +
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9554.c   | 328 
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  35 +-
 hw/ppc/pnv_i2c.c|  47 ++-
 include/hw/misc/pca9554.h   |  36 ++
 include/hw/misc/pca9554_regs.h  |  19 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 653 
 11 files changed, 1106 insertions(+), 22 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

-- 
2.31.1




[PATCH v2 2/8] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2023-11-10 Thread Glenn Miles
For power10, a pca9552 device is used for PCIe slot hotplug power
control by the Power Hypervisor code.  The code expects that some
time after it enables power to a PCIe slot by asserting one of the
pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv.c | 19 ++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7afaf1008f..0b24d7d8ed 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1882,7 +1882,24 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+I2CSlave* hotplug = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots after
+ * hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(hotplug), 0,
+  qdev_get_gpio_in(DEVICE(hotplug), 5));
+qdev_connect_gpio_out(DEVICE(hotplug), 1,
+  qdev_get_gpio_in(DEVICE(hotplug), 6));
+qdev_connect_gpio_out(DEVICE(hotplug), 2,
+  qdev_get_gpio_in(DEVICE(hotplug), 7));
+qdev_connect_gpio_out(DEVICE(hotplug), 3,
+  qdev_get_gpio_in(DEVICE(hotplug), 8));
+qdev_connect_gpio_out(DEVICE(hotplug), 4,
+  qdev_get_gpio_in(DEVICE(hotplug), 9));
 
 }
 
-- 
2.31.1




[PATCH v2 5/8] ppc/pnv: Use resettable interface to reset child I2C buses

2023-11-10 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f80589157b..9ced596b98 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -628,6 +628,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -648,7 +661,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH 2/8] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control

2023-11-10 Thread Glenn Miles
For power10, a pca9552 device is used for PCIe slot hotplug power
control by the Power Hypervisor code.  The code expects that some
time after it enables power to a PCIe slot by asserting one of the
pca9552 GPIO pins 0-4, it should see a "power good" signal asserted
on one of pca9552 GPIO pins 5-9.

To simulate this behavior, we simply connect the GPIO outputs for
pins 0-4 to the GPIO inputs for pins 5-9.

Each PCIe slot is assigned 3 GPIO pins on the pca9552 device, for
control of up to 5 PCIe slots.  The per-slot signal names are:

   SLOTx_EN...PHYP uses this as an output to enable
  slot power.  We connect this to the
  SLOTx_PG pin to simulate a PGOOD signal.
   SLOTx_PG...PHYP uses this as in input to detect
  PGOOD for the slot.  For our purposes
  we just connect this to the SLOTx_EN
  output.
   SLOTx_Control..PHYP uses this as an output to prevent
  a race condition in the real hotplug
  circuitry, but we can ignore this output
  for simulation.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv.c | 19 ++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7afaf1008f..0b24d7d8ed 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1882,7 +1882,24 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
  * Add a PCA9552 I2C device for PCIe hotplug control
  * to engine 2, bus 1, address 0x63
  */
-i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+I2CSlave* hotplug = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+"pca9552", 0x63);
+
+/*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots after
+ * hypervisor code sets a SLOTx_EN pin high.
+ */
+qdev_connect_gpio_out(DEVICE(hotplug), 0,
+  qdev_get_gpio_in(DEVICE(hotplug), 5));
+qdev_connect_gpio_out(DEVICE(hotplug), 1,
+  qdev_get_gpio_in(DEVICE(hotplug), 6));
+qdev_connect_gpio_out(DEVICE(hotplug), 2,
+  qdev_get_gpio_in(DEVICE(hotplug), 7));
+qdev_connect_gpio_out(DEVICE(hotplug), 3,
+  qdev_get_gpio_in(DEVICE(hotplug), 8));
+qdev_connect_gpio_out(DEVICE(hotplug), 4,
+  qdev_get_gpio_in(DEVICE(hotplug), 9));
 
 }
 
-- 
2.31.1




[PATCH 7/8] ppc/pnv: Add a pca9554 I2C device to powernv10

2023-11-10 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9554 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x25 (or left-
justified address of 0x4A).  This is used by the hypervisor code to
detect if a "Cable Card" is present.

Signed-off-by: Glenn Miles 
---
 hw/misc/Kconfig | 4 
 hw/misc/meson.build | 1 +
 hw/ppc/Kconfig  | 1 +
 hw/ppc/pnv.c| 5 +
 4 files changed, 11 insertions(+)

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cc8a8c1418..c347a132c2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
 bool
 depends on I2C
 
+config PCA9554
+bool
+depends on I2C
+
 config I2C_ECHO
 bool
 default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..c39410e4a7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: 
files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index f77ca773cf..2302778265 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -33,6 +33,7 @@ config POWERNV
 select FDT_PPC
 select PCI_POWERNV
 select PCA9552
+select PCA9554
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 516434bc9c..b6b4164d78 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1903,6 +1903,11 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 qdev_connect_gpio_out(DEVICE(hotplug), 4,
   qdev_get_gpio_in(DEVICE(hotplug), 9));
 
+/*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
-- 
2.31.1




[PATCH 5/8] ppc/pnv: Use resettable interface to reset child I2C buses

2023-11-10 Thread Glenn Miles
The QEMU I2C buses and devices use the resettable
interface for resetting while the PNV I2C controller
and parent buses and devices have not yet transitioned
to this new interface and use the old reset strategy.
This was preventing the I2C buses and devices wired
to the PNV I2C controller from being reset.

The short term fix for this is to have the PNV I2C
Controller's reset function explicitly call the resettable
interface function, bus_cold_reset(), on all child
I2C buses.

The long term fix should be to transition all PNV parent
devices and buses to use the resettable interface so that
all child buses and devices are automatically reset.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv_i2c.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f80589157b..9ced596b98 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -628,6 +628,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
+static void pnv_i2c_sys_reset(void *dev)
+{
+int port;
+PnvI2C *i2c = PNV_I2C(dev);
+
+pnv_i2c_reset(dev);
+
+/* reset all buses connected to this i2c controller */
+for (port = 0; port < i2c->num_busses; port++) {
+bus_cold_reset(BUS(i2c->busses[port]));
+}
+}
+
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
@@ -648,7 +661,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 
 fifo8_create(>fifo, PNV_I2C_FIFO_SIZE);
 
-qemu_register_reset(pnv_i2c_reset, dev);
+qemu_register_reset(pnv_i2c_sys_reset, dev);
 
 qdev_init_gpio_out(DEVICE(dev), >psi_irq, 1);
 }
-- 
2.31.1




[PATCH 3/8] ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses

2023-11-10 Thread Glenn Miles
The PNV I2C engines for power9 and power10 were being assigned a base
XSCOM address that was off by one I2C engine's address range such
that engine 0 had engine 1's address and so on.  The xscom address
assignment was being based on the device tree engine numbering, which
starts at 1.  Rather than changing the device tree numbering to start
with 0, the addressing was changed to be based on the existing device
tree numbers minus one.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv.c | 6 --
 hw/ppc/pnv_i2c.c | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0b24d7d8ed..516434bc9c 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1623,7 +1623,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE +
-   chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE,
+(chip9->i2c[i].engine - 1) *
+PNV9_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
@@ -1871,7 +1872,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 return;
 }
 pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE +
-chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE,
+(chip10->i2c[i].engine - 1) *
+PNV10_XSCOM_I2CM_SIZE,
 >i2c[i].xscom_regs);
 qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
   qdev_get_gpio_in(DEVICE(>psi),
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index f75e59e709..b2c738da50 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -593,7 +593,7 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 int i2c_offset;
 const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm";
 uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE +
-i2c->engine * PNV9_XSCOM_I2CM_SIZE;
+(i2c->engine - 1) * PNV9_XSCOM_I2CM_SIZE;
 uint32_t reg[2] = {
 cpu_to_be32(i2c_pcba),
 cpu_to_be32(PNV9_XSCOM_I2CM_SIZE)
-- 
2.31.1




[PATCH 1/8] ppc/pnv: Add pca9552 to powernv10 for PCIe hotplug power control

2023-11-10 Thread Glenn Miles
The Power Hypervisor code expects to see a pca9552 device connected
to the 3rd PNV I2C engine on port 1 at I2C address 0x63 (or left-
justified address of 0xC6).  This is used by hypervisor code to
control PCIe slot power during hotplug events.

Signed-off-by: Glenn Miles 
---
 hw/ppc/Kconfig | 1 +
 hw/ppc/pnv.c   | 7 +++
 2 files changed, 8 insertions(+)

diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 56f0475a8e..f77ca773cf 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,7 @@ config POWERNV
 select XIVE
 select FDT_PPC
 select PCI_POWERNV
+select PCA9552
 
 config PPC405
 bool
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 9c29727337..7afaf1008f 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1877,6 +1877,13 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
   qdev_get_gpio_in(DEVICE(>psi),
PSIHB9_IRQ_SBE_I2C));
 }
+
+/*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9552", 0x63);
+
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
-- 
2.31.1




[PATCH 6/8] misc: Add a pca9554 GPIO device model

2023-11-10 Thread Glenn Miles
Specs are available here:

https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf

This is a simple model supporting the basic registers for GPIO
mode.  The device also supports an interrupt output line but the
model does not yet support this.

Signed-off-by: Glenn Miles 
---
 MAINTAINERS|   2 +
 hw/misc/pca9554.c  | 328 +
 include/hw/misc/pca9554.h  |  36 
 include/hw/misc/pca9554_regs.h |  19 ++
 4 files changed, 385 insertions(+)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h

diff --git a/MAINTAINERS b/MAINTAINERS
index e73a3ff544..209080e1a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1504,8 +1504,10 @@ F: hw/ppc/pnv*
 F: hw/intc/pnv*
 F: hw/intc/xics_pnv.c
 F: hw/pci-host/pnv*
+F: hw/misc/pca9554.c
 F: include/hw/ppc/pnv*
 F: include/hw/pci-host/pnv*
+F: include/hw/misc/pca9554*.h
 F: pc-bios/skiboot.lid
 F: tests/qtest/pnv*
 
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 00..778b32e443
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+/*< private >*/
+I2CSlaveClass parent_class;
+/*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+   TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW  0x0
+#define PCA9554_PIN_HIZ  0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+int i;
+uint8_t config = s->regs[PCA9554_CONFIG];
+uint8_t output = s->regs[PCA9554_OUTPUT];
+uint8_t internal_state = config | output;
+
+for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+uint8_t bit_mask = 1 << i;
+uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+uint8_t new_value;
+
+switch (internal_pin_state) {
+case PCA9554_PIN_LOW:
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+break;
+case PCA9554_PIN_HIZ:
+/*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+if (s->ext_state[i] == PCA9554_PIN_LOW) {
+s->regs[PCA9554_INPUT] &= ~bit_mask;
+} else {
+s->regs[PCA9554_INPUT] |=  bit_mask;
+}
+break;
+default:
+break;
+}
+
+/* update irq state only if pin state changed */
+new_value = s->regs[PCA9554_INPUT] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
+}
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+switch (reg) {
+case PCA9554_INPUT:
+return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+case PCA9554_OUTPUT:
+case PCA9554_POLARITY:
+case PCA9554_CONFIG:
+return s->regs[reg];
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+  __func__, reg);
+return 0xFF;
+}
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+switch (reg) {
+case PCA9554_OUTPUT:
+case PCA9554_CONFIG:
+s->regs[reg] = data;
+pca9554_update_pin_input(s);
+break;
+case PCA9554_POLARITY:
+s->regs[reg] = data;
+break;
+case PCA9554_INPUT:
+default:
+qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+  __func__, reg);
+}
+}
+
+static uint8_t pca9554_recv(I2CSlave *i2c)
+{
+PCA9554State *s = PCA9554(i2c);
+uint8_t ret;
+
+ret = pca9554_read(s, s->pointer & 0x3);
+
+return ret;
+}
+
+static int pca9554_send(I2CSlave *i2c, uint8_t data)
+{
+PCA9554State *s = PCA9554(i2c);
+
+/* First byte sent by is the register address */
+if (s->l

[PATCH 4/8] ppc/pnv: Fix PNV I2C invalid status after reset

2023-11-10 Thread Glenn Miles
The PNV I2C Controller was clearing the status register
after a reset without repopulating the "upper threshold
for I2C ports", "Command Complete" and the SCL/SDA input
level fields.

Fixed this for resets caused by a system reset as well
as from writing to the "Immediate Reset" register.

Signed-off-by: Glenn Miles 
---
 hw/ppc/pnv_i2c.c | 42 ++
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index b2c738da50..f80589157b 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -462,6 +462,23 @@ static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr 
addr,
 return val;
 }
 
+static void pnv_i2c_reset(void *dev)
+{
+PnvI2C *i2c = PNV_I2C(dev);
+
+memset(i2c->regs, 0, sizeof(i2c->regs));
+
+i2c->regs[I2C_STAT_REG] =
+SETFIELD(I2C_STAT_UPPER_THRS, 0ull, i2c->num_busses - 1) |
+I2C_STAT_CMD_COMP | I2C_STAT_SCL_INPUT_LEVEL |
+I2C_STAT_SDA_INPUT_LEVEL;
+i2c->regs[I2C_EXTD_STAT_REG] =
+SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
+SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
+
+fifo8_reset(>fifo);
+}
+
 static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 uint64_t val, unsigned size)
 {
@@ -499,16 +516,7 @@ static void pnv_i2c_xscom_write(void *opaque, hwaddr addr,
 break;
 
 case I2C_RESET_I2C_REG:
-i2c->regs[I2C_MODE_REG] = 0;
-i2c->regs[I2C_CMD_REG] = 0;
-i2c->regs[I2C_WATERMARK_REG] = 0;
-i2c->regs[I2C_INTR_MASK_REG] = 0;
-i2c->regs[I2C_INTR_COND_REG] = 0;
-i2c->regs[I2C_INTR_RAW_COND_REG] = 0;
-i2c->regs[I2C_STAT_REG] = 0;
-i2c->regs[I2C_RESIDUAL_LEN_REG] = 0;
-i2c->regs[I2C_EXTD_STAT_REG] &=
-(I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION);
+pnv_i2c_reset(i2c);
 break;
 
 case I2C_RESET_ERRORS:
@@ -620,20 +628,6 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void 
*fdt,
 return 0;
 }
 
-static void pnv_i2c_reset(void *dev)
-{
-PnvI2C *i2c = PNV_I2C(dev);
-
-memset(i2c->regs, 0, sizeof(i2c->regs));
-
-i2c->regs[I2C_STAT_REG] = I2C_STAT_CMD_COMP;
-i2c->regs[I2C_EXTD_STAT_REG] =
-SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) |
-SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */
-
-fifo8_reset(>fifo);
-}
-
 static void pnv_i2c_realize(DeviceState *dev, Error **errp)
 {
 PnvI2C *i2c = PNV_I2C(dev);
-- 
2.31.1




[PATCH 8/8] ppc/pnv: Test pnv i2c master and connected devices

2023-11-10 Thread Glenn Miles
Tests the following for both P9 and P10:
  - I2C master POR status
  - I2C master status after immediate reset

Tests the following for P10 only:
  - Config pca9552 hotplug device pins as inputs then
Read the INPUT0/1 registers to verify all pins are high
  - Connected GPIO pin tests of P10 PCA9552 device.  Tests
output of pins 0-4 affect input of pins 5-9 respectively.
  - PCA9554 GPIO pins test.  Tests input and ouput functionality.

Signed-off-by: Glenn Miles 
---
 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 653 
 2 files changed, 654 insertions(+)
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 47dabf91d0..fbb0bd204c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -163,6 +163,7 @@ qtests_ppc64 = \
   qtests_ppc + \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + 
  \
   (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) +   
  \
+  (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) 
+  \
   (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) +
  \
   (slirp.found() ? ['pxe-test'] : []) +  \
   (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) 
+ \
diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c
new file mode 100644
index 00..8002dfcc0c
--- /dev/null
+++ b/tests/qtest/pnv-host-i2c-test.c
@@ -0,0 +1,653 @@
+/*
+ * QTest testcase for PowerNV 10 Host I2C Communications
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/misc/pca9552_regs.h"
+
+#define PPC_BIT(bit)(0x8000ULL >> (bit))
+#define PPC_BIT32(bit)  (0x8000 >> (bit))
+#define PPC_BIT8(bit)   (0x80 >> (bit))
+#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
+#define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
+ PPC_BIT32(bs))
+
+#define MASK_TO_LSH(m)  (__builtin_ffsll(m) - 1)
+#define GETFIELD(m, v)  (((v) & (m)) >> MASK_TO_LSH(m))
+#define SETFIELD(m, v, val) \
+(((v) & ~(m)) | typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
+
+#define P10_XSCOM_BASE  0x000603fcull
+#define PNV10_CHIP_MAX_I2C  5
+#define PNV10_XSCOM_I2CM_BASE   0xa
+#define PNV10_XSCOM_I2CM_SIZE   0x1000
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALLPPC_BITMASK(16, 31)
+#define I2C_INTR_INVALID_CMD   

[PATCH 0/8] Add powernv10 I2C devices and tests

2023-11-10 Thread Glenn Miles
This series of patches includes support, tests and fixes for
adding PCA9552 and PCA9554 I2C devices to the powernv10 chip.

The PCA9552 device is used for PCIe slot hotplug power control
and monitoring, while the PCA9554 device is used for presence
detection of IBM CableCard devices.  Both devices are required
by the Power Hypervisor Firmware on Power10 platforms.

Glenn Miles (8):
  ppc/pnv: Add pca9552 to powernv10 for PCIe hotplug power control
  ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control
  ppc/pnv: PNV I2C engines assigned incorrect XSCOM addresses
  ppc/pnv: Fix PNV I2C invalid status after reset
  ppc/pnv: Use resettable interface to reset child I2C buses
  misc: Add a pca9554 GPIO device model
  ppc/pnv: Add a pca9554 I2C device to powernv10
  ppc/pnv: Test pnv i2c master and connected devices

 MAINTAINERS |   2 +
 hw/misc/Kconfig |   4 +
 hw/misc/meson.build |   1 +
 hw/misc/pca9554.c   | 328 
 hw/ppc/Kconfig  |   2 +
 hw/ppc/pnv.c|  35 +-
 hw/ppc/pnv_i2c.c|  47 ++-
 include/hw/misc/pca9554.h   |  36 ++
 include/hw/misc/pca9554_regs.h  |  19 +
 tests/qtest/meson.build |   1 +
 tests/qtest/pnv-host-i2c-test.c | 653 
 11 files changed, 1106 insertions(+), 22 deletions(-)
 create mode 100644 hw/misc/pca9554.c
 create mode 100644 include/hw/misc/pca9554.h
 create mode 100644 include/hw/misc/pca9554_regs.h
 create mode 100644 tests/qtest/pnv-host-i2c-test.c

-- 
2.31.1




[PATCH v3] ppc/pnv: Fix number of I2C engines and ports for power9/10

2023-10-25 Thread Glenn Miles
Power9 is supposed to have 4 PIB-connected I2C engines with the
following number of ports on each engine:

0: 2
1: 13
2: 2
3: 2

Power10 also has 4 engines but has the following number of ports
on each engine:

0: 14
1: 14
2: 2
3: 16

Current code assumes that they all have the same (maximum) number.
This can be a problem if software expects to see a certain number
of ports present (Power Hypervisor seems to care).

Fixed this by adding separate tables for power9 and power10 that
map the I2C controller number to the number of I2C buses that should
be attached for that engine.

Reviewed-by: Cédric Le Goater 
Signed-off-by: Glenn Miles 
---
Based-on: <20231017221434.810363-1-mil...@linux.vnet.ibm.com>
([PATCH] ppc/pnv: Connect PNV I2C controller to powernv10)

Changes from v2:
- Moved I2C port definitions close to the class definitions
- Changed I2C port array type to const

 hw/ppc/pnv.c  | 12 
 include/hw/ppc/pnv_chip.h |  6 ++
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 2655b6e506..1a728b92a0 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1626,7 +1626,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pcc->i2c_ports_per_engine[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1651,6 +1652,7 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 {
 DeviceClass *dc = DEVICE_CLASS(klass);
 PnvChipClass *k = PNV_CHIP_CLASS(klass);
+static const int i2c_ports_per_engine[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2};
 
 k->chip_cfam_id = 0x220d10498000ull; /* P9 Nimbus DD2.0 */
 k->cores_mask = POWER9_CORE_MASK;
@@ -1667,7 +1669,7 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER9";
 k->num_pecs = PNV9_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV9_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS;
+k->i2c_ports_per_engine = i2c_ports_per_engine;
 
 device_class_set_parent_realize(dc, pnv_chip_power9_realize,
 >parent_realize);
@@ -1877,7 +1879,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pcc->i2c_ports_per_engine[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1902,6 +1905,7 @@ static void pnv_chip_power10_class_init(ObjectClass 
*klass, void *data)
 {
 DeviceClass *dc = DEVICE_CLASS(klass);
 PnvChipClass *k = PNV_CHIP_CLASS(klass);
+static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 
16};
 
 k->chip_cfam_id = 0x120da0498000ull; /* P10 DD1.0 (with NX) */
 k->cores_mask = POWER10_CORE_MASK;
@@ -1918,7 +1922,7 @@ static void pnv_chip_power10_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER10";
 k->num_pecs = PNV10_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV10_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS;
+k->i2c_ports_per_engine = i2c_ports_per_engine;
 
 device_class_set_parent_realize(dc, pnv_chip_power10_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 5815d96ecf..0ab5c42308 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -88,8 +88,7 @@ struct Pnv9Chip {
 #define PNV9_CHIP_MAX_PEC 3
 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
 
-#define PNV9_CHIP_MAX_I2C 3
-#define PNV9_CHIP_MAX_I2C_PORTS 1
+#define PNV9_CHIP_MAX_I2C 4
 PnvI2C  i2c[PNV9_CHIP_MAX_I2C];
 };
 
@@ -122,7 +121,6 @@ struct Pnv10Chip {
 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC];
 
 #define PNV10_CHIP_MAX_I2C 4
-#define PNV10_CHIP_MAX_I2C_PORTS 2
 PnvI2C   i2c[PNV10_CHIP_MAX_I2C];
 };
 
@@ -140,7 +138,7 @@ struct PnvChipClass {
 uint32_t num_phbs;
 
 uint32_t i2c_num_engines;
-uint32_t i2c_num_ports;
+const int*i2c_ports_per_engine;
 
 DeviceRealize parent_realize;
 
-- 
2.31.1




[PATCH v2 RESEND] ppc/pnv: Fix number of I2C engines and ports for power9/10

2023-10-24 Thread Glenn Miles
Power9 is supposed to have 4 PIB-connected I2C engines with the
following number of ports on each engine:

0: 2
1: 13
2: 2
3: 2

Power10 also has 4 engines but has the following number of ports
on each engine:

0: 14
1: 14
2: 2
3: 16

Current code assumes that they all have the same (maximum) number.
This can be a problem if software expects to see a certain number
of ports present (Power Hypervisor seems to care).

Fixed this by adding separate tables for power9 and power10 that
map the I2C controller number to the number of I2C buses that should
be attached for that engine.

Signed-off-by: Glenn Miles 
---
Based-on: <20231017221434.810363-1-mil...@linux.vnet.ibm.com>
([PATCH] ppc/pnv: Connect PNV I2C controller to powernv10)

Changes from v1:
- Added i2c_ports_per_engine to PnvChipClass
- replaced the word "ctlr" with "engine"

 hw/ppc/pnv.c  | 14 ++
 include/hw/ppc/pnv_chip.h |  6 ++
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 2655b6e506..f6dc84b869 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1507,6 +1507,8 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, 
Error **errp)
 }
 }
 
+static int pnv_power9_i2c_ports_per_engine[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2};
+
 static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
 {
 PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
@@ -1626,7 +1628,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pcc->i2c_ports_per_engine[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1667,7 +1670,7 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER9";
 k->num_pecs = PNV9_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV9_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS;
+k->i2c_ports_per_engine = pnv_power9_i2c_ports_per_engine;
 
 device_class_set_parent_realize(dc, pnv_chip_power9_realize,
 >parent_realize);
@@ -1751,6 +1754,8 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, 
Error **errp)
 }
 }
 
+static int pnv_power10_i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 
16};
+
 static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
 {
 PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
@@ -1877,7 +1882,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pcc->i2c_ports_per_engine[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1918,7 +1924,7 @@ static void pnv_chip_power10_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER10";
 k->num_pecs = PNV10_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV10_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS;
+k->i2c_ports_per_engine = pnv_power10_i2c_ports_per_engine;
 
 device_class_set_parent_realize(dc, pnv_chip_power10_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 5815d96ecf..3643e0fd86 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -88,8 +88,7 @@ struct Pnv9Chip {
 #define PNV9_CHIP_MAX_PEC 3
 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
 
-#define PNV9_CHIP_MAX_I2C 3
-#define PNV9_CHIP_MAX_I2C_PORTS 1
+#define PNV9_CHIP_MAX_I2C 4
 PnvI2C  i2c[PNV9_CHIP_MAX_I2C];
 };
 
@@ -122,7 +121,6 @@ struct Pnv10Chip {
 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC];
 
 #define PNV10_CHIP_MAX_I2C 4
-#define PNV10_CHIP_MAX_I2C_PORTS 2
 PnvI2C   i2c[PNV10_CHIP_MAX_I2C];
 };
 
@@ -140,7 +138,7 @@ struct PnvChipClass {
 uint32_t num_phbs;
 
 uint32_t i2c_num_engines;
-uint32_t i2c_num_ports;
+int  *i2c_ports_per_engine;
 
 DeviceRealize parent_realize;
 
-- 
2.31.1




[PATCH v2] ppc/pnv: Fix number of I2C engines and ports for power9/10

2023-10-24 Thread Glenn Miles
Power9 is supposed to have 4 PIB-connected I2C engines with the
following number of ports on each engine:

0: 2
1: 13
2: 2
3: 2

Power10 also has 4 engines but has the following number of ports
on each engine:

0: 14
1: 14
2: 2
3: 16

Current code assumes that they all have the same (maximum) number.
This can be a problem if software expects to see a certain number
of ports present (Power Hypervisor seems to care).

Fixed this by adding separate tables for power9 and power10 that
map the I2C controller number to the number of I2C buses that should
be attached for that engine.

Signed-off-by: Glenn Miles 
---

Changes from v1:
- Added i2c_ports_per_engine to PnvChipClass
- replaced the word "ctlr" with "engine"

 hw/ppc/pnv.c  | 14 ++
 include/hw/ppc/pnv_chip.h |  6 ++
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 2655b6e506..f6dc84b869 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1507,6 +1507,8 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, 
Error **errp)
 }
 }
 
+static int pnv_power9_i2c_ports_per_engine[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2};
+
 static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
 {
 PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
@@ -1626,7 +1628,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pcc->i2c_ports_per_engine[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1667,7 +1670,7 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER9";
 k->num_pecs = PNV9_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV9_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS;
+k->i2c_ports_per_engine = pnv_power9_i2c_ports_per_engine;
 
 device_class_set_parent_realize(dc, pnv_chip_power9_realize,
 >parent_realize);
@@ -1751,6 +1754,8 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, 
Error **errp)
 }
 }
 
+static int pnv_power10_i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 
16};
+
 static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
 {
 PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
@@ -1877,7 +1882,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pcc->i2c_ports_per_engine[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1918,7 +1924,7 @@ static void pnv_chip_power10_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER10";
 k->num_pecs = PNV10_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV10_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS;
+k->i2c_ports_per_engine = pnv_power10_i2c_ports_per_engine;
 
 device_class_set_parent_realize(dc, pnv_chip_power10_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 5815d96ecf..3643e0fd86 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -88,8 +88,7 @@ struct Pnv9Chip {
 #define PNV9_CHIP_MAX_PEC 3
 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
 
-#define PNV9_CHIP_MAX_I2C 3
-#define PNV9_CHIP_MAX_I2C_PORTS 1
+#define PNV9_CHIP_MAX_I2C 4
 PnvI2C  i2c[PNV9_CHIP_MAX_I2C];
 };
 
@@ -122,7 +121,6 @@ struct Pnv10Chip {
 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC];
 
 #define PNV10_CHIP_MAX_I2C 4
-#define PNV10_CHIP_MAX_I2C_PORTS 2
 PnvI2C   i2c[PNV10_CHIP_MAX_I2C];
 };
 
@@ -140,7 +138,7 @@ struct PnvChipClass {
 uint32_t num_phbs;
 
 uint32_t i2c_num_engines;
-uint32_t i2c_num_ports;
+int  *i2c_ports_per_engine;
 
 DeviceRealize parent_realize;
 
-- 
2.31.1




[PATCH v2 RESEND] misc/led: LED state is set opposite of what is expected

2023-10-24 Thread Glenn Miles
Testing of the LED state showed that when the LED polarity was
set to GPIO_POLARITY_ACTIVE_LOW and a low logic value was set on
the input GPIO of the LED, the LED was being turn off when it was
expected to be turned on.

Signed-off-by: Glenn Miles 
---

Changes from v1:
- Changed logic for readability

 hw/misc/led.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/misc/led.c b/hw/misc/led.c
index f6d6d68bce..42bb43a39a 100644
--- a/hw/misc/led.c
+++ b/hw/misc/led.c
@@ -63,7 +63,7 @@ static void led_set_state_gpio_handler(void *opaque, int 
line, int new_state)
 LEDState *s = LED(opaque);
 
 assert(line == 0);
-led_set_state(s, !!new_state != s->gpio_active_high);
+led_set_state(s, !!new_state == s->gpio_active_high);
 }
 
 static void led_reset(DeviceState *dev)
-- 
2.31.1




[PATCH v2] misc/led: LED state is set opposite of what is expected

2023-10-24 Thread Glenn Miles
Testing of the LED state showed that when the LED polarity was
set to GPIO_POLARITY_ACTIVE_LOW and a low logic value was set on
the input GPIO of the LED, the LED was being turn off when it was
expected to be turned on.

Signed-off-by: Glenn Miles 
---

Changes from v1:
- Changed logic for readability

 hw/misc/led.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/misc/led.c b/hw/misc/led.c
index f6d6d68bce..42bb43a39a 100644
--- a/hw/misc/led.c
+++ b/hw/misc/led.c
@@ -63,7 +63,7 @@ static void led_set_state_gpio_handler(void *opaque, int 
line, int new_state)
 LEDState *s = LED(opaque);
 
 assert(line == 0);
-led_set_state(s, !!new_state != s->gpio_active_high);
+led_set_state(s, !!new_state == s->gpio_active_high);
 }
 
 static void led_reset(DeviceState *dev)
-- 
2.31.1




[PATCH v3 1/2] misc/pca9552: Fix inverted input status

2023-10-24 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Reviewed-by: Andrew Jeffery 
Signed-off-by: Glenn Miles 
---

No changes from v2

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH v3 2/2] misc/pca9552: Let external devices set pca9552 inputs

2023-10-24 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Signed-off-by: Glenn Miles 
---
Changes from v2:
- Squashed changes to only update output GPIO if state changed
- Used Andrew's suggestion to simplify code

 hw/misc/pca9552.c | 50 +--
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 45 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..fe876471c8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -110,22 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 for (i = 0; i < k->pin_count; i++) {
 uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-uint8_t input_shift = (i % 8);
+uint8_t bit_mask = 1 << (i % 8);
 uint8_t config = pca955x_pin_get_config(s, i);
+uint8_t old_value = s->regs[input_reg] & bit_mask;
+uint8_t new_value;
 
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+s->regs[input_reg] &= ~bit_mask;
+} else {
+s->regs[input_reg] |=  bit_mask;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -133,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
 default:
 break;
 }
+
+/* update irq state only if pin state changed */
+new_value = s->regs[input_reg] & bit_mask;
+if (new_value != old_value) {
+qemu_set_irq(s->gpio_out[i], !!new_value);
+}
 }
 }
 
@@ -340,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +395,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->

[PATCH v3 0/2] misc/pca9552: Changes to support powernv10

2023-10-24 Thread Glenn Miles
This is a series of patches targeted at getting the pca9552
model ready for use by the powernv10 machine.

Changes from v2:
- Squashed changes to only update output GPIO if state changed
- Used Andrew's suggestion to simplify code

Glenn Miles (2):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs

 hw/misc/pca9552.c  | 58 +-
 include/hw/misc/pca9552.h  |  3 +-
 tests/qtest/pca9552-test.c |  6 ++--
 3 files changed, 56 insertions(+), 11 deletions(-)

-- 
2.31.1




[PATCH] misc/led: LED state is set opposite of what is expected

2023-10-24 Thread Glenn Miles
Testing of the LED state showed that when the LED polarity was
set to GPIO_POLARITY_ACTIVE_LOW and a low logic value was set on
the input GPIO of the LED, the LED was being turned off when it was
expected to be turned on.

Signed-off-by: Glenn Miles 
---
 hw/misc/led.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/misc/led.c b/hw/misc/led.c
index f6d6d68bce..96cad7578e 100644
--- a/hw/misc/led.c
+++ b/hw/misc/led.c
@@ -63,7 +63,7 @@ static void led_set_state_gpio_handler(void *opaque, int 
line, int new_state)
 LEDState *s = LED(opaque);
 
 assert(line == 0);
-led_set_state(s, !!new_state != s->gpio_active_high);
+led_set_state(s, !new_state != s->gpio_active_high);
 }
 
 static void led_reset(DeviceState *dev)
-- 
2.31.1




[PATCH] ppc/pnv: Fix number of I2C engines and ports for power9/10

2023-10-23 Thread Glenn Miles
Power9 is supposed to have 4 PIB-connected I2C engines with the
following number of ports on each engine:

0: 2
1: 13
2: 2
3: 2

Power10 also has 4 engines but has the following number of ports
on each engine:

0: 14
1: 14
2: 2
3: 16

Current code assumes that they all have the same (maximum) number.
This can be a problem if software expects to see a certain number
of ports present (Power Hypervisor seems to care).

Fixed this by adding separate tables for power9 and power10 that
map the I2C controller number to the number of I2C buses that should
be attached for that engine.

Signed-off-by: Glenn Miles 
---

Based-on: <20231017221434.810363-1-mil...@linux.vnet.ibm.com>
([PATCH] ppc/pnv: Connect PNV I2C controller to powernv10)

 hw/ppc/pnv.c  | 12 
 include/hw/ppc/pnv_chip.h |  5 +
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 2655b6e506..6ad4a1a7b1 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1507,6 +1507,8 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, 
Error **errp)
 }
 }
 
+static int pnv_power9_i2c_ports_per_ctlr[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2};
+
 static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
 {
 PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
@@ -1626,7 +1628,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pnv_power9_i2c_ports_per_ctlr[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1667,7 +1670,6 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER9";
 k->num_pecs = PNV9_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV9_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS;
 
 device_class_set_parent_realize(dc, pnv_chip_power9_realize,
 >parent_realize);
@@ -1751,6 +1753,8 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, 
Error **errp)
 }
 }
 
+static int pnv_power10_i2c_ports_per_ctlr[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 
16};
+
 static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
 {
 PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
@@ -1877,7 +1881,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 Object *obj =  OBJECT(>i2c[i]);
 
 object_property_set_int(obj, "engine", i + 1, _fatal);
-object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+object_property_set_int(obj, "num-busses",
+pnv_power10_i2c_ports_per_ctlr[i],
 _fatal);
 object_property_set_link(obj, "chip", OBJECT(chip), _abort);
 if (!qdev_realize(DEVICE(obj), NULL, errp)) {
@@ -1918,7 +1923,6 @@ static void pnv_chip_power10_class_init(ObjectClass 
*klass, void *data)
 dc->desc = "PowerNV Chip POWER10";
 k->num_pecs = PNV10_CHIP_MAX_PEC;
 k->i2c_num_engines = PNV10_CHIP_MAX_I2C;
-k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS;
 
 device_class_set_parent_realize(dc, pnv_chip_power10_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 5815d96ecf..be1fec5698 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -88,8 +88,7 @@ struct Pnv9Chip {
 #define PNV9_CHIP_MAX_PEC 3
 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
 
-#define PNV9_CHIP_MAX_I2C 3
-#define PNV9_CHIP_MAX_I2C_PORTS 1
+#define PNV9_CHIP_MAX_I2C 4
 PnvI2C  i2c[PNV9_CHIP_MAX_I2C];
 };
 
@@ -122,7 +121,6 @@ struct Pnv10Chip {
 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC];
 
 #define PNV10_CHIP_MAX_I2C 4
-#define PNV10_CHIP_MAX_I2C_PORTS 2
 PnvI2C   i2c[PNV10_CHIP_MAX_I2C];
 };
 
@@ -140,7 +138,6 @@ struct PnvChipClass {
 uint32_t num_phbs;
 
 uint32_t i2c_num_engines;
-uint32_t i2c_num_ports;
 
 DeviceRealize parent_realize;
 
-- 
2.31.1




[PATCH v2 2/3] misc/pca9552: Let external devices set pca9552 inputs

2023-10-20 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Signed-off-by: Glenn Miles 
---

No change from v1

 hw/misc/pca9552.c | 41 ++-
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..ed814d1f98 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -116,16 +118,22 @@ static void pca955x_update_pin_input(PCA955xState *s)
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
+qemu_set_irq(s->gpio_out[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+qemu_set_irq(s->gpio_out[i], 0);
+s->regs[input_reg] &= ~(1 << input_shift);
+} else {
+qemu_set_irq(s->gpio_out[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -340,6 +348,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +367,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +390,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +419,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
+qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
 }
 
 static Property pca955x_properties[] = {
diff --git a/include/hw/misc/pca9552.h b/include/hw/misc/pca9552.h
index b6f4e264fe..c36525f0c3 100644
--- a/include/hw/misc/pca9552.h
+++ b/include/hw/misc/pca9552.h
@@ -30,7 +30,8 @@ struct PCA955xState {
 uint8_t pointer;
 
 uint8_t regs[PCA955X_NR_REGS];
-qemu_irq gpio[PCA955X_PIN_COUNT_MAX];
+qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX];
+uint8_t ext_state[PCA955X_PIN_COUNT_MAX];
 char *description; /* For debugging purpose only */
 };
 
-- 
2.31.1




[PATCH v2 1/3] misc/pca9552: Fix inverted input status

2023-10-20 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Signed-off-by: Glenn Miles 
---

No change from v1

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH v2 0/3] misc/pca9552: Changes to support powernv10

2023-10-20 Thread Glenn Miles
This is a series of patches targeted at getting the pca9552
model ready for use by the powernv10 machine.

Changes from v1:
 - Added check to only update output GPIO if state changed

Glenn Miles (3):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs
  misc/pca9552: Only update output GPIOs if state changed

 hw/misc/pca9552.c  | 64 +-
 include/hw/misc/pca9552.h  |  3 +-
 tests/qtest/pca9552-test.c |  6 ++--
 3 files changed, 62 insertions(+), 11 deletions(-)

-- 
2.31.1




[PATCH v2 3/3] misc/pca9552: Only update output GPIOs if state changed

2023-10-20 Thread Glenn Miles
The pca9552 code was updating output GPIO states whenever
the pin state was updated even if the state did not change.
This commit adds a check so that we only update the GPIO
output when the pin state actually changes.

Signed-off-by: Glenn Miles 
---

New commit in v2

 hw/misc/pca9552.c | 25 ++---
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index ed814d1f98..4ed6903404 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -112,14 +112,15 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 for (i = 0; i < k->pin_count; i++) {
 uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
-uint8_t input_shift = (i % 8);
+uint8_t bit_mask = 1 << (i % 8);
 uint8_t config = pca955x_pin_get_config(s, i);
+uint8_t old_value = s->regs[input_reg] & bit_mask;
+uint8_t new_value;
 
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio_out[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 break;
 case PCA9552_LED_OFF:
 /*
@@ -128,11 +129,9 @@ static void pca955x_update_pin_input(PCA955xState *s)
  * external device drives it low.
  */
 if (s->ext_state[i] == PCA9552_PIN_LOW) {
-qemu_set_irq(s->gpio_out[i], 0);
-s->regs[input_reg] &= ~(1 << input_shift);
+s->regs[input_reg] &= ~bit_mask;
 } else {
-qemu_set_irq(s->gpio_out[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+s->regs[input_reg] |=  bit_mask;
 }
 break;
 case PCA9552_LED_PWM0:
@@ -141,6 +140,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 default:
 break;
 }
+
+/* update irq state only if pin state changed */
+new_value = s->regs[input_reg] & bit_mask;
+if (new_value != old_value) {
+if (new_value) {
+/* changed from 0 to 1 */
+qemu_set_irq(s->gpio_out[i], 1);
+} else {
+/* changed from 1 to 0 */
+qemu_set_irq(s->gpio_out[i], 0);
+}
+}
 }
 }
 
-- 
2.31.1




[PATCH 1/2] misc/pca9552: Fix inverted input status

2023-10-19 Thread Glenn Miles
The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Signed-off-by: Glenn Miles 
---

Changes from prior version:
- Added comment regarding pca953X
- Added cover letter

 hw/misc/pca9552.c  | 18 +-
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
 switch (config) {
 case PCA9552_LED_ON:
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
-break;
-case PCA9552_LED_OFF:
+/* Pin is set to 0V to turn on LED */
 qemu_set_irq(s->gpio[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
+case PCA9552_LED_OFF:
+/*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1.
+ */
+qemu_set_irq(s->gpio[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
 /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x55);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x0);
+g_assert_cmphex(value, ==, 0xFF);
 
 pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, 
QGuestAllocator *alloc)
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT0);
-g_assert_cmphex(value, ==, 0x01);
+g_assert_cmphex(value, ==, 0xFE);
 
 value = i2c_get8(i2cdev, PCA9552_LS3);
 g_assert_cmphex(value, ==, 0x54);
 
 value = i2c_get8(i2cdev, PCA9552_INPUT1);
-g_assert_cmphex(value, ==, 0x10);
+g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1




[PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs

2023-10-19 Thread Glenn Miles
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model.  This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.

In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off).  If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.

Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:

   PCA9552
   Configured
   State

  | Hi-Z | Low |
--+--+-+
  External   Hi-Z |  Hi  | Low |
  Device--+--+-+
  State  Low  |  Low | Low |
--+--+-+

Signed-off-by: Glenn Miles 
---

Changes from previous version:
 - Added #define's for external state values
 - Added logic state table to commit message
 - Added cover letter

 hw/misc/pca9552.c | 41 ++-
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..ed814d1f98 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
 #define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW  0x0
+#define PCA9552_PIN_HIZ  0x1
 
 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
 
@@ -116,16 +118,22 @@ static void pca955x_update_pin_input(PCA955xState *s)
 switch (config) {
 case PCA9552_LED_ON:
 /* Pin is set to 0V to turn on LED */
-qemu_set_irq(s->gpio[i], 0);
+qemu_set_irq(s->gpio_out[i], 0);
 s->regs[input_reg] &= ~(1 << input_shift);
 break;
 case PCA9552_LED_OFF:
 /*
  * Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
  */
-qemu_set_irq(s->gpio[i], 1);
-s->regs[input_reg] |= 1 << input_shift;
+if (s->ext_state[i] == PCA9552_PIN_LOW) {
+qemu_set_irq(s->gpio_out[i], 0);
+s->regs[input_reg] &= ~(1 << input_shift);
+} else {
+qemu_set_irq(s->gpio_out[i], 1);
+s->regs[input_reg] |= 1 << input_shift;
+}
 break;
 case PCA9552_LED_PWM0:
 case PCA9552_LED_PWM1:
@@ -340,6 +348,7 @@ static const VMStateDescription pca9552_vmstate = {
 VMSTATE_UINT8(len, PCA955xState),
 VMSTATE_UINT8(pointer, PCA955xState),
 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
 VMSTATE_I2C_SLAVE(i2c, PCA955xState),
 VMSTATE_END_OF_LIST()
 }
@@ -358,6 +367,7 @@ static void pca9552_reset(DeviceState *dev)
 s->regs[PCA9552_LS2] = 0x55;
 s->regs[PCA9552_LS3] = 0x55;
 
+memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
 pca955x_update_pin_input(s);
 
 s->pointer = 0xFF;
@@ -380,6 +390,26 @@ static void pca955x_initfn(Object *obj)
 }
 }
 
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+if (s->ext_state[pin] != level) {
+uint16_t pins_status = pca955x_pins_get_status(s);
+s->ext_state[pin] = level;
+pca955x_update_pin_input(s);
+pca955x_display_pins_status(s, pins_status);
+}
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+PCA955xState *s = PCA955X(opaque);
+PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+assert((pin >= 0) && (pin < k->pin_count));
+pca955x_set_ext_state(s, pin, level);
+}
+
 static void pca955x_realize(DeviceState *dev, Error **errp)
 {
 PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +419,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 s->description = g_strdup("pca-unspecified");
 }
 
-qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
+qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
 }
 
 static Property pca955x_properties[] = {
diff --git a/include/hw/misc/pca9552.h b/include/hw/misc/pca9552.h
index b6f4e264fe..c36525f0c3 100644
--- a/include/hw/misc/pca9552.h
+++ b/include/hw/misc/pca9552.h
@@ -30,7 +30,8 @@ struct PCA955xState {
 uint8_t pointer;
 
 uint8_t regs[PCA955X_NR_REGS];
-qemu_irq gpio[PCA955X_PIN_COUNT_MAX];
+qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX];
+uint8_t ext_state[PCA955X_PIN_COUNT_MAX];
 char *description; /* For debugging purpose only */
 };
 
-- 
2.31.1




[PATCH 0/2] misc/pca9552: Changes to support powernv10

2023-10-19 Thread Glenn Miles
This is a series of patches targeted at getting the pca9552
model ready for use by the powernv10 machine.

Glenn Miles (2):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs

 hw/misc/pca9552.c  | 51 +-
 include/hw/misc/pca9552.h  |  3 ++-
 tests/qtest/pca9552-test.c |  6 ++---
 3 files changed, 50 insertions(+), 10 deletions(-)

-- 
2.31.1




[PATCH] ppc/pnv: Connect PNV I2C controller to powernv10

2023-10-17 Thread Glenn Miles
Wires up four I2C controller instances to the powernv10 chip
XSCOM address space.

Each controller instance is wired up to two I2C buses of
its own.  No other I2C devices are connected to the buses
at this time.

Signed-off-by: Glenn Miles 
---
Based-on: <20231016222013.3739530-1-mil...@linux.vnet.ibm.com>
([PATCH v3 0/2] Add PowerNV I2C Controller Model)

 hw/ppc/pnv.c   | 29 +
 include/hw/ppc/pnv_chip.h  |  4 
 include/hw/ppc/pnv_xscom.h |  3 +++
 3 files changed, 36 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index e0b3478325..2655b6e506 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1695,6 +1695,10 @@ static void pnv_chip_power10_instance_init(Object *obj)
 object_initialize_child(obj, "pec[*]", >pecs[i],
 TYPE_PNV_PHB5_PEC);
 }
+
+for (i = 0; i < pcc->i2c_num_engines; i++) {
+object_initialize_child(obj, "i2c[*]", >i2c[i], TYPE_PNV_I2C);
+}
 }
 
 static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp)
@@ -1753,6 +1757,7 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 PnvChip *chip = PNV_CHIP(dev);
 Pnv10Chip *chip10 = PNV10_CHIP(dev);
 Error *local_err = NULL;
+int i;
 
 /* XSCOM bridge is first */
 pnv_xscom_realize(chip, PNV10_XSCOM_SIZE, _err);
@@ -1863,6 +1868,28 @@ static void pnv_chip_power10_realize(DeviceState *dev, 
Error **errp)
 error_propagate(errp, local_err);
 return;
 }
+
+
+/*
+ * I2C
+ */
+for (i = 0; i < pcc->i2c_num_engines; i++) {
+Object *obj =  OBJECT(>i2c[i]);
+
+object_property_set_int(obj, "engine", i + 1, _fatal);
+object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+_fatal);
+object_property_set_link(obj, "chip", OBJECT(chip), _abort);
+if (!qdev_realize(DEVICE(obj), NULL, errp)) {
+return;
+}
+pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE +
+chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE,
+>i2c[i].xscom_regs);
+qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
+  qdev_get_gpio_in(DEVICE(>psi),
+   PSIHB9_IRQ_SBE_I2C));
+}
 }
 
 static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -1890,6 +1917,8 @@ static void pnv_chip_power10_class_init(ObjectClass 
*klass, void *data)
 k->xscom_pcba = pnv_chip_power10_xscom_pcba;
 dc->desc = "PowerNV Chip POWER10";
 k->num_pecs = PNV10_CHIP_MAX_PEC;
+k->i2c_num_engines = PNV10_CHIP_MAX_I2C;
+k->i2c_num_ports = PNV10_CHIP_MAX_I2C_PORTS;
 
 device_class_set_parent_realize(dc, pnv_chip_power10_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 90cfbad1a5..5815d96ecf 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -120,6 +120,10 @@ struct Pnv10Chip {
 
 #define PNV10_CHIP_MAX_PEC 2
 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC];
+
+#define PNV10_CHIP_MAX_I2C 4
+#define PNV10_CHIP_MAX_I2C_PORTS 2
+PnvI2C   i2c[PNV10_CHIP_MAX_I2C];
 };
 
 #define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf)
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
index 0c8b873c4c..2b607b22c9 100644
--- a/include/hw/ppc/pnv_xscom.h
+++ b/include/hw/ppc/pnv_xscom.h
@@ -152,6 +152,9 @@ struct PnvXScomInterfaceClass {
 #define PNV10_XSCOM_PSIHB_BASE 0x3011D00
 #define PNV10_XSCOM_PSIHB_SIZE 0x100
 
+#define PNV10_XSCOM_I2CM_BASE  PNV9_XSCOM_I2CM_BASE
+#define PNV10_XSCOM_I2CM_SIZE  PNV9_XSCOM_I2CM_SIZE
+
 #define PNV10_XSCOM_OCC_BASE   PNV9_XSCOM_OCC_BASE
 #define PNV10_XSCOM_OCC_SIZE   PNV9_XSCOM_OCC_SIZE
 
-- 
2.31.1




[PATCH v3 0/2] Add PowerNV I2C Controller Model

2023-10-16 Thread Glenn Miles
Upstreams the PowerNV I2C controller model originally
authored by Cédric Le Goater with minor changes by
myself to split the actual addition of the model from
wiring it up to a power processor model.

This series only attaches the controller to the powernv9
chip model, but is expected to eventually also be attached
to the powernv10 chip model.

Cédric Le Goater (2):
  ppc/pnv: Add an I2C controller model
  ppc/pnv: Connect I2C controller model to powernv9 chip

 hw/ppc/meson.build |   1 +
 hw/ppc/pnv.c   |  28 ++
 hw/ppc/pnv_i2c.c   | 697 +
 include/hw/ppc/pnv_chip.h  |   8 +
 include/hw/ppc/pnv_i2c.h   |  38 ++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 775 insertions(+)
 create mode 100644 hw/ppc/pnv_i2c.c
 create mode 100644 include/hw/ppc/pnv_i2c.h

-- 
2.31.1




[PATCH v3 2/2] ppc/pnv: Connect I2C controller model to powernv9 chip

2023-10-16 Thread Glenn Miles
From: Cédric Le Goater 

Wires up three I2C controller instances to the powernv9 chip
XSCOM address space.

Each controller instance is wired up to a single I2C bus of
its own.  No other I2C devices are connected to the buses
at this time.

Signed-off-by: Cédric Le Goater 
[milesg: Split wiring from addition of model itself]
[milesg: Added new commit message]
[milesg: Moved hardcoded attributes into PnvChipClass]
[milesg: Removed TODO comment for I2C]
Signed-off-by: Glenn Miles 
---

Changes from v2:
-Removed TODO comment for I2C

 hw/ppc/pnv.c  | 28 
 include/hw/ppc/pnv_chip.h |  8 
 2 files changed, 36 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index eb54f93986..e0b3478325 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1438,6 +1438,10 @@ static void pnv_chip_power9_instance_init(Object *obj)
 object_initialize_child(obj, "pec[*]", >pecs[i],
 TYPE_PNV_PHB4_PEC);
 }
+
+for (i = 0; i < pcc->i2c_num_engines; i++) {
+object_initialize_child(obj, "i2c[*]", >i2c[i], TYPE_PNV_I2C);
+}
 }
 
 static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq,
@@ -1510,6 +1514,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 PnvChip *chip = PNV_CHIP(dev);
 Pnv9Psi *psi9 = >psi;
 Error *local_err = NULL;
+int i;
 
 /* XSCOM bridge is first */
 pnv_xscom_realize(chip, PNV9_XSCOM_SIZE, _err);
@@ -1613,6 +1618,27 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 error_propagate(errp, local_err);
 return;
 }
+
+/*
+ * I2C
+ */
+for (i = 0; i < pcc->i2c_num_engines; i++) {
+Object *obj =  OBJECT(>i2c[i]);
+
+object_property_set_int(obj, "engine", i + 1, _fatal);
+object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+_fatal);
+object_property_set_link(obj, "chip", OBJECT(chip), _abort);
+if (!qdev_realize(DEVICE(obj), NULL, errp)) {
+return;
+}
+pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE +
+   chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE,
+>i2c[i].xscom_regs);
+qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
+  qdev_get_gpio_in(DEVICE(>psi),
+   PSIHB9_IRQ_SBE_I2C));
+}
 }
 
 static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -1640,6 +1666,8 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 k->xscom_pcba = pnv_chip_power9_xscom_pcba;
 dc->desc = "PowerNV Chip POWER9";
 k->num_pecs = PNV9_CHIP_MAX_PEC;
+k->i2c_num_engines = PNV9_CHIP_MAX_I2C;
+k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS;
 
 device_class_set_parent_realize(dc, pnv_chip_power9_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 53e1d921d7..90cfbad1a5 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -9,6 +9,7 @@
 #include "hw/ppc/pnv_psi.h"
 #include "hw/ppc/pnv_sbe.h"
 #include "hw/ppc/pnv_xive.h"
+#include "hw/ppc/pnv_i2c.h"
 #include "hw/sysbus.h"
 
 OBJECT_DECLARE_TYPE(PnvChip, PnvChipClass,
@@ -86,6 +87,10 @@ struct Pnv9Chip {
 
 #define PNV9_CHIP_MAX_PEC 3
 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
+
+#define PNV9_CHIP_MAX_I2C 3
+#define PNV9_CHIP_MAX_I2C_PORTS 1
+PnvI2C  i2c[PNV9_CHIP_MAX_I2C];
 };
 
 /*
@@ -130,6 +135,9 @@ struct PnvChipClass {
 uint32_t num_pecs;
 uint32_t num_phbs;
 
+uint32_t i2c_num_engines;
+uint32_t i2c_num_ports;
+
 DeviceRealize parent_realize;
 
 uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
-- 
2.31.1




[PATCH v3 1/2] ppc/pnv: Add an I2C controller model

2023-10-16 Thread Glenn Miles
From: Cédric Le Goater 

The more recent IBM power processors have an embedded I2C
controller that is accessible by software via the XSCOM
address space.

Each instance of the I2C controller is capable of controlling
multiple I2C buses (one at a time).  Prior to beginning a
transaction on an I2C bus, the bus must be selected by writing
the port number associated with the bus into the PORT_NUM
field of the MODE register.  Once an I2C bus is selected,
the status of the bus can be determined by reading the
Status and Extended Status registers.

I2C bus transactions can be started by writing a command to
the Command register and reading/writing data from/to the
FIFO register.

Not supported :

 . 10 bit I2C addresses
 . Multimaster
 . Slave

Signed-off-by: Cédric Le Goater 
[milesg: Split wiring to powernv9 into its own commit]
[milesg: Added more detail to commit message]
[milesg: Added SPDX Licensed Identifier to new files]
[milesg: updated copyright dates]
[milesg: Added use of g_autofree]
[milesg: Added NULL check after pnv_i2c_get_bus]
Signed-off-by: Glenn Miles 
---

Changes from v2:
-Fixed uninitialized usage of g_autofree
-Added NULL checks after calls to pnv_i2c_get_bus

 hw/ppc/meson.build |   1 +
 hw/ppc/pnv_i2c.c   | 697 +
 include/hw/ppc/pnv_i2c.h   |  38 ++
 include/hw/ppc/pnv_xscom.h |   3 +
 4 files changed, 739 insertions(+)
 create mode 100644 hw/ppc/pnv_i2c.c
 create mode 100644 include/hw/ppc/pnv_i2c.h

diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index 7c2c52434a..87b756a701 100644
--- a/hw/ppc/meson.build
+++ b/hw/ppc/meson.build
@@ -43,6 +43,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
   'pnv.c',
   'pnv_xscom.c',
   'pnv_core.c',
+  'pnv_i2c.c',
   'pnv_lpc.c',
   'pnv_psi.c',
   'pnv_occ.c',
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
new file mode 100644
index 00..f75e59e709
--- /dev/null
+++ b/hw/ppc/pnv_i2c.c
@@ -0,0 +1,697 @@
+/*
+ * QEMU PowerPC PowerNV Processor I2C model
+ *
+ * Copyright (c) 2019-2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "sysemu/reset.h"
+
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_chip.h"
+#include "hw/ppc/pnv_i2c.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/fdt.h"
+
+#include 
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALLPPC_BITMASK(16, 31)
+#define I2C_INTR_INVALID_CMDPPC_BIT(16)
+#define I2C_INTR_LBUS_PARITY_ERRPPC_BIT(17)
+#define I2C_INTR_BKEND_OVERRUN_ERR  PPC_BIT(18)
+#define I2C_INTR_BKEND_ACCESS_ERR   PPC_BIT(19)
+#define I2C_INTR_ARBT_LOST_ERR  PPC_BIT(20)
+#define I2C_INTR_NACK_RCVD_ERR  PPC_BIT(21)
+#define I2C_INTR_DATA

[PATCH v2 2/2] ppc/pnv: Connect I2C controller model to powernv9 chip

2023-10-12 Thread Glenn Miles
From: Cédric Le Goater 

Wires up three I2C controller instances to the powernv9 chip
XSCOM address space.

Each controller instance is wired up to a single I2C bus of
its own.  No other I2C devices are connected to the buses
at this time.

Signed-off-by: Cédric Le Goater 
[milesg: Split wiring from addition of model itself]
[milesg: Added new commit message]
[milesg: Moved hardcoded attributes into PnvChipClass]
Signed-off-by: Glenn Miles 
---

Changes in v2:
- Moved some hardcoded attributes into PnvChipClass

 hw/ppc/pnv.c  | 29 +
 include/hw/ppc/pnv_chip.h |  8 
 2 files changed, 37 insertions(+)

diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index eb54f93986..7db6f3abe5 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -1438,6 +1438,10 @@ static void pnv_chip_power9_instance_init(Object *obj)
 object_initialize_child(obj, "pec[*]", >pecs[i],
 TYPE_PNV_PHB4_PEC);
 }
+
+for (i = 0; i < pcc->i2c_num_engines; i++) {
+object_initialize_child(obj, "i2c[*]", >i2c[i], TYPE_PNV_I2C);
+}
 }
 
 static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq,
@@ -1510,6 +1514,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 PnvChip *chip = PNV_CHIP(dev);
 Pnv9Psi *psi9 = >psi;
 Error *local_err = NULL;
+int i;
 
 /* XSCOM bridge is first */
 pnv_xscom_realize(chip, PNV9_XSCOM_SIZE, _err);
@@ -1613,6 +1618,28 @@ static void pnv_chip_power9_realize(DeviceState *dev, 
Error **errp)
 error_propagate(errp, local_err);
 return;
 }
+
+/*
+ * I2C
+ * TODO: The number of busses is specific to each platform
+ */
+for (i = 0; i < pcc->i2c_num_engines; i++) {
+Object *obj =  OBJECT(>i2c[i]);
+
+object_property_set_int(obj, "engine", i + 1, _fatal);
+object_property_set_int(obj, "num-busses", pcc->i2c_num_ports,
+_fatal);
+object_property_set_link(obj, "chip", OBJECT(chip), _abort);
+if (!qdev_realize(DEVICE(obj), NULL, errp)) {
+return;
+}
+pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE +
+   chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE,
+>i2c[i].xscom_regs);
+qdev_connect_gpio_out(DEVICE(>i2c[i]), 0,
+  qdev_get_gpio_in(DEVICE(>psi),
+   PSIHB9_IRQ_SBE_I2C));
+}
 }
 
 static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -1640,6 +1667,8 @@ static void pnv_chip_power9_class_init(ObjectClass 
*klass, void *data)
 k->xscom_pcba = pnv_chip_power9_xscom_pcba;
 dc->desc = "PowerNV Chip POWER9";
 k->num_pecs = PNV9_CHIP_MAX_PEC;
+k->i2c_num_engines = PNV9_CHIP_MAX_I2C;
+k->i2c_num_ports = PNV9_CHIP_MAX_I2C_PORTS;
 
 device_class_set_parent_realize(dc, pnv_chip_power9_realize,
 >parent_realize);
diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h
index 53e1d921d7..90cfbad1a5 100644
--- a/include/hw/ppc/pnv_chip.h
+++ b/include/hw/ppc/pnv_chip.h
@@ -9,6 +9,7 @@
 #include "hw/ppc/pnv_psi.h"
 #include "hw/ppc/pnv_sbe.h"
 #include "hw/ppc/pnv_xive.h"
+#include "hw/ppc/pnv_i2c.h"
 #include "hw/sysbus.h"
 
 OBJECT_DECLARE_TYPE(PnvChip, PnvChipClass,
@@ -86,6 +87,10 @@ struct Pnv9Chip {
 
 #define PNV9_CHIP_MAX_PEC 3
 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC];
+
+#define PNV9_CHIP_MAX_I2C 3
+#define PNV9_CHIP_MAX_I2C_PORTS 1
+PnvI2C  i2c[PNV9_CHIP_MAX_I2C];
 };
 
 /*
@@ -130,6 +135,9 @@ struct PnvChipClass {
 uint32_t num_pecs;
 uint32_t num_phbs;
 
+uint32_t i2c_num_engines;
+uint32_t i2c_num_ports;
+
 DeviceRealize parent_realize;
 
 uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
-- 
2.31.1




[PATCH v2 1/2] ppc/pnv: Add an I2C controller model

2023-10-12 Thread Glenn Miles
From: Cédric Le Goater 

The more recent IBM power processors have an embedded I2C
controller that is accessible by software via the XSCOM
address space.

Each instance of the I2C controller is capable of controlling
multiple I2C buses (one at a time).  Prior to beginning a
transaction on an I2C bus, the bus must be selected by writing
the port number associated with the bus into the PORT_NUM
field of the MODE register.  Once an I2C bus is selected,
the status of the bus can be determined by reading the
Status and Extended Status registers.

I2C bus transactions can be started by writing a command to
the Command register and reading/writing data from/to the
FIFO register.

Not supported :

 . 10 bit I2C addresses
 . Multimaster
 . Slave

Signed-off-by: Cédric Le Goater 
[milesg: Split wiring to powernv9 into its own commit]
[milesg: Added more detail to commit message]
[milesg: Added SPDX Licensed Identifier to new files]
[milesg: updated copyright dates]
[milesg: Added use of g_autofree]
Signed-off-by: Glenn Miles 
---

Changes in v2:
- Updated copyright dates
- Removed copyright paragraph (replaced by SPDX-License-Identifier)
- Added use of g_autofree

 hw/ppc/meson.build |   1 +
 hw/ppc/pnv_i2c.c   | 673 +
 include/hw/ppc/pnv_i2c.h   |  38 +++
 include/hw/ppc/pnv_xscom.h |   3 +
 4 files changed, 715 insertions(+)
 create mode 100644 hw/ppc/pnv_i2c.c
 create mode 100644 include/hw/ppc/pnv_i2c.h

diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index 7c2c52434a..87b756a701 100644
--- a/hw/ppc/meson.build
+++ b/hw/ppc/meson.build
@@ -43,6 +43,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
   'pnv.c',
   'pnv_xscom.c',
   'pnv_core.c',
+  'pnv_i2c.c',
   'pnv_lpc.c',
   'pnv_psi.c',
   'pnv_occ.c',
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
new file mode 100644
index 00..9c431bf1ee
--- /dev/null
+++ b/hw/ppc/pnv_i2c.c
@@ -0,0 +1,673 @@
+/*
+ * QEMU PowerPC PowerNV Processor I2C model
+ *
+ * Copyright (c) 2019-2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "sysemu/reset.h"
+
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_chip.h"
+#include "hw/ppc/pnv_i2c.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/fdt.h"
+
+#include 
+
+/* I2C FIFO register */
+#define I2C_FIFO_REG0x4
+#define I2C_FIFOPPC_BITMASK(0, 7)
+
+/* I2C command register */
+#define I2C_CMD_REG 0x5
+#define I2C_CMD_WITH_START  PPC_BIT(0)
+#define I2C_CMD_WITH_ADDR   PPC_BIT(1)
+#define I2C_CMD_READ_CONT   PPC_BIT(2)
+#define I2C_CMD_WITH_STOP   PPC_BIT(3)
+#define I2C_CMD_INTR_STEERING   PPC_BITMASK(6, 7) /* P9 */
+#define   I2C_CMD_INTR_STEER_HOST   1
+#define   I2C_CMD_INTR_STEER_OCC2
+#define I2C_CMD_DEV_ADDRPPC_BITMASK(8, 14)
+#define I2C_CMD_READ_NOT_WRITE  PPC_BIT(15)
+#define I2C_CMD_LEN_BYTES   PPC_BITMASK(16, 31)
+#define I2C_MAX_TFR_LEN 0xfff0ull
+
+/* I2C mode register */
+#define I2C_MODE_REG0x6
+#define I2C_MODE_BIT_RATE_DIV   PPC_BITMASK(0, 15)
+#define I2C_MODE_PORT_NUM   PPC_BITMASK(16, 21)
+#define I2C_MODE_ENHANCED   PPC_BIT(28)
+#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
+#define I2C_MODE_PACING_ALLOW   PPC_BIT(30)
+#define I2C_MODE_WRAP   PPC_BIT(31)
+
+/* I2C watermark register */
+#define I2C_WATERMARK_REG   0x7
+#define I2C_WATERMARK_HIGH  PPC_BITMASK(16, 19)
+#define I2C_WATERMARK_LOW   PPC_BITMASK(24, 27)
+
+/*
+ * I2C interrupt mask and condition registers
+ *
+ * NB: The function of 0x9 and 0xa changes depending on whether you're reading
+ * or writing to them. When read they return the interrupt condition bits
+ * and on writes they update the interrupt mask register.
+ *
+ *  The bit definitions are the same for all the interrupt registers.
+ */
+#define I2C_INTR_MASK_REG   0x8
+
+#define I2C_INTR_RAW_COND_REG   0x9 /* read */
+#define I2C_INTR_MASK_OR_REG0x9 /* write*/
+
+#define I2C_INTR_COND_REG   0xa /* read */
+#define I2C_INTR_MASK_AND_REG   0xa /* write */
+
+#define I2C_INTR_ALLPPC_BITMASK(16, 31)
+#define I2C_INTR_INVALID_CMDPPC_BIT(16)
+#define I2C_INTR_LBUS_PARITY_ERRPPC_BIT(17)
+#define I2C_INTR_BKEND_OVERRUN_ERR  PPC_BIT(18)
+#define I2C_INTR_BKEND_ACCESS_ERR   PPC_BIT(19)
+#define I2C_INTR_ARBT_LOST_ERR  PPC_BIT(20)
+#define I2C_INTR_NACK_RCVD_ERR  PPC_BIT(21)
+#define I2C_INTR_DATA_REQ   PPC_BI

[PATCH v2 0/2] Add PowerNV I2C Controller Model

2023-10-12 Thread Glenn Miles
Upstreams the PowerNV I2C controller model originally
authored by Cédric Le Goater with minor changes by
myself to split the actual addition of the model from
wiring it up to a power processor model.

This series only attaches the controller to the powernv9
chip model, but is expected to eventually also be attached
to the powernv10 chip model.

Cédric Le Goater (2):
  ppc/pnv: Add an I2C controller model
  ppc/pnv: Connect I2C controller model to powernv9 chip

 hw/ppc/meson.build |   1 +
 hw/ppc/pnv.c   |  29 ++
 hw/ppc/pnv_i2c.c   | 673 +
 include/hw/ppc/pnv_chip.h  |   8 +
 include/hw/ppc/pnv_i2c.h   |  38 +++
 include/hw/ppc/pnv_xscom.h |   3 +
 6 files changed, 752 insertions(+)
 create mode 100644 hw/ppc/pnv_i2c.c
 create mode 100644 include/hw/ppc/pnv_i2c.h

-- 
2.31.1




[PATCH v3] misc/pca9552: Fix for pca9552 not getting reset

2023-10-10 Thread Glenn Miles
Testing of the pca9552 device on the powernv platform
showed that the reset method was not being called when
an instance of the device was realized.  This was causing
the INPUT0/INPUT1 POR values to be incorrect.

Fixed by overriding the parent pca955x_realize method with a
new pca9552_realize method which first calls
the parent pca955x_realize method followed by the
pca9552_reset function.

Signed-off-by: Glenn Miles 
---
 hw/misc/pca9552.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..4e183cc554 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -384,6 +384,12 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
 qdev_init_gpio_out(dev, s->gpio, k->pin_count);
 }
 
+static void pca9552_realize(DeviceState *dev, Error **errp)
+{
+pca955x_realize(dev, errp);
+pca9552_reset(dev);
+}
+
 static Property pca955x_properties[] = {
 DEFINE_PROP_STRING("description", PCA955xState, description),
 DEFINE_PROP_END_OF_LIST(),
@@ -417,6 +423,7 @@ static void pca9552_class_init(ObjectClass *oc, void *data)
 PCA955xClass *pc = PCA955X_CLASS(oc);
 
 dc->reset = pca9552_reset;
+dc->realize = pca9552_realize;
 dc->vmsd = _vmstate;
 pc->max_reg = PCA9552_LS3;
 pc->pin_count = 16;
-- 
2.31.1




  1   2   >