xref: /illumos-kvm-cmd/hw/omap_gpmc.c (revision 68396ea9)
1 /*
2  * TI OMAP general purpose memory controller emulation.
3  *
4  * Copyright (C) 2007-2009 Nokia Corporation
5  * Original code written by Andrzej Zaborowski <andrew@openedhand.com>
6  * Enhancements for OMAP3 and NAND support written by Juha Riihimäki
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 or
11  * (at your option) any later version of the License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 #include "hw.h"
22 #include "flash.h"
23 #include "omap.h"
24 
25 /* General-Purpose Memory Controller */
26 struct omap_gpmc_s {
27     qemu_irq irq;
28 
29     uint8_t sysconfig;
30     uint16_t irqst;
31     uint16_t irqen;
32     uint16_t timeout;
33     uint16_t config;
34     uint32_t prefconfig[2];
35     int prefcontrol;
36     int preffifo;
37     int prefcount;
38     struct omap_gpmc_cs_file_s {
39         uint32_t config[7];
40         target_phys_addr_t base;
41         size_t size;
42         int iomemtype;
43         void (*base_update)(void *opaque, target_phys_addr_t new);
44         void (*unmap)(void *opaque);
45         void *opaque;
46     } cs_file[8];
47     int ecc_cs;
48     int ecc_ptr;
49     uint32_t ecc_cfg;
50     ECCState ecc[9];
51 };
52 
omap_gpmc_int_update(struct omap_gpmc_s * s)53 static void omap_gpmc_int_update(struct omap_gpmc_s *s)
54 {
55     qemu_set_irq(s->irq, s->irqen & s->irqst);
56 }
57 
omap_gpmc_cs_map(struct omap_gpmc_cs_file_s * f,int base,int mask)58 static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask)
59 {
60     /* TODO: check for overlapping regions and report access errors */
61     if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) ||
62                     (base < 0 || base >= 0x40) ||
63                     (base & 0x0f & ~mask)) {
64         fprintf(stderr, "%s: wrong cs address mapping/decoding!\n",
65                         __FUNCTION__);
66         return;
67     }
68 
69     if (!f->opaque)
70         return;
71 
72     f->base = base << 24;
73     f->size = (0x0fffffff & ~(mask << 24)) + 1;
74     /* TODO: rather than setting the size of the mapping (which should be
75      * constant), the mask should cause wrapping of the address space, so
76      * that the same memory becomes accessible at every <i>size</i> bytes
77      * starting from <i>base</i>.  */
78     if (f->iomemtype)
79         cpu_register_physical_memory(f->base, f->size, f->iomemtype);
80 
81     if (f->base_update)
82         f->base_update(f->opaque, f->base);
83 }
84 
omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s * f)85 static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f)
86 {
87     if (f->size) {
88         if (f->unmap)
89             f->unmap(f->opaque);
90         if (f->iomemtype)
91             cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED);
92         f->base = 0;
93         f->size = 0;
94     }
95 }
96 
omap_gpmc_reset(struct omap_gpmc_s * s)97 void omap_gpmc_reset(struct omap_gpmc_s *s)
98 {
99     int i;
100 
101     s->sysconfig = 0;
102     s->irqst = 0;
103     s->irqen = 0;
104     omap_gpmc_int_update(s);
105     s->timeout = 0;
106     s->config = 0xa00;
107     s->prefconfig[0] = 0x00004000;
108     s->prefconfig[1] = 0x00000000;
109     s->prefcontrol = 0;
110     s->preffifo = 0;
111     s->prefcount = 0;
112     for (i = 0; i < 8; i ++) {
113         if (s->cs_file[i].config[6] & (1 << 6))			/* CSVALID */
114             omap_gpmc_cs_unmap(s->cs_file + i);
115         s->cs_file[i].config[0] = i ? 1 << 12 : 0;
116         s->cs_file[i].config[1] = 0x101001;
117         s->cs_file[i].config[2] = 0x020201;
118         s->cs_file[i].config[3] = 0x10031003;
119         s->cs_file[i].config[4] = 0x10f1111;
120         s->cs_file[i].config[5] = 0;
121         s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6);
122         if (s->cs_file[i].config[6] & (1 << 6))			/* CSVALID */
123             omap_gpmc_cs_map(&s->cs_file[i],
124                             s->cs_file[i].config[6] & 0x1f,	/* MASKADDR */
125                         (s->cs_file[i].config[6] >> 8 & 0xf));	/* BASEADDR */
126     }
127     omap_gpmc_cs_map(s->cs_file, 0, 0xf);
128     s->ecc_cs = 0;
129     s->ecc_ptr = 0;
130     s->ecc_cfg = 0x3fcff000;
131     for (i = 0; i < 9; i ++)
132         ecc_reset(&s->ecc[i]);
133 }
134 
omap_gpmc_read(void * opaque,target_phys_addr_t addr)135 static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
136 {
137     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
138     int cs;
139     struct omap_gpmc_cs_file_s *f;
140 
141     switch (addr) {
142     case 0x000:	/* GPMC_REVISION */
143         return 0x20;
144 
145     case 0x010:	/* GPMC_SYSCONFIG */
146         return s->sysconfig;
147 
148     case 0x014:	/* GPMC_SYSSTATUS */
149         return 1;						/* RESETDONE */
150 
151     case 0x018:	/* GPMC_IRQSTATUS */
152         return s->irqst;
153 
154     case 0x01c:	/* GPMC_IRQENABLE */
155         return s->irqen;
156 
157     case 0x040:	/* GPMC_TIMEOUT_CONTROL */
158         return s->timeout;
159 
160     case 0x044:	/* GPMC_ERR_ADDRESS */
161     case 0x048:	/* GPMC_ERR_TYPE */
162         return 0;
163 
164     case 0x050:	/* GPMC_CONFIG */
165         return s->config;
166 
167     case 0x054:	/* GPMC_STATUS */
168         return 0x001;
169 
170     case 0x060 ... 0x1d4:
171         cs = (addr - 0x060) / 0x30;
172         addr -= cs * 0x30;
173         f = s->cs_file + cs;
174         switch (addr) {
175             case 0x60:	/* GPMC_CONFIG1 */
176                 return f->config[0];
177             case 0x64:	/* GPMC_CONFIG2 */
178                 return f->config[1];
179             case 0x68:	/* GPMC_CONFIG3 */
180                 return f->config[2];
181             case 0x6c:	/* GPMC_CONFIG4 */
182                 return f->config[3];
183             case 0x70:	/* GPMC_CONFIG5 */
184                 return f->config[4];
185             case 0x74:	/* GPMC_CONFIG6 */
186                 return f->config[5];
187             case 0x78:	/* GPMC_CONFIG7 */
188                 return f->config[6];
189             case 0x84:	/* GPMC_NAND_DATA */
190                 return 0;
191         }
192         break;
193 
194     case 0x1e0:	/* GPMC_PREFETCH_CONFIG1 */
195         return s->prefconfig[0];
196     case 0x1e4:	/* GPMC_PREFETCH_CONFIG2 */
197         return s->prefconfig[1];
198     case 0x1ec:	/* GPMC_PREFETCH_CONTROL */
199         return s->prefcontrol;
200     case 0x1f0:	/* GPMC_PREFETCH_STATUS */
201         return (s->preffifo << 24) |
202                 ((s->preffifo >
203                   ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) |
204                 s->prefcount;
205 
206     case 0x1f4:	/* GPMC_ECC_CONFIG */
207         return s->ecc_cs;
208     case 0x1f8:	/* GPMC_ECC_CONTROL */
209         return s->ecc_ptr;
210     case 0x1fc:	/* GPMC_ECC_SIZE_CONFIG */
211         return s->ecc_cfg;
212     case 0x200 ... 0x220:	/* GPMC_ECC_RESULT */
213         cs = (addr & 0x1f) >> 2;
214         /* TODO: check correctness */
215         return
216                 ((s->ecc[cs].cp    &  0x07) <<  0) |
217                 ((s->ecc[cs].cp    &  0x38) << 13) |
218                 ((s->ecc[cs].lp[0] & 0x1ff) <<  3) |
219                 ((s->ecc[cs].lp[1] & 0x1ff) << 19);
220 
221     case 0x230:	/* GPMC_TESTMODE_CTRL */
222         return 0;
223     case 0x234:	/* GPMC_PSA_LSB */
224     case 0x238:	/* GPMC_PSA_MSB */
225         return 0x00000000;
226     }
227 
228     OMAP_BAD_REG(addr);
229     return 0;
230 }
231 
omap_gpmc_write(void * opaque,target_phys_addr_t addr,uint32_t value)232 static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
233                 uint32_t value)
234 {
235     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
236     int cs;
237     struct omap_gpmc_cs_file_s *f;
238 
239     switch (addr) {
240     case 0x000:	/* GPMC_REVISION */
241     case 0x014:	/* GPMC_SYSSTATUS */
242     case 0x054:	/* GPMC_STATUS */
243     case 0x1f0:	/* GPMC_PREFETCH_STATUS */
244     case 0x200 ... 0x220:	/* GPMC_ECC_RESULT */
245     case 0x234:	/* GPMC_PSA_LSB */
246     case 0x238:	/* GPMC_PSA_MSB */
247         OMAP_RO_REG(addr);
248         break;
249 
250     case 0x010:	/* GPMC_SYSCONFIG */
251         if ((value >> 3) == 0x3)
252             fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
253                             __FUNCTION__, value >> 3);
254         if (value & 2)
255             omap_gpmc_reset(s);
256         s->sysconfig = value & 0x19;
257         break;
258 
259     case 0x018:	/* GPMC_IRQSTATUS */
260         s->irqen = ~value;
261         omap_gpmc_int_update(s);
262         break;
263 
264     case 0x01c:	/* GPMC_IRQENABLE */
265         s->irqen = value & 0xf03;
266         omap_gpmc_int_update(s);
267         break;
268 
269     case 0x040:	/* GPMC_TIMEOUT_CONTROL */
270         s->timeout = value & 0x1ff1;
271         break;
272 
273     case 0x044:	/* GPMC_ERR_ADDRESS */
274     case 0x048:	/* GPMC_ERR_TYPE */
275         break;
276 
277     case 0x050:	/* GPMC_CONFIG */
278         s->config = value & 0xf13;
279         break;
280 
281     case 0x060 ... 0x1d4:
282         cs = (addr - 0x060) / 0x30;
283         addr -= cs * 0x30;
284         f = s->cs_file + cs;
285         switch (addr) {
286             case 0x60:	/* GPMC_CONFIG1 */
287                 f->config[0] = value & 0xffef3e13;
288                 break;
289             case 0x64:	/* GPMC_CONFIG2 */
290                 f->config[1] = value & 0x001f1f8f;
291                 break;
292             case 0x68:	/* GPMC_CONFIG3 */
293                 f->config[2] = value & 0x001f1f8f;
294                 break;
295             case 0x6c:	/* GPMC_CONFIG4 */
296                 f->config[3] = value & 0x1f8f1f8f;
297                 break;
298             case 0x70:	/* GPMC_CONFIG5 */
299                 f->config[4] = value & 0x0f1f1f1f;
300                 break;
301             case 0x74:	/* GPMC_CONFIG6 */
302                 f->config[5] = value & 0x00000fcf;
303                 break;
304             case 0x78:	/* GPMC_CONFIG7 */
305                 if ((f->config[6] ^ value) & 0xf7f) {
306                     if (f->config[6] & (1 << 6))		/* CSVALID */
307                         omap_gpmc_cs_unmap(f);
308                     if (value & (1 << 6))			/* CSVALID */
309                         omap_gpmc_cs_map(f, value & 0x1f,	/* MASKADDR */
310                                         (value >> 8 & 0xf));	/* BASEADDR */
311                 }
312                 f->config[6] = value & 0x00000f7f;
313                 break;
314             case 0x7c:	/* GPMC_NAND_COMMAND */
315             case 0x80:	/* GPMC_NAND_ADDRESS */
316             case 0x84:	/* GPMC_NAND_DATA */
317                 break;
318 
319             default:
320                 goto bad_reg;
321         }
322         break;
323 
324     case 0x1e0:	/* GPMC_PREFETCH_CONFIG1 */
325         s->prefconfig[0] = value & 0x7f8f7fbf;
326         /* TODO: update interrupts, fifos, dmas */
327         break;
328 
329     case 0x1e4:	/* GPMC_PREFETCH_CONFIG2 */
330         s->prefconfig[1] = value & 0x3fff;
331         break;
332 
333     case 0x1ec:	/* GPMC_PREFETCH_CONTROL */
334         s->prefcontrol = value & 1;
335         if (s->prefcontrol) {
336             if (s->prefconfig[0] & 1)
337                 s->preffifo = 0x40;
338             else
339                 s->preffifo = 0x00;
340         }
341         /* TODO: start */
342         break;
343 
344     case 0x1f4:	/* GPMC_ECC_CONFIG */
345         s->ecc_cs = 0x8f;
346         break;
347     case 0x1f8:	/* GPMC_ECC_CONTROL */
348         if (value & (1 << 8))
349             for (cs = 0; cs < 9; cs ++)
350                 ecc_reset(&s->ecc[cs]);
351         s->ecc_ptr = value & 0xf;
352         if (s->ecc_ptr == 0 || s->ecc_ptr > 9) {
353             s->ecc_ptr = 0;
354             s->ecc_cs &= ~1;
355         }
356         break;
357     case 0x1fc:	/* GPMC_ECC_SIZE_CONFIG */
358         s->ecc_cfg = value & 0x3fcff1ff;
359         break;
360     case 0x230:	/* GPMC_TESTMODE_CTRL */
361         if (value & 7)
362             fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__);
363         break;
364 
365     default:
366     bad_reg:
367         OMAP_BAD_REG(addr);
368         return;
369     }
370 }
371 
372 static CPUReadMemoryFunc * const omap_gpmc_readfn[] = {
373     omap_badwidth_read32,	/* TODO */
374     omap_badwidth_read32,	/* TODO */
375     omap_gpmc_read,
376 };
377 
378 static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = {
379     omap_badwidth_write32,	/* TODO */
380     omap_badwidth_write32,	/* TODO */
381     omap_gpmc_write,
382 };
383 
omap_gpmc_init(target_phys_addr_t base,qemu_irq irq)384 struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq)
385 {
386     int iomemtype;
387     struct omap_gpmc_s *s = (struct omap_gpmc_s *)
388             qemu_mallocz(sizeof(struct omap_gpmc_s));
389 
390     omap_gpmc_reset(s);
391 
392     iomemtype = cpu_register_io_memory(omap_gpmc_readfn,
393                     omap_gpmc_writefn, s, DEVICE_NATIVE_ENDIAN);
394     cpu_register_physical_memory(base, 0x1000, iomemtype);
395 
396     return s;
397 }
398 
omap_gpmc_attach(struct omap_gpmc_s * s,int cs,int iomemtype,void (* base_upd)(void * opaque,target_phys_addr_t new),void (* unmap)(void * opaque),void * opaque)399 void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
400                 void (*base_upd)(void *opaque, target_phys_addr_t new),
401                 void (*unmap)(void *opaque), void *opaque)
402 {
403     struct omap_gpmc_cs_file_s *f;
404 
405     if (cs < 0 || cs >= 8) {
406         fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs);
407         exit(-1);
408     }
409     f = &s->cs_file[cs];
410 
411     f->iomemtype = iomemtype;
412     f->base_update = base_upd;
413     f->unmap = unmap;
414     f->opaque = opaque;
415 
416     if (f->config[6] & (1 << 6))				/* CSVALID */
417         omap_gpmc_cs_map(f, f->config[6] & 0x1f,		/* MASKADDR */
418                         (f->config[6] >> 8 & 0xf));		/* BASEADDR */
419 }
420