/* * Copyright (C) yajin 2008 * * This file is part of the virtualmips distribution. * See LICENSE file for terms of the license. */ #define _GNU_SOURCE #include #include #include #include #include "vp_lock.h" #include "utils.h" #include "mips.h" #include "vm.h" #include "cpu.h" #include "mips_exec.h" #include "debug.h" #include "pavo.h" #include "device.h" #include "dev_cs8900.h" #include "mips_jit.h" #define MIPS_TIMER_INTERRUPT 7 extern m_uint32_t jz4740_int_table[JZ4740_INT_INDEX_MAX]; int dev_jz4740_gpio_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_uart_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len, u_int irq, vtty_t * vtty); int dev_jz4740_cpm_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_emc_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_rtc_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_wdt_tcu_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_int_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_dma_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); int dev_jz4740_lcd_init (vm_instance_t * vm, char *name, m_pa_t paddr, m_uint32_t len); void dev_jz4740_gpio_setirq (int irq); void dev_jz4740_gpio_clearirq (int irq); /* Initialize default parameters for pavo */ static void pavo_init_defaults (pavo_t * pavo) { vm_instance_t *vm = pavo->vm; if (vm->configure_filename == NULL) vm->configure_filename = strdup (PAVO_DEFAULT_CONFIG_FILE); vm->ram_size = PAVO_DEFAULT_RAM_SIZE; vm->boot_method = PAVO_DEFAULT_BOOT_METHOD; vm->kernel_filename = strdup (PAVO_DEFAULT_KERNEL_FILENAME); } int pavo_init_cs8900 (pavo_t * pavo, char *name, m_pa_t paddr, m_uint32_t len, int irq_no) { struct vm_instance *vm = pavo->vm; int nio_type = -1; netio_desc_t *nio; int count; char *tokens[10]; struct cs8900_data *d; if ((count = m_strsplit (pavo->cs8900_iotype, ':', tokens, 10)) < 2) { vm_error (vm, "unable to parse NIO description '%s'.\n", pavo->cs8900_iotype); return (-1); } nio_type = netio_get_type (tokens[0]); switch (nio_type) { case NETIO_TYPE_TAP: nio = netio_desc_create_tap (name, tokens[1]); break; //case NETIO_TYPE_LINUX_ETH: // nio=netio_desc_create_lnxeth(name,tokens[1]); // break; default: return (-1); } if (!nio) { vm_error (vm, "unable to create NETIO descriptor %s\n", tokens[0]); return (-1); } d = dev_cs8900_init (vm, name, paddr, len, irq_no); if (!d) { vm_error (vm, "unable to int cs8900\n"); return (-1); } if (dev_cs8900_set_nio (d, nio) == -1) { vm_error (vm, "unable to set cs8900 nio \n"); return (-1); } return 0; } /* Initialize the PAVO Platform (MIPS) */ static int pavo_init_platform (pavo_t * pavo) { struct vm_instance *vm = pavo->vm; cpu_mips_t *cpu0; void *(*cpu_run_fn) (void *); vm_init_vtty (vm); /* Create a CPU group */ vm->cpu_group = cpu_group_create ("System CPU"); /* Initialize the virtual MIPS processor */ if (!(cpu0 = cpu_create (vm, CPU_TYPE_MIPS32, 0))) { vm_error (vm, "unable to create CPU0!\n"); return (-1); } /* Add this CPU to the system CPU group */ cpu_group_add (vm->cpu_group, cpu0); vm->boot_cpu = cpu0; cpu_run_fn = (void *) mips_exec_run_cpu; /* create the CPU thread execution */ if (pthread_create (&cpu0->cpu_thread, NULL, cpu_run_fn, cpu0) != 0) { fprintf (stderr, "cpu_create: unable to create thread for CPU%u\n", 0); free (cpu0); return (-1); } cpu0->addr_bus_mask = PAVO_ADDR_BUS_MASK; /* Initialize RAM */ vm_ram_init (vm, 0x00000000ULL); /*create 1GB nand flash */ if ((vm->flash_size == 0x400) && (vm->flash_type = FLASH_TYPE_NAND_FLASH)) if (dev_nand_flash_1g_init (vm, "NAND FLASH 1G", NAND_DATAPORT, 0x10004, &(pavo->nand_flash)) == -1) return (-1); if (dev_jz4740_gpio_init (vm, "JZ4740 GPIO", JZ4740_GPIO_BASE, JZ4740_GPIO_SIZE) == -1) return (-1); if (dev_jz4740_uart_init (vm, "JZ4740 UART0", JZ4740_UART0_BASE, JZ4740_UART0_SIZE, 9, vm->vtty_con1) == -1) return (-1); if (dev_jz4740_uart_init (vm, "JZ4740 UART1", JZ4740_UART1_BASE, JZ4740_UART1_SIZE, 8, vm->vtty_con2) == -1) return (-1); if (dev_jz4740_cpm_init (vm, "JZ4740 CPM", JZ4740_CPM_BASE, JZ4740_CPM_SIZE) == -1) return (-1); if (dev_jz4740_emc_init (vm, "JZ4740 EMC", JZ4740_EMC_BASE, JZ4740_EMC_SIZE) == -1) return (-1); if (dev_jz4740_rtc_init (vm, "JZ4740 RTC", JZ4740_RTC_BASE, JZ4740_RTC_SIZE) == -1) return (-1); if (dev_jz4740_wdt_tcu_init (vm, "JZ4740 WDT/TCU", JZ4740_WDT_TCU_BASE, JZ4740_WDT_TCU_SIZE) == -1) return (-1); if (dev_jz4740_int_init (vm, "JZ4740 INT", JZ4740_INT_BASE, JZ4740_INT_SIZE) == -1) return (-1); if (dev_jz4740_dma_init (vm, "JZ4740 DMA", JZ4740_DMA_BASE, JZ4740_DMA_SIZE) == -1) return (-1); if (pavo->cs8900_enable == 1) { if (pavo_init_cs8900 (pavo, "CS8900A", CS8900_IO_BASE, CS8900_SIZE, CS8900_DEFAULT_IRQ) == -1) return (-1); } /*LCD*/ #ifdef SIM_LCD if (dev_jz4740_lcd_init (vm, "JZ4740 LCD", JZ4740_LCD_BASE, JZ4740_LCD_SIZE) == -1) return (-1); #endif return (0); } static int pavo_boot (pavo_t * pavo) { vm_instance_t *vm = pavo->vm; if (!vm->boot_cpu) return (-1); return jz4740_reset (vm); } void pavo_clear_irq (vm_instance_t * vm, u_int irq) { m_uint32_t irq_mask; irq_mask = 1 << irq; /*clear ISR and IPR */ jz4740_int_table[INTC_ISR / 4] &= ~irq_mask; jz4740_int_table[INTC_IPR / 4] &= ~irq_mask; } /*map irq to soc irq*/ int forced_inline plat_soc_irq (u_int irq) { if ((irq >= 48) && (irq <= 175)) { dev_jz4740_gpio_setirq (irq); /*GPIO IRQ */ if ((irq >= 48) && (irq <= 79)) irq = IRQ_GPIO0; else if ((irq >= 80) && (irq <= 111)) irq = IRQ_GPIO1; else if ((irq >= 112) && (irq <= 143)) irq = IRQ_GPIO2; else if ((irq >= 144) && (irq <= 175)) irq = IRQ_GPIO3; } return irq; } void pavo_set_irq (vm_instance_t * vm, u_int irq) { m_uint32_t irq_mask; irq = plat_soc_irq (irq); irq_mask = 1 << irq; jz4740_int_table[INTC_ISR / 4] |= irq_mask; /*first check ICMR. masked interrupt is **invisible** to cpu */ if (unlikely (jz4740_int_table[INTC_IMR / 4] & irq_mask)) { /*the irq is masked. clear IPR */ jz4740_int_table[INTC_IPR / 4] &= ~irq_mask; } else { /*the irq is not masked */ /*set IPR */ /* * we set IPR, not *or* . yajin * * JZ Kernel 'plat_irq_dispatch' determine which is the highest priority interrupt * and handle. * It uses a function ffs to find first set irq from least bit to highest bit. * 260 irq = ffs(intc_ipr) - 1; * * That means when tcu0 irq and gpio1 irq occurs at the same time ,INTC_IPR=0x8800000 * and irq handler will handle tcu0 irq(bit 23) not gpio1 irq(bit 27). * * In pavo gpio1->cs8900 int * * TCU0 irq occurs every 10 ms and gpio1 occurs about 10ms (cs8900 has received a packet * or has txed a packet), jz kernel always handle tcu0 irq. gpio1 irq is hungry. So I just set * jz4740_int_table[INTC_IPR/4]= irq_mask not or(|) irq_mask. TCU0 irq may be lost. However, * gpio1 irq is not so ofen so it is not a big problem. * * In emulator, irq is not a good method for hardware to tell kernel something has happened. * Emulator likes polling more than interrupt :) . * */ jz4740_int_table[INTC_IPR / 4] = irq_mask; mips_set_irq (vm->boot_cpu, JZ4740_INT_TO_MIPS); mips_update_irq_flag (vm->boot_cpu); } } COMMON_CONFIG_INFO_ARRAY; static void printf_configure (pavo_t * pavo) { vm_instance_t *vm = pavo->vm; PRINT_COMMON_CONFIG_OPTION; /*print other configure information here */ if (pavo->cs8900_enable == 1) { printf ("CS8900 net card enabled\n"); printf ("CS8900 iotype %s \n", pavo->cs8900_iotype); } else printf ("CS8900 net card disenabled\n"); } static void pavo_parse_configure (pavo_t * pavo) { vm_instance_t *vm = pavo->vm; cfg_opt_t opts[] = { COMMON_CONFIG_OPTION /*add other configure information here */ CFG_SIMPLE_INT ("cs8900_enable", &(pavo->cs8900_enable)), CFG_SIMPLE_STR ("cs8900_iotype", &(pavo->cs8900_iotype)), CFG_SIMPLE_INT ("jit_use", &(vm->jit_use)), CFG_END () }; cfg_t *cfg; cfg = cfg_init (opts, 0); cfg_parse (cfg, vm->configure_filename); cfg_free (cfg); VALID_COMMON_CONFIG_OPTION; /*add other configure information validation here */ if (vm->boot_method == BOOT_BINARY) { ASSERT (vm->boot_from == 2, "boot_from must be 2(NAND Flash)\n pavo only can boot from NAND Flash.\n"); } if (vm->flash_size != 0) { ASSERT (vm->flash_size == 4, "flash_size should be 4.\n We only support 4MB NOR flash emulation\n"); } if (pavo->cs8900_enable == 1) { ASSERT (pavo->cs8900_iotype != NULL, "You must set cs8900_enable \n"); } if (vm->jit_use == 1) { ASSERT (JIT_SUPPORT == 1, "You must compile with JIT Support to use jit. \n"); } /*Print the configure information */ printf_configure (pavo); } /* Clear timer interrupt */ void clear_timer_irq (cpu_mips_t *cpu) { mips_clear_irq (cpu, MIPS_TIMER_INTERRUPT); mips_update_irq_flag (cpu); } /* Create a router instance */ vm_instance_t *create_instance (char *configure_filename) { pavo_t *pavo; char *name; if (!(pavo = malloc (sizeof (*pavo)))) { fprintf (stderr, "ADM5120': Unable to create new instance!\n"); return NULL; } memset (pavo, 0, sizeof (*pavo)); name = strdup ("pavo"); if (!(pavo->vm = vm_create (name, VM_TYPE_PAVO))) { fprintf (stderr, "PAVO : unable to create VM instance!\n"); goto err_vm; } free (name); if (configure_filename != NULL) pavo->vm->configure_filename = strdup (configure_filename); pavo_init_defaults (pavo); pavo_parse_configure (pavo); /*init gdb debug */ vm_debug_init (pavo->vm); pavo->vm->hw_data = pavo; return (pavo->vm); err_vm: free (name); free (pavo); return NULL; } int init_instance (vm_instance_t * vm) { pavo_t *pavo = VM_PAVO (vm); if (pavo_init_platform (pavo) == -1) { vm_error (vm, "unable to initialize the platform hardware.\n"); return (-1); } /* IRQ routing */ vm->set_irq = pavo_set_irq; vm->clear_irq = pavo_clear_irq; return (pavo_boot (pavo)); }