mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Cameron Ferguson <cameron.bare86@gmail.com>
To: Ahmad Fatoum <a.fatoum@pengutronix.de>, barebox@lists.infradead.org
Subject: Re: Small tweak to get ACPI watchdog working (iTCO)
Date: Wed, 29 Jan 2020 16:07:17 +0000	[thread overview]
Message-ID: <CAEKVsgYu15u7d6ptaU7aZGy6OXOhN7743aLdGOz_3fZOPK2JpQ@mail.gmail.com> (raw)
In-Reply-To: <a0c0afa2-cb44-3e40-b7e7-962fd80f2902@pengutronix.de>

> Please turn it to a proper device driver and send a patch when
> you got it working. :) Check e.g. drivers/watchdog/orion_wdt.c
> to see how a barebox watchdog driver looks like.


I'm not overly concerned with creating a proper watchdog driver...
right now I just need to start the watchdog timer running.


> I don't understand where this 0x10000 comes from. x86 I/O ports are 16-bits.


In order to write to ACPI addres 0x460, I can do this:

    *(unsigned *)(0x10460) = 0;

This works fine on x86.


> As you noticed writes aren't enough. You need the reads as well, as they
> might have side effects.


I've put all the reads and writes in now.


> This should be "WDAT". And then you are supposed to parse the ACPI table
> to arrive at the addresses you need to use instead of hardcoding them.
>
> The kernel code has a struct acpi_table_wdat that defines the layout


Barebox is missing the function "acpi_get_table". I briefly looked
into copying it from the Linux kernel but after a few minutes I just
decided to copy the hex values and hard-code them into my program.

I'm still tinkering about with this. Still no joy. Here's what I have now:

#define Cameron_read32(p) ioread32(p)
#define Cameron_write32(n,p)    (*(volatile unsigned int __force   *)(p) = (n))
#define Cameron_write32_rargs(n,p)    (Cameron_write32(p,n))

#define printks(...) (mdelay(500), printk( __VA_ARGS__ ))

struct list_head *g_instructions[MAX_WDAT_ACTIONS];

struct wdat_wdt g_wdat;

void Populate_Wdat_Table(void)
{
    printks("Cameron : Populating the WDAT table\n");

    static char unsigned const my_table[] = {

        /* acpi_table_header */
        0x57,0x44,0x41,0x54,0x04,0x01,0x00,0x00,0x01,
        0xCA,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

        /* + acpi_acpi_table_wdat */
        0x20,0x00,0x00,0x00,0xFF,0x00,0xFF,0xFF,
        0xFF,0x00,0x00,0x00,0x58,0x02,0x00,0x00,
        0xFF,0x03,0x00,0x00,0x02,0x00,0x00,0x00,
        0x81,0x00,0x00,0x00,0x08,0x00,0x00,0x00
    };

    struct acpi_table_wdat const *const tbl = (struct acpi_table_wdat
const *)&*my_table;

    g_wdat.period = tbl->timer_period;
    //g_wdat.wdd.min_hw_heartbeat_ms = g_wdat.period * tbl->min_count;
    //g_wdat.wdd.max_hw_heartbeat_ms = g_wdat.period * tbl->max_count;
    g_wdat.stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
    //g_wdat.wdd.info = &wdat_wdt_info;
    //g_wdat.wdd.ops = &wdat_wdt_ops;
    //g_wdat.pdev = pdev;
};

void Populate_Instruction_List(void)
{
    printks("Cameron : Populating the instruction table\n");

    static char unsigned const hex_instructions[] = {
         0x01,0x82,0x00,0x00,0x01,0x0A,0x00,0x03,0x60,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xFF,0x03,0x00,0x00
        ,0x06,0x83,0x00,0x00,0x01,0x0A,0x10,0x03,0x70,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x03,0x00,0x00,0xFF,0x03,0x00,0x00
        ,0x08,0x00,0x00,0x00,0x01,0x01,0x0B,0x03,0x68,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00
        ,0x09,0x82,0x00,0x00,0x01,0x01,0x0B,0x03,0x68,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00
        ,0x0A,0x00,0x00,0x00,0x01,0x01,0x0B,0x03,0x68,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00
        ,0x0B,0x82,0x00,0x00,0x01,0x01,0x0B,0x03,0x68,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00
        ,0x20,0x00,0x00,0x00,0x01,0x01,0x11,0x03,0x64,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00
        ,0x21,0x82,0x00,0x00,0x01,0x01,0x11,0x03,0x64,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00
    };

    struct acpi_wdat_entry const *const entries = (struct
acpi_wdat_entry const *)&*hex_instructions;

    for (unsigned i = 0; i < 8; ++i)
    {
        unsigned const action = entries[i].action;

        struct wdat_instruction *const instr = kzalloc(sizeof *instr,
GFP_KERNEL);

        INIT_LIST_HEAD(&instr->node);

        instr->entry = entries[i];
        instr->reg = (void __iomem *)(0x10000ull +
entries[i].register_region.address);

        printks("Cameron : Adding instruction, Action=0x%02X,
Address=0x%X, Pointer=0x%X\n", (unsigned)action,
(unsigned)entries[i].register_region.address, (unsigned)instr->reg);

        struct list_head *instructions = g_instructions[action];

        if ( !instructions )
        {
            instructions = kzalloc(sizeof *instructions, GFP_KERNEL);

            INIT_LIST_HEAD(instructions);

            g_instructions[action] = instructions;
        }

        list_add_tail(&instr->node, instructions);
    }
}

static int wdat_wdt_read(const struct wdat_instruction *instr, u32 *value)
{
    struct acpi_generic_address const *const gas =
&instr->entry.register_region;

    switch ( gas->access_width )
    {
    case 3:
        *value = Cameron_read32(instr->reg);
        printks("Cameron : Read 0x%x from pointer=0x%X\n", *value,
(unsigned)instr->reg);
        return 0;
    default:
        printks("Cameron : BAD READ\n");
        return -EINVAL;
    }
}

static int wdat_wdt_write(const struct wdat_instruction *instr, u32 value)
{
    struct acpi_generic_address const *const gas =
&instr->entry.register_region;

    switch ( gas->access_width )
    {
    case 3:
        Cameron_write32(value, instr->reg);
        printks("Cameron : Wrote 0x%x to pointer=0x%X\n", value,
(unsigned)instr->reg);
        return 0;
    default:
        printks("Cameron : BAD WRITE\n");
        return -EINVAL;
    }
}

static int wdat_wdt_run_action(unsigned int action, u32 param, u32 *retval)
{
    struct wdat_instruction *instr;

    if ( action >= ARRAY_SIZE(g_instructions) || !g_instructions[action] )
    {
        printks("Cameron : Cannot run invalid action %#x\n", action);
        return -EINVAL;
    }
    else
    {
        printks("Cameron : Running action %#x\n", action);
    }

    /* Run each instruction sequentially */
    list_for_each_entry(instr, g_instructions[action], node) {
        const struct acpi_wdat_entry *entry = &instr->entry;
        const struct acpi_generic_address *gas;
        u32 flags, value, mask, x, y;
        bool preserve;
        int ret;

        gas = &entry->register_region;

        preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER;
        flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER;
        value = entry->value;
        mask = entry->mask;

        switch (flags) {
        case ACPI_WDAT_READ_VALUE:
            ret = wdat_wdt_read(instr, &x);
            if (ret)
                return ret;
            x >>= gas->bit_offset;
            x &= mask;
            if (retval)
                *retval = x == value;
            break;

        case ACPI_WDAT_READ_COUNTDOWN:
            ret = wdat_wdt_read(instr, &x);
            if (ret)
                return ret;
            x >>= gas->bit_offset;
            x &= mask;
            if (retval)
                *retval = x;
            break;

        case ACPI_WDAT_WRITE_VALUE:
            x = value & mask;
            x <<= gas->bit_offset;
            if (preserve) {
                printks("Cameron : Preserving value\n");
                ret = wdat_wdt_read(instr, &y);
                if (ret)
                    return ret;
                y = y & ~(mask << gas->bit_offset);
                x |= y;
            }
            printks("Cameron : Not preserving value\n");
            ret = wdat_wdt_write(instr, x);
            if (ret)
                return ret;
            break;

        case ACPI_WDAT_WRITE_COUNTDOWN:
            x = param;
            x &= mask;
            x <<= gas->bit_offset;
            if (preserve) {
                printks("Cameron : Preserving value\n");
                ret = wdat_wdt_read(instr, &y);
                if (ret)
                    return ret;
                y = y & ~(mask << gas->bit_offset);
                x |= y;
            }
            printks("Cameron : Not preserving value\n");
            ret = wdat_wdt_write(instr, x);
            if (ret)
                return ret;
            break;

        default:
            printks("Cameron : Unknown instruction: %u\n", flags);
            return -EINVAL;
        }
    }

    return 0;
}

static int wdat_wdt_enable_reboot(void)
{
    int ret;

    /*
     * WDAT specification says that the watchdog is required to reboot
     * the system when it fires. However, it also states that it is
     * recommeded to make it configurable through hardware register. We
     * enable reboot now if it is configurable, just in case.
     */
    ret = wdat_wdt_run_action(ACPI_WDAT_SET_REBOOT, 0, NULL);
    if (ret && ret != -EOPNOTSUPP) {
        printks("Cameron : Failed to enable reboot when watchdog triggers\n");
        return ret;
    }

    return 0;
}

static void wdat_wdt_boot_status(void)
{
    u32 boot_status = 0;
    int ret;

    ret = wdat_wdt_run_action(ACPI_WDAT_GET_STATUS, 0, &boot_status);
    if (ret && ret != -EOPNOTSUPP) {
        printks("Cameron : Failed to read boot status\n");
        return;
    }

    //if (boot_status)
    //    wdat->wdd.bootstatus = WDIOF_CARDRESET;

    /* Clear the boot status in case BIOS did not do it */
    ret = wdat_wdt_run_action(ACPI_WDAT_SET_STATUS, 0, NULL);
    if (ret && ret != -EOPNOTSUPP)
        printks("Cameron : Failed to clear boot status\n");
}

static void wdat_wdt_set_running(void)
{
    u32 running = 0;
    int ret;

    ret = wdat_wdt_run_action(ACPI_WDAT_GET_RUNNING_STATE, 0,
                  &running);
    if (ret && ret != -EOPNOTSUPP)
        printks("Cameron : Failed to read running state\n");

    //if (running)
    //    set_bit(WDOG_HW_RUNNING, &wdat->wdd.status);
}

static int wdat_wdt_start(void)
{
    return wdat_wdt_run_action(
                   ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
}

static int wdat_wdt_stop(void)
{
    return wdat_wdt_run_action(
                   ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
}

static int wdat_wdt_ping(void)
{
    return wdat_wdt_run_action( ACPI_WDAT_RESET, 0, NULL);
}

static int wdat_wdt_set_timeout(unsigned int timeout)
{
    unsigned int periods;
    int ret;

    periods = timeout * 1000 / g_wdat.period;
    ret = wdat_wdt_run_action(ACPI_WDAT_SET_COUNTDOWN, periods, NULL);
    //if (!ret)
    //    wdd->timeout = timeout;
    return ret;
}

static unsigned int wdat_wdt_get_timeleft(void)
{
    u32 periods = 0;

    wdat_wdt_run_action(ACPI_WDAT_GET_CURRENT_COUNTDOWN, 0, &periods);
    return periods * g_wdat.period / 1000;
}

int rest_of_probe_Fancy(struct device_d *const pdev);
int rest_of_probe_Raw(struct device_d *const pdev);

static int wdat_wdt_probe_GREATLY_SIMPLIFIED(struct device_d *const pdev)
{
    /* ====== Welcome Banner Start ====== */
    static unsigned counter = 0;
    static char welcome_str[] = "Cameron : Enter function Probe (N=X)\n";
    welcome_str[33] = '0' + counter++;
    printks(welcome_str);
    /* ====== Welcome Banner End ======== */

    Populate_Wdat_Table();

    Populate_Instruction_List();

    request_ioport_region(dev_name(pdev), 0x460, 0x470);

    //return rest_of_probe_Fancy(pdev);
    return rest_of_probe_Raw(pdev);
}

int rest_of_probe_Fancy(struct device_d *const pdev)
{
#if 0
    printks("Cameron : Start Quarantine =========================== \n");
    for (unsigned i = 0; i != 10; ++i)
    {
        long long unsigned const base = 0x10460;

        printks("Cameron : Cameron_write32(0x4, base + 0);\n");
/* W */    Cameron_write32(0x4, base + 0u);  // This is a reset/kick

        printks("Cameron : Cameron_write32(0x0, base + 4);\n");
/* W */    Cameron_write32(0x0, base + 4u);
    }
    struct wdat_instruction *const instr =
list_first_entry(g_instructions[ACPI_WDAT_RESET], struct
wdat_instruction, node);
    wdat_wdt_write(instr,0x4);
    printks("Cameron : End Quarantine =========================== \n");
#endif

    //printks("Cameron : Action : Boot Status\n");
    //wdat_wdt_boot_status();

    //printks("Cameron : Action : Stop\n");
    //wdat_wdt_stop();

    //uint32_t const retval = Cameron_read32(0x10470);
    //printks("Cameron : Reading from 0x10470, value=%u\n", retval);

    printks("Cameron : Action : Set Timeout\n");
    wdat_wdt_set_timeout(10u);

// /* W */    Cameron_write32_rargs(0x10460 + 10u, 0x25000);
// printks("Cameron : Cameron_write32_rargs(base + 10, 0x25000);\n");

    printks("Cameron : Action : Ping\n");
    wdat_wdt_ping();

    printks("Cameron : Action : Set Running\n");
    wdat_wdt_set_running();

    printks("Cameron : Action : Start\n");
    wdat_wdt_start();

    return 0;
}

int rest_of_probe_Raw(struct device_d *const pdev)
{
    uint64_t const base = 0x10460;  // For x86, just add 0x10000 to 0x460

    uint32_t retval;

#if 1
/* These are extra resets that I added at the beginning */

    for (unsigned i = 0; i != 10; ++i)
    {
/* W */    Cameron_write32_rargs(base + 0u, 0x4);  // This is a reset/kick
        printks("Cameron : Cameron_write32_rargs(base +  0, 0x4);\n");

/* W */    Cameron_write32_rargs(base + 4u, 0x0);
        printks("Cameron : Cameron_write32_rargs(base +  4, 0x0);\n");
    }
#endif

/* Next 1 instruction is 0x20 (32) - Get Status */

/* R */    retval = Cameron_read32(base +  4u);  // This should be 0x0
    printks("Cameron : Cameron_read32(base +  4u) == 0x%08llx (should
be 0x0)\n", (long long unsigned)retval);

/* Next 2 instructions is 0x21 (33) - Set Status */

/* R */    retval = Cameron_read32(base +  4u);  // This should be 0x0
    printks("Cameron : Cameron_read32(base +  4u) == 0x%08llx (should
be 0x0)\n", (long long unsigned)retval);

/* W */    Cameron_write32_rargs(base + 4u, 0x20000);
    printks("Cameron : Cameron_write32_rargs(base +  4, 0x20000);\n");

/* Next 1 instruction is 0x08 (8) - Get Running State */

/* R */    retval = Cameron_read32(base +  8u);  // This should be 0x1800
    printks("Cameron : Cameron_read32(base +  8u) == 0x%08llx (should
be 0x1800)\n", (long long unsigned)retval);

/* Next 2 instructions is 0x09 (9) - Set Running State */

/* R */    retval = Cameron_read32(base +  8u);  // This should be 0x1800
    printks("Cameron : Cameron_read32(base +  8u) == 0x%08llx (should
be 0x1800)\n", (long long unsigned)retval);

/* W */    Cameron_write32_rargs(base + 8u, 0x1000);
    printks("Cameron : Cameron_write32_rargs(base +  8, 0x1000);\n");

/* Next 1 instruction is 0x01 (1) - Reset */

/* R */    retval = Cameron_read32(base +  0u);  // This should be 0x4
    printks("Cameron : Cameron_read32(base +  0u) == 0x%08llx (should
be 0x4)\n", (long long unsigned)retval);

/* W */    Cameron_write32_rargs(base + 0u,0x4);
    printks("Cameron : Cameron_write32_rargs(base +  0, 0x4);\n");

    return rest_of_probe_Fancy(pdev);

/* Next 2 instructions is 0x06 (6) - Set Countdown */

/* R */    retval = Cameron_read32(base + 10u);  // This should be 0x40000
    printks("Cameron : Cameron_read32(base + 10u) == 0x%08llx (should
be 0x40000)\n", (long long unsigned)retval);

/* W */    Cameron_write32_rargs(base + 10u, 0x640000);
    printks("Cameron : Cameron_write32_rargs(base + 10, 0x640000);\n");

/* Next 2 instructions is 0x01 (1) - Reset */

/* R */    retval = Cameron_read32(base +  0u);  // This should be 0x2
    printks("Cameron : Cameron_read32(base +  0u) == 0x%08llx (should
be 0x2)\n", (long long unsigned)retval);

/* W */    Cameron_write32_rargs(base + 0u,0x4);  // This is a reset/kick
    printks("Cameron : Cameron_write32_rargs(base +  0, 0x4);\n");

/* Next 2 instructions is 0x01 (1) - Reset */

    for (unsigned monkey = 0; monkey != 4; ++monkey)
    {
/* R */        retval = Cameron_read32(base +  0u);  // This should be 0x34
        printks("Cameron : Cameron_read32(base +  0u) == 0x%08llx
(should be 0x34)\n", (long long unsigned)retval);

/* W */        Cameron_write32_rargs(base + 0u,0x4);  // This is a reset/kick
        printks("Cameron : Cameron_write32_rargs(base +  0u, 0x4);\n");
    }

/* Next 2 instructions is the repeated kick (ad infinitum) */

    for (unsigned monkey = 0; monkey != 4; ++monkey)
    {
        mdelay(3000llu);

/* R */        retval = Cameron_read32(base +  0u);  // This should be 0x32
        printks("Cameron : Cameron_read32(base +  0u) == 0x%08llx
(should be 0x32)\n", (long long unsigned)retval);

/* W */        Cameron_write32_rargs(base + 0u,0x4);  // This is a kick
        printks("Cameron : Cameron_write32_rargs(base +  0u,
0x4);\n");
    }

    return rest_of_probe_Fancy(pdev);
}

static void Cameron_Remove(struct device_d *const pdev)
{
    printks("Cameron : Custom ACPI watchdog driver removed\n");
}

static struct acpi_driver cameron_itco_watchdog_acpi_driver = {
    .signature = "WDAT",
    .driver = {
        .name = "cameron_itco_watchdog_acpi_driver",
        .probe = wdat_wdt_probe_GREATLY_SIMPLIFIED,
        .remove = Cameron_Remove,
    }
};

device_acpi_driver(cameron_itco_watchdog_acpi_driver);

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

  reply	other threads:[~2020-01-29 16:07 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CAEKVsgZHZR2b28wcMcpac4Qa3KebrXMRf14STZ8GCum0NmnLgg@mail.gmail.com>
2020-01-28 15:19 ` Cameron Ferguson
2020-01-29  8:36   ` Cameron Ferguson
2020-01-29 11:20 ` Ahmad Fatoum
2020-01-29 16:07   ` Cameron Ferguson [this message]
2020-01-29 16:57     ` Cameron Ferguson
2020-01-29 16:57     ` Ahmad Fatoum
2020-01-29 17:55       ` Cameron Ferguson
2020-01-29 18:00         ` Ahmad Fatoum

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=CAEKVsgYu15u7d6ptaU7aZGy6OXOhN7743aLdGOz_3fZOPK2JpQ@mail.gmail.com \
    --to=cameron.bare86@gmail.com \
    --cc=a.fatoum@pengutronix.de \
    --cc=barebox@lists.infradead.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox