From: David Gibson <david@gibson.dropbear.id.au>
To: "Cédric Le Goater" <clg@kaod.org>
Cc: qemu-ppc@nongnu.org, qemu-devel@nongnu.org,
Benjamin Herrenschmidt <benh@kernel.crashing.org>
Subject: Re: [Qemu-devel] [PATCH v2 1/8] ppc/pnv: Add support for POWER8+ LPC Controller
Date: Tue, 11 Apr 2017 12:23:54 +1000 [thread overview]
Message-ID: <20170411022354.GV27571@umbus> (raw)
In-Reply-To: <1491832618-27536-2-git-send-email-clg@kaod.org>
[-- Attachment #1: Type: text/plain, Size: 10280 bytes --]
On Mon, Apr 10, 2017 at 03:56:51PM +0200, Cédric Le Goater wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
> It adds the Naples chip which supports proper LPC interrupts via the
> LPC controller rather than via an external CPLD.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> [clg: - updated for qemu-2.9
> - ported on latest PowerNV patchset
> - moved the IRQ handler in pnv_lpc.c
> - introduced pnv_lpc_isa_irq_create() to create the ISA IRQs ]
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>
> Changes since v1:
>
> - moved the IRQ handler in pnv_lpc.c
> - introduced pnv_lpc_isa_irq_create() to create the ISA IRQs
>
> hw/ppc/pnv.c | 45 +++-------------------
> hw/ppc/pnv_lpc.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++-
> include/hw/ppc/pnv_lpc.h | 8 ++++
> 3 files changed, 108 insertions(+), 42 deletions(-)
>
> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
> index 16f32c9bbd9d..27589b91d1cf 100644
> --- a/hw/ppc/pnv.c
> +++ b/hw/ppc/pnv.c
> @@ -346,36 +346,6 @@ static void ppc_powernv_reset(void)
> cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
> }
>
> -/* If we don't use the built-in LPC interrupt deserializer, we need
> - * to provide a set of qirqs for the ISA bus or things will go bad.
> - *
> - * Most machines using pre-Naples chips (without said deserializer)
> - * have a CPLD that will collect the SerIRQ and shoot them as a
> - * single level interrupt to the P8 chip. So let's setup a hook
> - * for doing just that.
> - */
> -static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
> -{
> - PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> - uint32_t old_state = pnv->cpld_irqstate;
> - PnvChip *chip = opaque;
> -
> - if (level) {
> - pnv->cpld_irqstate |= 1u << n;
> - } else {
> - pnv->cpld_irqstate &= ~(1u << n);
> - }
> - if (pnv->cpld_irqstate != old_state) {
> - pnv_psi_irq_set(&chip->psi, PSIHB_IRQ_EXTERNAL,
> - pnv->cpld_irqstate != 0);
> - }
> -}
> -
> -static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
> -{
> - /* XXX TODO */
> -}
> -
> static ISABus *pnv_isa_create(PnvChip *chip)
> {
> PnvLpcController *lpc = &chip->lpc;
> @@ -390,16 +360,7 @@ static ISABus *pnv_isa_create(PnvChip *chip)
> isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
> &error_fatal);
>
> - /* Not all variants have a working serial irq decoder. If not,
> - * handling of LPC interrupts becomes a platform issue (some
> - * platforms have a CPLD to do it).
> - */
> - if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
> - irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS);
> - } else {
> - irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip,
> - ISA_NUM_IRQS);
> - }
> + irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS);
>
> isa_bus_irqs(isa_bus, irqs);
> return isa_bus;
> @@ -699,6 +660,10 @@ static void pnv_chip_init(Object *obj)
> object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
> object_property_add_const_link(OBJECT(&chip->occ), "psi",
> OBJECT(&chip->psi), &error_abort);
> +
> + /* The LPC controller needs PSI to generate interrupts */
> + object_property_add_const_link(OBJECT(&chip->lpc), "psi",
> + OBJECT(&chip->psi), &error_abort);
> }
>
> static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
> diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
> index 78db52415b11..baee366d386a 100644
> --- a/hw/ppc/pnv_lpc.c
> +++ b/hw/ppc/pnv_lpc.c
> @@ -250,6 +250,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
> .endianness = DEVICE_BIG_ENDIAN,
> };
>
> +static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
> +{
> + bool lpc_to_opb_irq = false;
> +
> + /* Update LPC controller to OPB line */
> + if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
> + uint32_t irqs;
> +
> + irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
> + lpc_to_opb_irq = (irqs != 0);
> + }
> +
> + /* We don't honor the polarity register, it's pointless and unused
> + * anyway
> + */
> + if (lpc_to_opb_irq) {
> + lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
> + } else {
> + lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
> + }
> +
> + /* Update OPB internal latch */
> + lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
> +
> + /* Reflect the interrupt */
> + pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0);
> +}
> +
> static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
> {
> PnvLpcController *lpc = opaque;
> @@ -300,12 +328,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
> break;
> case LPC_HC_IRQSER_CTRL:
> lpc->lpc_hc_irqser_ctrl = val;
> + pnv_lpc_eval_irqs(lpc);
> break;
> case LPC_HC_IRQMASK:
> lpc->lpc_hc_irqmask = val;
> + pnv_lpc_eval_irqs(lpc);
> break;
> case LPC_HC_IRQSTAT:
> lpc->lpc_hc_irqstat &= ~val;
> + pnv_lpc_eval_irqs(lpc);
> break;
> case LPC_HC_ERROR_ADDRESS:
> break;
> @@ -363,14 +394,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
> switch (addr) {
> case OPB_MASTER_LS_IRQ_STAT:
> lpc->opb_irq_stat &= ~val;
> + pnv_lpc_eval_irqs(lpc);
> break;
> case OPB_MASTER_LS_IRQ_MASK:
> - /* XXX Filter out reserved bits */
> lpc->opb_irq_mask = val;
> + pnv_lpc_eval_irqs(lpc);
> break;
> case OPB_MASTER_LS_IRQ_POL:
> - /* XXX Filter out reserved bits */
> lpc->opb_irq_pol = val;
> + pnv_lpc_eval_irqs(lpc);
> break;
> case OPB_MASTER_LS_IRQ_INPUT:
> /* Read only */
> @@ -398,6 +430,8 @@ static const MemoryRegionOps opb_master_ops = {
> static void pnv_lpc_realize(DeviceState *dev, Error **errp)
> {
> PnvLpcController *lpc = PNV_LPC(dev);
> + Object *obj;
> + Error *error = NULL;
>
> /* Reg inits */
> lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
> @@ -441,6 +475,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
> pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
> &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
> PNV_XSCOM_LPC_SIZE);
> +
> + /* get PSI object from chip */
> + obj = object_property_get_link(OBJECT(dev), "psi", &error);
> + if (!obj) {
> + error_setg(errp, "%s: required link 'psi' not found: %s",
> + __func__, error_get_pretty(error));
> + return;
> + }
> + lpc->psi = PNV_PSI(obj);
> }
>
> static void pnv_lpc_class_init(ObjectClass *klass, void *data)
> @@ -470,3 +513,53 @@ static void pnv_lpc_register_types(void)
> }
>
> type_init(pnv_lpc_register_types)
> +
> +/* If we don't use the built-in LPC interrupt deserializer, we need
> + * to provide a set of qirqs for the ISA bus or things will go bad.
> + *
> + * Most machines using pre-Naples chips (without said deserializer)
> + * have a CPLD that will collect the SerIRQ and shoot them as a
> + * single level interrupt to the P8 chip. So let's setup a hook
> + * for doing just that.
> + */
> +static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
> +{
> + PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine());
> + uint32_t old_state = pnv->cpld_irqstate;
> + PnvLpcController *lpc = opaque;
You can use the existing PNV_LPC() and similar macros here, rather
than hard casting from a void *. That's a bit safer.
> +
> + if (level) {
> + pnv->cpld_irqstate |= 1u << n;
> + } else {
> + pnv->cpld_irqstate &= ~(1u << n);
> + }
> +
> + if (pnv->cpld_irqstate != old_state) {
> + pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0);
> + }
> +}
> +
> +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
> +{
> + PnvLpcController *lpc = opaque;
> +
> + /* The Naples HW latches the 1 levels, clearing is done by SW */
> + if (level) {
> + lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
> + pnv_lpc_eval_irqs(lpc);
> + }
> +}
> +
> +qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type,
> + int nirqs)
> +{
> + /* Not all variants have a working serial irq decoder. If not,
> + * handling of LPC interrupts becomes a platform issue (some
> + * platforms have a CPLD to do it).
> + */
> + if (chip_type == PNV_CHIP_POWER8NVL) {
> + return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs);
> + } else {
> + return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs);
> + }
> +}
> diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
> index 38e5506975aa..ccf969af9448 100644
> --- a/include/hw/ppc/pnv_lpc.h
> +++ b/include/hw/ppc/pnv_lpc.h
> @@ -23,6 +23,8 @@
> #define PNV_LPC(obj) \
> OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
>
> +typedef struct PnvPsi PnvPsi;
> +
> typedef struct PnvLpcController {
> DeviceState parent;
>
> @@ -62,6 +64,12 @@ typedef struct PnvLpcController {
>
> /* XSCOM registers */
> MemoryRegion xscom_regs;
> +
> + /* PSI to generate interrupts */
> + PnvPsi *psi;
> } PnvLpcController;
>
> +qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type,
> + int nirqs);
> +
> #endif /* _PPC_PNV_LPC_H */
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
next prev parent reply other threads:[~2017-04-11 2:44 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-04-10 13:56 [Qemu-devel] [PATCH v2 0/8] pnv: improvement of LPC support and IPMI support Cédric Le Goater
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 1/8] ppc/pnv: Add support for POWER8+ LPC Controller Cédric Le Goater
2017-04-11 2:23 ` David Gibson [this message]
2017-04-11 6:31 ` Cédric Le Goater
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 2/8] ppc/pnv: enable only one LPC bus Cédric Le Goater
2017-04-11 2:40 ` David Gibson
2017-04-11 7:06 ` Cédric Le Goater
2017-04-11 10:19 ` David Gibson
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 3/8] ppc/pnv: scan ISA bus to populate device tree Cédric Le Goater
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 4/8] ppc/pnv: populate device tree for RTC devices Cédric Le Goater
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 5/8] ppc/pnv: populate device tree for serial devices Cédric Le Goater
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 6/8] ppc/pnv: populate device tree for IPMI BT devices Cédric Le Goater
2017-04-11 2:43 ` David Gibson
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 7/8] ppc/pnv: add initial IPMI sensors for the BMC simulator Cédric Le Goater
2017-04-11 2:44 ` David Gibson
2017-04-10 13:56 ` [Qemu-devel] [PATCH v2 8/8] ppc/pnv: generate an OEM SEL event on shutdown Cédric Le Goater
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170411022354.GV27571@umbus \
--to=david@gibson.dropbear.id.au \
--cc=benh@kernel.crashing.org \
--cc=clg@kaod.org \
--cc=qemu-devel@nongnu.org \
--cc=qemu-ppc@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.