aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc64
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64')
-rw-r--r--arch/sparc64/Kconfig6
-rw-r--r--arch/sparc64/kernel/Makefile4
-rw-r--r--arch/sparc64/kernel/devices.c196
-rw-r--r--arch/sparc64/kernel/entry.S669
-rw-r--r--arch/sparc64/kernel/head.S31
-rw-r--r--arch/sparc64/kernel/hvapi.c192
-rw-r--r--arch/sparc64/kernel/irq.c83
-rw-r--r--arch/sparc64/kernel/itlb_miss.S4
-rw-r--r--arch/sparc64/kernel/mdesc.c619
-rw-r--r--arch/sparc64/kernel/pci.c54
-rw-r--r--arch/sparc64/kernel/pci_sabre.c7
-rw-r--r--arch/sparc64/kernel/pci_sun4v.c54
-rw-r--r--arch/sparc64/kernel/power.c2
-rw-r--r--arch/sparc64/kernel/process.c4
-rw-r--r--arch/sparc64/kernel/prom.c148
-rw-r--r--arch/sparc64/kernel/setup.c21
-rw-r--r--arch/sparc64/kernel/smp.c155
-rw-r--r--arch/sparc64/kernel/sstate.c104
-rw-r--r--arch/sparc64/kernel/sun4v_ivec.S30
-rw-r--r--arch/sparc64/kernel/time.c49
-rw-r--r--arch/sparc64/kernel/traps.c27
-rw-r--r--arch/sparc64/kernel/vmlinux.lds.S4
-rw-r--r--arch/sparc64/mm/init.c90
-rw-r--r--arch/sparc64/prom/misc.c19
24 files changed, 2070 insertions, 502 deletions
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index 831781cab27..bd00f89eed1 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -147,10 +147,10 @@ config SMP
If you don't know what to do here, say N.
config NR_CPUS
- int "Maximum number of CPUs (2-64)"
- range 2 64
+ int "Maximum number of CPUs (2-1024)"
+ range 2 1024
depends on SMP
- default "32"
+ default "64"
source "drivers/cpufreq/Kconfig"
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 6bf6fb65bc2..d8d19093d12 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -8,11 +8,11 @@ EXTRA_CFLAGS := -Werror
extra-y := head.o init_task.o vmlinux.lds
obj-y := process.o setup.o cpu.o idprom.o \
- traps.o devices.o auxio.o una_asm.o \
+ traps.o auxio.o una_asm.o \
irq.o ptrace.o time.o sys_sparc.o signal.o \
unaligned.o central.o pci.o starfire.o semaphore.o \
power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
- visemul.o prom.o of_device.o
+ visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c
deleted file mode 100644
index 0e03c8e218c..00000000000
--- a/arch/sparc64/kernel/devices.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/* devices.c: Initial scan of the prom device tree for important
- * Sparc device nodes which we need to find.
- *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <linux/kernel.h>
-#include <linux/threads.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/bootmem.h>
-
-#include <asm/page.h>
-#include <asm/oplib.h>
-#include <asm/system.h>
-#include <asm/smp.h>
-#include <asm/spitfire.h>
-#include <asm/timer.h>
-#include <asm/cpudata.h>
-
-/* Used to synchronize accesses to NatSemi SUPER I/O chip configure
- * operations in asm/ns87303.h
- */
-DEFINE_SPINLOCK(ns87303_lock);
-
-extern void cpu_probe(void);
-extern void central_probe(void);
-
-static const char *cpu_mid_prop(void)
-{
- if (tlb_type == spitfire)
- return "upa-portid";
- return "portid";
-}
-
-static int get_cpu_mid(struct device_node *dp)
-{
- struct property *prop;
-
- if (tlb_type == hypervisor) {
- struct linux_prom64_registers *reg;
- int len;
-
- prop = of_find_property(dp, "cpuid", &len);
- if (prop && len == 4)
- return *(int *) prop->value;
-
- prop = of_find_property(dp, "reg", NULL);
- reg = prop->value;
- return (reg[0].phys_addr >> 32) & 0x0fffffffUL;
- } else {
- const char *prop_name = cpu_mid_prop();
-
- prop = of_find_property(dp, prop_name, NULL);
- if (prop)
- return *(int *) prop->value;
- return 0;
- }
-}
-
-static int check_cpu_node(struct device_node *dp, int *cur_inst,
- int (*compare)(struct device_node *, int, void *),
- void *compare_arg,
- struct device_node **dev_node, int *mid)
-{
- if (!compare(dp, *cur_inst, compare_arg)) {
- if (dev_node)
- *dev_node = dp;
- if (mid)
- *mid = get_cpu_mid(dp);
- return 0;
- }
-
- (*cur_inst)++;
-
- return -ENODEV;
-}
-
-static int __cpu_find_by(int (*compare)(struct device_node *, int, void *),
- void *compare_arg,
- struct device_node **dev_node, int *mid)
-{
- struct device_node *dp;
- int cur_inst;
-
- cur_inst = 0;
- for_each_node_by_type(dp, "cpu") {
- int err = check_cpu_node(dp, &cur_inst,
- compare, compare_arg,
- dev_node, mid);
- if (err == 0)
- return 0;
- }
-
- return -ENODEV;
-}
-
-static int cpu_instance_compare(struct device_node *dp, int instance, void *_arg)
-{
- int desired_instance = (int) (long) _arg;
-
- if (instance == desired_instance)
- return 0;
- return -ENODEV;
-}
-
-int cpu_find_by_instance(int instance, struct device_node **dev_node, int *mid)
-{
- return __cpu_find_by(cpu_instance_compare, (void *)(long)instance,
- dev_node, mid);
-}
-
-static int cpu_mid_compare(struct device_node *dp, int instance, void *_arg)
-{
- int desired_mid = (int) (long) _arg;
- int this_mid;
-
- this_mid = get_cpu_mid(dp);
- if (this_mid == desired_mid)
- return 0;
- return -ENODEV;
-}
-
-int cpu_find_by_mid(int mid, struct device_node **dev_node)
-{
- return __cpu_find_by(cpu_mid_compare, (void *)(long)mid,
- dev_node, NULL);
-}
-
-void __init device_scan(void)
-{
- /* FIX ME FAST... -DaveM */
- ioport_resource.end = 0xffffffffffffffffUL;
-
- prom_printf("Booting Linux...\n");
-
-#ifndef CONFIG_SMP
- {
- struct device_node *dp;
- int err, def;
-
- err = cpu_find_by_instance(0, &dp, NULL);
- if (err) {
- prom_printf("No cpu nodes, cannot continue\n");
- prom_halt();
- }
- cpu_data(0).clock_tick =
- of_getintprop_default(dp, "clock-frequency", 0);
-
- def = ((tlb_type == hypervisor) ?
- (8 * 1024) :
- (16 * 1024));
- cpu_data(0).dcache_size = of_getintprop_default(dp,
- "dcache-size",
- def);
-
- def = 32;
- cpu_data(0).dcache_line_size =
- of_getintprop_default(dp, "dcache-line-size", def);
-
- def = 16 * 1024;
- cpu_data(0).icache_size = of_getintprop_default(dp,
- "icache-size",
- def);
-
- def = 32;
- cpu_data(0).icache_line_size =
- of_getintprop_default(dp, "icache-line-size", def);
-
- def = ((tlb_type == hypervisor) ?
- (3 * 1024 * 1024) :
- (4 * 1024 * 1024));
- cpu_data(0).ecache_size = of_getintprop_default(dp,
- "ecache-size",
- def);
-
- def = 64;
- cpu_data(0).ecache_line_size =
- of_getintprop_default(dp, "ecache-line-size", def);
- printk("CPU[0]: Caches "
- "D[sz(%d):line_sz(%d)] "
- "I[sz(%d):line_sz(%d)] "
- "E[sz(%d):line_sz(%d)]\n",
- cpu_data(0).dcache_size, cpu_data(0).dcache_line_size,
- cpu_data(0).icache_size, cpu_data(0).icache_line_size,
- cpu_data(0).ecache_size, cpu_data(0).ecache_line_size);
- }
-#endif
-
- central_probe();
-
- cpu_probe();
-}
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S
index c15a3edcb82..8f10dda0f5c 100644
--- a/arch/sparc64/kernel/entry.S
+++ b/arch/sparc64/kernel/entry.S
@@ -1725,96 +1725,142 @@ real_hard_smp_processor_id:
* returns %o0: sysino
*/
.globl sun4v_devino_to_sysino
+ .type sun4v_devino_to_sysino,#function
sun4v_devino_to_sysino:
mov HV_FAST_INTR_DEVINO2SYSINO, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
+ .size sun4v_devino_to_sysino, .-sun4v_devino_to_sysino
/* %o0: sysino
*
* returns %o0: intr_enabled (HV_INTR_{DISABLED,ENABLED})
*/
.globl sun4v_intr_getenabled
+ .type sun4v_intr_getenabled,#function
sun4v_intr_getenabled:
mov HV_FAST_INTR_GETENABLED, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
+ .size sun4v_intr_getenabled, .-sun4v_intr_getenabled
/* %o0: sysino
* %o1: intr_enabled (HV_INTR_{DISABLED,ENABLED})
*/
.globl sun4v_intr_setenabled
+ .type sun4v_intr_setenabled,#function
sun4v_intr_setenabled:
mov HV_FAST_INTR_SETENABLED, %o5
ta HV_FAST_TRAP
retl
nop
+ .size sun4v_intr_setenabled, .-sun4v_intr_setenabled
/* %o0: sysino
*
* returns %o0: intr_state (HV_INTR_STATE_*)
*/
.globl sun4v_intr_getstate
+ .type sun4v_intr_getstate,#function
sun4v_intr_getstate:
mov HV_FAST_INTR_GETSTATE, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
+ .size sun4v_intr_getstate, .-sun4v_intr_getstate
/* %o0: sysino
* %o1: intr_state (HV_INTR_STATE_*)
*/
.globl sun4v_intr_setstate
+ .type sun4v_intr_setstate,#function
sun4v_intr_setstate:
mov HV_FAST_INTR_SETSTATE, %o5
ta HV_FAST_TRAP
retl
nop
+ .size sun4v_intr_setstate, .-sun4v_intr_setstate
/* %o0: sysino
*
* returns %o0: cpuid
*/
.globl sun4v_intr_gettarget
+ .type sun4v_intr_gettarget,#function
sun4v_intr_gettarget:
mov HV_FAST_INTR_GETTARGET, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
+ .size sun4v_intr_gettarget, .-sun4v_intr_gettarget
/* %o0: sysino
* %o1: cpuid
*/
.globl sun4v_intr_settarget
+ .type sun4v_intr_settarget,#function
sun4v_intr_settarget:
mov HV_FAST_INTR_SETTARGET, %o5
ta HV_FAST_TRAP
retl
nop
+ .size sun4v_intr_settarget, .-sun4v_intr_settarget
- /* %o0: type
- * %o1: queue paddr
- * %o2: num queue entries
+ /* %o0: cpuid
+ * %o1: pc
+ * %o2: rtba
+ * %o3: arg0
*
* returns %o0: status
*/
- .globl sun4v_cpu_qconf
-sun4v_cpu_qconf:
- mov HV_FAST_CPU_QCONF, %o5
+ .globl sun4v_cpu_start
+ .type sun4v_cpu_start,#function
+sun4v_cpu_start:
+ mov HV_FAST_CPU_START, %o5
ta HV_FAST_TRAP
retl
nop
+ .size sun4v_cpu_start, .-sun4v_cpu_start
- /* returns %o0: status
+ /* %o0: cpuid
+ *
+ * returns %o0: status
*/
+ .globl sun4v_cpu_stop
+ .type sun4v_cpu_stop,#function
+sun4v_cpu_stop:
+ mov HV_FAST_CPU_STOP, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_cpu_stop, .-sun4v_cpu_stop
+
+ /* returns %o0: status */
.globl sun4v_cpu_yield
+ .type sun4v_cpu_yield, #function
sun4v_cpu_yield:
mov HV_FAST_CPU_YIELD, %o5
ta HV_FAST_TRAP
retl
nop
+ .size sun4v_cpu_yield, .-sun4v_cpu_yield
+
+ /* %o0: type
+ * %o1: queue paddr
+ * %o2: num queue entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_cpu_qconf
+ .type sun4v_cpu_qconf,#function
+sun4v_cpu_qconf:
+ mov HV_FAST_CPU_QCONF, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_cpu_qconf, .-sun4v_cpu_qconf
/* %o0: num cpus in cpu list
* %o1: cpu list paddr
@@ -1823,11 +1869,13 @@ sun4v_cpu_yield:
* returns %o0: status
*/
.globl sun4v_cpu_mondo_send
+ .type sun4v_cpu_mondo_send,#function
sun4v_cpu_mondo_send:
mov HV_FAST_CPU_MONDO_SEND, %o5
ta HV_FAST_TRAP
retl
nop
+ .size sun4v_cpu_mondo_send, .-sun4v_cpu_mondo_send
/* %o0: CPU ID
*
@@ -1835,6 +1883,7 @@ sun4v_cpu_mondo_send:
* %o0: cpu state as HV_CPU_STATE_*
*/
.globl sun4v_cpu_state
+ .type sun4v_cpu_state,#function
sun4v_cpu_state:
mov HV_FAST_CPU_STATE, %o5
ta HV_FAST_TRAP
@@ -1843,3 +1892,609 @@ sun4v_cpu_state:
mov %o1, %o0
1: retl
nop
+ .size sun4v_cpu_state, .-sun4v_cpu_state
+
+ /* %o0: virtual address
+ * %o1: must be zero
+ * %o2: TTE
+ * %o3: HV_MMU_* flags
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_mmu_map_perm_addr
+ .type sun4v_mmu_map_perm_addr,#function
+sun4v_mmu_map_perm_addr:
+ mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_mmu_map_perm_addr, .-sun4v_mmu_map_perm_addr
+
+ /* %o0: number of TSB descriptions
+ * %o1: TSB descriptions real address
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_mmu_tsb_ctx0
+ .type sun4v_mmu_tsb_ctx0,#function
+sun4v_mmu_tsb_ctx0:
+ mov HV_FAST_MMU_TSB_CTX0, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_mmu_tsb_ctx0, .-sun4v_mmu_tsb_ctx0
+
+ /* %o0: API group number
+ * %o1: pointer to unsigned long major number storage
+ * %o2: pointer to unsigned long minor number storage
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_get_version
+ .type sun4v_get_version,#function
+sun4v_get_version:
+ mov HV_CORE_GET_VER, %o5
+ mov %o1, %o3
+ mov %o2, %o4
+ ta HV_CORE_TRAP
+ stx %o1, [%o3]
+ retl
+ stx %o2, [%o4]
+ .size sun4v_get_version, .-sun4v_get_version
+
+ /* %o0: API group number
+ * %o1: desired major number
+ * %o2: desired minor number
+ * %o3: pointer to unsigned long actual minor number storage
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_set_version
+ .type sun4v_set_version,#function
+sun4v_set_version:
+ mov HV_CORE_SET_VER, %o5
+ mov %o3, %o4
+ ta HV_CORE_TRAP
+ retl
+ stx %o1, [%o4]
+ .size sun4v_set_version, .-sun4v_set_version
+
+ /* %o0: pointer to unsigned long time
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_tod_get
+ .type sun4v_tod_get,#function
+sun4v_tod_get:
+ mov %o0, %o4
+ mov HV_FAST_TOD_GET, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ .size sun4v_tod_get, .-sun4v_tod_get
+
+ /* %o0: time
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_tod_set
+ .type sun4v_tod_set,#function
+sun4v_tod_set:
+ mov HV_FAST_TOD_SET, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_tod_set, .-sun4v_tod_set
+
+ /* %o0: pointer to unsigned long status
+ *
+ * returns %o0: signed character
+ */
+ .globl sun4v_con_getchar
+ .type sun4v_con_getchar,#function
+sun4v_con_getchar:
+ mov %o0, %o4
+ mov HV_FAST_CONS_GETCHAR, %o5
+ clr %o0
+ clr %o1
+ ta HV_FAST_TRAP
+ stx %o0, [%o4]
+ retl
+ sra %o1, 0, %o0
+ .size sun4v_con_getchar, .-sun4v_con_getchar
+
+ /* %o0: signed long character
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_con_putchar
+ .type sun4v_con_putchar,#function
+sun4v_con_putchar:
+ mov HV_FAST_CONS_PUTCHAR, %o5
+ ta HV_FAST_TRAP
+ retl
+ sra %o0, 0, %o0
+ .size sun4v_con_putchar, .-sun4v_con_putchar
+
+ /* %o0: buffer real address
+ * %o1: buffer size
+ * %o2: pointer to unsigned long bytes_read
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_con_read
+ .type sun4v_con_read,#function
+sun4v_con_read:
+ mov %o2, %o4
+ mov HV_FAST_CONS_READ, %o5
+ ta HV_FAST_TRAP
+ brnz %o0, 1f
+ cmp %o1, -1 /* break */
+ be,a,pn %icc, 1f
+ mov %o1, %o0
+ cmp %o1, -2 /* hup */
+ be,a,pn %icc, 1f
+ mov %o1, %o0
+ stx %o1, [%o4]
+1: retl
+ nop
+ .size sun4v_con_read, .-sun4v_con_read
+
+ /* %o0: buffer real address
+ * %o1: buffer size
+ * %o2: pointer to unsigned long bytes_written
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_con_write
+ .type sun4v_con_write,#function
+sun4v_con_write:
+ mov %o2, %o4
+ mov HV_FAST_CONS_WRITE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ .size sun4v_con_write, .-sun4v_con_write
+
+ /* %o0: soft state
+ * %o1: address of description string
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_mach_set_soft_state
+ .type sun4v_mach_set_soft_state,#function
+sun4v_mach_set_soft_state:
+ mov HV_FAST_MACH_SET_SOFT_STATE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_mach_set_soft_state, .-sun4v_mach_set_soft_state
+
+ /* %o0: exit code
+ *
+ * Does not return.
+ */
+ .globl sun4v_mach_exit
+ .type sun4v_mach_exit,#function
+sun4v_mach_exit:
+ mov HV_FAST_MACH_EXIT, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_mach_exit, .-sun4v_mach_exit
+
+ /* %o0: buffer real address
+ * %o1: buffer length
+ * %o2: pointer to unsigned long real_buf_len
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_mach_desc
+ .type sun4v_mach_desc,#function
+sun4v_mach_desc:
+ mov %o2, %o4
+ mov HV_FAST_MACH_DESC, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ .size sun4v_mach_desc, .-sun4v_mach_desc
+
+ /* %o0: new timeout in milliseconds
+ * %o1: pointer to unsigned long orig_timeout
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_mach_set_watchdog
+ .type sun4v_mach_set_watchdog,#function
+sun4v_mach_set_watchdog:
+ mov %o1, %o4
+ mov HV_FAST_MACH_SET_WATCHDOG, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ .size sun4v_mach_set_watchdog, .-sun4v_mach_set_watchdog
+
+ /* No inputs and does not return. */
+ .globl sun4v_mach_sir
+ .type sun4v_mach_sir,#function
+sun4v_mach_sir:
+ mov %o1, %o4
+ mov HV_FAST_MACH_SIR, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ .size sun4v_mach_sir, .-sun4v_mach_sir
+
+ /* %o0: channel
+ * %o1: ra
+ * %o2: num_entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_tx_qconf
+ .type sun4v_ldc_tx_qconf,#function
+sun4v_ldc_tx_qconf:
+ mov HV_FAST_LDC_TX_QCONF, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_tx_qconf, .-sun4v_ldc_tx_qconf
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long ra
+ * %o2: pointer to unsigned long num_entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_tx_qinfo
+ .type sun4v_ldc_tx_qinfo,#function
+sun4v_ldc_tx_qinfo:
+ mov %o1, %g1
+ mov %o2, %g2
+ mov HV_FAST_LDC_TX_QINFO, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ .size sun4v_ldc_tx_qinfo, .-sun4v_ldc_tx_qinfo
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long head_off
+ * %o2: pointer to unsigned long tail_off
+ * %o2: pointer to unsigned long chan_state
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_tx_get_state
+ .type sun4v_ldc_tx_get_state,#function
+sun4v_ldc_tx_get_state:
+ mov %o1, %g1
+ mov %o2, %g2
+ mov %o3, %g3
+ mov HV_FAST_LDC_TX_GET_STATE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ stx %o3, [%g3]
+ retl
+ nop
+ .size sun4v_ldc_tx_get_state, .-sun4v_ldc_tx_get_state
+
+ /* %o0: channel
+ * %o1: tail_off
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_tx_set_qtail
+ .type sun4v_ldc_tx_set_qtail,#function
+sun4v_ldc_tx_set_qtail:
+ mov HV_FAST_LDC_TX_SET_QTAIL, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_tx_set_qtail, .-sun4v_ldc_tx_set_qtail
+
+ /* %o0: channel
+ * %o1: ra
+ * %o2: num_entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_rx_qconf
+ .type sun4v_ldc_rx_qconf,#function
+sun4v_ldc_rx_qconf:
+ mov HV_FAST_LDC_RX_QCONF, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_rx_qconf, .-sun4v_ldc_rx_qconf
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long ra
+ * %o2: pointer to unsigned long num_entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_rx_qinfo
+ .type sun4v_ldc_rx_qinfo,#function
+sun4v_ldc_rx_qinfo:
+ mov %o1, %g1
+ mov %o2, %g2
+ mov HV_FAST_LDC_RX_QINFO, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ .size sun4v_ldc_rx_qinfo, .-sun4v_ldc_rx_qinfo
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long head_off
+ * %o2: pointer to unsigned long tail_off
+ * %o2: pointer to unsigned long chan_state
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_rx_get_state
+ .type sun4v_ldc_rx_get_state,#function
+sun4v_ldc_rx_get_state:
+ mov %o1, %g1
+ mov %o2, %g2
+ mov %o3, %g3
+ mov HV_FAST_LDC_RX_GET_STATE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ stx %o3, [%g3]
+ retl
+ nop
+ .size sun4v_ldc_rx_get_state, .-sun4v_ldc_rx_get_state
+
+ /* %o0: channel
+ * %o1: head_off
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_rx_set_qhead
+ .type sun4v_ldc_rx_set_qhead,#function
+sun4v_ldc_rx_set_qhead:
+ mov HV_FAST_LDC_RX_SET_QHEAD, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_rx_set_qhead, .-sun4v_ldc_rx_set_qhead
+
+ /* %o0: channel
+ * %o1: ra
+ * %o2: num_entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_set_map_table
+ .type sun4v_ldc_set_map_table,#function
+sun4v_ldc_set_map_table:
+ mov HV_FAST_LDC_SET_MAP_TABLE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_set_map_table, .-sun4v_ldc_set_map_table
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long ra
+ * %o2: pointer to unsigned long num_entries
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_get_map_table
+ .type sun4v_ldc_get_map_table,#function
+sun4v_ldc_get_map_table:
+ mov %o1, %g1
+ mov %o2, %g2
+ mov HV_FAST_LDC_GET_MAP_TABLE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ .size sun4v_ldc_get_map_table, .-sun4v_ldc_get_map_table
+
+ /* %o0: channel
+ * %o1: dir_code
+ * %o2: tgt_raddr
+ * %o3: lcl_raddr
+ * %o4: len
+ * %o5: pointer to unsigned long actual_len
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_copy
+ .type sun4v_ldc_copy,#function
+sun4v_ldc_copy:
+ mov %o5, %g1
+ mov HV_FAST_LDC_COPY, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ .size sun4v_ldc_copy, .-sun4v_ldc_copy
+
+ /* %o0: channel
+ * %o1: cookie
+ * %o2: pointer to unsigned long ra
+ * %o3: pointer to unsigned long perm
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_mapin
+ .type sun4v_ldc_mapin,#function
+sun4v_ldc_mapin:
+ mov %o2, %g1
+ mov %o3, %g2
+ mov HV_FAST_LDC_MAPIN, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ .size sun4v_ldc_mapin, .-sun4v_ldc_mapin
+
+ /* %o0: ra
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_unmap
+ .type sun4v_ldc_unmap,#function
+sun4v_ldc_unmap:
+ mov HV_FAST_LDC_UNMAP, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_unmap, .-sun4v_ldc_unmap
+
+ /* %o0: cookie
+ * %o1: mte_cookie
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_ldc_revoke
+ .type sun4v_ldc_revoke,#function
+sun4v_ldc_revoke:
+ mov HV_FAST_LDC_REVOKE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_ldc_revoke, .-sun4v_ldc_revoke
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long cookie
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_get_cookie
+ .type sun4v_vintr_get_cookie,#function
+sun4v_vintr_get_cookie:
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_COOKIE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ .size sun4v_vintr_get_cookie, .-sun4v_vintr_get_cookie
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: cookie
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_set_cookie
+ .type sun4v_vintr_set_cookie,#function
+sun4v_vintr_set_cookie:
+ mov HV_FAST_VINTR_SET_COOKIE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_vintr_set_cookie, .-sun4v_vintr_set_cookie
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long valid_state
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_get_valid
+ .type sun4v_vintr_get_valid,#function
+sun4v_vintr_get_valid:
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_VALID, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ .size sun4v_vintr_get_valid, .-sun4v_vintr_get_valid
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: valid_state
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_set_valid
+ .type sun4v_vintr_set_valid,#function
+sun4v_vintr_set_valid:
+ mov HV_FAST_VINTR_SET_VALID, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_vintr_set_valid, .-sun4v_vintr_set_valid
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long state
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_get_state
+ .type sun4v_vintr_get_state,#function
+sun4v_vintr_get_state:
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_STATE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ .size sun4v_vintr_get_state, .-sun4v_vintr_get_state
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: state
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_set_state
+ .type sun4v_vintr_set_state,#function
+sun4v_vintr_set_state:
+ mov HV_FAST_VINTR_SET_STATE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_vintr_set_state, .-sun4v_vintr_set_state
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long cpuid
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_get_target
+ .type sun4v_vintr_get_target,#function
+sun4v_vintr_get_target:
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_TARGET, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ .size sun4v_vintr_get_target, .-sun4v_vintr_get_target
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: cpuid
+ *
+ * returns %o0: status
+ */
+ .globl sun4v_vintr_set_target
+ .type sun4v_vintr_set_target,#function
+sun4v_vintr_set_target:
+ mov HV_FAST_VINTR_SET_TARGET, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ .size sun4v_vintr_set_target, .-sun4v_vintr_set_target
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
index baea10a9819..77259526cb1 100644
--- a/arch/sparc64/kernel/head.S
+++ b/arch/sparc64/kernel/head.S
@@ -523,7 +523,7 @@ tlb_fixup_done:
#else
mov 0, %o0
#endif
- stb %o0, [%g6 + TI_CPU]
+ sth %o0, [%g6 + TI_CPU]
/* Off we go.... */
call start_kernel
@@ -653,33 +653,54 @@ setup_tba:
restore
sparc64_boot_end:
-#include "ktlb.S"
-#include "tsb.S"
#include "etrap.S"
#include "rtrap.S"
#include "winfixup.S"
#include "entry.S"
#include "sun4v_tlb_miss.S"
#include "sun4v_ivec.S"
+#include "ktlb.S"
+#include "tsb.S"
/*
* The following skip makes sure the trap table in ttable.S is aligned
* on a 32K boundary as required by the v9 specs for TBA register.
*
* We align to a 32K boundary, then we have the 32K kernel TSB,
- * then the 32K aligned trap table.
+ * the 64K kernel 4MB TSB, and then the 32K aligned trap table.
*/
1:
.skip 0x4000 + _start - 1b
+! 0x0000000000408000
+
.globl swapper_tsb
swapper_tsb:
.skip (32 * 1024)
-! 0x0000000000408000
+ .globl swapper_4m_tsb
+swapper_4m_tsb:
+ .skip (64 * 1024)
+
+! 0x0000000000420000
+ /* Some care needs to be exercised if you try to move the
+ * location of the trap table relative to other things. For
+ * one thing there are br* instructions in some of the
+ * trap table entires which branch back to code in ktlb.S
+ * Those instructions can only handle a signed 16-bit
+ * displacement.
+ *
+ * There is a binutils bug (bugzilla #4558) which causes
+ * the relocation overflow checks for such instructions to
+ * not be done correctly. So bintuils will not notice the
+ * error and will instead write junk into the relocation and
+ * you'll have an unbootable kernel.
+ */
#include "ttable.S"
+! 0x0000000000428000
+
#include "systbls.S"
.data
diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c
new file mode 100644
index 00000000000..f34f5d6181e
--- /dev/null
+++ b/arch/sparc64/kernel/hvapi.c
@@ -0,0 +1,192 @@
+/* hvapi.c: Hypervisor API management.
+ *
+ * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <asm/hypervisor.h>
+#include <asm/oplib.h>
+#include <asm/sstate.h>
+
+/* If the hypervisor indicates that the API setting
+ * calls are unsupported, by returning HV_EBADTRAP or
+ * HV_ENOTSUPPORTED, we assume that API groups with the
+ * PRE_API flag set are major 1 minor 0.
+ */
+struct api_info {
+ unsigned long group;
+ unsigned long major;
+ unsigned long minor;
+ unsigned int refcnt;
+ unsigned int flags;
+#define FLAG_PRE_API 0x00000001
+};
+
+static struct api_info api_table[] = {
+ { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_CORE, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_INTR, },
+ { .group = HV_GRP_SOFT_STATE, },
+ { .group = HV_GRP_PCI, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_LDOM, },
+ { .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_NCS, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_FIRE_PERF, },
+ { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API },
+};
+
+static DEFINE_SPINLOCK(hvapi_lock);
+
+static struct api_info *__get_info(unsigned long group)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(api_table); i++) {
+ if (api_table[i].group == group)
+ return &api_table[i];
+ }
+ return NULL;
+}
+
+static void __get_ref(struct api_info *p)
+{
+ p->refcnt++;
+}
+
+static void __put_ref(struct api_info *p)
+{
+ if (--p->refcnt == 0) {
+ unsigned long ignore;
+
+ sun4v_set_version(p->group, 0, 0, &ignore);
+ p->major = p->minor = 0;
+ }
+}
+
+/* Register a hypervisor API specification. It indicates the
+ * API group and desired major+minor.
+ *
+ * If an existing API registration exists '0' (success) will
+ * be returned if it is compatible with the one being registered.
+ * Otherwise a negative error code will be returned.
+ *
+ * Otherwise an attempt will be made to negotiate the requested
+ * API group/major/minor with the hypervisor, and errors returned
+ * if that does not succeed.
+ */
+int sun4v_hvapi_register(unsigned long group, unsigned long major,
+ unsigned long *minor)
+{
+ struct api_info *p;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&hvapi_lock, flags);
+ p = __get_info(group);
+ ret = -EINVAL;
+ if (p) {
+ if (p->refcnt) {
+ ret = -EINVAL;
+ if (p->major == major) {
+ *minor = p->minor;
+ ret = 0;
+ }
+ } else {
+ unsigned long actual_minor;
+ unsigned long hv_ret;
+
+ hv_ret = sun4v_set_version(group, major, *minor,
+ &actual_minor);
+ ret = -EINVAL;
+ if (hv_ret == HV_EOK) {
+ *minor = actual_minor;
+ p->major = major;
+ p->minor = actual_minor;
+ ret = 0;
+ } else if (hv_ret == HV_EBADTRAP ||
+ hv_ret == HV_ENOTSUPPORTED) {
+ if (p->flags & FLAG_PRE_API) {
+ if (major == 1) {
+ p->major = 1;
+ p->minor = 0;
+ *minor = 0;
+ ret = 0;
+ }
+ }
+ }
+ }
+
+ if (ret == 0)
+ __get_ref(p);
+ }
+ spin_unlock_irqrestore(&hvapi_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(sun4v_hvapi_register);
+
+void sun4v_hvapi_unregister(unsigned long group)
+{
+ struct api_info *p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hvapi_lock, flags);
+ p = __get_info(group);
+ if (p)
+ __put_ref(p);
+ spin_unlock_irqrestore(&hvapi_lock, flags);
+}
+EXPORT_SYMBOL(sun4v_hvapi_unregister);
+
+int sun4v_hvapi_get(unsigned long group,
+ unsigned long *major,
+ unsigned long *minor)
+{
+ struct api_info *p;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&hvapi_lock, flags);
+ ret = -EINVAL;
+ p = __get_info(group);
+ if (p && p->refcnt) {
+ *major = p->major;
+ *minor = p->minor;
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&hvapi_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(sun4v_hvapi_get);
+
+void __init sun4v_hvapi_init(void)
+{
+ unsigned long group, major, minor;
+
+ group = HV_GRP_SUN4V;
+ major = 1;
+ minor = 0;
+ if (sun4v_hvapi_register(group, major, &minor))
+ goto bad;
+
+ group = HV_GRP_CORE;
+ major = 1;
+ minor = 1;
+ if (sun4v_hvapi_register(group, major, &minor))
+ goto bad;
+
+ sun4v_sstate_init();
+
+ return;
+
+bad:
+ prom_printf("HVAPI: Cannot register API group "
+ "%lx with major(%u) minor(%u)\n",
+ group, major, minor);
+ prom_halt();
+}
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index 3edc18e1b81..a36f8dd0c02 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -171,8 +171,6 @@ skip:
return 0;
}
-extern unsigned long real_hard_smp_processor_id(void);
-
static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
{
unsigned int tid;
@@ -694,9 +692,20 @@ void init_irqwork_curcpu(void)
trap_block[cpu].irq_worklist = 0;
}
-static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type)
+/* Please be very careful with register_one_mondo() and
+ * sun4v_register_mondo_queues().
+ *
+ * On SMP this gets invoked from the CPU trampoline before
+ * the cpu has fully taken over the trap table from OBP,
+ * and it's kernel stack + %g6 thread register state is
+ * not fully cooked yet.
+ *
+ * Therefore you cannot make any OBP calls, not even prom_printf,
+ * from these two routines.
+ */
+static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type, unsigned long qmask)
{
- unsigned long num_entries = 128;
+ unsigned long num_entries = (qmask + 1) / 64;
unsigned long status;
status = sun4v_cpu_qconf(type, paddr, num_entries);
@@ -711,44 +720,58 @@ static void __cpuinit sun4v_register_mondo_queues(int this_cpu)
{
struct trap_per_cpu *tb = &trap_block[this_cpu];
- register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO);
- register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO);
- register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR);
- register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR);
+ register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO,
+ tb->cpu_mondo_qmask);
+ register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO,
+ tb->dev_mondo_qmask);
+ register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR,
+ tb->resum_qmask);
+ register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR,
+ tb->nonresum_qmask);
}
-static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, int use_bootmem)
+static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask, int use_bootmem)
{
- void *page;
+ unsigned long size = PAGE_ALIGN(qmask + 1);
+ unsigned long order = get_order(size);
+ void *p = NULL;
- if (use_bootmem)
- page = alloc_bootmem_low_pages(PAGE_SIZE);
- else
- page = (void *) get_zeroed_page(GFP_ATOMIC);
+ if (use_bootmem) {
+ p = __alloc_bootmem_low(size, size, 0);
+ } else {
+ struct page *page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, order);
+ if (page)
+ p = page_address(page);
+ }
- if (!page) {
+ if (!p) {
prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
prom_halt();
}
- *pa_ptr = __pa(page);
+ *pa_ptr = __pa(p);
}
-static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, int use_bootmem)
+static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask, int use_bootmem)
{
- void *page;
+ unsigned long size = PAGE_ALIGN(qmask + 1);
+ unsigned long order = get_order(size);
+ void *p = NULL;
- if (use_bootmem)
- page = alloc_bootmem_low_pages(PAGE_SIZE);
- else
- page = (void *) get_zeroed_page(GFP_ATOMIC);
+ if (use_bootmem) {
+ p = __alloc_bootmem_low(size, size, 0);
+ } else {
+ struct page *page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, order);
+ if (page)
+ p = page_address(page);
+ }
- if (!page) {
+ if (!p) {
prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
prom_halt();
}
- *pa_ptr = __pa(page);
+ *pa_ptr = __pa(p);
}
static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_bootmem)
@@ -779,12 +802,12 @@ void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int
struct trap_per_cpu *tb = &trap_block[cpu];
if (alloc) {
- alloc_one_mondo(&tb->cpu_mondo_pa, use_bootmem);
- alloc_one_mondo(&tb->dev_mondo_pa, use_bootmem);
- alloc_one_mondo(&tb->resum_mondo_pa, use_bootmem);
- alloc_one_kbuf(&tb->resum_kernel_buf_pa, use_bootmem);
- alloc_one_mondo(&tb->nonresum_mondo_pa, use_bootmem);
- alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, use_bootmem);
+ alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask, use_bootmem);
+ alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask, use_bootmem);
+ alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask, use_bootmem);
+ alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask, use_bootmem);
+ alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask, use_bootmem);
+ alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, tb->nonresum_qmask, use_bootmem);
init_cpu_send_mondo_info(tb, use_bootmem);
}
diff --git a/arch/sparc64/kernel/itlb_miss.S b/arch/sparc64/kernel/itlb_miss.S
index ad46e2024f4..5a8377b5495 100644
--- a/arch/sparc64/kernel/itlb_miss.S
+++ b/arch/sparc64/kernel/itlb_miss.S
@@ -11,12 +11,12 @@
/* ITLB ** ICACHE line 2: TSB compare and TLB load */
bne,pn %xcc, tsb_miss_itlb ! Miss
mov FAULT_CODE_ITLB, %g3
- andcc %g5, _PAGE_EXEC_4U, %g0 ! Executable?
+ sethi %hi(_PAGE_EXEC_4U), %g4
+ andcc %g5, %g4, %g0 ! Executable?
be,pn %xcc, tsb_do_fault
nop ! Delay slot, fill me
stxa %g5, [%g0] ASI_ITLB_DATA_IN ! Load TLB
retry ! Trap done
- nop
/* ITLB ** ICACHE line 3: */
nop
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c
new file mode 100644
index 00000000000..9246c2cf957
--- /dev/null
+++ b/arch/sparc64/kernel/mdesc.c
@@ -0,0 +1,619 @@
+/* mdesc.c: Sun4V machine description handling.
+ *
+ * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/bootmem.h>
+#include <linux/log2.h>
+
+#include <asm/hypervisor.h>
+#include <asm/mdesc.h>
+#include <asm/prom.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+
+/* Unlike the OBP device tree, the machine description is a full-on
+ * DAG. An arbitrary number of ARCs are possible from one
+ * node to other nodes and thus we can't use the OBP device_node
+ * data structure to represent these nodes inside of the kernel.
+ *
+ * Actually, it isn't even a DAG, because there are back pointers
+ * which create cycles in the graph.
+ *
+ * mdesc_hdr and mdesc_elem describe the layout of the data structure
+ * we get from the Hypervisor.
+ */
+struct mdesc_hdr {
+ u32 version; /* Transport version */
+ u32 node_sz; /* node block size */
+ u32 name_sz; /* name block size */
+ u32 data_sz; /* data block size */
+};
+
+struct mdesc_elem {
+ u8 tag;
+#define MD_LIST_END 0x00
+#define MD_NODE 0x4e
+#define MD_NODE_END 0x45
+#define MD_NOOP 0x20
+#define MD_PROP_ARC 0x61
+#define MD_PROP_VAL 0x76
+#define MD_PROP_STR 0x73
+#define MD_PROP_DATA 0x64
+ u8 name_len;
+ u16 resv;
+ u32 name_offset;
+ union {
+ struct {
+ u32 data_len;
+ u32 data_offset;
+ } data;
+ u64 val;
+ } d;
+};
+
+static struct mdesc_hdr *main_mdesc;
+static struct mdesc_node *allnodes;
+
+static struct mdesc_node *allnodes_tail;
+static unsigned int unique_id;
+
+static struct mdesc_node **mdesc_hash;
+static unsigned int mdesc_hash_size;
+
+static inline unsigned int node_hashfn(u64 node)
+{
+ return ((unsigned int) (node ^ (node >> 8) ^ (node >> 16)))
+ & (mdesc_hash_size - 1);
+}
+
+static inline void hash_node(struct mdesc_node *mp)
+{
+ struct mdesc_node **head = &mdesc_hash[node_hashfn(mp->node)];
+
+ mp->hash_next = *head;
+ *head = mp;
+
+ if (allnodes_tail) {
+ allnodes_tail->allnodes_next = mp;
+ allnodes_tail = mp;
+ } else {
+ allnodes = allnodes_tail = mp;
+ }
+}
+
+static struct mdesc_node *find_node(u64 node)
+{
+ struct mdesc_node *mp = mdesc_hash[node_hashfn(node)];
+
+ while (mp) {
+ if (mp->node == node)
+ return mp;
+
+ mp = mp->hash_next;
+ }
+ return NULL;
+}
+
+struct property *md_find_property(const struct mdesc_node *mp,
+ const char *name,
+ int *lenp)
+{
+ struct property *pp;
+
+ for (pp = mp->properties; pp != 0; pp = pp->next) {
+ if (strcasecmp(pp->name, name) == 0) {
+ if (lenp)
+ *lenp = pp->length;
+ break;
+ }
+ }
+ return pp;
+}
+EXPORT_SYMBOL(md_find_property);
+
+/*
+ * Find a property with a given name for a given node
+ * and return the value.
+ */
+const void *md_get_property(const struct mdesc_node *mp, const char *name,
+ int *lenp)
+{
+ struct property *pp = md_find_property(mp, name, lenp);
+ return pp ? pp->value : NULL;
+}
+EXPORT_SYMBOL(md_get_property);
+
+struct mdesc_node *md_find_node_by_name(struct mdesc_node *from,
+ const char *name)
+{
+ struct mdesc_node *mp;
+
+ mp = from ? from->allnodes_next : allnodes;
+ for (; mp != NULL; mp = mp->allnodes_next) {
+ if (strcmp(mp->name, name) == 0)
+ break;
+ }
+ return mp;
+}
+EXPORT_SYMBOL(md_find_node_by_name);
+
+static unsigned int mdesc_early_allocated;
+
+static void * __init mdesc_early_alloc(unsigned long size)
+{
+ void *ret;
+
+ ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
+ if (ret == NULL) {
+ prom_printf("MDESC: alloc of %lu bytes failed.\n", size);
+ prom_halt();
+ }
+
+ memset(ret, 0, size);
+
+ mdesc_early_allocated += size;
+
+ return ret;
+}
+
+static unsigned int __init count_arcs(struct mdesc_elem *ep)
+{
+ unsigned int ret = 0;
+
+ ep++;
+ while (ep->tag != MD_NODE_END) {
+ if (ep->tag == MD_PROP_ARC)
+ ret++;
+ ep++;
+ }
+ return ret;
+}
+
+static void __init mdesc_node_alloc(u64 node, struct mdesc_elem *ep, const char *names)
+{
+ unsigned int num_arcs = count_arcs(ep);
+ struct mdesc_node *mp;
+
+ mp = mdesc_early_alloc(sizeof(*mp) +
+ (num_arcs * sizeof(struct mdesc_arc)));
+ mp->name = names + ep->name_offset;
+ mp->node = node;
+ mp->unique_id = unique_id++;
+ mp->num_arcs = num_arcs;
+
+ hash_node(mp);
+}
+
+static inline struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
+{
+ return (struct mdesc_elem *) (mdesc + 1);
+}
+
+static inline void *name_block(struct mdesc_hdr *mdesc)
+{
+ return ((void *) node_block(mdesc)) + mdesc->node_sz;
+}
+
+static inline void *data_block(struct mdesc_hdr *mdesc)
+{
+ return ((void *) name_block(mdesc)) + mdesc->name_sz;
+}
+
+/* In order to avoid recursion (the graph can be very deep) we use a
+ * two pass algorithm. First we allocate all the nodes and hash them.
+ * Then we iterate over each node, filling in the arcs and properties.
+ */
+static void __init build_all_nodes(struct mdesc_hdr *mdesc)
+{
+ struct mdesc_elem *start, *ep;
+ struct mdesc_node *mp;
+ const char *names;
+ void *data;
+ u64 last_node;
+
+ start = ep = node_block(mdesc);
+ last_node = mdesc->node_sz / 16;
+
+ names = name_block(mdesc);
+
+ while (1) {
+ u64 node = ep - start;
+
+ if (ep->tag == MD_LIST_END)
+ break;
+
+ if (ep->tag != MD_NODE) {
+ prom_printf("MDESC: Inconsistent element list.\n");
+ prom_halt();
+ }
+
+ mdesc_node_alloc(node, ep, names);
+
+ if (ep->d.val >= last_node) {
+ printk("MDESC: Warning, early break out of node scan.\n");
+ printk("MDESC: Next node [%lu] last_node [%lu].\n",
+ node, last_node);
+ break;
+ }
+
+ ep = start + ep->d.val;
+ }
+
+ data = data_block(mdesc);
+ for (mp = allnodes; mp; mp = mp->allnodes_next) {
+ struct mdesc_elem *ep = start + mp->node;
+ struct property **link = &mp->properties;
+ unsigned int this_arc = 0;
+
+ ep++;
+ while (ep->tag != MD_NODE_END) {
+ switch (ep->tag) {
+ case MD_PROP_ARC: {
+ struct mdesc_node *target;
+
+ if (this_arc >= mp->num_arcs) {
+ prom_printf("MDESC: ARC overrun [%u:%u]\n",
+ this_arc, mp->num_arcs);
+ prom_halt();
+ }
+ target = find_node(ep->d.val);
+ if (!target) {
+ printk("MDESC: Warning, arc points to "
+ "missing node, ignoring.\n");
+ break;
+ }
+ mp->arcs[this_arc].name =
+ (names + ep->name_offset);
+ mp->arcs[this_arc].arc = target;
+ this_arc++;
+ break;
+ }
+
+ case MD_PROP_VAL:
+ case MD_PROP_STR:
+ case MD_PROP_DATA: {
+ struct property *p = mdesc_early_alloc(sizeof(*p));
+
+ p->unique_id = unique_id++;
+ p->name = (char *) names + ep->name_offset;
+ if (ep->tag == MD_PROP_VAL) {
+ p->value = &ep->d.val;
+ p->length = 8;
+ } else {
+ p->value = data + ep->d.data.data_offset;
+ p->length = ep->d.data.data_len;
+ }
+ *link = p;
+ link = &p->next;
+ break;
+ }
+
+ case MD_NOOP:
+ break;
+
+ default:
+ printk("MDESC: Warning, ignoring unknown tag type %02x\n",
+ ep->tag);
+ }
+ ep++;
+ }
+ }
+}
+
+static unsigned int __init count_nodes(struct mdesc_hdr *mdesc)
+{
+ struct mdesc_elem *ep = node_block(mdesc);
+ struct mdesc_elem *end;
+ unsigned int cnt = 0;
+
+ end = ((void *)ep) + mdesc->node_sz;
+ while (ep < end) {
+ if (ep->tag == MD_NODE)
+ cnt++;
+ ep++;
+ }
+ return cnt;
+}
+
+static void __init report_platform_properties(void)
+{
+ struct mdesc_node *pn = md_find_node_by_name(NULL, "platform");
+ const char *s;
+ const u64 *v;
+
+ if (!pn) {
+ prom_printf("No platform node in machine-description.\n");
+ prom_halt();
+ }
+
+ s = md_get_property(pn, "banner-name", NULL);
+ printk("PLATFORM: banner-name [%s]\n", s);
+ s = md_get_property(pn, "name", NULL);
+ printk("PLATFORM: name [%s]\n", s);
+
+ v = md_get_property(pn, "hostid", NULL);
+ if (v)
+ printk("PLATFORM: hostid [%08lx]\n", *v);
+ v = md_get_property(pn, "serial#", NULL);
+ if (v)
+ printk("PLATFORM: serial# [%08lx]\n", *v);
+ v = md_get_property(pn, "stick-frequency", NULL);
+ printk("PLATFORM: stick-frequency [%08lx]\n", *v);
+ v = md_get_property(pn, "mac-address", NULL);
+ if (v)
+ printk("PLATFORM: mac-address [%lx]\n", *v);
+ v = md_get_property(pn, "watchdog-resolution", NULL);
+ if (v)
+ printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v);
+ v = md_get_property(pn, "watchdog-max-timeout", NULL);
+ if (v)
+ printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v);
+ v = md_get_property(pn, "max-cpus", NULL);
+ if (v)
+ printk("PLATFORM: max-cpus [%lu]\n", *v);
+}
+
+static int inline find_in_proplist(const char *list, const char *match, int len)
+{
+ while (len > 0) {
+ int l;
+
+ if (!strcmp(list, match))
+ return 1;
+ l = strlen(list) + 1;
+ list += l;
+ len -= l;
+ }
+ return 0;
+}
+
+static void __init fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_node *mp)
+{
+ const u64 *level = md_get_property(mp, "level", NULL);
+ const u64 *size = md_get_property(mp, "size", NULL);
+ const u64 *line_size = md_get_property(mp, "line-size", NULL);
+ const char *type;
+ int type_len;
+
+ type = md_get_property(mp, "type", &type_len);
+
+ switch (*level) {
+ case 1:
+ if (find_in_proplist(type, "instn", type_len)) {
+ c->icache_size = *size;
+ c->icache_line_size = *line_size;
+ } else if (find_in_proplist(type, "data", type_len)) {
+ c->dcache_size = *size;
+ c->dcache_line_size = *line_size;
+ }
+ break;
+
+ case 2:
+ c->ecache_size = *size;
+ c->ecache_line_size = *line_size;
+ break;
+
+ default:
+ break;
+ }
+
+ if (*level == 1) {
+ unsigned int i;
+
+ for (i = 0; i < mp->num_arcs; i++) {
+ struct mdesc_node *t = mp->arcs[i].arc;
+
+ if (strcmp(mp->arcs[i].name, "fwd"))
+ continue;
+
+ if (!strcmp(t->name, "cache"))
+ fill_in_one_cache(c, t);
+ }
+ }
+}
+
+static void __init mark_core_ids(struct mdesc_node *mp, int core_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < mp->num_arcs; i++) {
+ struct mdesc_node *t = mp->arcs[i].arc;
+ const u64 *id;
+
+ if (strcmp(mp->arcs[i].name, "back"))
+ continue;
+
+ if (!strcmp(t->name, "cpu")) {
+ id = md_get_property(t, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_data(*id).core_id = core_id;
+ } else {
+ unsigned int j;
+
+ for (j = 0; j < t->num_arcs; j++) {
+ struct mdesc_node *n = t->arcs[j].arc;
+
+ if (strcmp(t->arcs[j].name, "back"))
+ continue;
+
+ if (strcmp(n->name, "cpu"))
+ continue;
+
+ id = md_get_property(n, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_data(*id).core_id = core_id;
+ }
+ }
+ }
+}
+
+static void __init set_core_ids(void)
+{
+ struct mdesc_node *mp;
+ int idx;
+
+ idx = 1;
+ md_for_each_node_by_name(mp, "cache") {
+ const u64 *level = md_get_property(mp, "level", NULL);
+ const char *type;
+ int len;
+
+ if (*level != 1)
+ continue;
+
+ type = md_get_property(mp, "type", &len);
+ if (!find_in_proplist(type, "instn", len))
+ continue;
+
+ mark_core_ids(mp, idx);
+
+ idx++;
+ }
+}
+
+static void __init get_one_mondo_bits(const u64 *p, unsigned int *mask, unsigned char def)
+{
+ u64 val;
+
+ if (!p)
+ goto use_default;
+ val = *p;
+
+ if (!val || val >= 64)
+ goto use_default;
+
+ *mask = ((1U << val) * 64U) - 1U;
+ return;
+
+use_default:
+ *mask = ((1U << def) * 64U) - 1U;
+}
+
+static void __init get_mondo_data(struct mdesc_node *mp, struct trap_per_cpu *tb)
+{
+ const u64 *val;
+
+ val = md_get_property(mp, "q-cpu-mondo-#bits", NULL);
+ get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7);
+
+ val = md_get_property(mp, "q-dev-mondo-#bits", NULL);
+ get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7);
+
+ val = md_get_property(mp, "q-resumable-#bits", NULL);
+ get_one_mondo_bits(val, &tb->resum_qmask, 6);
+
+ val = md_get_property(mp, "q-nonresumable-#bits", NULL);
+ get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
+}
+
+static void __init mdesc_fill_in_cpu_data(void)
+{
+ struct mdesc_node *mp;
+
+ ncpus_probed = 0;
+ md_for_each_node_by_name(mp, "cpu") {
+ const u64 *id = md_get_property(mp, "id", NULL);
+ const u64 *cfreq = md_get_property(mp, "clock-frequency", NULL);
+ struct trap_per_cpu *tb;
+ cpuinfo_sparc *c;
+ unsigned int i;
+ int cpuid;
+
+ ncpus_probed++;
+
+ cpuid = *id;
+
+#ifdef CONFIG_SMP
+ if (cpuid >= NR_CPUS)
+ continue;
+#else
+ /* On uniprocessor we only want the values for the
+ * real physical cpu the kernel booted onto, however
+ * cpu_data() only has one entry at index 0.
+ */
+ if (cpuid != real_hard_smp_processor_id())
+ continue;
+ cpuid = 0;
+#endif
+
+ c = &cpu_data(cpuid);
+ c->clock_tick = *cfreq;
+
+ tb = &trap_block[cpuid];
+ get_mondo_data(mp, tb);
+
+ for (i = 0; i < mp->num_arcs; i++) {
+ struct mdesc_node *t = mp->arcs[i].arc;
+ unsigned int j;
+
+ if (strcmp(mp->arcs[i].name, "fwd"))
+ continue;
+
+ if (!strcmp(t->name, "cache")) {
+ fill_in_one_cache(c, t);
+ continue;
+ }
+
+ for (j = 0; j < t->num_arcs; j++) {
+ struct mdesc_node *n;
+
+ n = t->arcs[j].arc;
+ if (strcmp(t->arcs[j].name, "fwd"))
+ continue;
+
+ if (!strcmp(n->name, "cache"))
+ fill_in_one_cache(c, n);
+ }
+ }
+
+#ifdef CONFIG_SMP
+ cpu_set(cpuid, cpu_present_map);
+ cpu_set(cpuid, phys_cpu_present_map);
+#endif
+
+ c->core_id = 0;
+ }
+
+ set_core_ids();
+
+ smp_fill_in_sib_core_maps();
+}
+
+void __init sun4v_mdesc_init(void)
+{
+ unsigned long len, real_len, status;
+
+ (void) sun4v_mach_desc(0UL, 0UL, &len);
+
+ printk("MDESC: Size is %lu bytes.\n", len);
+
+ main_mdesc = mdesc_early_alloc(len);
+
+ status = sun4v_mach_desc(__pa(main_mdesc), len, &real_len);
+ if (status != HV_EOK || real_len > len) {
+ prom_printf("sun4v_mach_desc fails, err(%lu), "
+ "len(%lu), real_len(%lu)\n",
+ status, len, real_len);
+ prom_halt();
+ }
+
+ len = count_nodes(main_mdesc);
+ printk("MDESC: %lu nodes.\n", len);
+
+ len = roundup_pow_of_two(len);
+
+ mdesc_hash = mdesc_early_alloc(len * sizeof(struct mdesc_node *));
+ mdesc_hash_size = len;
+
+ printk("MDESC: Hash size %lu entries.\n", len);
+
+ build_all_nodes(main_mdesc);
+
+ printk("MDESC: Built graph with %u bytes of memory.\n",
+ mdesc_early_allocated);
+
+ report_platform_properties();
+ mdesc_fill_in_cpu_data();
+}
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index d4c077dc5e8..38a32bc95d2 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -306,6 +306,20 @@ static void __init pci_controller_probe(void)
pci_controller_scan(pci_controller_init);
}
+static int ofpci_verbose;
+
+static int __init ofpci_debug(char *str)
+{
+ int val = 0;
+
+ get_option(&str, &val);
+ if (val)
+ ofpci_verbose = 1;
+ return 1;
+}
+
+__setup("ofpci_debug=", ofpci_debug);
+
static unsigned long pci_parse_of_flags(u32 addr0)
{
unsigned long flags = 0;
@@ -337,7 +351,9 @@ static void pci_parse_of_addrs(struct of_device *op,
addrs = of_get_property(node, "assigned-addresses", &proplen);
if (!addrs)
return;
- printk(" parse addresses (%d bytes) @ %p\n", proplen, addrs);
+ if (ofpci_verbose)
+ printk(" parse addresses (%d bytes) @ %p\n",
+ proplen, addrs);
op_res = &op->resource[0];
for (; proplen >= 20; proplen -= 20, addrs += 5, op_res++) {
struct resource *res;
@@ -348,8 +364,9 @@ static void pci_parse_of_addrs(struct of_device *op,
if (!flags)
continue;
i = addrs[0] & 0xff;
- printk(" start: %lx, end: %lx, i: %x\n",
- op_res->start, op_res->end, i);
+ if (ofpci_verbose)
+ printk(" start: %lx, end: %lx, i: %x\n",
+ op_res->start, op_res->end, i);
if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) {
res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2];
@@ -393,8 +410,9 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
if (type == NULL)
type = "";
- printk(" create device, devfn: %x, type: %s hostcontroller(%d)\n",
- devfn, type, host_controller);
+ if (ofpci_verbose)
+ printk(" create device, devfn: %x, type: %s\n",
+ devfn, type);
dev->bus = bus;
dev->sysdata = node;
@@ -434,8 +452,9 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
}
- printk(" class: 0x%x device name: %s\n",
- dev->class, pci_name(dev));
+ if (ofpci_verbose)
+ printk(" class: 0x%x device name: %s\n",
+ dev->class, pci_name(dev));
/* I have seen IDE devices which will not respond to
* the bmdma simplex check reads if bus mastering is
@@ -469,7 +488,8 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
}
pci_parse_of_addrs(sd->op, node, dev);
- printk(" adding to system ...\n");
+ if (ofpci_verbose)
+ printk(" adding to system ...\n");
pci_device_add(dev, bus);
@@ -547,7 +567,8 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
unsigned int flags;
u64 size;
- printk("of_scan_pci_bridge(%s)\n", node->full_name);
+ if (ofpci_verbose)
+ printk("of_scan_pci_bridge(%s)\n", node->full_name);
/* parse bus-range property */
busrange = of_get_property(node, "bus-range", &len);
@@ -632,7 +653,8 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
simba_cont:
sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus),
bus->number);
- printk(" bus name: %s\n", bus->name);
+ if (ofpci_verbose)
+ printk(" bus name: %s\n", bus->name);
pci_of_scan_bus(pbm, node, bus);
}
@@ -646,12 +668,14 @@ static void __devinit pci_of_scan_bus(struct pci_pbm_info *pbm,
int reglen, devfn;
struct pci_dev *dev;
- printk("PCI: scan_bus[%s] bus no %d\n",
- node->full_name, bus->number);
+ if (ofpci_verbose)
+ printk("PCI: scan_bus[%s] bus no %d\n",
+ node->full_name, bus->number);
child = NULL;
while ((child = of_get_next_child(node, child)) != NULL) {
- printk(" * %s\n", child->full_name);
+ if (ofpci_verbose)
+ printk(" * %s\n", child->full_name);
reg = of_get_property(child, "reg", &reglen);
if (reg == NULL || reglen < 20)
continue;
@@ -661,7 +685,9 @@ static void __devinit pci_of_scan_bus(struct pci_pbm_info *pbm,
dev = of_create_pci_dev(pbm, child, bus, devfn, 0);
if (!dev)
continue;
- printk("PCI: dev header type: %x\n", dev->hdr_type);
+ if (ofpci_verbose)
+ printk("PCI: dev header type: %x\n",
+ dev->hdr_type);
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index e2377796de8..323d6c27851 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -762,9 +762,10 @@ void sabre_init(struct device_node *dp, char *model_name)
/* Of course, Sun has to encode things a thousand
* different ways, inconsistently.
*/
- cpu_find_by_instance(0, &dp, NULL);
- if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe"))
- hummingbird_p = 1;
+ for_each_node_by_type(dp, "cpu") {
+ if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe"))
+ hummingbird_p = 1;
+ }
}
}
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c
index 044e8ec4c0f..6b3fe2c1d65 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc64/kernel/pci_sun4v.c
@@ -12,6 +12,7 @@
#include <linux/percpu.h>
#include <linux/irq.h>
#include <linux/msi.h>
+#include <linux/log2.h>
#include <asm/iommu.h>
#include <asm/irq.h>
@@ -26,6 +27,9 @@
#include "pci_sun4v.h"
+static unsigned long vpci_major = 1;
+static unsigned long vpci_minor = 1;
+
#define PGLIST_NENTS (PAGE_SIZE / sizeof(u64))
struct iommu_batch {
@@ -638,9 +642,8 @@ static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
{
struct iommu *iommu = pbm->iommu;
struct property *prop;
- unsigned long num_tsb_entries, sz;
+ unsigned long num_tsb_entries, sz, tsbsize;
u32 vdma[2], dma_mask, dma_offset;
- int tsbsize;
prop = of_find_property(pbm->prom_node, "virtual-dma", NULL);
if (prop) {
@@ -654,31 +657,15 @@ static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
vdma[1] = 0x80000000;
}
- dma_mask = vdma[0];
- switch (vdma[1]) {
- case 0x20000000:
- dma_mask |= 0x1fffffff;
- tsbsize = 64;
- break;
-
- case 0x40000000:
- dma_mask |= 0x3fffffff;
- tsbsize = 128;
- break;
-
- case 0x80000000:
- dma_mask |= 0x7fffffff;
- tsbsize = 256;
- break;
-
- default:
- prom_printf("PCI-SUN4V: strange virtual-dma size.\n");
- prom_halt();
+ if ((vdma[0] | vdma[1]) & ~IO_PAGE_MASK) {
+ prom_printf("PCI-SUN4V: strange virtual-dma[%08x:%08x].\n",
+ vdma[0], vdma[1]);
+ prom_halt();
};
- tsbsize *= (8 * 1024);
-
- num_tsb_entries = tsbsize / sizeof(iopte_t);
+ dma_mask = (roundup_pow_of_two(vdma[1]) - 1UL);
+ num_tsb_entries = vdma[1] / IO_PAGE_SIZE;
+ tsbsize = num_tsb_entries * sizeof(iopte_t);
dma_offset = vdma[0];
@@ -689,7 +676,7 @@ static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
iommu->dma_addr_mask = dma_mask;
/* Allocate and initialize the free area map. */
- sz = num_tsb_entries / 8;
+ sz = (num_tsb_entries + 7) / 8;
sz = (sz + 7UL) & ~7UL;
iommu->arena.map = kzalloc(sz, GFP_KERNEL);
if (!iommu->arena.map) {
@@ -1178,6 +1165,7 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node
void sun4v_pci_init(struct device_node *dp, char *model_name)
{
+ static int hvapi_negotiated = 0;
struct pci_controller_info *p;
struct pci_pbm_info *pbm;
struct iommu *iommu;
@@ -1186,6 +1174,20 @@ void sun4v_pci_init(struct device_node *dp, char *model_name)
u32 devhandle;
int i;
+ if (!hvapi_negotiated++) {
+ int err = sun4v_hvapi_register(HV_GRP_PCI,
+ vpci_major,
+ &vpci_minor);
+
+ if (err) {
+ prom_printf("SUN4V_PCI: Could not register hvapi, "
+ "err=%d\n", err);
+ prom_halt();
+ }
+ printk("SUN4V_PCI: Registered hvapi major[%lu] minor[%lu]\n",
+ vpci_major, vpci_minor);
+ }
+
prop = of_find_property(dp, "reg", NULL);
regs = prop->value;
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c
index 699b24b890d..5d6adea3967 100644
--- a/arch/sparc64/kernel/power.c
+++ b/arch/sparc64/kernel/power.c
@@ -19,6 +19,7 @@
#include <asm/prom.h>
#include <asm/of_device.h>
#include <asm/io.h>
+#include <asm/sstate.h>
#include <linux/unistd.h>
@@ -53,6 +54,7 @@ static void (*poweroff_method)(void) = machine_alt_power_off;
void machine_power_off(void)
{
+ sstate_poweroff();
if (!serial_console || scons_pwroff) {
#ifdef CONFIG_PCI
if (power_reg) {
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c
index 952762bfb4c..f5f97e2c669 100644
--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc64/kernel/process.c
@@ -45,6 +45,7 @@
#include <asm/mmu_context.h>
#include <asm/unistd.h>
#include <asm/hypervisor.h>
+#include <asm/sstate.h>
/* #define VERBOSE_SHOWREGS */
@@ -106,6 +107,7 @@ extern void (*prom_keyboard)(void);
void machine_halt(void)
{
+ sstate_halt();
if (!serial_console && prom_palette)
prom_palette (1);
if (prom_keyboard)
@@ -116,6 +118,7 @@ void machine_halt(void)
void machine_alt_power_off(void)
{
+ sstate_poweroff();
if (!serial_console && prom_palette)
prom_palette(1);
if (prom_keyboard)
@@ -128,6 +131,7 @@ void machine_restart(char * cmd)
{
char *p;
+ sstate_reboot();
p = strchr (reboot_command, '\n');
if (p) *p = 0;
if (!serial_console && prom_palette)
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 02830e4671f..dad4b3ba705 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -28,6 +28,7 @@
#include <asm/irq.h>
#include <asm/asi.h>
#include <asm/upa.h>
+#include <asm/smp.h>
static struct device_node *allnodes;
@@ -1665,6 +1666,150 @@ static struct device_node * __init build_tree(struct device_node *parent, phandl
return ret;
}
+static const char *get_mid_prop(void)
+{
+ return (tlb_type == spitfire ? "upa-portid" : "portid");
+}
+
+struct device_node *of_find_node_by_cpuid(int cpuid)
+{
+ struct device_node *dp;
+ const char *mid_prop = get_mid_prop();
+
+ for_each_node_by_type(dp, "cpu") {
+ int id = of_getintprop_default(dp, mid_prop, -1);
+ const char *this_mid_prop = mid_prop;
+
+ if (id < 0) {
+ this_mid_prop = "cpuid";
+ id = of_getintprop_default(dp, this_mid_prop, -1);
+ }
+
+ if (id < 0) {
+ prom_printf("OF: Serious problem, cpu lacks "
+ "%s property", this_mid_prop);
+ prom_halt();
+ }
+ if (cpuid == id)
+ return dp;
+ }
+ return NULL;
+}
+
+static void __init of_fill_in_cpu_data(void)
+{
+ struct device_node *dp;
+ const char *mid_prop = get_mid_prop();
+
+ ncpus_probed = 0;
+ for_each_node_by_type(dp, "cpu") {
+ int cpuid = of_getintprop_default(dp, mid_prop, -1);
+ const char *this_mid_prop = mid_prop;
+ struct device_node *portid_parent;
+ int portid = -1;
+
+ portid_parent = NULL;
+ if (cpuid < 0) {
+ this_mid_prop = "cpuid";
+ cpuid = of_getintprop_default(dp, this_mid_prop, -1);
+ if (cpuid >= 0) {
+ int limit = 2;
+
+ portid_parent = dp;
+ while (limit--) {
+ portid_parent = portid_parent->parent;
+ if (!portid_parent)
+ break;
+ portid = of_getintprop_default(portid_parent,
+ "portid", -1);
+ if (portid >= 0)
+ break;
+ }
+ }
+ }
+
+ if (cpuid < 0) {
+ prom_printf("OF: Serious problem, cpu lacks "
+ "%s property", this_mid_prop);
+ prom_halt();
+ }
+
+ ncpus_probed++;
+
+#ifdef CONFIG_SMP
+ if (cpuid >= NR_CPUS)
+ continue;
+#else
+ /* On uniprocessor we only want the values for the
+ * real physical cpu the kernel booted onto, however
+ * cpu_data() only has one entry at index 0.
+ */
+ if (cpuid != real_hard_smp_processor_id())
+ continue;
+ cpuid = 0;
+#endif
+
+ cpu_data(cpuid).clock_tick =
+ of_getintprop_default(dp, "clock-frequency", 0);
+
+ if (portid_parent) {
+ cpu_data(cpuid).dcache_size =
+ of_getintprop_default(dp, "l1-dcache-size",
+ 16 * 1024);
+ cpu_data(cpuid).dcache_line_size =
+ of_getintprop_default(dp, "l1-dcache-line-size",
+ 32);
+ cpu_data(cpuid).icache_size =
+ of_getintprop_default(dp, "l1-icache-size",
+ 8 * 1024);
+ cpu_data(cpuid).icache_line_size =
+ of_getintprop_default(dp, "l1-icache-line-size",
+ 32);
+ cpu_data(cpuid).ecache_size =
+ of_getintprop_default(dp, "l2-cache-size", 0);
+ cpu_data(cpuid).ecache_line_size =
+ of_getintprop_default(dp, "l2-cache-line-size", 0);
+ if (!cpu_data(cpuid).ecache_size ||
+ !cpu_data(cpuid).ecache_line_size) {
+ cpu_data(cpuid).ecache_size =
+ of_getintprop_default(portid_parent,
+ "l2-cache-size",
+ (4 * 1024 * 1024));
+ cpu_data(cpuid).ecache_line_size =
+ of_getintprop_default(portid_parent,
+ "l2-cache-line-size", 64);
+ }
+
+ cpu_data(cpuid).core_id = portid + 1;
+ } else {
+ cpu_data(cpuid).dcache_size =
+ of_getintprop_default(dp, "dcache-size", 16 * 1024);
+ cpu_data(cpuid).dcache_line_size =
+ of_getintprop_default(dp, "dcache-line-size", 32);
+
+ cpu_data(cpuid).icache_size =
+ of_getintprop_default(dp, "icache-size", 16 * 1024);
+ cpu_data(cpuid).icache_line_size =
+ of_getintprop_default(dp, "icache-line-size", 32);
+
+ cpu_data(cpuid).ecache_size =
+ of_getintprop_default(dp, "ecache-size",
+ (4 * 1024 * 1024));
+ cpu_data(cpuid).ecache_line_size =
+ of_getintprop_default(dp, "ecache-line-size", 64);
+
+ cpu_data(cpuid).core_id = 0;
+ }
+
+#ifdef CONFIG_SMP
+ cpu_set(cpuid, cpu_present_map);
+ cpu_set(cpuid, phys_cpu_present_map);
+#endif
+ }
+
+ smp_fill_in_sib_core_maps();
+}
+
void __init prom_build_devicetree(void)
{
struct device_node **nextp;
@@ -1679,4 +1824,7 @@ void __init prom_build_devicetree(void)
&nextp);
printk("PROM: Built device tree with %u bytes of memory.\n",
prom_early_allocated);
+
+ if (tlb_type != hypervisor)
+ of_fill_in_cpu_data();
}
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c
index 451028341c7..de9b4c13f1c 100644
--- a/arch/sparc64/kernel/setup.c
+++ b/arch/sparc64/kernel/setup.c
@@ -46,11 +46,17 @@
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/mmu.h>
+#include <asm/ns87303.h>
#ifdef CONFIG_IP_PNP
#include <net/ipconfig.h>
#endif
+/* Used to synchronize accesses to NatSemi SUPER I/O chip configure
+ * operations in asm/ns87303.h
+ */
+DEFINE_SPINLOCK(ns87303_lock);
+
struct screen_info screen_info = {
0, 0, /* orig-x, orig-y */
0, /* unused */
@@ -269,6 +275,7 @@ void __init per_cpu_patch(void)
void __init sun4v_patch(void)
{
+ extern void sun4v_hvapi_init(void);
struct sun4v_1insn_patch_entry *p1;
struct sun4v_2insn_patch_entry *p2;
@@ -300,6 +307,8 @@ void __init sun4v_patch(void)
p2++;
}
+
+ sun4v_hvapi_init();
}
#ifdef CONFIG_SMP
@@ -367,8 +376,6 @@ void __init setup_arch(char **cmdline_p)
init_cur_cpu_trap(current_thread_info());
paging_init();
-
- smp_setup_cpu_possible_map();
}
static int __init set_preferred_console(void)
@@ -421,7 +428,7 @@ extern void mmu_info(struct seq_file *);
unsigned int dcache_parity_tl1_occurred;
unsigned int icache_parity_tl1_occurred;
-static int ncpus_probed;
+int ncpus_probed;
static int show_cpuinfo(struct seq_file *m, void *__unused)
{
@@ -513,14 +520,6 @@ static int __init topology_init(void)
err = -ENOMEM;
- /* Count the number of physically present processors in
- * the machine, even on uniprocessor, so that /proc/cpuinfo
- * output is consistent with 2.4.x
- */
- ncpus_probed = 0;
- while (!cpu_find_by_instance(ncpus_probed, NULL, NULL))
- ncpus_probed++;
-
for_each_possible_cpu(i) {
struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p) {
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 24fdf1d0adc..c550bba3490 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -40,6 +40,7 @@
#include <asm/tlb.h>
#include <asm/sections.h>
#include <asm/prom.h>
+#include <asm/mdesc.h>
extern void calibrate_delay(void);
@@ -75,53 +76,6 @@ void smp_bogo(struct seq_file *m)
i, cpu_data(i).clock_tick);
}
-void __init smp_store_cpu_info(int id)
-{
- struct device_node *dp;
- int def;
-
- cpu_data(id).udelay_val = loops_per_jiffy;
-
- cpu_find_by_mid(id, &dp);
- cpu_data(id).clock_tick =
- of_getintprop_default(dp, "clock-frequency", 0);
-
- def = ((tlb_type == hypervisor) ? (8 * 1024) : (16 * 1024));
- cpu_data(id).dcache_size =
- of_getintprop_default(dp, "dcache-size", def);
-
- def = 32;
- cpu_data(id).dcache_line_size =
- of_getintprop_default(dp, "dcache-line-size", def);
-
- def = 16 * 1024;
- cpu_data(id).icache_size =
- of_getintprop_default(dp, "icache-size", def);
-
- def = 32;
- cpu_data(id).icache_line_size =
- of_getintprop_default(dp, "icache-line-size", def);
-
- def = ((tlb_type == hypervisor) ?
- (3 * 1024 * 1024) :
- (4 * 1024 * 1024));
- cpu_data(id).ecache_size =
- of_getintprop_default(dp, "ecache-size", def);
-
- def = 64;
- cpu_data(id).ecache_line_size =
- of_getintprop_default(dp, "ecache-line-size", def);
-
- printk("CPU[%d]: Caches "
- "D[sz(%d):line_sz(%d)] "
- "I[sz(%d):line_sz(%d)] "
- "E[sz(%d):line_sz(%d)]\n",
- id,
- cpu_data(id).dcache_size, cpu_data(id).dcache_line_size,
- cpu_data(id).icache_size, cpu_data(id).icache_line_size,
- cpu_data(id).ecache_size, cpu_data(id).ecache_line_size);
-}
-
extern void setup_sparc64_timer(void);
static volatile unsigned long callin_flag = 0;
@@ -145,7 +99,7 @@ void __init smp_callin(void)
local_irq_enable();
calibrate_delay();
- smp_store_cpu_info(cpuid);
+ cpu_data(cpuid).udelay_val = loops_per_jiffy;
callin_flag = 1;
__asm__ __volatile__("membar #Sync\n\t"
"flush %%g6" : : : "memory");
@@ -340,9 +294,8 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu)
prom_startcpu_cpuid(cpu, entry, cookie);
} else {
- struct device_node *dp;
+ struct device_node *dp = of_find_node_by_cpuid(cpu);
- cpu_find_by_mid(cpu, &dp);
prom_startcpu(dp->node, entry, cookie);
}
@@ -447,7 +400,7 @@ static __inline__ void spitfire_xcall_deliver(u64 data0, u64 data1, u64 data2, c
static void cheetah_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t mask)
{
u64 pstate, ver;
- int nack_busy_id, is_jbus;
+ int nack_busy_id, is_jbus, need_more;
if (cpus_empty(mask))
return;
@@ -463,6 +416,7 @@ static void cheetah_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t mas
__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
retry:
+ need_more = 0;
__asm__ __volatile__("wrpr %0, %1, %%pstate\n\t"
: : "r" (pstate), "i" (PSTATE_IE));
@@ -491,6 +445,10 @@ retry:
: /* no outputs */
: "r" (target), "i" (ASI_INTR_W));
nack_busy_id++;
+ if (nack_busy_id == 32) {
+ need_more = 1;
+ break;
+ }
}
}
@@ -507,6 +465,16 @@ retry:
if (dispatch_stat == 0UL) {
__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
: : "r" (pstate));
+ if (unlikely(need_more)) {
+ int i, cnt = 0;
+ for_each_cpu_mask(i, mask) {
+ cpu_clear(i, mask);
+ cnt++;
+ if (cnt == 32)
+ break;
+ }
+ goto retry;
+ }
return;
}
if (!--stuck)
@@ -544,6 +512,8 @@ retry:
if ((dispatch_stat & check_mask) == 0)
cpu_clear(i, mask);
this_busy_nack += 2;
+ if (this_busy_nack == 64)
+ break;
}
goto retry;
@@ -1191,23 +1161,14 @@ int setup_profiling_timer(unsigned int multiplier)
static void __init smp_tune_scheduling(void)
{
- struct device_node *dp;
- int instance;
- unsigned int def, smallest = ~0U;
-
- def = ((tlb_type == hypervisor) ?
- (3 * 1024 * 1024) :
- (4 * 1024 * 1024));
+ unsigned int smallest = ~0U;
+ int i;
- instance = 0;
- while (!cpu_find_by_instance(instance, &dp, NULL)) {
- unsigned int val;
+ for (i = 0; i < NR_CPUS; i++) {
+ unsigned int val = cpu_data(i).ecache_size;
- val = of_getintprop_default(dp, "ecache-size", def);
- if (val < smallest)
+ if (val && val < smallest)
smallest = val;
-
- instance++;
}
/* Any value less than 256K is nonsense. */
@@ -1230,58 +1191,42 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int i;
if (num_possible_cpus() > max_cpus) {
- int instance, mid;
-
- instance = 0;
- while (!cpu_find_by_instance(instance, NULL, &mid)) {
- if (mid != boot_cpu_id) {
- cpu_clear(mid, phys_cpu_present_map);
- cpu_clear(mid, cpu_present_map);
+ for_each_possible_cpu(i) {
+ if (i != boot_cpu_id) {
+ cpu_clear(i, phys_cpu_present_map);
+ cpu_clear(i, cpu_present_map);
if (num_possible_cpus() <= max_cpus)
break;
}
- instance++;
}
}
- for_each_possible_cpu(i) {
- if (tlb_type == hypervisor) {
- int j;
-
- /* XXX get this mapping from machine description */
- for_each_possible_cpu(j) {
- if ((j >> 2) == (i >> 2))
- cpu_set(j, cpu_sibling_map[i]);
- }
- } else {
- cpu_set(i, cpu_sibling_map[i]);
- }
- }
-
- smp_store_cpu_info(boot_cpu_id);
+ cpu_data(boot_cpu_id).udelay_val = loops_per_jiffy;
smp_tune_scheduling();
}
-/* Set this up early so that things like the scheduler can init
- * properly. We use the same cpu mask for both the present and
- * possible cpu map.
- */
-void __init smp_setup_cpu_possible_map(void)
+void __devinit smp_prepare_boot_cpu(void)
{
- int instance, mid;
-
- instance = 0;
- while (!cpu_find_by_instance(instance, NULL, &mid)) {
- if (mid < NR_CPUS) {
- cpu_set(mid, phys_cpu_present_map);
- cpu_set(mid, cpu_present_map);
- }
- instance++;
- }
}
-void __devinit smp_prepare_boot_cpu(void)
+void __devinit smp_fill_in_sib_core_maps(void)
{
+ unsigned int i;
+
+ for_each_possible_cpu(i) {
+ unsigned int j;
+
+ if (cpu_data(i).core_id == 0) {
+ cpu_set(i, cpu_sibling_map[i]);
+ continue;
+ }
+
+ for_each_possible_cpu(j) {
+ if (cpu_data(i).core_id ==
+ cpu_data(j).core_id)
+ cpu_set(j, cpu_sibling_map[i]);
+ }
+ }
}
int __cpuinit __cpu_up(unsigned int cpu)
@@ -1337,7 +1282,7 @@ unsigned long __per_cpu_shift __read_mostly;
EXPORT_SYMBOL(__per_cpu_base);
EXPORT_SYMBOL(__per_cpu_shift);
-void __init setup_per_cpu_areas(void)
+void __init real_setup_per_cpu_areas(void)
{
unsigned long goal, size, i;
char *ptr;
diff --git a/arch/sparc64/kernel/sstate.c b/arch/sparc64/kernel/sstate.c
new file mode 100644
index 00000000000..5b6e75b7f05
--- /dev/null
+++ b/arch/sparc64/kernel/sstate.c
@@ -0,0 +1,104 @@
+/* sstate.c: System soft state support.
+ *
+ * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/init.h>
+
+#include <asm/hypervisor.h>
+#include <asm/sstate.h>
+#include <asm/oplib.h>
+#include <asm/head.h>
+#include <asm/io.h>
+
+static int hv_supports_soft_state;
+
+static unsigned long kimage_addr_to_ra(const char *p)
+{
+ unsigned long val = (unsigned long) p;
+
+ return kern_base + (val - KERNBASE);
+}
+
+static void do_set_sstate(unsigned long state, const char *msg)
+{
+ unsigned long err;
+
+ if (!hv_supports_soft_state)
+ return;
+
+ err = sun4v_mach_set_soft_state(state, kimage_addr_to_ra(msg));
+ if (err) {
+ printk(KERN_WARNING "SSTATE: Failed to set soft-state to "
+ "state[%lx] msg[%s], err=%lu\n",
+ state, msg, err);
+ }
+}
+
+static const char booting_msg[32] __attribute__((aligned(32))) =
+ "Linux booting";
+static const char running_msg[32] __attribute__((aligned(32))) =
+ "Linux running";
+static const char halting_msg[32] __attribute__((aligned(32))) =
+ "Linux halting";
+static const char poweroff_msg[32] __attribute__((aligned(32))) =
+ "Linux powering off";
+static const char rebooting_msg[32] __attribute__((aligned(32))) =
+ "Linux rebooting";
+static const char panicing_msg[32] __attribute__((aligned(32))) =
+ "Linux panicing";
+
+void sstate_booting(void)
+{
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg);
+}
+
+void sstate_running(void)
+{
+ do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg);
+}
+
+void sstate_halt(void)
+{
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, halting_msg);
+}
+
+void sstate_poweroff(void)
+{
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, poweroff_msg);
+}
+
+void sstate_reboot(void)
+{
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, rebooting_msg);
+}
+
+static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr)
+{
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, panicing_msg);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sstate_panic_block = {
+ .notifier_call = sstate_panic_event,
+ .priority = INT_MAX,
+};
+
+void __init sun4v_sstate_init(void)
+{
+ unsigned long major, minor;
+
+ major = 1;
+ minor = 0;
+ if (sun4v_hvapi_register(HV_GRP_SOFT_STATE, major, &minor))
+ return;
+
+ hv_supports_soft_state = 1;
+
+ prom_sun4v_guest_soft_state();
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &sstate_panic_block);
+}
diff --git a/arch/sparc64/kernel/sun4v_ivec.S b/arch/sparc64/kernel/sun4v_ivec.S
index 405855dd886..574bc248bca 100644
--- a/arch/sparc64/kernel/sun4v_ivec.S
+++ b/arch/sparc64/kernel/sun4v_ivec.S
@@ -22,12 +22,12 @@ sun4v_cpu_mondo:
be,pn %xcc, sun4v_cpu_mondo_queue_empty
nop
- /* Get &trap_block[smp_processor_id()] into %g3. */
- ldxa [%g0] ASI_SCRATCHPAD, %g3
- sub %g3, TRAP_PER_CPU_FAULT_INFO, %g3
+ /* Get &trap_block[smp_processor_id()] into %g4. */
+ ldxa [%g0] ASI_SCRATCHPAD, %g4
+ sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
/* Get CPU mondo queue base phys address into %g7. */
- ldx [%g3 + TRAP_PER_CPU_CPU_MONDO_PA], %g7
+ ldx [%g4 + TRAP_PER_CPU_CPU_MONDO_PA], %g7
/* Now get the cross-call arguments and handler PC, same
* layout as sun4u:
@@ -47,8 +47,7 @@ sun4v_cpu_mondo:
add %g2, 0x40 - 0x8 - 0x8, %g2
/* Update queue head pointer. */
- sethi %hi(8192 - 1), %g4
- or %g4, %lo(8192 - 1), %g4
+ lduw [%g4 + TRAP_PER_CPU_CPU_MONDO_QMASK], %g4
and %g2, %g4, %g2
mov INTRQ_CPU_MONDO_HEAD, %g4
@@ -71,12 +70,12 @@ sun4v_dev_mondo:
be,pn %xcc, sun4v_dev_mondo_queue_empty
nop
- /* Get &trap_block[smp_processor_id()] into %g3. */
- ldxa [%g0] ASI_SCRATCHPAD, %g3
- sub %g3, TRAP_PER_CPU_FAULT_INFO, %g3
+ /* Get &trap_block[smp_processor_id()] into %g4. */
+ ldxa [%g0] ASI_SCRATCHPAD, %g4
+ sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
/* Get DEV mondo queue base phys address into %g5. */
- ldx [%g3 + TRAP_PER_CPU_DEV_MONDO_PA], %g5
+ ldx [%g4 + TRAP_PER_CPU_DEV_MONDO_PA], %g5
/* Load IVEC into %g3. */
ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
@@ -90,8 +89,7 @@ sun4v_dev_mondo:
*/
/* Update queue head pointer, this frees up some registers. */
- sethi %hi(8192 - 1), %g4
- or %g4, %lo(8192 - 1), %g4
+ lduw [%g4 + TRAP_PER_CPU_DEV_MONDO_QMASK], %g4
and %g2, %g4, %g2
mov INTRQ_DEVICE_MONDO_HEAD, %g4
@@ -143,6 +141,8 @@ sun4v_res_mondo:
brnz,pn %g1, sun4v_res_mondo_queue_full
nop
+ lduw [%g3 + TRAP_PER_CPU_RESUM_QMASK], %g4
+
/* Remember this entry's offset in %g1. */
mov %g2, %g1
@@ -173,8 +173,6 @@ sun4v_res_mondo:
add %g2, 0x08, %g2
/* Update queue head pointer. */
- sethi %hi(8192 - 1), %g4
- or %g4, %lo(8192 - 1), %g4
and %g2, %g4, %g2
mov INTRQ_RESUM_MONDO_HEAD, %g4
@@ -254,6 +252,8 @@ sun4v_nonres_mondo:
brnz,pn %g1, sun4v_nonres_mondo_queue_full
nop
+ lduw [%g3 + TRAP_PER_CPU_NONRESUM_QMASK], %g4
+
/* Remember this entry's offset in %g1. */
mov %g2, %g1
@@ -284,8 +284,6 @@ sun4v_nonres_mondo:
add %g2, 0x08, %g2
/* Update queue head pointer. */
- sethi %hi(8192 - 1), %g4
- or %g4, %lo(8192 - 1), %g4
and %g2, %g4, %g2
mov INTRQ_NONRESUM_MONDO_HEAD, %g4
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index 6b9a06e4254..a31a0439244 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc64/kernel/time.c
@@ -680,22 +680,14 @@ static int starfire_set_time(u32 val)
static u32 hypervisor_get_time(void)
{
- register unsigned long func asm("%o5");
- register unsigned long arg0 asm("%o0");
- register unsigned long arg1 asm("%o1");
+ unsigned long ret, time;
int retries = 10000;
retry:
- func = HV_FAST_TOD_GET;
- arg0 = 0;
- arg1 = 0;
- __asm__ __volatile__("ta %6"
- : "=&r" (func), "=&r" (arg0), "=&r" (arg1)
- : "0" (func), "1" (arg0), "2" (arg1),
- "i" (HV_FAST_TRAP));
- if (arg0 == HV_EOK)
- return arg1;
- if (arg0 == HV_EWOULDBLOCK) {
+ ret = sun4v_tod_get(&time);
+ if (ret == HV_EOK)
+ return time;
+ if (ret == HV_EWOULDBLOCK) {
if (--retries > 0) {
udelay(100);
goto retry;
@@ -709,20 +701,14 @@ retry:
static int hypervisor_set_time(u32 secs)
{
- register unsigned long func asm("%o5");
- register unsigned long arg0 asm("%o0");
+ unsigned long ret;
int retries = 10000;
retry:
- func = HV_FAST_TOD_SET;
- arg0 = secs;
- __asm__ __volatile__("ta %4"
- : "=&r" (func), "=&r" (arg0)
- : "0" (func), "1" (arg0),
- "i" (HV_FAST_TRAP));
- if (arg0 == HV_EOK)
+ ret = sun4v_tod_set(secs);
+ if (ret == HV_EOK)
return 0;
- if (arg0 == HV_EWOULDBLOCK) {
+ if (ret == HV_EWOULDBLOCK) {
if (--retries > 0) {
udelay(100);
goto retry;
@@ -862,7 +848,6 @@ fs_initcall(clock_init);
static unsigned long sparc64_init_timers(void)
{
struct device_node *dp;
- struct property *prop;
unsigned long clock;
#ifdef CONFIG_SMP
extern void smp_tick_init(void);
@@ -879,17 +864,15 @@ static unsigned long sparc64_init_timers(void)
if (manuf == 0x17 && impl == 0x13) {
/* Hummingbird, aka Ultra-IIe */
tick_ops = &hbtick_operations;
- prop = of_find_property(dp, "stick-frequency", NULL);
+ clock = of_getintprop_default(dp, "stick-frequency", 0);
} else {
tick_ops = &tick_operations;
- cpu_find_by_instance(0, &dp, NULL);
- prop = of_find_property(dp, "clock-frequency", NULL);
+ clock = local_cpu_data().clock_tick;
}
} else {
tick_ops = &stick_operations;
- prop = of_find_property(dp, "stick-frequency", NULL);
+ clock = of_getintprop_default(dp, "stick-frequency", 0);
}
- clock = *(unsigned int *) prop->value;
#ifdef CONFIG_SMP
smp_tick_init();
@@ -1030,7 +1013,7 @@ void __devinit setup_sparc64_timer(void)
clockevents_register_device(sevt);
}
-#define SPARC64_NSEC_PER_CYC_SHIFT 32UL
+#define SPARC64_NSEC_PER_CYC_SHIFT 10UL
static struct clocksource clocksource_tick = {
.rating = 100,
@@ -1365,6 +1348,7 @@ static int hypervisor_set_rtc_time(struct rtc_time *time)
return hypervisor_set_time(seconds);
}
+#ifdef CONFIG_PCI
static void bq4802_get_rtc_time(struct rtc_time *time)
{
unsigned char val = readb(bq4802_regs + 0x0e);
@@ -1436,6 +1420,7 @@ static int bq4802_set_rtc_time(struct rtc_time *time)
return 0;
}
+#endif /* CONFIG_PCI */
struct mini_rtc_ops {
void (*get_rtc_time)(struct rtc_time *);
@@ -1452,10 +1437,12 @@ static struct mini_rtc_ops hypervisor_rtc_ops = {
.set_rtc_time = hypervisor_set_rtc_time,
};
+#ifdef CONFIG_PCI
static struct mini_rtc_ops bq4802_rtc_ops = {
.get_rtc_time = bq4802_get_rtc_time,
.set_rtc_time = bq4802_set_rtc_time,
};
+#endif /* CONFIG_PCI */
static struct mini_rtc_ops *mini_rtc_ops;
@@ -1579,8 +1566,10 @@ static int __init rtc_mini_init(void)
mini_rtc_ops = &hypervisor_rtc_ops;
else if (this_is_starfire)
mini_rtc_ops = &starfire_rtc_ops;
+#ifdef CONFIG_PCI
else if (bq4802_regs)
mini_rtc_ops = &bq4802_rtc_ops;
+#endif /* CONFIG_PCI */
else
return -ENODEV;
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
index d0fde36395b..00a9e3286c8 100644
--- a/arch/sparc64/kernel/traps.c
+++ b/arch/sparc64/kernel/traps.c
@@ -795,8 +795,7 @@ extern unsigned int cheetah_deferred_trap_vector[], cheetah_deferred_trap_vector
void __init cheetah_ecache_flush_init(void)
{
unsigned long largest_size, smallest_linesize, order, ver;
- struct device_node *dp;
- int i, instance, sz;
+ int i, sz;
/* Scan all cpu device tree nodes, note two values:
* 1) largest E-cache size
@@ -805,18 +804,20 @@ void __init cheetah_ecache_flush_init(void)
largest_size = 0UL;
smallest_linesize = ~0UL;
- instance = 0;
- while (!cpu_find_by_instance(instance, &dp, NULL)) {
+ for (i = 0; i < NR_CPUS; i++) {
unsigned long val;
- val = of_getintprop_default(dp, "ecache-size",
- (2 * 1024 * 1024));
+ val = cpu_data(i).ecache_size;
+ if (!val)
+ continue;
+
if (val > largest_size)
largest_size = val;
- val = of_getintprop_default(dp, "ecache-line-size", 64);
+
+ val = cpu_data(i).ecache_line_size;
if (val < smallest_linesize)
smallest_linesize = val;
- instance++;
+
}
if (largest_size == 0UL || smallest_linesize == ~0UL) {
@@ -2564,7 +2565,15 @@ void __init trap_init(void)
(TRAP_PER_CPU_TSB_HUGE_TEMP !=
offsetof(struct trap_per_cpu, tsb_huge_temp)) ||
(TRAP_PER_CPU_IRQ_WORKLIST !=
- offsetof(struct trap_per_cpu, irq_worklist)))
+ offsetof(struct trap_per_cpu, irq_worklist)) ||
+ (TRAP_PER_CPU_CPU_MONDO_QMASK !=
+ offsetof(struct trap_per_cpu, cpu_mondo_qmask)) ||
+ (TRAP_PER_CPU_DEV_MONDO_QMASK !=
+ offsetof(struct trap_per_cpu, dev_mondo_qmask)) ||
+ (TRAP_PER_CPU_RESUM_QMASK !=
+ offsetof(struct trap_per_cpu, resum_qmask)) ||
+ (TRAP_PER_CPU_NONRESUM_QMASK !=
+ offsetof(struct trap_per_cpu, nonresum_qmask)))
trap_per_cpu_offsets_are_bolixed_dave();
if ((TSB_CONFIG_TSB !=
diff --git a/arch/sparc64/kernel/vmlinux.lds.S b/arch/sparc64/kernel/vmlinux.lds.S
index 13fa2a2e451..fb648de18a8 100644
--- a/arch/sparc64/kernel/vmlinux.lds.S
+++ b/arch/sparc64/kernel/vmlinux.lds.S
@@ -14,7 +14,7 @@ SECTIONS
.text 0x0000000000404000 :
{
_text = .;
- *(.text)
+ TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
@@ -27,7 +27,7 @@ SECTIONS
.data :
{
- *(.data)
+ DATA_DATA
CONSTRUCTORS
}
.data1 : { *(.data1) }
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index 6e5b01d779d..3010227fe24 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -23,6 +23,7 @@
#include <linux/kprobes.h>
#include <linux/cache.h>
#include <linux/sort.h>
+#include <linux/percpu.h>
#include <asm/head.h>
#include <asm/system.h>
@@ -43,8 +44,8 @@
#include <asm/tsb.h>
#include <asm/hypervisor.h>
#include <asm/prom.h>
-
-extern void device_scan(void);
+#include <asm/sstate.h>
+#include <asm/mdesc.h>
#define MAX_PHYS_ADDRESS (1UL << 42UL)
#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL)
@@ -60,8 +61,11 @@ unsigned long kern_linear_pte_xor[2] __read_mostly;
unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
#ifndef CONFIG_DEBUG_PAGEALLOC
-/* A special kernel TSB for 4MB and 256MB linear mappings. */
-struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
+/* A special kernel TSB for 4MB and 256MB linear mappings.
+ * Space is allocated for this right after the trap table
+ * in arch/sparc64/kernel/head.S
+ */
+extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
#endif
#define MAX_BANKS 32
@@ -190,12 +194,9 @@ inline void flush_dcache_page_impl(struct page *page)
}
#define PG_dcache_dirty PG_arch_1
-#define PG_dcache_cpu_shift 24UL
-#define PG_dcache_cpu_mask (256UL - 1UL)
-
-#if NR_CPUS > 256
-#error D-cache dirty tracking and thread_info->cpu need fixing for > 256 cpus
-#endif
+#define PG_dcache_cpu_shift 32UL
+#define PG_dcache_cpu_mask \
+ ((1UL<<ilog2(roundup_pow_of_two(NR_CPUS)))-1UL)
#define dcache_dirty_cpu(page) \
(((page)->flags >> PG_dcache_cpu_shift) & PG_dcache_cpu_mask)
@@ -557,26 +558,11 @@ static void __init hypervisor_tlb_lock(unsigned long vaddr,
unsigned long pte,
unsigned long mmu)
{
- register unsigned long func asm("%o5");
- register unsigned long arg0 asm("%o0");
- register unsigned long arg1 asm("%o1");
- register unsigned long arg2 asm("%o2");
- register unsigned long arg3 asm("%o3");
-
- func = HV_FAST_MMU_MAP_PERM_ADDR;
- arg0 = vaddr;
- arg1 = 0;
- arg2 = pte;
- arg3 = mmu;
- __asm__ __volatile__("ta 0x80"
- : "=&r" (func), "=&r" (arg0),
- "=&r" (arg1), "=&r" (arg2),
- "=&r" (arg3)
- : "0" (func), "1" (arg0), "2" (arg1),
- "3" (arg2), "4" (arg3));
- if (arg0 != 0) {
+ unsigned long ret = sun4v_mmu_map_perm_addr(vaddr, 0, pte, mmu);
+
+ if (ret != 0) {
prom_printf("hypervisor_tlb_lock[%lx:%lx:%lx:%lx]: "
- "errors with %lx\n", vaddr, 0, pte, mmu, arg0);
+ "errors with %lx\n", vaddr, 0, pte, mmu, ret);
prom_halt();
}
}
@@ -1313,20 +1299,16 @@ static void __init sun4v_ktsb_init(void)
void __cpuinit sun4v_ktsb_register(void)
{
- register unsigned long func asm("%o5");
- register unsigned long arg0 asm("%o0");
- register unsigned long arg1 asm("%o1");
- unsigned long pa;
+ unsigned long pa, ret;
pa = kern_base + ((unsigned long)&ktsb_descr[0] - KERNBASE);
- func = HV_FAST_MMU_TSB_CTX0;
- arg0 = NUM_KTSB_DESCR;
- arg1 = pa;
- __asm__ __volatile__("ta %6"
- : "=&r" (func), "=&r" (arg0), "=&r" (arg1)
- : "0" (func), "1" (arg0), "2" (arg1),
- "i" (HV_FAST_TRAP));
+ ret = sun4v_mmu_tsb_ctx0(NUM_KTSB_DESCR, pa);
+ if (ret != 0) {
+ prom_printf("hypervisor_mmu_tsb_ctx0[%lx]: "
+ "errors with %lx\n", pa, ret);
+ prom_halt();
+ }
}
/* paging_init() sets up the page tables */
@@ -1334,6 +1316,9 @@ void __cpuinit sun4v_ktsb_register(void)
extern void cheetah_ecache_flush_init(void);
extern void sun4v_patch_tlb_handlers(void);
+extern void cpu_probe(void);
+extern void central_probe(void);
+
static unsigned long last_valid_pfn;
pgd_t swapper_pg_dir[2048];
@@ -1345,9 +1330,24 @@ void __init paging_init(void)
unsigned long end_pfn, pages_avail, shift, phys_base;
unsigned long real_end, i;
+ /* These build time checkes make sure that the dcache_dirty_cpu()
+ * page->flags usage will work.
+ *
+ * When a page gets marked as dcache-dirty, we store the
+ * cpu number starting at bit 32 in the page->flags. Also,
+ * functions like clear_dcache_dirty_cpu use the cpu mask
+ * in 13-bit signed-immediate instruction fields.
+ */
+ BUILD_BUG_ON(FLAGS_RESERVED != 32);
+ BUILD_BUG_ON(SECTIONS_WIDTH + NODES_WIDTH + ZONES_WIDTH +
+ ilog2(roundup_pow_of_two(NR_CPUS)) > FLAGS_RESERVED);
+ BUILD_BUG_ON(NR_CPUS > 4096);
+
kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
kern_size = (unsigned long)&_end - (unsigned long)KERNBASE;
+ sstate_booting();
+
/* Invalidate both kernel TSBs. */
memset(swapper_tsb, 0x40, sizeof(swapper_tsb));
#ifndef CONFIG_DEBUG_PAGEALLOC
@@ -1416,8 +1416,13 @@ void __init paging_init(void)
kernel_physical_mapping_init();
+ real_setup_per_cpu_areas();
+
prom_build_devicetree();
+ if (tlb_type == hypervisor)
+ sun4v_mdesc_init();
+
{
unsigned long zones_size[MAX_NR_ZONES];
unsigned long zholes_size[MAX_NR_ZONES];
@@ -1434,7 +1439,10 @@ void __init paging_init(void)
zholes_size);
}
- device_scan();
+ prom_printf("Booting Linux...\n");
+
+ central_probe();
+ cpu_probe();
}
static void __init taint_real_pages(void)
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c
index 0b4213720d4..f3e0c14e9ee 100644
--- a/arch/sparc64/prom/misc.c
+++ b/arch/sparc64/prom/misc.c
@@ -15,6 +15,25 @@
#include <asm/oplib.h>
#include <asm/system.h>
+int prom_service_exists(const char *service_name)
+{
+ int err = p1275_cmd("test", P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_INOUT(1, 1), service_name);
+
+ if (err)
+ return 0;
+ return 1;
+}
+
+void prom_sun4v_guest_soft_state(void)
+{
+ const char *svc = "SUNW,soft-state-supported";
+
+ if (!prom_service_exists(svc))
+ return;
+ p1275_cmd(svc, P1275_INOUT(0, 0));
+}
+
/* Reset and reboot the machine with the command 'bcommand'. */
void prom_reboot(const char *bcommand)
{