diff options
Diffstat (limited to 'arch/mips/loongson')
-rw-r--r-- | arch/mips/loongson/Kconfig | 47 | ||||
-rw-r--r-- | arch/mips/loongson/Makefile | 6 | ||||
-rw-r--r-- | arch/mips/loongson/Platform | 1 | ||||
-rw-r--r-- | arch/mips/loongson/common/Makefile | 5 | ||||
-rw-r--r-- | arch/mips/loongson/common/dma-swiotlb.c | 136 | ||||
-rw-r--r-- | arch/mips/loongson/common/env.c | 67 | ||||
-rw-r--r-- | arch/mips/loongson/common/init.c | 11 | ||||
-rw-r--r-- | arch/mips/loongson/common/machtype.c | 4 | ||||
-rw-r--r-- | arch/mips/loongson/common/mem.c | 42 | ||||
-rw-r--r-- | arch/mips/loongson/common/pci.c | 6 | ||||
-rw-r--r-- | arch/mips/loongson/common/reset.c | 21 | ||||
-rw-r--r-- | arch/mips/loongson/common/serial.c | 26 | ||||
-rw-r--r-- | arch/mips/loongson/common/setup.c | 8 | ||||
-rw-r--r-- | arch/mips/loongson/common/uart_base.c | 9 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/Makefile | 6 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/irq.c | 126 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/smp.c | 443 | ||||
-rw-r--r-- | arch/mips/loongson/loongson-3/smp.h | 29 |
18 files changed, 958 insertions, 35 deletions
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig index 263beb9322a8..7397be226a06 100644 --- a/arch/mips/loongson/Kconfig +++ b/arch/mips/loongson/Kconfig @@ -59,6 +59,36 @@ config LEMOTE_MACH2F These family machines include fuloong2f mini PC, yeeloong2f notebook, LingLoong allinone PC and so forth. + +config LEMOTE_MACH3A + bool "Lemote Loongson 3A family machines" + select ARCH_SPARSEMEM_ENABLE + select GENERIC_ISA_DMA_SUPPORT_BROKEN + select GENERIC_HARDIRQS_NO__DO_IRQ + select BOOT_ELF32 + select BOARD_SCACHE + select CSRC_R4K + select CEVT_R4K + select CPU_HAS_WB + select HW_HAS_PCI + select ISA + select HT_PCI + select I8259 + select IRQ_CPU + select NR_CPUS_DEFAULT_4 + select SYS_HAS_CPU_LOONGSON3 + select SYS_HAS_EARLY_PRINTK + select SYS_SUPPORTS_SMP + select SYS_SUPPORTS_HOTPLUG_CPU + select SYS_SUPPORTS_64BIT_KERNEL + select SYS_SUPPORTS_HIGHMEM + select SYS_SUPPORTS_LITTLE_ENDIAN + select LOONGSON_MC146818 + select ZONE_DMA32 + select LEFI_FIRMWARE_INTERFACE + help + Lemote Loongson 3A family machines utilize the 3A revision of + Loongson processor and RS780/SBX00 chipset. endchoice config CS5536 @@ -86,8 +116,25 @@ config LOONGSON_UART_BASE default y depends on EARLY_PRINTK || SERIAL_8250 +config IOMMU_HELPER + bool + +config NEED_SG_DMA_LENGTH + bool + +config SWIOTLB + bool "Soft IOMMU Support for All-Memory DMA" + default y + depends on CPU_LOONGSON3 + select IOMMU_HELPER + select NEED_SG_DMA_LENGTH + select NEED_DMA_MAP_STATE + config LOONGSON_MC146818 bool default n +config LEFI_FIRMWARE_INTERFACE + bool + endif # MACH_LOONGSON diff --git a/arch/mips/loongson/Makefile b/arch/mips/loongson/Makefile index 0dc0055754cd..7429994e7604 100644 --- a/arch/mips/loongson/Makefile +++ b/arch/mips/loongson/Makefile @@ -15,3 +15,9 @@ obj-$(CONFIG_LEMOTE_FULOONG2E) += fuloong-2e/ # obj-$(CONFIG_LEMOTE_MACH2F) += lemote-2f/ + +# +# All Loongson-3 family machines +# + +obj-$(CONFIG_CPU_LOONGSON3) += loongson-3/ diff --git a/arch/mips/loongson/Platform b/arch/mips/loongson/Platform index 29692e5433b1..6205372b6c2d 100644 --- a/arch/mips/loongson/Platform +++ b/arch/mips/loongson/Platform @@ -30,3 +30,4 @@ platform-$(CONFIG_MACH_LOONGSON) += loongson/ cflags-$(CONFIG_MACH_LOONGSON) += -I$(srctree)/arch/mips/include/asm/mach-loongson -mno-branch-likely load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000 load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000 +load-$(CONFIG_CPU_LOONGSON3) += 0xffffffff80200000 diff --git a/arch/mips/loongson/common/Makefile b/arch/mips/loongson/common/Makefile index 9e4484ccbb03..0bb9cc9dc621 100644 --- a/arch/mips/loongson/common/Makefile +++ b/arch/mips/loongson/common/Makefile @@ -26,3 +26,8 @@ obj-$(CONFIG_CS5536) += cs5536/ # obj-$(CONFIG_LOONGSON_SUSPEND) += pm.o + +# +# Big Memory (SWIOTLB) Support +# +obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o diff --git a/arch/mips/loongson/common/dma-swiotlb.c b/arch/mips/loongson/common/dma-swiotlb.c new file mode 100644 index 000000000000..c2be01f91575 --- /dev/null +++ b/arch/mips/loongson/common/dma-swiotlb.c @@ -0,0 +1,136 @@ +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> +#include <linux/swiotlb.h> +#include <linux/bootmem.h> + +#include <asm/bootinfo.h> +#include <boot_param.h> +#include <dma-coherence.h> + +static void *loongson_dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs) +{ + void *ret; + + if (dma_alloc_from_coherent(dev, size, dma_handle, &ret)) + return ret; + + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM); + +#ifdef CONFIG_ISA + if (dev == NULL) + gfp |= __GFP_DMA; + else +#endif +#ifdef CONFIG_ZONE_DMA + if (dev->coherent_dma_mask < DMA_BIT_MASK(32)) + gfp |= __GFP_DMA; + else +#endif +#ifdef CONFIG_ZONE_DMA32 + if (dev->coherent_dma_mask < DMA_BIT_MASK(40)) + gfp |= __GFP_DMA32; + else +#endif + ; + gfp |= __GFP_NORETRY; + + ret = swiotlb_alloc_coherent(dev, size, dma_handle, gfp); + mb(); + return ret; +} + +static void loongson_dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs) +{ + int order = get_order(size); + + if (dma_release_from_coherent(dev, order, vaddr)) + return; + + swiotlb_free_coherent(dev, size, vaddr, dma_handle); +} + +static dma_addr_t loongson_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + dma_addr_t daddr = swiotlb_map_page(dev, page, offset, size, + dir, attrs); + mb(); + return daddr; +} + +static int loongson_dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + int r = swiotlb_map_sg_attrs(dev, sg, nents, dir, NULL); + mb(); + + return r; +} + +static void loongson_dma_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir) +{ + swiotlb_sync_single_for_device(dev, dma_handle, size, dir); + mb(); +} + +static void loongson_dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, int nents, + enum dma_data_direction dir) +{ + swiotlb_sync_sg_for_device(dev, sg, nents, dir); + mb(); +} + +static int loongson_dma_set_mask(struct device *dev, u64 mask) +{ + if (mask > DMA_BIT_MASK(loongson_sysconf.dma_mask_bits)) { + *dev->dma_mask = DMA_BIT_MASK(loongson_sysconf.dma_mask_bits); + return -EIO; + } + + *dev->dma_mask = mask; + + return 0; +} + +dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) +{ + return paddr; +} + +phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) +{ + return daddr; +} + +static struct dma_map_ops loongson_dma_map_ops = { + .alloc = loongson_dma_alloc_coherent, + .free = loongson_dma_free_coherent, + .map_page = loongson_dma_map_page, + .unmap_page = swiotlb_unmap_page, + .map_sg = loongson_dma_map_sg, + .unmap_sg = swiotlb_unmap_sg_attrs, + .sync_single_for_cpu = swiotlb_sync_single_for_cpu, + .sync_single_for_device = loongson_dma_sync_single_for_device, + .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, + .sync_sg_for_device = loongson_dma_sync_sg_for_device, + .mapping_error = swiotlb_dma_mapping_error, + .dma_supported = swiotlb_dma_supported, + .set_dma_mask = loongson_dma_set_mask +}; + +void __init plat_swiotlb_setup(void) +{ + swiotlb_init(1); + mips_dma_map_ops = &loongson_dma_map_ops; +} diff --git a/arch/mips/loongson/common/env.c b/arch/mips/loongson/common/env.c index 0a18fcf2d372..0c543eae49bf 100644 --- a/arch/mips/loongson/common/env.c +++ b/arch/mips/loongson/common/env.c @@ -18,29 +18,30 @@ * option) any later version. */ #include <linux/module.h> - #include <asm/bootinfo.h> - #include <loongson.h> +#include <boot_param.h> -unsigned long cpu_clock_freq; +u32 cpu_clock_freq; EXPORT_SYMBOL(cpu_clock_freq); -unsigned long memsize, highmemsize; +struct efi_memory_map_loongson *loongson_memmap; +struct loongson_system_configuration loongson_sysconf; #define parse_even_earlier(res, option, p) \ do { \ unsigned int tmp __maybe_unused; \ \ if (strncmp(option, (char *)p, strlen(option)) == 0) \ - tmp = strict_strtol((char *)p + strlen(option"="), 10, &res); \ + tmp = kstrtou32((char *)p + strlen(option"="), 10, &res); \ } while (0) void __init prom_init_env(void) { /* pmon passes arguments in 32bit pointers */ - int *_prom_envp; - unsigned long bus_clock; unsigned int processor_id; + +#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE + int *_prom_envp; long l; /* firmware arguments are initialized in head.S */ @@ -48,7 +49,6 @@ void __init prom_init_env(void) l = (long)*_prom_envp; while (l != 0) { - parse_even_earlier(bus_clock, "busclock", l); parse_even_earlier(cpu_clock_freq, "cpuclock", l); parse_even_earlier(memsize, "memsize", l); parse_even_earlier(highmemsize, "highmemsize", l); @@ -57,8 +57,48 @@ void __init prom_init_env(void) } if (memsize == 0) memsize = 256; - if (bus_clock == 0) - bus_clock = 66000000; + pr_info("memsize=%u, highmemsize=%u\n", memsize, highmemsize); +#else + struct boot_params *boot_p; + struct loongson_params *loongson_p; + struct efi_cpuinfo_loongson *ecpu; + struct irq_source_routing_table *eirq_source; + + /* firmware arguments are initialized in head.S */ + boot_p = (struct boot_params *)fw_arg2; + loongson_p = &(boot_p->efi.smbios.lp); + + ecpu = (struct efi_cpuinfo_loongson *) + ((u64)loongson_p + loongson_p->cpu_offset); + eirq_source = (struct irq_source_routing_table *) + ((u64)loongson_p + loongson_p->irq_offset); + loongson_memmap = (struct efi_memory_map_loongson *) + ((u64)loongson_p + loongson_p->memory_offset); + + cpu_clock_freq = ecpu->cpu_clock_freq; + loongson_sysconf.cputype = ecpu->cputype; + loongson_sysconf.nr_cpus = ecpu->nr_cpus; + if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0) + loongson_sysconf.nr_cpus = NR_CPUS; + + loongson_sysconf.pci_mem_start_addr = eirq_source->pci_mem_start_addr; + loongson_sysconf.pci_mem_end_addr = eirq_source->pci_mem_end_addr; + loongson_sysconf.pci_io_base = eirq_source->pci_io_start_addr; + loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits; + if (loongson_sysconf.dma_mask_bits < 32 || + loongson_sysconf.dma_mask_bits > 64) + loongson_sysconf.dma_mask_bits = 32; + + loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm; + loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown; + loongson_sysconf.suspend_addr = boot_p->reset_system.DoSuspend; + + loongson_sysconf.ht_control_base = 0x90000EFDFB000000; + loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios; + pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n", + loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr, + loongson_sysconf.vgabios_addr); +#endif if (cpu_clock_freq == 0) { processor_id = (¤t_cpu_data)->processor_id; switch (processor_id & PRID_REV_MASK) { @@ -68,12 +108,13 @@ void __init prom_init_env(void) case PRID_REV_LOONGSON2F: cpu_clock_freq = 797000000; break; + case PRID_REV_LOONGSON3A: + cpu_clock_freq = 900000000; + break; default: cpu_clock_freq = 100000000; break; } } - - pr_info("busclock=%ld, cpuclock=%ld, memsize=%ld, highmemsize=%ld\n", - bus_clock, cpu_clock_freq, memsize, highmemsize); + pr_info("CpuClock = %u\n", cpu_clock_freq); } diff --git a/arch/mips/loongson/common/init.c b/arch/mips/loongson/common/init.c index ae7af1fd5d59..f37fe5413b73 100644 --- a/arch/mips/loongson/common/init.c +++ b/arch/mips/loongson/common/init.c @@ -9,6 +9,7 @@ */ #include <linux/bootmem.h> +#include <asm/smp-ops.h> #include <loongson.h> @@ -17,10 +18,6 @@ unsigned long __maybe_unused _loongson_addrwincfg_base; void __init prom_init(void) { - /* init base address of io space */ - set_io_port_base((unsigned long) - ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE)); - #ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG _loongson_addrwincfg_base = (unsigned long) ioremap(LOONGSON_ADDRWINCFG_BASE, LOONGSON_ADDRWINCFG_SIZE); @@ -28,10 +25,16 @@ void __init prom_init(void) prom_init_cmdline(); prom_init_env(); + + /* init base address of io space */ + set_io_port_base((unsigned long) + ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE)); + prom_init_memory(); /*init the uart base address */ prom_init_uart_base(); + register_smp_ops(&loongson3_smp_ops); } void __init prom_free_prom_memory(void) diff --git a/arch/mips/loongson/common/machtype.c b/arch/mips/loongson/common/machtype.c index 4becd4f9ef2e..1a4797984b8d 100644 --- a/arch/mips/loongson/common/machtype.c +++ b/arch/mips/loongson/common/machtype.c @@ -27,6 +27,10 @@ static const char *system_types[] = { [MACH_DEXXON_GDIUM2F10] "dexxon-gdium-2f", [MACH_LEMOTE_NAS] "lemote-nas-2f", [MACH_LEMOTE_LL2F] "lemote-lynloong-2f", + [MACH_LEMOTE_A1004] "lemote-3a-notebook-a1004", + [MACH_LEMOTE_A1101] "lemote-3a-itx-a1101", + [MACH_LEMOTE_A1201] "lemote-2gq-notebook-a1201", + [MACH_LEMOTE_A1205] "lemote-2gq-aio-a1205", [MACH_LOONGSON_END] NULL, }; diff --git a/arch/mips/loongson/common/mem.c b/arch/mips/loongson/common/mem.c index 8626a42f5b94..b01d52473da8 100644 --- a/arch/mips/loongson/common/mem.c +++ b/arch/mips/loongson/common/mem.c @@ -11,9 +11,14 @@ #include <asm/bootinfo.h> #include <loongson.h> +#include <boot_param.h> #include <mem.h> #include <pci.h> +#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE + +u32 memsize, highmemsize; + void __init prom_init_memory(void) { add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM); @@ -49,6 +54,43 @@ void __init prom_init_memory(void) #endif /* !CONFIG_64BIT */ } +#else /* CONFIG_LEFI_FIRMWARE_INTERFACE */ + +void __init prom_init_memory(void) +{ + int i; + u32 node_id; + u32 mem_type; + + /* parse memory information */ + for (i = 0; i < loongson_memmap->nr_map; i++) { + node_id = loongson_memmap->map[i].node_id; + mem_type = loongson_memmap->map[i].mem_type; + + if (node_id == 0) { + switch (mem_type) { + case SYSTEM_RAM_LOW: + add_memory_region(loongson_memmap->map[i].mem_start, + (u64)loongson_memmap->map[i].mem_size << 20, + BOOT_MEM_RAM); + break; + case SYSTEM_RAM_HIGH: + add_memory_region(loongson_memmap->map[i].mem_start, + (u64)loongson_memmap->map[i].mem_size << 20, + BOOT_MEM_RAM); + break; + case MEM_RESERVED: + add_memory_region(loongson_memmap->map[i].mem_start, + (u64)loongson_memmap->map[i].mem_size << 20, + BOOT_MEM_RESERVED); + break; + } + } + } +} + +#endif /* CONFIG_LEFI_FIRMWARE_INTERFACE */ + /* override of arch/mips/mm/cache.c: __uncached_access */ int __uncached_access(struct file *file, unsigned long addr) { diff --git a/arch/mips/loongson/common/pci.c b/arch/mips/loongson/common/pci.c index fa7784459721..003ab4e618b3 100644 --- a/arch/mips/loongson/common/pci.c +++ b/arch/mips/loongson/common/pci.c @@ -11,6 +11,7 @@ #include <pci.h> #include <loongson.h> +#include <boot_param.h> static struct resource loongson_pci_mem_resource = { .name = "pci memory space", @@ -82,7 +83,10 @@ static int __init pcibios_init(void) setup_pcimap(); loongson_pci_controller.io_map_base = mips_io_port_base; - +#ifdef CONFIG_LEFI_FIRMWARE_INTERFACE + loongson_pci_mem_resource.start = loongson_sysconf.pci_mem_start_addr; + loongson_pci_mem_resource.end = loongson_sysconf.pci_mem_end_addr; +#endif register_pci_controller(&loongson_pci_controller); return 0; diff --git a/arch/mips/loongson/common/reset.c b/arch/mips/loongson/common/reset.c index 65bfbb5d06f4..a60715e11306 100644 --- a/arch/mips/loongson/common/reset.c +++ b/arch/mips/loongson/common/reset.c @@ -16,6 +16,7 @@ #include <asm/reboot.h> #include <loongson.h> +#include <boot_param.h> static inline void loongson_reboot(void) { @@ -37,17 +38,37 @@ static inline void loongson_reboot(void) static void loongson_restart(char *command) { +#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE /* do preparation for reboot */ mach_prepare_reboot(); /* reboot via jumping to boot base address */ loongson_reboot(); +#else + void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr; + + fw_restart(); + while (1) { + if (cpu_wait) + cpu_wait(); + } +#endif } static void loongson_poweroff(void) { +#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE mach_prepare_shutdown(); unreachable(); +#else + void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr; + + fw_poweroff(); + while (1) { + if (cpu_wait) + cpu_wait(); + } +#endif } static void loongson_halt(void) diff --git a/arch/mips/loongson/common/serial.c b/arch/mips/loongson/common/serial.c index 5f2b78ae97cc..bd2b7095b6dc 100644 --- a/arch/mips/loongson/common/serial.c +++ b/arch/mips/loongson/common/serial.c @@ -19,19 +19,19 @@ #include <loongson.h> #include <machine.h> -#define PORT(int) \ +#define PORT(int, clk) \ { \ .irq = int, \ - .uartclk = 1843200, \ + .uartclk = clk, \ .iotype = UPIO_PORT, \ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \ .regshift = 0, \ } -#define PORT_M(int) \ +#define PORT_M(int, clk) \ { \ .irq = MIPS_CPU_IRQ_BASE + (int), \ - .uartclk = 3686400, \ + .uartclk = clk, \ .iotype = UPIO_MEM, \ .membase = (void __iomem *)NULL, \ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \ @@ -40,13 +40,17 @@ static struct plat_serial8250_port uart8250_data[][2] = { [MACH_LOONGSON_UNKNOWN] {}, - [MACH_LEMOTE_FL2E] {PORT(4), {} }, - [MACH_LEMOTE_FL2F] {PORT(3), {} }, - [MACH_LEMOTE_ML2F7] {PORT_M(3), {} }, - [MACH_LEMOTE_YL2F89] {PORT_M(3), {} }, - [MACH_DEXXON_GDIUM2F10] {PORT_M(3), {} }, - [MACH_LEMOTE_NAS] {PORT_M(3), {} }, - [MACH_LEMOTE_LL2F] {PORT(3), {} }, + [MACH_LEMOTE_FL2E] {PORT(4, 1843200), {} }, + [MACH_LEMOTE_FL2F] {PORT(3, 1843200), {} }, + [MACH_LEMOTE_ML2F7] {PORT_M(3, 3686400), {} }, + [MACH_LEMOTE_YL2F89] {PORT_M(3, 3686400), {} }, + [MACH_DEXXON_GDIUM2F10] {PORT_M(3, 3686400), {} }, + [MACH_LEMOTE_NAS] {PORT_M(3, 3686400), {} }, + [MACH_LEMOTE_LL2F] {PORT(3, 1843200), {} }, + [MACH_LEMOTE_A1004] {PORT_M(2, 33177600), {} }, + [MACH_LEMOTE_A1101] {PORT_M(2, 25000000), {} }, + [MACH_LEMOTE_A1201] {PORT_M(2, 25000000), {} }, + [MACH_LEMOTE_A1205] {PORT_M(2, 25000000), {} }, [MACH_LOONGSON_END] {}, }; diff --git a/arch/mips/loongson/common/setup.c b/arch/mips/loongson/common/setup.c index 8223f8acfd59..bb4ac922e47a 100644 --- a/arch/mips/loongson/common/setup.c +++ b/arch/mips/loongson/common/setup.c @@ -18,9 +18,6 @@ #include <linux/screen_info.h> #endif -void (*__wbflush)(void); -EXPORT_SYMBOL(__wbflush); - static void wbflush_loongson(void) { asm(".set\tpush\n\t" @@ -32,10 +29,11 @@ static void wbflush_loongson(void) ".set mips0\n\t"); } +void (*__wbflush)(void) = wbflush_loongson; +EXPORT_SYMBOL(__wbflush); + void __init plat_mem_setup(void) { - __wbflush = wbflush_loongson; - #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) conswitchp = &vga_con; diff --git a/arch/mips/loongson/common/uart_base.c b/arch/mips/loongson/common/uart_base.c index e192ad021edc..1e1eeea73fde 100644 --- a/arch/mips/loongson/common/uart_base.c +++ b/arch/mips/loongson/common/uart_base.c @@ -35,9 +35,16 @@ void prom_init_loongson_uart_base(void) case MACH_DEXXON_GDIUM2F10: case MACH_LEMOTE_NAS: default: - /* The CPU provided serial port */ + /* The CPU provided serial port (LPC) */ loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8; break; + case MACH_LEMOTE_A1004: + case MACH_LEMOTE_A1101: + case MACH_LEMOTE_A1201: + case MACH_LEMOTE_A1205: + /* The CPU provided serial port (CPU) */ + loongson_uart_base = LOONGSON_REG_BASE + 0x1e0; + break; } _loongson_uart_base = diff --git a/arch/mips/loongson/loongson-3/Makefile b/arch/mips/loongson/loongson-3/Makefile new file mode 100644 index 000000000000..70152b252ddc --- /dev/null +++ b/arch/mips/loongson/loongson-3/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Loongson-3 family machines +# +obj-y += irq.o + +obj-$(CONFIG_SMP) += smp.o diff --git a/arch/mips/loongson/loongson-3/irq.c b/arch/mips/loongson/loongson-3/irq.c new file mode 100644 index 000000000000..f240828181ff --- /dev/null +++ b/arch/mips/loongson/loongson-3/irq.c @@ -0,0 +1,126 @@ +#include <loongson.h> +#include <irq.h> +#include <linux/interrupt.h> +#include <linux/module.h> + +#include <asm/irq_cpu.h> +#include <asm/i8259.h> +#include <asm/mipsregs.h> + +unsigned int ht_irq[] = {1, 3, 4, 5, 6, 7, 8, 12, 14, 15}; + +static void ht_irqdispatch(void) +{ + unsigned int i, irq; + + irq = LOONGSON_HT1_INT_VECTOR(0); + LOONGSON_HT1_INT_VECTOR(0) = irq; /* Acknowledge the IRQs */ + + for (i = 0; i < ARRAY_SIZE(ht_irq); i++) { + if (irq & (0x1 << ht_irq[i])) + do_IRQ(ht_irq[i]); + } +} + +void mach_irq_dispatch(unsigned int pending) +{ + if (pending & CAUSEF_IP7) + do_IRQ(LOONGSON_TIMER_IRQ); +#if defined(CONFIG_SMP) + else if (pending & CAUSEF_IP6) + loongson3_ipi_interrupt(NULL); +#endif + else if (pending & CAUSEF_IP3) + ht_irqdispatch(); + else if (pending & CAUSEF_IP2) + do_IRQ(LOONGSON_UART_IRQ); + else { + pr_err("%s : spurious interrupt\n", __func__); + spurious_interrupt(); + } +} + +static struct irqaction cascade_irqaction = { + .handler = no_action, + .name = "cascade", +}; + +static inline void mask_loongson_irq(struct irq_data *d) +{ + clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); + irq_disable_hazard(); + + /* Workaround: UART IRQ may deliver to any core */ + if (d->irq == LOONGSON_UART_IRQ) { + int cpu = smp_processor_id(); + + LOONGSON_INT_ROUTER_INTENCLR = 1 << 10; + LOONGSON_INT_ROUTER_LPC = 0x10 + (1<<cpu); + } +} + +static inline void unmask_loongson_irq(struct irq_data *d) +{ + /* Workaround: UART IRQ may deliver to any core */ + if (d->irq == LOONGSON_UART_IRQ) { + int cpu = smp_processor_id(); + + LOONGSON_INT_ROUTER_INTENSET = 1 << 10; + LOONGSON_INT_ROUTER_LPC = 0x10 + (1<<cpu); + } + + set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE)); + irq_enable_hazard(); +} + + /* For MIPS IRQs which shared by all cores */ +static struct irq_chip loongson_irq_chip = { + .name = "Loongson", + .irq_ack = mask_loongson_irq, + .irq_mask = mask_loongson_irq, + .irq_mask_ack = mask_loongson_irq, + .irq_unmask = unmask_loongson_irq, + .irq_eoi = unmask_loongson_irq, +}; + +void irq_router_init(void) +{ + int i; + + /* route LPC int to cpu core0 int 0 */ + LOONGSON_INT_ROUTER_LPC = LOONGSON_INT_CORE0_INT0; + /* route HT1 int0 ~ int7 to cpu core0 INT1*/ + for (i = 0; i < 8; i++) + LOONGSON_INT_ROUTER_HT1(i) = LOONGSON_INT_CORE0_INT1; + /* enable HT1 interrupt */ + LOONGSON_HT1_INTN_EN(0) = 0xffffffff; + /* enable router interrupt intenset */ + LOONGSON_INT_ROUTER_INTENSET = + LOONGSON_INT_ROUTER_INTEN | (0xffff << 16) | 0x1 << 10; +} + +void __init mach_init_irq(void) +{ + clear_c0_status(ST0_IM | ST0_BEV); + + irq_router_init(); + mips_cpu_irq_init(); + init_i8259_irqs(); + irq_set_chip_and_handler(LOONGSON_UART_IRQ, + &loongson_irq_chip, handle_level_irq); + + /* setup HT1 irq */ + setup_irq(LOONGSON_HT1_IRQ, &cascade_irqaction); + + set_c0_status(STATUSF_IP2 | STATUSF_IP6); +} + +#ifdef CONFIG_HOTPLUG_CPU + +void fixup_irqs(void) +{ + irq_cpu_offline(); + clear_c0_status(ST0_IM); +} + +#endif diff --git a/arch/mips/loongson/loongson-3/smp.c b/arch/mips/loongson/loongson-3/smp.c new file mode 100644 index 000000000000..c665fe16d4c9 --- /dev/null +++ b/arch/mips/loongson/loongson-3/smp.c @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2010, 2011, 2012, Lemote, Inc. + * Author: Chen Huacai, chenhc@lemote.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/init.h> +#include <linux/cpu.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/cpufreq.h> +#include <asm/processor.h> +#include <asm/time.h> +#include <asm/clock.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <loongson.h> + +#include "smp.h" + +DEFINE_PER_CPU(int, cpu_state); +DEFINE_PER_CPU(uint32_t, core0_c0count); + +/* read a 32bit value from ipi register */ +#define loongson3_ipi_read32(addr) readl(addr) +/* read a 64bit value from ipi register */ +#define loongson3_ipi_read64(addr) readq(addr) +/* write a 32bit value to ipi register */ +#define loongson3_ipi_write32(action, addr) \ + do { \ + writel(action, addr); \ + __wbflush(); \ + } while (0) +/* write a 64bit value to ipi register */ +#define loongson3_ipi_write64(action, addr) \ + do { \ + writeq(action, addr); \ + __wbflush(); \ + } while (0) + +static void *ipi_set0_regs[] = { + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0), +}; + +static void *ipi_clear0_regs[] = { + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0), +}; + +static void *ipi_status0_regs[] = { + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0), +}; + +static void *ipi_en0_regs[] = { + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0), +}; + +static void *ipi_mailbox_buf[] = { + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF), + (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF), + (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF), + (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF), + (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF), +}; + +/* + * Simple enough, just poke the appropriate ipi register + */ +static void loongson3_send_ipi_single(int cpu, unsigned int action) +{ + loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]); +} + +static void +loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) +{ + unsigned int i; + + for_each_cpu(i, mask) + loongson3_ipi_write32((u32)action, ipi_set0_regs[i]); +} + +void loongson3_ipi_interrupt(struct pt_regs *regs) +{ + int i, cpu = smp_processor_id(); + unsigned int action, c0count; + + /* Load the ipi register to figure out what we're supposed to do */ + action = loongson3_ipi_read32(ipi_status0_regs[cpu]); + + /* Clear the ipi register to clear the interrupt */ + loongson3_ipi_write32((u32)action, ipi_clear0_regs[cpu]); + + if (action & SMP_RESCHEDULE_YOURSELF) + scheduler_ipi(); + + if (action & SMP_CALL_FUNCTION) + smp_call_function_interrupt(); + + if (action & SMP_ASK_C0COUNT) { + BUG_ON(cpu != 0); + c0count = read_c0_count(); + for (i = 1; i < loongson_sysconf.nr_cpus; i++) + per_cpu(core0_c0count, i) = c0count; + } +} + +#define MAX_LOOPS 1111 +/* + * SMP init and finish on secondary CPUs + */ +static void loongson3_init_secondary(void) +{ + int i; + uint32_t initcount; + unsigned int cpu = smp_processor_id(); + unsigned int imask = STATUSF_IP7 | STATUSF_IP6 | + STATUSF_IP3 | STATUSF_IP2; + + /* Set interrupt mask, but don't enable */ + change_c0_status(ST0_IM, imask); + + for (i = 0; i < loongson_sysconf.nr_cpus; i++) + loongson3_ipi_write32(0xffffffff, ipi_en0_regs[i]); + + per_cpu(cpu_state, cpu) = CPU_ONLINE; + + i = 0; + __get_cpu_var(core0_c0count) = 0; + loongson3_send_ipi_single(0, SMP_ASK_C0COUNT); + while (!__get_cpu_var(core0_c0count)) { + i++; + cpu_relax(); + } + + if (i > MAX_LOOPS) + i = MAX_LOOPS; + initcount = __get_cpu_var(core0_c0count) + i; + write_c0_count(initcount); +} + +static void loongson3_smp_finish(void) +{ + write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + local_irq_enable(); + loongson3_ipi_write64(0, + (void *)(ipi_mailbox_buf[smp_processor_id()]+0x0)); + pr_info("CPU#%d finished, CP0_ST=%x\n", + smp_processor_id(), read_c0_status()); +} + +static void __init loongson3_smp_setup(void) +{ + int i, num; + + init_cpu_possible(cpu_none_mask); + set_cpu_possible(0, true); + + __cpu_number_map[0] = 0; + __cpu_logical_map[0] = 0; + + /* For unified kernel, NR_CPUS is the maximum possible value, + * loongson_sysconf.nr_cpus is the really present value */ + for (i = 1, num = 0; i < loongson_sysconf.nr_cpus; i++) { + set_cpu_possible(i, true); + __cpu_number_map[i] = ++num; + __cpu_logical_map[num] = i; + } + pr_info("Detected %i available secondary CPU(s)\n", num); +} + +static void __init loongson3_prepare_cpus(unsigned int max_cpus) +{ + init_cpu_present(cpu_possible_mask); + per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; +} + +/* + * Setup the PC, SP, and GP of a secondary processor and start it runing! + */ +static void loongson3_boot_secondary(int cpu, struct task_struct *idle) +{ + unsigned long startargs[4]; + + pr_info("Booting CPU#%d...\n", cpu); + + /* startargs[] are initial PC, SP and GP for secondary CPU */ + startargs[0] = (unsigned long)&smp_bootstrap; + startargs[1] = (unsigned long)__KSTK_TOS(idle); + startargs[2] = (unsigned long)task_thread_info(idle); + startargs[3] = 0; + + pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n", + cpu, startargs[0], startargs[1], startargs[2]); + + loongson3_ipi_write64(startargs[3], (void *)(ipi_mailbox_buf[cpu]+0x18)); + loongson3_ipi_write64(startargs[2], (void *)(ipi_mailbox_buf[cpu]+0x10)); + loongson3_ipi_write64(startargs[1], (void *)(ipi_mailbox_buf[cpu]+0x8)); + loongson3_ipi_write64(startargs[0], (void *)(ipi_mailbox_buf[cpu]+0x0)); +} + +/* + * Final cleanup after all secondaries booted + */ +static void __init loongson3_cpus_done(void) +{ +} + +#ifdef CONFIG_HOTPLUG_CPU + +static int loongson3_cpu_disable(void) +{ + unsigned long flags; + unsigned int cpu = smp_processor_id(); + + if (cpu == 0) + return -EBUSY; + + set_cpu_online(cpu, false); + cpu_clear(cpu, cpu_callin_map); + local_irq_save(flags); + fixup_irqs(); + local_irq_restore(flags); + flush_cache_all(); + local_flush_tlb_all(); + + return 0; +} + + +static void loongson3_cpu_die(unsigned int cpu) +{ + while (per_cpu(cpu_state, cpu) != CPU_DEAD) + cpu_relax(); + + mb(); +} + +/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and + * flush all L1 entries at first. Then, another core (usually Core 0) can + * safely disable the clock of the target core. loongson3_play_dead() is + * called via CKSEG1 (uncached and unmmaped) */ +static void loongson3_play_dead(int *state_addr) +{ + register int val; + register long cpuid, core, node, count; + register void *addr, *base, *initfunc; + + __asm__ __volatile__( + " .set push \n" + " .set noreorder \n" + " li %[addr], 0x80000000 \n" /* KSEG0 */ + "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */ + " cache 0, 1(%[addr]) \n" + " cache 0, 2(%[addr]) \n" + " cache 0, 3(%[addr]) \n" + " cache 1, 0(%[addr]) \n" /* flush L1 DCache */ + " cache 1, 1(%[addr]) \n" + " cache 1, 2(%[addr]) \n" + " cache 1, 3(%[addr]) \n" + " addiu %[sets], %[sets], -1 \n" + " bnez %[sets], 1b \n" + " addiu %[addr], %[addr], 0x20 \n" + " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */ + " sw %[val], (%[state_addr]) \n" + " sync \n" + " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */ + " .set pop \n" + : [addr] "=&r" (addr), [val] "=&r" (val) + : [state_addr] "r" (state_addr), + [sets] "r" (cpu_data[smp_processor_id()].dcache.sets)); + + __asm__ __volatile__( + " .set push \n" + " .set noreorder \n" + " .set mips64 \n" + " mfc0 %[cpuid], $15, 1 \n" + " andi %[cpuid], 0x3ff \n" + " dli %[base], 0x900000003ff01000 \n" + " andi %[core], %[cpuid], 0x3 \n" + " sll %[core], 8 \n" /* get core id */ + " or %[base], %[base], %[core] \n" + " andi %[node], %[cpuid], 0xc \n" + " dsll %[node], 42 \n" /* get node id */ + " or %[base], %[base], %[node] \n" + "1: li %[count], 0x100 \n" /* wait for init loop */ + "2: bnez %[count], 2b \n" /* limit mailbox access */ + " addiu %[count], -1 \n" + " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */ + " beqz %[initfunc], 1b \n" + " nop \n" + " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */ + " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */ + " ld $a1, 0x38(%[base]) \n" + " jr %[initfunc] \n" /* jump to initial PC */ + " nop \n" + " .set pop \n" + : [core] "=&r" (core), [node] "=&r" (node), + [base] "=&r" (base), [cpuid] "=&r" (cpuid), + [count] "=&r" (count), [initfunc] "=&r" (initfunc) + : /* No Input */ + : "a1"); +} + +void play_dead(void) +{ + int *state_addr; + unsigned int cpu = smp_processor_id(); + void (*play_dead_at_ckseg1)(int *); + + idle_task_exit(); + play_dead_at_ckseg1 = + (void *)CKSEG1ADDR((unsigned long)loongson3_play_dead); + state_addr = &per_cpu(cpu_state, cpu); + mb(); + play_dead_at_ckseg1(state_addr); +} + +#define CPU_POST_DEAD_FROZEN (CPU_POST_DEAD | CPU_TASKS_FROZEN) +static int loongson3_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + + switch (action) { + case CPU_POST_DEAD: + case CPU_POST_DEAD_FROZEN: + pr_info("Disable clock for CPU#%d\n", cpu); + LOONGSON_CHIPCFG0 &= ~(1 << (12 + cpu)); + break; + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + pr_info("Enable clock for CPU#%d\n", cpu); + LOONGSON_CHIPCFG0 |= 1 << (12 + cpu); + break; + } + + return NOTIFY_OK; +} + +static int register_loongson3_notifier(void) +{ + hotcpu_notifier(loongson3_cpu_callback, 0); + return 0; +} +early_initcall(register_loongson3_notifier); + +#endif + +struct plat_smp_ops loongson3_smp_ops = { + .send_ipi_single = loongson3_send_ipi_single, + .send_ipi_mask = loongson3_send_ipi_mask, + .init_secondary = loongson3_init_secondary, + .smp_finish = loongson3_smp_finish, + .cpus_done = loongson3_cpus_done, + .boot_secondary = loongson3_boot_secondary, + .smp_setup = loongson3_smp_setup, + .prepare_cpus = loongson3_prepare_cpus, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = loongson3_cpu_disable, + .cpu_die = loongson3_cpu_die, +#endif +}; diff --git a/arch/mips/loongson/loongson-3/smp.h b/arch/mips/loongson/loongson-3/smp.h new file mode 100644 index 000000000000..3453e8c4f2f0 --- /dev/null +++ b/arch/mips/loongson/loongson-3/smp.h @@ -0,0 +1,29 @@ +#ifndef __LOONGSON_SMP_H_ +#define __LOONGSON_SMP_H_ + +/* for Loongson-3A smp support */ + +/* 4 groups(nodes) in maximum in numa case */ +#define SMP_CORE_GROUP0_BASE 0x900000003ff01000 +#define SMP_CORE_GROUP1_BASE 0x900010003ff01000 +#define SMP_CORE_GROUP2_BASE 0x900020003ff01000 +#define SMP_CORE_GROUP3_BASE 0x900030003ff01000 + +/* 4 cores in each group(node) */ +#define SMP_CORE0_OFFSET 0x000 +#define SMP_CORE1_OFFSET 0x100 +#define SMP_CORE2_OFFSET 0x200 +#define SMP_CORE3_OFFSET 0x300 + +/* ipi registers offsets */ +#define STATUS0 0x00 +#define EN0 0x04 +#define SET0 0x08 +#define CLEAR0 0x0c +#define STATUS1 0x10 +#define MASK1 0x14 +#define SET1 0x18 +#define CLEAR1 0x1c +#define BUF 0x20 + +#endif |