2 * Beagle board emulation. http://beagleboard.org/
\r
4 * Copyright (C) 2008 yajin(yajin@vm-kernel.org)
\r
6 * This program is free software; you can redistribute it and/or
\r
7 * modify it under the terms of the GNU General Public License as
\r
8 * published by the Free Software Foundation; either version 2 or
\r
9 * (at your option) version 3 of the License.
\r
11 * This program is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program; if not, write to the Free Software
\r
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
\r
22 #include "qemu-common.h"
\r
25 #include "arm-misc.h"
\r
27 #include "console.h"
\r
30 #include "devices.h"
\r
35 #define BEAGLE_NAND_CS 0
\r
36 #define BEAGLE_TWL4030_ADDR 0x4b /* Power management */
\r
41 #define GPMC_ONENAND 3
\r
43 #define MMC_ONENAND 5
\r
46 #define TST_DEVICE 0x0
\r
47 #define EMU_DEVICE 0x1
\r
48 #define HS_DEVICE 0x2
\r
49 #define GP_DEVICE 0x3
\r
51 #define DEBUG_BEAGLE
\r
54 #define BEAGLE_DEBUG(...) do { fprintf(stderr, __VA_ARGS__); } while(0)
\r
56 #define BEAGLE_DEBUG(x)
\r
59 /* Beagle board support */
\r
61 struct omap_mpu_state_s *cpu;
\r
63 struct nand_bflash_s *nand;
\r
64 struct omap3_lcd_panel_s *lcd_panel;
\r
70 static struct arm_boot_info beagle_binfo = {
\r
71 .ram_size = 0x08000000,
\r
75 static uint32_t beagle_nand_read16(void *opaque, target_phys_addr_t addr)
\r
77 struct beagle_s *s = (struct beagle_s *) opaque;
\r
78 //BEAGLE_DEBUG("beagle_nand_read16 offset %x\n",addr);
\r
82 case 0x7C: /*NAND_COMMAND*/
\r
83 case 0x80: /*NAND_ADDRESS*/
\r
86 case 0x84: /*NAND_DATA*/
\r
87 return nandb_read_data16(s->nand);
\r
96 static void beagle_nand_write16(void *opaque, target_phys_addr_t addr,
\r
99 struct beagle_s *s = (struct beagle_s *) opaque;
\r
102 case 0x7C: /*NAND_COMMAND*/
\r
103 nandb_write_command(s->nand,value);
\r
105 case 0x80: /*NAND_ADDRESS*/
\r
106 nandb_write_address(s->nand,value);
\r
108 case 0x84: /*NAND_DATA*/
\r
109 nandb_write_data16(s->nand,value);
\r
112 OMAP_BAD_REG(addr);
\r
118 static CPUReadMemoryFunc *beagle_nand_readfn[] = {
\r
119 beagle_nand_read16,
\r
120 beagle_nand_read16,
\r
121 omap_badwidth_read32,
\r
124 static CPUWriteMemoryFunc *beagle_nand_writefn[] = {
\r
125 beagle_nand_write16,
\r
126 beagle_nand_write16,
\r
127 omap_badwidth_write32,
\r
130 static void beagle_nand_setup(struct beagle_s *s)
\r
135 s->nand = nandb_init(NAND_MFR_MICRON,0xba);
\r
136 /*wp=1, no write protect!!! */
\r
137 //nand_set_wp(s->nand, 1);
\r
139 /* iomemtype = cpu_register_io_memory(0, beagle_nand_readfn,
\r
140 beagle_nand_writefn, s);
\r
141 cpu_register_physical_memory(0x6e00007c, 0xc, iomemtype);*/
\r
142 omap_gpmc_attach(s->cpu->gpmc, 0, 0, NULL, NULL, s, beagle_nand_readfn, beagle_nand_writefn);
\r
145 omap3_set_mem_type(s->cpu,GPMC_NAND);
\r
149 static int beagle_nand_read_page(struct beagle_s *s,uint8_t *buf, uint16_t page_addr)
\r
156 /*send command 0x0*/
\r
157 beagle_nand_write16(s,0x7C,0);
\r
158 /*send page address */
\r
159 beagle_nand_write16(s,0x80,page_addr&0xff);
\r
160 beagle_nand_write16(s,0x80,(page_addr>>8)&0x7);
\r
161 beagle_nand_write16(s,0x80,(page_addr>>11)&0xff);
\r
162 beagle_nand_write16(s,0x80,(page_addr>>19)&0xff);
\r
163 beagle_nand_write16(s,0x80,(page_addr>>27)&0xff);
\r
164 /*send command 0x30*/
\r
165 beagle_nand_write16(s,0x7C,0x30);
\r
167 for (i=0;i<0x800/2;i++)
\r
169 *p++ = beagle_nand_read16(s,0x84);
\r
174 static uint32_t get_le32(void *p)
\r
176 uint8_t *q = (uint8_t *)p;
\r
179 v |= q[2]; v <<= 8;
\r
180 v |= q[1]; v <<= 8;
\r
185 static uint32_t get_le16(void *p)
\r
187 uint8_t *q = (uint8_t *)p;
\r
194 /* returns ptr to matching dir entry / zero entry or 0 if unsuccessful */
\r
195 static uint8_t *beagle_scan_fat_dir_sector(uint8_t *s)
\r
199 /* there are 0x10 items with 0x20 bytes per item */
\r
200 for (i = 0x10; i--; s += 0x20) {
\r
201 if (*s == 0xe5 || (s[0x0b] & 0x0f) == 0x0f) continue; /* erased or LFN */
\r
202 if (!*s || !strncasecmp((void *)s, "mlo ", 8+3)) return s;
\r
207 struct beagle_fat_drv_s {
\r
208 BlockDriverState *bs;
\r
209 uint8_t ptype; // 12, 16, 32
\r
210 uint64_t c0; // physical byte offset for data cluster 0
\r
211 uint64_t fat; // physical byte offset for used FAT sector 0
\r
212 uint32_t spc; // sectors per cluster
\r
215 /* returns cluster data in the buffer and next cluster chain number or 0 if unsuccessful */
\r
216 static uint32_t beagle_read_fat_cluster(uint8_t *data,
\r
217 struct beagle_fat_drv_s *drv,
\r
221 uint32_t len = drv->spc * 0x200; // number of bytes to read i.e. bytes per cluster
\r
223 switch (drv->ptype) { /* check for EOF */
\r
224 case 12: if (cl > 0xff0) return 0; break;
\r
225 case 16: if (cl > 0xfff0) return 0; break;
\r
226 case 32: if (cl > 0x0ffffff0) return 0; break;
\r
230 if (bdrv_pread(drv->bs, drv->c0 + ((drv->ptype == 32 ? cl - 2 : cl) * len), data, len) != len) return 0;
\r
232 switch (drv->ptype) { /* determine next cluster # */
\r
233 case 12: fprintf(stderr, "%s: FAT12 parsing not implemented!\n", __FUNCTION__); break;
\r
234 case 16: return (bdrv_pread(drv->bs, drv->fat + cl * 2, buf, 2) != 2) ? 0 : get_le16(buf);
\r
235 case 32: return (bdrv_pread(drv->bs, drv->fat + cl * 4, buf, 4) != 4) ? 0 : get_le32(buf) & 0x0fffffff;
\r
241 static int beagle_boot_from_mmc(struct beagle_s *s)
\r
243 struct beagle_fat_drv_s drv;
\r
244 int sdindex = drive_get_index(IF_SD, 0, 0);
\r
245 uint8_t sector[0x200], *p, *q;
\r
246 uint32_t pstart, i, j, k, cluster0, fatsize, bootsize, rootsize;
\r
247 uint32_t img_size, img_addr;
\r
250 /* very simple implementation, requires MBR partitioning with an active FAT partition
\r
251 and does no sanity checks for the partition table or FAT boot sector entries */
\r
252 if (sdindex >= 0) {
\r
253 drv.bs = drives_table[sdindex].bdrv;
\r
254 if (bdrv_pread(drv.bs, 0, sector, 0x200) == 0x200
\r
255 && sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa) { /* MBR signature */
\r
256 for (i = 0, p = sector + 0x1be; i < 4; i++, p += 0x10) if (p[0] == 0x80) break;
\r
257 if (i < 4 /* was there an active partition? */
\r
258 && (p[4] == 1 || p[4] == 4 || p[4] == 6 || p[4] == 11
\r
259 || p[4] == 12 || p[4] == 14 || p[4] == 15) /* FAT partition type */
\r
260 && bdrv_pread(drv.bs, (pstart = get_le32(p + 8)) * 0x200, sector, 0x200) == 0x200
\r
261 && sector[0x1fe] ==0x55 && sector[0x1ff] == 0xaa) { /* boot sector signature */
\r
262 /* determine FAT type */
\r
263 fatsize = get_le16(sector + 0x16); if (!fatsize) fatsize = get_le32(sector + 0x24);
\r
264 bootsize = get_le16(sector + 0x0e);
\r
265 cluster0 = bootsize + fatsize * sector[0x10];
\r
266 rootsize = get_le16(sector + 0x11); if (rootsize & 0x0f) rootsize += 0x10; rootsize >>= 4;
\r
267 drv.spc = sector[0x0d];
\r
268 i = get_le16(sector + 0x13); if (!i) i = get_le32(sector + 0x20);
\r
269 i = (i - (cluster0 + rootsize)) / drv.spc;
\r
270 drv.ptype = (i < 4085) ? 12 : (i < 65525) ? 16 : 32;
\r
271 /* search for MLO file */
\r
272 drv.fat = (bootsize + pstart) * 0x200;
\r
273 drv.c0 = (cluster0 + pstart) * 0x200;
\r
274 if (drv.ptype == 32) {
\r
275 i = get_le32(sector + 0x2c); /* first root cluster # */
\r
276 j = get_le16(sector + 0x28);
\r
277 if (j & 0x80) drv.fat += (j & 0x0f) * fatsize * 0x200;
\r
278 uint8_t *cluster = qemu_mallocz(drv.spc * 0x200);
\r
279 for (p = 0; !p && (i = beagle_read_fat_cluster(cluster, &drv, i)); ) {
\r
280 for (j = drv.spc, q=cluster; j-- & !p; q += 0x200)
\r
281 p = beagle_scan_fat_dir_sector(q);
\r
282 if (p) memcpy(sector, q - 0x200, 0x200); // save the sector
\r
285 } else { /* FAT12/16 */
\r
286 for (i = rootsize, j = 0, p = 0; i-- && !p; j++) {
\r
287 if (bdrv_pread(drv.bs, drv.c0 + j * 0x200, sector, 0x200) != 0x200) break;
\r
288 p = beagle_scan_fat_dir_sector(sector);
\r
291 if (p && *p) { // did we indeed find the file?
\r
292 i = get_le16(p + 0x14); i <<= 16; i |= get_le16(p + 0x1a);
\r
293 j = drv.spc * 0x200;
\r
294 uint8 *data = qemu_mallocz(j);
\r
295 if ((i = beagle_read_fat_cluster(data, &drv, i))) {
\r
296 /* TODO: support HS device boot - for now only GP device is supported */
\r
297 img_size = get_le32(data);
\r
298 img_addr = get_le32(data + 4);
\r
299 s->cpu->env->regs[15] = img_addr;
\r
300 cpu_physical_memory_write(img_addr, data + 8,
\r
301 (k = (j - 8 >= img_size) ? img_size : j - 8));
\r
302 for (img_addr += k, img_size -= k;
\r
303 img_size && (i = beagle_read_fat_cluster(data, &drv, i));
\r
304 img_addr += k, img_size -= k) {
\r
305 cpu_physical_memory_write(img_addr, data,
\r
306 (k = (j >= img_size) ? img_size : j));
\r
310 fprintf(stderr, "%s: unable to read MLO file contents from SD card\n", __FUNCTION__);
\r
313 fprintf(stderr, "%s: MLO file not found in the root directory\n", __FUNCTION__);
\r
315 fprintf(stderr, "%s: no active FAT partition found on SD card\n", __FUNCTION__);
\r
317 fprintf(stderr, "%s: MBR not found on SD card\n", __FUNCTION__);
\r
323 /*read the xloader from NAND Flash into internal RAM*/
\r
324 static int beagle_boot_from_nand(struct beagle_s *s)
\r
326 uint32_t loadaddr, len;
\r
327 uint8_t nand_page[0x800],*load_dest;
\r
328 uint32_t nand_pages,i;
\r
330 /* The first two words(8 bytes) in first nand flash page have special meaning.
\r
331 First word:x-loader len
\r
332 Second word: x-load address in internal ram */
\r
333 beagle_nand_read_page(s,nand_page,0);
\r
334 len = *((uint32_t*)nand_page);
\r
335 loadaddr = *((uint32_t*)(nand_page+4));
\r
336 if ((len==0)||(loadaddr==0)||(len==0xffffffff)||(loadaddr==0xffffffff))
\r
339 /*put the first page into internal ram*/
\r
340 load_dest = phys_ram_base +beagle_binfo.ram_size;
\r
341 load_dest += loadaddr-OMAP3_SRAM_BASE;
\r
343 BEAGLE_DEBUG("load_dest %x phys_ram_base %x \n",(unsigned)load_dest,(unsigned)phys_ram_base);
\r
345 memcpy(load_dest,nand_page+8,0x800-8);
\r
346 load_dest += 0x800-8;
\r
348 nand_pages = len/0x800;
\r
352 for (i=1;i<nand_pages;i++)
\r
354 beagle_nand_read_page(s,nand_page,i*0x800);
\r
355 memcpy(load_dest,nand_page,0x800);
\r
356 load_dest += 0x800;
\r
358 s->cpu->env->regs[15] = loadaddr;
\r
362 static int beagle_rom_emu(struct beagle_s *s)
\r
364 if (beagle_boot_from_mmc(s)<0)
\r
366 if (beagle_boot_from_nand(s)<0)
\r
372 static void beagle_dss_setup(struct beagle_s *s, DisplayState *ds)
\r
374 s->lcd_panel = omap3_lcd_panel_init(ds);
\r
375 omap3_lcd_panel_attach(s->cpu->dss, 0, s->lcd_panel);
\r
376 s->lcd_panel->dss = s->cpu->dss;
\r
379 static void beagle_mmc_cs_cb(void *opaque, int line, int level)
\r
381 /* TODO: this seems to actually be connected to the menelaus, to
\r
382 * which also both MMC slots connect. */
\r
383 omap_mmc_enable((struct omap_mmc_s *) opaque, !level);
\r
385 printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1);
\r
388 static void beagle_i2c_setup(struct beagle_s *s)
\r
391 /* Attach the CPU on one end of our I2C bus. */
\r
392 s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
\r
394 /* Attach a menelaus PM chip */
\r
395 i2c_set_slave_address(
\r
396 twl4030_init(s->i2c,
\r
397 s->cpu->irq[0][OMAP_INT_35XX_SYS_NIRQ]),
\r
398 BEAGLE_TWL4030_ADDR);
\r
402 static void beagle_init(ram_addr_t ram_size, int vga_ram_size,
\r
403 const char *boot_device, DisplayState *ds,
\r
404 const char *kernel_filename, const char *kernel_cmdline,
\r
405 const char *initrd_filename, const char *cpu_model)
\r
407 struct beagle_s *s = (struct beagle_s *) qemu_mallocz(sizeof(*s));
\r
408 int sdram_size = beagle_binfo.ram_size;
\r
410 if (ram_size < sdram_size + OMAP3530_SRAM_SIZE) {
\r
411 fprintf(stderr, "This architecture uses %i bytes of memory\n",
\r
412 sdram_size + OMAP3530_SRAM_SIZE);
\r
415 s->cpu = omap3530_mpu_init(sdram_size, NULL, NULL);
\r
416 beagle_nand_setup(s);
\r
417 beagle_i2c_setup(s);
\r
418 beagle_dss_setup(s,ds);
\r
419 omap3_set_device_type(s->cpu,GP_DEVICE);
\r
420 if (beagle_rom_emu(s) < 0) {
\r
421 fprintf(stderr,"boot from MMC and nand failed \n");
\r
428 QEMUMachine beagle_machine = {
\r
430 .desc = "Beagle board (OMAP3530)",
\r
431 .init = beagle_init,
\r
432 .ram_require = (0x08000000 + OMAP3530_SRAM_SIZE) | RAMSIZE_FIXED,
\r