Commit 46fb7809 authored by Stephen Checkoway's avatar Stephen Checkoway Committed by Philippe Mathieu-Daudé
Browse files

hw/block/pflash_cfi02: Fix CFI in autoselect mode



After a flash device enters CFI mode from autoselect mode, the reset
command returns the device to autoselect mode. An additional reset
command is necessary to return to read array mode.

Signed-off-by: default avatarStephen Checkoway <stephen.checkoway@oberlin.edu>
Message-Id: <20190426162624.55977-7-stephen.checkoway@oberlin.edu>
Tested-by: default avatarPhilippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: default avatarThomas Huth <thuth@redhat.com>
Acked-by: default avatarAlistair Francis <alistair.francis@wdc.com>
Acked-by: default avatarPhilippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: default avatarPhilippe Mathieu-Daudé <philmd@redhat.com>
parent 8a508e70
Loading
Loading
Loading
Loading
+18 −4
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ do { \
/* Special write cycles for CFI queries. */
enum {
    WCYCLE_CFI              = 7,
    WCYCLE_AUTOSELECT_CFI   = 8,
};

struct PFlashCFI02 {
@@ -311,6 +312,12 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
    cmd = value;
    if (pfl->cmd != 0xA0) {
        if (cmd == 0xF0) {
            if (pfl->wcycle == WCYCLE_AUTOSELECT_CFI) {
                /* Return to autoselect mode. */
                pfl->wcycle = 3;
                pfl->cmd = 0x90;
                return;
            }
            goto reset_flash;
        }
    }
@@ -333,7 +340,6 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
        /* We're in read mode */
    check_unlock0:
        if (boff == 0x55 && cmd == 0x98) {
        enter_CFI_mode:
            /* Enter CFI query mode */
            pfl->wcycle = WCYCLE_CFI;
            pfl->cmd = 0x98;
@@ -410,9 +416,16 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
                /* Unlock bypass reset */
                goto reset_flash;
            }
            /* We can enter CFI query mode from autoselect mode */
            if (boff == 0x55 && cmd == 0x98)
                goto enter_CFI_mode;
            /*
             * We can enter CFI query mode from autoselect mode, but we must
             * return to autoselect mode after a reset.
             */
            if (boff == 0x55 && cmd == 0x98) {
                /* Enter autoselect CFI query mode */
                pfl->wcycle = WCYCLE_AUTOSELECT_CFI;
                pfl->cmd = 0x98;
                return;
            }
            /* No break here */
        default:
            DPRINTF("%s: invalid write for command %02x\n",
@@ -493,6 +506,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
        break;
    /* Special values for CFI queries */
    case WCYCLE_CFI:
    case WCYCLE_AUTOSELECT_CFI:
        DPRINTF("%s: invalid write in CFI query mode\n", __func__);
        goto reset_flash;
    default:
+39 −0
Original line number Diff line number Diff line
@@ -407,6 +407,42 @@ static void test_geometry(const void *opaque)
    qtest_quit(qtest);
}

/*
 * Test that
 * 1. enter autoselect mode;
 * 2. enter CFI mode; and then
 * 3. exit CFI mode
 * leaves the flash device in autoselect mode.
 */
static void test_cfi_in_autoselect(const void *opaque)
{
    const FlashConfig *config = opaque;
    QTestState *qtest;
    qtest = qtest_initf("-M musicpal,accel=qtest"
                        " -drive if=pflash,file=%s,format=raw,copy-on-read",
                        image_path);
    FlashConfig explicit_config = expand_config_defaults(config);
    explicit_config.qtest = qtest;
    const FlashConfig *c = &explicit_config;

    /* 1. Enter autoselect. */
    unlock(c);
    flash_cmd(c, UNLOCK0_ADDR, AUTOSELECT_CMD);
    g_assert_cmpint(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF));

    /* 2. Enter CFI. */
    flash_cmd(c, CFI_ADDR, CFI_CMD);
    g_assert_cmpint(flash_query(c, FLASH_ADDR(0x10)), ==, replicate(c, 'Q'));
    g_assert_cmpint(flash_query(c, FLASH_ADDR(0x11)), ==, replicate(c, 'R'));
    g_assert_cmpint(flash_query(c, FLASH_ADDR(0x12)), ==, replicate(c, 'Y'));

    /* 3. Exit CFI. */
    reset(c);
    g_assert_cmpint(flash_query(c, FLASH_ADDR(0)), ==, replicate(c, 0xBF));

    qtest_quit(qtest);
}

static void cleanup(void *opaque)
{
    unlink(image_path);
@@ -474,6 +510,9 @@ int main(int argc, char **argv)
        qtest_add_data_func(path, config, test_geometry);
        g_free(path);
    }

    qtest_add_data_func("pflash-cfi02/cfi-in-autoselect", &configuration[0],
                        test_cfi_in_autoselect);
    int result = g_test_run();
    cleanup(NULL);
    return result;