assembler: Remove two fixmes.
[b43-tools.git] / assembler / initvals.c
1 /*
2  *   Copyright (C) 2007  Michael Buesch <mb@bu3sch.de>
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License version 2
6  *   as published by the Free Software Foundation.
7  *
8  *   This program is distributed in the hope that it will be useful,
9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *   GNU General Public License for more details.
12  */
13
14 #include "initvals.h"
15 #include "list.h"
16 #include "util.h"
17 #include "args.h"
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23
24 struct initval {
25         unsigned int offset;
26         unsigned int size;
27 #define SIZE_16BIT      2
28 #define SIZE_32BIT      4
29         unsigned int value;
30
31         struct list_head list;
32 };
33
34 /* The IV in the binary file */
35 struct initval_raw {
36         be16_t offset_size;
37         union {
38                 be16_t d16;
39                 be32_t d32;
40         } data __attribute__((__packed__));
41 } __attribute__((__packed__));
42
43 #define FW_IV_OFFSET_MASK       0x7FFF
44 #define FW_IV_32BIT             0x8000
45
46
47 struct ivals_context {
48         /* Pointer to the parsed section structure */
49         const struct initvals_sect *sect;
50         /* List of struct initval */
51         struct list_head ivals;
52         /* Number of initvals. */
53         unsigned int ivals_count;
54 };
55
56 #define _msg_helper(type, ctx, msg, x...)       do {            \
57         fprintf(stderr, "InitVals " type);                      \
58         fprintf(stderr, " (Section \"%s\")", ctx->sect->name);  \
59         fprintf(stderr, ":\n  " msg "\n" ,##x);                 \
60                                                 } while (0)
61
62 #define iv_error(ctx, msg, x...)        do {            \
63         _msg_helper("ERROR", ctx, msg ,##x);            \
64         exit(1);                                        \
65                                         } while (0)
66
67 #define iv_warn(ctx, msg, x...) \
68         _msg_helper("warning", ctx, msg ,##x)
69
70 #define iv_info(ctx, msg, x...) \
71         _msg_helper("info", ctx, msg ,##x)
72
73
74 static void assemble_write_mmio(struct ivals_context *ctx,
75                                 unsigned int offset,
76                                 unsigned int size,
77                                 unsigned int value)
78 {
79         struct initval *iv;
80
81         iv = xmalloc(sizeof(struct initval));
82         iv->offset = offset;
83         iv->size = size;
84         iv->value = value;
85         INIT_LIST_HEAD(&iv->list);
86         list_add_tail(&iv->list, &ctx->ivals);
87         ctx->ivals_count++;
88 }
89
90 static void assemble_write_phy(struct ivals_context *ctx,
91                                unsigned int offset,
92                                unsigned int value)
93 {
94         assemble_write_mmio(ctx, 0x3FC, SIZE_16BIT, offset);
95         assemble_write_mmio(ctx, 0x3FE, SIZE_16BIT, value);
96 }
97
98 static void assemble_write_radio(struct ivals_context *ctx,
99                                  unsigned int offset,
100                                  unsigned int value)
101 {
102         assemble_write_mmio(ctx, 0x3F6, SIZE_16BIT, offset);
103         assemble_write_mmio(ctx, 0x3FA, SIZE_16BIT, value);
104 }
105
106 static void shm_control_word(struct ivals_context *ctx,
107                              unsigned int routing,
108                              unsigned int offset)
109 {
110         unsigned int control;
111
112         control = (routing & 0xFFFF);
113         control <<= 16;
114         control |= (offset & 0xFFFF);
115         assemble_write_mmio(ctx, 0x160, SIZE_32BIT, control);
116 }
117
118 static void shm_write32(struct ivals_context *ctx,
119                         unsigned int routing,
120                         unsigned int offset,
121                         unsigned int value)
122 {
123         if ((routing & 0xFF) == 0x01) {
124                 /* Is SHM Shared-memory */
125 //TODO          assert((offset & 0x0001) == 0);
126                 if (offset & 0x0003) {
127                         /* Unaligned access */
128                         shm_control_word(ctx, routing, offset >> 2);
129                         assemble_write_mmio(ctx, 0x166, SIZE_16BIT,
130                                             (value >> 16) & 0xFFFF);
131                         shm_control_word(ctx, routing, (offset >> 2) + 1);
132                         assemble_write_mmio(ctx, 0x164, SIZE_16BIT,
133                                             (value & 0xFFFF));
134                         return;
135                 }
136                 offset >>= 2;
137         }
138         shm_control_word(ctx, routing, offset);
139         assemble_write_mmio(ctx, 0x164, SIZE_32BIT, value);
140 }
141
142 static void shm_write16(struct ivals_context *ctx,
143                         unsigned int routing,
144                         unsigned int offset,
145                         unsigned int value)
146 {
147         if ((routing & 0xFF) == 0x01) {
148                 /* Is SHM Shared-memory */
149 //TODO          assert((offset & 0x0001) == 0);
150                 if (offset & 0x0003) {
151                         /* Unaligned access */
152                         shm_control_word(ctx, routing, offset >> 2);
153                         assemble_write_mmio(ctx, 0x166, SIZE_16BIT,
154                                             value);
155                         return;
156                 }
157                 offset >>= 2;
158         }
159         shm_control_word(ctx, routing, offset);
160         assemble_write_mmio(ctx, 0x164, SIZE_16BIT, value);
161 }
162
163 static void assemble_write_shm(struct ivals_context *ctx,
164                                unsigned int routing,
165                                unsigned int offset,
166                                unsigned int value,
167                                unsigned int size)
168 {
169         switch (routing & 0xFF) {
170         case 0: case 1: case 2: case 3: case 4:
171                 break;
172         default:
173                 //TODO error
174                 break;
175         }
176         //TODO check offset
177
178         //TODO check value
179         switch (size) {
180         case SIZE_16BIT:
181                 shm_write16(ctx, routing, offset, value);
182                 break;
183         case SIZE_32BIT:
184                 shm_write32(ctx, routing, offset, value);
185                 break;
186         default:
187                 fprintf(stderr, "Internal assembler BUG. SHMwrite invalid size\n");
188                 exit(1);
189         }
190 }
191
192 static void assemble_ival_section(struct ivals_context *ctx,
193                                   const struct initvals_sect *sect)
194 {
195         struct initval_op *op;
196
197         ctx->sect = sect;
198         if (list_empty(&sect->ops)) {
199                 //TODO warning
200                 return;
201         }
202         list_for_each_entry(op, &sect->ops, list) {
203                 switch (op->type) {
204                 case IVAL_W_MMIO16:
205                         assemble_write_mmio(ctx, op->args[1],
206                                             SIZE_16BIT,
207                                             op->args[0]);
208                         break;
209                 case IVAL_W_MMIO32:
210                         assemble_write_mmio(ctx, op->args[1],
211                                             SIZE_32BIT,
212                                             op->args[0]);
213                         break;
214                 case IVAL_W_PHY:
215                         assemble_write_phy(ctx, op->args[1],
216                                            op->args[0]);
217                         break;
218                 case IVAL_W_RADIO:
219                         assemble_write_radio(ctx, op->args[1],
220                                              op->args[0]);
221                         break;
222                 case IVAL_W_SHM16:
223                         assemble_write_shm(ctx, op->args[1],
224                                            op->args[2],
225                                            op->args[0],
226                                            SIZE_16BIT);
227                         break;
228                 case IVAL_W_SHM32:
229                         assemble_write_shm(ctx, op->args[1],
230                                            op->args[2],
231                                            op->args[0],
232                                            SIZE_32BIT);
233                         break;
234                 }
235         }
236 }
237
238 static unsigned int initval_to_raw(struct ivals_context *ctx,
239                                    struct initval_raw *raw,
240                                    const struct initval *iv)
241 {
242         unsigned int size;
243
244         memset(raw, 0, sizeof(*raw));
245         if (iv->offset & ~FW_IV_OFFSET_MASK) {
246                 iv_error(ctx, "Initval offset 0x%04X too big. "
247                          "Offset must be <= 0x%04X",
248                          iv->offset, FW_IV_OFFSET_MASK);
249         }
250         raw->offset_size = cpu_to_be16(iv->offset);
251
252         switch (iv->size) {
253         case SIZE_16BIT:
254                 raw->data.d16 = cpu_to_be16(iv->value);
255                 size = sizeof(be16_t) + sizeof(be16_t);
256                 break;
257         case SIZE_32BIT:
258                 raw->data.d32 = cpu_to_be32(iv->value);
259                 raw->offset_size |= cpu_to_be16(FW_IV_32BIT);
260                 size = sizeof(be16_t) + sizeof(be32_t);
261                 break;
262         default:
263                 iv_error(ctx, "Internal error. initval_to_raw invalid size.");
264                 break;
265         }
266
267         return size;
268 }
269
270 static void emit_ival_section(struct ivals_context *ctx)
271 {
272         FILE *fd;
273         char *fn;
274         size_t fn_len;
275         struct initval *iv;
276         struct initval_raw raw;
277         struct fw_header hdr;
278         unsigned int size;
279
280         memset(&hdr, 0, sizeof(hdr));
281         hdr.type = FW_TYPE_IV;
282         hdr.ver = FW_HDR_VER;
283         hdr.size = cpu_to_be32(ctx->ivals_count);
284
285         fn_len = strlen(outfile_name) + 512;
286         fn = xmalloc(fn_len);
287         snprintf(fn, fn_len, "%s.%s.initval", outfile_name, ctx->sect->name);
288         fd = fopen(fn, "w+");
289         if (!fd) {
290                 fprintf(stderr, "Could not open initval output file \"%s\"\n", fn);
291                 free(fn);
292                 exit(1);
293         }
294
295         if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) {
296                 fprintf(stderr, "Could not write initvals outfile\n");
297                 exit(1);
298         }
299
300         if (IS_VERBOSE_DEBUG)
301                 fprintf(stderr, "\nInitvals \"%s\":\n", ctx->sect->name);
302         list_for_each_entry(iv, &ctx->ivals, list) {
303                 if (IS_VERBOSE_DEBUG) {
304                         fprintf(stderr, "%04X %u %08X\n",
305                                 iv->offset,
306                                 iv->size,
307                                 iv->value);
308                 }
309                 size = initval_to_raw(ctx, &raw, iv);
310                 if (fwrite(&raw, size, 1, fd) != 1) {
311                         fprintf(stderr, "Could not write initvals outfile\n");
312                         exit(1);
313                 }
314         }
315         fclose(fd);
316         free(fn);
317 }
318
319 void assemble_initvals(void)
320 {
321         struct ivals_context ctx;
322         struct initvals_sect *sect;
323
324         list_for_each_entry(sect, &infile.ivals, list) {
325                 memset(&ctx, 0, sizeof(ctx));
326                 INIT_LIST_HEAD(&ctx.ivals);
327
328                 assemble_ival_section(&ctx, sect);
329                 emit_ival_section(&ctx);
330         }
331 }