nand: boot code cleanup
[qemu/mini2440.git] / hw / mini2440.c
blob40edb06c8f0268f53805ab6657af3d07490b4876
1 /*
2 * mini2440 development board support
4 * Copyright Michel Pollet <buserror@gmail.com>
6 * This code is licensed under the GNU GPL v2.
7 */
9 #include "hw.h"
10 #include "s3c.h"
11 #include "arm-misc.h"
12 #include "sysemu.h"
13 #include "i2c.h"
14 #include "qemu-timer.h"
15 #include "devices.h"
16 #include "audio/audio.h"
17 #include "boards.h"
18 #include "console.h"
19 #include "usb.h"
20 #include "net.h"
21 #include "sd.h"
22 #include "dm9000.h"
23 #include "eeprom24c0x.h"
25 #define mini2440_printf(format, ...) \
26 fprintf(stderr, "QEMU %s: " format, __FUNCTION__, ##__VA_ARGS__)
28 #define MINI2440_GPIO_BACKLIGHT S3C_GPG(4)
29 #define MINI2440_GPIO_LCD_RESET S3C_GPC(6)
30 #define MINI2440_GPIO_nSD_DETECT S3C_GPG(8)
31 #define MINI2440_GPIO_WP_SD S3C_GPH(8)
32 #define MINI2440_GPIO_DM9000 S3C_GPF(7)
33 #define MINI2440_GPIO_USB_PULLUP S3C_GPC(5)
35 #define MINI2440_IRQ_nSD_DETECT S3C_EINT(16)
36 #define MINI2440_IRQ_DM9000 S3C_EINT(7)
38 #define FLASH_NOR_SIZE (2*1024*1024)
40 #define BOOT_NONE 0
41 #define BOOT_NOR 1
42 #define BOOT_NAND 2
44 struct mini2440_board_s {
45 struct s3c_state_s *cpu;
46 unsigned int ram;
47 struct ee24c08_s * eeprom;
48 const char * kernel;
49 SDState * mmc;
50 NANDFlashState *nand;
51 pflash_t * nor;
52 int bl_level;
53 int boot_mode;
57 * the 24c08 sits on 4 addresses on the bus, and uses the lower address bits
58 * to address the 256 byte "page" of the eeprom. We thus need to use 4 i2c_slaves
59 * and keep track of which one was used to set the read/write pointer into the data
61 struct ee24c08_s;
62 typedef struct ee24cxx_page_s {
63 i2c_slave i2c;
64 struct ee24c08_s * eeprom;
65 uint8_t page;
66 } ee24cxx_page_s;
67 typedef struct ee24c08_s {
68 /* that memory takes 4 addresses */
69 i2c_slave * slave[4];
70 uint16_t ptr;
71 uint16_t count;
72 uint8_t data[1024];
73 } ee24c08;
75 static void ee24c08_event(i2c_slave *i2c, enum i2c_event event)
77 ee24cxx_page_s *s = FROM_I2C_SLAVE(ee24cxx_page_s, i2c);
79 if (!s->eeprom)
80 return;
82 s->eeprom->ptr = s->page * 256;
83 s->eeprom->count = 0;
86 static int ee24c08_tx(i2c_slave *i2c, uint8_t data)
88 ee24cxx_page_s *s = FROM_I2C_SLAVE(ee24cxx_page_s, i2c);
90 if (!s->eeprom)
91 return 0;
92 if (s->eeprom->count++ == 0) {
93 /* first byte is address offset */
94 s->eeprom->ptr = (s->page * 256) + data;
95 } else {
96 mini2440_printf("write %04x=%02x\n", s->eeprom->ptr, data);
97 s->eeprom->data[s->eeprom->ptr] = data;
98 s->eeprom->ptr = (s->eeprom->ptr & ~0xff) | ((s->eeprom->ptr + 1) & 0xff);
99 s->eeprom->count++;
101 return 0;
104 static int ee24c08_rx(i2c_slave *i2c)
106 ee24cxx_page_s *s = FROM_I2C_SLAVE(ee24cxx_page_s, i2c);
107 uint8_t res;
108 if (!s->eeprom)
109 return 0;
111 res = s->eeprom->data[s->eeprom->ptr];
113 s->eeprom->ptr = (s->eeprom->ptr & ~0xff) | ((s->eeprom->ptr + 1) & 0xff);
114 s->eeprom->count++;
115 return res;
118 static void ee24c08_save(QEMUFile *f, void *opaque)
120 struct ee24c08_s *s = (struct ee24c08_s *) opaque;
121 int i;
123 qemu_put_be16s(f, &s->ptr);
124 qemu_put_be16s(f, &s->count);
125 qemu_put_buffer(f, s->data, sizeof(s->data));
127 for (i = 0; i < 4; i++)
128 i2c_slave_save(f, s->slave[i]);
131 static int ee24c08_load(QEMUFile *f, void *opaque, int version_id)
133 struct ee24c08_s *s = (struct ee24c08_s *) opaque;
134 int i;
136 qemu_get_be16s(f, &s->ptr);
137 qemu_get_be16s(f, &s->count);
138 qemu_get_buffer(f, s->data, sizeof(s->data));
140 for (i = 0; i < 4; i++)
141 i2c_slave_load(f, s->slave[i]);
142 return 0;
145 static void ee24c08_page_init(i2c_slave *i2c)
147 /* nothing to do here */
150 static I2CSlaveInfo ee24c08_info = {
151 .init = ee24c08_page_init,
152 .event = ee24c08_event,
153 .recv = ee24c08_rx,
154 .send = ee24c08_tx
157 static void ee24c08_register_devices(void)
159 i2c_register_slave("24C08", sizeof(ee24cxx_page_s), &ee24c08_info);
162 device_init(ee24c08_register_devices);
164 static ee24c08 * ee24c08_init(i2c_bus * bus)
166 ee24c08 *s = qemu_malloc(sizeof(ee24c08));
167 int i = 0;
169 printf("QEMU: %s\n", __FUNCTION__);
171 memset(s->data, 0xff, sizeof(s->data));
173 for (i = 0; i < 4; i++) {
174 DeviceState *dev = i2c_create_slave(bus, "24C08", 0x50 + i);
175 if (!dev) {
176 mini2440_printf("failed address %02x\n", 0x50+i);
178 s->slave[i] = I2C_SLAVE_FROM_QDEV(dev);
179 ee24cxx_page_s *ss = FROM_I2C_SLAVE(ee24cxx_page_s, s->slave[i]);
180 ss->page = i;
181 ss->eeprom = s;
183 register_savevm("ee24c08", -1, 0, ee24c08_save, ee24c08_load, s);
184 return s;
187 /* Handlers for output ports */
188 static void mini2440_bl_switch(void *opaque, int line, int level)
190 printf("QEMU: %s: LCD Backlight now %s (%d).\n", __FUNCTION__, level ? "on" : "off", level);
193 static void mini2440_bl_intensity(int line, int level, void *opaque)
195 struct mini2440_board_s *s = (struct mini2440_board_s *) opaque;
197 if ((level >> 8) != s->bl_level) {
198 s->bl_level = level >> 8;
199 printf("%s: LCD Backlight now at %04x\n", __FUNCTION__, level);
203 static void mini2440_gpio_setup(struct mini2440_board_s *s)
205 /* set the "input" pin values */
206 s3c_gpio_set_dat(s->cpu->io, S3C_GPG(13), 1);
207 s3c_gpio_set_dat(s->cpu->io, S3C_GPG(14), 1);
208 s3c_gpio_set_dat(s->cpu->io, S3C_GPG(15), 0);
210 s3c_gpio_out_set(s->cpu->io, MINI2440_GPIO_BACKLIGHT,
211 *qemu_allocate_irqs(mini2440_bl_switch, s, 1));
213 s3c_timers_cmp_handler_set(s->cpu->timers, 1, mini2440_bl_intensity, s);
215 /* Register the SD card pins to the lower SD driver */
216 sd_set_cb(s->mmc,
217 s3c_gpio_in_get(s->cpu->io)[MINI2440_GPIO_WP_SD],
218 qemu_irq_invert(s3c_gpio_in_get(s->cpu->io)[MINI2440_IRQ_nSD_DETECT]));
222 #if 0
223 static void hexdump(const void* address, uint32_t len)
225 const unsigned char* p = address;
226 int i, j;
228 for (i = 0; i < len; i += 16) {
229 for (j = 0; j < 16 && i + j < len; j++)
230 fprintf(stderr, "%02x ", p[i + j]);
231 for (; j < 16; j++)
232 fprintf(stderr, " ");
233 fprintf(stderr, " ");
234 for (j = 0; j < 16 && i + j < len; j++)
235 fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
236 fprintf(stderr, "\n");
239 #endif
241 static int mini2440_load_from_nand(NANDFlashState *nand,
242 uint32_t nand_offset, uint32_t s3c_base_offset, uint32_t size)
244 uint8_t buffer[512];
245 uint32_t src = 0;
246 int page = 0;
247 uint32_t dst = 0;
249 if (!nand)
250 return 0;
252 for (page = 0; page < (size / 512); page++, src += 512 + 16, dst += 512) {
253 if (nand_readraw(nand, nand_offset + src, buffer, 512)) {
254 cpu_physical_memory_write(s3c_base_offset + dst, buffer, 512);
255 } else {
256 mini2440_printf("failed to load nand %d:%d\n",
257 nand_offset + src, 512 + 16);
258 return 0;
261 return (int) size;
264 static void mini2440_reset(void *opaque)
266 struct mini2440_board_s *s = (struct mini2440_board_s *) opaque;
267 int32_t image_size;
269 s->cpu->env->regs[15] = 0;
271 if (s->boot_mode == BOOT_NAND) {
273 * Normally we would load 4 KB of nand to SRAM and jump there, but
274 * it is not working perfectly as expected, so we cheat and load
275 * it from nand directly relocated to 0x33f80000 and jump there
277 if (mini2440_load_from_nand(s->nand, 0, S3C_RAM_BASE | 0x03f80000, 256*1024)> 0) {
278 mini2440_printf("loaded default u-boot from NAND\n");
279 s->cpu->env->regs[15] = S3C_RAM_BASE | 0x03f80000; /* start address, u-boot already relocated */
281 #if 0 && defined(LATER)
282 if (mini2440_load_from_nand(s->nand, 0, S3C_SRAM_BASE_NANDBOOT, S3C_SRAM_SIZE) > 0) {
283 s->cpu->env->regs[15] = S3C_SRAM_BASE_NANDBOOT; /* start address, u-boot relocating code */
284 mini2440_printf("4KB SteppingStone loaded from NAND\n");
286 #endif
289 * if a u--boot is available as a file, we always use it
292 image_size = load_image("mini2440/u-boot.bin", qemu_get_ram_ptr(0x03f80000));
293 if (image_size < 0)
294 image_size = load_image("u-boot.bin", qemu_get_ram_ptr(0x03f80000));
295 if (image_size > 0) {
296 if (image_size & (512 -1)) /* round size to a NAND block size */
297 image_size = (image_size + 512) & ~(512-1);
298 mini2440_printf("loaded override u-boot (size %x)\n", image_size);
299 s->cpu->env->regs[15] = S3C_RAM_BASE | 0x03f80000; /* start address, u-boot already relocated */
304 * if a kernel was explicitly specified, we load it too
306 if (s->kernel) {
307 image_size = load_image(s->kernel, qemu_get_ram_ptr(0x02000000));
308 if (image_size > 0) {
309 if (image_size & (512 -1)) /* round size to a NAND block size */
310 image_size = (image_size + 512) & ~(512-1);
311 mini2440_printf("loaded %s (size %x)\n", s->kernel, image_size);
317 /* Typical touchscreen calibration values */
318 static const int mini2440_ts_scale[6] = {
319 0, (90 - 960) * 256 / 1021, -90 * 256 * 32,
320 (940 - 75) * 256 / 1021, 0, 75 * 256 * 32,
323 static void
324 mini2440_init(
325 ram_addr_t ram_size,
326 const char *boot_device,
327 const char *kernel_filename,
328 const char *kernel_cmdline,
329 const char *initrd_filename,
330 const char *cpu_model)
332 struct mini2440_board_s *mini =
333 (struct mini2440_board_s *)qemu_mallocz(sizeof(struct mini2440_board_s));
334 int sd_idx = drive_get_index(IF_SD, 0, 0);
335 int nor_idx = drive_get_index(IF_PFLASH, 0, 0);
336 int nand_idx = drive_get_index(IF_MTD, 0, 0);
337 int nand_cid = 0x76; // 128MB flash == 0xf1
339 mini->ram = 0x04000000;
340 mini->kernel = kernel_filename;
341 mini->mmc = sd_idx >= 0 ? sd_init(drives_table[sd_idx].bdrv, 0) : NULL;
342 mini->boot_mode = BOOT_NAND;
344 if (cpu_model && strcmp(cpu_model, "arm920t")) {
345 mini2440_printf("This platform requires an ARM920T core\n");
346 exit(2);
349 uint32_t sram_base = 0;
351 * Use an environment variable to set the boot mode "switch"
353 const char * boot_mode = getenv("MINI2440_BOOT");
355 if (boot_mode) {
356 if (!strcasecmp(boot_mode, "nor")) {
357 if (nor_idx < 0) {
358 printf("%s MINI2440_BOOT(nor) error, no flash file specified", __func__);
359 abort();
360 } else
361 mini->boot_mode = BOOT_NOR;
362 } else if (!strcasecmp(boot_mode, "nand")) {
363 if (nor_idx < 0) {
364 printf("%s MINI2440_BOOT(nand) error, no flash file specified", __func__);
365 abort();
366 } else
367 mini->boot_mode = BOOT_NOR;
368 } else
369 printf("%s MINI2440_BOOT(%s) ignored, invalid value", __func__, boot_mode);
371 printf("%s: Boot mode: %s\n", __func__, mini->boot_mode == BOOT_NOR ? "NOR": "NAND");
372 /* Check the boot mode */
373 switch (mini->boot_mode) {
374 case BOOT_NAND:
375 sram_base = S3C_SRAM_BASE_NANDBOOT;
376 int size = bdrv_getlength(drives_table[nand_idx].bdrv);
377 switch (size) {
378 case 2 * 65536 * (512 + 16):
379 nand_cid = 0x76;
380 break;
381 case 4 * 65536 * (512 + 16):
382 nand_cid = 0xf1;
383 break;
384 default:
385 printf("%s: Unknown NAND size/id %d (%dMB) defaulting to old 64MB\n",
386 __func__, size, ((size / (512 + 16)) * 512) / 1024 / 1024);
387 break;
389 break;
390 case BOOT_NOR: {
391 sram_base = S3C_SRAM_BASE_NORBOOT;
392 int nor_size = bdrv_getlength(drives_table[nor_idx].bdrv);
393 if (nor_size > FLASH_NOR_SIZE)
394 printf("%s: file too big (2MBytes)\n", __func__);
395 printf("%s: Register parallel flash %d size 0x%x '%s'\n", __func__,
396 nor_idx, nor_size,
397 bdrv_get_device_name(drives_table[nor_idx].bdrv));
398 } break;
401 mini->cpu = s3c24xx_init(S3C_CPU_2440, 12000000 /* 12 mhz */, mini->ram, sram_base, mini->mmc);
403 /* Setup peripherals */
404 mini2440_gpio_setup(mini);
406 mini->eeprom = ee24c08_init(s3c_i2c_bus(mini->cpu->i2c));
409 NICInfo* nd;
410 nd = &nd_table[0];
411 if (!nd->model)
412 nd->model = "dm9000";
413 if (strcmp(nd->model, "dm9000") == 0) {
414 dm9000_init(nd, 0x20000000, 0x300, 0x304, s3c_gpio_in_get(mini->cpu->io)[MINI2440_IRQ_DM9000]);
418 s3c_adc_setscale(mini->cpu->adc, mini2440_ts_scale);
420 /* Setup initial (reset) machine state */
421 qemu_register_reset(mini2440_reset, mini);
423 // Original 64MB NAND (obsolete part)
424 // mini->nand = nand_init(NAND_MFR_SAMSUNG, 0x76);
425 // 128MB nand -- the hardware can also have 256, 1GB parts
426 mini->nand = nand_init(NAND_MFR_SAMSUNG, nand_cid);
427 mini->cpu->nand->reg(mini->cpu->nand, mini->nand);
429 mini->nor = pflash_cfi02_register(0,
430 qemu_ram_alloc(FLASH_NOR_SIZE),
431 nor_idx != -1 ? drives_table[nor_idx].bdrv : NULL, (64 * 1024),
432 FLASH_NOR_SIZE >> 16,
433 1, 2, 0x0000, 0x0000, 0x0000, 0x0000,
434 0x555, 0x2aa);
436 mini2440_reset(mini);
439 QEMUMachine mini2440_machine = {
440 "mini2440",
441 "MINI2440 Chinese Samsung SoC dev board (S3C2440A)",
442 .init = mini2440_init,