nexmon – Rev 1

Subversion Repositories:
Rev:
/*
 *   Copyright (C) 2007  Michael Buesch <m@bues.ch>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License version 2
 *   as published by the Free Software Foundation.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 */

#include "initvals.h"
#include "list.h"
#include "util.h"
#include "args.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


struct initval {
        unsigned int offset;
        unsigned int size;
#define SIZE_16BIT      2
#define SIZE_32BIT      4
        unsigned int value;

        struct list_head list;
};

/* The IV in the binary file */
struct initval_raw {
        be16_t offset_size;
        union {
                be16_t d16;
                be32_t d32;
        } data __attribute__((__packed__));
} __attribute__((__packed__));

#define FW_IV_OFFSET_MASK       0x7FFF
#define FW_IV_32BIT             0x8000


struct ivals_context {
        /* Pointer to the parsed section structure */
        const struct initvals_sect *sect;
        /* List of struct initval */
        struct list_head ivals;
        /* Number of initvals. */
        unsigned int ivals_count;
};

#define _msg_helper(type, ctx, msg, x...)       do {            \
        fprintf(stderr, "InitVals " type);                      \
        fprintf(stderr, " (Section \"%s\")", ctx->sect->name);  \
        fprintf(stderr, ":\n  " msg "\n" ,##x);                 \
                                                } while (0)

#define iv_error(ctx, msg, x...)        do {            \
        _msg_helper("ERROR", ctx, msg ,##x);            \
        exit(1);                                        \
                                        } while (0)

#define iv_warn(ctx, msg, x...) \
        _msg_helper("warning", ctx, msg ,##x)

#define iv_info(ctx, msg, x...) \
        _msg_helper("info", ctx, msg ,##x)


static void assemble_write_mmio(struct ivals_context *ctx,
                                unsigned int offset,
                                unsigned int size,
                                unsigned int value)
{
        struct initval *iv;

        iv = xmalloc(sizeof(struct initval));
        iv->offset = offset;
        iv->size = size;
        iv->value = value;
        INIT_LIST_HEAD(&iv->list);
        list_add_tail(&iv->list, &ctx->ivals);
        ctx->ivals_count++;
}

static void assemble_write_phy(struct ivals_context *ctx,
                               unsigned int offset,
                               unsigned int value)
{
        assemble_write_mmio(ctx, 0x3FC, SIZE_16BIT, offset);
        assemble_write_mmio(ctx, 0x3FE, SIZE_16BIT, value);
}

static void assemble_write_radio(struct ivals_context *ctx,
                                 unsigned int offset,
                                 unsigned int value)
{
        assemble_write_mmio(ctx, 0x3F6, SIZE_16BIT, offset);
        assemble_write_mmio(ctx, 0x3FA, SIZE_16BIT, value);
}

static void shm_control_word(struct ivals_context *ctx,
                             unsigned int routing,
                             unsigned int offset)
{
        unsigned int control;

        control = (routing & 0xFFFF);
        control <<= 16;
        control |= (offset & 0xFFFF);
        assemble_write_mmio(ctx, 0x160, SIZE_32BIT, control);
}

static void shm_write32(struct ivals_context *ctx,
                        unsigned int routing,
                        unsigned int offset,
                        unsigned int value)
{
        if ((routing & 0xFF) == 0x01) {
                /* Is SHM Shared-memory */
//TODO          assert((offset & 0x0001) == 0);
                if (offset & 0x0003) {
                        /* Unaligned access */
                        shm_control_word(ctx, routing, offset >> 2);
                        assemble_write_mmio(ctx, 0x166, SIZE_16BIT,
                                            (value >> 16) & 0xFFFF);
                        shm_control_word(ctx, routing, (offset >> 2) + 1);
                        assemble_write_mmio(ctx, 0x164, SIZE_16BIT,
                                            (value & 0xFFFF));
                        return;
                }
                offset >>= 2;
        }
        shm_control_word(ctx, routing, offset);
        assemble_write_mmio(ctx, 0x164, SIZE_32BIT, value);
}

static void shm_write16(struct ivals_context *ctx,
                        unsigned int routing,
                        unsigned int offset,
                        unsigned int value)
{
        if ((routing & 0xFF) == 0x01) {
                /* Is SHM Shared-memory */
//TODO          assert((offset & 0x0001) == 0);
                if (offset & 0x0003) {
                        /* Unaligned access */
                        shm_control_word(ctx, routing, offset >> 2);
                        assemble_write_mmio(ctx, 0x166, SIZE_16BIT,
                                            value);
                        return;
                }
                offset >>= 2;
        }
        shm_control_word(ctx, routing, offset);
        assemble_write_mmio(ctx, 0x164, SIZE_16BIT, value);
}

static void assemble_write_shm(struct ivals_context *ctx,
                               unsigned int routing,
                               unsigned int offset,
                               unsigned int value,
                               unsigned int size)
{
        switch (routing & 0xFF) {
        case 0: case 1: case 2: case 3: case 4:
                break;
        default:
                //TODO error
                break;
        }
        //TODO check offset

        //TODO check value
        switch (size) {
        case SIZE_16BIT:
                shm_write16(ctx, routing, offset, value);
                break;
        case SIZE_32BIT:
                shm_write32(ctx, routing, offset, value);
                break;
        default:
                fprintf(stderr, "Internal assembler BUG. SHMwrite invalid size\n");
                exit(1);
        }
}

/* Template RAM write */
static void assemble_write_tram(struct ivals_context *ctx,
                                unsigned int offset,
                                unsigned int value)
{
        assemble_write_mmio(ctx, 0x130, SIZE_32BIT, offset);
        assemble_write_mmio(ctx, 0x134, SIZE_32BIT, value);
}

static void assemble_ival_section(struct ivals_context *ctx,
                                  const struct initvals_sect *sect)
{
        struct initval_op *op;

        ctx->sect = sect;
        if (list_empty(&sect->ops)) {
                //TODO warning
                return;
        }
        list_for_each_entry(op, &sect->ops, list) {
                switch (op->type) {
                case IVAL_W_MMIO16:
                        assemble_write_mmio(ctx, op->args[1],
                                            SIZE_16BIT,
                                            op->args[0]);
                        break;
                case IVAL_W_MMIO32:
                        assemble_write_mmio(ctx, op->args[1],
                                            SIZE_32BIT,
                                            op->args[0]);
                        break;
                case IVAL_W_PHY:
                        assemble_write_phy(ctx, op->args[1],
                                           op->args[0]);
                        break;
                case IVAL_W_RADIO:
                        assemble_write_radio(ctx, op->args[1],
                                             op->args[0]);
                        break;
                case IVAL_W_SHM16:
                        assemble_write_shm(ctx, op->args[1],
                                           op->args[2],
                                           op->args[0],
                                           SIZE_16BIT);
                        break;
                case IVAL_W_SHM32:
                        assemble_write_shm(ctx, op->args[1],
                                           op->args[2],
                                           op->args[0],
                                           SIZE_32BIT);
                        break;
                case IVAL_W_TRAM:
                        assemble_write_tram(ctx, op->args[1],
                                            op->args[0]);
                        break;
                }
        }
}

static unsigned int initval_to_raw(struct ivals_context *ctx,
                                   struct initval_raw *raw,
                                   const struct initval *iv)
{
        unsigned int size;

        memset(raw, 0, sizeof(*raw));
        if (iv->offset & ~FW_IV_OFFSET_MASK) {
                iv_error(ctx, "Initval offset 0x%04X too big. "
                         "Offset must be <= 0x%04X",
                         iv->offset, FW_IV_OFFSET_MASK);
        }
        raw->offset_size = cpu_to_be16(iv->offset);

        switch (iv->size) {
        case SIZE_16BIT:
                raw->data.d16 = cpu_to_be16(iv->value);
                size = sizeof(be16_t) + sizeof(be16_t);
                break;
        case SIZE_32BIT:
                raw->data.d32 = cpu_to_be32(iv->value);
                raw->offset_size |= cpu_to_be16(FW_IV_32BIT);
                size = sizeof(be16_t) + sizeof(be32_t);
                break;
        default:
                iv_error(ctx, "Internal error. initval_to_raw invalid size.");
                break;
        }

        return size;
}

static void emit_ival_section(struct ivals_context *ctx)
{
        FILE *fd;
        char *fn;
        size_t fn_len;
        struct initval *iv;
        struct initval_raw raw;
        struct fw_header hdr;
        unsigned int size;
        unsigned int filesize = 0;

        memset(&hdr, 0, sizeof(hdr));
        hdr.type = FW_TYPE_IV;
        hdr.ver = FW_HDR_VER;
        hdr.size = cpu_to_be32(ctx->ivals_count);

        fn_len = strlen(ctx->sect->name) + strlen(cmdargs.initvals_fn_extension ? : "") + 1;
        fn = xmalloc(fn_len);
        snprintf(fn, fn_len, "%s%s", ctx->sect->name, cmdargs.initvals_fn_extension ? : "");
        fd = fopen(fn, "w+");
        if (!fd) {
                fprintf(stderr, "Could not open initval output file \"%s\"\n", fn);
                free(fn);
                exit(1);
        }

        if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) {
                fprintf(stderr, "Could not write initvals outfile\n");
                exit(1);
        }

        if (IS_VERBOSE_DEBUG)
                fprintf(stderr, "\nInitvals \"%s\":\n", ctx->sect->name);
        list_for_each_entry(iv, &ctx->ivals, list) {
                if (IS_VERBOSE_DEBUG) {
                        fprintf(stderr, "%04X %u %08X\n",
                                iv->offset,
                                iv->size,
                                iv->value);
                }
                size = initval_to_raw(ctx, &raw, iv);
                if (fwrite(&raw, size, 1, fd) != 1) {
                        fprintf(stderr, "Could not write initvals outfile\n");
                        exit(1);
                }
                filesize += size;
        }

        if (cmdargs.print_sizes) {
                printf("%s:  %d values (%u bytes)\n",
                       fn, ctx->ivals_count, filesize);
        }

        fclose(fd);
        free(fn);
}

void assemble_initvals(void)
{
        struct ivals_context ctx;
        struct initvals_sect *sect;

        list_for_each_entry(sect, &infile.ivals, list) {
                memset(&ctx, 0, sizeof(ctx));
                INIT_LIST_HEAD(&ctx.ivals);

                assemble_ival_section(&ctx, sect);
                emit_ival_section(&ctx);
        }
}