aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/loongson
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/loongson')
-rw-r--r--arch/mips/loongson/Kconfig47
-rw-r--r--arch/mips/loongson/Makefile6
-rw-r--r--arch/mips/loongson/Platform1
-rw-r--r--arch/mips/loongson/common/Makefile5
-rw-r--r--arch/mips/loongson/common/dma-swiotlb.c136
-rw-r--r--arch/mips/loongson/common/env.c67
-rw-r--r--arch/mips/loongson/common/init.c11
-rw-r--r--arch/mips/loongson/common/machtype.c4
-rw-r--r--arch/mips/loongson/common/mem.c42
-rw-r--r--arch/mips/loongson/common/pci.c6
-rw-r--r--arch/mips/loongson/common/reset.c21
-rw-r--r--arch/mips/loongson/common/serial.c26
-rw-r--r--arch/mips/loongson/common/setup.c8
-rw-r--r--arch/mips/loongson/common/uart_base.c9
-rw-r--r--arch/mips/loongson/loongson-3/Makefile6
-rw-r--r--arch/mips/loongson/loongson-3/irq.c126
-rw-r--r--arch/mips/loongson/loongson-3/smp.c443
-rw-r--r--arch/mips/loongson/loongson-3/smp.h29
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
59 59
60 These family machines include fuloong2f mini PC, yeeloong2f notebook, 60 These family machines include fuloong2f mini PC, yeeloong2f notebook,
61 LingLoong allinone PC and so forth. 61 LingLoong allinone PC and so forth.
62
63config LEMOTE_MACH3A
64 bool "Lemote Loongson 3A family machines"
65 select ARCH_SPARSEMEM_ENABLE
66 select GENERIC_ISA_DMA_SUPPORT_BROKEN
67 select GENERIC_HARDIRQS_NO__DO_IRQ
68 select BOOT_ELF32
69 select BOARD_SCACHE
70 select CSRC_R4K
71 select CEVT_R4K
72 select CPU_HAS_WB
73 select HW_HAS_PCI
74 select ISA
75 select HT_PCI
76 select I8259
77 select IRQ_CPU
78 select NR_CPUS_DEFAULT_4
79 select SYS_HAS_CPU_LOONGSON3
80 select SYS_HAS_EARLY_PRINTK
81 select SYS_SUPPORTS_SMP
82 select SYS_SUPPORTS_HOTPLUG_CPU
83 select SYS_SUPPORTS_64BIT_KERNEL
84 select SYS_SUPPORTS_HIGHMEM
85 select SYS_SUPPORTS_LITTLE_ENDIAN
86 select LOONGSON_MC146818
87 select ZONE_DMA32
88 select LEFI_FIRMWARE_INTERFACE
89 help
90 Lemote Loongson 3A family machines utilize the 3A revision of
91 Loongson processor and RS780/SBX00 chipset.
62endchoice 92endchoice
63 93
64config CS5536 94config CS5536
@@ -86,8 +116,25 @@ config LOONGSON_UART_BASE
86 default y 116 default y
87 depends on EARLY_PRINTK || SERIAL_8250 117 depends on EARLY_PRINTK || SERIAL_8250
88 118
119config IOMMU_HELPER
120 bool
121
122config NEED_SG_DMA_LENGTH
123 bool
124
125config SWIOTLB
126 bool "Soft IOMMU Support for All-Memory DMA"
127 default y
128 depends on CPU_LOONGSON3
129 select IOMMU_HELPER
130 select NEED_SG_DMA_LENGTH
131 select NEED_DMA_MAP_STATE
132
89config LOONGSON_MC146818 133config LOONGSON_MC146818
90 bool 134 bool
91 default n 135 default n
92 136
137config LEFI_FIRMWARE_INTERFACE
138 bool
139
93endif # MACH_LOONGSON 140endif # 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/
15# 15#
16 16
17obj-$(CONFIG_LEMOTE_MACH2F) += lemote-2f/ 17obj-$(CONFIG_LEMOTE_MACH2F) += lemote-2f/
18
19#
20# All Loongson-3 family machines
21#
22
23obj-$(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/
30cflags-$(CONFIG_MACH_LOONGSON) += -I$(srctree)/arch/mips/include/asm/mach-loongson -mno-branch-likely 30cflags-$(CONFIG_MACH_LOONGSON) += -I$(srctree)/arch/mips/include/asm/mach-loongson -mno-branch-likely
31load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000 31load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000
32load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000 32load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000
33load-$(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/
26# 26#
27 27
28obj-$(CONFIG_LOONGSON_SUSPEND) += pm.o 28obj-$(CONFIG_LOONGSON_SUSPEND) += pm.o
29
30#
31# Big Memory (SWIOTLB) Support
32#
33obj-$(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 @@
1#include <linux/mm.h>
2#include <linux/init.h>
3#include <linux/dma-mapping.h>
4#include <linux/scatterlist.h>
5#include <linux/swiotlb.h>
6#include <linux/bootmem.h>
7
8#include <asm/bootinfo.h>
9#include <boot_param.h>
10#include <dma-coherence.h>
11
12static void *loongson_dma_alloc_coherent(struct device *dev, size_t size,
13 dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
14{
15 void *ret;
16
17 if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
18 return ret;
19
20 /* ignore region specifiers */
21 gfp &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
22
23#ifdef CONFIG_ISA
24 if (dev == NULL)
25 gfp |= __GFP_DMA;
26 else
27#endif
28#ifdef CONFIG_ZONE_DMA
29 if (dev->coherent_dma_mask < DMA_BIT_MASK(32))
30 gfp |= __GFP_DMA;
31 else
32#endif
33#ifdef CONFIG_ZONE_DMA32
34 if (dev->coherent_dma_mask < DMA_BIT_MASK(40))
35 gfp |= __GFP_DMA32;
36 else
37#endif
38 ;
39 gfp |= __GFP_NORETRY;
40
41 ret = swiotlb_alloc_coherent(dev, size, dma_handle, gfp);
42 mb();
43 return ret;
44}
45
46static void loongson_dma_free_coherent(struct device *dev, size_t size,
47 void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs)
48{
49 int order = get_order(size);
50
51 if (dma_release_from_coherent(dev, order, vaddr))
52 return;
53
54 swiotlb_free_coherent(dev, size, vaddr, dma_handle);
55}
56
57static dma_addr_t loongson_dma_map_page(struct device *dev, struct page *page,
58 unsigned long offset, size_t size,
59 enum dma_data_direction dir,
60 struct dma_attrs *attrs)
61{
62 dma_addr_t daddr = swiotlb_map_page(dev, page, offset, size,
63 dir, attrs);
64 mb();
65 return daddr;
66}
67
68static int loongson_dma_map_sg(struct device *dev, struct scatterlist *sg,
69 int nents, enum dma_data_direction dir,
70 struct dma_attrs *attrs)
71{
72 int r = swiotlb_map_sg_attrs(dev, sg, nents, dir, NULL);
73 mb();
74
75 return r;
76}
77
78static void loongson_dma_sync_single_for_device(struct device *dev,
79 dma_addr_t dma_handle, size_t size,
80 enum dma_data_direction dir)
81{
82 swiotlb_sync_single_for_device(dev, dma_handle, size, dir);
83 mb();
84}
85
86static void loongson_dma_sync_sg_for_device(struct device *dev,
87 struct scatterlist *sg, int nents,
88 enum dma_data_direction dir)
89{
90 swiotlb_sync_sg_for_device(dev, sg, nents, dir);
91 mb();
92}
93
94static int loongson_dma_set_mask(struct device *dev, u64 mask)
95{
96 if (mask > DMA_BIT_MASK(loongson_sysconf.dma_mask_bits)) {
97 *dev->dma_mask = DMA_BIT_MASK(loongson_sysconf.dma_mask_bits);
98 return -EIO;
99 }
100
101 *dev->dma_mask = mask;
102
103 return 0;
104}
105
106dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
107{
108 return paddr;
109}
110
111phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
112{
113 return daddr;
114}
115
116static struct dma_map_ops loongson_dma_map_ops = {
117 .alloc = loongson_dma_alloc_coherent,
118 .free = loongson_dma_free_coherent,
119 .map_page = loongson_dma_map_page,
120 .unmap_page = swiotlb_unmap_page,
121 .map_sg = loongson_dma_map_sg,
122 .unmap_sg = swiotlb_unmap_sg_attrs,
123 .sync_single_for_cpu = swiotlb_sync_single_for_cpu,
124 .sync_single_for_device = loongson_dma_sync_single_for_device,
125 .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
126 .sync_sg_for_device = loongson_dma_sync_sg_for_device,
127 .mapping_error = swiotlb_dma_mapping_error,
128 .dma_supported = swiotlb_dma_supported,
129 .set_dma_mask = loongson_dma_set_mask
130};
131
132void __init plat_swiotlb_setup(void)
133{
134 swiotlb_init(1);
135 mips_dma_map_ops = &loongson_dma_map_ops;
136}
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 @@
18 * option) any later version. 18 * option) any later version.
19 */ 19 */
20#include <linux/module.h> 20#include <linux/module.h>
21
22#include <asm/bootinfo.h> 21#include <asm/bootinfo.h>
23
24#include <loongson.h> 22#include <loongson.h>
23#include <boot_param.h>
25 24
26unsigned long cpu_clock_freq; 25u32 cpu_clock_freq;
27EXPORT_SYMBOL(cpu_clock_freq); 26EXPORT_SYMBOL(cpu_clock_freq);
28unsigned long memsize, highmemsize; 27struct efi_memory_map_loongson *loongson_memmap;
28struct loongson_system_configuration loongson_sysconf;
29 29
30#define parse_even_earlier(res, option, p) \ 30#define parse_even_earlier(res, option, p) \
31do { \ 31do { \
32 unsigned int tmp __maybe_unused; \ 32 unsigned int tmp __maybe_unused; \
33 \ 33 \
34 if (strncmp(option, (char *)p, strlen(option)) == 0) \ 34 if (strncmp(option, (char *)p, strlen(option)) == 0) \
35 tmp = strict_strtol((char *)p + strlen(option"="), 10, &res); \ 35 tmp = kstrtou32((char *)p + strlen(option"="), 10, &res); \
36} while (0) 36} while (0)
37 37
38void __init prom_init_env(void) 38void __init prom_init_env(void)
39{ 39{
40 /* pmon passes arguments in 32bit pointers */ 40 /* pmon passes arguments in 32bit pointers */
41 int *_prom_envp;
42 unsigned long bus_clock;
43 unsigned int processor_id; 41 unsigned int processor_id;
42
43#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
44 int *_prom_envp;
44 long l; 45 long l;
45 46
46 /* firmware arguments are initialized in head.S */ 47 /* firmware arguments are initialized in head.S */
@@ -48,7 +49,6 @@ void __init prom_init_env(void)
48 49
49 l = (long)*_prom_envp; 50 l = (long)*_prom_envp;
50 while (l != 0) { 51 while (l != 0) {
51 parse_even_earlier(bus_clock, "busclock", l);
52 parse_even_earlier(cpu_clock_freq, "cpuclock", l); 52 parse_even_earlier(cpu_clock_freq, "cpuclock", l);
53 parse_even_earlier(memsize, "memsize", l); 53 parse_even_earlier(memsize, "memsize", l);
54 parse_even_earlier(highmemsize, "highmemsize", l); 54 parse_even_earlier(highmemsize, "highmemsize", l);
@@ -57,8 +57,48 @@ void __init prom_init_env(void)
57 } 57 }
58 if (memsize == 0) 58 if (memsize == 0)
59 memsize = 256; 59 memsize = 256;
60 if (bus_clock == 0) 60 pr_info("memsize=%u, highmemsize=%u\n", memsize, highmemsize);
61 bus_clock = 66000000; 61#else
62 struct boot_params *boot_p;
63 struct loongson_params *loongson_p;
64 struct efi_cpuinfo_loongson *ecpu;
65 struct irq_source_routing_table *eirq_source;
66
67 /* firmware arguments are initialized in head.S */
68 boot_p = (struct boot_params *)fw_arg2;
69 loongson_p = &(boot_p->efi.smbios.lp);
70
71 ecpu = (struct efi_cpuinfo_loongson *)
72 ((u64)loongson_p + loongson_p->cpu_offset);
73 eirq_source = (struct irq_source_routing_table *)
74 ((u64)loongson_p + loongson_p->irq_offset);
75 loongson_memmap = (struct efi_memory_map_loongson *)
76 ((u64)loongson_p + loongson_p->memory_offset);
77
78 cpu_clock_freq = ecpu->cpu_clock_freq;
79 loongson_sysconf.cputype = ecpu->cputype;
80 loongson_sysconf.nr_cpus = ecpu->nr_cpus;
81 if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0)
82 loongson_sysconf.nr_cpus = NR_CPUS;
83
84 loongson_sysconf.pci_mem_start_addr = eirq_source->pci_mem_start_addr;
85 loongson_sysconf.pci_mem_end_addr = eirq_source->pci_mem_end_addr;
86 loongson_sysconf.pci_io_base = eirq_source->pci_io_start_addr;
87 loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits;
88 if (loongson_sysconf.dma_mask_bits < 32 ||
89 loongson_sysconf.dma_mask_bits > 64)
90 loongson_sysconf.dma_mask_bits = 32;
91
92 loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm;
93 loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown;
94 loongson_sysconf.suspend_addr = boot_p->reset_system.DoSuspend;
95
96 loongson_sysconf.ht_control_base = 0x90000EFDFB000000;
97 loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios;
98 pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n",
99 loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr,
100 loongson_sysconf.vgabios_addr);
101#endif
62 if (cpu_clock_freq == 0) { 102 if (cpu_clock_freq == 0) {
63 processor_id = (&current_cpu_data)->processor_id; 103 processor_id = (&current_cpu_data)->processor_id;
64 switch (processor_id & PRID_REV_MASK) { 104 switch (processor_id & PRID_REV_MASK) {
@@ -68,12 +108,13 @@ void __init prom_init_env(void)
68 case PRID_REV_LOONGSON2F: 108 case PRID_REV_LOONGSON2F:
69 cpu_clock_freq = 797000000; 109 cpu_clock_freq = 797000000;
70 break; 110 break;
111 case PRID_REV_LOONGSON3A:
112 cpu_clock_freq = 900000000;
113 break;
71 default: 114 default:
72 cpu_clock_freq = 100000000; 115 cpu_clock_freq = 100000000;
73 break; 116 break;
74 } 117 }
75 } 118 }
76 119 pr_info("CpuClock = %u\n", cpu_clock_freq);
77 pr_info("busclock=%ld, cpuclock=%ld, memsize=%ld, highmemsize=%ld\n",
78 bus_clock, cpu_clock_freq, memsize, highmemsize);
79} 120}
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 @@
9 */ 9 */
10 10
11#include <linux/bootmem.h> 11#include <linux/bootmem.h>
12#include <asm/smp-ops.h>
12 13
13#include <loongson.h> 14#include <loongson.h>
14 15
@@ -17,10 +18,6 @@ unsigned long __maybe_unused _loongson_addrwincfg_base;
17 18
18void __init prom_init(void) 19void __init prom_init(void)
19{ 20{
20 /* init base address of io space */
21 set_io_port_base((unsigned long)
22 ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE));
23
24#ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG 21#ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
25 _loongson_addrwincfg_base = (unsigned long) 22 _loongson_addrwincfg_base = (unsigned long)
26 ioremap(LOONGSON_ADDRWINCFG_BASE, LOONGSON_ADDRWINCFG_SIZE); 23 ioremap(LOONGSON_ADDRWINCFG_BASE, LOONGSON_ADDRWINCFG_SIZE);
@@ -28,10 +25,16 @@ void __init prom_init(void)
28 25
29 prom_init_cmdline(); 26 prom_init_cmdline();
30 prom_init_env(); 27 prom_init_env();
28
29 /* init base address of io space */
30 set_io_port_base((unsigned long)
31 ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE));
32
31 prom_init_memory(); 33 prom_init_memory();
32 34
33 /*init the uart base address */ 35 /*init the uart base address */
34 prom_init_uart_base(); 36 prom_init_uart_base();
37 register_smp_ops(&loongson3_smp_ops);
35} 38}
36 39
37void __init prom_free_prom_memory(void) 40void __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[] = {
27 [MACH_DEXXON_GDIUM2F10] "dexxon-gdium-2f", 27 [MACH_DEXXON_GDIUM2F10] "dexxon-gdium-2f",
28 [MACH_LEMOTE_NAS] "lemote-nas-2f", 28 [MACH_LEMOTE_NAS] "lemote-nas-2f",
29 [MACH_LEMOTE_LL2F] "lemote-lynloong-2f", 29 [MACH_LEMOTE_LL2F] "lemote-lynloong-2f",
30 [MACH_LEMOTE_A1004] "lemote-3a-notebook-a1004",
31 [MACH_LEMOTE_A1101] "lemote-3a-itx-a1101",
32 [MACH_LEMOTE_A1201] "lemote-2gq-notebook-a1201",
33 [MACH_LEMOTE_A1205] "lemote-2gq-aio-a1205",
30 [MACH_LOONGSON_END] NULL, 34 [MACH_LOONGSON_END] NULL,
31}; 35};
32 36
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 @@
11#include <asm/bootinfo.h> 11#include <asm/bootinfo.h>
12 12
13#include <loongson.h> 13#include <loongson.h>
14#include <boot_param.h>
14#include <mem.h> 15#include <mem.h>
15#include <pci.h> 16#include <pci.h>
16 17
18#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
19
20u32 memsize, highmemsize;
21
17void __init prom_init_memory(void) 22void __init prom_init_memory(void)
18{ 23{
19 add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM); 24 add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
@@ -49,6 +54,43 @@ void __init prom_init_memory(void)
49#endif /* !CONFIG_64BIT */ 54#endif /* !CONFIG_64BIT */
50} 55}
51 56
57#else /* CONFIG_LEFI_FIRMWARE_INTERFACE */
58
59void __init prom_init_memory(void)
60{
61 int i;
62 u32 node_id;
63 u32 mem_type;
64
65 /* parse memory information */
66 for (i = 0; i < loongson_memmap->nr_map; i++) {
67 node_id = loongson_memmap->map[i].node_id;
68 mem_type = loongson_memmap->map[i].mem_type;
69
70 if (node_id == 0) {
71 switch (mem_type) {
72 case SYSTEM_RAM_LOW:
73 add_memory_region(loongson_memmap->map[i].mem_start,
74 (u64)loongson_memmap->map[i].mem_size << 20,
75 BOOT_MEM_RAM);
76 break;
77 case SYSTEM_RAM_HIGH:
78 add_memory_region(loongson_memmap->map[i].mem_start,
79 (u64)loongson_memmap->map[i].mem_size << 20,
80 BOOT_MEM_RAM);
81 break;
82 case MEM_RESERVED:
83 add_memory_region(loongson_memmap->map[i].mem_start,
84 (u64)loongson_memmap->map[i].mem_size << 20,
85 BOOT_MEM_RESERVED);
86 break;
87 }
88 }
89 }
90}
91
92#endif /* CONFIG_LEFI_FIRMWARE_INTERFACE */
93
52/* override of arch/mips/mm/cache.c: __uncached_access */ 94/* override of arch/mips/mm/cache.c: __uncached_access */
53int __uncached_access(struct file *file, unsigned long addr) 95int __uncached_access(struct file *file, unsigned long addr)
54{ 96{
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 @@
11 11
12#include <pci.h> 12#include <pci.h>
13#include <loongson.h> 13#include <loongson.h>
14#include <boot_param.h>
14 15
15static struct resource loongson_pci_mem_resource = { 16static struct resource loongson_pci_mem_resource = {
16 .name = "pci memory space", 17 .name = "pci memory space",
@@ -82,7 +83,10 @@ static int __init pcibios_init(void)
82 setup_pcimap(); 83 setup_pcimap();
83 84
84 loongson_pci_controller.io_map_base = mips_io_port_base; 85 loongson_pci_controller.io_map_base = mips_io_port_base;
85 86#ifdef CONFIG_LEFI_FIRMWARE_INTERFACE
87 loongson_pci_mem_resource.start = loongson_sysconf.pci_mem_start_addr;
88 loongson_pci_mem_resource.end = loongson_sysconf.pci_mem_end_addr;
89#endif
86 register_pci_controller(&loongson_pci_controller); 90 register_pci_controller(&loongson_pci_controller);
87 91
88 return 0; 92 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 @@
16#include <asm/reboot.h> 16#include <asm/reboot.h>
17 17
18#include <loongson.h> 18#include <loongson.h>
19#include <boot_param.h>
19 20
20static inline void loongson_reboot(void) 21static inline void loongson_reboot(void)
21{ 22{
@@ -37,17 +38,37 @@ static inline void loongson_reboot(void)
37 38
38static void loongson_restart(char *command) 39static void loongson_restart(char *command)
39{ 40{
41#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
40 /* do preparation for reboot */ 42 /* do preparation for reboot */
41 mach_prepare_reboot(); 43 mach_prepare_reboot();
42 44
43 /* reboot via jumping to boot base address */ 45 /* reboot via jumping to boot base address */
44 loongson_reboot(); 46 loongson_reboot();
47#else
48 void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr;
49
50 fw_restart();
51 while (1) {
52 if (cpu_wait)
53 cpu_wait();
54 }
55#endif
45} 56}
46 57
47static void loongson_poweroff(void) 58static void loongson_poweroff(void)
48{ 59{
60#ifndef CONFIG_LEFI_FIRMWARE_INTERFACE
49 mach_prepare_shutdown(); 61 mach_prepare_shutdown();
50 unreachable(); 62 unreachable();
63#else
64 void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr;
65
66 fw_poweroff();
67 while (1) {
68 if (cpu_wait)
69 cpu_wait();
70 }
71#endif
51} 72}
52 73
53static void loongson_halt(void) 74static 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 @@
19#include <loongson.h> 19#include <loongson.h>
20#include <machine.h> 20#include <machine.h>
21 21
22#define PORT(int) \ 22#define PORT(int, clk) \
23{ \ 23{ \
24 .irq = int, \ 24 .irq = int, \
25 .uartclk = 1843200, \ 25 .uartclk = clk, \
26 .iotype = UPIO_PORT, \ 26 .iotype = UPIO_PORT, \
27 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \ 27 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \
28 .regshift = 0, \ 28 .regshift = 0, \
29} 29}
30 30
31#define PORT_M(int) \ 31#define PORT_M(int, clk) \
32{ \ 32{ \
33 .irq = MIPS_CPU_IRQ_BASE + (int), \ 33 .irq = MIPS_CPU_IRQ_BASE + (int), \
34 .uartclk = 3686400, \ 34 .uartclk = clk, \
35 .iotype = UPIO_MEM, \ 35 .iotype = UPIO_MEM, \
36 .membase = (void __iomem *)NULL, \ 36 .membase = (void __iomem *)NULL, \
37 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \ 37 .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \
@@ -40,13 +40,17 @@
40 40
41static struct plat_serial8250_port uart8250_data[][2] = { 41static struct plat_serial8250_port uart8250_data[][2] = {
42 [MACH_LOONGSON_UNKNOWN] {}, 42 [MACH_LOONGSON_UNKNOWN] {},
43 [MACH_LEMOTE_FL2E] {PORT(4), {} }, 43 [MACH_LEMOTE_FL2E] {PORT(4, 1843200), {} },
44 [MACH_LEMOTE_FL2F] {PORT(3), {} }, 44 [MACH_LEMOTE_FL2F] {PORT(3, 1843200), {} },
45 [MACH_LEMOTE_ML2F7] {PORT_M(3), {} }, 45 [MACH_LEMOTE_ML2F7] {PORT_M(3, 3686400), {} },
46 [MACH_LEMOTE_YL2F89] {PORT_M(3), {} }, 46 [MACH_LEMOTE_YL2F89] {PORT_M(3, 3686400), {} },
47 [MACH_DEXXON_GDIUM2F10] {PORT_M(3), {} }, 47 [MACH_DEXXON_GDIUM2F10] {PORT_M(3, 3686400), {} },
48 [MACH_LEMOTE_NAS] {PORT_M(3), {} }, 48 [MACH_LEMOTE_NAS] {PORT_M(3, 3686400), {} },
49 [MACH_LEMOTE_LL2F] {PORT(3), {} }, 49 [MACH_LEMOTE_LL2F] {PORT(3, 1843200), {} },
50 [MACH_LEMOTE_A1004] {PORT_M(2, 33177600), {} },
51 [MACH_LEMOTE_A1101] {PORT_M(2, 25000000), {} },
52 [MACH_LEMOTE_A1201] {PORT_M(2, 25000000), {} },
53 [MACH_LEMOTE_A1205] {PORT_M(2, 25000000), {} },
50 [MACH_LOONGSON_END] {}, 54 [MACH_LOONGSON_END] {},
51}; 55};
52 56
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 @@
18#include <linux/screen_info.h> 18#include <linux/screen_info.h>
19#endif 19#endif
20 20
21void (*__wbflush)(void);
22EXPORT_SYMBOL(__wbflush);
23
24static void wbflush_loongson(void) 21static void wbflush_loongson(void)
25{ 22{
26 asm(".set\tpush\n\t" 23 asm(".set\tpush\n\t"
@@ -32,10 +29,11 @@ static void wbflush_loongson(void)
32 ".set mips0\n\t"); 29 ".set mips0\n\t");
33} 30}
34 31
32void (*__wbflush)(void) = wbflush_loongson;
33EXPORT_SYMBOL(__wbflush);
34
35void __init plat_mem_setup(void) 35void __init plat_mem_setup(void)
36{ 36{
37 __wbflush = wbflush_loongson;
38
39#ifdef CONFIG_VT 37#ifdef CONFIG_VT
40#if defined(CONFIG_VGA_CONSOLE) 38#if defined(CONFIG_VGA_CONSOLE)
41 conswitchp = &vga_con; 39 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)
35 case MACH_DEXXON_GDIUM2F10: 35 case MACH_DEXXON_GDIUM2F10:
36 case MACH_LEMOTE_NAS: 36 case MACH_LEMOTE_NAS:
37 default: 37 default:
38 /* The CPU provided serial port */ 38 /* The CPU provided serial port (LPC) */
39 loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8; 39 loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
40 break; 40 break;
41 case MACH_LEMOTE_A1004:
42 case MACH_LEMOTE_A1101:
43 case MACH_LEMOTE_A1201:
44 case MACH_LEMOTE_A1205:
45 /* The CPU provided serial port (CPU) */
46 loongson_uart_base = LOONGSON_REG_BASE + 0x1e0;
47 break;
41 } 48 }
42 49
43 _loongson_uart_base = 50 _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 @@
1#
2# Makefile for Loongson-3 family machines
3#
4obj-y += irq.o
5
6obj-$(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 @@
1#include <loongson.h>
2#include <irq.h>
3#include <linux/interrupt.h>
4#include <linux/module.h>
5
6#include <asm/irq_cpu.h>
7#include <asm/i8259.h>
8#include <asm/mipsregs.h>
9
10unsigned int ht_irq[] = {1, 3, 4, 5, 6, 7, 8, 12, 14, 15};
11
12static void ht_irqdispatch(void)
13{
14 unsigned int i, irq;
15
16 irq = LOONGSON_HT1_INT_VECTOR(0);
17 LOONGSON_HT1_INT_VECTOR(0) = irq; /* Acknowledge the IRQs */
18
19 for (i = 0; i < ARRAY_SIZE(ht_irq); i++) {
20 if (irq & (0x1 << ht_irq[i]))
21 do_IRQ(ht_irq[i]);
22 }
23}
24
25void mach_irq_dispatch(unsigned int pending)
26{
27 if (pending & CAUSEF_IP7)
28 do_IRQ(LOONGSON_TIMER_IRQ);
29#if defined(CONFIG_SMP)
30 else if (pending & CAUSEF_IP6)
31 loongson3_ipi_interrupt(NULL);
32#endif
33 else if (pending & CAUSEF_IP3)
34 ht_irqdispatch();
35 else if (pending & CAUSEF_IP2)
36 do_IRQ(LOONGSON_UART_IRQ);
37 else {
38 pr_err("%s : spurious interrupt\n", __func__);
39 spurious_interrupt();
40 }
41}
42
43static struct irqaction cascade_irqaction = {
44 .handler = no_action,
45 .name = "cascade",
46};
47
48static inline void mask_loongson_irq(struct irq_data *d)
49{
50 clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
51 irq_disable_hazard();
52
53 /* Workaround: UART IRQ may deliver to any core */
54 if (d->irq == LOONGSON_UART_IRQ) {
55 int cpu = smp_processor_id();
56
57 LOONGSON_INT_ROUTER_INTENCLR = 1 << 10;
58 LOONGSON_INT_ROUTER_LPC = 0x10 + (1<<cpu);
59 }
60}
61
62static inline void unmask_loongson_irq(struct irq_data *d)
63{
64 /* Workaround: UART IRQ may deliver to any core */
65 if (d->irq == LOONGSON_UART_IRQ) {
66 int cpu = smp_processor_id();
67
68 LOONGSON_INT_ROUTER_INTENSET = 1 << 10;
69 LOONGSON_INT_ROUTER_LPC = 0x10 + (1<<cpu);
70 }
71
72 set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
73 irq_enable_hazard();
74}
75
76 /* For MIPS IRQs which shared by all cores */
77static struct irq_chip loongson_irq_chip = {
78 .name = "Loongson",
79 .irq_ack = mask_loongson_irq,
80 .irq_mask = mask_loongson_irq,
81 .irq_mask_ack = mask_loongson_irq,
82 .irq_unmask = unmask_loongson_irq,
83 .irq_eoi = unmask_loongson_irq,
84};
85
86void irq_router_init(void)
87{
88 int i;
89
90 /* route LPC int to cpu core0 int 0 */
91 LOONGSON_INT_ROUTER_LPC = LOONGSON_INT_CORE0_INT0;
92 /* route HT1 int0 ~ int7 to cpu core0 INT1*/
93 for (i = 0; i < 8; i++)
94 LOONGSON_INT_ROUTER_HT1(i) = LOONGSON_INT_CORE0_INT1;
95 /* enable HT1 interrupt */
96 LOONGSON_HT1_INTN_EN(0) = 0xffffffff;
97 /* enable router interrupt intenset */
98 LOONGSON_INT_ROUTER_INTENSET =
99 LOONGSON_INT_ROUTER_INTEN | (0xffff << 16) | 0x1 << 10;
100}
101
102void __init mach_init_irq(void)
103{
104 clear_c0_status(ST0_IM | ST0_BEV);
105
106 irq_router_init();
107 mips_cpu_irq_init();
108 init_i8259_irqs();
109 irq_set_chip_and_handler(LOONGSON_UART_IRQ,
110 &loongson_irq_chip, handle_level_irq);
111
112 /* setup HT1 irq */
113 setup_irq(LOONGSON_HT1_IRQ, &cascade_irqaction);
114
115 set_c0_status(STATUSF_IP2 | STATUSF_IP6);
116}
117
118#ifdef CONFIG_HOTPLUG_CPU
119
120void fixup_irqs(void)
121{
122 irq_cpu_offline();
123 clear_c0_status(ST0_IM);
124}
125
126#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 @@
1/*
2 * Copyright (C) 2010, 2011, 2012, Lemote, Inc.
3 * Author: Chen Huacai, chenhc@lemote.com
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/init.h>
18#include <linux/cpu.h>
19#include <linux/sched.h>
20#include <linux/smp.h>
21#include <linux/cpufreq.h>
22#include <asm/processor.h>
23#include <asm/time.h>
24#include <asm/clock.h>
25#include <asm/tlbflush.h>
26#include <asm/cacheflush.h>
27#include <loongson.h>
28
29#include "smp.h"
30
31DEFINE_PER_CPU(int, cpu_state);
32DEFINE_PER_CPU(uint32_t, core0_c0count);
33
34/* read a 32bit value from ipi register */
35#define loongson3_ipi_read32(addr) readl(addr)
36/* read a 64bit value from ipi register */
37#define loongson3_ipi_read64(addr) readq(addr)
38/* write a 32bit value to ipi register */
39#define loongson3_ipi_write32(action, addr) \
40 do { \
41 writel(action, addr); \
42 __wbflush(); \
43 } while (0)
44/* write a 64bit value to ipi register */
45#define loongson3_ipi_write64(action, addr) \
46 do { \
47 writeq(action, addr); \
48 __wbflush(); \
49 } while (0)
50
51static void *ipi_set0_regs[] = {
52 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0),
53 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0),
54 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0),
55 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0),
56 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0),
57 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0),
58 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0),
59 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0),
60 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0),
61 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0),
62 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0),
63 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0),
64 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0),
65 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0),
66 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0),
67 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0),
68};
69
70static void *ipi_clear0_regs[] = {
71 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0),
72 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0),
73 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0),
74 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0),
75 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0),
76 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0),
77 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0),
78 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0),
79 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0),
80 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0),
81 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0),
82 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0),
83 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0),
84 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0),
85 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0),
86 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0),
87};
88
89static void *ipi_status0_regs[] = {
90 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0),
91 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0),
92 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0),
93 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0),
94 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0),
95 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0),
96 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0),
97 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0),
98 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0),
99 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0),
100 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0),
101 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0),
102 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0),
103 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0),
104 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0),
105 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0),
106};
107
108static void *ipi_en0_regs[] = {
109 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0),
110 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0),
111 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0),
112 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0),
113 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0),
114 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0),
115 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0),
116 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0),
117 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0),
118 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0),
119 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0),
120 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0),
121 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0),
122 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0),
123 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0),
124 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0),
125};
126
127static void *ipi_mailbox_buf[] = {
128 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF),
129 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF),
130 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF),
131 (void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF),
132 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF),
133 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF),
134 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF),
135 (void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF),
136 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF),
137 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF),
138 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF),
139 (void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF),
140 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF),
141 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF),
142 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF),
143 (void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF),
144};
145
146/*
147 * Simple enough, just poke the appropriate ipi register
148 */
149static void loongson3_send_ipi_single(int cpu, unsigned int action)
150{
151 loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]);
152}
153
154static void
155loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
156{
157 unsigned int i;
158
159 for_each_cpu(i, mask)
160 loongson3_ipi_write32((u32)action, ipi_set0_regs[i]);
161}
162
163void loongson3_ipi_interrupt(struct pt_regs *regs)
164{
165 int i, cpu = smp_processor_id();
166 unsigned int action, c0count;
167
168 /* Load the ipi register to figure out what we're supposed to do */
169 action = loongson3_ipi_read32(ipi_status0_regs[cpu]);
170
171 /* Clear the ipi register to clear the interrupt */
172 loongson3_ipi_write32((u32)action, ipi_clear0_regs[cpu]);
173
174 if (action & SMP_RESCHEDULE_YOURSELF)
175 scheduler_ipi();
176
177 if (action & SMP_CALL_FUNCTION)
178 smp_call_function_interrupt();
179
180 if (action & SMP_ASK_C0COUNT) {
181 BUG_ON(cpu != 0);
182 c0count = read_c0_count();
183 for (i = 1; i < loongson_sysconf.nr_cpus; i++)
184 per_cpu(core0_c0count, i) = c0count;
185 }
186}
187
188#define MAX_LOOPS 1111
189/*
190 * SMP init and finish on secondary CPUs
191 */
192static void loongson3_init_secondary(void)
193{
194 int i;
195 uint32_t initcount;
196 unsigned int cpu = smp_processor_id();
197 unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
198 STATUSF_IP3 | STATUSF_IP2;
199
200 /* Set interrupt mask, but don't enable */
201 change_c0_status(ST0_IM, imask);
202
203 for (i = 0; i < loongson_sysconf.nr_cpus; i++)
204 loongson3_ipi_write32(0xffffffff, ipi_en0_regs[i]);
205
206 per_cpu(cpu_state, cpu) = CPU_ONLINE;
207
208 i = 0;
209 __get_cpu_var(core0_c0count) = 0;
210 loongson3_send_ipi_single(0, SMP_ASK_C0COUNT);
211 while (!__get_cpu_var(core0_c0count)) {
212 i++;
213 cpu_relax();
214 }
215
216 if (i > MAX_LOOPS)
217 i = MAX_LOOPS;
218 initcount = __get_cpu_var(core0_c0count) + i;
219 write_c0_count(initcount);
220}
221
222static void loongson3_smp_finish(void)
223{
224 write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ);
225 local_irq_enable();
226 loongson3_ipi_write64(0,
227 (void *)(ipi_mailbox_buf[smp_processor_id()]+0x0));
228 pr_info("CPU#%d finished, CP0_ST=%x\n",
229 smp_processor_id(), read_c0_status());
230}
231
232static void __init loongson3_smp_setup(void)
233{
234 int i, num;
235
236 init_cpu_possible(cpu_none_mask);
237 set_cpu_possible(0, true);
238
239 __cpu_number_map[0] = 0;
240 __cpu_logical_map[0] = 0;
241
242 /* For unified kernel, NR_CPUS is the maximum possible value,
243 * loongson_sysconf.nr_cpus is the really present value */
244 for (i = 1, num = 0; i < loongson_sysconf.nr_cpus; i++) {
245 set_cpu_possible(i, true);
246 __cpu_number_map[i] = ++num;
247 __cpu_logical_map[num] = i;
248 }
249 pr_info("Detected %i available secondary CPU(s)\n", num);
250}
251
252static void __init loongson3_prepare_cpus(unsigned int max_cpus)
253{
254 init_cpu_present(cpu_possible_mask);
255 per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
256}
257
258/*
259 * Setup the PC, SP, and GP of a secondary processor and start it runing!
260 */
261static void loongson3_boot_secondary(int cpu, struct task_struct *idle)
262{
263 unsigned long startargs[4];
264
265 pr_info("Booting CPU#%d...\n", cpu);
266
267 /* startargs[] are initial PC, SP and GP for secondary CPU */
268 startargs[0] = (unsigned long)&smp_bootstrap;
269 startargs[1] = (unsigned long)__KSTK_TOS(idle);
270 startargs[2] = (unsigned long)task_thread_info(idle);
271 startargs[3] = 0;
272
273 pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n",
274 cpu, startargs[0], startargs[1], startargs[2]);
275
276 loongson3_ipi_write64(startargs[3], (void *)(ipi_mailbox_buf[cpu]+0x18));
277 loongson3_ipi_write64(startargs[2], (void *)(ipi_mailbox_buf[cpu]+0x10));
278 loongson3_ipi_write64(startargs[1], (void *)(ipi_mailbox_buf[cpu]+0x8));
279 loongson3_ipi_write64(startargs[0], (void *)(ipi_mailbox_buf[cpu]+0x0));
280}
281
282/*
283 * Final cleanup after all secondaries booted
284 */
285static void __init loongson3_cpus_done(void)
286{
287}
288
289#ifdef CONFIG_HOTPLUG_CPU
290
291static int loongson3_cpu_disable(void)
292{
293 unsigned long flags;
294 unsigned int cpu = smp_processor_id();
295
296 if (cpu == 0)
297 return -EBUSY;
298
299 set_cpu_online(cpu, false);
300 cpu_clear(cpu, cpu_callin_map);
301 local_irq_save(flags);
302 fixup_irqs();
303 local_irq_restore(flags);
304 flush_cache_all();
305 local_flush_tlb_all();
306
307 return 0;
308}
309
310
311static void loongson3_cpu_die(unsigned int cpu)
312{
313 while (per_cpu(cpu_state, cpu) != CPU_DEAD)
314 cpu_relax();
315
316 mb();
317}
318
319/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and
320 * flush all L1 entries at first. Then, another core (usually Core 0) can
321 * safely disable the clock of the target core. loongson3_play_dead() is
322 * called via CKSEG1 (uncached and unmmaped) */
323static void loongson3_play_dead(int *state_addr)
324{
325 register int val;
326 register long cpuid, core, node, count;
327 register void *addr, *base, *initfunc;
328
329 __asm__ __volatile__(
330 " .set push \n"
331 " .set noreorder \n"
332 " li %[addr], 0x80000000 \n" /* KSEG0 */
333 "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
334 " cache 0, 1(%[addr]) \n"
335 " cache 0, 2(%[addr]) \n"
336 " cache 0, 3(%[addr]) \n"
337 " cache 1, 0(%[addr]) \n" /* flush L1 DCache */
338 " cache 1, 1(%[addr]) \n"
339 " cache 1, 2(%[addr]) \n"
340 " cache 1, 3(%[addr]) \n"
341 " addiu %[sets], %[sets], -1 \n"
342 " bnez %[sets], 1b \n"
343 " addiu %[addr], %[addr], 0x20 \n"
344 " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
345 " sw %[val], (%[state_addr]) \n"
346 " sync \n"
347 " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
348 " .set pop \n"
349 : [addr] "=&r" (addr), [val] "=&r" (val)
350 : [state_addr] "r" (state_addr),
351 [sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
352
353 __asm__ __volatile__(
354 " .set push \n"
355 " .set noreorder \n"
356 " .set mips64 \n"
357 " mfc0 %[cpuid], $15, 1 \n"
358 " andi %[cpuid], 0x3ff \n"
359 " dli %[base], 0x900000003ff01000 \n"
360 " andi %[core], %[cpuid], 0x3 \n"
361 " sll %[core], 8 \n" /* get core id */
362 " or %[base], %[base], %[core] \n"
363 " andi %[node], %[cpuid], 0xc \n"
364 " dsll %[node], 42 \n" /* get node id */
365 " or %[base], %[base], %[node] \n"
366 "1: li %[count], 0x100 \n" /* wait for init loop */
367 "2: bnez %[count], 2b \n" /* limit mailbox access */
368 " addiu %[count], -1 \n"
369 " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
370 " beqz %[initfunc], 1b \n"
371 " nop \n"
372 " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
373 " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
374 " ld $a1, 0x38(%[base]) \n"
375 " jr %[initfunc] \n" /* jump to initial PC */
376 " nop \n"
377 " .set pop \n"
378 : [core] "=&r" (core), [node] "=&r" (node),
379 [base] "=&r" (base), [cpuid] "=&r" (cpuid),
380 [count] "=&r" (count), [initfunc] "=&r" (initfunc)
381 : /* No Input */
382 : "a1");
383}
384
385void play_dead(void)
386{
387 int *state_addr;
388 unsigned int cpu = smp_processor_id();
389 void (*play_dead_at_ckseg1)(int *);
390
391 idle_task_exit();
392 play_dead_at_ckseg1 =
393 (void *)CKSEG1ADDR((unsigned long)loongson3_play_dead);
394 state_addr = &per_cpu(cpu_state, cpu);
395 mb();
396 play_dead_at_ckseg1(state_addr);
397}
398
399#define CPU_POST_DEAD_FROZEN (CPU_POST_DEAD | CPU_TASKS_FROZEN)
400static int loongson3_cpu_callback(struct notifier_block *nfb,
401 unsigned long action, void *hcpu)
402{
403 unsigned int cpu = (unsigned long)hcpu;
404
405 switch (action) {
406 case CPU_POST_DEAD:
407 case CPU_POST_DEAD_FROZEN:
408 pr_info("Disable clock for CPU#%d\n", cpu);
409 LOONGSON_CHIPCFG0 &= ~(1 << (12 + cpu));
410 break;
411 case CPU_UP_PREPARE:
412 case CPU_UP_PREPARE_FROZEN:
413 pr_info("Enable clock for CPU#%d\n", cpu);
414 LOONGSON_CHIPCFG0 |= 1 << (12 + cpu);
415 break;
416 }
417
418 return NOTIFY_OK;
419}
420
421static int register_loongson3_notifier(void)
422{
423 hotcpu_notifier(loongson3_cpu_callback, 0);
424 return 0;
425}
426early_initcall(register_loongson3_notifier);
427
428#endif
429
430struct plat_smp_ops loongson3_smp_ops = {
431 .send_ipi_single = loongson3_send_ipi_single,
432 .send_ipi_mask = loongson3_send_ipi_mask,
433 .init_secondary = loongson3_init_secondary,
434 .smp_finish = loongson3_smp_finish,
435 .cpus_done = loongson3_cpus_done,
436 .boot_secondary = loongson3_boot_secondary,
437 .smp_setup = loongson3_smp_setup,
438 .prepare_cpus = loongson3_prepare_cpus,
439#ifdef CONFIG_HOTPLUG_CPU
440 .cpu_disable = loongson3_cpu_disable,
441 .cpu_die = loongson3_cpu_die,
442#endif
443};
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 @@
1#ifndef __LOONGSON_SMP_H_
2#define __LOONGSON_SMP_H_
3
4/* for Loongson-3A smp support */
5
6/* 4 groups(nodes) in maximum in numa case */
7#define SMP_CORE_GROUP0_BASE 0x900000003ff01000
8#define SMP_CORE_GROUP1_BASE 0x900010003ff01000
9#define SMP_CORE_GROUP2_BASE 0x900020003ff01000
10#define SMP_CORE_GROUP3_BASE 0x900030003ff01000
11
12/* 4 cores in each group(node) */
13#define SMP_CORE0_OFFSET 0x000
14#define SMP_CORE1_OFFSET 0x100
15#define SMP_CORE2_OFFSET 0x200
16#define SMP_CORE3_OFFSET 0x300
17
18/* ipi registers offsets */
19#define STATUS0 0x00
20#define EN0 0x04
21#define SET0 0x08
22#define CLEAR0 0x0c
23#define STATUS1 0x10
24#define MASK1 0x14
25#define SET1 0x18
26#define CLEAR1 0x1c
27#define BUF 0x20
28
29#endif