/* * Copyright (C) 2007 Michael Buesch * * 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 #include #include 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(§->ops)) { //TODO warning return; } list_for_each_entry(op, §->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); } }