From: David Gibson <david@gibson.dropbear.id.au>
To: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Cc: qemu-devel@nongnu.org, qemu-ppc@nongnu.org, benh@kernel.crashing.org
Subject: Re: [Qemu-devel] [PATCH 5/8] ppc/mac: More rework of the DBDMA emulation
Date: Tue, 19 Sep 2017 06:37:02 +1000 [thread overview]
Message-ID: <20170918203702.GH15823@umbus> (raw)
In-Reply-To: <1505668548-16616-6-git-send-email-mark.cave-ayland@ilande.co.uk>
[-- Attachment #1: Type: text/plain, Size: 13348 bytes --]
On Sun, Sep 17, 2017 at 06:15:45PM +0100, Mark Cave-Ayland wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
> This completely reworks the handling of the control register
> according to my understanding of the HW and the spec.
>
> It should (hopefully ... still testing) fix a number of issues
> most notably cases of MacOS hanging.
>
> Also update dbdma_unassigned_rw() and dbdma_unassigned_flush() to
> have the expected behaviour now that flush is handled slightly
> differently.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Applied to ppc-for-2.11.
> ---
> hw/misc/macio/mac_dbdma.c | 191 +++++++++++++++++++++++++++++++++------------
> 1 file changed, 139 insertions(+), 52 deletions(-)
>
> diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c
> index 15452b9..3fe5073 100644
> --- a/hw/misc/macio/mac_dbdma.c
> +++ b/hw/misc/macio/mac_dbdma.c
> @@ -96,9 +96,8 @@ static void dbdma_cmdptr_load(DBDMA_channel *ch)
>
> static void dbdma_cmdptr_save(DBDMA_channel *ch)
> {
> - DBDMA_DPRINTFCH(ch, "dbdma_cmdptr_save 0x%08x\n",
> - ch->regs[DBDMA_CMDPTR_LO]);
> - DBDMA_DPRINTFCH(ch, "xfer_status 0x%08x res_count 0x%04x\n",
> + DBDMA_DPRINTFCH(ch, "-> update 0x%08x stat=0x%08x, res=0x%04x\n",
> + ch->regs[DBDMA_CMDPTR_LO],
> le16_to_cpu(ch->current.xfer_status),
> le16_to_cpu(ch->current.res_count));
> dma_memory_write(&address_space_memory, ch->regs[DBDMA_CMDPTR_LO],
> @@ -166,15 +165,14 @@ static int conditional_wait(DBDMA_channel *ch)
> uint16_t sel_mask, sel_value;
> uint32_t status;
> int cond;
> -
> - DBDMA_DPRINTFCH(ch, "conditional_wait\n");
> + int res = 0;
>
> wait = le16_to_cpu(current->command) & WAIT_MASK;
> -
> switch(wait) {
> case WAIT_NEVER: /* don't wait */
> return 0;
> case WAIT_ALWAYS: /* always wait */
> + DBDMA_DPRINTFCH(ch, " [WAIT_ALWAYS]\n");
> return 1;
> }
>
> @@ -187,15 +185,19 @@ static int conditional_wait(DBDMA_channel *ch)
>
> switch(wait) {
> case WAIT_IFSET: /* wait if condition bit is 1 */
> - if (cond)
> - return 1;
> - return 0;
> + if (cond) {
> + res = 1;
> + }
> + DBDMA_DPRINTFCH(ch, " [WAIT_IFSET=%d]\n", res);
> + break;
> case WAIT_IFCLR: /* wait if condition bit is 0 */
> - if (!cond)
> - return 1;
> - return 0;
> + if (!cond) {
> + res = 1;
> + }
> + DBDMA_DPRINTFCH(ch, " [WAIT_IFCLR=%d]\n", res);
> + break;
> }
> - return 0;
> + return res;
> }
>
> static void next(DBDMA_channel *ch)
> @@ -226,8 +228,6 @@ static void conditional_branch(DBDMA_channel *ch)
> uint32_t status;
> int cond;
>
> - DBDMA_DPRINTFCH(ch, "conditional_branch\n");
> -
> /* check if we must branch */
>
> br = le16_to_cpu(current->command) & BR_MASK;
> @@ -237,6 +237,7 @@ static void conditional_branch(DBDMA_channel *ch)
> next(ch);
> return;
> case BR_ALWAYS: /* always branch */
> + DBDMA_DPRINTFCH(ch, " [BR_ALWAYS]\n");
> branch(ch);
> return;
> }
> @@ -250,16 +251,22 @@ static void conditional_branch(DBDMA_channel *ch)
>
> switch(br) {
> case BR_IFSET: /* branch if condition bit is 1 */
> - if (cond)
> + if (cond) {
> + DBDMA_DPRINTFCH(ch, " [BR_IFSET = 1]\n");
> branch(ch);
> - else
> + } else {
> + DBDMA_DPRINTFCH(ch, " [BR_IFSET = 0]\n");
> next(ch);
> + }
> return;
> case BR_IFCLR: /* branch if condition bit is 0 */
> - if (!cond)
> + if (!cond) {
> + DBDMA_DPRINTFCH(ch, " [BR_IFCLR = 1]\n");
> branch(ch);
> - else
> + } else {
> + DBDMA_DPRINTFCH(ch, " [BR_IFCLR = 0]\n");
> next(ch);
> + }
> return;
> }
> }
> @@ -428,7 +435,7 @@ wait:
>
> static void stop(DBDMA_channel *ch)
> {
> - ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH);
> + ch->regs[DBDMA_STATUS] &= ~(ACTIVE);
>
> /* the stop command does not increment command pointer */
> }
> @@ -471,18 +478,22 @@ static void channel_run(DBDMA_channel *ch)
>
> switch (cmd) {
> case OUTPUT_MORE:
> + DBDMA_DPRINTFCH(ch, "* OUTPUT_MORE *\n");
> start_output(ch, key, phy_addr, req_count, 0);
> return;
>
> case OUTPUT_LAST:
> + DBDMA_DPRINTFCH(ch, "* OUTPUT_LAST *\n");
> start_output(ch, key, phy_addr, req_count, 1);
> return;
>
> case INPUT_MORE:
> + DBDMA_DPRINTFCH(ch, "* INPUT_MORE *\n");
> start_input(ch, key, phy_addr, req_count, 0);
> return;
>
> case INPUT_LAST:
> + DBDMA_DPRINTFCH(ch, "* INPUT_LAST *\n");
> start_input(ch, key, phy_addr, req_count, 1);
> return;
> }
> @@ -508,10 +519,12 @@ static void channel_run(DBDMA_channel *ch)
>
> switch (cmd) {
> case LOAD_WORD:
> + DBDMA_DPRINTFCH(ch, "* LOAD_WORD *\n");
> load_word(ch, key, phy_addr, req_count);
> return;
>
> case STORE_WORD:
> + DBDMA_DPRINTFCH(ch, "* STORE_WORD *\n");
> store_word(ch, key, phy_addr, req_count);
> return;
> }
> @@ -562,43 +575,117 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
> ch->io.opaque = opaque;
> }
>
> -static void
> -dbdma_control_write(DBDMA_channel *ch)
> +static void dbdma_control_write(DBDMA_channel *ch)
> {
> uint16_t mask, value;
> uint32_t status;
> + bool do_flush = false;
>
> mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff;
> value = ch->regs[DBDMA_CONTROL] & 0xffff;
>
> - value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
> -
> + /* This is the status register which we'll update
> + * appropriately and store back
> + */
> status = ch->regs[DBDMA_STATUS];
>
> - status = (value & mask) | (status & ~mask);
> + /* RUN and PAUSE are bits under SW control only
> + * FLUSH and WAKE are set by SW and cleared by HW
> + * DEAD, ACTIVE and BT are only under HW control
> + *
> + * We handle ACTIVE separately at the end of the
> + * logic to ensure all cases are covered.
> + */
>
> - if (status & WAKE)
> - status |= ACTIVE;
> - if (status & RUN) {
> - status |= ACTIVE;
> - status &= ~DEAD;
> + /* Setting RUN will tentatively activate the channel
> + */
> + if ((mask & RUN) && (value & RUN)) {
> + status |= RUN;
> + DBDMA_DPRINTFCH(ch, " Setting RUN !\n");
> + }
> +
> + /* Clearing RUN 1->0 will stop the channel */
> + if ((mask & RUN) && !(value & RUN)) {
> + /* This has the side effect of clearing the DEAD bit */
> + status &= ~(DEAD | RUN);
> + DBDMA_DPRINTFCH(ch, " Clearing RUN !\n");
> + }
> +
> + /* Setting WAKE wakes up an idle channel if it's running
> + *
> + * Note: The doc doesn't say so but assume that only works
> + * on a channel whose RUN bit is set.
> + *
> + * We set WAKE in status, it's not terribly useful as it will
> + * be cleared on the next command fetch but it seems to mimmic
> + * the HW behaviour and is useful for the way we handle
> + * ACTIVE further down.
> + */
> + if ((mask & WAKE) && (value & WAKE) && (status & RUN)) {
> + status |= WAKE;
> + DBDMA_DPRINTFCH(ch, " Setting WAKE !\n");
> + }
> +
> + /* PAUSE being set will deactivate (or prevent activation)
> + * of the channel. We just copy it over for now, ACTIVE will
> + * be re-evaluated later.
> + */
> + if (mask & PAUSE) {
> + status = (status & ~PAUSE) | (value & PAUSE);
> + DBDMA_DPRINTFCH(ch, " %sing PAUSE !\n",
> + (value & PAUSE) ? "sett" : "clear");
> + }
> +
> + /* FLUSH is its own thing */
> + if ((mask & FLUSH) && (value & FLUSH)) {
> + DBDMA_DPRINTFCH(ch, " Setting FLUSH !\n");
> + /* We set flush directly in the status register, we do *NOT*
> + * set it in "status" so that it gets naturally cleared when
> + * we update the status register further down. That way it
> + * will be set only during the HW flush operation so it is
> + * visible to any completions happening during that time.
> + */
> + ch->regs[DBDMA_STATUS] |= FLUSH;
> + do_flush = true;
> }
> - if (status & PAUSE)
> +
> + /* If either RUN or PAUSE is clear, so should ACTIVE be,
> + * otherwise, ACTIVE will be set if we modified RUN, PAUSE or
> + * set WAKE. That means that PAUSE was just cleared, RUN was
> + * just set or WAKE was just set.
> + */
> + if ((status & PAUSE) || !(status & RUN)) {
> status &= ~ACTIVE;
> - if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
> - /* RUN is cleared */
> - status &= ~(ACTIVE|DEAD);
> + DBDMA_DPRINTFCH(ch, " -> ACTIVE down !\n");
> +
> + /* We stopped processing, we want the underlying HW command
> + * to complete *before* we clear the ACTIVE bit. Otherwise
> + * we can get into a situation where the command status will
> + * have RUN or ACTIVE not set which is going to confuse the
> + * MacOS driver.
> + */
> + do_flush = true;
> + } else if (mask & (RUN | PAUSE)) {
> + status |= ACTIVE;
> + DBDMA_DPRINTFCH(ch, " -> ACTIVE up !\n");
> + } else if ((mask & WAKE) && (value & WAKE)) {
> + status |= ACTIVE;
> + DBDMA_DPRINTFCH(ch, " -> ACTIVE up !\n");
> }
>
> - if ((status & FLUSH) && ch->flush) {
> + DBDMA_DPRINTFCH(ch, " new status=0x%08x\n", status);
> +
> + /* If we need to flush the underlying HW, do it now, this happens
> + * both on FLUSH commands and when stopping the channel for safety.
> + */
> + if (do_flush && ch->flush) {
> ch->flush(&ch->io);
> - status &= ~FLUSH;
> }
>
> - DBDMA_DPRINTFCH(ch, " status 0x%08x\n", status);
> -
> + /* Finally update the status register image */
> ch->regs[DBDMA_STATUS] = status;
>
> + /* If active, make sure the BH gets to run */
> if (status & ACTIVE) {
> DBDMA_kick(dbdma_from_ch(ch));
> }
> @@ -666,13 +753,9 @@ static uint64_t dbdma_read(void *opaque, hwaddr addr,
>
> value = ch->regs[reg];
>
> - DBDMA_DPRINTFCH(ch, "readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
> - DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n",
> - (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
> -
> switch(reg) {
> case DBDMA_CONTROL:
> - value = 0;
> + value = ch->regs[DBDMA_STATUS];
> break;
> case DBDMA_STATUS:
> case DBDMA_CMDPTR_LO:
> @@ -698,6 +781,10 @@ static uint64_t dbdma_read(void *opaque, hwaddr addr,
> break;
> }
>
> + DBDMA_DPRINTFCH(ch, "readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
> + DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n",
> + (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
> +
> return value;
> }
>
> @@ -776,28 +863,28 @@ static void dbdma_reset(void *opaque)
> static void dbdma_unassigned_rw(DBDMA_io *io)
> {
> DBDMA_channel *ch = io->channel;
> - qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
> - __func__, ch->channel);
> - ch->io.processing = false;
> -}
> -
> -static void dbdma_unassigned_flush(DBDMA_io *io)
> -{
> - DBDMA_channel *ch = io->channel;
> dbdma_cmd *current = &ch->current;
> uint16_t cmd;
> qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
> __func__, ch->channel);
> + ch->io.processing = false;
>
> cmd = le16_to_cpu(current->command) & COMMAND_MASK;
> if (cmd == OUTPUT_MORE || cmd == OUTPUT_LAST ||
> cmd == INPUT_MORE || cmd == INPUT_LAST) {
> - current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS] | FLUSH);
> + current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
> current->res_count = cpu_to_le16(io->len);
> dbdma_cmdptr_save(ch);
> }
> }
>
> +static void dbdma_unassigned_flush(DBDMA_io *io)
> +{
> + DBDMA_channel *ch = io->channel;
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
> + __func__, ch->channel);
> +}
> +
> void* DBDMA_init (MemoryRegion **dbdma_mem)
> {
> DBDMAState *s;
--
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: 833 bytes --]
next prev parent reply other threads:[~2017-09-19 10:36 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-09-17 17:15 [Qemu-devel] [PATCH 0/8] ppc: more Mac-related fixups Mark Cave-Ayland
2017-09-17 17:15 ` [Qemu-devel] [PATCH 1/8] ppc: QOMify g3beige machine Mark Cave-Ayland
2017-09-18 3:42 ` Philippe Mathieu-Daudé
2017-09-18 20:25 ` David Gibson
2017-09-17 17:15 ` [Qemu-devel] [PATCH 2/8] ppc/mac: Advertise a high clock frequency for NewWorld Macs Mark Cave-Ayland
2017-09-18 20:25 ` David Gibson
2017-09-17 17:15 ` [Qemu-devel] [PATCH 3/8] ppc/ide/macio: Add missing registers Mark Cave-Ayland
2017-09-18 20:27 ` David Gibson
2017-09-17 17:15 ` [Qemu-devel] [PATCH 4/8] macio: convert pmac_ide_ops from old_mmio Mark Cave-Ayland
2017-09-18 20:36 ` David Gibson
2017-09-19 19:31 ` Mark Cave-Ayland
2017-09-17 17:15 ` [Qemu-devel] [PATCH 5/8] ppc/mac: More rework of the DBDMA emulation Mark Cave-Ayland
2017-09-18 20:37 ` David Gibson [this message]
2017-09-17 17:15 ` [Qemu-devel] [PATCH 6/8] ppc: Fix OpenPIC model Mark Cave-Ayland
2017-09-18 20:37 ` David Gibson
2017-09-17 17:15 ` [Qemu-devel] [PATCH 7/8] openpic: add missing timer fields to vmstate_openpic_timer Mark Cave-Ayland
2017-09-18 22:44 ` David Gibson
2017-09-19 19:50 ` Mark Cave-Ayland
2017-09-20 12:04 ` David Gibson
2017-09-17 17:15 ` [Qemu-devel] [PATCH 8/8] openpic: Fix problem when IRQ transitions from edge to level Mark Cave-Ayland
2017-09-18 20:39 ` David Gibson
2017-09-19 19:52 ` Mark Cave-Ayland
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=20170918203702.GH15823@umbus \
--to=david@gibson.dropbear.id.au \
--cc=benh@kernel.crashing.org \
--cc=mark.cave-ayland@ilande.co.uk \
--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.