diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2008-08-11 18:20:38 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2008-08-11 18:20:38 +0000 |
commit | a36626f3753e8e4bd36d46c273593541a2c9f42d (patch) | |
tree | 35ed229f75d2a075a9e86ca5b7d5af83bae698a7 /sys/arch/sparc64 | |
parent | e69b5e697f30e54b54e2ef3e22e8a7eef8a28bf8 (diff) |
Add hw.setperf support for UltraSPARC-IIe support.
tested by miod@, matthieu@, naddy@, jsg@, djm@
Diffstat (limited to 'sys/arch/sparc64')
-rw-r--r-- | sys/arch/sparc64/sparc64/cpu.c | 239 |
1 files changed, 237 insertions, 2 deletions
diff --git a/sys/arch/sparc64/sparc64/cpu.c b/sys/arch/sparc64/sparc64/cpu.c index 2d0dd141742..57140c329e8 100644 --- a/sys/arch/sparc64/sparc64/cpu.c +++ b/sys/arch/sparc64/sparc64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.46 2008/08/07 21:25:47 kettenis Exp $ */ +/* $OpenBSD: cpu.c,v 1.47 2008/08/11 18:20:37 kettenis Exp $ */ /* $NetBSD: cpu.c,v 1.13 2001/05/26 21:27:15 chs Exp $ */ /* @@ -53,8 +53,9 @@ */ #include <sys/param.h> -#include <sys/systm.h> #include <sys/device.h> +#include <sys/sysctl.h> +#include <sys/systm.h> #include <uvm/uvm_extern.h> @@ -98,6 +99,15 @@ struct cfattach cpu_ca = { void cpu_init(struct cpu_info *ci); void cpu_hatch(void); +int hummingbird_div(uint64_t); +uint64_t hummingbird_estar_mode(int); +void hummingbird_enable_self_refresh(void); +void hummingbird_disable_self_refresh(void); +void hummingbird_set_refresh_count(int, int); +void hummingbird_setperf(int); +int hummingbird_cpuspeed(int *); +void hummingbird_init(struct cpu_info *ci); + #define IU_IMPL(v) ((((u_int64_t)(v))&VER_IMPL) >> VER_IMPL_SHIFT) #define IU_VERS(v) ((((u_int64_t)(v))&VER_MASK) >> VER_MASK_SHIFT) @@ -258,6 +268,7 @@ cpu_attach(parent, dev, aux) * Allocate cpu_info structure if needed. */ ci = alloc_cpuinfo(ma); + ci->ci_node = ma->ma_node; clk = getpropint(node, "clock-frequency", 0); if (clk == 0) { @@ -366,6 +377,12 @@ cpu_attach(parent, dev, aux) (long)cacheinfo.ec_totalsize/1024, (long)cacheinfo.ec_linesize); } + +#ifndef SMALL_KERNEL + if (impl == IMPL_HUMMINGBIRD) + hummingbird_init(ci); +#endif + printf("\n"); cache_enable(); } @@ -444,6 +461,224 @@ struct cfdriver cpu_cd = { NULL, "cpu", DV_DULL }; +#ifndef SMALL_KERNEL + +/* + * Hummingbird (UltraSPARC-IIe) has a clock control unit that enables + * Energy Star mode. This only works in combination with unbuffered + * DIMMs so it is not supported on all machines with UltraSPARC-IIe + * CPUs. + */ + +/* Memory_Control_0 (MC0) register. */ +#define HB_MC0 0x1fe0000f010ULL +#define HB_MC0_SELF_REFRESH 0x00010000 +#define HB_MC0_REFRESH_COUNT_MASK 0x00007f00 +#define HB_MC0_REFRESH_COUNT_SHIFT 8 +#define HB_MC0_REFRESH_COUNT(reg) \ + (((reg) & HB_MC0_REFRESH_COUNT_MASK) >> HB_MC0_REFRESH_COUNT_SHIFT) +#define HB_MC0_REFRESH_CLOCKS_PER_COUNT 64ULL +#define HB_MC0_REFRESH_INTERVAL 7800ULL + +/* Energy Star register. */ +#define HB_ESTAR 0x1fe0000f080ULL +#define HB_ESTAR_MODE_MASK 0x00000007 +#define HB_ESTAR_MODE_DIV_1 0x00000000 +#define HB_ESTAR_MODE_DIV_2 0x00000001 +#define HB_ESTAR_MODE_DIV_4 0x00000003 +#define HB_ESTAR_MODE_DIV_6 0x00000002 +#define HB_ESTAR_MODE_DIV_8 0x00000004 +#define HB_ESTAR_NUM_MODES 5 + +int hummingbird_divisors[HB_ESTAR_NUM_MODES]; + +int +hummingbird_div(uint64_t estar_mode) +{ + switch(estar_mode) { + case HB_ESTAR_MODE_DIV_1: + return 1; + case HB_ESTAR_MODE_DIV_2: + return 2; + case HB_ESTAR_MODE_DIV_4: + return 4; + case HB_ESTAR_MODE_DIV_6: + return 6; + case HB_ESTAR_MODE_DIV_8: + return 8; + default: + panic("bad E-Star mode"); + } +} + +uint64_t +hummingbird_estar_mode(int div) +{ + switch(div) { + case 1: + return HB_ESTAR_MODE_DIV_1; + case 2: + return HB_ESTAR_MODE_DIV_2; + case 4: + return HB_ESTAR_MODE_DIV_4; + case 6: + return HB_ESTAR_MODE_DIV_6; + case 8: + return HB_ESTAR_MODE_DIV_8; + default: + panic("bad clock divisor"); + } +} + +void +hummingbird_enable_self_refresh(void) +{ + uint64_t reg; + + reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED); + reg |= HB_MC0_SELF_REFRESH; + stxa(HB_MC0, ASI_PHYS_NON_CACHED, reg); + reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED); +} + +void +hummingbird_disable_self_refresh(void) +{ + uint64_t reg; + + reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED); + reg &= ~HB_MC0_SELF_REFRESH; + stxa(HB_MC0, ASI_PHYS_NON_CACHED, reg); + reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED); +} + +void +hummingbird_set_refresh_count(int div, int new_div) +{ + extern u_int64_t cpu_clockrate[]; + uint64_t count, new_count; + uint64_t delta; + uint64_t reg; + + reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED); + count = HB_MC0_REFRESH_COUNT(reg); + new_count = (HB_MC0_REFRESH_INTERVAL * cpu_clockrate[0]) / + (HB_MC0_REFRESH_CLOCKS_PER_COUNT * new_div * 1000000000); + reg &= ~HB_MC0_REFRESH_COUNT_MASK; + reg |= (new_count << HB_MC0_REFRESH_COUNT_SHIFT); + stxa(HB_MC0, ASI_PHYS_NON_CACHED, reg); + reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED); + + if (new_div > div && (reg & HB_MC0_SELF_REFRESH) == 0) { + delta = HB_MC0_REFRESH_CLOCKS_PER_COUNT * + ((count + new_count) * 1000000UL * div) / cpu_clockrate[0]; + delay(delta + 1); + } +} + +void +hummingbird_setperf(int level) +{ + uint64_t estar_mode, new_estar_mode; + uint64_t reg, s; + int div, new_div, i; + + new_estar_mode = HB_ESTAR_MODE_DIV_1; + for (i = 0; i < HB_ESTAR_NUM_MODES && hummingbird_divisors[i]; i++) { + if (level <= 100 / hummingbird_divisors[i]) + new_estar_mode = + hummingbird_estar_mode(hummingbird_divisors[i]); + } + + reg = ldxa(HB_ESTAR, ASI_PHYS_NON_CACHED); + estar_mode = reg & HB_ESTAR_MODE_MASK; + if (estar_mode == new_estar_mode) + return; + + reg &= ~HB_ESTAR_MODE_MASK; + div = hummingbird_div(estar_mode); + new_div = hummingbird_div(new_estar_mode); + + s = intr_disable(); + if (estar_mode == HB_ESTAR_MODE_DIV_1 && + new_estar_mode == HB_ESTAR_MODE_DIV_2) { + hummingbird_set_refresh_count(1, 2); + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_2); + delay(1); + hummingbird_enable_self_refresh(); + } else if (estar_mode == HB_ESTAR_MODE_DIV_2 && + new_estar_mode == HB_ESTAR_MODE_DIV_1) { + hummingbird_disable_self_refresh(); + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_1); + delay(1); + hummingbird_set_refresh_count(2, 1); + } else if (estar_mode == HB_ESTAR_MODE_DIV_1) { + /* + * Transition to 1/2 speed first, then to + * lower speed. + */ + hummingbird_set_refresh_count(1, 2); + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_2); + delay(1); + hummingbird_enable_self_refresh(); + + hummingbird_set_refresh_count(2, new_div); + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | new_estar_mode); + delay(1); + } else if (new_estar_mode == HB_ESTAR_MODE_DIV_1) { + /* + * Transition to 1/2 speed first, then to + * full speed. + */ + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_2); + delay(1); + hummingbird_set_refresh_count(div, 2); + + hummingbird_disable_self_refresh(); + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_1); + delay(1); + hummingbird_set_refresh_count(2, 1); + } else if (div < new_div) { + hummingbird_set_refresh_count(div, new_div); + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | new_estar_mode); + delay(1); + } else if (div > new_div) { + stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | new_estar_mode); + delay(1); + hummingbird_set_refresh_count(div, new_div); + } + intr_restore(s); +} + +int +hummingbird_cpuspeed(int *freq) +{ + extern u_int64_t cpu_clockrate[]; + uint64_t reg; + int div; + + reg = ldxa(HB_ESTAR, ASI_PHYS_NON_CACHED); + div = hummingbird_div(reg & HB_ESTAR_MODE_MASK); + *freq = cpu_clockrate[1] / div; + return (0); +} + +void +hummingbird_init(struct cpu_info *ci) +{ + /* + * The "clock-divisors" property seems to indicate which + * frequency scalings are supported on a particular model. + */ + if (OF_getprop(ci->ci_node, "clock-divisors", + &hummingbird_divisors, sizeof(hummingbird_divisors)) <= 0) + return; + + cpu_setperf = hummingbird_setperf; + cpu_cpuspeed = hummingbird_cpuspeed; +} +#endif + #ifdef MULTIPROCESSOR void cpu_mp_startup(void); |