Hi Cédric,

Adding a bit more context here: I'll fold these details into the commit log in 
the next revision.

LTPI (LVDS Tunneling Protocol & Interface) is defined in the OCP DC-SCM 2.0 
specification (see Figure 2):
https://www.opencompute.org/documents/ocp-dc-scm-2-0-ltpi-ver-1-0-pdf

LTPI is a protocol and physical interface for tunneling various low-speed 
signals between the HPM and SCM. In Figure 2, the AST27x0 (left) integrates two 
LTPI controllers, allowing it to connect to up to two AST1700 boards. On the 
right, the AST1700 side consolidates the HPM FPGA and other interfaces 
(GPIO/UART/I2C/I3C, etc.) onto a single board.

Because the AST1700 exposes additional I/O interfaces (GPIO/I2C/I3C, etc.), we 
refer to it as an "I/O expander". Once connected over LTPI, the AST27x0 can 
control more downstream devices. Note that the AST1700 contains ROM code only 
and does not support firmware updates, so its behavior is fixed.

When using the AST1700, include the following DTS snippets to enable the 
additional interfaces:
https://github.com/AspeedTech-BMC/linux/blob/aspeed-master-v6.6/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi
https://github.com/AspeedTech-BMC/linux/blob/aspeed-master-v6.6/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi

If any information is missing, please let me know and I will do my best to 
provide further details.

Best Regards,
Kane
> -----Original Message-----
> From: Cédric Le Goater <[email protected]>
> Sent: Wednesday, September 17, 2025 4:22 PM
> To: Kane Chen <[email protected]>; Peter Maydell
> <[email protected]>; Steven Lee <[email protected]>; Troy
> Lee <[email protected]>; Jamin Lin <[email protected]>; Andrew
> Jeffery <[email protected]>; Joel Stanley <[email protected]>;
> open list:ASPEED BMCs <[email protected]>; open list:All patches CC
> here <[email protected]>
> Cc: Troy Lee <[email protected]>
> Subject: Re: [SPAM] [PATCH v1 1/6] hw/arm/aspeed: Add 'ioexps-num'
> property for AST27x0
> 
> Hello Kane,
> 
> On 9/17/25 03:31, Kane Chen wrote:
> > From: Kane-Chen-AS <[email protected]>
> >
> > AST27x0 platforms can attach up to two AST1700 IO expander boards.
> 
> what are AST1700 IO expanders ? How are they attached to the main board ?
> on which bus ?
> 
> > Introduce the 'ioexps-num' property to let users specify how many IO
> > expanders to instantiate for a given machine.
> > > This enables modeling board variants that ship with 0-2 expanders.
> >
> > Note: AST2500 and AST2600 do not support IO expanders; this property
> > is only available on AST27x0 machines.
> >
> > Command usage:
> > ```
> > ./qemu-system-aarch64 -M ast2700a1-evb,ioexps-num=2 \
> >    -drive image-bmc,format=raw,if=mtd \
> >    ...
> > ```
> 
> I would prefer these changes modifying the uapi to come at the end when the
> modeling is done.
> 
> 
> Thanks,
> 
> C.
> 
> 
> 
> 
> > Signed-off-by: Kane-Chen-AS <[email protected]>
> > ---
> >   include/hw/arm/aspeed_soc.h |  2 ++
> >   hw/arm/aspeed.c             | 49
> +++++++++++++++++++++++++++++++++++++
> >   2 files changed, 51 insertions(+)
> >
> > diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
> > index 217ef0eafd..77263cc6ec 100644
> > --- a/include/hw/arm/aspeed_soc.h
> > +++ b/include/hw/arm/aspeed_soc.h
> > @@ -49,6 +49,7 @@
> >   #define ASPEED_MACS_NUM  4
> >   #define ASPEED_UARTS_NUM 13
> >   #define ASPEED_JTAG_NUM  2
> > +#define ASPEED_IOEXP_NUM 2
> >
> >   struct AspeedSoCState {
> >       DeviceState parent;
> > @@ -103,6 +104,7 @@ struct AspeedSoCState {
> >       UnimplementedDeviceState ltpi;
> >       UnimplementedDeviceState jtag[ASPEED_JTAG_NUM];
> >       AspeedAPB2OPBState fsi[2];
> > +    uint8_t ioexp_num;
> >   };
> >
> >   #define TYPE_ASPEED_SOC "aspeed-soc"
> > diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index
> > c31bbe7701..593cb87bfe 100644
> > --- a/hw/arm/aspeed.c
> > +++ b/hw/arm/aspeed.c
> > @@ -32,6 +32,7 @@
> >   #include "qemu/units.h"
> >   #include "hw/qdev-clock.h"
> >   #include "system/system.h"
> > +#include "qapi/visitor.h"
> >
> >   static struct arm_boot_info aspeed_board_binfo = {
> >       .board_id = -1, /* device-tree-only board */ @@ -49,6 +50,7 @@
> > struct AspeedMachineState {
> >       char *fmc_model;
> >       char *spi_model;
> >       uint32_t hw_strap1;
> > +    uint32_t ioexp_num;
> >   };
> >
> >   /* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */
> > @@ -444,6 +446,9 @@ static void aspeed_machine_init(MachineState
> *machine)
> >                                OBJECT(get_system_memory()),
> &error_abort);
> >       object_property_set_link(OBJECT(bmc->soc), "dram",
> >                                OBJECT(machine->ram),
> &error_abort);
> > +
> > +    bmc->soc->ioexp_num = bmc->ioexp_num;
> > +
> >       if (amc->sdhci_wp_inverted) {
> >           for (i = 0; i < bmc->soc->sdhci.num_slots; i++) {
> >
> > object_property_set_bool(OBJECT(&bmc->soc->sdhci.slots[i]),
> > @@ -1486,6 +1491,49 @@ static void
> aspeed_machine_ast2600_class_emmc_init(ObjectClass *oc)
> >                                             "Set or unset boot
> from EMMC");
> >   }
> >
> > +#ifdef TARGET_AARCH64
> > +static void aspeed_get_ioexps_num(Object *obj,
> > +                                  Visitor *v,
> > +                                  const char *name,
> > +                                  void *opaque,
> > +                                  Error **errp) {
> > +    AspeedMachineState *bmc = ASPEED_MACHINE(obj);
> > +
> > +    visit_type_uint32(v, name, &bmc->ioexp_num, errp); }
> > +
> > +static void aspeed_set_ioexps_num(Object *obj,
> > +                                  Visitor *v,
> > +                                  const char *name,
> > +                                  void *opaque,
> > +                                  Error **errp) {
> > +    uint32_t val;
> > +    AspeedMachineState *bmc = ASPEED_MACHINE(obj);
> > +
> > +    if (!visit_type_uint32(v, name, &val, errp)) {
> > +        return;
> > +    }
> > +
> > +    if (val > ASPEED_IOEXP_NUM) {
> > +        error_setg(errp, "IOEXP number is exceeded: %d", val);
> > +        return;
> > +    }
> > +
> > +    bmc->ioexp_num = val;
> > +}
> > +
> > +
> > +static void aspeed_machine_ast1700_class_init(ObjectClass *oc) {
> > +    object_class_property_add(oc, "ioexps-num", "uint32",
> > +                              aspeed_get_ioexps_num,
> > +                              aspeed_set_ioexps_num,
> > +                              NULL, NULL); } #endif
> > +
> >   static void aspeed_machine_class_init(ObjectClass *oc, const void *data)
> >   {
> >       MachineClass *mc = MACHINE_CLASS(oc); @@ -2032,6 +2080,7
> @@
> > static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc,
> >       mc->auto_create_sdcard = true;
> >       mc->default_ram_size = 1 * GiB;
> >       aspeed_machine_class_init_cpus_defaults(mc);
> > +    aspeed_machine_ast1700_class_init(oc);
> >   }
> >   #endif
> >

Reply via email to