diff options
53 files changed, 3809 insertions, 1008 deletions
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 6524c6e2189..159e94f4b22 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -69,6 +69,16 @@ LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y) CFLAGS-$(CONFIG_PPC64) := -mminimal-toc -mtraceback=no -mcall-aixdesc CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple + +CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4) +CFLAGS-$(CONFIG_CELL_CPU) += $(call cc-option,-mcpu=cell) +CFLAGS-$(CONFIG_POWER4_CPU) += $(call cc-option,-mcpu=power4) +CFLAGS-$(CONFIG_POWER5_CPU) += $(call cc-option,-mcpu=power5) +CFLAGS-$(CONFIG_POWER6_CPU) += $(call cc-option,-mcpu=power6) +CFLAGS-$(CONFIG_POWER7_CPU) += $(call cc-option,-mcpu=power7) + +CFLAGS-$(CONFIG_TUNE_CELL) += $(call cc-option,-mtune=cell) + KBUILD_CPPFLAGS += -Iarch/$(ARCH) KBUILD_AFLAGS += -Iarch/$(ARCH) KBUILD_CFLAGS += -msoft-float -pipe -Iarch/$(ARCH) $(CFLAGS-y) @@ -76,32 +86,11 @@ CPP = $(CC) -E $(KBUILD_CFLAGS) CHECKFLAGS += -m$(CONFIG_WORD_SIZE) -D__powerpc__ -D__powerpc$(CONFIG_WORD_SIZE)__ -ifeq ($(CONFIG_PPC64),y) -GCC_BROKEN_VEC := $(call cc-ifversion, -lt, 0400, y) - -ifeq ($(CONFIG_POWER4_ONLY),y) -ifeq ($(CONFIG_ALTIVEC),y) -ifeq ($(GCC_BROKEN_VEC),y) - KBUILD_CFLAGS += $(call cc-option,-mcpu=970) -else - KBUILD_CFLAGS += $(call cc-option,-mcpu=power4) -endif -else - KBUILD_CFLAGS += $(call cc-option,-mcpu=power4) -endif -else - KBUILD_CFLAGS += $(call cc-option,-mtune=power4) -endif -endif - KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o -ifeq ($(CONFIG_TUNE_CELL),y) - KBUILD_CFLAGS += $(call cc-option,-mtune=cell) -endif - -# No AltiVec instruction when building kernel +# No AltiVec or VSX instructions when building kernel KBUILD_CFLAGS += $(call cc-option,-mno-altivec) +KBUILD_CFLAGS += $(call cc-option,-mno-vsx) # No SPE instruction when building kernel # (We use all available options to help semi-broken compilers) @@ -234,10 +223,11 @@ archprepare: checkbin # Use the file '.tmp_gas_check' for binutils tests, as gas won't output # to stdout and these checks are run even on install targets. TOUT := .tmp_gas_check -# Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later for altivec -# instructions. -# gcc-3.4 and binutils-2.14 are a fatal combination. +# Check gcc and binutils versions: +# - gcc-3.4 and binutils-2.14 are a fatal combination +# - Require gcc 4.0 or above on 64-bit +# - gcc-4.2.0 has issues compiling modules on 64-bit checkbin: @if test "$(call cc-version)" = "0304" ; then \ if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \ @@ -247,6 +237,12 @@ checkbin: false; \ fi ; \ fi + @if test "$(call cc-version)" -lt "0400" \ + && test "x${CONFIG_PPC64}" = "xy" ; then \ + echo -n "Sorry, GCC v4.0 or above is required to build " ; \ + echo "the 64-bit powerpc kernel." ; \ + false ; \ + fi @if test "$(call cc-fullversion)" = "040200" \ && test "x${CONFIG_MODULES}${CONFIG_PPC64}" = "xyy" ; then \ echo -n '*** GCC-4.2.0 cannot compile the 64-bit powerpc ' ; \ diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 1196c34163b..07b7f2af2dc 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -1,5 +1,4 @@ CONFIG_PPC64=y -CONFIG_POWER4_ONLY=y CONFIG_ALTIVEC=y CONFIG_SMP=y CONFIG_NR_CPUS=4 diff --git a/arch/powerpc/configs/maple_defconfig b/arch/powerpc/configs/maple_defconfig index 2244d370f24..02ac96b679b 100644 --- a/arch/powerpc/configs/maple_defconfig +++ b/arch/powerpc/configs/maple_defconfig @@ -1,5 +1,4 @@ CONFIG_PPC64=y -CONFIG_POWER4_ONLY=y CONFIG_SMP=y CONFIG_NR_CPUS=4 CONFIG_EXPERIMENTAL=y diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig index f4deb0b78cf..840a2c2d043 100644 --- a/arch/powerpc/configs/pasemi_defconfig +++ b/arch/powerpc/configs/pasemi_defconfig @@ -1,5 +1,4 @@ CONFIG_PPC64=y -CONFIG_POWER4_ONLY=y CONFIG_ALTIVEC=y # CONFIG_VIRT_CPU_ACCOUNTING is not set CONFIG_SMP=y diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index ded867871e9..c2f4b4a86ec 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -6,7 +6,6 @@ CONFIG_NR_CPUS=2 CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y -CONFIG_SPARSE_IRQ=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EMBEDDED=y @@ -25,7 +24,6 @@ CONFIG_PS3_DISK=y CONFIG_PS3_ROM=y CONFIG_PS3_FLASH=y CONFIG_PS3_VRAM=m -CONFIG_PS3_LPM=m # CONFIG_PPC_OF_BOOT_TRAMPOLINE is not set CONFIG_HIGH_RES_TIMERS=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set @@ -53,8 +51,6 @@ CONFIG_IP_PNP_DHCP=y # CONFIG_INET_DIAG is not set CONFIG_IPV6=y CONFIG_BT=m -CONFIG_BT_L2CAP=y -CONFIG_BT_SCO=y CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y CONFIG_BT_BNEP=m @@ -63,7 +59,6 @@ CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=m CONFIG_BT_HCIBTUSB=m CONFIG_CFG80211=m -# CONFIG_WIRELESS_EXT_SYSFS is not set CONFIG_MAC80211=m CONFIG_MAC80211_RC_PID=y # CONFIG_MAC80211_RC_MINSTREL is not set @@ -181,7 +176,6 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_WRITECOUNT=y CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_LIST=y -CONFIG_SYSCTL_SYSCALL_CHECK=y # CONFIG_FTRACE is not set CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_CRYPTO_CCM=m diff --git a/arch/powerpc/include/asm/asm-compat.h b/arch/powerpc/include/asm/asm-compat.h index decad950f11..5d7fbe1950f 100644 --- a/arch/powerpc/include/asm/asm-compat.h +++ b/arch/powerpc/include/asm/asm-compat.h @@ -29,18 +29,9 @@ #define PPC_LLARX(t, a, b, eh) PPC_LDARX(t, a, b, eh) #define PPC_STLCX stringify_in_c(stdcx.) #define PPC_CNTLZL stringify_in_c(cntlzd) +#define PPC_MTOCRF(FXM, RS) MTOCRF((FXM), (RS)) #define PPC_LR_STKOFF 16 #define PPC_MIN_STKFRM 112 - -/* Move to CR, single-entry optimized version. Only available - * on POWER4 and later. - */ -#ifdef CONFIG_POWER4_ONLY -#define PPC_MTOCRF stringify_in_c(mtocrf) -#else -#define PPC_MTOCRF stringify_in_c(mtcrf) -#endif - #else /* 32-bit */ /* operations for longs and pointers */ diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h index a76254af0aa..531fe0c3108 100644 --- a/arch/powerpc/include/asm/lppaca.h +++ b/arch/powerpc/include/asm/lppaca.h @@ -20,18 +20,16 @@ #define _ASM_POWERPC_LPPACA_H #ifdef __KERNEL__ -/* These definitions relate to hypervisors that only exist when using +/* + * These definitions relate to hypervisors that only exist when using * a server type processor */ #ifdef CONFIG_PPC_BOOK3S -//============================================================================= -// -// This control block contains the data that is shared between the -// hypervisor (PLIC) and the OS. -// -// -//---------------------------------------------------------------------------- +/* + * This control block contains the data that is shared between the + * hypervisor and the OS. + */ #include <linux/cache.h> #include <linux/threads.h> #include <asm/types.h> @@ -43,123 +41,65 @@ */ #define NR_LPPACAS 1 - -/* The Hypervisor barfs if the lppaca crosses a page boundary. A 1k - * alignment is sufficient to prevent this */ +/* + * The Hypervisor barfs if the lppaca crosses a page boundary. A 1k + * alignment is sufficient to prevent this + */ struct lppaca { -//============================================================================= -// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data -// NOTE: The xDynXyz fields are fields that will be dynamically changed by -// PLIC when preparing to bring a processor online or when dispatching a -// virtual processor! -//============================================================================= - u32 desc; // Eye catcher 0xD397D781 x00-x03 - u16 size; // Size of this struct x04-x05 - u16 reserved1; // Reserved x06-x07 - u16 reserved2:14; // Reserved x08-x09 - u8 shared_proc:1; // Shared processor indicator ... - u8 secondary_thread:1; // Secondary thread indicator ... - volatile u8 dyn_proc_status:8; // Dynamic Status of this proc x0A-x0A - u8 secondary_thread_count; // Secondary thread count x0B-x0B - volatile u16 dyn_hv_phys_proc_index;// Dynamic HV Physical Proc Index0C-x0D - volatile u16 dyn_hv_log_proc_index;// Dynamic HV Logical Proc Indexx0E-x0F - u32 decr_val; // Value for Decr programming x10-x13 - u32 pmc_val; // Value for PMC regs x14-x17 - volatile u32 dyn_hw_node_id; // Dynamic Hardware Node id x18-x1B - volatile u32 dyn_hw_proc_id; // Dynamic Hardware Proc Id x1C-x1F - volatile u32 dyn_pir; // Dynamic ProcIdReg value x20-x23 - u32 dsei_data; // DSEI data x24-x27 - u64 sprg3; // SPRG3 value x28-x2F - u8 reserved3[40]; // Reserved x30-x57 - volatile u8 vphn_assoc_counts[8]; // Virtual processor home node - // associativity change counters x58-x5F - u8 reserved4[32]; // Reserved x60-x7F - -//============================================================================= -// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data -//============================================================================= - // This Dword contains a byte for each type of interrupt that can occur. - // The IPI is a count while the others are just a binary 1 or 0. - union { - u64 any_int; - struct { - u16 reserved; // Reserved - cleared by #mpasmbl - u8 xirr_int; // Indicates xXirrValue is valid or Immed IO - u8 ipi_cnt; // IPI Count - u8 decr_int; // DECR interrupt occurred - u8 pdc_int; // PDC interrupt occurred - u8 quantum_int; // Interrupt quantum reached - u8 old_plic_deferred_ext_int; // Old PLIC has a deferred XIRR pending - } fields; - } int_dword; - - // Whenever any fields in this Dword are set then PLIC will defer the - // processing of external interrupts. Note that PLIC will store the - // XIRR directly into the xXirrValue field so that another XIRR will - // not be presented until this one clears. The layout of the low - // 4-bytes of this Dword is up to SLIC - PLIC just checks whether the - // entire Dword is zero or not. A non-zero value in the low order - // 2-bytes will result in SLIC being granted the highest thread - // priority upon return. A 0 will return to SLIC as medium priority. - u64 plic_defer_ints_area; // Entire Dword - - // Used to pass the real SRR0/1 from PLIC to SLIC as well as to - // pass the target SRR0/1 from SLIC to PLIC on a SetAsrAndRfid. - u64 saved_srr0; // Saved SRR0 x10-x17 - u64 saved_srr1; // Saved SRR1 x18-x1F - - // Used to pass parms from the OS to PLIC for SetAsrAndRfid - u64 saved_gpr3; // Saved GPR3 x20-x27 - u64 saved_gpr4; // Saved GPR4 x28-x2F - union { - u64 saved_gpr5; /* Saved GPR5 x30-x37 */ - struct { - u8 cede_latency_hint; /* x30 */ - u8 reserved[7]; /* x31-x36 */ - } fields; - } gpr5_dword; - - - u8 dtl_enable_mask; // Dispatch Trace Log mask x38-x38 - u8 donate_dedicated_cpu; // Donate dedicated CPU cycles x39-x39 - u8 fpregs_in_use; // FP regs in use x3A-x3A - u8 pmcregs_in_use; // PMC regs in use x3B-x3B - volatile u32 saved_decr; // Saved Decr Value x3C-x3F - volatile u64 emulated_time_base;// Emulated TB for this thread x40-x47 - volatile u64 cur_plic_latency; // Unaccounted PLIC latency x48-x4F - u64 tot_plic_latency; // Accumulated PLIC latency x50-x57 - u64 wait_state_cycles; // Wait cycles for this proc x58-x5F - u64 end_of_quantum; // TB at end of quantum x60-x67 - u64 pdc_saved_sprg1; // Saved SPRG1 for PMC int x68-x6F - u64 pdc_saved_srr0; // Saved SRR0 for PMC int x70-x77 - volatile u32 virtual_decr; // Virtual DECR for shared procsx78-x7B - u16 slb_count; // # of SLBs to maintain x7C-x7D - u8 idle; // Indicate OS is idle x7E - u8 vmxregs_in_use; // VMX registers in use x7F - - -//============================================================================= -// CACHE_LINE_3 0x0100 - 0x017F: This line is shared with other processors -//============================================================================= - // This is the yield_count. An "odd" value (low bit on) means that - // the processor is yielded (either because of an OS yield or a PLIC - // preempt). An even value implies that the processor is currently - // executing. - // NOTE: This value will ALWAYS be zero for dedicated processors and - // will NEVER be zero for shared processors (ie, initialized to a 1). - volatile u32 yield_count; // PLIC increments each dispatchx00-x03 - volatile u32 dispersion_count; // dispatch changed phys cpu x04-x07 - volatile u64 cmo_faults; // CMO page fault count x08-x0F - volatile u64 cmo_fault_time; // CMO page fault time x10-x17 - u8 reserved7[104]; // Reserved x18-x7F - -//============================================================================= -// CACHE_LINE_4-5 0x0180 - 0x027F Contains PMC interrupt data -//============================================================================= - u32 page_ins; // CMO Hint - # page ins by OS x00-x03 - u8 reserved8[148]; // Reserved x04-x97 - volatile u64 dtl_idx; // Dispatch Trace Log head idx x98-x9F - u8 reserved9[96]; // Reserved xA0-xFF + /* cacheline 1 contains read-only data */ + + u32 desc; /* Eye catcher 0xD397D781 */ + u16 size; /* Size of this struct */ + u16 reserved1; + u16 reserved2:14; + u8 shared_proc:1; /* Shared processor indicator */ + u8 secondary_thread:1; /* Secondary thread indicator */ + u8 reserved3[14]; + volatile u32 dyn_hw_node_id; /* Dynamic hardware node id */ + volatile u32 dyn_hw_proc_id; /* Dynamic hardware proc id */ + u8 reserved4[56]; + volatile u8 vphn_assoc_counts[8]; /* Virtual processor home node */ + /* associativity change counters */ + u8 reserved5[32]; + + /* cacheline 2 contains local read-write data */ + + u8 reserved6[48]; + u8 cede_latency_hint; + u8 reserved7[7]; + u8 dtl_enable_mask; /* Dispatch Trace Log mask */ + u8 donate_dedicated_cpu; /* Donate dedicated CPU cycles */ + u8 fpregs_in_use; + u8 pmcregs_in_use; + u8 reserved8[28]; + u64 wait_state_cycles; /* Wait cycles for this proc */ + u8 reserved9[28]; + u16 slb_count; /* # of SLBs to maintain */ + u8 idle; /* Indicate OS is idle */ + u8 vmxregs_in_use; + + /* cacheline 3 is shared with other processors */ + + /* + * This is the yield_count. An "odd" value (low bit on) means that + * the processor is yielded (either because of an OS yield or a + * hypervisor preempt). An even value implies that the processor is + * currently executing. + * NOTE: This value will ALWAYS be zero for dedicated processors and + * will NEVER be zero for shared processors (ie, initialized to a 1). + */ + volatile u32 yield_count; + volatile u32 dispersion_count; /* dispatch changed physical cpu */ + volatile u64 cmo_faults; /* CMO page fault count */ + volatile u64 cmo_fault_time; /* CMO page fault time */ + u8 reserved10[104]; + + /* cacheline 4-5 */ + + u32 page_ins; /* CMO Hint - # page ins by OS */ + u8 reserved11[148]; + volatile u64 dtl_idx; /* Dispatch Trace Log head index */ + u8 reserved12[96]; } __attribute__((__aligned__(0x400))); extern struct lppaca lppaca[]; @@ -172,13 +112,13 @@ extern struct lppaca lppaca[]; * ESID is stored in the lower 64bits, then the VSID. */ struct slb_shadow { - u32 persistent; // Number of persistent SLBs x00-x03 - u32 buffer_length; // Total shadow buffer length x04-x07 - u64 reserved; // Alignment x08-x0f + u32 persistent; /* Number of persistent SLBs */ + u32 buffer_length; /* Total shadow buffer length */ + u64 reserved; struct { u64 esid; u64 vsid; - } save_area[SLB_NUM_BOLTED]; // x10-x40 + } save_area[SLB_NUM_BOLTED]; } ____cacheline_aligned; extern struct slb_shadow slb_shadow[]; diff --git a/arch/powerpc/include/asm/lv1call.h b/arch/powerpc/include/asm/lv1call.h index 233f9ecae76..f5117674bf9 100644 --- a/arch/powerpc/include/asm/lv1call.h +++ b/arch/powerpc/include/asm/lv1call.h @@ -265,8 +265,8 @@ LV1_CALL(get_spe_irq_outlet, 2, 1, 78 ) LV1_CALL(set_spe_privilege_state_area_1_register, 3, 0, 79 ) LV1_CALL(create_repository_node, 6, 0, 90 ) LV1_CALL(read_repository_node, 5, 2, 91 ) -LV1_CALL(modify_repository_node_value, 6, 0, 92 ) -LV1_CALL(remove_repository_node, 4, 0, 93 ) +LV1_CALL(write_repository_node, 6, 0, 92 ) +LV1_CALL(delete_repository_node, 4, 0, 93 ) LV1_CALL(read_htab_entries, 2, 5, 95 ) LV1_CALL(set_dabr, 2, 0, 96 ) LV1_CALL(get_total_execution_time, 2, 1, 103 ) diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 50f73aa2ba2..15444204a3a 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -369,7 +369,15 @@ BEGIN_FTR_SECTION \ END_FTR_SECTION_IFCLR(CPU_FTR_601) #endif - +#ifdef CONFIG_PPC64 +#define MTOCRF(FXM, RS) \ + BEGIN_FTR_SECTION_NESTED(848); \ + mtcrf (FXM), (RS); \ + FTR_SECTION_ELSE_NESTED(848); \ + mtocrf (FXM), (RS); \ + ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_NOEXECUTE, 848) +#endif + /* * This instruction is not implemented on the PPC 603 or 601; however, on * the 403GCX and 405GP tlbia IS defined and tlbie is not. diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index 84cc7840cd1..9c21ed42aba 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -354,12 +354,6 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, #define PTRACE_GETREGS64 22 #define PTRACE_SETREGS64 23 -/* (old) PTRACE requests with inverted arguments */ -#define PPC_PTRACE_GETREGS 0x99 /* Get GPRs 0 - 31 */ -#define PPC_PTRACE_SETREGS 0x98 /* Set GPRs 0 - 31 */ -#define PPC_PTRACE_GETFPREGS 0x97 /* Get FPRs 0 - 31 */ -#define PPC_PTRACE_SETFPREGS 0x96 /* Set FPRs 0 - 31 */ - /* Calls to trace a 64bit program from a 32bit program */ #define PPC_PTRACE_PEEKTEXT_3264 0x95 #define PPC_PTRACE_PEEKDATA_3264 0x94 diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h index caf82d0a00d..1a6320290d2 100644 --- a/arch/powerpc/include/asm/switch_to.h +++ b/arch/powerpc/include/asm/switch_to.h @@ -21,7 +21,6 @@ extern void disable_kernel_fp(void); extern void enable_kernel_fp(void); extern void flush_fp_to_thread(struct task_struct *); extern void enable_kernel_altivec(void); -extern void giveup_altivec(struct task_struct *); extern void load_up_altivec(struct task_struct *); extern int emulate_altivec(struct pt_regs *); extern void __giveup_vsx(struct task_struct *); @@ -40,10 +39,15 @@ static inline void discard_lazy_cpu_state(void) #ifdef CONFIG_ALTIVEC extern void flush_altivec_to_thread(struct task_struct *); +extern void giveup_altivec(struct task_struct *); +extern void giveup_altivec_notask(void); #else static inline void flush_altivec_to_thread(struct task_struct *t) { } +static inline void giveup_altivec(struct task_struct *t) +{ +} #endif #ifdef CONFIG_VSX diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 34b8afe94a5..4554dc2fe85 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -188,10 +188,6 @@ int main(void) DEFINE(SLBSHADOW_STACKESID, offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].esid)); DEFINE(SLBSHADOW_SAVEAREA, offsetof(struct slb_shadow, save_area)); - DEFINE(LPPACASRR0, offsetof(struct lppaca, saved_srr0)); - DEFINE(LPPACASRR1, offsetof(struct lppaca, saved_srr1)); - DEFINE(LPPACAANYINT, offsetof(struct lppaca, int_dword.any_int)); - DEFINE(LPPACADECRINT, offsetof(struct lppaca, int_dword.fields.decr_int)); DEFINE(LPPACA_PMCINUSE, offsetof(struct lppaca, pmcregs_in_use)); DEFINE(LPPACA_DTLIDX, offsetof(struct lppaca, dtl_idx)); DEFINE(LPPACA_YIELDCOUNT, offsetof(struct lppaca, yield_count)); diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index fc6015027a8..29f13570214 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -63,15 +63,9 @@ system_call_common: std r0,GPR0(r1) std r10,GPR1(r1) ACCOUNT_CPU_USER_ENTRY(r10, r11) - /* - * This "crclr so" clears CR0.SO, which is the error indication on - * return from this system call. There must be no cmp instruction - * between it and the "mfcr r9" below, otherwise if XER.SO is set, - * CR0.SO will get set, causing all system calls to appear to fail. - */ - crclr so std r2,GPR2(r1) std r3,GPR3(r1) + mfcr r2 std r4,GPR4(r1) std r5,GPR5(r1) std r6,GPR6(r1) @@ -82,18 +76,20 @@ system_call_common: std r11,GPR10(r1) std r11,GPR11(r1) std r11,GPR12(r1) + std r11,_XER(r1) + std r11,_CTR(r1) std r9,GPR13(r1) - mfcr r9 mflr r10 + /* + * This clears CR0.SO (bit 28), which is the error indication on + * return from this system call. + */ + rldimi r2,r11,28,(63-28) li r11,0xc01 - std r9,_CCR(r1) std r10,_LINK(r1) std r11,_TRAP(r1) - mfxer r9 - mfctr r10 - std r9,_XER(r1) - std r10,_CTR(r1) std r3,ORIG_GPR3(r1) + std r2,_CCR(r1) ld r2,PACATOC(r13) addi r9,r1,STACK_FRAME_OVERHEAD ld r11,exception_marker@toc(r2) @@ -154,7 +150,7 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) ld r10,TI_FLAGS(r11) andi. r11,r10,_TIF_SYSCALL_T_OR_A bne- syscall_dotrace -syscall_dotrace_cont: +.Lsyscall_dotrace_cont: cmpldi 0,r0,NR_syscalls bge- syscall_enosys @@ -211,7 +207,7 @@ syscall_exit: cmpld r3,r11 ld r5,_CCR(r1) bge- syscall_error -syscall_error_cont: +.Lsyscall_error_cont: ld r7,_NIP(r1) BEGIN_FTR_SECTION stdcx. r0,0,r1 /* to clear the reservation */ @@ -246,7 +242,7 @@ syscall_error: oris r5,r5,0x1000 /* Set SO bit in CR */ neg r3,r3 std r5,_CCR(r1) - b syscall_error_cont + b .Lsyscall_error_cont /* Traced system call support */ syscall_dotrace: @@ -268,7 +264,7 @@ syscall_dotrace: addi r9,r1,STACK_FRAME_OVERHEAD clrrdi r10,r1,THREAD_SHIFT ld r10,TI_FLAGS(r10) - b syscall_dotrace_cont + b .Lsyscall_dotrace_cont syscall_enosys: li r3,-ENOSYS diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 8f880bc77c5..f7bed44ee16 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -94,12 +94,10 @@ machine_check_pSeries_1: data_access_pSeries: HMT_MEDIUM SET_SCRATCH0(r13) -#ifndef CONFIG_POWER4_ONLY BEGIN_FTR_SECTION b data_access_check_stab data_access_not_stab: END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) -#endif EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, data_access_common, EXC_STD, KVMTEST, 0x300) @@ -301,7 +299,6 @@ machine_check_fwnmi: EXC_STD, KVMTEST, 0x200) KVM_HANDLER_SKIP(PACA_EXMC, EXC_STD, 0x200) -#ifndef CONFIG_POWER4_ONLY /* moved from 0x300 */ data_access_check_stab: GET_PACA(r13) @@ -328,7 +325,6 @@ do_stab_bolted_pSeries: GET_SCRATCH0(r10) std r10,PACA_EXSLB+EX_R13(r13) EXCEPTION_PROLOG_PSERIES_1(.do_stab_bolted, EXC_STD) -#endif /* CONFIG_POWER4_ONLY */ KVM_HANDLER_SKIP(PACA_EXGEN, EXC_STD, 0x300) KVM_HANDLER_SKIP(PACA_EXSLB, EXC_STD, 0x380) diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S index 7dd2981bcc5..22d608e8bb7 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S @@ -778,14 +778,6 @@ _GLOBAL(__fixup_440A_mcheck) blr /* - * extern void giveup_altivec(struct task_struct *prev) - * - * The 44x core does not have an AltiVec unit. - */ -_GLOBAL(giveup_altivec) - blr - -/* * extern void giveup_fpu(struct task_struct *prev) * * The 44x core does not have an FPU. diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 28e62598d0e..de80e0f9a2b 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -874,14 +874,6 @@ _GLOBAL(__setup_e500mc_ivors) sync blr -/* - * extern void giveup_altivec(struct task_struct *prev) - * - * The e500 core does not have an AltiVec unit. - */ -_GLOBAL(giveup_altivec) - blr - #ifdef CONFIG_SPE /* * extern void giveup_spe(struct task_struct *prev) diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 0bb1f98613b..fbe1a12dc7f 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -36,10 +36,7 @@ struct lppaca lppaca[] = { [0 ... (NR_LPPACAS-1)] = { .desc = 0xd397d781, /* "LpPa" */ .size = sizeof(struct lppaca), - .dyn_proc_status = 2, - .decr_val = 0x00ff0000, .fpregs_in_use = 1, - .end_of_quantum = 0xfffffffffffffffful, .slb_count = 64, .vmxregs_in_use = 0, .page_ins = 0, diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 4937c969009..bb58f41fc04 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -124,7 +124,7 @@ void enable_kernel_altivec(void) if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) giveup_altivec(current); else - giveup_altivec(NULL); /* just enable AltiVec for kernel - force */ + giveup_altivec_notask(); #else giveup_altivec(last_task_used_altivec); #endif /* CONFIG_SMP */ diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 8d8e028893b..ac3cb008fb3 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1432,40 +1432,6 @@ static long ppc_del_hwdebug(struct task_struct *child, long addr, long data) #endif } -/* - * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, - * we mark them as obsolete now, they will be removed in a future version - */ -static long arch_ptrace_old(struct task_struct *child, long request, - unsigned long addr, unsigned long data) -{ - void __user *datavp = (void __user *) data; - - switch (request) { - case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ - return copy_regset_to_user(child, &user_ppc_native_view, - REGSET_GPR, 0, 32 * sizeof(long), - datavp); - - case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ - return copy_regset_from_user(child, &user_ppc_native_view, - REGSET_GPR, 0, 32 * sizeof(long), - datavp); - - case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ - return copy_regset_to_user(child, &user_ppc_native_view, - REGSET_FPR, 0, 32 * sizeof(double), - datavp); - - case PPC_PTRACE_SETFPREGS: /* Set FPRs 0 - 31. */ - return copy_regset_from_user(child, &user_ppc_native_view, - REGSET_FPR, 0, 32 * sizeof(double), - datavp); - } - - return -EPERM; -} - long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -1687,14 +1653,6 @@ long arch_ptrace(struct task_struct *child, long request, datavp); #endif - /* Old reverse args ptrace callss */ - case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ - case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ - case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ - case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */ - ret = arch_ptrace_old(child, request, addr, data); - break; - default: ret = ptrace_request(child, request, addr, data); break; diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 469349d14a9..8c21658719d 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -39,30 +39,6 @@ * in exit.c or in signal.c. */ -/* - * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, - * we mark them as obsolete now, they will be removed in a future version - */ -static long compat_ptrace_old(struct task_struct *child, long request, - long addr, long data) -{ - switch (request) { - case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ - return copy_regset_to_user(child, - task_user_regset_view(current), 0, - 0, 32 * sizeof(compat_long_t), - compat_ptr(data)); - - case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ - return copy_regset_from_user(child, - task_user_regset_view(current), 0, - 0, 32 * sizeof(compat_long_t), - compat_ptr(data)); - } - - return -EPERM; -} - /* Macros to workout the correct index for the FPR in the thread struct */ #define FPRNUMBER(i) (((i) - PT_FPR0) >> 1) #define FPRHALF(i) (((i) - PT_FPR0) & 1) @@ -308,8 +284,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, case PTRACE_SETVSRREGS: case PTRACE_GETREGS64: case PTRACE_SETREGS64: - case PPC_PTRACE_GETFPREGS: - case PPC_PTRACE_SETFPREGS: case PTRACE_KILL: case PTRACE_SINGLESTEP: case PTRACE_DETACH: @@ -322,12 +296,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ret = arch_ptrace(child, request, addr, data); break; - /* Old reverse args ptrace callss */ - case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ - case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ - ret = compat_ptrace_old(child, request, addr, data); - break; - default: ret = compat_ptrace_request(child, request, addr, data); break; diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index 4d5a3edff49..e830289d2e4 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -89,6 +89,16 @@ _GLOBAL(load_up_altivec) /* restore registers and return */ blr +_GLOBAL(giveup_altivec_notask) + mfmsr r3 + andis. r4,r3,MSR_VEC@h + bnelr /* Already enabled? */ + oris r3,r3,MSR_VEC@h + SYNC + MTMSRD(r3) /* enable use of VMX now */ + isync + blr + /* * giveup_altivec(tsk) * Disable VMX for the task given as the argument, diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S index 773d38f90aa..d73a5901490 100644 --- a/arch/powerpc/lib/copyuser_64.S +++ b/arch/powerpc/lib/copyuser_64.S @@ -30,7 +30,7 @@ _GLOBAL(__copy_tofrom_user_base) dcbt 0,r4 beq .Lcopy_page_4K andi. r6,r6,7 - PPC_MTOCRF 0x01,r5 + PPC_MTOCRF(0x01,r5) blt cr1,.Lshort_copy /* Below we want to nop out the bne if we're on a CPU that has the * CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit @@ -186,7 +186,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) blr .Ldst_unaligned: - PPC_MTOCRF 0x01,r6 /* put #bytes to 8B bdry into cr7 */ + PPC_MTOCRF(0x01,r6) /* put #bytes to 8B bdry into cr7 */ subf r5,r6,r5 li r7,0 cmpldi cr1,r5,16 @@ -201,7 +201,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) 2: bf cr7*4+1,3f 37: lwzx r0,r7,r4 83: stwx r0,r7,r3 -3: PPC_MTOCRF 0x01,r5 +3: PPC_MTOCRF(0x01,r5) add r4,r6,r4 add r3,r6,r3 b .Ldst_aligned diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S index 11ce045e21f..f4fcb0bc656 100644 --- a/arch/powerpc/lib/mem_64.S +++ b/arch/powerpc/lib/mem_64.S @@ -19,7 +19,7 @@ _GLOBAL(memset) rlwimi r4,r4,16,0,15 cmplw cr1,r5,r0 /* do we get that far? */ rldimi r4,r4,32,0 - PPC_MTOCRF 1,r0 + PPC_MTOCRF(1,r0) mr r6,r3 blt cr1,8f beq+ 3f /* if already 8-byte aligned */ @@ -49,7 +49,7 @@ _GLOBAL(memset) bdnz 4b 5: srwi. r0,r5,3 clrlwi r5,r5,29 - PPC_MTOCRF 1,r0 + PPC_MTOCRF(1,r0) beq 8f bf 29,6f std r4,0(r6) @@ -65,7 +65,7 @@ _GLOBAL(memset) std r4,0(r6) addi r6,r6,8 8: cmpwi r5,0 - PPC_MTOCRF 1,r5 + PPC_MTOCRF(1,r5) beqlr+ bf 29,9f stw r4,0(r6) diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S index e178922b2c2..82fea3963e1 100644 --- a/arch/powerpc/lib/memcpy_64.S +++ b/arch/powerpc/lib/memcpy_64.S @@ -12,7 +12,7 @@ .align 7 _GLOBAL(memcpy) std r3,48(r1) /* save destination pointer for return value */ - PPC_MTOCRF 0x01,r5 + PPC_MTOCRF(0x01,r5) cmpldi cr1,r5,16 neg r6,r3 # LS 3 bits = # bytes to 8-byte dest bdry andi. r6,r6,7 @@ -154,7 +154,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) blr .Ldst_unaligned: - PPC_MTOCRF 0x01,r6 # put #bytes to 8B bdry into cr7 + PPC_MTOCRF(0x01,r6) # put #bytes to 8B bdry into cr7 subf r5,r6,r5 li r7,0 cmpldi cr1,r5,16 @@ -169,7 +169,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) 2: bf cr7*4+1,3f lwzx r0,r7,r4 stwx r0,r7,r3 -3: PPC_MTOCRF 0x01,r5 +3: PPC_MTOCRF(0x01,r5) add r4,r6,r4 add r3,r6,r3 b .Ldst_aligned diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 425db18580a..0b731459213 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -78,6 +78,36 @@ config PPC_BOOK3E_64 endchoice +choice + prompt "CPU selection" + depends on PPC64 + default GENERIC_CPU + help + This will create a kernel which is optimised for a particular CPU. + The resulting kernel may not run on other CPUs, so use this with care. + + If unsure, select Generic. + +config GENERIC_CPU + bool "Generic" + +config CELL_CPU + bool "Cell Broadband Engine" + +config POWER4_CPU + bool "POWER4" + +config POWER5_CPU + bool "POWER5" + +config POWER6_CPU + bool "POWER6" + +config POWER7_CPU + bool "POWER7" + +endchoice + config PPC_BOOK3S def_bool y depends on PPC_BOOK3S_32 || PPC_BOOK3S_64 @@ -86,15 +116,6 @@ config PPC_BOOK3E def_bool y depends on PPC_BOOK3E_64 -config POWER4_ONLY - bool "Optimize for POWER4" - depends on PPC64 && PPC_BOOK3S - default n - ---help--- - Cause the compiler to optimize for POWER4/POWER5/PPC970 processors. - The resulting binary will not work on POWER3 or RS64 processors - when compiled with binutils 2.15 or later. - config 6xx def_bool y depends on PPC32 && PPC_BOOK3S diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 03685a329d7..fc536f2971c 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -1503,6 +1503,7 @@ static int __init pmac_i2c_create_platform_devices(void) if (bus->platform_dev == NULL) return -ENOMEM; bus->platform_dev->dev.platform_data = bus; + bus->platform_dev->dev.of_node = bus->busnode; platform_device_add(bus->platform_dev); } diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 476d9d9b240..46b7f023252 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -7,7 +7,6 @@ config PPC_PS3 select USB_OHCI_BIG_ENDIAN_MMIO select USB_ARCH_HAS_EHCI select USB_EHCI_BIG_ENDIAN_MMIO - select MEMORY_HOTPLUG select PPC_PCI_CHOICE help This option enables support for the Sony PS3 game console @@ -74,7 +73,7 @@ config PS3_PS3AV help Include support for the PS3 AV Settings driver. - This support is required for graphics and sound. In + This support is required for PS3 graphics and sound. In general, all users will say Y or M. config PS3_SYS_MANAGER @@ -85,9 +84,22 @@ config PS3_SYS_MANAGER help Include support for the PS3 System Manager. - This support is required for system control. In + This support is required for PS3 system control. In general, all users will say Y or M. +config PS3_REPOSITORY_WRITE + bool "PS3 Repository write support" if PS3_ADVANCED + depends on PPC_PS3 + default n + help + Enables support for writing to the PS3 System Repository. + + This support is intended for bootloaders that need to store data + in the repository for later boot stages. + + If in doubt, say N here and reduce the size of the kernel by a + small amount. + config PS3_STORAGE depends on PPC_PS3 tristate @@ -122,7 +134,7 @@ config PS3_FLASH This support is required to access the PS3 FLASH ROM, which contains the boot loader and some boot options. - In general, all users will say Y or M. + In general, PS3 OtherOS users will say Y or M. As this driver needs a fixed buffer of 256 KiB of memory, it can be disabled on the kernel command line using "ps3flash=off", to @@ -156,7 +168,7 @@ config PS3GELIC_UDBG via the Ethernet port (UDP port number 18194). This driver uses a trivial implementation and is independent - from the main network driver. + from the main PS3 gelic network driver. If in doubt, say N here. diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index de2aea42170..0c9f643d9e2 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/export.h> -#include <linux/memory_hotplug.h> #include <linux/memblock.h> #include <linux/slab.h> @@ -79,12 +78,14 @@ enum { * @base: base address * @size: size in bytes * @offset: difference between base and rm.size + * @destroy: flag if region should be destroyed upon shutdown */ struct mem_region { u64 base; u64 size; unsigned long offset; + int destroy; }; /** @@ -96,7 +97,7 @@ struct mem_region { * The HV virtual address space (vas) allows for hotplug memory regions. * Memory regions can be created and destroyed in the vas at runtime. * @rm: real mode (bootmem) region - * @r1: hotplug memory region(s) + * @r1: highmem region(s) * * ps3 addresses * virt_addr: a cpu 'translated' effective address @@ -222,10 +223,6 @@ void ps3_mm_vas_destroy(void) } } -/*============================================================================*/ -/* memory hotplug routines */ -/*============================================================================*/ - /** * ps3_mm_region_create - create a memory region in the vas * @r: pointer to a struct mem_region to accept initialized values @@ -262,6 +259,7 @@ static int ps3_mm_region_create(struct mem_region *r, unsigned long size) goto zero_region; } + r->destroy = 1; r->offset = r->base - map.rm.size; return result; @@ -279,7 +277,14 @@ static void ps3_mm_region_destroy(struct mem_region *r) { int result; + if (!r->destroy) { + pr_info("%s:%d: Not destroying high region: %llxh %llxh\n", + __func__, __LINE__, r->base, r->size); + return; + } + DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base); + if (r->base) { result = lv1_release_memory(r->base); BUG_ON(result); @@ -288,50 +293,36 @@ static void ps3_mm_region_destroy(struct mem_region *r) } } -/** - * ps3_mm_add_memory - hot add memory - */ - -static int __init ps3_mm_add_memory(void) +static int ps3_mm_get_repository_highmem(struct mem_region *r) { int result; - unsigned long start_addr; - unsigned long start_pfn; - unsigned long nr_pages; - - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) - return -ENODEV; - BUG_ON(!mem_init_done); + /* Assume a single highmem region. */ - start_addr = map.rm.size; - start_pfn = start_addr >> PAGE_SHIFT; - nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT; + result = ps3_repository_read_highmem_info(0, &r->base, &r->size); - DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n", - __func__, __LINE__, start_addr, start_pfn, nr_pages); - - result = add_memory(0, start_addr, map.r1.size); + if (result) + goto zero_region; - if (result) { - pr_err("%s:%d: add_memory failed: (%d)\n", - __func__, __LINE__, result); - return result; + if (!r->base || !r->size) { + result = -1; + goto zero_region; } - memblock_add(start_addr, map.r1.size); + r->offset = r->base - map.rm.size; - result = online_pages(start_pfn, nr_pages); + DBG("%s:%d: Found high region in repository: %llxh %llxh\n", + __func__, __LINE__, r->base, r->size); - if (result) - pr_err("%s:%d: online_pages failed: (%d)\n", - __func__, __LINE__, result); + return 0; +zero_region: + DBG("%s:%d: No high region in repository.\n", __func__, __LINE__); + + r->size = r->base = r->offset = 0; return result; } -device_initcall(ps3_mm_add_memory); - /*============================================================================*/ /* dma routines */ /*============================================================================*/ @@ -1217,13 +1208,23 @@ void __init ps3_mm_init(void) BUG_ON(map.rm.base); BUG_ON(!map.rm.size); + /* Check if we got the highmem region from an earlier boot step */ - /* arrange to do this in ps3_mm_add_memory */ - ps3_mm_region_create(&map.r1, map.total - map.rm.size); + if (ps3_mm_get_repository_highmem(&map.r1)) + ps3_mm_region_create(&map.r1, map.total - map.rm.size); /* correct map.total for the real total amount of memory we use */ map.total = map.rm.size + map.r1.size; + if (!map.r1.size) { + DBG("%s:%d: No highmem region found\n", __func__, __LINE__); + } else { + DBG("%s:%d: Adding highmem region: %llxh %llxh\n", + __func__, __LINE__, map.rm.size, + map.total - map.rm.size); + memblock_add(map.rm.size, map.total - map.rm.size); + } + DBG(" <- %s:%d\n", __func__, __LINE__); } diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index 1a633ed0fe9..d71329a8e32 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h @@ -188,6 +188,22 @@ int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size); int ps3_repository_read_region_total(u64 *region_total); int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total); +int ps3_repository_read_highmem_region_count(unsigned int *region_count); +int ps3_repository_read_highmem_base(unsigned int region_index, + u64 *highmem_base); +int ps3_repository_read_highmem_size(unsigned int region_index, + u64 *highmem_size); +int ps3_repository_read_highmem_info(unsigned int region_index, + u64 *highmem_base, u64 *highmem_size); + +int ps3_repository_write_highmem_region_count(unsigned int region_count); +int ps3_repository_write_highmem_base(unsigned int region_index, + u64 highmem_base); +int ps3_repository_write_highmem_size(unsigned int region_index, + u64 highmem_size); +int ps3_repository_write_highmem_info(unsigned int region_index, + u64 highmem_base, u64 highmem_size); +int ps3_repository_delete_highmem_info(unsigned int region_index); /* repository pme info */ diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index 7bdfea336f5..9b47ba7a5de 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -779,6 +779,72 @@ int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, u64 *region_total) } /** + * ps3_repository_read_highmem_region_count - Read the number of highmem regions + * + * Bootloaders must arrange the repository nodes such that regions are indexed + * with a region_index from 0 to region_count-1. + */ + +int ps3_repository_read_highmem_region_count(unsigned int *region_count) +{ + int result; + u64 v1 = 0; + + result = read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", 0), + make_field("count", 0), + 0, + &v1, NULL); + *region_count = v1; + return result; +} + + +int ps3_repository_read_highmem_base(unsigned int region_index, + u64 *highmem_base) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0, + highmem_base, NULL); +} + +int ps3_repository_read_highmem_size(unsigned int region_index, + u64 *highmem_size) +{ + return read_node(PS3_LPAR_ID_CURRENT, + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0, + highmem_size, NULL); +} + +/** + * ps3_repository_read_highmem_info - Read high memory region info + * @region_index: Region index, {0,..,region_count-1}. + * @highmem_base: High memory base address. + * @highmem_size: High memory size. + * + * Bootloaders that preallocate highmem regions must place the + * region info into the repository at these well known nodes. + */ + +int ps3_repository_read_highmem_info(unsigned int region_index, + u64 *highmem_base, u64 *highmem_size) +{ + int result; + + *highmem_base = 0; + result = ps3_repository_read_highmem_base(region_index, highmem_base); + return result ? result + : ps3_repository_read_highmem_size(region_index, highmem_size); +} + +/** * ps3_repository_read_num_spu_reserved - Number of physical spus reserved. * @num_spu: Number of physical spus. */ @@ -1002,6 +1068,138 @@ int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, lpar, rights); } +#if defined(CONFIG_PS3_REPOSITORY_WRITE) + +static int create_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) +{ + int result; + + dump_node(0, n1, n2, n3, n4, v1, v2); + + result = lv1_create_repository_node(n1, n2, n3, n4, v1, v2); + + if (result) { + pr_devel("%s:%d: lv1_create_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +static int delete_node(u64 n1, u64 n2, u64 n3, u64 n4) +{ + int result; + + dump_node(0, n1, n2, n3, n4, 0, 0); + + result = lv1_delete_repository_node(n1, n2, n3, n4); + + if (result) { + pr_devel("%s:%d: lv1_delete_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +static int write_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) +{ + int result; + + result = create_node(n1, n2, n3, n4, v1, v2); + + if (!result) + return 0; + + result = lv1_write_repository_node(n1, n2, n3, n4, v1, v2); + + if (result) { + pr_devel("%s:%d: lv1_write_repository_node failed: %s\n", + __func__, __LINE__, ps3_result(result)); + return -ENOENT; + } + + return 0; +} + +int ps3_repository_write_highmem_region_count(unsigned int region_count) +{ + int result; + u64 v1 = (u64)region_count; + + result = write_node( + make_first_field("highmem", 0), + make_field("region", 0), + make_field("count", 0), + 0, + v1, 0); + return result; +} + +int ps3_repository_write_highmem_base(unsigned int region_index, + u64 highmem_base) +{ + return write_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0, + highmem_base, 0); +} + +int ps3_repository_write_highmem_size(unsigned int region_index, + u64 highmem_size) +{ + return write_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0, + highmem_size, 0); +} + +int ps3_repository_write_highmem_info(unsigned int region_index, + u64 highmem_base, u64 highmem_size) +{ + int result; + + result = ps3_repository_write_highmem_base(region_index, highmem_base); + return result ? result + : ps3_repository_write_highmem_size(region_index, highmem_size); +} + +static int ps3_repository_delete_highmem_base(unsigned int region_index) +{ + return delete_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("base", 0), + 0); +} + +static int ps3_repository_delete_highmem_size(unsigned int region_index) +{ + return delete_node( + make_first_field("highmem", 0), + make_field("region", region_index), + make_field("size", 0), + 0); +} + +int ps3_repository_delete_highmem_info(unsigned int region_index) +{ + int result; + + result = ps3_repository_delete_highmem_base(region_index); + result += ps3_repository_delete_highmem_size(region_index); + + return result ? -1 : 0; +} + +#endif /* defined(CONFIG_PS3_WRITE_REPOSITORY) */ + #if defined(DEBUG) int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index a75e37dc41a..ecd394cf34e 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -489,7 +489,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) * a stack trace will help the device-driver authors figure * out what happened. So print that out. */ - dump_stack(); + WARN(1, "EEH: failure detected\n"); return 1; dn_unlock: diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index 342797fc0f9..13e8cc43adf 100644 --- a/arch/powerpc/platforms/pseries/plpar_wrappers.h +++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h @@ -22,12 +22,12 @@ static inline long poll_pending(void) static inline u8 get_cede_latency_hint(void) { - return get_lppaca()->gpr5_dword.fields.cede_latency_hint; + return get_lppaca()->cede_latency_hint; } static inline void set_cede_latency_hint(u8 latency_hint) { - get_lppaca()->gpr5_dword.fields.cede_latency_hint = latency_hint; + get_lppaca()->cede_latency_hint = latency_hint; } static inline long cede_processor(void) diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 7b397c6f607..31c47e18d83 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -227,6 +227,72 @@ static int __devexit i2c_powermac_remove(struct platform_device *dev) return 0; } +static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, + struct pmac_i2c_bus *bus) +{ + struct i2c_client *newdev; + struct device_node *node; + + for_each_child_of_node(adap->dev.of_node, node) { + struct i2c_board_info info = {}; + struct dev_archdata dev_ad = {}; + const __be32 *reg; + char tmp[16]; + u32 addr; + int len; + + /* Get address & channel */ + reg = of_get_property(node, "reg", &len); + if (!reg || (len < sizeof(int))) { + dev_err(&adap->dev, "i2c-powermac: invalid reg on %s\n", + node->full_name); + continue; + } + addr = be32_to_cpup(reg); + + /* Multibus setup, check channel */ + if (!pmac_i2c_match_adapter(node, adap)) + continue; + + dev_dbg(&adap->dev, "i2c-powermac: register %s\n", + node->full_name); + + /* Make up a modalias. Note: we to _NOT_ want the standard + * i2c drivers to match with any of our powermac stuff + * unless they have been specifically modified to handle + * it on a case by case basis. For example, for thermal + * control, things like lm75 etc... shall match with their + * corresponding windfarm drivers, _NOT_ the generic ones, + * so we force a prefix of AAPL, onto the modalias to + * make that happen + */ + if (of_modalias_node(node, tmp, sizeof(tmp)) < 0) { + dev_err(&adap->dev, "i2c-powermac: modalias failure" + " on %s\n", node->full_name); + continue; + } + snprintf(info.type, sizeof(info.type), "MAC,%s", tmp); + + /* Fill out the rest of the info structure */ + info.addr = (addr & 0xff) >> 1; + info.irq = irq_of_parse_and_map(node, 0); + info.of_node = of_node_get(node); + info.archdata = &dev_ad; + + newdev = i2c_new_device(adap, &info); + if (!newdev) { + dev_err(&adap->dev, "i2c-powermac: Failure to register" + " %s\n", node->full_name); + of_node_put(node); + /* We do not dispose of the interrupt mapping on + * purpose. It's not necessary (interrupt cannot be + * re-used) and somebody else might have grabbed it + * via direct DT lookup so let's not bother + */ + continue; + } + } +} static int __devinit i2c_powermac_probe(struct platform_device *dev) { @@ -272,6 +338,7 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) adapter->algo = &i2c_powermac_algorithm; i2c_set_adapdata(adapter, bus); adapter->dev.parent = &dev->dev; + adapter->dev.of_node = dev->dev.of_node; rc = i2c_add_adapter(adapter); if (rc) { printk(KERN_ERR "i2c-powermac: Adapter %s registration " @@ -281,33 +348,10 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); - if (!strncmp(basename, "uni-n", 5)) { - struct device_node *np; - const u32 *prop; - struct i2c_board_info info; - - /* Instantiate I2C motion sensor if present */ - np = of_find_node_by_name(NULL, "accelerometer"); - if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") && - (prop = of_get_property(np, "reg", NULL))) { - int i2c_bus; - const char *tmp_bus; - - /* look for bus either using "reg" or by path */ - tmp_bus = strstr(np->full_name, "/i2c-bus@"); - if (tmp_bus) - i2c_bus = *(tmp_bus + 9) - '0'; - else - i2c_bus = ((*prop) >> 8) & 0x0f; - - if (pmac_i2c_get_channel(bus) == i2c_bus) { - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = ((*prop) & 0xff) >> 1; - strlcpy(info.type, "ams", I2C_NAME_SIZE); - i2c_new_device(adapter, &info); - } - } - } + /* Cannot use of_i2c_register_devices() due to Apple device-tree + * funkyness + */ + i2c_powermac_register_devices(adapter, bus); return rc; } diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index fa51af11c6f..a555da64224 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -204,11 +204,14 @@ config THERM_ADT746X better fan behaviour by default, and some manual control. config THERM_PM72 - tristate "Support for thermal management on PowerMac G5" + tristate "Support for thermal management on PowerMac G5 (AGP)" depends on I2C && I2C_POWERMAC && PPC_PMAC64 + default n help This driver provides thermostat and fan control for the desktop - G5 machines. + G5 machines. + + This is deprecated, use windfarm instead. config WINDFARM tristate "New PowerMac thermal control infrastructure" @@ -221,6 +224,22 @@ config WINDFARM_PM81 help This driver provides thermal control for the iMacG5 +config WINDFARM_PM72 + tristate "Support for thermal management on PowerMac G5 (AGP)" + depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU + select I2C_POWERMAC + help + This driver provides thermal control for the PowerMac G5 + "AGP" variants (PowerMac 7,2 and 7,3) + +config WINDFARM_RM31 + tristate "Support for thermal management on Xserve G5" + depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU + select I2C_POWERMAC + help + This driver provides thermal control for the Xserve G5 + (RackMac3,1) + config WINDFARM_PM91 tristate "Support for thermal management on PowerMac9,1" depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index 6652a6ebb6f..6753b65f8ed 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -29,6 +29,20 @@ obj-$(CONFIG_THERM_PM72) += therm_pm72.o obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o obj-$(CONFIG_WINDFARM) += windfarm_core.o +obj-$(CONFIG_WINDFARM_PM72) += windfarm_fcu_controls.o \ + windfarm_ad7417_sensor.o \ + windfarm_lm75_sensor.o \ + windfarm_max6690_sensor.o \ + windfarm_pid.o \ + windfarm_cpufreq_clamp.o \ + windfarm_pm72.o +obj-$(CONFIG_WINDFARM_RM31) += windfarm_fcu_controls.o \ + windfarm_ad7417_sensor.o \ + windfarm_lm75_sensor.o \ + windfarm_lm87_sensor.o \ + windfarm_pid.o \ + windfarm_cpufreq_clamp.o \ + windfarm_rm31.o obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \ windfarm_smu_sensors.o \ windfarm_lm75_sensor.o windfarm_pid.o \ diff --git a/drivers/macintosh/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c index abeecd27b48..978eda8d667 100644 --- a/drivers/macintosh/ams/ams-i2c.c +++ b/drivers/macintosh/ams/ams-i2c.c @@ -65,7 +65,7 @@ static int ams_i2c_probe(struct i2c_client *client, static int ams_i2c_remove(struct i2c_client *client); static const struct i2c_device_id ams_id[] = { - { "ams", 0 }, + { "MAC,accelerometer_1", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ams_id); diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index fc71723cbc4..f433521a6f9 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -47,7 +47,7 @@ static u8 FAN_SPD_SET[2] = {0x30, 0x31}; static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ -static const char *sensor_location[3]; +static const char *sensor_location[3] = { "?", "?", "?" }; static int limit_adjust; static int fan_speed = -1; @@ -79,18 +79,16 @@ struct thermostat { int last_speed[2]; int last_var[2]; int pwm_inv[2]; + struct task_struct *thread; + struct platform_device *pdev; + enum { + ADT7460, + ADT7467 + } type; }; -static enum {ADT7460, ADT7467} therm_type; -static int therm_bus, therm_address; -static struct platform_device * of_dev; -static struct thermostat* thermostat; -static struct task_struct *thread_therm = NULL; - static void write_both_fan_speed(struct thermostat *th, int speed); static void write_fan_speed(struct thermostat *th, int speed, int fan); -static void thermostat_create_files(void); -static void thermostat_remove_files(void); static int write_reg(struct thermostat* th, int reg, u8 data) @@ -126,66 +124,6 @@ read_reg(struct thermostat* th, int reg) return data; } -static struct i2c_driver thermostat_driver; - -static int -attach_thermostat(struct i2c_adapter *adapter) -{ - unsigned long bus_no; - struct i2c_board_info info; - struct i2c_client *client; - - if (strncmp(adapter->name, "uni-n", 5)) - return -ENODEV; - bus_no = simple_strtoul(adapter->name + 6, NULL, 10); - if (bus_no != therm_bus) - return -ENODEV; - - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE); - info.addr = therm_address; - client = i2c_new_device(adapter, &info); - if (!client) - return -ENODEV; - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &thermostat_driver.clients); - return 0; -} - -static int -remove_thermostat(struct i2c_client *client) -{ - struct thermostat *th = i2c_get_clientdata(client); - int i; - - thermostat_remove_files(); - - if (thread_therm != NULL) { - kthread_stop(thread_therm); - } - - printk(KERN_INFO "adt746x: Putting max temperatures back from " - "%d, %d, %d to %d, %d, %d\n", - th->limits[0], th->limits[1], th->limits[2], - th->initial_limits[0], th->initial_limits[1], - th->initial_limits[2]); - - for (i = 0; i < 3; i++) - write_reg(th, LIMIT_REG[i], th->initial_limits[i]); - - write_both_fan_speed(th, -1); - - thermostat = NULL; - - kfree(th); - - return 0; -} - static int read_fan_speed(struct thermostat *th, u8 addr) { u8 tmp[2]; @@ -203,7 +141,7 @@ static int read_fan_speed(struct thermostat *th, u8 addr) static void write_both_fan_speed(struct thermostat *th, int speed) { write_fan_speed(th, speed, 0); - if (therm_type == ADT7460) + if (th->type == ADT7460) write_fan_speed(th, speed, 1); } @@ -216,7 +154,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) else if (speed < -1) speed = 0; - if (therm_type == ADT7467 && fan == 1) + if (th->type == ADT7467 && fan == 1) return; if (th->last_speed[fan] != speed) { @@ -239,7 +177,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) write_reg(th, FAN_SPD_SET[fan], speed); } else { /* back to automatic */ - if(therm_type == ADT7460) { + if(th->type == ADT7460) { manual = read_reg(th, MANUAL_MODE[fan]) & (~MANUAL_MASK); manual &= ~INVERT_MASK; @@ -293,7 +231,7 @@ static void update_fans_speed (struct thermostat *th) /* we don't care about local sensor, so we start at sensor 1 */ for (i = 1; i < 3; i++) { int started = 0; - int fan_number = (therm_type == ADT7460 && i == 2); + int fan_number = (th->type == ADT7460 && i == 2); int var = th->temps[i] - th->limits[i]; if (var > -1) { @@ -370,116 +308,22 @@ static int monitor_task(void *arg) static void set_limit(struct thermostat *th, int i) { - /* Set sensor1 limit higher to avoid powerdowns */ - th->limits[i] = default_limits_chip[i] + limit_adjust; - write_reg(th, LIMIT_REG[i], th->limits[i]); + /* Set sensor1 limit higher to avoid powerdowns */ + th->limits[i] = default_limits_chip[i] + limit_adjust; + write_reg(th, LIMIT_REG[i], th->limits[i]); - /* set our limits to normal */ - th->limits[i] = default_limits_local[i] + limit_adjust; + /* set our limits to normal */ + th->limits[i] = default_limits_local[i] + limit_adjust; } -static int probe_thermostat(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct thermostat* th; - int rc; - int i; - - if (thermostat) - return 0; - - th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); - if (!th) - return -ENOMEM; - - i2c_set_clientdata(client, th); - th->clt = client; - - rc = read_reg(th, CONFIG_REG); - if (rc < 0) { - dev_err(&client->dev, "Thermostat failed to read config!\n"); - kfree(th); - return -ENODEV; - } - - /* force manual control to start the fan quieter */ - if (fan_speed == -1) - fan_speed = 64; - - if(therm_type == ADT7460) { - printk(KERN_INFO "adt746x: ADT7460 initializing\n"); - /* The 7460 needs to be started explicitly */ - write_reg(th, CONFIG_REG, 1); - } else - printk(KERN_INFO "adt746x: ADT7467 initializing\n"); - - for (i = 0; i < 3; i++) { - th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); - set_limit(th, i); - } - - printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" - " to %d, %d, %d\n", - th->initial_limits[0], th->initial_limits[1], - th->initial_limits[2], th->limits[0], th->limits[1], - th->limits[2]); - - thermostat = th; - - /* record invert bit status because fw can corrupt it after suspend */ - th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; - th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; - - /* be sure to really write fan speed the first time */ - th->last_speed[0] = -2; - th->last_speed[1] = -2; - th->last_var[0] = -80; - th->last_var[1] = -80; - - if (fan_speed != -1) { - /* manual mode, stop fans */ - write_both_fan_speed(th, 0); - } else { - /* automatic mode */ - write_both_fan_speed(th, -1); - } - - thread_therm = kthread_run(monitor_task, th, "kfand"); - - if (thread_therm == ERR_PTR(-ENOMEM)) { - printk(KERN_INFO "adt746x: Kthread creation failed\n"); - thread_therm = NULL; - return -ENOMEM; - } - - thermostat_create_files(); - - return 0; +#define BUILD_SHOW_FUNC_INT(name, data) \ +static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct thermostat *th = dev_get_drvdata(dev); \ + return sprintf(buf, "%d\n", data); \ } -static const struct i2c_device_id therm_adt746x_id[] = { - { "therm_adt746x", 0 }, - { } -}; - -static struct i2c_driver thermostat_driver = { - .driver = { - .name = "therm_adt746x", - }, - .attach_adapter = attach_thermostat, - .probe = probe_thermostat, - .remove = remove_thermostat, - .id_table = therm_adt746x_id, -}; - -/* - * Now, unfortunately, sysfs doesn't give us a nice void * we could - * pass around to the attribute functions, so we don't really have - * choice but implement a bunch of them... - * - * FIXME, it does now... - */ -#define BUILD_SHOW_FUNC_INT(name, data) \ +#define BUILD_SHOW_FUNC_INT_LITE(name, data) \ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ { \ return sprintf(buf, "%d\n", data); \ @@ -494,22 +338,24 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch #define BUILD_SHOW_FUNC_FAN(name, data) \ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ { \ + struct thermostat *th = dev_get_drvdata(dev); \ return sprintf(buf, "%d (%d rpm)\n", \ - thermostat->last_speed[data], \ - read_fan_speed(thermostat, FAN_SPEED[data]) \ + th->last_speed[data], \ + read_fan_speed(th, FAN_SPEED[data]) \ ); \ } #define BUILD_STORE_FUNC_DEG(name, data) \ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ { \ + struct thermostat *th = dev_get_drvdata(dev); \ int val; \ int i; \ val = simple_strtol(buf, NULL, 10); \ printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ limit_adjust = val; \ for (i=0; i < 3; i++) \ - set_limit(thermostat, i); \ + set_limit(th, i); \ return n; \ } @@ -525,20 +371,21 @@ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, c return n; \ } -BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1]))) -BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2]))) -BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1]) -BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2]) +BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(th, TEMP_REG[1]))) +BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(th, TEMP_REG[2]))) +BUILD_SHOW_FUNC_INT(sensor1_limit, th->limits[1]) +BUILD_SHOW_FUNC_INT(sensor2_limit, th->limits[2]) BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) -BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed) +BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed) +BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) + BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) -BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) -BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust) -BUILD_STORE_FUNC_DEG(limit_adjust, thermostat) +BUILD_SHOW_FUNC_INT_LITE(limit_adjust, limit_adjust) +BUILD_STORE_FUNC_DEG(limit_adjust, th) static DEVICE_ATTR(sensor1_temperature, S_IRUGO, show_sensor1_temperature,NULL); @@ -564,53 +411,77 @@ static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO, static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, show_limit_adjust, store_limit_adjust); - -static int __init -thermostat_init(void) +static void thermostat_create_files(struct thermostat *th) { - struct device_node* np; - const u32 *prop; - int i = 0, offset = 0; + struct device_node *np = th->clt->dev.of_node; + struct device *dev; + int err; - np = of_find_node_by_name(NULL, "fan"); - if (!np) - return -ENODEV; - if (of_device_is_compatible(np, "adt7460")) - therm_type = ADT7460; - else if (of_device_is_compatible(np, "adt7467")) - therm_type = ADT7467; - else { - of_node_put(np); - return -ENODEV; - } + /* To maintain ABI compatibility with userspace, create + * the old style platform driver and attach the attributes + * to it here + */ + th->pdev = of_platform_device_create(np, "temperatures", NULL); + if (!th->pdev) + return; + dev = &th->pdev->dev; + dev_set_drvdata(dev, th); + err = device_create_file(dev, &dev_attr_sensor1_temperature); + err |= device_create_file(dev, &dev_attr_sensor2_temperature); + err |= device_create_file(dev, &dev_attr_sensor1_limit); + err |= device_create_file(dev, &dev_attr_sensor2_limit); + err |= device_create_file(dev, &dev_attr_sensor1_location); + err |= device_create_file(dev, &dev_attr_sensor2_location); + err |= device_create_file(dev, &dev_attr_limit_adjust); + err |= device_create_file(dev, &dev_attr_specified_fan_speed); + err |= device_create_file(dev, &dev_attr_sensor1_fan_speed); + if(th->type == ADT7460) + err |= device_create_file(dev, &dev_attr_sensor2_fan_speed); + if (err) + printk(KERN_WARNING + "Failed to create temperature attribute file(s).\n"); +} - prop = of_get_property(np, "hwsensor-params-version", NULL); - printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, - (*prop == 1)?"":"un"); - if (*prop != 1) { - of_node_put(np); - return -ENODEV; - } +static void thermostat_remove_files(struct thermostat *th) +{ + struct device *dev; - prop = of_get_property(np, "reg", NULL); - if (!prop) { - of_node_put(np); - return -ENODEV; - } + if (!th->pdev) + return; + dev = &th->pdev->dev; + device_remove_file(dev, &dev_attr_sensor1_temperature); + device_remove_file(dev, &dev_attr_sensor2_temperature); + device_remove_file(dev, &dev_attr_sensor1_limit); + device_remove_file(dev, &dev_attr_sensor2_limit); + device_remove_file(dev, &dev_attr_sensor1_location); + device_remove_file(dev, &dev_attr_sensor2_location); + device_remove_file(dev, &dev_attr_limit_adjust); + device_remove_file(dev, &dev_attr_specified_fan_speed); + device_remove_file(dev, &dev_attr_sensor1_fan_speed); + if (th->type == ADT7460) + device_remove_file(dev, &dev_attr_sensor2_fan_speed); + of_device_unregister(th->pdev); - /* look for bus either by path or using "reg" */ - if (strstr(np->full_name, "/i2c-bus@") != NULL) { - const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9); - therm_bus = tmp_bus[0]-'0'; - } else { - therm_bus = ((*prop) >> 8) & 0x0f; - } +} - therm_address = ((*prop) & 0xff) >> 1; +static int probe_thermostat(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *np = client->dev.of_node; + struct thermostat* th; + const __be32 *prop; + int i, rc, vers, offset = 0; - printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, " - "limit_adjust: %d, fan_speed: %d\n", - therm_bus, therm_address, limit_adjust, fan_speed); + if (!np) + return -ENXIO; + prop = of_get_property(np, "hwsensor-params-version", NULL); + if (!prop) + return -ENXIO; + vers = be32_to_cpup(prop); + printk(KERN_INFO "adt746x: version %d (%ssupported)\n", + vers, vers == 1 ? "" : "un"); + if (vers != 1) + return -ENXIO; if (of_get_property(np, "hwsensor-location", NULL)) { for (i = 0; i < 3; i++) { @@ -623,72 +494,129 @@ thermostat_init(void) printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); offset += strlen(sensor_location[i]) + 1; } - } else { - sensor_location[0] = "?"; - sensor_location[1] = "?"; - sensor_location[2] = "?"; } - of_dev = of_platform_device_create(np, "temperatures", NULL); - of_node_put(np); + th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); + if (!th) + return -ENOMEM; + + i2c_set_clientdata(client, th); + th->clt = client; + th->type = id->driver_data; - if (of_dev == NULL) { - printk(KERN_ERR "Can't register temperatures device !\n"); + rc = read_reg(th, CONFIG_REG); + if (rc < 0) { + dev_err(&client->dev, "Thermostat failed to read config!\n"); + kfree(th); return -ENODEV; } -#ifndef CONFIG_I2C_POWERMAC - request_module("i2c-powermac"); -#endif + /* force manual control to start the fan quieter */ + if (fan_speed == -1) + fan_speed = 64; + + if (th->type == ADT7460) { + printk(KERN_INFO "adt746x: ADT7460 initializing\n"); + /* The 7460 needs to be started explicitly */ + write_reg(th, CONFIG_REG, 1); + } else + printk(KERN_INFO "adt746x: ADT7467 initializing\n"); - return i2c_add_driver(&thermostat_driver); + for (i = 0; i < 3; i++) { + th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); + set_limit(th, i); + } + + printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" + " to %d, %d, %d\n", + th->initial_limits[0], th->initial_limits[1], + th->initial_limits[2], th->limits[0], th->limits[1], + th->limits[2]); + + /* record invert bit status because fw can corrupt it after suspend */ + th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; + th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; + + /* be sure to really write fan speed the first time */ + th->last_speed[0] = -2; + th->last_speed[1] = -2; + th->last_var[0] = -80; + th->last_var[1] = -80; + + if (fan_speed != -1) { + /* manual mode, stop fans */ + write_both_fan_speed(th, 0); + } else { + /* automatic mode */ + write_both_fan_speed(th, -1); + } + + th->thread = kthread_run(monitor_task, th, "kfand"); + if (th->thread == ERR_PTR(-ENOMEM)) { + printk(KERN_INFO "adt746x: Kthread creation failed\n"); + th->thread = NULL; + return -ENOMEM; + } + + thermostat_create_files(th); + + return 0; } -static void thermostat_create_files(void) +static int remove_thermostat(struct i2c_client *client) { - int err; + struct thermostat *th = i2c_get_clientdata(client); + int i; + + thermostat_remove_files(th); - err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location); - err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust); - err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); - if(therm_type == ADT7460) - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed); - if (err) - printk(KERN_WARNING - "Failed to create temperature attribute file(s).\n"); + if (th->thread != NULL) + kthread_stop(th->thread); + + printk(KERN_INFO "adt746x: Putting max temperatures back from " + "%d, %d, %d to %d, %d, %d\n", + th->limits[0], th->limits[1], th->limits[2], + th->initial_limits[0], th->initial_limits[1], + th->initial_limits[2]); + + for (i = 0; i < 3; i++) + write_reg(th, LIMIT_REG[i], th->initial_limits[i]); + + write_both_fan_speed(th, -1); + + kfree(th); + + return 0; } -static void thermostat_remove_files(void) +static const struct i2c_device_id therm_adt746x_id[] = { + { "MAC,adt7460", ADT7460 }, + { "MAC,adt7467", ADT7467 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, therm_adt746x_id); + +static struct i2c_driver thermostat_driver = { + .driver = { + .name = "therm_adt746x", + }, + .probe = probe_thermostat, + .remove = remove_thermostat, + .id_table = therm_adt746x_id, +}; + +static int __init thermostat_init(void) { - if (of_dev) { - device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature); - device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature); - device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit); - device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit); - device_remove_file(&of_dev->dev, &dev_attr_sensor1_location); - device_remove_file(&of_dev->dev, &dev_attr_sensor2_location); - device_remove_file(&of_dev->dev, &dev_attr_limit_adjust); - device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed); - device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); - - if(therm_type == ADT7460) - device_remove_file(&of_dev->dev, - &dev_attr_sensor2_fan_speed); +#ifndef CONFIG_I2C_POWERMAC + request_module("i2c-powermac"); +#endif - } + return i2c_add_driver(&thermostat_driver); } -static void __exit -thermostat_exit(void) +static void __exit thermostat_exit(void) { i2c_del_driver(&thermostat_driver); - of_device_unregister(of_dev); } module_init(thermostat_init); diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h index 7a2482cc26a..028cdac2d33 100644 --- a/drivers/macintosh/windfarm.h +++ b/drivers/macintosh/windfarm.h @@ -17,7 +17,7 @@ #include <linux/device.h> /* Display a 16.16 fixed point value */ -#define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16) +#define FIX32TOPRINT(f) (((s32)(f)) >> 16),(((((s32)(f)) & 0xffff) * 1000) >> 16) /* * Control objects @@ -35,12 +35,13 @@ struct wf_control_ops { }; struct wf_control { - struct list_head link; - struct wf_control_ops *ops; - char *name; - int type; - struct kref ref; - struct device_attribute attr; + struct list_head link; + const struct wf_control_ops *ops; + const char *name; + int type; + struct kref ref; + struct device_attribute attr; + void *priv; }; #define WF_CONTROL_TYPE_GENERIC 0 @@ -72,6 +73,26 @@ static inline int wf_control_set_min(struct wf_control *ct) return ct->ops->set_value(ct, vmin); } +static inline int wf_control_set(struct wf_control *ct, s32 val) +{ + return ct->ops->set_value(ct, val); +} + +static inline int wf_control_get(struct wf_control *ct, s32 *val) +{ + return ct->ops->get_value(ct, val); +} + +static inline s32 wf_control_get_min(struct wf_control *ct) +{ + return ct->ops->get_min(ct); +} + +static inline s32 wf_control_get_max(struct wf_control *ct) +{ + return ct->ops->get_max(ct); +} + /* * Sensor objects */ @@ -85,11 +106,12 @@ struct wf_sensor_ops { }; struct wf_sensor { - struct list_head link; - struct wf_sensor_ops *ops; - char *name; - struct kref ref; - struct device_attribute attr; + struct list_head link; + const struct wf_sensor_ops *ops; + const char *name; + struct kref ref; + struct device_attribute attr; + void *priv; }; /* Same lifetime rules as controls */ @@ -99,6 +121,11 @@ extern struct wf_sensor * wf_find_sensor(const char *name); extern int wf_get_sensor(struct wf_sensor *sr); extern void wf_put_sensor(struct wf_sensor *sr); +static inline int wf_sensor_get(struct wf_sensor *sr, s32 *val) +{ + return sr->ops->get_value(sr, val); +} + /* For use by clients. Note that we are a bit racy here since * notifier_block doesn't have a module owner field. I may fix * it one day ... diff --git a/drivers/macintosh/windfarm_ad7417_sensor.c b/drivers/macintosh/windfarm_ad7417_sensor.c new file mode 100644 index 00000000000..ac3f243b9c5 --- /dev/null +++ b/drivers/macintosh/windfarm_ad7417_sensor.c @@ -0,0 +1,347 @@ +/* + * Windfarm PowerMac thermal control. AD7417 sensors + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/i2c.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/sections.h> + +#include "windfarm.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +struct wf_ad7417_priv { + struct kref ref; + struct i2c_client *i2c; + u8 config; + u8 cpu; + const struct mpu_data *mpu; + struct wf_sensor sensors[5]; + struct mutex lock; +}; + +static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value) +{ + struct wf_ad7417_priv *pv = sr->priv; + u8 buf[2]; + s16 raw; + int rc; + + *value = 0; + mutex_lock(&pv->lock); + + /* Read temp register */ + buf[0] = 0; + rc = i2c_master_send(pv->i2c, buf, 1); + if (rc < 0) + goto error; + rc = i2c_master_recv(pv->i2c, buf, 2); + if (rc < 0) + goto error; + + /* Read a a 16-bit signed value */ + raw = be16_to_cpup((__le16 *)buf); + + /* Convert 8.8-bit to 16.16 fixed point */ + *value = ((s32)raw) << 8; + + mutex_unlock(&pv->lock); + return 0; + +error: + mutex_unlock(&pv->lock); + return -1; +} + +/* + * Scaling factors for the AD7417 ADC converters (except + * for the CPU diode which is obtained from the EEPROM). + * Those values are obtained from the property list of + * the darwin driver + */ +#define ADC_12V_CURRENT_SCALE 0x0320 /* _AD2 */ +#define ADC_CPU_VOLTAGE_SCALE 0x00a0 /* _AD3 */ +#define ADC_CPU_CURRENT_SCALE 0x1f40 /* _AD4 */ + +static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv, + int chan, s32 raw, s32 *value) +{ + switch(chan) { + case 1: /* Diode */ + *value = (raw * (s32)pv->mpu->mdiode + + ((s32)pv->mpu->bdiode << 12)) >> 2; + break; + case 2: /* 12v current */ + *value = raw * ADC_12V_CURRENT_SCALE; + break; + case 3: /* core voltage */ + *value = raw * ADC_CPU_VOLTAGE_SCALE; + break; + case 4: /* core current */ + *value = raw * ADC_CPU_CURRENT_SCALE; + break; + } +} + +static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value) +{ + struct wf_ad7417_priv *pv = sr->priv; + int chan = sr - pv->sensors; + int i, rc; + u8 buf[2]; + u16 raw; + + *value = 0; + mutex_lock(&pv->lock); + for (i = 0; i < 10; i++) { + /* Set channel */ + buf[0] = 1; + buf[1] = (pv->config & 0x1f) | (chan << 5); + rc = i2c_master_send(pv->i2c, buf, 2); + if (rc < 0) + goto error; + + /* Wait for conversion */ + msleep(1); + + /* Switch to data register */ + buf[0] = 4; + rc = i2c_master_send(pv->i2c, buf, 1); + if (rc < 0) + goto error; + + /* Read result */ + rc = i2c_master_recv(pv->i2c, buf, 2); + if (rc < 0) + goto error; + + /* Read a a 16-bit signed value */ + raw = be16_to_cpup((__le16 *)buf) >> 6; + wf_ad7417_adc_convert(pv, chan, raw, value); + + dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]" + " raw value: 0x%x, conv to: 0x%08x\n", + chan, sr->name, raw, *value); + + mutex_unlock(&pv->lock); + return 0; + + error: + dev_dbg(&pv->i2c->dev, + "Error reading ADC, try %d...\n", i); + if (i < 9) + msleep(10); + } + mutex_unlock(&pv->lock); + return -1; +} + +static void wf_ad7417_release(struct kref *ref) +{ + struct wf_ad7417_priv *pv = container_of(ref, + struct wf_ad7417_priv, ref); + kfree(pv); +} + +static void wf_ad7417_sensor_release(struct wf_sensor *sr) +{ + struct wf_ad7417_priv *pv = sr->priv; + + kfree(sr->name); + kref_put(&pv->ref, wf_ad7417_release); +} + +static const struct wf_sensor_ops wf_ad7417_temp_ops = { + .get_value = wf_ad7417_temp_get, + .release = wf_ad7417_sensor_release, + .owner = THIS_MODULE, +}; + +static const struct wf_sensor_ops wf_ad7417_adc_ops = { + .get_value = wf_ad7417_adc_get, + .release = wf_ad7417_sensor_release, + .owner = THIS_MODULE, +}; + +static void __devinit wf_ad7417_add_sensor(struct wf_ad7417_priv *pv, + int index, const char *name, + const struct wf_sensor_ops *ops) +{ + pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu); + pv->sensors[index].priv = pv; + pv->sensors[index].ops = ops; + if (!wf_register_sensor(&pv->sensors[index])) + kref_get(&pv->ref); +} + +static void __devinit wf_ad7417_init_chip(struct wf_ad7417_priv *pv) +{ + int rc; + u8 buf[2]; + u8 config = 0; + + /* + * Read ADC the configuration register and cache it. We + * also make sure Config2 contains proper values, I've seen + * cases where we got stale grabage in there, thus preventing + * proper reading of conv. values + */ + + /* Clear Config2 */ + buf[0] = 5; + buf[1] = 0; + i2c_master_send(pv->i2c, buf, 2); + + /* Read & cache Config1 */ + buf[0] = 1; + rc = i2c_master_send(pv->i2c, buf, 1); + if (rc > 0) { + rc = i2c_master_recv(pv->i2c, buf, 1); + if (rc > 0) { + config = buf[0]; + + dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n", + config); + + /* Disable shutdown mode */ + config &= 0xfe; + buf[0] = 1; + buf[1] = config; + rc = i2c_master_send(pv->i2c, buf, 2); + } + } + if (rc <= 0) + dev_err(&pv->i2c->dev, "Error reading ADC config\n"); + + pv->config = config; +} + +static int __devinit wf_ad7417_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wf_ad7417_priv *pv; + const struct mpu_data *mpu; + const char *loc; + int cpu_nr; + + loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); + if (!loc) { + dev_warn(&client->dev, "Missing hwsensor-location property!\n"); + return -ENXIO; + } + + /* + * Identify which CPU we belong to by looking at the first entry + * in the hwsensor-location list + */ + if (!strncmp(loc, "CPU A", 5)) + cpu_nr = 0; + else if (!strncmp(loc, "CPU B", 5)) + cpu_nr = 1; + else { + pr_err("wf_ad7417: Can't identify location %s\n", loc); + return -ENXIO; + } + mpu = wf_get_mpu(cpu_nr); + if (!mpu) { + dev_err(&client->dev, "Failed to retrieve MPU data\n"); + return -ENXIO; + } + + pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL); + if (pv == NULL) + return -ENODEV; + + kref_init(&pv->ref); + mutex_init(&pv->lock); + pv->i2c = client; + pv->cpu = cpu_nr; + pv->mpu = mpu; + dev_set_drvdata(&client->dev, pv); + + /* Initialize the chip */ + wf_ad7417_init_chip(pv); + + /* + * We cannot rely on Apple device-tree giving us child + * node with the names of the individual sensors so we + * just hard code what we know about them + */ + wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops); + wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops); + wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops); + wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops); + wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops); + + return 0; +} + +static int __devexit wf_ad7417_remove(struct i2c_client *client) +{ + struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev); + int i; + + /* Mark client detached */ + pv->i2c = NULL; + + /* Release sensor */ + for (i = 0; i < 5; i++) + wf_unregister_sensor(&pv->sensors[i]); + + kref_put(&pv->ref, wf_ad7417_release); + + return 0; +} + +static const struct i2c_device_id wf_ad7417_id[] = { + { "MAC,ad7417", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wf_ad7417_id); + +static struct i2c_driver wf_ad7417_driver = { + .driver = { + .name = "wf_ad7417", + }, + .probe = wf_ad7417_probe, + .remove = wf_ad7417_remove, + .id_table = wf_ad7417_id, +}; + +static int __devinit wf_ad7417_init(void) +{ + /* This is only supported on these machines */ + if (!of_machine_is_compatible("PowerMac7,2") && + !of_machine_is_compatible("PowerMac7,3") && + !of_machine_is_compatible("RackMac3,1")) + return -ENODEV; + + return i2c_add_driver(&wf_ad7417_driver); +} + +static void __devexit wf_ad7417_exit(void) +{ + i2c_del_driver(&wf_ad7417_driver); +} + +module_init(wf_ad7417_init); +module_exit(wf_ad7417_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index ce8897933a8..3ee198b6584 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -164,13 +164,27 @@ static ssize_t wf_show_control(struct device *dev, struct device_attribute *attr, char *buf) { struct wf_control *ctrl = container_of(attr, struct wf_control, attr); + const char *typestr; s32 val = 0; int err; err = ctrl->ops->get_value(ctrl, &val); - if (err < 0) + if (err < 0) { + if (err == -EFAULT) + return sprintf(buf, "<HW FAULT>\n"); return err; - return sprintf(buf, "%d\n", val); + } + switch(ctrl->type) { + case WF_CONTROL_RPM_FAN: + typestr = " RPM"; + break; + case WF_CONTROL_PWM_FAN: + typestr = " %"; + break; + default: + typestr = ""; + } + return sprintf(buf, "%d%s\n", val, typestr); } /* This is really only for debugging... */ @@ -470,11 +484,6 @@ static int __init windfarm_core_init(void) { DBG("wf: core loaded\n"); - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; platform_device_register(&wf_platform_device); return 0; } diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c index 1a77a7c97d0..72d1fdfe02a 100644 --- a/drivers/macintosh/windfarm_cpufreq_clamp.c +++ b/drivers/macintosh/windfarm_cpufreq_clamp.c @@ -75,12 +75,6 @@ static int __init wf_cpufreq_clamp_init(void) { struct wf_control *clamp; - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; - clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); if (clamp == NULL) return -ENOMEM; diff --git a/drivers/macintosh/windfarm_fcu_controls.c b/drivers/macintosh/windfarm_fcu_controls.c new file mode 100644 index 00000000000..b3411edb324 --- /dev/null +++ b/drivers/macintosh/windfarm_fcu_controls.c @@ -0,0 +1,613 @@ +/* + * Windfarm PowerMac thermal control. FCU fan control + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + */ +#undef DEBUG + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/i2c.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/sections.h> + +#include "windfarm.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +/* + * This option is "weird" :) Basically, if you define this to 1 + * the control loop for the RPMs fans (not PWMs) will apply the + * correction factor obtained from the PID to the actual RPM + * speed read from the FCU. + * + * If you define the below constant to 0, then it will be + * applied to the setpoint RPM speed, that is basically the + * speed we proviously "asked" for. + * + * I'm using 0 for now which is what therm_pm72 used to do and + * what Darwin -apparently- does based on observed behaviour. + */ +#define RPM_PID_USE_ACTUAL_SPEED 0 + +/* Default min/max for pumps */ +#define CPU_PUMP_OUTPUT_MAX 3200 +#define CPU_PUMP_OUTPUT_MIN 1250 + +#define FCU_FAN_RPM 0 +#define FCU_FAN_PWM 1 + +struct wf_fcu_priv { + struct kref ref; + struct i2c_client *i2c; + struct mutex lock; + struct list_head fan_list; + int rpm_shift; +}; + +struct wf_fcu_fan { + struct list_head link; + int id; + s32 min, max, target; + struct wf_fcu_priv *fcu_priv; + struct wf_control ctrl; +}; + +static void wf_fcu_release(struct kref *ref) +{ + struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref); + + kfree(pv); +} + +static void wf_fcu_fan_release(struct wf_control *ct) +{ + struct wf_fcu_fan *fan = ct->priv; + + kref_put(&fan->fcu_priv->ref, wf_fcu_release); + kfree(fan); +} + +static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg, + unsigned char *buf, int nb) +{ + int tries, nr, nw; + + mutex_lock(&pv->lock); + + buf[0] = reg; + tries = 0; + for (;;) { + nw = i2c_master_send(pv->i2c, buf, 1); + if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) + break; + msleep(10); + ++tries; + } + if (nw <= 0) { + pr_err("Failure writing address to FCU: %d", nw); + nr = nw; + goto bail; + } + tries = 0; + for (;;) { + nr = i2c_master_recv(pv->i2c, buf, nb); + if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100) + break; + msleep(10); + ++tries; + } + if (nr <= 0) + pr_err("wf_fcu: Failure reading data from FCU: %d", nw); + bail: + mutex_unlock(&pv->lock); + return nr; +} + +static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg, + const unsigned char *ptr, int nb) +{ + int tries, nw; + unsigned char buf[16]; + + buf[0] = reg; + memcpy(buf+1, ptr, nb); + ++nb; + tries = 0; + for (;;) { + nw = i2c_master_send(pv->i2c, buf, nb); + if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) + break; + msleep(10); + ++tries; + } + if (nw < 0) + pr_err("wf_fcu: Failure writing to FCU: %d", nw); + return nw; +} + +static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + int rc, shift = pv->rpm_shift; + unsigned char buf[2]; + + if (value < fan->min) + value = fan->min; + if (value > fan->max) + value = fan->max; + + fan->target = value; + + buf[0] = value >> (8 - shift); + buf[1] = value << shift; + rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2); + if (rc < 0) + return -EIO; + return 0; +} + +static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + int rc, reg_base, shift = pv->rpm_shift; + unsigned char failure; + unsigned char active; + unsigned char buf[2]; + + rc = wf_fcu_read_reg(pv, 0xb, &failure, 1); + if (rc != 1) + return -EIO; + if ((failure & (1 << fan->id)) != 0) + return -EFAULT; + rc = wf_fcu_read_reg(pv, 0xd, &active, 1); + if (rc != 1) + return -EIO; + if ((active & (1 << fan->id)) == 0) + return -ENXIO; + + /* Programmed value or real current speed */ +#if RPM_PID_USE_ACTUAL_SPEED + reg_base = 0x11; +#else + reg_base = 0x10; +#endif + rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2); + if (rc != 2) + return -EIO; + + *value = (buf[0] << (8 - shift)) | buf[1] >> shift; + + return 0; +} + +static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + unsigned char buf[2]; + int rc; + + if (value < fan->min) + value = fan->min; + if (value > fan->max) + value = fan->max; + + fan->target = value; + + value = (value * 2559) / 1000; + buf[0] = value; + rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1); + if (rc < 0) + return -EIO; + return 0; +} + +static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + unsigned char failure; + unsigned char active; + unsigned char buf[2]; + int rc; + + rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1); + if (rc != 1) + return -EIO; + if ((failure & (1 << fan->id)) != 0) + return -EFAULT; + rc = wf_fcu_read_reg(pv, 0x2d, &active, 1); + if (rc != 1) + return -EIO; + if ((active & (1 << fan->id)) == 0) + return -ENXIO; + + rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1); + if (rc != 1) + return -EIO; + + *value = (((s32)buf[0]) * 1000) / 2559; + + return 0; +} + +static s32 wf_fcu_fan_min(struct wf_control *ct) +{ + struct wf_fcu_fan *fan = ct->priv; + + return fan->min; +} + +static s32 wf_fcu_fan_max(struct wf_control *ct) +{ + struct wf_fcu_fan *fan = ct->priv; + + return fan->max; +} + +static const struct wf_control_ops wf_fcu_fan_rpm_ops = { + .set_value = wf_fcu_fan_set_rpm, + .get_value = wf_fcu_fan_get_rpm, + .get_min = wf_fcu_fan_min, + .get_max = wf_fcu_fan_max, + .release = wf_fcu_fan_release, + .owner = THIS_MODULE, +}; + +static const struct wf_control_ops wf_fcu_fan_pwm_ops = { + .set_value = wf_fcu_fan_set_pwm, + .get_value = wf_fcu_fan_get_pwm, + .get_min = wf_fcu_fan_min, + .get_max = wf_fcu_fan_max, + .release = wf_fcu_fan_release, + .owner = THIS_MODULE, +}; + +static void __devinit wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan) +{ + const struct mpu_data *mpu = wf_get_mpu(0); + u16 pump_min = 0, pump_max = 0xffff; + u16 tmp[4]; + + /* Try to fetch pumps min/max infos from eeprom */ + if (mpu) { + memcpy(&tmp, mpu->processor_part_num, 8); + if (tmp[0] != 0xffff && tmp[1] != 0xffff) { + pump_min = max(pump_min, tmp[0]); + pump_max = min(pump_max, tmp[1]); + } + if (tmp[2] != 0xffff && tmp[3] != 0xffff) { + pump_min = max(pump_min, tmp[2]); + pump_max = min(pump_max, tmp[3]); + } + } + + /* Double check the values, this _IS_ needed as the EEPROM on + * some dual 2.5Ghz G5s seem, at least, to have both min & max + * same to the same value ... (grrrr) + */ + if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) { + pump_min = CPU_PUMP_OUTPUT_MIN; + pump_max = CPU_PUMP_OUTPUT_MAX; + } + + fan->min = pump_min; + fan->max = pump_max; + + DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n", + fan->ctrl.name, pump_min, pump_max); +} + +static void __devinit wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan) +{ + struct wf_fcu_priv *pv = fan->fcu_priv; + const struct mpu_data *mpu0 = wf_get_mpu(0); + const struct mpu_data *mpu1 = wf_get_mpu(1); + + /* Default */ + fan->min = 2400 >> pv->rpm_shift; + fan->max = 56000 >> pv->rpm_shift; + + /* CPU fans have min/max in MPU */ + if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) { + fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); + fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); + goto bail; + } + if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) { + fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan); + fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan); + goto bail; + } + if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) { + fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan); + fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan); + goto bail; + } + if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) { + fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan); + fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan); + goto bail; + } + /* Rackmac variants, we just use mpu0 intake */ + if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) { + fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); + fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); + goto bail; + } + bail: + DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n", + fan->ctrl.name, fan->min, fan->max); +} + +static void __devinit wf_fcu_add_fan(struct wf_fcu_priv *pv, + const char *name, + int type, int id) +{ + struct wf_fcu_fan *fan; + + fan = kzalloc(sizeof(*fan), GFP_KERNEL); + if (!fan) + return; + fan->fcu_priv = pv; + fan->id = id; + fan->ctrl.name = name; + fan->ctrl.priv = fan; + + /* min/max is oddball but the code comes from + * therm_pm72 which seems to work so ... + */ + if (type == FCU_FAN_RPM) { + if (!strncmp(name, "cpu-pump", strlen("cpu-pump"))) + wf_fcu_get_pump_minmax(fan); + else + wf_fcu_get_rpmfan_minmax(fan); + fan->ctrl.type = WF_CONTROL_RPM_FAN; + fan->ctrl.ops = &wf_fcu_fan_rpm_ops; + } else { + fan->min = 10; + fan->max = 100; + fan->ctrl.type = WF_CONTROL_PWM_FAN; + fan->ctrl.ops = &wf_fcu_fan_pwm_ops; + } + + if (wf_register_control(&fan->ctrl)) { + pr_err("wf_fcu: Failed to register fan %s\n", name); + kfree(fan); + return; + } + list_add(&fan->link, &pv->fan_list); + kref_get(&pv->ref); +} + +static void __devinit wf_fcu_lookup_fans(struct wf_fcu_priv *pv) +{ + /* Translation of device-tree location properties to + * windfarm fan names + */ + static const struct { + const char *dt_name; /* Device-tree name */ + const char *ct_name; /* Control name */ + } loc_trans[] = { + { "BACKSIDE", "backside-fan", }, + { "SYS CTRLR FAN", "backside-fan", }, + { "DRIVE BAY", "drive-bay-fan", }, + { "SLOT", "slots-fan", }, + { "PCI FAN", "slots-fan", }, + { "CPU A INTAKE", "cpu-front-fan-0", }, + { "CPU A EXHAUST", "cpu-rear-fan-0", }, + { "CPU B INTAKE", "cpu-front-fan-1", }, + { "CPU B EXHAUST", "cpu-rear-fan-1", }, + { "CPU A PUMP", "cpu-pump-0", }, + { "CPU B PUMP", "cpu-pump-1", }, + { "CPU A 1", "cpu-fan-a-0", }, + { "CPU A 2", "cpu-fan-b-0", }, + { "CPU A 3", "cpu-fan-c-0", }, + { "CPU B 1", "cpu-fan-a-1", }, + { "CPU B 2", "cpu-fan-b-1", }, + { "CPU B 3", "cpu-fan-c-1", }, + }; + struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node; + int i; + + DBG("Looking up FCU controls in device-tree...\n"); + + while ((np = of_get_next_child(fcu, np)) != NULL) { + int id, type = -1; + const char *loc; + const char *name; + const u32 *reg; + + DBG(" control: %s, type: %s\n", np->name, np->type); + + /* Detect control type */ + if (!strcmp(np->type, "fan-rpm-control") || + !strcmp(np->type, "fan-rpm")) + type = FCU_FAN_RPM; + if (!strcmp(np->type, "fan-pwm-control") || + !strcmp(np->type, "fan-pwm")) + type = FCU_FAN_PWM; + /* Only care about fans for now */ + if (type == -1) + continue; + + /* Lookup for a matching location */ + loc = of_get_property(np, "location", NULL); + reg = of_get_property(np, "reg", NULL); + if (loc == NULL || reg == NULL) + continue; + DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); + + for (i = 0; i < ARRAY_SIZE(loc_trans); i++) { + if (strncmp(loc, loc_trans[i].dt_name, + strlen(loc_trans[i].dt_name))) + continue; + name = loc_trans[i].ct_name; + + DBG(" location match, name: %s\n", name); + + if (type == FCU_FAN_RPM) + id = ((*reg) - 0x10) / 2; + else + id = ((*reg) - 0x30) / 2; + if (id > 7) { + pr_warning("wf_fcu: Can't parse " + "fan ID in device-tree for %s\n", + np->full_name); + break; + } + wf_fcu_add_fan(pv, name, type, id); + break; + } + } +} + +static void __devinit wf_fcu_default_fans(struct wf_fcu_priv *pv) +{ + /* We only support the default fans for PowerMac7,2 */ + if (!of_machine_is_compatible("PowerMac7,2")) + return; + + wf_fcu_add_fan(pv, "backside-fan", FCU_FAN_PWM, 1); + wf_fcu_add_fan(pv, "drive-bay-fan", FCU_FAN_RPM, 2); + wf_fcu_add_fan(pv, "slots-fan", FCU_FAN_PWM, 2); + wf_fcu_add_fan(pv, "cpu-front-fan-0", FCU_FAN_RPM, 3); + wf_fcu_add_fan(pv, "cpu-rear-fan-0", FCU_FAN_RPM, 4); + wf_fcu_add_fan(pv, "cpu-front-fan-1", FCU_FAN_RPM, 5); + wf_fcu_add_fan(pv, "cpu-rear-fan-1", FCU_FAN_RPM, 6); +} + +static int __devinit wf_fcu_init_chip(struct wf_fcu_priv *pv) +{ + unsigned char buf = 0xff; + int rc; + + rc = wf_fcu_write_reg(pv, 0xe, &buf, 1); + if (rc < 0) + return -EIO; + rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1); + if (rc < 0) + return -EIO; + rc = wf_fcu_read_reg(pv, 0, &buf, 1); + if (rc < 0) + return -EIO; + pv->rpm_shift = (buf == 1) ? 2 : 3; + + pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n", + pv->rpm_shift); + + return 0; +} + +static int __devinit wf_fcu_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wf_fcu_priv *pv; + + pv = kzalloc(sizeof(*pv), GFP_KERNEL); + if (!pv) + return -ENOMEM; + + kref_init(&pv->ref); + mutex_init(&pv->lock); + INIT_LIST_HEAD(&pv->fan_list); + pv->i2c = client; + + /* + * First we must start the FCU which will query the + * shift value to apply to RPMs + */ + if (wf_fcu_init_chip(pv)) { + pr_err("wf_fcu: Initialization failed !\n"); + kfree(pv); + return -ENXIO; + } + + /* First lookup fans in the device-tree */ + wf_fcu_lookup_fans(pv); + + /* + * Older machines don't have the device-tree entries + * we are looking for, just hard code the list + */ + if (list_empty(&pv->fan_list)) + wf_fcu_default_fans(pv); + + /* Still no fans ? FAIL */ + if (list_empty(&pv->fan_list)) { + pr_err("wf_fcu: Failed to find fans for your machine\n"); + kfree(pv); + return -ENODEV; + } + + dev_set_drvdata(&client->dev, pv); + + return 0; +} + +static int __devexit wf_fcu_remove(struct i2c_client *client) +{ + struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev); + struct wf_fcu_fan *fan; + + while (!list_empty(&pv->fan_list)) { + fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link); + list_del(&fan->link); + wf_unregister_control(&fan->ctrl); + } + kref_put(&pv->ref, wf_fcu_release); + return 0; +} + +static const struct i2c_device_id wf_fcu_id[] = { + { "MAC,fcu", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wf_fcu_id); + +static struct i2c_driver wf_fcu_driver = { + .driver = { + .name = "wf_fcu", + }, + .probe = wf_fcu_probe, + .remove = wf_fcu_remove, + .id_table = wf_fcu_id, +}; + +static int __init wf_fcu_init(void) +{ + return i2c_add_driver(&wf_fcu_driver); +} + +static void __exit wf_fcu_exit(void) +{ + i2c_del_driver(&wf_fcu_driver); +} + + +module_init(wf_fcu_init); +module_exit(wf_fcu_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 4d6a90a1372..b0c2d3695b3 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -23,7 +23,7 @@ #include "windfarm.h" -#define VERSION "0.2" +#define VERSION "1.0" #undef DEBUG @@ -36,8 +36,8 @@ struct wf_lm75_sensor { int ds1775 : 1; int inited : 1; - struct i2c_client *i2c; - struct wf_sensor sens; + struct i2c_client *i2c; + struct wf_sensor sens; }; #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) @@ -90,40 +90,19 @@ static struct wf_sensor_ops wf_lm75_ops = { static int wf_lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ +{ struct wf_lm75_sensor *lm; - int rc; - - lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); - if (lm == NULL) - return -ENODEV; - - lm->inited = 0; - lm->ds1775 = id->driver_data; - lm->i2c = client; - lm->sens.name = client->dev.platform_data; - lm->sens.ops = &wf_lm75_ops; - i2c_set_clientdata(client, lm); - - rc = wf_register_sensor(&lm->sens); - if (rc) - kfree(lm); - - return rc; -} - -static struct i2c_driver wf_lm75_driver; - -static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, - u8 addr, int ds1775, - const char *loc) -{ - struct i2c_board_info info; - struct i2c_client *client; - char *name; + int rc, ds1775 = id->driver_data; + const char *name, *loc; DBG("wf_lm75: creating %s device at address 0x%02x\n", - ds1775 ? "ds1775" : "lm75", addr); + ds1775 ? "ds1775" : "lm75", client->addr); + + loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); + if (!loc) { + dev_warn(&client->dev, "Missing hwsensor-location property!\n"); + return -ENXIO; + } /* Usual rant about sensor names not beeing very consistent in * the device-tree, oh well ... @@ -137,68 +116,31 @@ static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, name = "optical-drive-temp"; else if (!strcmp(loc, "HD Temp")) name = "hard-drive-temp"; + else if (!strcmp(loc, "PCI SLOTS")) + name = "slots-temp"; + else if (!strcmp(loc, "CPU A INLET")) + name = "cpu-inlet-temp-0"; + else if (!strcmp(loc, "CPU B INLET")) + name = "cpu-inlet-temp-1"; else - goto fail; - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = (addr >> 1) & 0x7f; - info.platform_data = name; - strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE); - - client = i2c_new_device(adapter, &info); - if (client == NULL) { - printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n", - ds1775 ? "ds1775" : "lm75", name); - goto fail; - } - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &wf_lm75_driver.clients); - return client; - fail: - return NULL; -} - -static int wf_lm75_attach(struct i2c_adapter *adapter) -{ - struct device_node *busnode, *dev; - struct pmac_i2c_bus *bus; + return -ENXIO; + - DBG("wf_lm75: adapter %s detected\n", adapter->name); - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) + lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); + if (lm == NULL) return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - DBG("wf_lm75: bus found, looking for device...\n"); - - /* Now look for lm75(s) in there */ - for (dev = NULL; - (dev = of_get_next_child(busnode, dev)) != NULL;) { - const char *loc = - of_get_property(dev, "hwsensor-location", NULL); - u8 addr; + lm->inited = 0; + lm->ds1775 = ds1775; + lm->i2c = client; + lm->sens.name = (char *)name; /* XXX fix constness in structure */ + lm->sens.ops = &wf_lm75_ops; + i2c_set_clientdata(client, lm); - /* We must re-match the adapter in order to properly check - * the channel on multibus setups - */ - if (!pmac_i2c_match_adapter(dev, adapter)) - continue; - addr = pmac_i2c_get_dev_addr(dev); - if (loc == NULL || addr == 0) - continue; - /* real lm75 */ - if (of_device_is_compatible(dev, "lm75")) - wf_lm75_create(adapter, addr, 0, loc); - /* ds1775 (compatible, better resolution */ - else if (of_device_is_compatible(dev, "ds1775")) - wf_lm75_create(adapter, addr, 1, loc); - } - return 0; + rc = wf_register_sensor(&lm->sens); + if (rc) + kfree(lm); + return rc; } static int wf_lm75_remove(struct i2c_client *client) @@ -217,16 +159,16 @@ static int wf_lm75_remove(struct i2c_client *client) } static const struct i2c_device_id wf_lm75_id[] = { - { "wf_lm75", 0 }, - { "wf_ds1775", 1 }, + { "MAC,lm75", 0 }, + { "MAC,ds1775", 1 }, { } }; +MODULE_DEVICE_TABLE(i2c, wf_lm75_id); static struct i2c_driver wf_lm75_driver = { .driver = { .name = "wf_lm75", }, - .attach_adapter = wf_lm75_attach, .probe = wf_lm75_probe, .remove = wf_lm75_remove, .id_table = wf_lm75_id, @@ -234,11 +176,6 @@ static struct i2c_driver wf_lm75_driver = { static int __init wf_lm75_sensor_init(void) { - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; return i2c_add_driver(&wf_lm75_driver); } diff --git a/drivers/macintosh/windfarm_lm87_sensor.c b/drivers/macintosh/windfarm_lm87_sensor.c new file mode 100644 index 00000000000..c071aab79dd --- /dev/null +++ b/drivers/macintosh/windfarm_lm87_sensor.c @@ -0,0 +1,201 @@ +/* + * Windfarm PowerMac thermal control. LM87 sensor + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + * + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/i2c.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/sections.h> +#include <asm/pmac_low_i2c.h> + +#include "windfarm.h" + +#define VERSION "1.0" + +#undef DEBUG + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +struct wf_lm87_sensor { + struct i2c_client *i2c; + struct wf_sensor sens; +}; +#define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens) + + +static int wf_lm87_read_reg(struct i2c_client *chip, int reg) +{ + int rc, tries = 0; + u8 buf; + + for (;;) { + /* Set address */ + buf = (u8)reg; + rc = i2c_master_send(chip, &buf, 1); + if (rc <= 0) + goto error; + rc = i2c_master_recv(chip, &buf, 1); + if (rc <= 0) + goto error; + return (int)buf; + error: + DBG("wf_lm87: Error reading LM87, retrying...\n"); + if (++tries > 10) { + printk(KERN_ERR "wf_lm87: Error reading LM87 !\n"); + return -EIO; + } + msleep(10); + } +} + +static int wf_lm87_get(struct wf_sensor *sr, s32 *value) +{ + struct wf_lm87_sensor *lm = sr->priv; + s32 temp; + + if (lm->i2c == NULL) + return -ENODEV; + +#define LM87_INT_TEMP 0x27 + + /* Read temperature register */ + temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP); + if (temp < 0) + return temp; + *value = temp << 16; + + return 0; +} + +static void wf_lm87_release(struct wf_sensor *sr) +{ + struct wf_lm87_sensor *lm = wf_to_lm87(sr); + + kfree(lm); +} + +static struct wf_sensor_ops wf_lm87_ops = { + .get_value = wf_lm87_get, + .release = wf_lm87_release, + .owner = THIS_MODULE, +}; + +static int wf_lm87_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wf_lm87_sensor *lm; + const char *name = NULL, *loc; + struct device_node *np = NULL; + int rc; + + /* + * The lm87 contains a whole pile of sensors, additionally, + * the Xserve G5 has several lm87's. However, for now we only + * care about the internal temperature sensor + */ + while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) { + if (strcmp(np->name, "int-temp")) + continue; + loc = of_get_property(np, "location", NULL); + if (!loc) + continue; + if (strstr(loc, "DIMM")) + name = "dimms-temp"; + else if (strstr(loc, "Processors")) + name = "between-cpus-temp"; + if (name) { + of_node_put(np); + break; + } + } + if (!name) { + pr_warning("wf_lm87: Unsupported sensor %s\n", + client->dev.of_node->full_name); + return -ENODEV; + } + + lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL); + if (lm == NULL) + return -ENODEV; + + lm->i2c = client; + lm->sens.name = name; + lm->sens.ops = &wf_lm87_ops; + lm->sens.priv = lm; + i2c_set_clientdata(client, lm); + + rc = wf_register_sensor(&lm->sens); + if (rc) + kfree(lm); + return rc; +} + +static int wf_lm87_remove(struct i2c_client *client) +{ + struct wf_lm87_sensor *lm = i2c_get_clientdata(client); + + DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name); + + /* Mark client detached */ + lm->i2c = NULL; + + /* release sensor */ + wf_unregister_sensor(&lm->sens); + + return 0; +} + +static const struct i2c_device_id wf_lm87_id[] = { + { "MAC,lm87cimt", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wf_lm87_id); + +static struct i2c_driver wf_lm87_driver = { + .driver = { + .name = "wf_lm87", + }, + .probe = wf_lm87_probe, + .remove = wf_lm87_remove, + .id_table = wf_lm87_id, +}; + +static int __init wf_lm87_sensor_init(void) +{ + /* We only support this on the Xserve */ + if (!of_machine_is_compatible("RackMac3,1")) + return -ENODEV; + + return i2c_add_driver(&wf_lm87_driver); +} + +static void __exit wf_lm87_sensor_exit(void) +{ + i2c_del_driver(&wf_lm87_driver); +} + + +module_init(wf_lm87_sensor_init); +module_exit(wf_lm87_sensor_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index 8204113268f..371b058d2f7 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -16,7 +16,7 @@ #include "windfarm.h" -#define VERSION "0.2" +#define VERSION "1.0" /* This currently only exports the external temperature sensor, since that's all the control loops need. */ @@ -64,9 +64,29 @@ static struct wf_sensor_ops wf_max6690_ops = { static int wf_max6690_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const char *name, *loc; struct wf_6690_sensor *max; int rc; + loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); + if (!loc) { + dev_warn(&client->dev, "Missing hwsensor-location property!\n"); + return -ENXIO; + } + + /* + * We only expose the external temperature register for + * now as this is all we need for our control loops + */ + if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT")) + name = "backside-temp"; + else if (!strcmp(loc, "NB Ambient")) + name = "north-bridge-temp"; + else if (!strcmp(loc, "GPU Ambient")) + name = "gpu-temp"; + else + return -ENXIO; + max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); if (max == NULL) { printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " @@ -75,90 +95,16 @@ static int wf_max6690_probe(struct i2c_client *client, } max->i2c = client; - max->sens.name = client->dev.platform_data; + max->sens.name = (char *)name; /* XXX fix constness in structure */ max->sens.ops = &wf_max6690_ops; i2c_set_clientdata(client, max); rc = wf_register_sensor(&max->sens); - if (rc) { + if (rc) kfree(max); - } - return rc; } -static struct i2c_driver wf_max6690_driver; - -static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, - u8 addr, const char *loc) -{ - struct i2c_board_info info; - struct i2c_client *client; - char *name; - - if (!strcmp(loc, "BACKSIDE")) - name = "backside-temp"; - else if (!strcmp(loc, "NB Ambient")) - name = "north-bridge-temp"; - else if (!strcmp(loc, "GPU Ambient")) - name = "gpu-temp"; - else - goto fail; - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = addr >> 1; - info.platform_data = name; - strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE); - - client = i2c_new_device(adapter, &info); - if (client == NULL) { - printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n"); - goto fail; - } - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &wf_max6690_driver.clients); - return client; - - fail: - return NULL; -} - -static int wf_max6690_attach(struct i2c_adapter *adapter) -{ - struct device_node *busnode, *dev = NULL; - struct pmac_i2c_bus *bus; - const char *loc; - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) - return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - while ((dev = of_get_next_child(busnode, dev)) != NULL) { - u8 addr; - - /* We must re-match the adapter in order to properly check - * the channel on multibus setups - */ - if (!pmac_i2c_match_adapter(dev, adapter)) - continue; - if (!of_device_is_compatible(dev, "max6690")) - continue; - addr = pmac_i2c_get_dev_addr(dev); - loc = of_get_property(dev, "hwsensor-location", NULL); - if (loc == NULL || addr == 0) - continue; - printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); - wf_max6690_create(adapter, addr, loc); - } - - return 0; -} - static int wf_max6690_remove(struct i2c_client *client) { struct wf_6690_sensor *max = i2c_get_clientdata(client); @@ -170,15 +116,15 @@ static int wf_max6690_remove(struct i2c_client *client) } static const struct i2c_device_id wf_max6690_id[] = { - { "wf_max6690", 0 }, + { "MAC,max6690", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, wf_max6690_id); static struct i2c_driver wf_max6690_driver = { .driver = { .name = "wf_max6690", }, - .attach_adapter = wf_max6690_attach, .probe = wf_max6690_probe, .remove = wf_max6690_remove, .id_table = wf_max6690_id, @@ -186,11 +132,6 @@ static struct i2c_driver wf_max6690_driver = { static int __init wf_max6690_sensor_init(void) { - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; return i2c_add_driver(&wf_max6690_driver); } diff --git a/drivers/macintosh/windfarm_mpu.h b/drivers/macintosh/windfarm_mpu.h new file mode 100644 index 00000000000..046edc8c2ec --- /dev/null +++ b/drivers/macintosh/windfarm_mpu.h @@ -0,0 +1,105 @@ +/* + * Windfarm PowerMac thermal control + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + */ + +#ifndef __WINDFARM_MPU_H +#define __WINDFARM_MPU_H + +typedef unsigned short fu16; +typedef int fs32; +typedef short fs16; + +/* Definition of the MPU data structure which contains per CPU + * calibration information (among others) for the G5 machines + */ +struct mpu_data +{ + u8 signature; /* 0x00 - EEPROM sig. */ + u8 bytes_used; /* 0x01 - Bytes used in eeprom (160 ?) */ + u8 size; /* 0x02 - EEPROM size (256 ?) */ + u8 version; /* 0x03 - EEPROM version */ + u32 data_revision; /* 0x04 - Dataset revision */ + u8 processor_bin_code[3]; /* 0x08 - Processor BIN code */ + u8 bin_code_expansion; /* 0x0b - ??? (padding ?) */ + u8 processor_num; /* 0x0c - Number of CPUs on this MPU */ + u8 input_mul_bus_div; /* 0x0d - Clock input multiplier/bus divider */ + u8 reserved1[2]; /* 0x0e - */ + u32 input_clk_freq_high; /* 0x10 - Input clock frequency high */ + u8 cpu_nb_target_cycles; /* 0x14 - ??? */ + u8 cpu_statlat; /* 0x15 - ??? */ + u8 cpu_snooplat; /* 0x16 - ??? */ + u8 cpu_snoopacc; /* 0x17 - ??? */ + u8 nb_paamwin; /* 0x18 - ??? */ + u8 nb_statlat; /* 0x19 - ??? */ + u8 nb_snooplat; /* 0x1a - ??? */ + u8 nb_snoopwin; /* 0x1b - ??? */ + u8 api_bus_mode; /* 0x1c - ??? */ + u8 reserved2[3]; /* 0x1d - */ + u32 input_clk_freq_low; /* 0x20 - Input clock frequency low */ + u8 processor_card_slot; /* 0x24 - Processor card slot number */ + u8 reserved3[2]; /* 0x25 - */ + u8 padjmax; /* 0x27 - Max power adjustment (Not in OF!) */ + u8 ttarget; /* 0x28 - Target temperature */ + u8 tmax; /* 0x29 - Max temperature */ + u8 pmaxh; /* 0x2a - Max power */ + u8 tguardband; /* 0x2b - Guardband temp ??? Hist. len in OSX */ + fs32 pid_gp; /* 0x2c - PID proportional gain */ + fs32 pid_gr; /* 0x30 - PID reset gain */ + fs32 pid_gd; /* 0x34 - PID derivative gain */ + fu16 voph; /* 0x38 - Vop High */ + fu16 vopl; /* 0x3a - Vop Low */ + fs16 nactual_die; /* 0x3c - nActual Die */ + fs16 nactual_heatsink; /* 0x3e - nActual Heatsink */ + fs16 nactual_system; /* 0x40 - nActual System */ + u16 calibration_flags; /* 0x42 - Calibration flags */ + fu16 mdiode; /* 0x44 - Diode M value (scaling factor) */ + fs16 bdiode; /* 0x46 - Diode B value (offset) */ + fs32 theta_heat_sink; /* 0x48 - Theta heat sink */ + u16 rminn_intake_fan; /* 0x4c - Intake fan min RPM */ + u16 rmaxn_intake_fan; /* 0x4e - Intake fan max RPM */ + u16 rminn_exhaust_fan; /* 0x50 - Exhaust fan min RPM */ + u16 rmaxn_exhaust_fan; /* 0x52 - Exhaust fan max RPM */ + u8 processor_part_num[8]; /* 0x54 - Processor part number XX pumps min/max */ + u32 processor_lot_num; /* 0x5c - Processor lot number */ + u8 orig_card_sernum[0x10]; /* 0x60 - Card original serial number */ + u8 curr_card_sernum[0x10]; /* 0x70 - Card current serial number */ + u8 mlb_sernum[0x18]; /* 0x80 - MLB serial number */ + u32 checksum1; /* 0x98 - */ + u32 checksum2; /* 0x9c - */ +}; /* Total size = 0xa0 */ + +static inline const struct mpu_data *wf_get_mpu(int cpu) +{ + struct device_node *np; + char nodename[64]; + const void *data; + int len; + + /* + * prom.c routine for finding a node by path is a bit brain dead + * and requires exact @xxx unit numbers. This is a bit ugly but + * will work for these machines + */ + sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0); + np = of_find_node_by_path(nodename); + if (!np) + return NULL; + data = of_get_property(np, "cpuid", &len); + of_node_put(np); + if (!data) + return NULL; + + /* + * We are naughty, we have dropped the reference to the device + * node and still return a pointer to the content. We know we + * can do that though as this is only ever called on PowerMac + * which cannot remove those nodes + */ + return data; +} + +#endif /* __WINDFARM_MPU_H */ diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c new file mode 100644 index 00000000000..84ac913d7e3 --- /dev/null +++ b/drivers/macintosh/windfarm_pm72.c @@ -0,0 +1,847 @@ +/* + * Windfarm PowerMac thermal control. + * Control loops for PowerMac7,2 and 7,3 + * + * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Use and redistribute under the terms of the GNU GPL v2. + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <asm/prom.h> +#include <asm/smu.h> + +#include "windfarm.h" +#include "windfarm_pid.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +#undef DEBUG +#undef LOTSA_DEBUG + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +#ifdef LOTSA_DEBUG +#define DBG_LOTS(args...) printk(args) +#else +#define DBG_LOTS(args...) do { } while(0) +#endif + +/* define this to force CPU overtemp to 60 degree, useful for testing + * the overtemp code + */ +#undef HACKED_OVERTEMP + +/* We currently only handle 2 chips */ +#define NR_CHIPS 2 +#define NR_CPU_FANS 3 * NR_CHIPS + +/* Controls and sensors */ +static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; +static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; +static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; +static struct wf_sensor *backside_temp; +static struct wf_sensor *drives_temp; + +static struct wf_control *cpu_front_fans[NR_CHIPS]; +static struct wf_control *cpu_rear_fans[NR_CHIPS]; +static struct wf_control *cpu_pumps[NR_CHIPS]; +static struct wf_control *backside_fan; +static struct wf_control *drives_fan; +static struct wf_control *slots_fan; +static struct wf_control *cpufreq_clamp; + +/* We keep a temperature history for average calculation of 180s */ +#define CPU_TEMP_HIST_SIZE 180 + +/* Fixed speed for slot fan */ +#define SLOTS_FAN_DEFAULT_PWM 40 + +/* Scale value for CPU intake fans */ +#define CPU_INTAKE_SCALE 0x0000f852 + +/* PID loop state */ +static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; +static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; +static bool cpu_pid_combined; +static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; +static int cpu_thist_pt; +static s64 cpu_thist_total; +static s32 cpu_all_tmax = 100 << 16; +static struct wf_pid_state backside_pid; +static int backside_tick; +static struct wf_pid_state drives_pid; +static int drives_tick; + +static int nr_chips; +static bool have_all_controls; +static bool have_all_sensors; +static bool started; + +static int failure_state; +#define FAILURE_SENSOR 1 +#define FAILURE_FAN 2 +#define FAILURE_PERM 4 +#define FAILURE_LOW_OVERTEMP 8 +#define FAILURE_HIGH_OVERTEMP 16 + +/* Overtemp values */ +#define LOW_OVER_AVERAGE 0 +#define LOW_OVER_IMMEDIATE (10 << 16) +#define LOW_OVER_CLEAR ((-10) << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) +#define HIGH_OVER_AVERAGE (10 << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) + + +static void cpu_max_all_fans(void) +{ + int i; + + /* We max all CPU fans in case of a sensor error. We also do the + * cpufreq clamping now, even if it's supposedly done later by the + * generic code anyway, we do it earlier here to react faster + */ + if (cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + for (i = 0; i < nr_chips; i++) { + if (cpu_front_fans[i]) + wf_control_set_max(cpu_front_fans[i]); + if (cpu_rear_fans[i]) + wf_control_set_max(cpu_rear_fans[i]); + if (cpu_pumps[i]) + wf_control_set_max(cpu_pumps[i]); + } +} + +static int cpu_check_overtemp(s32 temp) +{ + int new_state = 0; + s32 t_avg, t_old; + static bool first = true; + + /* First check for immediate overtemps */ + if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" + " temperature !\n"); + } + if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " immediate CPU temperature !\n"); + } + + /* + * The first time around, initialize the array with the first + * temperature reading + */ + if (first) { + int i; + + cpu_thist_total = 0; + for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { + cpu_thist[i] = temp; + cpu_thist_total += temp; + } + first = false; + } + + /* + * We calculate a history of max temperatures and use that for the + * overtemp management + */ + t_old = cpu_thist[cpu_thist_pt]; + cpu_thist[cpu_thist_pt] = temp; + cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; + cpu_thist_total -= t_old; + cpu_thist_total += temp; + t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; + + DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", + FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); + + /* Now check for average overtemps */ + if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to average CPU" + " temperature !\n"); + } + if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " average CPU temperature !\n"); + } + + /* Now handle overtemp conditions. We don't currently use the windfarm + * overtemp handling core as it's not fully suited to the needs of those + * new machine. This will be fixed later. + */ + if (new_state) { + /* High overtemp -> immediate shutdown */ + if (new_state & FAILURE_HIGH_OVERTEMP) + machine_power_off(); + if ((failure_state & new_state) != new_state) + cpu_max_all_fans(); + failure_state |= new_state; + } else if ((failure_state & FAILURE_LOW_OVERTEMP) && + (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { + printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); + failure_state &= ~FAILURE_LOW_OVERTEMP; + } + + return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); +} + +static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) +{ + s32 dtemp, volts, amps; + int rc; + + /* Get diode temperature */ + rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); + if (rc) { + DBG(" CPU%d: temp reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); + *temp = dtemp; + + /* Get voltage */ + rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); + if (rc) { + DBG(" CPU%d, volts reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); + + /* Get current */ + rc = wf_sensor_get(sens_cpu_amps[cpu], &s); + if (rc) { + DBG(" CPU%d, current reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); + + /* Calculate power */ + + /* Scale voltage and current raw sensor values according to fixed scales + * obtained in Darwin and calculate power from I and V + */ + *power = (((u64)volts) * ((u64)amps)) >> 16; + + DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); + + return 0; + +} + +static void cpu_fans_tick_split(void) +{ + int err, cpu; + s32 intake, temp, power, t_max = 0; + + DBG_LOTS("* cpu fans_tick_split()\n"); + + for (cpu = 0; cpu < nr_chips; ++cpu) { + struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; + + /* Read current speed */ + wf_control_get(cpu_rear_fans[cpu], &sp->target); + + DBG_LOTS(" CPU%d: cur_target = %d RPM\n", cpu, sp->target); + + err = read_one_cpu_vals(cpu, &temp, &power); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + + /* Keep track of highest temp */ + t_max = max(t_max, temp); + + /* Handle possible overtemps */ + if (cpu_check_overtemp(t_max)) + return; + + /* Run PID */ + wf_cpu_pid_run(sp, power, temp); + + DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); + + /* Apply result directly to exhaust fan */ + err = wf_control_set(cpu_rear_fans[cpu], sp->target); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_rear_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + break; + } + + /* Scale result for intake fan */ + intake = (sp->target * CPU_INTAKE_SCALE) >> 16; + DBG_LOTS(" CPU%d: intake = %d RPM\n", cpu, intake); + err = wf_control_set(cpu_front_fans[cpu], intake); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_front_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + break; + } + } +} + +static void cpu_fans_tick_combined(void) +{ + s32 temp0, power0, temp1, power1, t_max = 0; + s32 temp, power, intake, pump; + struct wf_control *pump0, *pump1; + struct wf_cpu_pid_state *sp = &cpu_pid[0]; + int err, cpu; + + DBG_LOTS("* cpu fans_tick_combined()\n"); + + /* Read current speed from cpu 0 */ + wf_control_get(cpu_rear_fans[0], &sp->target); + + DBG_LOTS(" CPUs: cur_target = %d RPM\n", sp->target); + + /* Read values for both CPUs */ + err = read_one_cpu_vals(0, &temp0, &power0); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + err = read_one_cpu_vals(1, &temp1, &power1); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + + /* Keep track of highest temp */ + t_max = max(t_max, max(temp0, temp1)); + + /* Handle possible overtemps */ + if (cpu_check_overtemp(t_max)) + return; + + /* Use the max temp & power of both */ + temp = max(temp0, temp1); + power = max(power0, power1); + + /* Run PID */ + wf_cpu_pid_run(sp, power, temp); + + /* Scale result for intake fan */ + intake = (sp->target * CPU_INTAKE_SCALE) >> 16; + + /* Same deal with pump speed */ + pump0 = cpu_pumps[0]; + pump1 = cpu_pumps[1]; + if (!pump0) { + pump0 = pump1; + pump1 = NULL; + } + pump = (sp->target * wf_control_get_max(pump0)) / + cpu_mpu_data[0]->rmaxn_exhaust_fan; + + DBG_LOTS(" CPUs: target = %d RPM\n", sp->target); + DBG_LOTS(" CPUs: intake = %d RPM\n", intake); + DBG_LOTS(" CPUs: pump = %d RPM\n", pump); + + for (cpu = 0; cpu < nr_chips; cpu++) { + err = wf_control_set(cpu_rear_fans[cpu], sp->target); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_rear_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + } + err = wf_control_set(cpu_front_fans[cpu], intake); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_front_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + } + err = 0; + if (cpu_pumps[cpu]) + err = wf_control_set(cpu_pumps[cpu], pump); + if (err) { + pr_warning("wf_pm72: Pump %s reports error %d\n", + cpu_pumps[cpu]->name, err); + failure_state |= FAILURE_FAN; + } + } +} + +/* Implementation... */ +static int cpu_setup_pid(int cpu) +{ + struct wf_cpu_pid_param pid; + const struct mpu_data *mpu = cpu_mpu_data[cpu]; + s32 tmax, ttarget, ptarget; + int fmin, fmax, hsize; + + /* Get PID params from the appropriate MPU EEPROM */ + tmax = mpu->tmax << 16; + ttarget = mpu->ttarget << 16; + ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; + + DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", + cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); + + /* We keep a global tmax for overtemp calculations */ + if (tmax < cpu_all_tmax) + cpu_all_tmax = tmax; + + /* Set PID min/max by using the rear fan min/max */ + fmin = wf_control_get_min(cpu_rear_fans[cpu]); + fmax = wf_control_get_max(cpu_rear_fans[cpu]); + DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); + + /* History size */ + hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); + DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); + + /* Initialize PID loop */ + pid.interval = 1; /* seconds */ + pid.history_len = hsize; + pid.gd = mpu->pid_gd; + pid.gp = mpu->pid_gp; + pid.gr = mpu->pid_gr; + pid.tmax = tmax; + pid.ttarget = ttarget; + pid.pmaxadj = ptarget; + pid.min = fmin; + pid.max = fmax; + + wf_cpu_pid_init(&cpu_pid[cpu], &pid); + cpu_pid[cpu].target = 1000; + + return 0; +} + +/* Backside/U3 fan */ +static struct wf_pid_param backside_u3_param = { + .interval = 5, + .history_len = 2, + .gd = 40 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 65 << 16, + .additive = 1, + .min = 20, + .max = 100, +}; + +static struct wf_pid_param backside_u3h_param = { + .interval = 5, + .history_len = 2, + .gd = 20 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 75 << 16, + .additive = 1, + .min = 20, + .max = 100, +}; + +static void backside_fan_tick(void) +{ + s32 temp; + int speed; + int err; + + if (!backside_fan || !backside_temp || !backside_tick) + return; + if (--backside_tick > 0) + return; + backside_tick = backside_pid.param.interval; + + DBG_LOTS("* backside fans tick\n"); + + /* Update fan speed from actual fans */ + err = wf_control_get(backside_fan, &speed); + if (!err) + backside_pid.target = speed; + + err = wf_sensor_get(backside_temp, &temp); + if (err) { + printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n", + err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(backside_fan); + return; + } + speed = wf_pid_run(&backside_pid, temp); + + DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + err = wf_control_set(backside_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: backside fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void backside_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(backside_fan); + s32 fmax = wf_control_get_max(backside_fan); + struct wf_pid_param param; + struct device_node *u3; + int u3h = 1; /* conservative by default */ + + u3 = of_find_node_by_path("/u3@0,f8000000"); + if (u3 != NULL) { + const u32 *vers = of_get_property(u3, "device-rev", NULL); + if (vers) + if (((*vers) & 0x3f) < 0x34) + u3h = 0; + of_node_put(u3); + } + + param = u3h ? backside_u3h_param : backside_u3_param; + + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&backside_pid, ¶m); + backside_tick = 1; + + pr_info("wf_pm72: Backside control loop started.\n"); +} + +/* Drive bay fan */ +static const struct wf_pid_param drives_param = { + .interval = 5, + .history_len = 2, + .gd = 30 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 40 << 16, + .additive = 1, + .min = 300, + .max = 4000, +}; + +static void drives_fan_tick(void) +{ + s32 temp; + int speed; + int err; + + if (!drives_fan || !drives_temp || !drives_tick) + return; + if (--drives_tick > 0) + return; + drives_tick = drives_pid.param.interval; + + DBG_LOTS("* drives fans tick\n"); + + /* Update fan speed from actual fans */ + err = wf_control_get(drives_fan, &speed); + if (!err) + drives_pid.target = speed; + + err = wf_sensor_get(drives_temp, &temp); + if (err) { + pr_warning("wf_pm72: drive bay temp sensor error %d\n", err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(drives_fan); + return; + } + speed = wf_pid_run(&drives_pid, temp); + + DBG_LOTS("drives PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + err = wf_control_set(drives_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void drives_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(drives_fan); + s32 fmax = wf_control_get_max(drives_fan); + struct wf_pid_param param = drives_param; + + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&drives_pid, ¶m); + drives_tick = 1; + + pr_info("wf_pm72: Drive bay control loop started.\n"); +} + +static void set_fail_state(void) +{ + cpu_max_all_fans(); + + if (backside_fan) + wf_control_set_max(backside_fan); + if (slots_fan) + wf_control_set_max(slots_fan); + if (drives_fan) + wf_control_set_max(drives_fan); +} + +static void pm72_tick(void) +{ + int i, last_failure; + + if (!started) { + started = 1; + printk(KERN_INFO "windfarm: CPUs control loops started.\n"); + for (i = 0; i < nr_chips; ++i) { + if (cpu_setup_pid(i) < 0) { + failure_state = FAILURE_PERM; + set_fail_state(); + break; + } + } + DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); + + backside_setup_pid(); + drives_setup_pid(); + + /* + * We don't have the right stuff to drive the PCI fan + * so we fix it to a default value + */ + wf_control_set(slots_fan, SLOTS_FAN_DEFAULT_PWM); + +#ifdef HACKED_OVERTEMP + cpu_all_tmax = 60 << 16; +#endif + } + + /* Permanent failure, bail out */ + if (failure_state & FAILURE_PERM) + return; + + /* + * Clear all failure bits except low overtemp which will be eventually + * cleared by the control loop itself + */ + last_failure = failure_state; + failure_state &= FAILURE_LOW_OVERTEMP; + if (cpu_pid_combined) + cpu_fans_tick_combined(); + else + cpu_fans_tick_split(); + backside_fan_tick(); + drives_fan_tick(); + + DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", + last_failure, failure_state); + + /* Check for failures. Any failure causes cpufreq clamping */ + if (failure_state && last_failure == 0 && cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + if (failure_state == 0 && last_failure && cpufreq_clamp) + wf_control_set_min(cpufreq_clamp); + + /* That's it for now, we might want to deal with other failures + * differently in the future though + */ +} + +static void pm72_new_control(struct wf_control *ct) +{ + bool all_controls; + bool had_pump = cpu_pumps[0] || cpu_pumps[1]; + + if (!strcmp(ct->name, "cpu-front-fan-0")) + cpu_front_fans[0] = ct; + else if (!strcmp(ct->name, "cpu-front-fan-1")) + cpu_front_fans[1] = ct; + else if (!strcmp(ct->name, "cpu-rear-fan-0")) + cpu_rear_fans[0] = ct; + else if (!strcmp(ct->name, "cpu-rear-fan-1")) + cpu_rear_fans[1] = ct; + else if (!strcmp(ct->name, "cpu-pump-0")) + cpu_pumps[0] = ct; + else if (!strcmp(ct->name, "cpu-pump-1")) + cpu_pumps[1] = ct; + else if (!strcmp(ct->name, "backside-fan")) + backside_fan = ct; + else if (!strcmp(ct->name, "slots-fan")) + slots_fan = ct; + else if (!strcmp(ct->name, "drive-bay-fan")) + drives_fan = ct; + else if (!strcmp(ct->name, "cpufreq-clamp")) + cpufreq_clamp = ct; + + all_controls = + cpu_front_fans[0] && + cpu_rear_fans[0] && + backside_fan && + slots_fan && + drives_fan; + if (nr_chips > 1) + all_controls &= + cpu_front_fans[1] && + cpu_rear_fans[1]; + have_all_controls = all_controls; + + if ((cpu_pumps[0] || cpu_pumps[1]) && !had_pump) { + pr_info("wf_pm72: Liquid cooling pump(s) detected," + " using new algorithm !\n"); + cpu_pid_combined = true; + } +} + + +static void pm72_new_sensor(struct wf_sensor *sr) +{ + bool all_sensors; + + if (!strcmp(sr->name, "cpu-diode-temp-0")) + sens_cpu_temp[0] = sr; + else if (!strcmp(sr->name, "cpu-diode-temp-1")) + sens_cpu_temp[1] = sr; + else if (!strcmp(sr->name, "cpu-voltage-0")) + sens_cpu_volts[0] = sr; + else if (!strcmp(sr->name, "cpu-voltage-1")) + sens_cpu_volts[1] = sr; + else if (!strcmp(sr->name, "cpu-current-0")) + sens_cpu_amps[0] = sr; + else if (!strcmp(sr->name, "cpu-current-1")) + sens_cpu_amps[1] = sr; + else if (!strcmp(sr->name, "backside-temp")) + backside_temp = sr; + else if (!strcmp(sr->name, "hd-temp")) + drives_temp = sr; + + all_sensors = + sens_cpu_temp[0] && + sens_cpu_volts[0] && + sens_cpu_amps[0] && + backside_temp && + drives_temp; + if (nr_chips > 1) + all_sensors &= + sens_cpu_temp[1] && + sens_cpu_volts[1] && + sens_cpu_amps[1]; + + have_all_sensors = all_sensors; +} + +static int pm72_wf_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + switch (event) { + case WF_EVENT_NEW_SENSOR: + pm72_new_sensor(data); + break; + case WF_EVENT_NEW_CONTROL: + pm72_new_control(data); + break; + case WF_EVENT_TICK: + if (have_all_controls && have_all_sensors) + pm72_tick(); + } + return 0; +} + +static struct notifier_block pm72_events = { + .notifier_call = pm72_wf_notify, +}; + +static int wf_pm72_probe(struct platform_device *dev) +{ + wf_register_client(&pm72_events); + return 0; +} + +static int __devexit wf_pm72_remove(struct platform_device *dev) +{ + wf_unregister_client(&pm72_events); + + /* should release all sensors and controls */ + return 0; +} + +static struct platform_driver wf_pm72_driver = { + .probe = wf_pm72_probe, + .remove = wf_pm72_remove, + .driver = { + .name = "windfarm", + .owner = THIS_MODULE, + }, +}; + +static int __init wf_pm72_init(void) +{ + struct device_node *cpu; + int i; + + if (!of_machine_is_compatible("PowerMac7,2") && + !of_machine_is_compatible("PowerMac7,3")) + return -ENODEV; + + /* Count the number of CPU cores */ + nr_chips = 0; + for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + ++nr_chips; + if (nr_chips > NR_CHIPS) + nr_chips = NR_CHIPS; + + pr_info("windfarm: Initializing for desktop G5 with %d chips\n", + nr_chips); + + /* Get MPU data for each CPU */ + for (i = 0; i < nr_chips; i++) { + cpu_mpu_data[i] = wf_get_mpu(i); + if (!cpu_mpu_data[i]) { + pr_err("wf_pm72: Failed to find MPU data for CPU %d\n", i); + return -ENXIO; + } + } + +#ifdef MODULE + request_module("windfarm_fcu_controls"); + request_module("windfarm_lm75_sensor"); + request_module("windfarm_ad7417_sensor"); + request_module("windfarm_max6690_sensor"); + request_module("windfarm_cpufreq_clamp"); +#endif /* MODULE */ + + platform_driver_register(&wf_pm72_driver); + return 0; +} + +static void __exit wf_pm72_exit(void) +{ + platform_driver_unregister(&wf_pm72_driver); +} + +module_init(wf_pm72_init); +module_exit(wf_pm72_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("Thermal control for AGP PowerMac G5s"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:windfarm"); diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index fc13d0f2663..990c87606be 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c @@ -302,13 +302,13 @@ static void wf_smu_create_sys_fans(void) pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; pid_param.itarget = param->itarget; - pid_param.min = fan_system->ops->get_min(fan_system); - pid_param.max = fan_system->ops->get_max(fan_system); + pid_param.min = wf_control_get_min(fan_system); + pid_param.max = wf_control_get_max(fan_system); if (fan_hd) { pid_param.min = - max(pid_param.min,fan_hd->ops->get_min(fan_hd)); + max(pid_param.min, wf_control_get_min(fan_hd)); pid_param.max = - min(pid_param.max,fan_hd->ops->get_max(fan_hd)); + min(pid_param.max, wf_control_get_max(fan_hd)); } wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); @@ -337,7 +337,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) } st->ticks = WF_SMU_SYS_FANS_INTERVAL; - rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); + rc = wf_sensor_get(sensor_hd_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", rc); @@ -373,7 +373,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) st->hd_setpoint = new_setpoint; readjust: if (fan_system && wf_smu_failure_state == 0) { - rc = fan_system->ops->set_value(fan_system, st->sys_setpoint); + rc = wf_control_set(fan_system, st->sys_setpoint); if (rc) { printk(KERN_WARNING "windfarm: Sys fan error %d\n", rc); @@ -381,7 +381,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) } } if (fan_hd && wf_smu_failure_state == 0) { - rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint); + rc = wf_control_set(fan_hd, st->hd_setpoint); if (rc) { printk(KERN_WARNING "windfarm: HD fan error %d\n", rc); @@ -447,8 +447,8 @@ static void wf_smu_create_cpu_fans(void) pid_param.ttarget = tmax - tdelta; pid_param.pmaxadj = maxpow - powadj; - pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); - pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); + pid_param.min = wf_control_get_min(fan_cpu_main); + pid_param.max = wf_control_get_max(fan_cpu_main); wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); @@ -481,7 +481,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } st->ticks = WF_SMU_CPU_FANS_INTERVAL; - rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); + rc = wf_sensor_get(sensor_cpu_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", rc); @@ -489,7 +489,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) return; } - rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); + rc = wf_sensor_get(sensor_cpu_power, &power); if (rc) { printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", rc); @@ -525,8 +525,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) st->cpu_setpoint = new_setpoint; readjust: if (fan_cpu_main && wf_smu_failure_state == 0) { - rc = fan_cpu_main->ops->set_value(fan_cpu_main, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU main fan" " error %d\n", rc); diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index a9430ed4f36..7653603cb00 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c @@ -192,8 +192,8 @@ static void wf_smu_create_cpu_fans(void) pid_param.ttarget = tmax - tdelta; pid_param.pmaxadj = maxpow - powadj; - pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); - pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); + pid_param.min = wf_control_get_min(fan_cpu_main); + pid_param.max = wf_control_get_max(fan_cpu_main); wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); @@ -226,7 +226,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } st->ticks = WF_SMU_CPU_FANS_INTERVAL; - rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); + rc = wf_sensor_get(sensor_cpu_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", rc); @@ -234,7 +234,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) return; } - rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); + rc = wf_sensor_get(sensor_cpu_power, &power); if (rc) { printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", rc); @@ -261,8 +261,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) st->cpu_setpoint = new_setpoint; readjust: if (fan_cpu_main && wf_smu_failure_state == 0) { - rc = fan_cpu_main->ops->set_value(fan_cpu_main, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU main fan" " error %d\n", rc); @@ -270,8 +269,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } } if (fan_cpu_second && wf_smu_failure_state == 0) { - rc = fan_cpu_second->ops->set_value(fan_cpu_second, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_second, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU second fan" " error %d\n", rc); @@ -279,8 +277,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } } if (fan_cpu_third && wf_smu_failure_state == 0) { - rc = fan_cpu_main->ops->set_value(fan_cpu_third, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_third, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU third fan" " error %d\n", rc); @@ -312,8 +309,8 @@ static void wf_smu_create_drive_fans(void) /* Fill PID params */ param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN); - param.min = fan_hd->ops->get_min(fan_hd); - param.max = fan_hd->ops->get_max(fan_hd); + param.min = wf_control_get_min(fan_hd); + param.max = wf_control_get_max(fan_hd); wf_pid_init(&wf_smu_drive_fans->pid, ¶m); DBG("wf: Drive Fan control initialized.\n"); @@ -338,7 +335,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) } st->ticks = st->pid.param.interval; - rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); + rc = wf_sensor_get(sensor_hd_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", rc); @@ -361,7 +358,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) st->setpoint = new_setpoint; readjust: if (fan_hd && wf_smu_failure_state == 0) { - rc = fan_hd->ops->set_value(fan_hd, st->setpoint); + rc = wf_control_set(fan_hd, st->setpoint); if (rc) { printk(KERN_WARNING "windfarm: HD fan error %d\n", rc); @@ -393,8 +390,8 @@ static void wf_smu_create_slots_fans(void) /* Fill PID params */ param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN); - param.min = fan_slots->ops->get_min(fan_slots); - param.max = fan_slots->ops->get_max(fan_slots); + param.min = wf_control_get_min(fan_slots); + param.max = wf_control_get_max(fan_slots); wf_pid_init(&wf_smu_slots_fans->pid, ¶m); DBG("wf: Slots Fan control initialized.\n"); @@ -419,7 +416,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) } st->ticks = st->pid.param.interval; - rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power); + rc = wf_sensor_get(sensor_slots_power, &power); if (rc) { printk(KERN_WARNING "windfarm: Slots power sensor error %d\n", rc); @@ -444,7 +441,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) st->setpoint = new_setpoint; readjust: if (fan_slots && wf_smu_failure_state == 0) { - rc = fan_slots->ops->set_value(fan_slots, st->setpoint); + rc = wf_control_set(fan_slots, st->setpoint); if (rc) { printk(KERN_WARNING "windfarm: Slots fan error %d\n", rc); diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c new file mode 100644 index 00000000000..3eca6d4b52f --- /dev/null +++ b/drivers/macintosh/windfarm_rm31.c @@ -0,0 +1,740 @@ +/* + * Windfarm PowerMac thermal control. + * Control loops for RackMack3,1 (Xserve G5) + * + * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Use and redistribute under the terms of the GNU GPL v2. + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <asm/prom.h> +#include <asm/smu.h> + +#include "windfarm.h" +#include "windfarm_pid.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +#undef DEBUG +#undef LOTSA_DEBUG + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +#ifdef LOTSA_DEBUG +#define DBG_LOTS(args...) printk(args) +#else +#define DBG_LOTS(args...) do { } while(0) +#endif + +/* define this to force CPU overtemp to 60 degree, useful for testing + * the overtemp code + */ +#undef HACKED_OVERTEMP + +/* We currently only handle 2 chips */ +#define NR_CHIPS 2 +#define NR_CPU_FANS 3 * NR_CHIPS + +/* Controls and sensors */ +static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; +static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; +static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; +static struct wf_sensor *backside_temp; +static struct wf_sensor *slots_temp; +static struct wf_sensor *dimms_temp; + +static struct wf_control *cpu_fans[NR_CHIPS][3]; +static struct wf_control *backside_fan; +static struct wf_control *slots_fan; +static struct wf_control *cpufreq_clamp; + +/* We keep a temperature history for average calculation of 180s */ +#define CPU_TEMP_HIST_SIZE 180 + +/* PID loop state */ +static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; +static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; +static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; +static int cpu_thist_pt; +static s64 cpu_thist_total; +static s32 cpu_all_tmax = 100 << 16; +static struct wf_pid_state backside_pid; +static int backside_tick; +static struct wf_pid_state slots_pid; +static int slots_tick; +static int slots_speed; +static struct wf_pid_state dimms_pid; +static int dimms_output_clamp; + +static int nr_chips; +static bool have_all_controls; +static bool have_all_sensors; +static bool started; + +static int failure_state; +#define FAILURE_SENSOR 1 +#define FAILURE_FAN 2 +#define FAILURE_PERM 4 +#define FAILURE_LOW_OVERTEMP 8 +#define FAILURE_HIGH_OVERTEMP 16 + +/* Overtemp values */ +#define LOW_OVER_AVERAGE 0 +#define LOW_OVER_IMMEDIATE (10 << 16) +#define LOW_OVER_CLEAR ((-10) << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) +#define HIGH_OVER_AVERAGE (10 << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) + + +static void cpu_max_all_fans(void) +{ + int i; + + /* We max all CPU fans in case of a sensor error. We also do the + * cpufreq clamping now, even if it's supposedly done later by the + * generic code anyway, we do it earlier here to react faster + */ + if (cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + for (i = 0; i < nr_chips; i++) { + if (cpu_fans[i][0]) + wf_control_set_max(cpu_fans[i][0]); + if (cpu_fans[i][1]) + wf_control_set_max(cpu_fans[i][1]); + if (cpu_fans[i][2]) + wf_control_set_max(cpu_fans[i][2]); + } +} + +static int cpu_check_overtemp(s32 temp) +{ + int new_state = 0; + s32 t_avg, t_old; + static bool first = true; + + /* First check for immediate overtemps */ + if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" + " temperature !\n"); + } + if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " immediate CPU temperature !\n"); + } + + /* + * The first time around, initialize the array with the first + * temperature reading + */ + if (first) { + int i; + + cpu_thist_total = 0; + for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { + cpu_thist[i] = temp; + cpu_thist_total += temp; + } + first = false; + } + + /* + * We calculate a history of max temperatures and use that for the + * overtemp management + */ + t_old = cpu_thist[cpu_thist_pt]; + cpu_thist[cpu_thist_pt] = temp; + cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; + cpu_thist_total -= t_old; + cpu_thist_total += temp; + t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; + + DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", + FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); + + /* Now check for average overtemps */ + if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to average CPU" + " temperature !\n"); + } + if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " average CPU temperature !\n"); + } + + /* Now handle overtemp conditions. We don't currently use the windfarm + * overtemp handling core as it's not fully suited to the needs of those + * new machine. This will be fixed later. + */ + if (new_state) { + /* High overtemp -> immediate shutdown */ + if (new_state & FAILURE_HIGH_OVERTEMP) + machine_power_off(); + if ((failure_state & new_state) != new_state) + cpu_max_all_fans(); + failure_state |= new_state; + } else if ((failure_state & FAILURE_LOW_OVERTEMP) && + (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { + printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); + failure_state &= ~FAILURE_LOW_OVERTEMP; + } + + return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); +} + +static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) +{ + s32 dtemp, volts, amps; + int rc; + + /* Get diode temperature */ + rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); + if (rc) { + DBG(" CPU%d: temp reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); + *temp = dtemp; + + /* Get voltage */ + rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); + if (rc) { + DBG(" CPU%d, volts reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); + + /* Get current */ + rc = wf_sensor_get(sens_cpu_amps[cpu], &s); + if (rc) { + DBG(" CPU%d, current reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); + + /* Calculate power */ + + /* Scale voltage and current raw sensor values according to fixed scales + * obtained in Darwin and calculate power from I and V + */ + *power = (((u64)volts) * ((u64)amps)) >> 16; + + DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); + + return 0; + +} + +static void cpu_fans_tick(void) +{ + int err, cpu, i; + s32 speed, temp, power, t_max = 0; + + DBG_LOTS("* cpu fans_tick_split()\n"); + + for (cpu = 0; cpu < nr_chips; ++cpu) { + struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; + + /* Read current speed */ + wf_control_get(cpu_fans[cpu][0], &sp->target); + + err = read_one_cpu_vals(cpu, &temp, &power); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + + /* Keep track of highest temp */ + t_max = max(t_max, temp); + + /* Handle possible overtemps */ + if (cpu_check_overtemp(t_max)) + return; + + /* Run PID */ + wf_cpu_pid_run(sp, power, temp); + + DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); + + /* Apply DIMMs clamp */ + speed = max(sp->target, dimms_output_clamp); + + /* Apply result to all cpu fans */ + for (i = 0; i < 3; i++) { + err = wf_control_set(cpu_fans[cpu][i], speed); + if (err) { + pr_warning("wf_rm31: Fan %s reports error %d\n", + cpu_fans[cpu][i]->name, err); + failure_state |= FAILURE_FAN; + } + } + } +} + +/* Implementation... */ +static int cpu_setup_pid(int cpu) +{ + struct wf_cpu_pid_param pid; + const struct mpu_data *mpu = cpu_mpu_data[cpu]; + s32 tmax, ttarget, ptarget; + int fmin, fmax, hsize; + + /* Get PID params from the appropriate MPU EEPROM */ + tmax = mpu->tmax << 16; + ttarget = mpu->ttarget << 16; + ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; + + DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", + cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); + + /* We keep a global tmax for overtemp calculations */ + if (tmax < cpu_all_tmax) + cpu_all_tmax = tmax; + + /* Set PID min/max by using the rear fan min/max */ + fmin = wf_control_get_min(cpu_fans[cpu][0]); + fmax = wf_control_get_max(cpu_fans[cpu][0]); + DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); + + /* History size */ + hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); + DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); + + /* Initialize PID loop */ + pid.interval = 1; /* seconds */ + pid.history_len = hsize; + pid.gd = mpu->pid_gd; + pid.gp = mpu->pid_gp; + pid.gr = mpu->pid_gr; + pid.tmax = tmax; + pid.ttarget = ttarget; + pid.pmaxadj = ptarget; + pid.min = fmin; + pid.max = fmax; + + wf_cpu_pid_init(&cpu_pid[cpu], &pid); + cpu_pid[cpu].target = 4000; + + return 0; +} + +/* Backside/U3 fan */ +static struct wf_pid_param backside_param = { + .interval = 1, + .history_len = 2, + .gd = 0x00500000, + .gp = 0x0004cccc, + .gr = 0, + .itarget = 70 << 16, + .additive = 0, + .min = 20, + .max = 100, +}; + +/* DIMMs temperature (clamp the backside fan) */ +static struct wf_pid_param dimms_param = { + .interval = 1, + .history_len = 20, + .gd = 0, + .gp = 0, + .gr = 0x06553600, + .itarget = 50 << 16, + .additive = 0, + .min = 4000, + .max = 14000, +}; + +static void backside_fan_tick(void) +{ + s32 temp, dtemp; + int speed, dspeed, fan_min; + int err; + + if (!backside_fan || !backside_temp || !dimms_temp || !backside_tick) + return; + if (--backside_tick > 0) + return; + backside_tick = backside_pid.param.interval; + + DBG_LOTS("* backside fans tick\n"); + + /* Update fan speed from actual fans */ + err = wf_control_get(backside_fan, &speed); + if (!err) + backside_pid.target = speed; + + err = wf_sensor_get(backside_temp, &temp); + if (err) { + printk(KERN_WARNING "windfarm: U3 temp sensor error %d\n", + err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(backside_fan); + return; + } + speed = wf_pid_run(&backside_pid, temp); + + DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + err = wf_sensor_get(dimms_temp, &dtemp); + if (err) { + printk(KERN_WARNING "windfarm: DIMMs temp sensor error %d\n", + err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(backside_fan); + return; + } + dspeed = wf_pid_run(&dimms_pid, dtemp); + dimms_output_clamp = dspeed; + + fan_min = (dspeed * 100) / 14000; + fan_min = max(fan_min, backside_param.min); + speed = max(speed, fan_min); + + err = wf_control_set(backside_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: backside fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void backside_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(backside_fan); + s32 fmax = wf_control_get_max(backside_fan); + struct wf_pid_param param; + + param = backside_param; + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&backside_pid, ¶m); + + param = dimms_param; + wf_pid_init(&dimms_pid, ¶m); + + backside_tick = 1; + + pr_info("wf_rm31: Backside control loop started.\n"); +} + +/* Slots fan */ +static const struct wf_pid_param slots_param = { + .interval = 5, + .history_len = 2, + .gd = 30 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 40 << 16, + .additive = 1, + .min = 300, + .max = 4000, +}; + +static void slots_fan_tick(void) +{ + s32 temp; + int speed; + int err; + + if (!slots_fan || !slots_temp || !slots_tick) + return; + if (--slots_tick > 0) + return; + slots_tick = slots_pid.param.interval; + + DBG_LOTS("* slots fans tick\n"); + + err = wf_sensor_get(slots_temp, &temp); + if (err) { + pr_warning("wf_rm31: slots temp sensor error %d\n", err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(slots_fan); + return; + } + speed = wf_pid_run(&slots_pid, temp); + + DBG_LOTS("slots PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + slots_speed = speed; + err = wf_control_set(slots_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: slots bay fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void slots_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(slots_fan); + s32 fmax = wf_control_get_max(slots_fan); + struct wf_pid_param param = slots_param; + + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&slots_pid, ¶m); + slots_tick = 1; + + pr_info("wf_rm31: Slots control loop started.\n"); +} + +static void set_fail_state(void) +{ + cpu_max_all_fans(); + + if (backside_fan) + wf_control_set_max(backside_fan); + if (slots_fan) + wf_control_set_max(slots_fan); +} + +static void rm31_tick(void) +{ + int i, last_failure; + + if (!started) { + started = 1; + printk(KERN_INFO "windfarm: CPUs control loops started.\n"); + for (i = 0; i < nr_chips; ++i) { + if (cpu_setup_pid(i) < 0) { + failure_state = FAILURE_PERM; + set_fail_state(); + break; + } + } + DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); + + backside_setup_pid(); + slots_setup_pid(); + +#ifdef HACKED_OVERTEMP + cpu_all_tmax = 60 << 16; +#endif + } + + /* Permanent failure, bail out */ + if (failure_state & FAILURE_PERM) + return; + + /* + * Clear all failure bits except low overtemp which will be eventually + * cleared by the control loop itself + */ + last_failure = failure_state; + failure_state &= FAILURE_LOW_OVERTEMP; + backside_fan_tick(); + slots_fan_tick(); + + /* We do CPUs last because they can be clamped high by + * DIMM temperature + */ + cpu_fans_tick(); + + DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", + last_failure, failure_state); + + /* Check for failures. Any failure causes cpufreq clamping */ + if (failure_state && last_failure == 0 && cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + if (failure_state == 0 && last_failure && cpufreq_clamp) + wf_control_set_min(cpufreq_clamp); + + /* That's it for now, we might want to deal with other failures + * differently in the future though + */ +} + +static void rm31_new_control(struct wf_control *ct) +{ + bool all_controls; + + if (!strcmp(ct->name, "cpu-fan-a-0")) + cpu_fans[0][0] = ct; + else if (!strcmp(ct->name, "cpu-fan-b-0")) + cpu_fans[0][1] = ct; + else if (!strcmp(ct->name, "cpu-fan-c-0")) + cpu_fans[0][2] = ct; + else if (!strcmp(ct->name, "cpu-fan-a-1")) + cpu_fans[1][0] = ct; + else if (!strcmp(ct->name, "cpu-fan-b-1")) + cpu_fans[1][1] = ct; + else if (!strcmp(ct->name, "cpu-fan-c-1")) + cpu_fans[1][2] = ct; + else if (!strcmp(ct->name, "backside-fan")) + backside_fan = ct; + else if (!strcmp(ct->name, "slots-fan")) + slots_fan = ct; + else if (!strcmp(ct->name, "cpufreq-clamp")) + cpufreq_clamp = ct; + + all_controls = + cpu_fans[0][0] && + cpu_fans[0][1] && + cpu_fans[0][2] && + backside_fan && + slots_fan; + if (nr_chips > 1) + all_controls &= + cpu_fans[1][0] && + cpu_fans[1][1] && + cpu_fans[1][2]; + have_all_controls = all_controls; +} + + +static void rm31_new_sensor(struct wf_sensor *sr) +{ + bool all_sensors; + + if (!strcmp(sr->name, "cpu-diode-temp-0")) + sens_cpu_temp[0] = sr; + else if (!strcmp(sr->name, "cpu-diode-temp-1")) + sens_cpu_temp[1] = sr; + else if (!strcmp(sr->name, "cpu-voltage-0")) + sens_cpu_volts[0] = sr; + else if (!strcmp(sr->name, "cpu-voltage-1")) + sens_cpu_volts[1] = sr; + else if (!strcmp(sr->name, "cpu-current-0")) + sens_cpu_amps[0] = sr; + else if (!strcmp(sr->name, "cpu-current-1")) + sens_cpu_amps[1] = sr; + else if (!strcmp(sr->name, "backside-temp")) + backside_temp = sr; + else if (!strcmp(sr->name, "slots-temp")) + slots_temp = sr; + else if (!strcmp(sr->name, "dimms-temp")) + dimms_temp = sr; + + all_sensors = + sens_cpu_temp[0] && + sens_cpu_volts[0] && + sens_cpu_amps[0] && + backside_temp && + slots_temp && + dimms_temp; + if (nr_chips > 1) + all_sensors &= + sens_cpu_temp[1] && + sens_cpu_volts[1] && + sens_cpu_amps[1]; + + have_all_sensors = all_sensors; +} + +static int rm31_wf_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + switch (event) { + case WF_EVENT_NEW_SENSOR: + rm31_new_sensor(data); + break; + case WF_EVENT_NEW_CONTROL: + rm31_new_control(data); + break; + case WF_EVENT_TICK: + if (have_all_controls && have_all_sensors) + rm31_tick(); + } + return 0; +} + +static struct notifier_block rm31_events = { + .notifier_call = rm31_wf_notify, +}; + +static int wf_rm31_probe(struct platform_device *dev) +{ + wf_register_client(&rm31_events); + return 0; +} + +static int __devexit wf_rm31_remove(struct platform_device *dev) +{ + wf_unregister_client(&rm31_events); + + /* should release all sensors and controls */ + return 0; +} + +static struct platform_driver wf_rm31_driver = { + .probe = wf_rm31_probe, + .remove = wf_rm31_remove, + .driver = { + .name = "windfarm", + .owner = THIS_MODULE, + }, +}; + +static int __init wf_rm31_init(void) +{ + struct device_node *cpu; + int i; + + if (!of_machine_is_compatible("RackMac3,1")) + return -ENODEV; + + /* Count the number of CPU cores */ + nr_chips = 0; + for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + ++nr_chips; + if (nr_chips > NR_CHIPS) + nr_chips = NR_CHIPS; + + pr_info("windfarm: Initializing for desktop G5 with %d chips\n", + nr_chips); + + /* Get MPU data for each CPU */ + for (i = 0; i < nr_chips; i++) { + cpu_mpu_data[i] = wf_get_mpu(i); + if (!cpu_mpu_data[i]) { + pr_err("wf_rm31: Failed to find MPU data for CPU %d\n", i); + return -ENXIO; + } + } + +#ifdef MODULE + request_module("windfarm_fcu_controls"); + request_module("windfarm_lm75_sensor"); + request_module("windfarm_lm87_sensor"); + request_module("windfarm_ad7417_sensor"); + request_module("windfarm_max6690_sensor"); + request_module("windfarm_cpufreq_clamp"); +#endif /* MODULE */ + + platform_driver_register(&wf_rm31_driver); + return 0; +} + +static void __exit wf_rm31_exit(void) +{ + platform_driver_unregister(&wf_rm31_driver); +} + +module_init(wf_rm31_init); +module_exit(wf_rm31_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("Thermal control for Xserve G5"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:windfarm"); diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index 3c2be5193fd..c155a54e863 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c @@ -172,7 +172,6 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, fct->fan_type = pwm_fan; fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN; - sysfs_attr_init(&fct->ctrl.attr.attr); /* We use the name & location here the same way we do for SMU sensors, * see the comment in windfarm_smu_sensors.c. The locations are a bit diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index 65a8ff3e1f8..e2989cef230 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -20,7 +20,7 @@ #include "windfarm.h" -#define VERSION "0.2" +#define VERSION "1.0" #define DEBUG @@ -34,11 +34,12 @@ #define MAX_AGE msecs_to_jiffies(800) struct wf_sat { + struct kref ref; int nr; - atomic_t refcnt; struct mutex mutex; unsigned long last_read; /* jiffies when cache last updated */ u8 cache[16]; + struct list_head sensors; struct i2c_client *i2c; struct device_node *node; }; @@ -46,11 +47,12 @@ struct wf_sat { static struct wf_sat *sats[2]; struct wf_sat_sensor { - int index; - int index2; /* used for power sensors */ - int shift; - struct wf_sat *sat; - struct wf_sensor sens; + struct list_head link; + int index; + int index2; /* used for power sensors */ + int shift; + struct wf_sat *sat; + struct wf_sensor sens; }; #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) @@ -142,7 +144,7 @@ static int wf_sat_read_cache(struct wf_sat *sat) return 0; } -static int wf_sat_get(struct wf_sensor *sr, s32 *value) +static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value) { struct wf_sat_sensor *sens = wf_to_sat(sr); struct wf_sat *sat = sens->sat; @@ -175,58 +177,30 @@ static int wf_sat_get(struct wf_sensor *sr, s32 *value) return err; } -static void wf_sat_release(struct wf_sensor *sr) +static void wf_sat_release(struct kref *ref) +{ + struct wf_sat *sat = container_of(ref, struct wf_sat, ref); + + if (sat->nr >= 0) + sats[sat->nr] = NULL; + kfree(sat); +} + +static void wf_sat_sensor_release(struct wf_sensor *sr) { struct wf_sat_sensor *sens = wf_to_sat(sr); struct wf_sat *sat = sens->sat; - if (atomic_dec_and_test(&sat->refcnt)) { - if (sat->nr >= 0) - sats[sat->nr] = NULL; - kfree(sat); - } kfree(sens); + kref_put(&sat->ref, wf_sat_release); } static struct wf_sensor_ops wf_sat_ops = { - .get_value = wf_sat_get, - .release = wf_sat_release, + .get_value = wf_sat_sensor_get, + .release = wf_sat_sensor_release, .owner = THIS_MODULE, }; -static struct i2c_driver wf_sat_driver; - -static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) -{ - struct i2c_board_info info; - struct i2c_client *client; - const u32 *reg; - u8 addr; - - reg = of_get_property(dev, "reg", NULL); - if (reg == NULL) - return; - addr = *reg; - DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = (addr >> 1) & 0x7f; - info.platform_data = dev; - strlcpy(info.type, "wf_sat", I2C_NAME_SIZE); - - client = i2c_new_device(adapter, &info); - if (client == NULL) { - printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); - return; - } - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &wf_sat_driver.clients); -} - static int wf_sat_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -246,9 +220,10 @@ static int wf_sat_probe(struct i2c_client *client, return -ENOMEM; sat->nr = -1; sat->node = of_node_get(dev); - atomic_set(&sat->refcnt, 0); + kref_init(&sat->ref); mutex_init(&sat->mutex); sat->i2c = client; + INIT_LIST_HEAD(&sat->sensors); i2c_set_clientdata(client, sat); vsens[0] = vsens[1] = -1; @@ -310,14 +285,15 @@ static int wf_sat_probe(struct i2c_client *client, sens->index2 = -1; sens->shift = shift; sens->sat = sat; - atomic_inc(&sat->refcnt); sens->sens.ops = &wf_sat_ops; sens->sens.name = (char *) (sens + 1); - snprintf(sens->sens.name, 16, "%s-%d", name, cpu); + snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu); - if (wf_register_sensor(&sens->sens)) { - atomic_dec(&sat->refcnt); + if (wf_register_sensor(&sens->sens)) kfree(sens); + else { + list_add(&sens->link, &sat->sensors); + kref_get(&sat->ref); } } @@ -336,14 +312,15 @@ static int wf_sat_probe(struct i2c_client *client, sens->index2 = isens[core]; sens->shift = 0; sens->sat = sat; - atomic_inc(&sat->refcnt); sens->sens.ops = &wf_sat_ops; sens->sens.name = (char *) (sens + 1); - snprintf(sens->sens.name, 16, "cpu-power-%d", cpu); + snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu); - if (wf_register_sensor(&sens->sens)) { - atomic_dec(&sat->refcnt); + if (wf_register_sensor(&sens->sens)) kfree(sens); + else { + list_add(&sens->link, &sat->sensors); + kref_get(&sat->ref); } } @@ -353,42 +330,35 @@ static int wf_sat_probe(struct i2c_client *client, return 0; } -static int wf_sat_attach(struct i2c_adapter *adapter) -{ - struct device_node *busnode, *dev = NULL; - struct pmac_i2c_bus *bus; - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) - return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - while ((dev = of_get_next_child(busnode, dev)) != NULL) - if (of_device_is_compatible(dev, "smu-sat")) - wf_sat_create(adapter, dev); - return 0; -} - static int wf_sat_remove(struct i2c_client *client) { struct wf_sat *sat = i2c_get_clientdata(client); + struct wf_sat_sensor *sens; - /* XXX TODO */ - + /* release sensors */ + while(!list_empty(&sat->sensors)) { + sens = list_first_entry(&sat->sensors, + struct wf_sat_sensor, link); + list_del(&sens->link); + wf_unregister_sensor(&sens->sens); + } sat->i2c = NULL; + i2c_set_clientdata(client, NULL); + kref_put(&sat->ref, wf_sat_release); + return 0; } static const struct i2c_device_id wf_sat_id[] = { - { "wf_sat", 0 }, + { "MAC,smu-sat", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, wf_sat_id); static struct i2c_driver wf_sat_driver = { .driver = { .name = "wf_smu_sat", }, - .attach_adapter = wf_sat_attach, .probe = wf_sat_probe, .remove = wf_sat_remove, .id_table = wf_sat_id, @@ -399,15 +369,13 @@ static int __init sat_sensors_init(void) return i2c_add_driver(&wf_sat_driver); } -#if 0 /* uncomment when module_exit() below is uncommented */ static void __exit sat_sensors_exit(void) { i2c_del_driver(&wf_sat_driver); } -#endif module_init(sat_sensors_init); -/*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */ +module_exit(sat_sensors_exit); MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index a409fa050a1..93d0a8b7718 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -338,7 +338,7 @@ int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, mutex_unlock(&ps3av->mutex); return 0; - err: +err: mutex_unlock(&ps3av->mutex); printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); return res; @@ -477,7 +477,6 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) return 0; } - EXPORT_SYMBOL_GPL(ps3av_set_audio_mode); static int ps3av_set_videomode(void) @@ -501,7 +500,7 @@ static void ps3av_set_videomode_packet(u32 id) video_mode = &video_mode_table[id & PS3AV_MODE_MASK]; - avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ + avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ avb_param.num_of_audio_pkt = 0; avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi + ps3av->av_hw_conf.num_of_avmulti; @@ -521,7 +520,7 @@ static void ps3av_set_videomode_packet(u32 id) #ifndef PS3AV_HDMI_YUV if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) - av_video_cs = RGB8; /* use RGB for HDMI */ + av_video_cs = RGB8; /* use RGB for HDMI */ #endif len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], ps3av->av_port[i], @@ -590,8 +589,8 @@ static void ps3avd(struct work_struct *work) #define SHIFT_VESA 8 static const struct { - unsigned mask : 19; - unsigned id : 4; + unsigned mask:19; + unsigned id:4; } ps3av_preferred_modes[] = { { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA }, { PS3AV_RESBIT_1920x1080P << SHIFT_60, PS3AV_MODE_1080P60 }, @@ -667,7 +666,8 @@ static enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info) return id; } -static void ps3av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info *monitor_info) +static void ps3av_monitor_info_dump( + const struct ps3av_pkt_av_get_monitor_info *monitor_info) { const struct ps3av_info_monitor *info = &monitor_info->info; const struct ps3av_info_audio *audio = info->audio; @@ -717,8 +717,8 @@ static void ps3av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info * /* audio block */ for (i = 0; i < info->num_of_audio_block; i++) { - pr_debug("audio[%d] type: %02x max_ch: %02x fs: %02x sbit: " - "%02x\n", + pr_debug( + "audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n", i, audio->type, audio->max_num_of_ch, audio->fs, audio->sbit); audio++; @@ -870,21 +870,18 @@ int ps3av_set_video_mode(int id) return 0; } - EXPORT_SYMBOL_GPL(ps3av_set_video_mode); int ps3av_get_auto_mode(void) { return ps3av_auto_videomode(&ps3av->av_hw_conf); } - EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); int ps3av_get_mode(void) { return ps3av ? ps3av->ps3av_mode : 0; } - EXPORT_SYMBOL_GPL(ps3av_get_mode); /* get resolution by video_mode */ @@ -902,7 +899,6 @@ int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres) *yres = video_mode_table[id].y; return 0; } - EXPORT_SYMBOL_GPL(ps3av_video_mode2res); /* mute */ @@ -911,7 +907,6 @@ int ps3av_video_mute(int mute) return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON : PS3AV_CMD_MUTE_OFF); } - EXPORT_SYMBOL_GPL(ps3av_video_mute); /* mute analog output only */ @@ -935,7 +930,6 @@ int ps3av_audio_mute(int mute) return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON : PS3AV_CMD_MUTE_OFF); } - EXPORT_SYMBOL_GPL(ps3av_audio_mute); static int __devinit ps3av_probe(struct ps3_system_bus_device *dev) |