diff options
85 files changed, 7827 insertions, 1080 deletions
diff --git a/Documentation/ABI/testing/debugfs-pfo-nx-crypto b/Documentation/ABI/testing/debugfs-pfo-nx-crypto new file mode 100644 index 000000000000..685d5a448423 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-pfo-nx-crypto | |||
@@ -0,0 +1,45 @@ | |||
1 | What: /sys/kernel/debug/nx-crypto/* | ||
2 | Date: March 2012 | ||
3 | KernelVersion: 3.4 | ||
4 | Contact: Kent Yoder <key@linux.vnet.ibm.com> | ||
5 | Description: | ||
6 | |||
7 | These debugfs interfaces are built by the nx-crypto driver, built in | ||
8 | arch/powerpc/crypto/nx. | ||
9 | |||
10 | Error Detection | ||
11 | =============== | ||
12 | |||
13 | errors: | ||
14 | - A u32 providing a total count of errors since the driver was loaded. The | ||
15 | only errors counted here are those returned from the hcall, H_COP_OP. | ||
16 | |||
17 | last_error: | ||
18 | - The most recent non-zero return code from the H_COP_OP hcall. -EBUSY is not | ||
19 | recorded here (the hcall will retry until -EBUSY goes away). | ||
20 | |||
21 | last_error_pid: | ||
22 | - The process ID of the process who received the most recent error from the | ||
23 | hcall. | ||
24 | |||
25 | Device Use | ||
26 | ========== | ||
27 | |||
28 | aes_bytes: | ||
29 | - The total number of bytes encrypted using AES in any of the driver's | ||
30 | supported modes. | ||
31 | |||
32 | aes_ops: | ||
33 | - The total number of AES operations submitted to the hardware. | ||
34 | |||
35 | sha256_bytes: | ||
36 | - The total number of bytes hashed by the hardware using SHA-256. | ||
37 | |||
38 | sha256_ops: | ||
39 | - The total number of SHA-256 operations submitted to the hardware. | ||
40 | |||
41 | sha512_bytes: | ||
42 | - The total number of bytes hashed by the hardware using SHA-512. | ||
43 | |||
44 | sha512_ops: | ||
45 | - The total number of SHA-512 operations submitted to the hardware. | ||
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 8a01098eaaca..0a947bd9c076 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -350,7 +350,7 @@ config ARCH_ENABLE_MEMORY_HOTREMOVE | |||
350 | 350 | ||
351 | config KEXEC | 351 | config KEXEC |
352 | bool "kexec system call (EXPERIMENTAL)" | 352 | bool "kexec system call (EXPERIMENTAL)" |
353 | depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP && !PPC_47x)) && EXPERIMENTAL | 353 | depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP)) && EXPERIMENTAL |
354 | help | 354 | help |
355 | kexec is a system call that implements the ability to shutdown your | 355 | kexec is a system call that implements the ability to shutdown your |
356 | current kernel, and to start another kernel. It is like a reboot | 356 | current kernel, and to start another kernel. It is like a reboot |
@@ -367,7 +367,7 @@ config KEXEC | |||
367 | 367 | ||
368 | config CRASH_DUMP | 368 | config CRASH_DUMP |
369 | bool "Build a kdump crash kernel" | 369 | bool "Build a kdump crash kernel" |
370 | depends on PPC64 || 6xx || FSL_BOOKE || (44x && !SMP && !PPC_47x) | 370 | depends on PPC64 || 6xx || FSL_BOOKE || (44x && !SMP) |
371 | select RELOCATABLE if PPC64 || 44x | 371 | select RELOCATABLE if PPC64 || 44x |
372 | select DYNAMIC_MEMSTART if FSL_BOOKE | 372 | select DYNAMIC_MEMSTART if FSL_BOOKE |
373 | help | 373 | help |
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 6524c6e21896..950d1f7a5a39 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile | |||
@@ -69,6 +69,16 @@ LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y) | |||
69 | 69 | ||
70 | CFLAGS-$(CONFIG_PPC64) := -mminimal-toc -mtraceback=no -mcall-aixdesc | 70 | CFLAGS-$(CONFIG_PPC64) := -mminimal-toc -mtraceback=no -mcall-aixdesc |
71 | CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple | 71 | CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple |
72 | |||
73 | CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4) | ||
74 | CFLAGS-$(CONFIG_CELL_CPU) += $(call cc-option,-mcpu=cell) | ||
75 | CFLAGS-$(CONFIG_POWER4_CPU) += $(call cc-option,-mcpu=power4) | ||
76 | CFLAGS-$(CONFIG_POWER5_CPU) += $(call cc-option,-mcpu=power5) | ||
77 | CFLAGS-$(CONFIG_POWER6_CPU) += $(call cc-option,-mcpu=power6) | ||
78 | CFLAGS-$(CONFIG_POWER7_CPU) += $(call cc-option,-mcpu=power7) | ||
79 | |||
80 | CFLAGS-$(CONFIG_TUNE_CELL) += $(call cc-option,-mtune=cell) | ||
81 | |||
72 | KBUILD_CPPFLAGS += -Iarch/$(ARCH) | 82 | KBUILD_CPPFLAGS += -Iarch/$(ARCH) |
73 | KBUILD_AFLAGS += -Iarch/$(ARCH) | 83 | KBUILD_AFLAGS += -Iarch/$(ARCH) |
74 | KBUILD_CFLAGS += -msoft-float -pipe -Iarch/$(ARCH) $(CFLAGS-y) | 84 | KBUILD_CFLAGS += -msoft-float -pipe -Iarch/$(ARCH) $(CFLAGS-y) |
@@ -76,32 +86,11 @@ CPP = $(CC) -E $(KBUILD_CFLAGS) | |||
76 | 86 | ||
77 | CHECKFLAGS += -m$(CONFIG_WORD_SIZE) -D__powerpc__ -D__powerpc$(CONFIG_WORD_SIZE)__ | 87 | CHECKFLAGS += -m$(CONFIG_WORD_SIZE) -D__powerpc__ -D__powerpc$(CONFIG_WORD_SIZE)__ |
78 | 88 | ||
79 | ifeq ($(CONFIG_PPC64),y) | ||
80 | GCC_BROKEN_VEC := $(call cc-ifversion, -lt, 0400, y) | ||
81 | |||
82 | ifeq ($(CONFIG_POWER4_ONLY),y) | ||
83 | ifeq ($(CONFIG_ALTIVEC),y) | ||
84 | ifeq ($(GCC_BROKEN_VEC),y) | ||
85 | KBUILD_CFLAGS += $(call cc-option,-mcpu=970) | ||
86 | else | ||
87 | KBUILD_CFLAGS += $(call cc-option,-mcpu=power4) | ||
88 | endif | ||
89 | else | ||
90 | KBUILD_CFLAGS += $(call cc-option,-mcpu=power4) | ||
91 | endif | ||
92 | else | ||
93 | KBUILD_CFLAGS += $(call cc-option,-mtune=power4) | ||
94 | endif | ||
95 | endif | ||
96 | |||
97 | KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o | 89 | KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o |
98 | 90 | ||
99 | ifeq ($(CONFIG_TUNE_CELL),y) | 91 | # No AltiVec or VSX instructions when building kernel |
100 | KBUILD_CFLAGS += $(call cc-option,-mtune=cell) | ||
101 | endif | ||
102 | |||
103 | # No AltiVec instruction when building kernel | ||
104 | KBUILD_CFLAGS += $(call cc-option,-mno-altivec) | 92 | KBUILD_CFLAGS += $(call cc-option,-mno-altivec) |
93 | KBUILD_CFLAGS += $(call cc-option,-mno-vsx) | ||
105 | 94 | ||
106 | # No SPE instruction when building kernel | 95 | # No SPE instruction when building kernel |
107 | # (We use all available options to help semi-broken compilers) | 96 | # (We use all available options to help semi-broken compilers) |
@@ -160,6 +149,7 @@ core-$(CONFIG_KVM) += arch/powerpc/kvm/ | |||
160 | core-$(CONFIG_PERF_EVENTS) += arch/powerpc/perf/ | 149 | core-$(CONFIG_PERF_EVENTS) += arch/powerpc/perf/ |
161 | 150 | ||
162 | drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/ | 151 | drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/ |
152 | drivers-$(CONFIG_CRYPTO_DEV_NX) += drivers/crypto/nx/ | ||
163 | 153 | ||
164 | # Default to zImage, override when needed | 154 | # Default to zImage, override when needed |
165 | all: zImage | 155 | all: zImage |
@@ -234,10 +224,11 @@ archprepare: checkbin | |||
234 | # Use the file '.tmp_gas_check' for binutils tests, as gas won't output | 224 | # Use the file '.tmp_gas_check' for binutils tests, as gas won't output |
235 | # to stdout and these checks are run even on install targets. | 225 | # to stdout and these checks are run even on install targets. |
236 | TOUT := .tmp_gas_check | 226 | TOUT := .tmp_gas_check |
237 | # Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later for altivec | ||
238 | # instructions. | ||
239 | # gcc-3.4 and binutils-2.14 are a fatal combination. | ||
240 | 227 | ||
228 | # Check gcc and binutils versions: | ||
229 | # - gcc-3.4 and binutils-2.14 are a fatal combination | ||
230 | # - Require gcc 4.0 or above on 64-bit | ||
231 | # - gcc-4.2.0 has issues compiling modules on 64-bit | ||
241 | checkbin: | 232 | checkbin: |
242 | @if test "$(call cc-version)" = "0304" ; then \ | 233 | @if test "$(call cc-version)" = "0304" ; then \ |
243 | if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \ | 234 | if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \ |
@@ -247,6 +238,12 @@ checkbin: | |||
247 | false; \ | 238 | false; \ |
248 | fi ; \ | 239 | fi ; \ |
249 | fi | 240 | fi |
241 | @if test "$(call cc-version)" -lt "0400" \ | ||
242 | && test "x${CONFIG_PPC64}" = "xy" ; then \ | ||
243 | echo -n "Sorry, GCC v4.0 or above is required to build " ; \ | ||
244 | echo "the 64-bit powerpc kernel." ; \ | ||
245 | false ; \ | ||
246 | fi | ||
250 | @if test "$(call cc-fullversion)" = "040200" \ | 247 | @if test "$(call cc-fullversion)" = "040200" \ |
251 | && test "x${CONFIG_MODULES}${CONFIG_PPC64}" = "xyy" ; then \ | 248 | && test "x${CONFIG_MODULES}${CONFIG_PPC64}" = "xyy" ; then \ |
252 | echo -n '*** GCC-4.2.0 cannot compile the 64-bit powerpc ' ; \ | 249 | echo -n '*** GCC-4.2.0 cannot compile the 64-bit powerpc ' ; \ |
diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts index 7bda373f10ef..9d4917aebe6b 100644 --- a/arch/powerpc/boot/dts/bluestone.dts +++ b/arch/powerpc/boot/dts/bluestone.dts | |||
@@ -373,5 +373,30 @@ | |||
373 | 0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */ | 373 | 0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */ |
374 | 0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>; | 374 | 0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>; |
375 | }; | 375 | }; |
376 | |||
377 | MSI: ppc4xx-msi@C10000000 { | ||
378 | compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; | ||
379 | reg = < 0xC 0x10000000 0x100 | ||
380 | 0xC 0x10000000 0x100>; | ||
381 | sdr-base = <0x36C>; | ||
382 | msi-data = <0x00004440>; | ||
383 | msi-mask = <0x0000ffe0>; | ||
384 | interrupts =<0 1 2 3 4 5 6 7>; | ||
385 | interrupt-parent = <&MSI>; | ||
386 | #interrupt-cells = <1>; | ||
387 | #address-cells = <0>; | ||
388 | #size-cells = <0>; | ||
389 | msi-available-ranges = <0x0 0x100>; | ||
390 | interrupt-map = < | ||
391 | 0 &UIC3 0x18 1 | ||
392 | 1 &UIC3 0x19 1 | ||
393 | 2 &UIC3 0x1A 1 | ||
394 | 3 &UIC3 0x1B 1 | ||
395 | 4 &UIC3 0x1C 1 | ||
396 | 5 &UIC3 0x1D 1 | ||
397 | 6 &UIC3 0x1E 1 | ||
398 | 7 &UIC3 0x1F 1 | ||
399 | >; | ||
400 | }; | ||
376 | }; | 401 | }; |
377 | }; | 402 | }; |
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 1196c34163b7..07b7f2af2dca 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig | |||
@@ -1,5 +1,4 @@ | |||
1 | CONFIG_PPC64=y | 1 | CONFIG_PPC64=y |
2 | CONFIG_POWER4_ONLY=y | ||
3 | CONFIG_ALTIVEC=y | 2 | CONFIG_ALTIVEC=y |
4 | CONFIG_SMP=y | 3 | CONFIG_SMP=y |
5 | CONFIG_NR_CPUS=4 | 4 | CONFIG_NR_CPUS=4 |
diff --git a/arch/powerpc/configs/maple_defconfig b/arch/powerpc/configs/maple_defconfig index 2244d370f24d..02ac96b679b8 100644 --- a/arch/powerpc/configs/maple_defconfig +++ b/arch/powerpc/configs/maple_defconfig | |||
@@ -1,5 +1,4 @@ | |||
1 | CONFIG_PPC64=y | 1 | CONFIG_PPC64=y |
2 | CONFIG_POWER4_ONLY=y | ||
3 | CONFIG_SMP=y | 2 | CONFIG_SMP=y |
4 | CONFIG_NR_CPUS=4 | 3 | CONFIG_NR_CPUS=4 |
5 | CONFIG_EXPERIMENTAL=y | 4 | CONFIG_EXPERIMENTAL=y |
diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig index f4deb0b78cf0..840a2c2d0430 100644 --- a/arch/powerpc/configs/pasemi_defconfig +++ b/arch/powerpc/configs/pasemi_defconfig | |||
@@ -1,5 +1,4 @@ | |||
1 | CONFIG_PPC64=y | 1 | CONFIG_PPC64=y |
2 | CONFIG_POWER4_ONLY=y | ||
3 | CONFIG_ALTIVEC=y | 2 | CONFIG_ALTIVEC=y |
4 | # CONFIG_VIRT_CPU_ACCOUNTING is not set | 3 | # CONFIG_VIRT_CPU_ACCOUNTING is not set |
5 | CONFIG_SMP=y | 4 | CONFIG_SMP=y |
diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index ded867871e97..c2f4b4a86ece 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig | |||
@@ -6,7 +6,6 @@ CONFIG_NR_CPUS=2 | |||
6 | CONFIG_EXPERIMENTAL=y | 6 | CONFIG_EXPERIMENTAL=y |
7 | CONFIG_SYSVIPC=y | 7 | CONFIG_SYSVIPC=y |
8 | CONFIG_POSIX_MQUEUE=y | 8 | CONFIG_POSIX_MQUEUE=y |
9 | CONFIG_SPARSE_IRQ=y | ||
10 | CONFIG_BLK_DEV_INITRD=y | 9 | CONFIG_BLK_DEV_INITRD=y |
11 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y | 10 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y |
12 | CONFIG_EMBEDDED=y | 11 | CONFIG_EMBEDDED=y |
@@ -25,7 +24,6 @@ CONFIG_PS3_DISK=y | |||
25 | CONFIG_PS3_ROM=y | 24 | CONFIG_PS3_ROM=y |
26 | CONFIG_PS3_FLASH=y | 25 | CONFIG_PS3_FLASH=y |
27 | CONFIG_PS3_VRAM=m | 26 | CONFIG_PS3_VRAM=m |
28 | CONFIG_PS3_LPM=m | ||
29 | # CONFIG_PPC_OF_BOOT_TRAMPOLINE is not set | 27 | # CONFIG_PPC_OF_BOOT_TRAMPOLINE is not set |
30 | CONFIG_HIGH_RES_TIMERS=y | 28 | CONFIG_HIGH_RES_TIMERS=y |
31 | # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set | 29 | # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set |
@@ -53,8 +51,6 @@ CONFIG_IP_PNP_DHCP=y | |||
53 | # CONFIG_INET_DIAG is not set | 51 | # CONFIG_INET_DIAG is not set |
54 | CONFIG_IPV6=y | 52 | CONFIG_IPV6=y |
55 | CONFIG_BT=m | 53 | CONFIG_BT=m |
56 | CONFIG_BT_L2CAP=y | ||
57 | CONFIG_BT_SCO=y | ||
58 | CONFIG_BT_RFCOMM=m | 54 | CONFIG_BT_RFCOMM=m |
59 | CONFIG_BT_RFCOMM_TTY=y | 55 | CONFIG_BT_RFCOMM_TTY=y |
60 | CONFIG_BT_BNEP=m | 56 | CONFIG_BT_BNEP=m |
@@ -63,7 +59,6 @@ CONFIG_BT_BNEP_PROTO_FILTER=y | |||
63 | CONFIG_BT_HIDP=m | 59 | CONFIG_BT_HIDP=m |
64 | CONFIG_BT_HCIBTUSB=m | 60 | CONFIG_BT_HCIBTUSB=m |
65 | CONFIG_CFG80211=m | 61 | CONFIG_CFG80211=m |
66 | # CONFIG_WIRELESS_EXT_SYSFS is not set | ||
67 | CONFIG_MAC80211=m | 62 | CONFIG_MAC80211=m |
68 | CONFIG_MAC80211_RC_PID=y | 63 | CONFIG_MAC80211_RC_PID=y |
69 | # CONFIG_MAC80211_RC_MINSTREL is not set | 64 | # CONFIG_MAC80211_RC_MINSTREL is not set |
@@ -181,7 +176,6 @@ CONFIG_DEBUG_INFO=y | |||
181 | CONFIG_DEBUG_WRITECOUNT=y | 176 | CONFIG_DEBUG_WRITECOUNT=y |
182 | CONFIG_DEBUG_MEMORY_INIT=y | 177 | CONFIG_DEBUG_MEMORY_INIT=y |
183 | CONFIG_DEBUG_LIST=y | 178 | CONFIG_DEBUG_LIST=y |
184 | CONFIG_SYSCTL_SYSCALL_CHECK=y | ||
185 | # CONFIG_FTRACE is not set | 179 | # CONFIG_FTRACE is not set |
186 | CONFIG_DEBUG_STACKOVERFLOW=y | 180 | CONFIG_DEBUG_STACKOVERFLOW=y |
187 | CONFIG_CRYPTO_CCM=m | 181 | CONFIG_CRYPTO_CCM=m |
diff --git a/arch/powerpc/include/asm/asm-compat.h b/arch/powerpc/include/asm/asm-compat.h index decad950f11a..5d7fbe1950f9 100644 --- a/arch/powerpc/include/asm/asm-compat.h +++ b/arch/powerpc/include/asm/asm-compat.h | |||
@@ -29,18 +29,9 @@ | |||
29 | #define PPC_LLARX(t, a, b, eh) PPC_LDARX(t, a, b, eh) | 29 | #define PPC_LLARX(t, a, b, eh) PPC_LDARX(t, a, b, eh) |
30 | #define PPC_STLCX stringify_in_c(stdcx.) | 30 | #define PPC_STLCX stringify_in_c(stdcx.) |
31 | #define PPC_CNTLZL stringify_in_c(cntlzd) | 31 | #define PPC_CNTLZL stringify_in_c(cntlzd) |
32 | #define PPC_MTOCRF(FXM, RS) MTOCRF((FXM), (RS)) | ||
32 | #define PPC_LR_STKOFF 16 | 33 | #define PPC_LR_STKOFF 16 |
33 | #define PPC_MIN_STKFRM 112 | 34 | #define PPC_MIN_STKFRM 112 |
34 | |||
35 | /* Move to CR, single-entry optimized version. Only available | ||
36 | * on POWER4 and later. | ||
37 | */ | ||
38 | #ifdef CONFIG_POWER4_ONLY | ||
39 | #define PPC_MTOCRF stringify_in_c(mtocrf) | ||
40 | #else | ||
41 | #define PPC_MTOCRF stringify_in_c(mtcrf) | ||
42 | #endif | ||
43 | |||
44 | #else /* 32-bit */ | 35 | #else /* 32-bit */ |
45 | 36 | ||
46 | /* operations for longs and pointers */ | 37 | /* operations for longs and pointers */ |
diff --git a/arch/powerpc/include/asm/cputhreads.h b/arch/powerpc/include/asm/cputhreads.h index ce516e5eb0d3..ac3eedb9b74a 100644 --- a/arch/powerpc/include/asm/cputhreads.h +++ b/arch/powerpc/include/asm/cputhreads.h | |||
@@ -9,7 +9,7 @@ | |||
9 | * Note: This implementation is limited to a power of 2 number of | 9 | * Note: This implementation is limited to a power of 2 number of |
10 | * threads per core and the same number for each core in the system | 10 | * threads per core and the same number for each core in the system |
11 | * (though it would work if some processors had less threads as long | 11 | * (though it would work if some processors had less threads as long |
12 | * as the CPU numbers are still allocated, just not brought offline). | 12 | * as the CPU numbers are still allocated, just not brought online). |
13 | * | 13 | * |
14 | * However, the API allows for a different implementation in the future | 14 | * However, the API allows for a different implementation in the future |
15 | * if needed, as long as you only use the functions and not the variables | 15 | * if needed, as long as you only use the functions and not the variables |
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 1c324ff55ea8..612252388190 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h | |||
@@ -77,8 +77,27 @@ | |||
77 | #define H_MR_CONDITION -43 | 77 | #define H_MR_CONDITION -43 |
78 | #define H_NOT_ENOUGH_RESOURCES -44 | 78 | #define H_NOT_ENOUGH_RESOURCES -44 |
79 | #define H_R_STATE -45 | 79 | #define H_R_STATE -45 |
80 | #define H_RESCINDEND -46 | 80 | #define H_RESCINDED -46 |
81 | #define H_MULTI_THREADS_ACTIVE -9005 | 81 | #define H_P2 -55 |
82 | #define H_P3 -56 | ||
83 | #define H_P4 -57 | ||
84 | #define H_P5 -58 | ||
85 | #define H_P6 -59 | ||
86 | #define H_P7 -60 | ||
87 | #define H_P8 -61 | ||
88 | #define H_P9 -62 | ||
89 | #define H_TOO_BIG -64 | ||
90 | #define H_OVERLAP -68 | ||
91 | #define H_INTERRUPT -69 | ||
92 | #define H_BAD_DATA -70 | ||
93 | #define H_NOT_ACTIVE -71 | ||
94 | #define H_SG_LIST -72 | ||
95 | #define H_OP_MODE -73 | ||
96 | #define H_COP_HW -74 | ||
97 | #define H_UNSUPPORTED_FLAG_START -256 | ||
98 | #define H_UNSUPPORTED_FLAG_END -511 | ||
99 | #define H_MULTI_THREADS_ACTIVE -9005 | ||
100 | #define H_OUTSTANDING_COP_OPS -9006 | ||
82 | 101 | ||
83 | 102 | ||
84 | /* Long Busy is a condition that can be returned by the firmware | 103 | /* Long Busy is a condition that can be returned by the firmware |
@@ -240,6 +259,8 @@ | |||
240 | #define H_GET_MPP 0x2D4 | 259 | #define H_GET_MPP 0x2D4 |
241 | #define H_HOME_NODE_ASSOCIATIVITY 0x2EC | 260 | #define H_HOME_NODE_ASSOCIATIVITY 0x2EC |
242 | #define H_BEST_ENERGY 0x2F4 | 261 | #define H_BEST_ENERGY 0x2F4 |
262 | #define H_RANDOM 0x300 | ||
263 | #define H_COP 0x304 | ||
243 | #define H_GET_MPP_X 0x314 | 264 | #define H_GET_MPP_X 0x314 |
244 | #define MAX_HCALL_OPCODE H_GET_MPP_X | 265 | #define MAX_HCALL_OPCODE H_GET_MPP_X |
245 | 266 | ||
diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h index a76254af0aaa..531fe0c3108f 100644 --- a/arch/powerpc/include/asm/lppaca.h +++ b/arch/powerpc/include/asm/lppaca.h | |||
@@ -20,18 +20,16 @@ | |||
20 | #define _ASM_POWERPC_LPPACA_H | 20 | #define _ASM_POWERPC_LPPACA_H |
21 | #ifdef __KERNEL__ | 21 | #ifdef __KERNEL__ |
22 | 22 | ||
23 | /* These definitions relate to hypervisors that only exist when using | 23 | /* |
24 | * These definitions relate to hypervisors that only exist when using | ||
24 | * a server type processor | 25 | * a server type processor |
25 | */ | 26 | */ |
26 | #ifdef CONFIG_PPC_BOOK3S | 27 | #ifdef CONFIG_PPC_BOOK3S |
27 | 28 | ||
28 | //============================================================================= | 29 | /* |
29 | // | 30 | * This control block contains the data that is shared between the |
30 | // This control block contains the data that is shared between the | 31 | * hypervisor and the OS. |
31 | // hypervisor (PLIC) and the OS. | 32 | */ |
32 | // | ||
33 | // | ||
34 | //---------------------------------------------------------------------------- | ||
35 | #include <linux/cache.h> | 33 | #include <linux/cache.h> |
36 | #include <linux/threads.h> | 34 | #include <linux/threads.h> |
37 | #include <asm/types.h> | 35 | #include <asm/types.h> |
@@ -43,123 +41,65 @@ | |||
43 | */ | 41 | */ |
44 | #define NR_LPPACAS 1 | 42 | #define NR_LPPACAS 1 |
45 | 43 | ||
46 | 44 | /* | |
47 | /* The Hypervisor barfs if the lppaca crosses a page boundary. A 1k | 45 | * The Hypervisor barfs if the lppaca crosses a page boundary. A 1k |
48 | * alignment is sufficient to prevent this */ | 46 | * alignment is sufficient to prevent this |
47 | */ | ||
49 | struct lppaca { | 48 | struct lppaca { |
50 | //============================================================================= | 49 | /* cacheline 1 contains read-only data */ |
51 | // CACHE_LINE_1 0x0000 - 0x007F Contains read-only data | 50 | |
52 | // NOTE: The xDynXyz fields are fields that will be dynamically changed by | 51 | u32 desc; /* Eye catcher 0xD397D781 */ |
53 | // PLIC when preparing to bring a processor online or when dispatching a | 52 | u16 size; /* Size of this struct */ |
54 | // virtual processor! | 53 | u16 reserved1; |
55 | //============================================================================= | 54 | u16 reserved2:14; |
56 | u32 desc; // Eye catcher 0xD397D781 x00-x03 | 55 | u8 shared_proc:1; /* Shared processor indicator */ |
57 | u16 size; // Size of this struct x04-x05 | 56 | u8 secondary_thread:1; /* Secondary thread indicator */ |
58 | u16 reserved1; // Reserved x06-x07 | 57 | u8 reserved3[14]; |
59 | u16 reserved2:14; // Reserved x08-x09 | 58 | volatile u32 dyn_hw_node_id; /* Dynamic hardware node id */ |
60 | u8 shared_proc:1; // Shared processor indicator ... | 59 | volatile u32 dyn_hw_proc_id; /* Dynamic hardware proc id */ |
61 | u8 secondary_thread:1; // Secondary thread indicator ... | 60 | u8 reserved4[56]; |
62 | volatile u8 dyn_proc_status:8; // Dynamic Status of this proc x0A-x0A | 61 | volatile u8 vphn_assoc_counts[8]; /* Virtual processor home node */ |
63 | u8 secondary_thread_count; // Secondary thread count x0B-x0B | 62 | /* associativity change counters */ |
64 | volatile u16 dyn_hv_phys_proc_index;// Dynamic HV Physical Proc Index0C-x0D | 63 | u8 reserved5[32]; |
65 | volatile u16 dyn_hv_log_proc_index;// Dynamic HV Logical Proc Indexx0E-x0F | 64 | |
66 | u32 decr_val; // Value for Decr programming x10-x13 | 65 | /* cacheline 2 contains local read-write data */ |
67 | u32 pmc_val; // Value for PMC regs x14-x17 | 66 | |
68 | volatile u32 dyn_hw_node_id; // Dynamic Hardware Node id x18-x1B | 67 | u8 reserved6[48]; |
69 | volatile u32 dyn_hw_proc_id; // Dynamic Hardware Proc Id x1C-x1F | 68 | u8 cede_latency_hint; |
70 | volatile u32 dyn_pir; // Dynamic ProcIdReg value x20-x23 | 69 | u8 reserved7[7]; |
71 | u32 dsei_data; // DSEI data x24-x27 | 70 | u8 dtl_enable_mask; /* Dispatch Trace Log mask */ |
72 | u64 sprg3; // SPRG3 value x28-x2F | 71 | u8 donate_dedicated_cpu; /* Donate dedicated CPU cycles */ |
73 | u8 reserved3[40]; // Reserved x30-x57 | 72 | u8 fpregs_in_use; |
74 | volatile u8 vphn_assoc_counts[8]; // Virtual processor home node | 73 | u8 pmcregs_in_use; |
75 | // associativity change counters x58-x5F | 74 | u8 reserved8[28]; |
76 | u8 reserved4[32]; // Reserved x60-x7F | 75 | u64 wait_state_cycles; /* Wait cycles for this proc */ |
77 | 76 | u8 reserved9[28]; | |
78 | //============================================================================= | 77 | u16 slb_count; /* # of SLBs to maintain */ |
79 | // CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data | 78 | u8 idle; /* Indicate OS is idle */ |
80 | //============================================================================= | 79 | u8 vmxregs_in_use; |
81 | // This Dword contains a byte for each type of interrupt that can occur. | 80 | |
82 | // The IPI is a count while the others are just a binary 1 or 0. | 81 | /* cacheline 3 is shared with other processors */ |
83 | union { | 82 | |
84 | u64 any_int; | 83 | /* |
85 | struct { | 84 | * This is the yield_count. An "odd" value (low bit on) means that |
86 | u16 reserved; // Reserved - cleared by #mpasmbl | 85 | * the processor is yielded (either because of an OS yield or a |
87 | u8 xirr_int; // Indicates xXirrValue is valid or Immed IO | 86 | * hypervisor preempt). An even value implies that the processor is |
88 | u8 ipi_cnt; // IPI Count | 87 | * currently executing. |
89 | u8 decr_int; // DECR interrupt occurred | 88 | * NOTE: This value will ALWAYS be zero for dedicated processors and |
90 | u8 pdc_int; // PDC interrupt occurred | 89 | * will NEVER be zero for shared processors (ie, initialized to a 1). |
91 | u8 quantum_int; // Interrupt quantum reached | 90 | */ |
92 | u8 old_plic_deferred_ext_int; // Old PLIC has a deferred XIRR pending | 91 | volatile u32 yield_count; |
93 | } fields; | 92 | volatile u32 dispersion_count; /* dispatch changed physical cpu */ |
94 | } int_dword; | 93 | volatile u64 cmo_faults; /* CMO page fault count */ |
95 | 94 | volatile u64 cmo_fault_time; /* CMO page fault time */ | |
96 | // Whenever any fields in this Dword are set then PLIC will defer the | 95 | u8 reserved10[104]; |
97 | // processing of external interrupts. Note that PLIC will store the | 96 | |
98 | // XIRR directly into the xXirrValue field so that another XIRR will | 97 | /* cacheline 4-5 */ |
99 | // not be presented until this one clears. The layout of the low | 98 | |
100 | // 4-bytes of this Dword is up to SLIC - PLIC just checks whether the | 99 | u32 page_ins; /* CMO Hint - # page ins by OS */ |
101 | // entire Dword is zero or not. A non-zero value in the low order | 100 | u8 reserved11[148]; |
102 | // 2-bytes will result in SLIC being granted the highest thread | 101 | volatile u64 dtl_idx; /* Dispatch Trace Log head index */ |
103 | // priority upon return. A 0 will return to SLIC as medium priority. | 102 | u8 reserved12[96]; |
104 | u64 plic_defer_ints_area; // Entire Dword | ||
105 | |||
106 | // Used to pass the real SRR0/1 from PLIC to SLIC as well as to | ||
107 | // pass the target SRR0/1 from SLIC to PLIC on a SetAsrAndRfid. | ||
108 | u64 saved_srr0; // Saved SRR0 x10-x17 | ||
109 | u64 saved_srr1; // Saved SRR1 x18-x1F | ||
110 | |||
111 | // Used to pass parms from the OS to PLIC for SetAsrAndRfid | ||
112 | u64 saved_gpr3; // Saved GPR3 x20-x27 | ||
113 | u64 saved_gpr4; // Saved GPR4 x28-x2F | ||
114 | union { | ||
115 | u64 saved_gpr5; /* Saved GPR5 x30-x37 */ | ||
116 | struct { | ||
117 | u8 cede_latency_hint; /* x30 */ | ||
118 | u8 reserved[7]; /* x31-x36 */ | ||
119 | } fields; | ||
120 | } gpr5_dword; | ||
121 | |||
122 | |||
123 | u8 dtl_enable_mask; // Dispatch Trace Log mask x38-x38 | ||
124 | u8 donate_dedicated_cpu; // Donate dedicated CPU cycles x39-x39 | ||
125 | u8 fpregs_in_use; // FP regs in use x3A-x3A | ||
126 | u8 pmcregs_in_use; // PMC regs in use x3B-x3B | ||
127 | volatile u32 saved_decr; // Saved Decr Value x3C-x3F | ||
128 | volatile u64 emulated_time_base;// Emulated TB for this thread x40-x47 | ||
129 | volatile u64 cur_plic_latency; // Unaccounted PLIC latency x48-x4F | ||
130 | u64 tot_plic_latency; // Accumulated PLIC latency x50-x57 | ||
131 | u64 wait_state_cycles; // Wait cycles for this proc x58-x5F | ||
132 | u64 end_of_quantum; // TB at end of quantum x60-x67 | ||
133 | u64 pdc_saved_sprg1; // Saved SPRG1 for PMC int x68-x6F | ||
134 | u64 pdc_saved_srr0; // Saved SRR0 for PMC int x70-x77 | ||
135 | volatile u32 virtual_decr; // Virtual DECR for shared procsx78-x7B | ||
136 | u16 slb_count; // # of SLBs to maintain x7C-x7D | ||
137 | u8 idle; // Indicate OS is idle x7E | ||
138 | u8 vmxregs_in_use; // VMX registers in use x7F | ||
139 | |||
140 | |||
141 | //============================================================================= | ||
142 | // CACHE_LINE_3 0x0100 - 0x017F: This line is shared with other processors | ||
143 | //============================================================================= | ||
144 | // This is the yield_count. An "odd" value (low bit on) means that | ||
145 | // the processor is yielded (either because of an OS yield or a PLIC | ||
146 | // preempt). An even value implies that the processor is currently | ||
147 | // executing. | ||
148 | // NOTE: This value will ALWAYS be zero for dedicated processors and | ||
149 | // will NEVER be zero for shared processors (ie, initialized to a 1). | ||
150 | volatile u32 yield_count; // PLIC increments each dispatchx00-x03 | ||
151 | volatile u32 dispersion_count; // dispatch changed phys cpu x04-x07 | ||
152 | volatile u64 cmo_faults; // CMO page fault count x08-x0F | ||
153 | volatile u64 cmo_fault_time; // CMO page fault time x10-x17 | ||
154 | u8 reserved7[104]; // Reserved x18-x7F | ||
155 | |||
156 | //============================================================================= | ||
157 | // CACHE_LINE_4-5 0x0180 - 0x027F Contains PMC interrupt data | ||
158 | //============================================================================= | ||
159 | u32 page_ins; // CMO Hint - # page ins by OS x00-x03 | ||
160 | u8 reserved8[148]; // Reserved x04-x97 | ||
161 | volatile u64 dtl_idx; // Dispatch Trace Log head idx x98-x9F | ||
162 | u8 reserved9[96]; // Reserved xA0-xFF | ||
163 | } __attribute__((__aligned__(0x400))); | 103 | } __attribute__((__aligned__(0x400))); |
164 | 104 | ||
165 | extern struct lppaca lppaca[]; | 105 | extern struct lppaca lppaca[]; |
@@ -172,13 +112,13 @@ extern struct lppaca lppaca[]; | |||
172 | * ESID is stored in the lower 64bits, then the VSID. | 112 | * ESID is stored in the lower 64bits, then the VSID. |
173 | */ | 113 | */ |
174 | struct slb_shadow { | 114 | struct slb_shadow { |
175 | u32 persistent; // Number of persistent SLBs x00-x03 | 115 | u32 persistent; /* Number of persistent SLBs */ |
176 | u32 buffer_length; // Total shadow buffer length x04-x07 | 116 | u32 buffer_length; /* Total shadow buffer length */ |
177 | u64 reserved; // Alignment x08-x0f | 117 | u64 reserved; |
178 | struct { | 118 | struct { |
179 | u64 esid; | 119 | u64 esid; |
180 | u64 vsid; | 120 | u64 vsid; |
181 | } save_area[SLB_NUM_BOLTED]; // x10-x40 | 121 | } save_area[SLB_NUM_BOLTED]; |
182 | } ____cacheline_aligned; | 122 | } ____cacheline_aligned; |
183 | 123 | ||
184 | extern struct slb_shadow slb_shadow[]; | 124 | extern struct slb_shadow slb_shadow[]; |
diff --git a/arch/powerpc/include/asm/lv1call.h b/arch/powerpc/include/asm/lv1call.h index 233f9ecae761..f5117674bf92 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 ) | |||
265 | LV1_CALL(set_spe_privilege_state_area_1_register, 3, 0, 79 ) | 265 | LV1_CALL(set_spe_privilege_state_area_1_register, 3, 0, 79 ) |
266 | LV1_CALL(create_repository_node, 6, 0, 90 ) | 266 | LV1_CALL(create_repository_node, 6, 0, 90 ) |
267 | LV1_CALL(read_repository_node, 5, 2, 91 ) | 267 | LV1_CALL(read_repository_node, 5, 2, 91 ) |
268 | LV1_CALL(modify_repository_node_value, 6, 0, 92 ) | 268 | LV1_CALL(write_repository_node, 6, 0, 92 ) |
269 | LV1_CALL(remove_repository_node, 4, 0, 93 ) | 269 | LV1_CALL(delete_repository_node, 4, 0, 93 ) |
270 | LV1_CALL(read_htab_entries, 2, 5, 95 ) | 270 | LV1_CALL(read_htab_entries, 2, 5, 95 ) |
271 | LV1_CALL(set_dabr, 2, 0, 96 ) | 271 | LV1_CALL(set_dabr, 2, 0, 96 ) |
272 | LV1_CALL(get_total_execution_time, 2, 1, 103 ) | 272 | LV1_CALL(get_total_execution_time, 2, 1, 103 ) |
diff --git a/arch/powerpc/include/asm/pSeries_reconfig.h b/arch/powerpc/include/asm/pSeries_reconfig.h index 23cd6cc30bcf..c07edfe98b98 100644 --- a/arch/powerpc/include/asm/pSeries_reconfig.h +++ b/arch/powerpc/include/asm/pSeries_reconfig.h | |||
@@ -13,6 +13,18 @@ | |||
13 | #define PSERIES_RECONFIG_REMOVE 0x0002 | 13 | #define PSERIES_RECONFIG_REMOVE 0x0002 |
14 | #define PSERIES_DRCONF_MEM_ADD 0x0003 | 14 | #define PSERIES_DRCONF_MEM_ADD 0x0003 |
15 | #define PSERIES_DRCONF_MEM_REMOVE 0x0004 | 15 | #define PSERIES_DRCONF_MEM_REMOVE 0x0004 |
16 | #define PSERIES_UPDATE_PROPERTY 0x0005 | ||
17 | |||
18 | /** | ||
19 | * pSeries_reconfig_notify - Notifier value structure for OFDT property updates | ||
20 | * | ||
21 | * @node: Device tree node which owns the property being updated | ||
22 | * @property: Updated property | ||
23 | */ | ||
24 | struct pSeries_reconfig_prop_update { | ||
25 | struct device_node *node; | ||
26 | struct property *property; | ||
27 | }; | ||
16 | 28 | ||
17 | #ifdef CONFIG_PPC_PSERIES | 29 | #ifdef CONFIG_PPC_PSERIES |
18 | extern int pSeries_reconfig_notifier_register(struct notifier_block *); | 30 | extern int pSeries_reconfig_notifier_register(struct notifier_block *); |
diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 50f73aa2ba21..15444204a3a1 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h | |||
@@ -369,7 +369,15 @@ BEGIN_FTR_SECTION \ | |||
369 | END_FTR_SECTION_IFCLR(CPU_FTR_601) | 369 | END_FTR_SECTION_IFCLR(CPU_FTR_601) |
370 | #endif | 370 | #endif |
371 | 371 | ||
372 | 372 | #ifdef CONFIG_PPC64 | |
373 | #define MTOCRF(FXM, RS) \ | ||
374 | BEGIN_FTR_SECTION_NESTED(848); \ | ||
375 | mtcrf (FXM), (RS); \ | ||
376 | FTR_SECTION_ELSE_NESTED(848); \ | ||
377 | mtocrf (FXM), (RS); \ | ||
378 | ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_NOEXECUTE, 848) | ||
379 | #endif | ||
380 | |||
373 | /* | 381 | /* |
374 | * This instruction is not implemented on the PPC 603 or 601; however, on | 382 | * This instruction is not implemented on the PPC 603 or 601; however, on |
375 | * the 403GCX and 405GP tlbia IS defined and tlbie is not. | 383 | * 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 84cc7840cd18..9c21ed42aba6 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, | |||
354 | #define PTRACE_GETREGS64 22 | 354 | #define PTRACE_GETREGS64 22 |
355 | #define PTRACE_SETREGS64 23 | 355 | #define PTRACE_SETREGS64 23 |
356 | 356 | ||
357 | /* (old) PTRACE requests with inverted arguments */ | ||
358 | #define PPC_PTRACE_GETREGS 0x99 /* Get GPRs 0 - 31 */ | ||
359 | #define PPC_PTRACE_SETREGS 0x98 /* Set GPRs 0 - 31 */ | ||
360 | #define PPC_PTRACE_GETFPREGS 0x97 /* Get FPRs 0 - 31 */ | ||
361 | #define PPC_PTRACE_SETFPREGS 0x96 /* Set FPRs 0 - 31 */ | ||
362 | |||
363 | /* Calls to trace a 64bit program from a 32bit program */ | 357 | /* Calls to trace a 64bit program from a 32bit program */ |
364 | #define PPC_PTRACE_PEEKTEXT_3264 0x95 | 358 | #define PPC_PTRACE_PEEKTEXT_3264 0x95 |
365 | #define PPC_PTRACE_PEEKDATA_3264 0x94 | 359 | #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 caf82d0a00de..1a6320290d26 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); | |||
21 | extern void enable_kernel_fp(void); | 21 | extern void enable_kernel_fp(void); |
22 | extern void flush_fp_to_thread(struct task_struct *); | 22 | extern void flush_fp_to_thread(struct task_struct *); |
23 | extern void enable_kernel_altivec(void); | 23 | extern void enable_kernel_altivec(void); |
24 | extern void giveup_altivec(struct task_struct *); | ||
25 | extern void load_up_altivec(struct task_struct *); | 24 | extern void load_up_altivec(struct task_struct *); |
26 | extern int emulate_altivec(struct pt_regs *); | 25 | extern int emulate_altivec(struct pt_regs *); |
27 | extern void __giveup_vsx(struct task_struct *); | 26 | extern void __giveup_vsx(struct task_struct *); |
@@ -40,10 +39,15 @@ static inline void discard_lazy_cpu_state(void) | |||
40 | 39 | ||
41 | #ifdef CONFIG_ALTIVEC | 40 | #ifdef CONFIG_ALTIVEC |
42 | extern void flush_altivec_to_thread(struct task_struct *); | 41 | extern void flush_altivec_to_thread(struct task_struct *); |
42 | extern void giveup_altivec(struct task_struct *); | ||
43 | extern void giveup_altivec_notask(void); | ||
43 | #else | 44 | #else |
44 | static inline void flush_altivec_to_thread(struct task_struct *t) | 45 | static inline void flush_altivec_to_thread(struct task_struct *t) |
45 | { | 46 | { |
46 | } | 47 | } |
48 | static inline void giveup_altivec(struct task_struct *t) | ||
49 | { | ||
50 | } | ||
47 | #endif | 51 | #endif |
48 | 52 | ||
49 | #ifdef CONFIG_VSX | 53 | #ifdef CONFIG_VSX |
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index 1a1bb00f061a..a556ccc16b58 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h | |||
@@ -113,7 +113,6 @@ static inline struct thread_info *current_thread_info(void) | |||
113 | #define _TIF_NOERROR (1<<TIF_NOERROR) | 113 | #define _TIF_NOERROR (1<<TIF_NOERROR) |
114 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) | 114 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) |
115 | #define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) | 115 | #define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) |
116 | #define _TIF_RUNLATCH (1<<TIF_RUNLATCH) | ||
117 | #define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ | 116 | #define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ |
118 | _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT) | 117 | _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT) |
119 | 118 | ||
diff --git a/arch/powerpc/include/asm/vio.h b/arch/powerpc/include/asm/vio.h index 6bfd5ffe1d4f..b19adf751dd9 100644 --- a/arch/powerpc/include/asm/vio.h +++ b/arch/powerpc/include/asm/vio.h | |||
@@ -46,6 +46,48 @@ | |||
46 | 46 | ||
47 | struct iommu_table; | 47 | struct iommu_table; |
48 | 48 | ||
49 | /* | ||
50 | * Platform Facilities Option (PFO)-specific data | ||
51 | */ | ||
52 | |||
53 | /* Starting unit address for PFO devices on the VIO BUS */ | ||
54 | #define VIO_BASE_PFO_UA 0x50000000 | ||
55 | |||
56 | /** | ||
57 | * vio_pfo_op - PFO operation parameters | ||
58 | * | ||
59 | * @flags: h_call subfunctions and modifiers | ||
60 | * @in: Input data block logical real address | ||
61 | * @inlen: If non-negative, the length of the input data block. If negative, | ||
62 | * the length of the input data descriptor list in bytes. | ||
63 | * @out: Output data block logical real address | ||
64 | * @outlen: If non-negative, the length of the input data block. If negative, | ||
65 | * the length of the input data descriptor list in bytes. | ||
66 | * @csbcpb: Logical real address of the 4k naturally-aligned storage block | ||
67 | * containing the CSB & optional FC field specific CPB | ||
68 | * @timeout: # of milliseconds to retry h_call, 0 for no timeout. | ||
69 | * @hcall_err: pointer to return the h_call return value, else NULL | ||
70 | */ | ||
71 | struct vio_pfo_op { | ||
72 | u64 flags; | ||
73 | s64 in; | ||
74 | s64 inlen; | ||
75 | s64 out; | ||
76 | s64 outlen; | ||
77 | u64 csbcpb; | ||
78 | void *done; | ||
79 | unsigned long handle; | ||
80 | unsigned int timeout; | ||
81 | long hcall_err; | ||
82 | }; | ||
83 | |||
84 | /* End PFO specific data */ | ||
85 | |||
86 | enum vio_dev_family { | ||
87 | VDEVICE, /* The OF node is a child of /vdevice */ | ||
88 | PFO, /* The OF node is a child of /ibm,platform-facilities */ | ||
89 | }; | ||
90 | |||
49 | /** | 91 | /** |
50 | * vio_dev - This structure is used to describe virtual I/O devices. | 92 | * vio_dev - This structure is used to describe virtual I/O devices. |
51 | * | 93 | * |
@@ -58,6 +100,7 @@ struct vio_dev { | |||
58 | const char *name; | 100 | const char *name; |
59 | const char *type; | 101 | const char *type; |
60 | uint32_t unit_address; | 102 | uint32_t unit_address; |
103 | uint32_t resource_id; | ||
61 | unsigned int irq; | 104 | unsigned int irq; |
62 | struct { | 105 | struct { |
63 | size_t desired; | 106 | size_t desired; |
@@ -65,6 +108,7 @@ struct vio_dev { | |||
65 | size_t allocated; | 108 | size_t allocated; |
66 | atomic_t allocs_failed; | 109 | atomic_t allocs_failed; |
67 | } cmo; | 110 | } cmo; |
111 | enum vio_dev_family family; | ||
68 | struct device dev; | 112 | struct device dev; |
69 | }; | 113 | }; |
70 | 114 | ||
@@ -95,6 +139,8 @@ extern void vio_cmo_set_dev_desired(struct vio_dev *viodev, size_t desired); | |||
95 | 139 | ||
96 | extern void __devinit vio_unregister_device(struct vio_dev *dev); | 140 | extern void __devinit vio_unregister_device(struct vio_dev *dev); |
97 | 141 | ||
142 | extern int vio_h_cop_sync(struct vio_dev *vdev, struct vio_pfo_op *op); | ||
143 | |||
98 | struct device_node; | 144 | struct device_node; |
99 | 145 | ||
100 | extern struct vio_dev *vio_register_device_node( | 146 | extern struct vio_dev *vio_register_device_node( |
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 34b8afe94a50..4554dc2fe857 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c | |||
@@ -188,10 +188,6 @@ int main(void) | |||
188 | DEFINE(SLBSHADOW_STACKESID, | 188 | DEFINE(SLBSHADOW_STACKESID, |
189 | offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].esid)); | 189 | offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].esid)); |
190 | DEFINE(SLBSHADOW_SAVEAREA, offsetof(struct slb_shadow, save_area)); | 190 | DEFINE(SLBSHADOW_SAVEAREA, offsetof(struct slb_shadow, save_area)); |
191 | DEFINE(LPPACASRR0, offsetof(struct lppaca, saved_srr0)); | ||
192 | DEFINE(LPPACASRR1, offsetof(struct lppaca, saved_srr1)); | ||
193 | DEFINE(LPPACAANYINT, offsetof(struct lppaca, int_dword.any_int)); | ||
194 | DEFINE(LPPACADECRINT, offsetof(struct lppaca, int_dword.fields.decr_int)); | ||
195 | DEFINE(LPPACA_PMCINUSE, offsetof(struct lppaca, pmcregs_in_use)); | 191 | DEFINE(LPPACA_PMCINUSE, offsetof(struct lppaca, pmcregs_in_use)); |
196 | DEFINE(LPPACA_DTLIDX, offsetof(struct lppaca, dtl_idx)); | 192 | DEFINE(LPPACA_DTLIDX, offsetof(struct lppaca, dtl_idx)); |
197 | DEFINE(LPPACA_YIELDCOUNT, offsetof(struct lppaca, yield_count)); | 193 | 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 ef2074c3e906..ed1718feb9d9 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S | |||
@@ -63,15 +63,9 @@ system_call_common: | |||
63 | std r0,GPR0(r1) | 63 | std r0,GPR0(r1) |
64 | std r10,GPR1(r1) | 64 | std r10,GPR1(r1) |
65 | ACCOUNT_CPU_USER_ENTRY(r10, r11) | 65 | ACCOUNT_CPU_USER_ENTRY(r10, r11) |
66 | /* | ||
67 | * This "crclr so" clears CR0.SO, which is the error indication on | ||
68 | * return from this system call. There must be no cmp instruction | ||
69 | * between it and the "mfcr r9" below, otherwise if XER.SO is set, | ||
70 | * CR0.SO will get set, causing all system calls to appear to fail. | ||
71 | */ | ||
72 | crclr so | ||
73 | std r2,GPR2(r1) | 66 | std r2,GPR2(r1) |
74 | std r3,GPR3(r1) | 67 | std r3,GPR3(r1) |
68 | mfcr r2 | ||
75 | std r4,GPR4(r1) | 69 | std r4,GPR4(r1) |
76 | std r5,GPR5(r1) | 70 | std r5,GPR5(r1) |
77 | std r6,GPR6(r1) | 71 | std r6,GPR6(r1) |
@@ -82,18 +76,20 @@ system_call_common: | |||
82 | std r11,GPR10(r1) | 76 | std r11,GPR10(r1) |
83 | std r11,GPR11(r1) | 77 | std r11,GPR11(r1) |
84 | std r11,GPR12(r1) | 78 | std r11,GPR12(r1) |
79 | std r11,_XER(r1) | ||
80 | std r11,_CTR(r1) | ||
85 | std r9,GPR13(r1) | 81 | std r9,GPR13(r1) |
86 | mfcr r9 | ||
87 | mflr r10 | 82 | mflr r10 |
83 | /* | ||
84 | * This clears CR0.SO (bit 28), which is the error indication on | ||
85 | * return from this system call. | ||
86 | */ | ||
87 | rldimi r2,r11,28,(63-28) | ||
88 | li r11,0xc01 | 88 | li r11,0xc01 |
89 | std r9,_CCR(r1) | ||
90 | std r10,_LINK(r1) | 89 | std r10,_LINK(r1) |
91 | std r11,_TRAP(r1) | 90 | std r11,_TRAP(r1) |
92 | mfxer r9 | ||
93 | mfctr r10 | ||
94 | std r9,_XER(r1) | ||
95 | std r10,_CTR(r1) | ||
96 | std r3,ORIG_GPR3(r1) | 91 | std r3,ORIG_GPR3(r1) |
92 | std r2,_CCR(r1) | ||
97 | ld r2,PACATOC(r13) | 93 | ld r2,PACATOC(r13) |
98 | addi r9,r1,STACK_FRAME_OVERHEAD | 94 | addi r9,r1,STACK_FRAME_OVERHEAD |
99 | ld r11,exception_marker@toc(r2) | 95 | ld r11,exception_marker@toc(r2) |
@@ -154,7 +150,7 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) | |||
154 | ld r10,TI_FLAGS(r11) | 150 | ld r10,TI_FLAGS(r11) |
155 | andi. r11,r10,_TIF_SYSCALL_T_OR_A | 151 | andi. r11,r10,_TIF_SYSCALL_T_OR_A |
156 | bne- syscall_dotrace | 152 | bne- syscall_dotrace |
157 | syscall_dotrace_cont: | 153 | .Lsyscall_dotrace_cont: |
158 | cmpldi 0,r0,NR_syscalls | 154 | cmpldi 0,r0,NR_syscalls |
159 | bge- syscall_enosys | 155 | bge- syscall_enosys |
160 | 156 | ||
@@ -211,7 +207,7 @@ syscall_exit: | |||
211 | cmpld r3,r11 | 207 | cmpld r3,r11 |
212 | ld r5,_CCR(r1) | 208 | ld r5,_CCR(r1) |
213 | bge- syscall_error | 209 | bge- syscall_error |
214 | syscall_error_cont: | 210 | .Lsyscall_error_cont: |
215 | ld r7,_NIP(r1) | 211 | ld r7,_NIP(r1) |
216 | BEGIN_FTR_SECTION | 212 | BEGIN_FTR_SECTION |
217 | stdcx. r0,0,r1 /* to clear the reservation */ | 213 | stdcx. r0,0,r1 /* to clear the reservation */ |
@@ -246,7 +242,7 @@ syscall_error: | |||
246 | oris r5,r5,0x1000 /* Set SO bit in CR */ | 242 | oris r5,r5,0x1000 /* Set SO bit in CR */ |
247 | neg r3,r3 | 243 | neg r3,r3 |
248 | std r5,_CCR(r1) | 244 | std r5,_CCR(r1) |
249 | b syscall_error_cont | 245 | b .Lsyscall_error_cont |
250 | 246 | ||
251 | /* Traced system call support */ | 247 | /* Traced system call support */ |
252 | syscall_dotrace: | 248 | syscall_dotrace: |
@@ -268,7 +264,7 @@ syscall_dotrace: | |||
268 | addi r9,r1,STACK_FRAME_OVERHEAD | 264 | addi r9,r1,STACK_FRAME_OVERHEAD |
269 | clrrdi r10,r1,THREAD_SHIFT | 265 | clrrdi r10,r1,THREAD_SHIFT |
270 | ld r10,TI_FLAGS(r10) | 266 | ld r10,TI_FLAGS(r10) |
271 | b syscall_dotrace_cont | 267 | b .Lsyscall_dotrace_cont |
272 | 268 | ||
273 | syscall_enosys: | 269 | syscall_enosys: |
274 | li r3,-ENOSYS | 270 | li r3,-ENOSYS |
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 8f880bc77c56..f7bed44ee165 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S | |||
@@ -94,12 +94,10 @@ machine_check_pSeries_1: | |||
94 | data_access_pSeries: | 94 | data_access_pSeries: |
95 | HMT_MEDIUM | 95 | HMT_MEDIUM |
96 | SET_SCRATCH0(r13) | 96 | SET_SCRATCH0(r13) |
97 | #ifndef CONFIG_POWER4_ONLY | ||
98 | BEGIN_FTR_SECTION | 97 | BEGIN_FTR_SECTION |
99 | b data_access_check_stab | 98 | b data_access_check_stab |
100 | data_access_not_stab: | 99 | data_access_not_stab: |
101 | END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) | 100 | END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) |
102 | #endif | ||
103 | EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, data_access_common, EXC_STD, | 101 | EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, data_access_common, EXC_STD, |
104 | KVMTEST, 0x300) | 102 | KVMTEST, 0x300) |
105 | 103 | ||
@@ -301,7 +299,6 @@ machine_check_fwnmi: | |||
301 | EXC_STD, KVMTEST, 0x200) | 299 | EXC_STD, KVMTEST, 0x200) |
302 | KVM_HANDLER_SKIP(PACA_EXMC, EXC_STD, 0x200) | 300 | KVM_HANDLER_SKIP(PACA_EXMC, EXC_STD, 0x200) |
303 | 301 | ||
304 | #ifndef CONFIG_POWER4_ONLY | ||
305 | /* moved from 0x300 */ | 302 | /* moved from 0x300 */ |
306 | data_access_check_stab: | 303 | data_access_check_stab: |
307 | GET_PACA(r13) | 304 | GET_PACA(r13) |
@@ -328,7 +325,6 @@ do_stab_bolted_pSeries: | |||
328 | GET_SCRATCH0(r10) | 325 | GET_SCRATCH0(r10) |
329 | std r10,PACA_EXSLB+EX_R13(r13) | 326 | std r10,PACA_EXSLB+EX_R13(r13) |
330 | EXCEPTION_PROLOG_PSERIES_1(.do_stab_bolted, EXC_STD) | 327 | EXCEPTION_PROLOG_PSERIES_1(.do_stab_bolted, EXC_STD) |
331 | #endif /* CONFIG_POWER4_ONLY */ | ||
332 | 328 | ||
333 | KVM_HANDLER_SKIP(PACA_EXGEN, EXC_STD, 0x300) | 329 | KVM_HANDLER_SKIP(PACA_EXGEN, EXC_STD, 0x300) |
334 | KVM_HANDLER_SKIP(PACA_EXSLB, EXC_STD, 0x380) | 330 | 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 7dd2981bcc50..22d608e8bb7d 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S | |||
@@ -778,14 +778,6 @@ _GLOBAL(__fixup_440A_mcheck) | |||
778 | blr | 778 | blr |
779 | 779 | ||
780 | /* | 780 | /* |
781 | * extern void giveup_altivec(struct task_struct *prev) | ||
782 | * | ||
783 | * The 44x core does not have an AltiVec unit. | ||
784 | */ | ||
785 | _GLOBAL(giveup_altivec) | ||
786 | blr | ||
787 | |||
788 | /* | ||
789 | * extern void giveup_fpu(struct task_struct *prev) | 781 | * extern void giveup_fpu(struct task_struct *prev) |
790 | * | 782 | * |
791 | * The 44x core does not have an FPU. | 783 | * 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 28e62598d0e8..de80e0f9a2bd 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) | |||
874 | sync | 874 | sync |
875 | blr | 875 | blr |
876 | 876 | ||
877 | /* | ||
878 | * extern void giveup_altivec(struct task_struct *prev) | ||
879 | * | ||
880 | * The e500 core does not have an AltiVec unit. | ||
881 | */ | ||
882 | _GLOBAL(giveup_altivec) | ||
883 | blr | ||
884 | |||
885 | #ifdef CONFIG_SPE | 877 | #ifdef CONFIG_SPE |
886 | /* | 878 | /* |
887 | * extern void giveup_spe(struct task_struct *prev) | 879 | * extern void giveup_spe(struct task_struct *prev) |
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 641da9e868ce..7835a5e1ea5f 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -587,7 +587,7 @@ int irq_choose_cpu(const struct cpumask *mask) | |||
587 | { | 587 | { |
588 | int cpuid; | 588 | int cpuid; |
589 | 589 | ||
590 | if (cpumask_equal(mask, cpu_all_mask)) { | 590 | if (cpumask_equal(mask, cpu_online_mask)) { |
591 | static int irq_rover; | 591 | static int irq_rover; |
592 | static DEFINE_RAW_SPINLOCK(irq_rover_lock); | 592 | static DEFINE_RAW_SPINLOCK(irq_rover_lock); |
593 | unsigned long flags; | 593 | unsigned long flags; |
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 7cd07b42ca1a..386d57f66f28 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S | |||
@@ -738,8 +738,23 @@ relocate_new_kernel: | |||
738 | mr r5, r31 | 738 | mr r5, r31 |
739 | 739 | ||
740 | li r0, 0 | 740 | li r0, 0 |
741 | #elif defined(CONFIG_44x) && !defined(CONFIG_PPC_47x) | 741 | #elif defined(CONFIG_44x) |
742 | 742 | ||
743 | /* Save our parameters */ | ||
744 | mr r29, r3 | ||
745 | mr r30, r4 | ||
746 | mr r31, r5 | ||
747 | |||
748 | #ifdef CONFIG_PPC_47x | ||
749 | /* Check for 47x cores */ | ||
750 | mfspr r3,SPRN_PVR | ||
751 | srwi r3,r3,16 | ||
752 | cmplwi cr0,r3,PVR_476@h | ||
753 | beq setup_map_47x | ||
754 | cmplwi cr0,r3,PVR_476_ISS@h | ||
755 | beq setup_map_47x | ||
756 | #endif /* CONFIG_PPC_47x */ | ||
757 | |||
743 | /* | 758 | /* |
744 | * Code for setting up 1:1 mapping for PPC440x for KEXEC | 759 | * Code for setting up 1:1 mapping for PPC440x for KEXEC |
745 | * | 760 | * |
@@ -753,16 +768,15 @@ relocate_new_kernel: | |||
753 | * 5) Invalidate the tmp mapping. | 768 | * 5) Invalidate the tmp mapping. |
754 | * | 769 | * |
755 | * - Based on the kexec support code for FSL BookE | 770 | * - Based on the kexec support code for FSL BookE |
756 | * - Doesn't support 47x yet. | ||
757 | * | 771 | * |
758 | */ | 772 | */ |
759 | /* Save our parameters */ | ||
760 | mr r29, r3 | ||
761 | mr r30, r4 | ||
762 | mr r31, r5 | ||
763 | 773 | ||
764 | /* Load our MSR_IS and TID to MMUCR for TLB search */ | 774 | /* |
765 | mfspr r3,SPRN_PID | 775 | * Load the PID with kernel PID (0). |
776 | * Also load our MSR_IS and TID to MMUCR for TLB search. | ||
777 | */ | ||
778 | li r3, 0 | ||
779 | mtspr SPRN_PID, r3 | ||
766 | mfmsr r4 | 780 | mfmsr r4 |
767 | andi. r4,r4,MSR_IS@l | 781 | andi. r4,r4,MSR_IS@l |
768 | beq wmmucr | 782 | beq wmmucr |
@@ -900,6 +914,179 @@ next_tlb: | |||
900 | li r3, 0 | 914 | li r3, 0 |
901 | tlbwe r3, r24, PPC44x_TLB_PAGEID | 915 | tlbwe r3, r24, PPC44x_TLB_PAGEID |
902 | sync | 916 | sync |
917 | b ppc44x_map_done | ||
918 | |||
919 | #ifdef CONFIG_PPC_47x | ||
920 | |||
921 | /* 1:1 mapping for 47x */ | ||
922 | |||
923 | setup_map_47x: | ||
924 | |||
925 | /* | ||
926 | * Load the kernel pid (0) to PID and also to MMUCR[TID]. | ||
927 | * Also set the MSR IS->MMUCR STS | ||
928 | */ | ||
929 | li r3, 0 | ||
930 | mtspr SPRN_PID, r3 /* Set PID */ | ||
931 | mfmsr r4 /* Get MSR */ | ||
932 | andi. r4, r4, MSR_IS@l /* TS=1? */ | ||
933 | beq 1f /* If not, leave STS=0 */ | ||
934 | oris r3, r3, PPC47x_MMUCR_STS@h /* Set STS=1 */ | ||
935 | 1: mtspr SPRN_MMUCR, r3 /* Put MMUCR */ | ||
936 | sync | ||
937 | |||
938 | /* Find the entry we are running from */ | ||
939 | bl 2f | ||
940 | 2: mflr r23 | ||
941 | tlbsx r23, 0, r23 | ||
942 | tlbre r24, r23, 0 /* TLB Word 0 */ | ||
943 | tlbre r25, r23, 1 /* TLB Word 1 */ | ||
944 | tlbre r26, r23, 2 /* TLB Word 2 */ | ||
945 | |||
946 | |||
947 | /* | ||
948 | * Invalidates all the tlb entries by writing to 256 RPNs(r4) | ||
949 | * of 4k page size in all 4 ways (0-3 in r3). | ||
950 | * This would invalidate the entire UTLB including the one we are | ||
951 | * running from. However the shadow TLB entries would help us | ||
952 | * to continue the execution, until we flush them (rfi/isync). | ||
953 | */ | ||
954 | addis r3, 0, 0x8000 /* specify the way */ | ||
955 | addi r4, 0, 0 /* TLB Word0 = (EPN=0, VALID = 0) */ | ||
956 | addi r5, 0, 0 | ||
957 | b clear_utlb_entry | ||
958 | |||
959 | /* Align the loop to speed things up. from head_44x.S */ | ||
960 | .align 6 | ||
961 | |||
962 | clear_utlb_entry: | ||
963 | |||
964 | tlbwe r4, r3, 0 | ||
965 | tlbwe r5, r3, 1 | ||
966 | tlbwe r5, r3, 2 | ||
967 | addis r3, r3, 0x2000 /* Increment the way */ | ||
968 | cmpwi r3, 0 | ||
969 | bne clear_utlb_entry | ||
970 | addis r3, 0, 0x8000 | ||
971 | addis r4, r4, 0x100 /* Increment the EPN */ | ||
972 | cmpwi r4, 0 | ||
973 | bne clear_utlb_entry | ||
974 | |||
975 | /* Create the entries in the other address space */ | ||
976 | mfmsr r5 | ||
977 | rlwinm r7, r5, 27, 31, 31 /* Get the TS (Bit 26) from MSR */ | ||
978 | xori r7, r7, 1 /* r7 = !TS */ | ||
979 | |||
980 | insrwi r24, r7, 1, 21 /* Change the TS in the saved TLB word 0 */ | ||
981 | |||
982 | /* | ||
983 | * write out the TLB entries for the tmp mapping | ||
984 | * Use way '0' so that we could easily invalidate it later. | ||
985 | */ | ||
986 | lis r3, 0x8000 /* Way '0' */ | ||
987 | |||
988 | tlbwe r24, r3, 0 | ||
989 | tlbwe r25, r3, 1 | ||
990 | tlbwe r26, r3, 2 | ||
991 | |||
992 | /* Update the msr to the new TS */ | ||
993 | insrwi r5, r7, 1, 26 | ||
994 | |||
995 | bl 1f | ||
996 | 1: mflr r6 | ||
997 | addi r6, r6, (2f-1b) | ||
998 | |||
999 | mtspr SPRN_SRR0, r6 | ||
1000 | mtspr SPRN_SRR1, r5 | ||
1001 | rfi | ||
1002 | |||
1003 | /* | ||
1004 | * Now we are in the tmp address space. | ||
1005 | * Create a 1:1 mapping for 0-2GiB in the original TS. | ||
1006 | */ | ||
1007 | 2: | ||
1008 | li r3, 0 | ||
1009 | li r4, 0 /* TLB Word 0 */ | ||
1010 | li r5, 0 /* TLB Word 1 */ | ||
1011 | li r6, 0 | ||
1012 | ori r6, r6, PPC47x_TLB2_S_RWX /* TLB word 2 */ | ||
1013 | |||
1014 | li r8, 0 /* PageIndex */ | ||
1015 | |||
1016 | xori r7, r7, 1 /* revert back to original TS */ | ||
1017 | |||
1018 | write_utlb: | ||
1019 | rotlwi r5, r8, 28 /* RPN = PageIndex * 256M */ | ||
1020 | /* ERPN = 0 as we don't use memory above 2G */ | ||
1021 | |||
1022 | mr r4, r5 /* EPN = RPN */ | ||
1023 | ori r4, r4, (PPC47x_TLB0_VALID | PPC47x_TLB0_256M) | ||
1024 | insrwi r4, r7, 1, 21 /* Insert the TS to Word 0 */ | ||
1025 | |||
1026 | tlbwe r4, r3, 0 /* Write out the entries */ | ||
1027 | tlbwe r5, r3, 1 | ||
1028 | tlbwe r6, r3, 2 | ||
1029 | addi r8, r8, 1 | ||
1030 | cmpwi r8, 8 /* Have we completed ? */ | ||
1031 | bne write_utlb | ||
1032 | |||
1033 | /* make sure we complete the TLB write up */ | ||
1034 | isync | ||
1035 | |||
1036 | /* | ||
1037 | * Prepare to jump to the 1:1 mapping. | ||
1038 | * 1) Extract page size of the tmp mapping | ||
1039 | * DSIZ = TLB_Word0[22:27] | ||
1040 | * 2) Calculate the physical address of the address | ||
1041 | * to jump to. | ||
1042 | */ | ||
1043 | rlwinm r10, r24, 0, 22, 27 | ||
1044 | |||
1045 | cmpwi r10, PPC47x_TLB0_4K | ||
1046 | bne 0f | ||
1047 | li r10, 0x1000 /* r10 = 4k */ | ||
1048 | bl 1f | ||
1049 | |||
1050 | 0: | ||
1051 | /* Defaults to 256M */ | ||
1052 | lis r10, 0x1000 | ||
1053 | |||
1054 | bl 1f | ||
1055 | 1: mflr r4 | ||
1056 | addi r4, r4, (2f-1b) /* virtual address of 2f */ | ||
1057 | |||
1058 | subi r11, r10, 1 /* offsetmask = Pagesize - 1 */ | ||
1059 | not r10, r11 /* Pagemask = ~(offsetmask) */ | ||
1060 | |||
1061 | and r5, r25, r10 /* Physical page */ | ||
1062 | and r6, r4, r11 /* offset within the current page */ | ||
1063 | |||
1064 | or r5, r5, r6 /* Physical address for 2f */ | ||
1065 | |||
1066 | /* Switch the TS in MSR to the original one */ | ||
1067 | mfmsr r8 | ||
1068 | insrwi r8, r7, 1, 26 | ||
1069 | |||
1070 | mtspr SPRN_SRR1, r8 | ||
1071 | mtspr SPRN_SRR0, r5 | ||
1072 | rfi | ||
1073 | |||
1074 | 2: | ||
1075 | /* Invalidate the tmp mapping */ | ||
1076 | lis r3, 0x8000 /* Way '0' */ | ||
1077 | |||
1078 | clrrwi r24, r24, 12 /* Clear the valid bit */ | ||
1079 | tlbwe r24, r3, 0 | ||
1080 | tlbwe r25, r3, 1 | ||
1081 | tlbwe r26, r3, 2 | ||
1082 | |||
1083 | /* Make sure we complete the TLB write and flush the shadow TLB */ | ||
1084 | isync | ||
1085 | |||
1086 | #endif | ||
1087 | |||
1088 | ppc44x_map_done: | ||
1089 | |||
903 | 1090 | ||
904 | /* Restore the parameters */ | 1091 | /* Restore the parameters */ |
905 | mr r3, r29 | 1092 | mr r3, r29 |
diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 0bb1f98613ba..fbe1a12dc7f1 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c | |||
@@ -36,10 +36,7 @@ struct lppaca lppaca[] = { | |||
36 | [0 ... (NR_LPPACAS-1)] = { | 36 | [0 ... (NR_LPPACAS-1)] = { |
37 | .desc = 0xd397d781, /* "LpPa" */ | 37 | .desc = 0xd397d781, /* "LpPa" */ |
38 | .size = sizeof(struct lppaca), | 38 | .size = sizeof(struct lppaca), |
39 | .dyn_proc_status = 2, | ||
40 | .decr_val = 0x00ff0000, | ||
41 | .fpregs_in_use = 1, | 39 | .fpregs_in_use = 1, |
42 | .end_of_quantum = 0xfffffffffffffffful, | ||
43 | .slb_count = 64, | 40 | .slb_count = 64, |
44 | .vmxregs_in_use = 0, | 41 | .vmxregs_in_use = 0, |
45 | .page_ins = 0, | 42 | .page_ins = 0, |
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index aa05935b6947..7f8ec1de0ace 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
@@ -124,7 +124,7 @@ void enable_kernel_altivec(void) | |||
124 | if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) | 124 | if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) |
125 | giveup_altivec(current); | 125 | giveup_altivec(current); |
126 | else | 126 | else |
127 | giveup_altivec(NULL); /* just enable AltiVec for kernel - force */ | 127 | giveup_altivec_notask(); |
128 | #else | 128 | #else |
129 | giveup_altivec(last_task_used_altivec); | 129 | giveup_altivec(last_task_used_altivec); |
130 | #endif /* CONFIG_SMP */ | 130 | #endif /* CONFIG_SMP */ |
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 99860273211b..1b488e5305c5 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c | |||
@@ -680,6 +680,9 @@ static void __init early_cmdline_parse(void) | |||
680 | #define OV3_VMX 0x40 /* VMX/Altivec */ | 680 | #define OV3_VMX 0x40 /* VMX/Altivec */ |
681 | #define OV3_DFP 0x20 /* decimal FP */ | 681 | #define OV3_DFP 0x20 /* decimal FP */ |
682 | 682 | ||
683 | /* Option vector 4: IBM PAPR implementation */ | ||
684 | #define OV4_MIN_ENT_CAP 0x01 /* minimum VP entitled capacity */ | ||
685 | |||
683 | /* Option vector 5: PAPR/OF options supported */ | 686 | /* Option vector 5: PAPR/OF options supported */ |
684 | #define OV5_LPAR 0x80 /* logical partitioning supported */ | 687 | #define OV5_LPAR 0x80 /* logical partitioning supported */ |
685 | #define OV5_SPLPAR 0x40 /* shared-processor LPAR supported */ | 688 | #define OV5_SPLPAR 0x40 /* shared-processor LPAR supported */ |
@@ -701,6 +704,8 @@ static void __init early_cmdline_parse(void) | |||
701 | #define OV5_XCMO 0x00 | 704 | #define OV5_XCMO 0x00 |
702 | #endif | 705 | #endif |
703 | #define OV5_TYPE1_AFFINITY 0x80 /* Type 1 NUMA affinity */ | 706 | #define OV5_TYPE1_AFFINITY 0x80 /* Type 1 NUMA affinity */ |
707 | #define OV5_PFO_HW_RNG 0x80 /* PFO Random Number Generator */ | ||
708 | #define OV5_PFO_HW_ENCR 0x20 /* PFO Encryption Accelerator */ | ||
704 | 709 | ||
705 | /* Option Vector 6: IBM PAPR hints */ | 710 | /* Option Vector 6: IBM PAPR hints */ |
706 | #define OV6_LINUX 0x02 /* Linux is our OS */ | 711 | #define OV6_LINUX 0x02 /* Linux is our OS */ |
@@ -744,11 +749,12 @@ static unsigned char ibm_architecture_vec[] = { | |||
744 | OV3_FP | OV3_VMX | OV3_DFP, | 749 | OV3_FP | OV3_VMX | OV3_DFP, |
745 | 750 | ||
746 | /* option vector 4: IBM PAPR implementation */ | 751 | /* option vector 4: IBM PAPR implementation */ |
747 | 2 - 2, /* length */ | 752 | 3 - 2, /* length */ |
748 | 0, /* don't halt */ | 753 | 0, /* don't halt */ |
754 | OV4_MIN_ENT_CAP, /* minimum VP entitled capacity */ | ||
749 | 755 | ||
750 | /* option vector 5: PAPR/OF options */ | 756 | /* option vector 5: PAPR/OF options */ |
751 | 13 - 2, /* length */ | 757 | 18 - 2, /* length */ |
752 | 0, /* don't ignore, don't halt */ | 758 | 0, /* don't ignore, don't halt */ |
753 | OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | | 759 | OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | |
754 | OV5_DONATE_DEDICATE_CPU | OV5_MSI, | 760 | OV5_DONATE_DEDICATE_CPU | OV5_MSI, |
@@ -762,8 +768,13 @@ static unsigned char ibm_architecture_vec[] = { | |||
762 | * must match by the macro below. Update the definition if | 768 | * must match by the macro below. Update the definition if |
763 | * the structure layout changes. | 769 | * the structure layout changes. |
764 | */ | 770 | */ |
765 | #define IBM_ARCH_VEC_NRCORES_OFFSET 100 | 771 | #define IBM_ARCH_VEC_NRCORES_OFFSET 101 |
766 | W(NR_CPUS), /* number of cores supported */ | 772 | W(NR_CPUS), /* number of cores supported */ |
773 | 0, | ||
774 | 0, | ||
775 | 0, | ||
776 | 0, | ||
777 | OV5_PFO_HW_RNG | OV5_PFO_HW_ENCR, | ||
767 | 778 | ||
768 | /* option vector 6: IBM PAPR hints */ | 779 | /* option vector 6: IBM PAPR hints */ |
769 | 4 - 2, /* length */ | 780 | 4 - 2, /* length */ |
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index dd5e214cdf21..c10fc28b9092 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) | |||
1432 | #endif | 1432 | #endif |
1433 | } | 1433 | } |
1434 | 1434 | ||
1435 | /* | ||
1436 | * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, | ||
1437 | * we mark them as obsolete now, they will be removed in a future version | ||
1438 | */ | ||
1439 | static long arch_ptrace_old(struct task_struct *child, long request, | ||
1440 | unsigned long addr, unsigned long data) | ||
1441 | { | ||
1442 | void __user *datavp = (void __user *) data; | ||
1443 | |||
1444 | switch (request) { | ||
1445 | case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ | ||
1446 | return copy_regset_to_user(child, &user_ppc_native_view, | ||
1447 | REGSET_GPR, 0, 32 * sizeof(long), | ||
1448 | datavp); | ||
1449 | |||
1450 | case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ | ||
1451 | return copy_regset_from_user(child, &user_ppc_native_view, | ||
1452 | REGSET_GPR, 0, 32 * sizeof(long), | ||
1453 | datavp); | ||
1454 | |||
1455 | case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ | ||
1456 | return copy_regset_to_user(child, &user_ppc_native_view, | ||
1457 | REGSET_FPR, 0, 32 * sizeof(double), | ||
1458 | datavp); | ||
1459 | |||
1460 | case PPC_PTRACE_SETFPREGS: /* Set FPRs 0 - 31. */ | ||
1461 | return copy_regset_from_user(child, &user_ppc_native_view, | ||
1462 | REGSET_FPR, 0, 32 * sizeof(double), | ||
1463 | datavp); | ||
1464 | } | ||
1465 | |||
1466 | return -EPERM; | ||
1467 | } | ||
1468 | |||
1469 | long arch_ptrace(struct task_struct *child, long request, | 1435 | long arch_ptrace(struct task_struct *child, long request, |
1470 | unsigned long addr, unsigned long data) | 1436 | unsigned long addr, unsigned long data) |
1471 | { | 1437 | { |
@@ -1687,14 +1653,6 @@ long arch_ptrace(struct task_struct *child, long request, | |||
1687 | datavp); | 1653 | datavp); |
1688 | #endif | 1654 | #endif |
1689 | 1655 | ||
1690 | /* Old reverse args ptrace callss */ | ||
1691 | case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ | ||
1692 | case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ | ||
1693 | case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ | ||
1694 | case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */ | ||
1695 | ret = arch_ptrace_old(child, request, addr, data); | ||
1696 | break; | ||
1697 | |||
1698 | default: | 1656 | default: |
1699 | ret = ptrace_request(child, request, addr, data); | 1657 | ret = ptrace_request(child, request, addr, data); |
1700 | break; | 1658 | break; |
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 469349d14a97..8c21658719d9 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c | |||
@@ -39,30 +39,6 @@ | |||
39 | * in exit.c or in signal.c. | 39 | * in exit.c or in signal.c. |
40 | */ | 40 | */ |
41 | 41 | ||
42 | /* | ||
43 | * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls, | ||
44 | * we mark them as obsolete now, they will be removed in a future version | ||
45 | */ | ||
46 | static long compat_ptrace_old(struct task_struct *child, long request, | ||
47 | long addr, long data) | ||
48 | { | ||
49 | switch (request) { | ||
50 | case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ | ||
51 | return copy_regset_to_user(child, | ||
52 | task_user_regset_view(current), 0, | ||
53 | 0, 32 * sizeof(compat_long_t), | ||
54 | compat_ptr(data)); | ||
55 | |||
56 | case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ | ||
57 | return copy_regset_from_user(child, | ||
58 | task_user_regset_view(current), 0, | ||
59 | 0, 32 * sizeof(compat_long_t), | ||
60 | compat_ptr(data)); | ||
61 | } | ||
62 | |||
63 | return -EPERM; | ||
64 | } | ||
65 | |||
66 | /* Macros to workout the correct index for the FPR in the thread struct */ | 42 | /* Macros to workout the correct index for the FPR in the thread struct */ |
67 | #define FPRNUMBER(i) (((i) - PT_FPR0) >> 1) | 43 | #define FPRNUMBER(i) (((i) - PT_FPR0) >> 1) |
68 | #define FPRHALF(i) (((i) - PT_FPR0) & 1) | 44 | #define FPRHALF(i) (((i) - PT_FPR0) & 1) |
@@ -308,8 +284,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
308 | case PTRACE_SETVSRREGS: | 284 | case PTRACE_SETVSRREGS: |
309 | case PTRACE_GETREGS64: | 285 | case PTRACE_GETREGS64: |
310 | case PTRACE_SETREGS64: | 286 | case PTRACE_SETREGS64: |
311 | case PPC_PTRACE_GETFPREGS: | ||
312 | case PPC_PTRACE_SETFPREGS: | ||
313 | case PTRACE_KILL: | 287 | case PTRACE_KILL: |
314 | case PTRACE_SINGLESTEP: | 288 | case PTRACE_SINGLESTEP: |
315 | case PTRACE_DETACH: | 289 | case PTRACE_DETACH: |
@@ -322,12 +296,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
322 | ret = arch_ptrace(child, request, addr, data); | 296 | ret = arch_ptrace(child, request, addr, data); |
323 | break; | 297 | break; |
324 | 298 | ||
325 | /* Old reverse args ptrace callss */ | ||
326 | case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ | ||
327 | case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ | ||
328 | ret = compat_ptrace_old(child, request, addr, data); | ||
329 | break; | ||
330 | |||
331 | default: | 299 | default: |
332 | ret = compat_ptrace_request(child, request, addr, data); | 300 | ret = compat_ptrace_request(child, request, addr, data); |
333 | break; | 301 | break; |
diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index 4d5a3edff49e..e830289d2e48 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S | |||
@@ -89,6 +89,16 @@ _GLOBAL(load_up_altivec) | |||
89 | /* restore registers and return */ | 89 | /* restore registers and return */ |
90 | blr | 90 | blr |
91 | 91 | ||
92 | _GLOBAL(giveup_altivec_notask) | ||
93 | mfmsr r3 | ||
94 | andis. r4,r3,MSR_VEC@h | ||
95 | bnelr /* Already enabled? */ | ||
96 | oris r3,r3,MSR_VEC@h | ||
97 | SYNC | ||
98 | MTMSRD(r3) /* enable use of VMX now */ | ||
99 | isync | ||
100 | blr | ||
101 | |||
92 | /* | 102 | /* |
93 | * giveup_altivec(tsk) | 103 | * giveup_altivec(tsk) |
94 | * Disable VMX for the task given as the argument, | 104 | * Disable VMX for the task given as the argument, |
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index a3a99901c8ec..cb87301ccd55 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c | |||
@@ -14,7 +14,9 @@ | |||
14 | * 2 of the License, or (at your option) any later version. | 14 | * 2 of the License, or (at your option) any later version. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/cpu.h> | ||
17 | #include <linux/types.h> | 18 | #include <linux/types.h> |
19 | #include <linux/delay.h> | ||
18 | #include <linux/stat.h> | 20 | #include <linux/stat.h> |
19 | #include <linux/device.h> | 21 | #include <linux/device.h> |
20 | #include <linux/init.h> | 22 | #include <linux/init.h> |
@@ -709,13 +711,26 @@ static int vio_cmo_bus_probe(struct vio_dev *viodev) | |||
709 | struct vio_driver *viodrv = to_vio_driver(dev->driver); | 711 | struct vio_driver *viodrv = to_vio_driver(dev->driver); |
710 | unsigned long flags; | 712 | unsigned long flags; |
711 | size_t size; | 713 | size_t size; |
714 | bool dma_capable = false; | ||
715 | |||
716 | /* A device requires entitlement if it has a DMA window property */ | ||
717 | switch (viodev->family) { | ||
718 | case VDEVICE: | ||
719 | if (of_get_property(viodev->dev.of_node, | ||
720 | "ibm,my-dma-window", NULL)) | ||
721 | dma_capable = true; | ||
722 | break; | ||
723 | case PFO: | ||
724 | dma_capable = false; | ||
725 | break; | ||
726 | default: | ||
727 | dev_warn(dev, "unknown device family: %d\n", viodev->family); | ||
728 | BUG(); | ||
729 | break; | ||
730 | } | ||
712 | 731 | ||
713 | /* | 732 | /* Configure entitlement for the device. */ |
714 | * Check to see that device has a DMA window and configure | 733 | if (dma_capable) { |
715 | * entitlement for the device. | ||
716 | */ | ||
717 | if (of_get_property(viodev->dev.of_node, | ||
718 | "ibm,my-dma-window", NULL)) { | ||
719 | /* Check that the driver is CMO enabled and get desired DMA */ | 734 | /* Check that the driver is CMO enabled and get desired DMA */ |
720 | if (!viodrv->get_desired_dma) { | 735 | if (!viodrv->get_desired_dma) { |
721 | dev_err(dev, "%s: device driver does not support CMO\n", | 736 | dev_err(dev, "%s: device driver does not support CMO\n", |
@@ -1050,6 +1065,94 @@ static void vio_cmo_sysfs_init(void) { } | |||
1050 | EXPORT_SYMBOL(vio_cmo_entitlement_update); | 1065 | EXPORT_SYMBOL(vio_cmo_entitlement_update); |
1051 | EXPORT_SYMBOL(vio_cmo_set_dev_desired); | 1066 | EXPORT_SYMBOL(vio_cmo_set_dev_desired); |
1052 | 1067 | ||
1068 | |||
1069 | /* | ||
1070 | * Platform Facilities Option (PFO) support | ||
1071 | */ | ||
1072 | |||
1073 | /** | ||
1074 | * vio_h_cop_sync - Perform a synchronous PFO co-processor operation | ||
1075 | * | ||
1076 | * @vdev - Pointer to a struct vio_dev for device | ||
1077 | * @op - Pointer to a struct vio_pfo_op for the operation parameters | ||
1078 | * | ||
1079 | * Calls the hypervisor to synchronously perform the PFO operation | ||
1080 | * described in @op. In the case of a busy response from the hypervisor, | ||
1081 | * the operation will be re-submitted indefinitely unless a non-zero timeout | ||
1082 | * is specified or an error occurs. The timeout places a limit on when to | ||
1083 | * stop re-submitting a operation, the total time can be exceeded if an | ||
1084 | * operation is in progress. | ||
1085 | * | ||
1086 | * If op->hcall_ret is not NULL, this will be set to the return from the | ||
1087 | * last h_cop_op call or it will be 0 if an error not involving the h_call | ||
1088 | * was encountered. | ||
1089 | * | ||
1090 | * Returns: | ||
1091 | * 0 on success, | ||
1092 | * -EINVAL if the h_call fails due to an invalid parameter, | ||
1093 | * -E2BIG if the h_call can not be performed synchronously, | ||
1094 | * -EBUSY if a timeout is specified and has elapsed, | ||
1095 | * -EACCES if the memory area for data/status has been rescinded, or | ||
1096 | * -EPERM if a hardware fault has been indicated | ||
1097 | */ | ||
1098 | int vio_h_cop_sync(struct vio_dev *vdev, struct vio_pfo_op *op) | ||
1099 | { | ||
1100 | struct device *dev = &vdev->dev; | ||
1101 | unsigned long deadline = 0; | ||
1102 | long hret = 0; | ||
1103 | int ret = 0; | ||
1104 | |||
1105 | if (op->timeout) | ||
1106 | deadline = jiffies + msecs_to_jiffies(op->timeout); | ||
1107 | |||
1108 | while (true) { | ||
1109 | hret = plpar_hcall_norets(H_COP, op->flags, | ||
1110 | vdev->resource_id, | ||
1111 | op->in, op->inlen, op->out, | ||
1112 | op->outlen, op->csbcpb); | ||
1113 | |||
1114 | if (hret == H_SUCCESS || | ||
1115 | (hret != H_NOT_ENOUGH_RESOURCES && | ||
1116 | hret != H_BUSY && hret != H_RESOURCE) || | ||
1117 | (op->timeout && time_after(deadline, jiffies))) | ||
1118 | break; | ||
1119 | |||
1120 | dev_dbg(dev, "%s: hcall ret(%ld), retrying.\n", __func__, hret); | ||
1121 | } | ||
1122 | |||
1123 | switch (hret) { | ||
1124 | case H_SUCCESS: | ||
1125 | ret = 0; | ||
1126 | break; | ||
1127 | case H_OP_MODE: | ||
1128 | case H_TOO_BIG: | ||
1129 | ret = -E2BIG; | ||
1130 | break; | ||
1131 | case H_RESCINDED: | ||
1132 | ret = -EACCES; | ||
1133 | break; | ||
1134 | case H_HARDWARE: | ||
1135 | ret = -EPERM; | ||
1136 | break; | ||
1137 | case H_NOT_ENOUGH_RESOURCES: | ||
1138 | case H_RESOURCE: | ||
1139 | case H_BUSY: | ||
1140 | ret = -EBUSY; | ||
1141 | break; | ||
1142 | default: | ||
1143 | ret = -EINVAL; | ||
1144 | break; | ||
1145 | } | ||
1146 | |||
1147 | if (ret) | ||
1148 | dev_dbg(dev, "%s: Sync h_cop_op failure (ret:%d) (hret:%ld)\n", | ||
1149 | __func__, ret, hret); | ||
1150 | |||
1151 | op->hcall_err = hret; | ||
1152 | return ret; | ||
1153 | } | ||
1154 | EXPORT_SYMBOL(vio_h_cop_sync); | ||
1155 | |||
1053 | static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) | 1156 | static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) |
1054 | { | 1157 | { |
1055 | const unsigned char *dma_window; | 1158 | const unsigned char *dma_window; |
@@ -1211,35 +1314,87 @@ static void __devinit vio_dev_release(struct device *dev) | |||
1211 | struct vio_dev *vio_register_device_node(struct device_node *of_node) | 1314 | struct vio_dev *vio_register_device_node(struct device_node *of_node) |
1212 | { | 1315 | { |
1213 | struct vio_dev *viodev; | 1316 | struct vio_dev *viodev; |
1317 | struct device_node *parent_node; | ||
1214 | const unsigned int *unit_address; | 1318 | const unsigned int *unit_address; |
1319 | const unsigned int *pfo_resid = NULL; | ||
1320 | enum vio_dev_family family; | ||
1321 | const char *of_node_name = of_node->name ? of_node->name : "<unknown>"; | ||
1215 | 1322 | ||
1216 | /* we need the 'device_type' property, in order to match with drivers */ | 1323 | /* |
1217 | if (of_node->type == NULL) { | 1324 | * Determine if this node is a under the /vdevice node or under the |
1218 | printk(KERN_WARNING "%s: node %s missing 'device_type'\n", | 1325 | * /ibm,platform-facilities node. This decides the device's family. |
1219 | __func__, | 1326 | */ |
1220 | of_node->name ? of_node->name : "<unknown>"); | 1327 | parent_node = of_get_parent(of_node); |
1328 | if (parent_node) { | ||
1329 | if (!strcmp(parent_node->full_name, "/ibm,platform-facilities")) | ||
1330 | family = PFO; | ||
1331 | else if (!strcmp(parent_node->full_name, "/vdevice")) | ||
1332 | family = VDEVICE; | ||
1333 | else { | ||
1334 | pr_warn("%s: parent(%s) of %s not recognized.\n", | ||
1335 | __func__, | ||
1336 | parent_node->full_name, | ||
1337 | of_node_name); | ||
1338 | of_node_put(parent_node); | ||
1339 | return NULL; | ||
1340 | } | ||
1341 | of_node_put(parent_node); | ||
1342 | } else { | ||
1343 | pr_warn("%s: could not determine the parent of node %s.\n", | ||
1344 | __func__, of_node_name); | ||
1221 | return NULL; | 1345 | return NULL; |
1222 | } | 1346 | } |
1223 | 1347 | ||
1224 | unit_address = of_get_property(of_node, "reg", NULL); | 1348 | if (family == PFO) { |
1225 | if (unit_address == NULL) { | 1349 | if (of_get_property(of_node, "interrupt-controller", NULL)) { |
1226 | printk(KERN_WARNING "%s: node %s missing 'reg'\n", | 1350 | pr_debug("%s: Skipping the interrupt controller %s.\n", |
1227 | __func__, | 1351 | __func__, of_node_name); |
1228 | of_node->name ? of_node->name : "<unknown>"); | 1352 | return NULL; |
1229 | return NULL; | 1353 | } |
1230 | } | 1354 | } |
1231 | 1355 | ||
1232 | /* allocate a vio_dev for this node */ | 1356 | /* allocate a vio_dev for this node */ |
1233 | viodev = kzalloc(sizeof(struct vio_dev), GFP_KERNEL); | 1357 | viodev = kzalloc(sizeof(struct vio_dev), GFP_KERNEL); |
1234 | if (viodev == NULL) | 1358 | if (viodev == NULL) { |
1359 | pr_warn("%s: allocation failure for VIO device.\n", __func__); | ||
1235 | return NULL; | 1360 | return NULL; |
1361 | } | ||
1236 | 1362 | ||
1237 | viodev->irq = irq_of_parse_and_map(of_node, 0); | 1363 | /* we need the 'device_type' property, in order to match with drivers */ |
1364 | viodev->family = family; | ||
1365 | if (viodev->family == VDEVICE) { | ||
1366 | if (of_node->type != NULL) | ||
1367 | viodev->type = of_node->type; | ||
1368 | else { | ||
1369 | pr_warn("%s: node %s is missing the 'device_type' " | ||
1370 | "property.\n", __func__, of_node_name); | ||
1371 | goto out; | ||
1372 | } | ||
1373 | |||
1374 | unit_address = of_get_property(of_node, "reg", NULL); | ||
1375 | if (unit_address == NULL) { | ||
1376 | pr_warn("%s: node %s missing 'reg'\n", | ||
1377 | __func__, of_node_name); | ||
1378 | goto out; | ||
1379 | } | ||
1380 | dev_set_name(&viodev->dev, "%x", *unit_address); | ||
1381 | viodev->irq = irq_of_parse_and_map(of_node, 0); | ||
1382 | viodev->unit_address = *unit_address; | ||
1383 | } else { | ||
1384 | /* PFO devices need their resource_id for submitting COP_OPs | ||
1385 | * This is an optional field for devices, but is required when | ||
1386 | * performing synchronous ops */ | ||
1387 | pfo_resid = of_get_property(of_node, "ibm,resource-id", NULL); | ||
1388 | if (pfo_resid != NULL) | ||
1389 | viodev->resource_id = *pfo_resid; | ||
1390 | |||
1391 | unit_address = NULL; | ||
1392 | dev_set_name(&viodev->dev, "%s", of_node_name); | ||
1393 | viodev->type = of_node_name; | ||
1394 | viodev->irq = 0; | ||
1395 | } | ||
1238 | 1396 | ||
1239 | dev_set_name(&viodev->dev, "%x", *unit_address); | ||
1240 | viodev->name = of_node->name; | 1397 | viodev->name = of_node->name; |
1241 | viodev->type = of_node->type; | ||
1242 | viodev->unit_address = *unit_address; | ||
1243 | viodev->dev.of_node = of_node_get(of_node); | 1398 | viodev->dev.of_node = of_node_get(of_node); |
1244 | 1399 | ||
1245 | if (firmware_has_feature(FW_FEATURE_CMO)) | 1400 | if (firmware_has_feature(FW_FEATURE_CMO)) |
@@ -1267,16 +1422,51 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node) | |||
1267 | } | 1422 | } |
1268 | 1423 | ||
1269 | return viodev; | 1424 | return viodev; |
1425 | |||
1426 | out: /* Use this exit point for any return prior to device_register */ | ||
1427 | kfree(viodev); | ||
1428 | |||
1429 | return NULL; | ||
1270 | } | 1430 | } |
1271 | EXPORT_SYMBOL(vio_register_device_node); | 1431 | EXPORT_SYMBOL(vio_register_device_node); |
1272 | 1432 | ||
1433 | /* | ||
1434 | * vio_bus_scan_for_devices - Scan OF and register each child device | ||
1435 | * @root_name - OF node name for the root of the subtree to search. | ||
1436 | * This must be non-NULL | ||
1437 | * | ||
1438 | * Starting from the root node provide, register the device node for | ||
1439 | * each child beneath the root. | ||
1440 | */ | ||
1441 | static void vio_bus_scan_register_devices(char *root_name) | ||
1442 | { | ||
1443 | struct device_node *node_root, *node_child; | ||
1444 | |||
1445 | if (!root_name) | ||
1446 | return; | ||
1447 | |||
1448 | node_root = of_find_node_by_name(NULL, root_name); | ||
1449 | if (node_root) { | ||
1450 | |||
1451 | /* | ||
1452 | * Create struct vio_devices for each virtual device in | ||
1453 | * the device tree. Drivers will associate with them later. | ||
1454 | */ | ||
1455 | node_child = of_get_next_child(node_root, NULL); | ||
1456 | while (node_child) { | ||
1457 | vio_register_device_node(node_child); | ||
1458 | node_child = of_get_next_child(node_root, node_child); | ||
1459 | } | ||
1460 | of_node_put(node_root); | ||
1461 | } | ||
1462 | } | ||
1463 | |||
1273 | /** | 1464 | /** |
1274 | * vio_bus_init: - Initialize the virtual IO bus | 1465 | * vio_bus_init: - Initialize the virtual IO bus |
1275 | */ | 1466 | */ |
1276 | static int __init vio_bus_init(void) | 1467 | static int __init vio_bus_init(void) |
1277 | { | 1468 | { |
1278 | int err; | 1469 | int err; |
1279 | struct device_node *node_vroot; | ||
1280 | 1470 | ||
1281 | if (firmware_has_feature(FW_FEATURE_CMO)) | 1471 | if (firmware_has_feature(FW_FEATURE_CMO)) |
1282 | vio_cmo_sysfs_init(); | 1472 | vio_cmo_sysfs_init(); |
@@ -1301,19 +1491,8 @@ static int __init vio_bus_init(void) | |||
1301 | if (firmware_has_feature(FW_FEATURE_CMO)) | 1491 | if (firmware_has_feature(FW_FEATURE_CMO)) |
1302 | vio_cmo_bus_init(); | 1492 | vio_cmo_bus_init(); |
1303 | 1493 | ||
1304 | node_vroot = of_find_node_by_name(NULL, "vdevice"); | 1494 | vio_bus_scan_register_devices("vdevice"); |
1305 | if (node_vroot) { | 1495 | vio_bus_scan_register_devices("ibm,platform-facilities"); |
1306 | struct device_node *of_node; | ||
1307 | |||
1308 | /* | ||
1309 | * Create struct vio_devices for each virtual device in | ||
1310 | * the device tree. Drivers will associate with them later. | ||
1311 | */ | ||
1312 | for (of_node = node_vroot->child; of_node != NULL; | ||
1313 | of_node = of_node->sibling) | ||
1314 | vio_register_device_node(of_node); | ||
1315 | of_node_put(node_vroot); | ||
1316 | } | ||
1317 | 1496 | ||
1318 | return 0; | 1497 | return 0; |
1319 | } | 1498 | } |
@@ -1436,12 +1615,28 @@ struct vio_dev *vio_find_node(struct device_node *vnode) | |||
1436 | { | 1615 | { |
1437 | const uint32_t *unit_address; | 1616 | const uint32_t *unit_address; |
1438 | char kobj_name[20]; | 1617 | char kobj_name[20]; |
1618 | struct device_node *vnode_parent; | ||
1619 | const char *dev_type; | ||
1620 | |||
1621 | vnode_parent = of_get_parent(vnode); | ||
1622 | if (!vnode_parent) | ||
1623 | return NULL; | ||
1624 | |||
1625 | dev_type = of_get_property(vnode_parent, "device_type", NULL); | ||
1626 | of_node_put(vnode_parent); | ||
1627 | if (!dev_type) | ||
1628 | return NULL; | ||
1439 | 1629 | ||
1440 | /* construct the kobject name from the device node */ | 1630 | /* construct the kobject name from the device node */ |
1441 | unit_address = of_get_property(vnode, "reg", NULL); | 1631 | if (!strcmp(dev_type, "vdevice")) { |
1442 | if (!unit_address) | 1632 | unit_address = of_get_property(vnode, "reg", NULL); |
1633 | if (!unit_address) | ||
1634 | return NULL; | ||
1635 | snprintf(kobj_name, sizeof(kobj_name), "%x", *unit_address); | ||
1636 | } else if (!strcmp(dev_type, "ibm,platform-facilities")) | ||
1637 | snprintf(kobj_name, sizeof(kobj_name), "%s", vnode->name); | ||
1638 | else | ||
1443 | return NULL; | 1639 | return NULL; |
1444 | snprintf(kobj_name, sizeof(kobj_name), "%x", *unit_address); | ||
1445 | 1640 | ||
1446 | return vio_find_name(kobj_name); | 1641 | return vio_find_name(kobj_name); |
1447 | } | 1642 | } |
diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S index 773d38f90aaa..d73a59014900 100644 --- a/arch/powerpc/lib/copyuser_64.S +++ b/arch/powerpc/lib/copyuser_64.S | |||
@@ -30,7 +30,7 @@ _GLOBAL(__copy_tofrom_user_base) | |||
30 | dcbt 0,r4 | 30 | dcbt 0,r4 |
31 | beq .Lcopy_page_4K | 31 | beq .Lcopy_page_4K |
32 | andi. r6,r6,7 | 32 | andi. r6,r6,7 |
33 | PPC_MTOCRF 0x01,r5 | 33 | PPC_MTOCRF(0x01,r5) |
34 | blt cr1,.Lshort_copy | 34 | blt cr1,.Lshort_copy |
35 | /* Below we want to nop out the bne if we're on a CPU that has the | 35 | /* Below we want to nop out the bne if we're on a CPU that has the |
36 | * CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit | 36 | * 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) | |||
186 | blr | 186 | blr |
187 | 187 | ||
188 | .Ldst_unaligned: | 188 | .Ldst_unaligned: |
189 | PPC_MTOCRF 0x01,r6 /* put #bytes to 8B bdry into cr7 */ | 189 | PPC_MTOCRF(0x01,r6) /* put #bytes to 8B bdry into cr7 */ |
190 | subf r5,r6,r5 | 190 | subf r5,r6,r5 |
191 | li r7,0 | 191 | li r7,0 |
192 | cmpldi cr1,r5,16 | 192 | cmpldi cr1,r5,16 |
@@ -201,7 +201,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) | |||
201 | 2: bf cr7*4+1,3f | 201 | 2: bf cr7*4+1,3f |
202 | 37: lwzx r0,r7,r4 | 202 | 37: lwzx r0,r7,r4 |
203 | 83: stwx r0,r7,r3 | 203 | 83: stwx r0,r7,r3 |
204 | 3: PPC_MTOCRF 0x01,r5 | 204 | 3: PPC_MTOCRF(0x01,r5) |
205 | add r4,r6,r4 | 205 | add r4,r6,r4 |
206 | add r3,r6,r3 | 206 | add r3,r6,r3 |
207 | b .Ldst_aligned | 207 | b .Ldst_aligned |
diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S index 11ce045e21fd..f4fcb0bc6563 100644 --- a/arch/powerpc/lib/mem_64.S +++ b/arch/powerpc/lib/mem_64.S | |||
@@ -19,7 +19,7 @@ _GLOBAL(memset) | |||
19 | rlwimi r4,r4,16,0,15 | 19 | rlwimi r4,r4,16,0,15 |
20 | cmplw cr1,r5,r0 /* do we get that far? */ | 20 | cmplw cr1,r5,r0 /* do we get that far? */ |
21 | rldimi r4,r4,32,0 | 21 | rldimi r4,r4,32,0 |
22 | PPC_MTOCRF 1,r0 | 22 | PPC_MTOCRF(1,r0) |
23 | mr r6,r3 | 23 | mr r6,r3 |
24 | blt cr1,8f | 24 | blt cr1,8f |
25 | beq+ 3f /* if already 8-byte aligned */ | 25 | beq+ 3f /* if already 8-byte aligned */ |
@@ -49,7 +49,7 @@ _GLOBAL(memset) | |||
49 | bdnz 4b | 49 | bdnz 4b |
50 | 5: srwi. r0,r5,3 | 50 | 5: srwi. r0,r5,3 |
51 | clrlwi r5,r5,29 | 51 | clrlwi r5,r5,29 |
52 | PPC_MTOCRF 1,r0 | 52 | PPC_MTOCRF(1,r0) |
53 | beq 8f | 53 | beq 8f |
54 | bf 29,6f | 54 | bf 29,6f |
55 | std r4,0(r6) | 55 | std r4,0(r6) |
@@ -65,7 +65,7 @@ _GLOBAL(memset) | |||
65 | std r4,0(r6) | 65 | std r4,0(r6) |
66 | addi r6,r6,8 | 66 | addi r6,r6,8 |
67 | 8: cmpwi r5,0 | 67 | 8: cmpwi r5,0 |
68 | PPC_MTOCRF 1,r5 | 68 | PPC_MTOCRF(1,r5) |
69 | beqlr+ | 69 | beqlr+ |
70 | bf 29,9f | 70 | bf 29,9f |
71 | stw r4,0(r6) | 71 | stw r4,0(r6) |
diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S index e178922b2c21..82fea3963e15 100644 --- a/arch/powerpc/lib/memcpy_64.S +++ b/arch/powerpc/lib/memcpy_64.S | |||
@@ -12,7 +12,7 @@ | |||
12 | .align 7 | 12 | .align 7 |
13 | _GLOBAL(memcpy) | 13 | _GLOBAL(memcpy) |
14 | std r3,48(r1) /* save destination pointer for return value */ | 14 | std r3,48(r1) /* save destination pointer for return value */ |
15 | PPC_MTOCRF 0x01,r5 | 15 | PPC_MTOCRF(0x01,r5) |
16 | cmpldi cr1,r5,16 | 16 | cmpldi cr1,r5,16 |
17 | neg r6,r3 # LS 3 bits = # bytes to 8-byte dest bdry | 17 | neg r6,r3 # LS 3 bits = # bytes to 8-byte dest bdry |
18 | andi. r6,r6,7 | 18 | andi. r6,r6,7 |
@@ -154,7 +154,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) | |||
154 | blr | 154 | blr |
155 | 155 | ||
156 | .Ldst_unaligned: | 156 | .Ldst_unaligned: |
157 | PPC_MTOCRF 0x01,r6 # put #bytes to 8B bdry into cr7 | 157 | PPC_MTOCRF(0x01,r6) # put #bytes to 8B bdry into cr7 |
158 | subf r5,r6,r5 | 158 | subf r5,r6,r5 |
159 | li r7,0 | 159 | li r7,0 |
160 | cmpldi cr1,r5,16 | 160 | cmpldi cr1,r5,16 |
@@ -169,7 +169,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD) | |||
169 | 2: bf cr7*4+1,3f | 169 | 2: bf cr7*4+1,3f |
170 | lwzx r0,r7,r4 | 170 | lwzx r0,r7,r4 |
171 | stwx r0,r7,r3 | 171 | stwx r0,r7,r3 |
172 | 3: PPC_MTOCRF 0x01,r5 | 172 | 3: PPC_MTOCRF(0x01,r5) |
173 | add r4,r6,r4 | 173 | add r4,r6,r4 |
174 | add r3,r6,r3 | 174 | add r3,r6,r3 |
175 | b .Ldst_aligned | 175 | b .Ldst_aligned |
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index 2e4e64abfab4..8abf6fb8f410 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig | |||
@@ -23,6 +23,8 @@ config BLUESTONE | |||
23 | default n | 23 | default n |
24 | select PPC44x_SIMPLE | 24 | select PPC44x_SIMPLE |
25 | select APM821xx | 25 | select APM821xx |
26 | select PCI_MSI | ||
27 | select PPC4xx_MSI | ||
26 | select PPC4xx_PCI_EXPRESS | 28 | select PPC4xx_PCI_EXPRESS |
27 | select IBM_EMAC_RGMII | 29 | select IBM_EMAC_RGMII |
28 | help | 30 | help |
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 9c80fc07384a..61c9550819a2 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype | |||
@@ -78,6 +78,36 @@ config PPC_BOOK3E_64 | |||
78 | 78 | ||
79 | endchoice | 79 | endchoice |
80 | 80 | ||
81 | choice | ||
82 | prompt "CPU selection" | ||
83 | depends on PPC64 | ||
84 | default GENERIC_CPU | ||
85 | help | ||
86 | This will create a kernel which is optimised for a particular CPU. | ||
87 | The resulting kernel may not run on other CPUs, so use this with care. | ||
88 | |||
89 | If unsure, select Generic. | ||
90 | |||
91 | config GENERIC_CPU | ||
92 | bool "Generic" | ||
93 | |||
94 | config CELL_CPU | ||
95 | bool "Cell Broadband Engine" | ||
96 | |||
97 | config POWER4_CPU | ||
98 | bool "POWER4" | ||
99 | |||
100 | config POWER5_CPU | ||
101 | bool "POWER5" | ||
102 | |||
103 | config POWER6_CPU | ||
104 | bool "POWER6" | ||
105 | |||
106 | config POWER7_CPU | ||
107 | bool "POWER7" | ||
108 | |||
109 | endchoice | ||
110 | |||
81 | config PPC_BOOK3S | 111 | config PPC_BOOK3S |
82 | def_bool y | 112 | def_bool y |
83 | depends on PPC_BOOK3S_32 || PPC_BOOK3S_64 | 113 | depends on PPC_BOOK3S_32 || PPC_BOOK3S_64 |
@@ -86,15 +116,6 @@ config PPC_BOOK3E | |||
86 | def_bool y | 116 | def_bool y |
87 | depends on PPC_BOOK3E_64 | 117 | depends on PPC_BOOK3E_64 |
88 | 118 | ||
89 | config POWER4_ONLY | ||
90 | bool "Optimize for POWER4" | ||
91 | depends on PPC64 && PPC_BOOK3S | ||
92 | default n | ||
93 | ---help--- | ||
94 | Cause the compiler to optimize for POWER4/POWER5/PPC970 processors. | ||
95 | The resulting binary will not work on POWER3 or RS64 processors | ||
96 | when compiled with binutils 2.15 or later. | ||
97 | |||
98 | config 6xx | 119 | config 6xx |
99 | def_bool y | 120 | def_bool y |
100 | depends on PPC32 && PPC_BOOK3S | 121 | depends on PPC32 && PPC_BOOK3S |
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index 03685a329d7d..fc536f2971c0 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) | |||
1503 | if (bus->platform_dev == NULL) | 1503 | if (bus->platform_dev == NULL) |
1504 | return -ENOMEM; | 1504 | return -ENOMEM; |
1505 | bus->platform_dev->dev.platform_data = bus; | 1505 | bus->platform_dev->dev.platform_data = bus; |
1506 | bus->platform_dev->dev.of_node = bus->busnode; | ||
1506 | platform_device_add(bus->platform_dev); | 1507 | platform_device_add(bus->platform_dev); |
1507 | } | 1508 | } |
1508 | 1509 | ||
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index 476d9d9b2405..46b7f0232523 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig | |||
@@ -7,7 +7,6 @@ config PPC_PS3 | |||
7 | select USB_OHCI_BIG_ENDIAN_MMIO | 7 | select USB_OHCI_BIG_ENDIAN_MMIO |
8 | select USB_ARCH_HAS_EHCI | 8 | select USB_ARCH_HAS_EHCI |
9 | select USB_EHCI_BIG_ENDIAN_MMIO | 9 | select USB_EHCI_BIG_ENDIAN_MMIO |
10 | select MEMORY_HOTPLUG | ||
11 | select PPC_PCI_CHOICE | 10 | select PPC_PCI_CHOICE |
12 | help | 11 | help |
13 | This option enables support for the Sony PS3 game console | 12 | This option enables support for the Sony PS3 game console |
@@ -74,7 +73,7 @@ config PS3_PS3AV | |||
74 | help | 73 | help |
75 | Include support for the PS3 AV Settings driver. | 74 | Include support for the PS3 AV Settings driver. |
76 | 75 | ||
77 | This support is required for graphics and sound. In | 76 | This support is required for PS3 graphics and sound. In |
78 | general, all users will say Y or M. | 77 | general, all users will say Y or M. |
79 | 78 | ||
80 | config PS3_SYS_MANAGER | 79 | config PS3_SYS_MANAGER |
@@ -85,9 +84,22 @@ config PS3_SYS_MANAGER | |||
85 | help | 84 | help |
86 | Include support for the PS3 System Manager. | 85 | Include support for the PS3 System Manager. |
87 | 86 | ||
88 | This support is required for system control. In | 87 | This support is required for PS3 system control. In |
89 | general, all users will say Y or M. | 88 | general, all users will say Y or M. |
90 | 89 | ||
90 | config PS3_REPOSITORY_WRITE | ||
91 | bool "PS3 Repository write support" if PS3_ADVANCED | ||
92 | depends on PPC_PS3 | ||
93 | default n | ||
94 | help | ||
95 | Enables support for writing to the PS3 System Repository. | ||
96 | |||
97 | This support is intended for bootloaders that need to store data | ||
98 | in the repository for later boot stages. | ||
99 | |||
100 | If in doubt, say N here and reduce the size of the kernel by a | ||
101 | small amount. | ||
102 | |||
91 | config PS3_STORAGE | 103 | config PS3_STORAGE |
92 | depends on PPC_PS3 | 104 | depends on PPC_PS3 |
93 | tristate | 105 | tristate |
@@ -122,7 +134,7 @@ config PS3_FLASH | |||
122 | 134 | ||
123 | This support is required to access the PS3 FLASH ROM, which | 135 | This support is required to access the PS3 FLASH ROM, which |
124 | contains the boot loader and some boot options. | 136 | contains the boot loader and some boot options. |
125 | In general, all users will say Y or M. | 137 | In general, PS3 OtherOS users will say Y or M. |
126 | 138 | ||
127 | As this driver needs a fixed buffer of 256 KiB of memory, it can | 139 | As this driver needs a fixed buffer of 256 KiB of memory, it can |
128 | be disabled on the kernel command line using "ps3flash=off", to | 140 | be disabled on the kernel command line using "ps3flash=off", to |
@@ -156,7 +168,7 @@ config PS3GELIC_UDBG | |||
156 | via the Ethernet port (UDP port number 18194). | 168 | via the Ethernet port (UDP port number 18194). |
157 | 169 | ||
158 | This driver uses a trivial implementation and is independent | 170 | This driver uses a trivial implementation and is independent |
159 | from the main network driver. | 171 | from the main PS3 gelic network driver. |
160 | 172 | ||
161 | If in doubt, say N here. | 173 | If in doubt, say N here. |
162 | 174 | ||
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index de2aea421707..0c9f643d9e2a 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c | |||
@@ -20,7 +20,6 @@ | |||
20 | 20 | ||
21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/export.h> | 22 | #include <linux/export.h> |
23 | #include <linux/memory_hotplug.h> | ||
24 | #include <linux/memblock.h> | 23 | #include <linux/memblock.h> |
25 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
26 | 25 | ||
@@ -79,12 +78,14 @@ enum { | |||
79 | * @base: base address | 78 | * @base: base address |
80 | * @size: size in bytes | 79 | * @size: size in bytes |
81 | * @offset: difference between base and rm.size | 80 | * @offset: difference between base and rm.size |
81 | * @destroy: flag if region should be destroyed upon shutdown | ||
82 | */ | 82 | */ |
83 | 83 | ||
84 | struct mem_region { | 84 | struct mem_region { |
85 | u64 base; | 85 | u64 base; |
86 | u64 size; | 86 | u64 size; |
87 | unsigned long offset; | 87 | unsigned long offset; |
88 | int destroy; | ||
88 | }; | 89 | }; |
89 | 90 | ||
90 | /** | 91 | /** |
@@ -96,7 +97,7 @@ struct mem_region { | |||
96 | * The HV virtual address space (vas) allows for hotplug memory regions. | 97 | * The HV virtual address space (vas) allows for hotplug memory regions. |
97 | * Memory regions can be created and destroyed in the vas at runtime. | 98 | * Memory regions can be created and destroyed in the vas at runtime. |
98 | * @rm: real mode (bootmem) region | 99 | * @rm: real mode (bootmem) region |
99 | * @r1: hotplug memory region(s) | 100 | * @r1: highmem region(s) |
100 | * | 101 | * |
101 | * ps3 addresses | 102 | * ps3 addresses |
102 | * virt_addr: a cpu 'translated' effective address | 103 | * virt_addr: a cpu 'translated' effective address |
@@ -222,10 +223,6 @@ void ps3_mm_vas_destroy(void) | |||
222 | } | 223 | } |
223 | } | 224 | } |
224 | 225 | ||
225 | /*============================================================================*/ | ||
226 | /* memory hotplug routines */ | ||
227 | /*============================================================================*/ | ||
228 | |||
229 | /** | 226 | /** |
230 | * ps3_mm_region_create - create a memory region in the vas | 227 | * ps3_mm_region_create - create a memory region in the vas |
231 | * @r: pointer to a struct mem_region to accept initialized values | 228 | * @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) | |||
262 | goto zero_region; | 259 | goto zero_region; |
263 | } | 260 | } |
264 | 261 | ||
262 | r->destroy = 1; | ||
265 | r->offset = r->base - map.rm.size; | 263 | r->offset = r->base - map.rm.size; |
266 | return result; | 264 | return result; |
267 | 265 | ||
@@ -279,7 +277,14 @@ static void ps3_mm_region_destroy(struct mem_region *r) | |||
279 | { | 277 | { |
280 | int result; | 278 | int result; |
281 | 279 | ||
280 | if (!r->destroy) { | ||
281 | pr_info("%s:%d: Not destroying high region: %llxh %llxh\n", | ||
282 | __func__, __LINE__, r->base, r->size); | ||
283 | return; | ||
284 | } | ||
285 | |||
282 | DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base); | 286 | DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base); |
287 | |||
283 | if (r->base) { | 288 | if (r->base) { |
284 | result = lv1_release_memory(r->base); | 289 | result = lv1_release_memory(r->base); |
285 | BUG_ON(result); | 290 | BUG_ON(result); |
@@ -288,50 +293,36 @@ static void ps3_mm_region_destroy(struct mem_region *r) | |||
288 | } | 293 | } |
289 | } | 294 | } |
290 | 295 | ||
291 | /** | 296 | static int ps3_mm_get_repository_highmem(struct mem_region *r) |
292 | * ps3_mm_add_memory - hot add memory | ||
293 | */ | ||
294 | |||
295 | static int __init ps3_mm_add_memory(void) | ||
296 | { | 297 | { |
297 | int result; | 298 | int result; |
298 | unsigned long start_addr; | ||
299 | unsigned long start_pfn; | ||
300 | unsigned long nr_pages; | ||
301 | |||
302 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
303 | return -ENODEV; | ||
304 | 299 | ||
305 | BUG_ON(!mem_init_done); | 300 | /* Assume a single highmem region. */ |
306 | 301 | ||
307 | start_addr = map.rm.size; | 302 | result = ps3_repository_read_highmem_info(0, &r->base, &r->size); |
308 | start_pfn = start_addr >> PAGE_SHIFT; | ||
309 | nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
310 | 303 | ||
311 | DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n", | 304 | if (result) |
312 | __func__, __LINE__, start_addr, start_pfn, nr_pages); | 305 | goto zero_region; |
313 | |||
314 | result = add_memory(0, start_addr, map.r1.size); | ||
315 | 306 | ||
316 | if (result) { | 307 | if (!r->base || !r->size) { |
317 | pr_err("%s:%d: add_memory failed: (%d)\n", | 308 | result = -1; |
318 | __func__, __LINE__, result); | 309 | goto zero_region; |
319 | return result; | ||
320 | } | 310 | } |
321 | 311 | ||
322 | memblock_add(start_addr, map.r1.size); | 312 | r->offset = r->base - map.rm.size; |
323 | 313 | ||
324 | result = online_pages(start_pfn, nr_pages); | 314 | DBG("%s:%d: Found high region in repository: %llxh %llxh\n", |
315 | __func__, __LINE__, r->base, r->size); | ||
325 | 316 | ||
326 | if (result) | 317 | return 0; |
327 | pr_err("%s:%d: online_pages failed: (%d)\n", | ||
328 | __func__, __LINE__, result); | ||
329 | 318 | ||
319 | zero_region: | ||
320 | DBG("%s:%d: No high region in repository.\n", __func__, __LINE__); | ||
321 | |||
322 | r->size = r->base = r->offset = 0; | ||
330 | return result; | 323 | return result; |
331 | } | 324 | } |
332 | 325 | ||
333 | device_initcall(ps3_mm_add_memory); | ||
334 | |||
335 | /*============================================================================*/ | 326 | /*============================================================================*/ |
336 | /* dma routines */ | 327 | /* dma routines */ |
337 | /*============================================================================*/ | 328 | /*============================================================================*/ |
@@ -1217,13 +1208,23 @@ void __init ps3_mm_init(void) | |||
1217 | BUG_ON(map.rm.base); | 1208 | BUG_ON(map.rm.base); |
1218 | BUG_ON(!map.rm.size); | 1209 | BUG_ON(!map.rm.size); |
1219 | 1210 | ||
1211 | /* Check if we got the highmem region from an earlier boot step */ | ||
1220 | 1212 | ||
1221 | /* arrange to do this in ps3_mm_add_memory */ | 1213 | if (ps3_mm_get_repository_highmem(&map.r1)) |
1222 | ps3_mm_region_create(&map.r1, map.total - map.rm.size); | 1214 | ps3_mm_region_create(&map.r1, map.total - map.rm.size); |
1223 | 1215 | ||
1224 | /* correct map.total for the real total amount of memory we use */ | 1216 | /* correct map.total for the real total amount of memory we use */ |
1225 | map.total = map.rm.size + map.r1.size; | 1217 | map.total = map.rm.size + map.r1.size; |
1226 | 1218 | ||
1219 | if (!map.r1.size) { | ||
1220 | DBG("%s:%d: No highmem region found\n", __func__, __LINE__); | ||
1221 | } else { | ||
1222 | DBG("%s:%d: Adding highmem region: %llxh %llxh\n", | ||
1223 | __func__, __LINE__, map.rm.size, | ||
1224 | map.total - map.rm.size); | ||
1225 | memblock_add(map.rm.size, map.total - map.rm.size); | ||
1226 | } | ||
1227 | |||
1227 | DBG(" <- %s:%d\n", __func__, __LINE__); | 1228 | DBG(" <- %s:%d\n", __func__, __LINE__); |
1228 | } | 1229 | } |
1229 | 1230 | ||
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index 1a633ed0fe98..d71329a8e325 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); | |||
188 | int ps3_repository_read_region_total(u64 *region_total); | 188 | int ps3_repository_read_region_total(u64 *region_total); |
189 | int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, | 189 | int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, |
190 | u64 *region_total); | 190 | u64 *region_total); |
191 | int ps3_repository_read_highmem_region_count(unsigned int *region_count); | ||
192 | int ps3_repository_read_highmem_base(unsigned int region_index, | ||
193 | u64 *highmem_base); | ||
194 | int ps3_repository_read_highmem_size(unsigned int region_index, | ||
195 | u64 *highmem_size); | ||
196 | int ps3_repository_read_highmem_info(unsigned int region_index, | ||
197 | u64 *highmem_base, u64 *highmem_size); | ||
198 | |||
199 | int ps3_repository_write_highmem_region_count(unsigned int region_count); | ||
200 | int ps3_repository_write_highmem_base(unsigned int region_index, | ||
201 | u64 highmem_base); | ||
202 | int ps3_repository_write_highmem_size(unsigned int region_index, | ||
203 | u64 highmem_size); | ||
204 | int ps3_repository_write_highmem_info(unsigned int region_index, | ||
205 | u64 highmem_base, u64 highmem_size); | ||
206 | int ps3_repository_delete_highmem_info(unsigned int region_index); | ||
191 | 207 | ||
192 | /* repository pme info */ | 208 | /* repository pme info */ |
193 | 209 | ||
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index 7bdfea336f5e..9b47ba7a5de7 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) | |||
779 | } | 779 | } |
780 | 780 | ||
781 | /** | 781 | /** |
782 | * ps3_repository_read_highmem_region_count - Read the number of highmem regions | ||
783 | * | ||
784 | * Bootloaders must arrange the repository nodes such that regions are indexed | ||
785 | * with a region_index from 0 to region_count-1. | ||
786 | */ | ||
787 | |||
788 | int ps3_repository_read_highmem_region_count(unsigned int *region_count) | ||
789 | { | ||
790 | int result; | ||
791 | u64 v1 = 0; | ||
792 | |||
793 | result = read_node(PS3_LPAR_ID_CURRENT, | ||
794 | make_first_field("highmem", 0), | ||
795 | make_field("region", 0), | ||
796 | make_field("count", 0), | ||
797 | 0, | ||
798 | &v1, NULL); | ||
799 | *region_count = v1; | ||
800 | return result; | ||
801 | } | ||
802 | |||
803 | |||
804 | int ps3_repository_read_highmem_base(unsigned int region_index, | ||
805 | u64 *highmem_base) | ||
806 | { | ||
807 | return read_node(PS3_LPAR_ID_CURRENT, | ||
808 | make_first_field("highmem", 0), | ||
809 | make_field("region", region_index), | ||
810 | make_field("base", 0), | ||
811 | 0, | ||
812 | highmem_base, NULL); | ||
813 | } | ||
814 | |||
815 | int ps3_repository_read_highmem_size(unsigned int region_index, | ||
816 | u64 *highmem_size) | ||
817 | { | ||
818 | return read_node(PS3_LPAR_ID_CURRENT, | ||
819 | make_first_field("highmem", 0), | ||
820 | make_field("region", region_index), | ||
821 | make_field("size", 0), | ||
822 | 0, | ||
823 | highmem_size, NULL); | ||
824 | } | ||
825 | |||
826 | /** | ||
827 | * ps3_repository_read_highmem_info - Read high memory region info | ||
828 | * @region_index: Region index, {0,..,region_count-1}. | ||
829 | * @highmem_base: High memory base address. | ||
830 | * @highmem_size: High memory size. | ||
831 | * | ||
832 | * Bootloaders that preallocate highmem regions must place the | ||
833 | * region info into the repository at these well known nodes. | ||
834 | */ | ||
835 | |||
836 | int ps3_repository_read_highmem_info(unsigned int region_index, | ||
837 | u64 *highmem_base, u64 *highmem_size) | ||
838 | { | ||
839 | int result; | ||
840 | |||
841 | *highmem_base = 0; | ||
842 | result = ps3_repository_read_highmem_base(region_index, highmem_base); | ||
843 | return result ? result | ||
844 | : ps3_repository_read_highmem_size(region_index, highmem_size); | ||
845 | } | ||
846 | |||
847 | /** | ||
782 | * ps3_repository_read_num_spu_reserved - Number of physical spus reserved. | 848 | * ps3_repository_read_num_spu_reserved - Number of physical spus reserved. |
783 | * @num_spu: Number of physical spus. | 849 | * @num_spu: Number of physical spus. |
784 | */ | 850 | */ |
@@ -1002,6 +1068,138 @@ int ps3_repository_read_lpm_privileges(unsigned int be_index, u64 *lpar, | |||
1002 | lpar, rights); | 1068 | lpar, rights); |
1003 | } | 1069 | } |
1004 | 1070 | ||
1071 | #if defined(CONFIG_PS3_REPOSITORY_WRITE) | ||
1072 | |||
1073 | static int create_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) | ||
1074 | { | ||
1075 | int result; | ||
1076 | |||
1077 | dump_node(0, n1, n2, n3, n4, v1, v2); | ||
1078 | |||
1079 | result = lv1_create_repository_node(n1, n2, n3, n4, v1, v2); | ||
1080 | |||
1081 | if (result) { | ||
1082 | pr_devel("%s:%d: lv1_create_repository_node failed: %s\n", | ||
1083 | __func__, __LINE__, ps3_result(result)); | ||
1084 | return -ENOENT; | ||
1085 | } | ||
1086 | |||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1090 | static int delete_node(u64 n1, u64 n2, u64 n3, u64 n4) | ||
1091 | { | ||
1092 | int result; | ||
1093 | |||
1094 | dump_node(0, n1, n2, n3, n4, 0, 0); | ||
1095 | |||
1096 | result = lv1_delete_repository_node(n1, n2, n3, n4); | ||
1097 | |||
1098 | if (result) { | ||
1099 | pr_devel("%s:%d: lv1_delete_repository_node failed: %s\n", | ||
1100 | __func__, __LINE__, ps3_result(result)); | ||
1101 | return -ENOENT; | ||
1102 | } | ||
1103 | |||
1104 | return 0; | ||
1105 | } | ||
1106 | |||
1107 | static int write_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2) | ||
1108 | { | ||
1109 | int result; | ||
1110 | |||
1111 | result = create_node(n1, n2, n3, n4, v1, v2); | ||
1112 | |||
1113 | if (!result) | ||
1114 | return 0; | ||
1115 | |||
1116 | result = lv1_write_repository_node(n1, n2, n3, n4, v1, v2); | ||
1117 | |||
1118 | if (result) { | ||
1119 | pr_devel("%s:%d: lv1_write_repository_node failed: %s\n", | ||
1120 | __func__, __LINE__, ps3_result(result)); | ||
1121 | return -ENOENT; | ||
1122 | } | ||
1123 | |||
1124 | return 0; | ||
1125 | } | ||
1126 | |||
1127 | int ps3_repository_write_highmem_region_count(unsigned int region_count) | ||
1128 | { | ||
1129 | int result; | ||
1130 | u64 v1 = (u64)region_count; | ||
1131 | |||
1132 | result = write_node( | ||
1133 | make_first_field("highmem", 0), | ||
1134 | make_field("region", 0), | ||
1135 | make_field("count", 0), | ||
1136 | 0, | ||
1137 | v1, 0); | ||
1138 | return result; | ||
1139 | } | ||
1140 | |||
1141 | int ps3_repository_write_highmem_base(unsigned int region_index, | ||
1142 | u64 highmem_base) | ||
1143 | { | ||
1144 | return write_node( | ||
1145 | make_first_field("highmem", 0), | ||
1146 | make_field("region", region_index), | ||
1147 | make_field("base", 0), | ||
1148 | 0, | ||
1149 | highmem_base, 0); | ||
1150 | } | ||
1151 | |||
1152 | int ps3_repository_write_highmem_size(unsigned int region_index, | ||
1153 | u64 highmem_size) | ||
1154 | { | ||
1155 | return write_node( | ||
1156 | make_first_field("highmem", 0), | ||
1157 | make_field("region", region_index), | ||
1158 | make_field("size", 0), | ||
1159 | 0, | ||
1160 | highmem_size, 0); | ||
1161 | } | ||
1162 | |||
1163 | int ps3_repository_write_highmem_info(unsigned int region_index, | ||
1164 | u64 highmem_base, u64 highmem_size) | ||
1165 | { | ||
1166 | int result; | ||
1167 | |||
1168 | result = ps3_repository_write_highmem_base(region_index, highmem_base); | ||
1169 | return result ? result | ||
1170 | : ps3_repository_write_highmem_size(region_index, highmem_size); | ||
1171 | } | ||
1172 | |||
1173 | static int ps3_repository_delete_highmem_base(unsigned int region_index) | ||
1174 | { | ||
1175 | return delete_node( | ||
1176 | make_first_field("highmem", 0), | ||
1177 | make_field("region", region_index), | ||
1178 | make_field("base", 0), | ||
1179 | 0); | ||
1180 | } | ||
1181 | |||
1182 | static int ps3_repository_delete_highmem_size(unsigned int region_index) | ||
1183 | { | ||
1184 | return delete_node( | ||
1185 | make_first_field("highmem", 0), | ||
1186 | make_field("region", region_index), | ||
1187 | make_field("size", 0), | ||
1188 | 0); | ||
1189 | } | ||
1190 | |||
1191 | int ps3_repository_delete_highmem_info(unsigned int region_index) | ||
1192 | { | ||
1193 | int result; | ||
1194 | |||
1195 | result = ps3_repository_delete_highmem_base(region_index); | ||
1196 | result += ps3_repository_delete_highmem_size(region_index); | ||
1197 | |||
1198 | return result ? -1 : 0; | ||
1199 | } | ||
1200 | |||
1201 | #endif /* defined(CONFIG_PS3_WRITE_REPOSITORY) */ | ||
1202 | |||
1005 | #if defined(DEBUG) | 1203 | #if defined(DEBUG) |
1006 | 1204 | ||
1007 | int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo) | 1205 | 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 a75e37dc41aa..ecd394cf34e6 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) | |||
489 | * a stack trace will help the device-driver authors figure | 489 | * a stack trace will help the device-driver authors figure |
490 | * out what happened. So print that out. | 490 | * out what happened. So print that out. |
491 | */ | 491 | */ |
492 | dump_stack(); | 492 | WARN(1, "EEH: failure detected\n"); |
493 | return 1; | 493 | return 1; |
494 | 494 | ||
495 | dn_unlock: | 495 | dn_unlock: |
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index 342797fc0f9c..13e8cc43adf7 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) | |||
22 | 22 | ||
23 | static inline u8 get_cede_latency_hint(void) | 23 | static inline u8 get_cede_latency_hint(void) |
24 | { | 24 | { |
25 | return get_lppaca()->gpr5_dword.fields.cede_latency_hint; | 25 | return get_lppaca()->cede_latency_hint; |
26 | } | 26 | } |
27 | 27 | ||
28 | static inline void set_cede_latency_hint(u8 latency_hint) | 28 | static inline void set_cede_latency_hint(u8 latency_hint) |
29 | { | 29 | { |
30 | get_lppaca()->gpr5_dword.fields.cede_latency_hint = latency_hint; | 30 | get_lppaca()->cede_latency_hint = latency_hint; |
31 | } | 31 | } |
32 | 32 | ||
33 | static inline long cede_processor(void) | 33 | static inline long cede_processor(void) |
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 168651acdd83..7b3bf76ef834 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
@@ -103,11 +103,13 @@ int pSeries_reconfig_notifier_register(struct notifier_block *nb) | |||
103 | { | 103 | { |
104 | return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb); | 104 | return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb); |
105 | } | 105 | } |
106 | EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_register); | ||
106 | 107 | ||
107 | void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) | 108 | void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) |
108 | { | 109 | { |
109 | blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb); | 110 | blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb); |
110 | } | 111 | } |
112 | EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_unregister); | ||
111 | 113 | ||
112 | int pSeries_reconfig_notify(unsigned long action, void *p) | 114 | int pSeries_reconfig_notify(unsigned long action, void *p) |
113 | { | 115 | { |
@@ -426,6 +428,7 @@ static int do_remove_property(char *buf, size_t bufsize) | |||
426 | static int do_update_property(char *buf, size_t bufsize) | 428 | static int do_update_property(char *buf, size_t bufsize) |
427 | { | 429 | { |
428 | struct device_node *np; | 430 | struct device_node *np; |
431 | struct pSeries_reconfig_prop_update upd_value; | ||
429 | unsigned char *value; | 432 | unsigned char *value; |
430 | char *name, *end, *next_prop; | 433 | char *name, *end, *next_prop; |
431 | int rc, length; | 434 | int rc, length; |
@@ -454,6 +457,10 @@ static int do_update_property(char *buf, size_t bufsize) | |||
454 | return -ENODEV; | 457 | return -ENODEV; |
455 | } | 458 | } |
456 | 459 | ||
460 | upd_value.node = np; | ||
461 | upd_value.property = newprop; | ||
462 | pSeries_reconfig_notify(PSERIES_UPDATE_PROPERTY, &upd_value); | ||
463 | |||
457 | rc = prom_update_property(np, newprop, oldprop); | 464 | rc = prom_update_property(np, newprop, oldprop); |
458 | if (rc) | 465 | if (rc) |
459 | return rc; | 466 | return rc; |
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c index 1c2d7af17bbe..82c6702dcbab 100644 --- a/arch/powerpc/sysdev/ppc4xx_msi.c +++ b/arch/powerpc/sysdev/ppc4xx_msi.c | |||
@@ -28,10 +28,11 @@ | |||
28 | #include <linux/of_platform.h> | 28 | #include <linux/of_platform.h> |
29 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
30 | #include <linux/export.h> | 30 | #include <linux/export.h> |
31 | #include <linux/kernel.h> | ||
31 | #include <asm/prom.h> | 32 | #include <asm/prom.h> |
32 | #include <asm/hw_irq.h> | 33 | #include <asm/hw_irq.h> |
33 | #include <asm/ppc-pci.h> | 34 | #include <asm/ppc-pci.h> |
34 | #include <boot/dcr.h> | 35 | #include <asm/dcr.h> |
35 | #include <asm/dcr-regs.h> | 36 | #include <asm/dcr-regs.h> |
36 | #include <asm/msi_bitmap.h> | 37 | #include <asm/msi_bitmap.h> |
37 | 38 | ||
@@ -43,13 +44,14 @@ | |||
43 | #define PEIH_FLUSH0 0x30 | 44 | #define PEIH_FLUSH0 0x30 |
44 | #define PEIH_FLUSH1 0x38 | 45 | #define PEIH_FLUSH1 0x38 |
45 | #define PEIH_CNTRST 0x48 | 46 | #define PEIH_CNTRST 0x48 |
46 | #define NR_MSI_IRQS 4 | 47 | |
48 | static int msi_irqs; | ||
47 | 49 | ||
48 | struct ppc4xx_msi { | 50 | struct ppc4xx_msi { |
49 | u32 msi_addr_lo; | 51 | u32 msi_addr_lo; |
50 | u32 msi_addr_hi; | 52 | u32 msi_addr_hi; |
51 | void __iomem *msi_regs; | 53 | void __iomem *msi_regs; |
52 | int msi_virqs[NR_MSI_IRQS]; | 54 | int *msi_virqs; |
53 | struct msi_bitmap bitmap; | 55 | struct msi_bitmap bitmap; |
54 | struct device_node *msi_dev; | 56 | struct device_node *msi_dev; |
55 | }; | 57 | }; |
@@ -61,7 +63,7 @@ static int ppc4xx_msi_init_allocator(struct platform_device *dev, | |||
61 | { | 63 | { |
62 | int err; | 64 | int err; |
63 | 65 | ||
64 | err = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, | 66 | err = msi_bitmap_alloc(&msi_data->bitmap, msi_irqs, |
65 | dev->dev.of_node); | 67 | dev->dev.of_node); |
66 | if (err) | 68 | if (err) |
67 | return err; | 69 | return err; |
@@ -83,6 +85,11 @@ static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | |||
83 | struct msi_desc *entry; | 85 | struct msi_desc *entry; |
84 | struct ppc4xx_msi *msi_data = &ppc4xx_msi; | 86 | struct ppc4xx_msi *msi_data = &ppc4xx_msi; |
85 | 87 | ||
88 | msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int), | ||
89 | GFP_KERNEL); | ||
90 | if (!msi_data->msi_virqs) | ||
91 | return -ENOMEM; | ||
92 | |||
86 | list_for_each_entry(entry, &dev->msi_list, list) { | 93 | list_for_each_entry(entry, &dev->msi_list, list) { |
87 | int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); | 94 | int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); |
88 | if (int_no >= 0) | 95 | if (int_no >= 0) |
@@ -150,12 +157,11 @@ static int ppc4xx_setup_pcieh_hw(struct platform_device *dev, | |||
150 | if (!sdr_addr) | 157 | if (!sdr_addr) |
151 | return -1; | 158 | return -1; |
152 | 159 | ||
153 | SDR0_WRITE(sdr_addr, (u64)res.start >> 32); /*HIGH addr */ | 160 | mtdcri(SDR0, *sdr_addr, upper_32_bits(res.start)); /*HIGH addr */ |
154 | SDR0_WRITE(sdr_addr + 1, res.start & 0xFFFFFFFF); /* Low addr */ | 161 | mtdcri(SDR0, *sdr_addr + 1, lower_32_bits(res.start)); /* Low addr */ |
155 | |||
156 | 162 | ||
157 | msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi"); | 163 | msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi"); |
158 | if (msi->msi_dev) | 164 | if (!msi->msi_dev) |
159 | return -ENODEV; | 165 | return -ENODEV; |
160 | 166 | ||
161 | msi->msi_regs = of_iomap(msi->msi_dev, 0); | 167 | msi->msi_regs = of_iomap(msi->msi_dev, 0); |
@@ -167,9 +173,12 @@ static int ppc4xx_setup_pcieh_hw(struct platform_device *dev, | |||
167 | (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs)); | 173 | (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs)); |
168 | 174 | ||
169 | msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL); | 175 | msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL); |
170 | msi->msi_addr_hi = 0x0; | 176 | if (!msi_virt) |
171 | msi->msi_addr_lo = (u32) msi_phys; | 177 | return -ENOMEM; |
172 | dev_dbg(&dev->dev, "PCIE-MSI: msi address 0x%x\n", msi->msi_addr_lo); | 178 | msi->msi_addr_hi = upper_32_bits(msi_phys); |
179 | msi->msi_addr_lo = lower_32_bits(msi_phys & 0xffffffff); | ||
180 | dev_dbg(&dev->dev, "PCIE-MSI: msi address high 0x%x, low 0x%x\n", | ||
181 | msi->msi_addr_hi, msi->msi_addr_lo); | ||
173 | 182 | ||
174 | /* Progam the Interrupt handler Termination addr registers */ | 183 | /* Progam the Interrupt handler Termination addr registers */ |
175 | out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); | 184 | out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi); |
@@ -185,6 +194,8 @@ static int ppc4xx_setup_pcieh_hw(struct platform_device *dev, | |||
185 | out_be32(msi->msi_regs + PEIH_MSIED, *msi_data); | 194 | out_be32(msi->msi_regs + PEIH_MSIED, *msi_data); |
186 | out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask); | 195 | out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask); |
187 | 196 | ||
197 | dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys); | ||
198 | |||
188 | return 0; | 199 | return 0; |
189 | } | 200 | } |
190 | 201 | ||
@@ -194,7 +205,7 @@ static int ppc4xx_of_msi_remove(struct platform_device *dev) | |||
194 | int i; | 205 | int i; |
195 | int virq; | 206 | int virq; |
196 | 207 | ||
197 | for (i = 0; i < NR_MSI_IRQS; i++) { | 208 | for (i = 0; i < msi_irqs; i++) { |
198 | virq = msi->msi_virqs[i]; | 209 | virq = msi->msi_virqs[i]; |
199 | if (virq != NO_IRQ) | 210 | if (virq != NO_IRQ) |
200 | irq_dispose_mapping(virq); | 211 | irq_dispose_mapping(virq); |
@@ -215,8 +226,6 @@ static int __devinit ppc4xx_msi_probe(struct platform_device *dev) | |||
215 | struct resource res; | 226 | struct resource res; |
216 | int err = 0; | 227 | int err = 0; |
217 | 228 | ||
218 | msi = &ppc4xx_msi;/*keep the msi data for further use*/ | ||
219 | |||
220 | dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n"); | 229 | dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n"); |
221 | 230 | ||
222 | msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); | 231 | msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL); |
@@ -234,6 +243,10 @@ static int __devinit ppc4xx_msi_probe(struct platform_device *dev) | |||
234 | goto error_out; | 243 | goto error_out; |
235 | } | 244 | } |
236 | 245 | ||
246 | msi_irqs = of_irq_count(dev->dev.of_node); | ||
247 | if (!msi_irqs) | ||
248 | return -ENODEV; | ||
249 | |||
237 | if (ppc4xx_setup_pcieh_hw(dev, res, msi)) | 250 | if (ppc4xx_setup_pcieh_hw(dev, res, msi)) |
238 | goto error_out; | 251 | goto error_out; |
239 | 252 | ||
@@ -242,6 +255,7 @@ static int __devinit ppc4xx_msi_probe(struct platform_device *dev) | |||
242 | dev_err(&dev->dev, "Error allocating MSI bitmap\n"); | 255 | dev_err(&dev->dev, "Error allocating MSI bitmap\n"); |
243 | goto error_out; | 256 | goto error_out; |
244 | } | 257 | } |
258 | ppc4xx_msi = *msi; | ||
245 | 259 | ||
246 | ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; | 260 | ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs; |
247 | ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; | 261 | ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs; |
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index b2402eb076c7..c225314468ee 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig | |||
@@ -250,3 +250,16 @@ config UML_RANDOM | |||
250 | (check your distro, or download from | 250 | (check your distro, or download from |
251 | http://sourceforge.net/projects/gkernel/). rngd periodically reads | 251 | http://sourceforge.net/projects/gkernel/). rngd periodically reads |
252 | /dev/hwrng and injects the entropy into /dev/random. | 252 | /dev/hwrng and injects the entropy into /dev/random. |
253 | |||
254 | config HW_RANDOM_PSERIES | ||
255 | tristate "pSeries HW Random Number Generator support" | ||
256 | depends on HW_RANDOM && PPC64 && IBMVIO | ||
257 | default HW_RANDOM | ||
258 | ---help--- | ||
259 | This driver provides kernel-side support for the Random Number | ||
260 | Generator hardware found on POWER7+ machines and above | ||
261 | |||
262 | To compile this driver as a module, choose M here: the | ||
263 | module will be called pseries-rng. | ||
264 | |||
265 | If unsure, say Y. | ||
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b2ff5265a996..d901dfa30321 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile | |||
@@ -22,3 +22,4 @@ obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o | |||
22 | obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o | 22 | obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o |
23 | obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o | 23 | obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o |
24 | obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o | 24 | obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o |
25 | obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o | ||
diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c new file mode 100644 index 000000000000..5f1197929f0c --- /dev/null +++ b/drivers/char/hw_random/pseries-rng.c | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Michael Neuling IBM Corporation | ||
3 | * | ||
4 | * Driver for the pseries hardware RNG for POWER7+ and above | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/hw_random.h> | ||
22 | #include <asm/vio.h> | ||
23 | |||
24 | #define MODULE_NAME "pseries-rng" | ||
25 | |||
26 | static int pseries_rng_data_read(struct hwrng *rng, u32 *data) | ||
27 | { | ||
28 | if (plpar_hcall(H_RANDOM, (unsigned long *)data) != H_SUCCESS) { | ||
29 | printk(KERN_ERR "pseries rng hcall error\n"); | ||
30 | return 0; | ||
31 | } | ||
32 | return 8; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations | ||
37 | * | ||
38 | * This is a required function for a driver to operate in a CMO environment | ||
39 | * but this device does not make use of DMA allocations, return 0. | ||
40 | * | ||
41 | * Return value: | ||
42 | * Number of bytes of IO data the driver will need to perform well -> 0 | ||
43 | */ | ||
44 | static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev) | ||
45 | { | ||
46 | return 0; | ||
47 | }; | ||
48 | |||
49 | static struct hwrng pseries_rng = { | ||
50 | .name = MODULE_NAME, | ||
51 | .data_read = pseries_rng_data_read, | ||
52 | }; | ||
53 | |||
54 | static int __init pseries_rng_probe(struct vio_dev *dev, | ||
55 | const struct vio_device_id *id) | ||
56 | { | ||
57 | return hwrng_register(&pseries_rng); | ||
58 | } | ||
59 | |||
60 | static int __exit pseries_rng_remove(struct vio_dev *dev) | ||
61 | { | ||
62 | hwrng_unregister(&pseries_rng); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static struct vio_device_id pseries_rng_driver_ids[] = { | ||
67 | { "ibm,random-v1", "ibm,random"}, | ||
68 | { "", "" } | ||
69 | }; | ||
70 | MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids); | ||
71 | |||
72 | static struct vio_driver pseries_rng_driver = { | ||
73 | .name = MODULE_NAME, | ||
74 | .probe = pseries_rng_probe, | ||
75 | .remove = pseries_rng_remove, | ||
76 | .get_desired_dma = pseries_rng_get_desired_dma, | ||
77 | .id_table = pseries_rng_driver_ids | ||
78 | }; | ||
79 | |||
80 | static int __init rng_init(void) | ||
81 | { | ||
82 | printk(KERN_INFO "Registering IBM pSeries RNG driver\n"); | ||
83 | return vio_register_driver(&pseries_rng_driver); | ||
84 | } | ||
85 | |||
86 | module_init(rng_init); | ||
87 | |||
88 | static void __exit rng_exit(void) | ||
89 | { | ||
90 | vio_unregister_driver(&pseries_rng_driver); | ||
91 | } | ||
92 | module_exit(rng_exit); | ||
93 | |||
94 | MODULE_LICENSE("GPL"); | ||
95 | MODULE_AUTHOR("Michael Neuling <mikey@neuling.org>"); | ||
96 | MODULE_DESCRIPTION("H/W RNG driver for IBM pSeries processors"); | ||
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 371f13cc38eb..6373fa0ddb65 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig | |||
@@ -297,4 +297,21 @@ config CRYPTO_DEV_TEGRA_AES | |||
297 | To compile this driver as a module, choose M here: the module | 297 | To compile this driver as a module, choose M here: the module |
298 | will be called tegra-aes. | 298 | will be called tegra-aes. |
299 | 299 | ||
300 | config CRYPTO_DEV_NX | ||
301 | tristate "Support for Power7+ in-Nest cryptographic accleration" | ||
302 | depends on PPC64 && IBMVIO | ||
303 | select CRYPTO_AES | ||
304 | select CRYPTO_CBC | ||
305 | select CRYPTO_ECB | ||
306 | select CRYPTO_CCM | ||
307 | select CRYPTO_GCM | ||
308 | select CRYPTO_AUTHENC | ||
309 | select CRYPTO_XCBC | ||
310 | select CRYPTO_SHA256 | ||
311 | select CRYPTO_SHA512 | ||
312 | help | ||
313 | Support for Power7+ in-Nest cryptographic acceleration. This | ||
314 | module supports acceleration for AES and SHA2 algorithms. If you | ||
315 | choose 'M' here, this module will be called nx_crypto. | ||
316 | |||
300 | endif # CRYPTO_HW | 317 | endif # CRYPTO_HW |
diff --git a/drivers/crypto/nx/Makefile b/drivers/crypto/nx/Makefile new file mode 100644 index 000000000000..411ce59c80d1 --- /dev/null +++ b/drivers/crypto/nx/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | obj-$(CONFIG_CRYPTO_DEV_NX) += nx-crypto.o | ||
2 | nx-crypto-objs := nx.o \ | ||
3 | nx_debugfs.o \ | ||
4 | nx-aes-cbc.o \ | ||
5 | nx-aes-ecb.o \ | ||
6 | nx-aes-gcm.o \ | ||
7 | nx-aes-ccm.o \ | ||
8 | nx-aes-ctr.o \ | ||
9 | nx-aes-xcbc.o \ | ||
10 | nx-sha256.o \ | ||
11 | nx-sha512.o | ||
diff --git a/drivers/crypto/nx/nx-aes-cbc.c b/drivers/crypto/nx/nx-aes-cbc.c new file mode 100644 index 000000000000..69ed796ee327 --- /dev/null +++ b/drivers/crypto/nx/nx-aes-cbc.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /** | ||
2 | * AES CBC routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/aes.h> | ||
23 | #include <crypto/algapi.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/crypto.h> | ||
27 | #include <asm/vio.h> | ||
28 | |||
29 | #include "nx_csbcpb.h" | ||
30 | #include "nx.h" | ||
31 | |||
32 | |||
33 | static int cbc_aes_nx_set_key(struct crypto_tfm *tfm, | ||
34 | const u8 *in_key, | ||
35 | unsigned int key_len) | ||
36 | { | ||
37 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); | ||
38 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
39 | |||
40 | nx_ctx_init(nx_ctx, HCOP_FC_AES); | ||
41 | |||
42 | switch (key_len) { | ||
43 | case AES_KEYSIZE_128: | ||
44 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128); | ||
45 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128]; | ||
46 | break; | ||
47 | case AES_KEYSIZE_192: | ||
48 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192); | ||
49 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192]; | ||
50 | break; | ||
51 | case AES_KEYSIZE_256: | ||
52 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256); | ||
53 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256]; | ||
54 | break; | ||
55 | default: | ||
56 | return -EINVAL; | ||
57 | } | ||
58 | |||
59 | csbcpb->cpb.hdr.mode = NX_MODE_AES_CBC; | ||
60 | memcpy(csbcpb->cpb.aes_cbc.key, in_key, key_len); | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int cbc_aes_nx_crypt(struct blkcipher_desc *desc, | ||
66 | struct scatterlist *dst, | ||
67 | struct scatterlist *src, | ||
68 | unsigned int nbytes, | ||
69 | int enc) | ||
70 | { | ||
71 | struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm); | ||
72 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
73 | int rc; | ||
74 | |||
75 | if (nbytes > nx_ctx->ap->databytelen) | ||
76 | return -EINVAL; | ||
77 | |||
78 | if (enc) | ||
79 | NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT; | ||
80 | else | ||
81 | NX_CPB_FDM(csbcpb) &= ~NX_FDM_ENDE_ENCRYPT; | ||
82 | |||
83 | rc = nx_build_sg_lists(nx_ctx, desc, dst, src, nbytes, | ||
84 | csbcpb->cpb.aes_cbc.iv); | ||
85 | if (rc) | ||
86 | goto out; | ||
87 | |||
88 | if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) { | ||
89 | rc = -EINVAL; | ||
90 | goto out; | ||
91 | } | ||
92 | |||
93 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
94 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
95 | if (rc) | ||
96 | goto out; | ||
97 | |||
98 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
99 | atomic64_add(csbcpb->csb.processed_byte_count, | ||
100 | &(nx_ctx->stats->aes_bytes)); | ||
101 | out: | ||
102 | return rc; | ||
103 | } | ||
104 | |||
105 | static int cbc_aes_nx_encrypt(struct blkcipher_desc *desc, | ||
106 | struct scatterlist *dst, | ||
107 | struct scatterlist *src, | ||
108 | unsigned int nbytes) | ||
109 | { | ||
110 | return cbc_aes_nx_crypt(desc, dst, src, nbytes, 1); | ||
111 | } | ||
112 | |||
113 | static int cbc_aes_nx_decrypt(struct blkcipher_desc *desc, | ||
114 | struct scatterlist *dst, | ||
115 | struct scatterlist *src, | ||
116 | unsigned int nbytes) | ||
117 | { | ||
118 | return cbc_aes_nx_crypt(desc, dst, src, nbytes, 0); | ||
119 | } | ||
120 | |||
121 | struct crypto_alg nx_cbc_aes_alg = { | ||
122 | .cra_name = "cbc(aes)", | ||
123 | .cra_driver_name = "cbc-aes-nx", | ||
124 | .cra_priority = 300, | ||
125 | .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, | ||
126 | .cra_blocksize = AES_BLOCK_SIZE, | ||
127 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
128 | .cra_type = &crypto_blkcipher_type, | ||
129 | .cra_module = THIS_MODULE, | ||
130 | .cra_list = LIST_HEAD_INIT(nx_cbc_aes_alg.cra_list), | ||
131 | .cra_init = nx_crypto_ctx_aes_cbc_init, | ||
132 | .cra_exit = nx_crypto_ctx_exit, | ||
133 | .cra_blkcipher = { | ||
134 | .min_keysize = AES_MIN_KEY_SIZE, | ||
135 | .max_keysize = AES_MAX_KEY_SIZE, | ||
136 | .ivsize = AES_BLOCK_SIZE, | ||
137 | .setkey = cbc_aes_nx_set_key, | ||
138 | .encrypt = cbc_aes_nx_encrypt, | ||
139 | .decrypt = cbc_aes_nx_decrypt, | ||
140 | } | ||
141 | }; | ||
diff --git a/drivers/crypto/nx/nx-aes-ccm.c b/drivers/crypto/nx/nx-aes-ccm.c new file mode 100644 index 000000000000..7aeac678b9c0 --- /dev/null +++ b/drivers/crypto/nx/nx-aes-ccm.c | |||
@@ -0,0 +1,468 @@ | |||
1 | /** | ||
2 | * AES CCM routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/internal/aead.h> | ||
23 | #include <crypto/aes.h> | ||
24 | #include <crypto/algapi.h> | ||
25 | #include <crypto/scatterwalk.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/types.h> | ||
28 | #include <linux/crypto.h> | ||
29 | #include <asm/vio.h> | ||
30 | |||
31 | #include "nx_csbcpb.h" | ||
32 | #include "nx.h" | ||
33 | |||
34 | |||
35 | static int ccm_aes_nx_set_key(struct crypto_aead *tfm, | ||
36 | const u8 *in_key, | ||
37 | unsigned int key_len) | ||
38 | { | ||
39 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base); | ||
40 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
41 | struct nx_csbcpb *csbcpb_aead = nx_ctx->csbcpb_aead; | ||
42 | |||
43 | nx_ctx_init(nx_ctx, HCOP_FC_AES); | ||
44 | |||
45 | switch (key_len) { | ||
46 | case AES_KEYSIZE_128: | ||
47 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128); | ||
48 | NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_128); | ||
49 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128]; | ||
50 | break; | ||
51 | default: | ||
52 | return -EINVAL; | ||
53 | } | ||
54 | |||
55 | csbcpb->cpb.hdr.mode = NX_MODE_AES_CCM; | ||
56 | memcpy(csbcpb->cpb.aes_ccm.key, in_key, key_len); | ||
57 | |||
58 | csbcpb_aead->cpb.hdr.mode = NX_MODE_AES_CCA; | ||
59 | memcpy(csbcpb_aead->cpb.aes_cca.key, in_key, key_len); | ||
60 | |||
61 | return 0; | ||
62 | |||
63 | } | ||
64 | |||
65 | static int ccm4309_aes_nx_set_key(struct crypto_aead *tfm, | ||
66 | const u8 *in_key, | ||
67 | unsigned int key_len) | ||
68 | { | ||
69 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base); | ||
70 | |||
71 | if (key_len < 3) | ||
72 | return -EINVAL; | ||
73 | |||
74 | key_len -= 3; | ||
75 | |||
76 | memcpy(nx_ctx->priv.ccm.nonce, in_key + key_len, 3); | ||
77 | |||
78 | return ccm_aes_nx_set_key(tfm, in_key, key_len); | ||
79 | } | ||
80 | |||
81 | static int ccm_aes_nx_setauthsize(struct crypto_aead *tfm, | ||
82 | unsigned int authsize) | ||
83 | { | ||
84 | switch (authsize) { | ||
85 | case 4: | ||
86 | case 6: | ||
87 | case 8: | ||
88 | case 10: | ||
89 | case 12: | ||
90 | case 14: | ||
91 | case 16: | ||
92 | break; | ||
93 | default: | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | |||
97 | crypto_aead_crt(tfm)->authsize = authsize; | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int ccm4309_aes_nx_setauthsize(struct crypto_aead *tfm, | ||
103 | unsigned int authsize) | ||
104 | { | ||
105 | switch (authsize) { | ||
106 | case 8: | ||
107 | case 12: | ||
108 | case 16: | ||
109 | break; | ||
110 | default: | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | |||
114 | crypto_aead_crt(tfm)->authsize = authsize; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /* taken from crypto/ccm.c */ | ||
120 | static int set_msg_len(u8 *block, unsigned int msglen, int csize) | ||
121 | { | ||
122 | __be32 data; | ||
123 | |||
124 | memset(block, 0, csize); | ||
125 | block += csize; | ||
126 | |||
127 | if (csize >= 4) | ||
128 | csize = 4; | ||
129 | else if (msglen > (unsigned int)(1 << (8 * csize))) | ||
130 | return -EOVERFLOW; | ||
131 | |||
132 | data = cpu_to_be32(msglen); | ||
133 | memcpy(block - csize, (u8 *)&data + 4 - csize, csize); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | /* taken from crypto/ccm.c */ | ||
139 | static inline int crypto_ccm_check_iv(const u8 *iv) | ||
140 | { | ||
141 | /* 2 <= L <= 8, so 1 <= L' <= 7. */ | ||
142 | if (1 > iv[0] || iv[0] > 7) | ||
143 | return -EINVAL; | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* based on code from crypto/ccm.c */ | ||
149 | static int generate_b0(u8 *iv, unsigned int assoclen, unsigned int authsize, | ||
150 | unsigned int cryptlen, u8 *b0) | ||
151 | { | ||
152 | unsigned int l, lp, m = authsize; | ||
153 | int rc; | ||
154 | |||
155 | memcpy(b0, iv, 16); | ||
156 | |||
157 | lp = b0[0]; | ||
158 | l = lp + 1; | ||
159 | |||
160 | /* set m, bits 3-5 */ | ||
161 | *b0 |= (8 * ((m - 2) / 2)); | ||
162 | |||
163 | /* set adata, bit 6, if associated data is used */ | ||
164 | if (assoclen) | ||
165 | *b0 |= 64; | ||
166 | |||
167 | rc = set_msg_len(b0 + 16 - l, cryptlen, l); | ||
168 | |||
169 | return rc; | ||
170 | } | ||
171 | |||
172 | static int generate_pat(u8 *iv, | ||
173 | struct aead_request *req, | ||
174 | struct nx_crypto_ctx *nx_ctx, | ||
175 | unsigned int authsize, | ||
176 | unsigned int nbytes, | ||
177 | u8 *out) | ||
178 | { | ||
179 | struct nx_sg *nx_insg = nx_ctx->in_sg; | ||
180 | struct nx_sg *nx_outsg = nx_ctx->out_sg; | ||
181 | unsigned int iauth_len = 0; | ||
182 | struct vio_pfo_op *op = NULL; | ||
183 | u8 tmp[16], *b1 = NULL, *b0 = NULL, *result = NULL; | ||
184 | int rc; | ||
185 | |||
186 | /* zero the ctr value */ | ||
187 | memset(iv + 15 - iv[0], 0, iv[0] + 1); | ||
188 | |||
189 | if (!req->assoclen) { | ||
190 | b0 = nx_ctx->csbcpb->cpb.aes_ccm.in_pat_or_b0; | ||
191 | } else if (req->assoclen <= 14) { | ||
192 | /* if associated data is 14 bytes or less, we do 1 GCM | ||
193 | * operation on 2 AES blocks, B0 (stored in the csbcpb) and B1, | ||
194 | * which is fed in through the source buffers here */ | ||
195 | b0 = nx_ctx->csbcpb->cpb.aes_ccm.in_pat_or_b0; | ||
196 | b1 = nx_ctx->priv.ccm.iauth_tag; | ||
197 | iauth_len = req->assoclen; | ||
198 | |||
199 | nx_insg = nx_build_sg_list(nx_insg, b1, 16, nx_ctx->ap->sglen); | ||
200 | nx_outsg = nx_build_sg_list(nx_outsg, tmp, 16, | ||
201 | nx_ctx->ap->sglen); | ||
202 | |||
203 | /* inlen should be negative, indicating to phyp that its a | ||
204 | * pointer to an sg list */ | ||
205 | nx_ctx->op.inlen = (nx_ctx->in_sg - nx_insg) * | ||
206 | sizeof(struct nx_sg); | ||
207 | nx_ctx->op.outlen = (nx_ctx->out_sg - nx_outsg) * | ||
208 | sizeof(struct nx_sg); | ||
209 | |||
210 | NX_CPB_FDM(nx_ctx->csbcpb) |= NX_FDM_ENDE_ENCRYPT; | ||
211 | NX_CPB_FDM(nx_ctx->csbcpb) |= NX_FDM_INTERMEDIATE; | ||
212 | |||
213 | op = &nx_ctx->op; | ||
214 | result = nx_ctx->csbcpb->cpb.aes_ccm.out_pat_or_mac; | ||
215 | } else if (req->assoclen <= 65280) { | ||
216 | /* if associated data is less than (2^16 - 2^8), we construct | ||
217 | * B1 differently and feed in the associated data to a CCA | ||
218 | * operation */ | ||
219 | b0 = nx_ctx->csbcpb_aead->cpb.aes_cca.b0; | ||
220 | b1 = nx_ctx->csbcpb_aead->cpb.aes_cca.b1; | ||
221 | iauth_len = 14; | ||
222 | |||
223 | /* remaining assoc data must have scatterlist built for it */ | ||
224 | nx_insg = nx_walk_and_build(nx_insg, nx_ctx->ap->sglen, | ||
225 | req->assoc, iauth_len, | ||
226 | req->assoclen - iauth_len); | ||
227 | nx_ctx->op_aead.inlen = (nx_ctx->in_sg - nx_insg) * | ||
228 | sizeof(struct nx_sg); | ||
229 | |||
230 | op = &nx_ctx->op_aead; | ||
231 | result = nx_ctx->csbcpb_aead->cpb.aes_cca.out_pat_or_b0; | ||
232 | } else { | ||
233 | /* if associated data is less than (2^32), we construct B1 | ||
234 | * differently yet again and feed in the associated data to a | ||
235 | * CCA operation */ | ||
236 | pr_err("associated data len is %u bytes (returning -EINVAL)\n", | ||
237 | req->assoclen); | ||
238 | rc = -EINVAL; | ||
239 | } | ||
240 | |||
241 | rc = generate_b0(iv, req->assoclen, authsize, nbytes, b0); | ||
242 | if (rc) | ||
243 | goto done; | ||
244 | |||
245 | if (b1) { | ||
246 | memset(b1, 0, 16); | ||
247 | *(u16 *)b1 = (u16)req->assoclen; | ||
248 | |||
249 | scatterwalk_map_and_copy(b1 + 2, req->assoc, 0, | ||
250 | iauth_len, SCATTERWALK_FROM_SG); | ||
251 | |||
252 | rc = nx_hcall_sync(nx_ctx, op, | ||
253 | req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
254 | if (rc) | ||
255 | goto done; | ||
256 | |||
257 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
258 | atomic64_add(req->assoclen, &(nx_ctx->stats->aes_bytes)); | ||
259 | |||
260 | memcpy(out, result, AES_BLOCK_SIZE); | ||
261 | } | ||
262 | done: | ||
263 | return rc; | ||
264 | } | ||
265 | |||
266 | static int ccm_nx_decrypt(struct aead_request *req, | ||
267 | struct blkcipher_desc *desc) | ||
268 | { | ||
269 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
270 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
271 | unsigned int nbytes = req->cryptlen; | ||
272 | unsigned int authsize = crypto_aead_authsize(crypto_aead_reqtfm(req)); | ||
273 | struct nx_ccm_priv *priv = &nx_ctx->priv.ccm; | ||
274 | int rc = -1; | ||
275 | |||
276 | if (nbytes > nx_ctx->ap->databytelen) | ||
277 | return -EINVAL; | ||
278 | |||
279 | nbytes -= authsize; | ||
280 | |||
281 | /* copy out the auth tag to compare with later */ | ||
282 | scatterwalk_map_and_copy(priv->oauth_tag, | ||
283 | req->src, nbytes, authsize, | ||
284 | SCATTERWALK_FROM_SG); | ||
285 | |||
286 | rc = generate_pat(desc->info, req, nx_ctx, authsize, nbytes, | ||
287 | csbcpb->cpb.aes_ccm.in_pat_or_b0); | ||
288 | if (rc) | ||
289 | goto out; | ||
290 | |||
291 | rc = nx_build_sg_lists(nx_ctx, desc, req->dst, req->src, nbytes, | ||
292 | csbcpb->cpb.aes_ccm.iv_or_ctr); | ||
293 | if (rc) | ||
294 | goto out; | ||
295 | |||
296 | NX_CPB_FDM(nx_ctx->csbcpb) &= ~NX_FDM_ENDE_ENCRYPT; | ||
297 | NX_CPB_FDM(nx_ctx->csbcpb) &= ~NX_FDM_INTERMEDIATE; | ||
298 | |||
299 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
300 | req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
301 | if (rc) | ||
302 | goto out; | ||
303 | |||
304 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
305 | atomic64_add(csbcpb->csb.processed_byte_count, | ||
306 | &(nx_ctx->stats->aes_bytes)); | ||
307 | |||
308 | rc = memcmp(csbcpb->cpb.aes_ccm.out_pat_or_mac, priv->oauth_tag, | ||
309 | authsize) ? -EBADMSG : 0; | ||
310 | out: | ||
311 | return rc; | ||
312 | } | ||
313 | |||
314 | static int ccm_nx_encrypt(struct aead_request *req, | ||
315 | struct blkcipher_desc *desc) | ||
316 | { | ||
317 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
318 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
319 | unsigned int nbytes = req->cryptlen; | ||
320 | unsigned int authsize = crypto_aead_authsize(crypto_aead_reqtfm(req)); | ||
321 | int rc = -1; | ||
322 | |||
323 | if (nbytes > nx_ctx->ap->databytelen) | ||
324 | return -EINVAL; | ||
325 | |||
326 | rc = generate_pat(desc->info, req, nx_ctx, authsize, nbytes, | ||
327 | csbcpb->cpb.aes_ccm.in_pat_or_b0); | ||
328 | if (rc) | ||
329 | goto out; | ||
330 | |||
331 | rc = nx_build_sg_lists(nx_ctx, desc, req->dst, req->src, nbytes, | ||
332 | csbcpb->cpb.aes_ccm.iv_or_ctr); | ||
333 | if (rc) | ||
334 | goto out; | ||
335 | |||
336 | NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT; | ||
337 | NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE; | ||
338 | |||
339 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
340 | req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
341 | if (rc) | ||
342 | goto out; | ||
343 | |||
344 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
345 | atomic64_add(csbcpb->csb.processed_byte_count, | ||
346 | &(nx_ctx->stats->aes_bytes)); | ||
347 | |||
348 | /* copy out the auth tag */ | ||
349 | scatterwalk_map_and_copy(csbcpb->cpb.aes_ccm.out_pat_or_mac, | ||
350 | req->dst, nbytes, authsize, | ||
351 | SCATTERWALK_TO_SG); | ||
352 | out: | ||
353 | return rc; | ||
354 | } | ||
355 | |||
356 | static int ccm4309_aes_nx_encrypt(struct aead_request *req) | ||
357 | { | ||
358 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
359 | struct blkcipher_desc desc; | ||
360 | u8 *iv = nx_ctx->priv.ccm.iv; | ||
361 | |||
362 | iv[0] = 3; | ||
363 | memcpy(iv + 1, nx_ctx->priv.ccm.nonce, 3); | ||
364 | memcpy(iv + 4, req->iv, 8); | ||
365 | |||
366 | desc.info = iv; | ||
367 | desc.tfm = (struct crypto_blkcipher *)req->base.tfm; | ||
368 | |||
369 | return ccm_nx_encrypt(req, &desc); | ||
370 | } | ||
371 | |||
372 | static int ccm_aes_nx_encrypt(struct aead_request *req) | ||
373 | { | ||
374 | struct blkcipher_desc desc; | ||
375 | int rc; | ||
376 | |||
377 | desc.info = req->iv; | ||
378 | desc.tfm = (struct crypto_blkcipher *)req->base.tfm; | ||
379 | |||
380 | rc = crypto_ccm_check_iv(desc.info); | ||
381 | if (rc) | ||
382 | return rc; | ||
383 | |||
384 | return ccm_nx_encrypt(req, &desc); | ||
385 | } | ||
386 | |||
387 | static int ccm4309_aes_nx_decrypt(struct aead_request *req) | ||
388 | { | ||
389 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
390 | struct blkcipher_desc desc; | ||
391 | u8 *iv = nx_ctx->priv.ccm.iv; | ||
392 | |||
393 | iv[0] = 3; | ||
394 | memcpy(iv + 1, nx_ctx->priv.ccm.nonce, 3); | ||
395 | memcpy(iv + 4, req->iv, 8); | ||
396 | |||
397 | desc.info = iv; | ||
398 | desc.tfm = (struct crypto_blkcipher *)req->base.tfm; | ||
399 | |||
400 | return ccm_nx_decrypt(req, &desc); | ||
401 | } | ||
402 | |||
403 | static int ccm_aes_nx_decrypt(struct aead_request *req) | ||
404 | { | ||
405 | struct blkcipher_desc desc; | ||
406 | int rc; | ||
407 | |||
408 | desc.info = req->iv; | ||
409 | desc.tfm = (struct crypto_blkcipher *)req->base.tfm; | ||
410 | |||
411 | rc = crypto_ccm_check_iv(desc.info); | ||
412 | if (rc) | ||
413 | return rc; | ||
414 | |||
415 | return ccm_nx_decrypt(req, &desc); | ||
416 | } | ||
417 | |||
418 | /* tell the block cipher walk routines that this is a stream cipher by | ||
419 | * setting cra_blocksize to 1. Even using blkcipher_walk_virt_block | ||
420 | * during encrypt/decrypt doesn't solve this problem, because it calls | ||
421 | * blkcipher_walk_done under the covers, which doesn't use walk->blocksize, | ||
422 | * but instead uses this tfm->blocksize. */ | ||
423 | struct crypto_alg nx_ccm_aes_alg = { | ||
424 | .cra_name = "ccm(aes)", | ||
425 | .cra_driver_name = "ccm-aes-nx", | ||
426 | .cra_priority = 300, | ||
427 | .cra_flags = CRYPTO_ALG_TYPE_AEAD | | ||
428 | CRYPTO_ALG_NEED_FALLBACK, | ||
429 | .cra_blocksize = 1, | ||
430 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
431 | .cra_type = &crypto_aead_type, | ||
432 | .cra_module = THIS_MODULE, | ||
433 | .cra_list = LIST_HEAD_INIT(nx_ccm_aes_alg.cra_list), | ||
434 | .cra_init = nx_crypto_ctx_aes_ccm_init, | ||
435 | .cra_exit = nx_crypto_ctx_exit, | ||
436 | .cra_aead = { | ||
437 | .ivsize = AES_BLOCK_SIZE, | ||
438 | .maxauthsize = AES_BLOCK_SIZE, | ||
439 | .setkey = ccm_aes_nx_set_key, | ||
440 | .setauthsize = ccm_aes_nx_setauthsize, | ||
441 | .encrypt = ccm_aes_nx_encrypt, | ||
442 | .decrypt = ccm_aes_nx_decrypt, | ||
443 | } | ||
444 | }; | ||
445 | |||
446 | struct crypto_alg nx_ccm4309_aes_alg = { | ||
447 | .cra_name = "rfc4309(ccm(aes))", | ||
448 | .cra_driver_name = "rfc4309-ccm-aes-nx", | ||
449 | .cra_priority = 300, | ||
450 | .cra_flags = CRYPTO_ALG_TYPE_AEAD | | ||
451 | CRYPTO_ALG_NEED_FALLBACK, | ||
452 | .cra_blocksize = 1, | ||
453 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
454 | .cra_type = &crypto_nivaead_type, | ||
455 | .cra_module = THIS_MODULE, | ||
456 | .cra_list = LIST_HEAD_INIT(nx_ccm4309_aes_alg.cra_list), | ||
457 | .cra_init = nx_crypto_ctx_aes_ccm_init, | ||
458 | .cra_exit = nx_crypto_ctx_exit, | ||
459 | .cra_aead = { | ||
460 | .ivsize = 8, | ||
461 | .maxauthsize = AES_BLOCK_SIZE, | ||
462 | .setkey = ccm4309_aes_nx_set_key, | ||
463 | .setauthsize = ccm4309_aes_nx_setauthsize, | ||
464 | .encrypt = ccm4309_aes_nx_encrypt, | ||
465 | .decrypt = ccm4309_aes_nx_decrypt, | ||
466 | .geniv = "seqiv", | ||
467 | } | ||
468 | }; | ||
diff --git a/drivers/crypto/nx/nx-aes-ctr.c b/drivers/crypto/nx/nx-aes-ctr.c new file mode 100644 index 000000000000..52d4eb05e8f7 --- /dev/null +++ b/drivers/crypto/nx/nx-aes-ctr.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /** | ||
2 | * AES CTR routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/aes.h> | ||
23 | #include <crypto/ctr.h> | ||
24 | #include <crypto/algapi.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/crypto.h> | ||
28 | #include <asm/vio.h> | ||
29 | |||
30 | #include "nx_csbcpb.h" | ||
31 | #include "nx.h" | ||
32 | |||
33 | |||
34 | static int ctr_aes_nx_set_key(struct crypto_tfm *tfm, | ||
35 | const u8 *in_key, | ||
36 | unsigned int key_len) | ||
37 | { | ||
38 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); | ||
39 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
40 | |||
41 | nx_ctx_init(nx_ctx, HCOP_FC_AES); | ||
42 | |||
43 | switch (key_len) { | ||
44 | case AES_KEYSIZE_128: | ||
45 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128); | ||
46 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128]; | ||
47 | break; | ||
48 | case AES_KEYSIZE_192: | ||
49 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192); | ||
50 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192]; | ||
51 | break; | ||
52 | case AES_KEYSIZE_256: | ||
53 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256); | ||
54 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256]; | ||
55 | break; | ||
56 | default: | ||
57 | return -EINVAL; | ||
58 | } | ||
59 | |||
60 | csbcpb->cpb.hdr.mode = NX_MODE_AES_CTR; | ||
61 | memcpy(csbcpb->cpb.aes_ctr.key, in_key, key_len); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int ctr3686_aes_nx_set_key(struct crypto_tfm *tfm, | ||
67 | const u8 *in_key, | ||
68 | unsigned int key_len) | ||
69 | { | ||
70 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); | ||
71 | |||
72 | if (key_len < CTR_RFC3686_NONCE_SIZE) | ||
73 | return -EINVAL; | ||
74 | |||
75 | memcpy(nx_ctx->priv.ctr.iv, | ||
76 | in_key + key_len - CTR_RFC3686_NONCE_SIZE, | ||
77 | CTR_RFC3686_NONCE_SIZE); | ||
78 | |||
79 | key_len -= CTR_RFC3686_NONCE_SIZE; | ||
80 | |||
81 | return ctr_aes_nx_set_key(tfm, in_key, key_len); | ||
82 | } | ||
83 | |||
84 | static int ctr_aes_nx_crypt(struct blkcipher_desc *desc, | ||
85 | struct scatterlist *dst, | ||
86 | struct scatterlist *src, | ||
87 | unsigned int nbytes) | ||
88 | { | ||
89 | struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm); | ||
90 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
91 | int rc; | ||
92 | |||
93 | if (nbytes > nx_ctx->ap->databytelen) | ||
94 | return -EINVAL; | ||
95 | |||
96 | rc = nx_build_sg_lists(nx_ctx, desc, dst, src, nbytes, | ||
97 | csbcpb->cpb.aes_ctr.iv); | ||
98 | if (rc) | ||
99 | goto out; | ||
100 | |||
101 | if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) { | ||
102 | rc = -EINVAL; | ||
103 | goto out; | ||
104 | } | ||
105 | |||
106 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
107 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
108 | if (rc) | ||
109 | goto out; | ||
110 | |||
111 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
112 | atomic64_add(csbcpb->csb.processed_byte_count, | ||
113 | &(nx_ctx->stats->aes_bytes)); | ||
114 | out: | ||
115 | return rc; | ||
116 | } | ||
117 | |||
118 | static int ctr3686_aes_nx_crypt(struct blkcipher_desc *desc, | ||
119 | struct scatterlist *dst, | ||
120 | struct scatterlist *src, | ||
121 | unsigned int nbytes) | ||
122 | { | ||
123 | struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm); | ||
124 | u8 *iv = nx_ctx->priv.ctr.iv; | ||
125 | |||
126 | memcpy(iv + CTR_RFC3686_NONCE_SIZE, | ||
127 | desc->info, CTR_RFC3686_IV_SIZE); | ||
128 | iv[15] = 1; | ||
129 | |||
130 | desc->info = nx_ctx->priv.ctr.iv; | ||
131 | |||
132 | return ctr_aes_nx_crypt(desc, dst, src, nbytes); | ||
133 | } | ||
134 | |||
135 | struct crypto_alg nx_ctr_aes_alg = { | ||
136 | .cra_name = "ctr(aes)", | ||
137 | .cra_driver_name = "ctr-aes-nx", | ||
138 | .cra_priority = 300, | ||
139 | .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, | ||
140 | .cra_blocksize = 1, | ||
141 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
142 | .cra_type = &crypto_blkcipher_type, | ||
143 | .cra_module = THIS_MODULE, | ||
144 | .cra_list = LIST_HEAD_INIT(nx_ctr_aes_alg.cra_list), | ||
145 | .cra_init = nx_crypto_ctx_aes_ctr_init, | ||
146 | .cra_exit = nx_crypto_ctx_exit, | ||
147 | .cra_blkcipher = { | ||
148 | .min_keysize = AES_MIN_KEY_SIZE, | ||
149 | .max_keysize = AES_MAX_KEY_SIZE, | ||
150 | .ivsize = AES_BLOCK_SIZE, | ||
151 | .setkey = ctr_aes_nx_set_key, | ||
152 | .encrypt = ctr_aes_nx_crypt, | ||
153 | .decrypt = ctr_aes_nx_crypt, | ||
154 | } | ||
155 | }; | ||
156 | |||
157 | struct crypto_alg nx_ctr3686_aes_alg = { | ||
158 | .cra_name = "rfc3686(ctr(aes))", | ||
159 | .cra_driver_name = "rfc3686-ctr-aes-nx", | ||
160 | .cra_priority = 300, | ||
161 | .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, | ||
162 | .cra_blocksize = 1, | ||
163 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
164 | .cra_type = &crypto_blkcipher_type, | ||
165 | .cra_module = THIS_MODULE, | ||
166 | .cra_list = LIST_HEAD_INIT(nx_ctr3686_aes_alg.cra_list), | ||
167 | .cra_init = nx_crypto_ctx_aes_ctr_init, | ||
168 | .cra_exit = nx_crypto_ctx_exit, | ||
169 | .cra_blkcipher = { | ||
170 | .min_keysize = AES_MIN_KEY_SIZE + CTR_RFC3686_NONCE_SIZE, | ||
171 | .max_keysize = AES_MAX_KEY_SIZE + CTR_RFC3686_NONCE_SIZE, | ||
172 | .ivsize = CTR_RFC3686_IV_SIZE, | ||
173 | .geniv = "seqiv", | ||
174 | .setkey = ctr3686_aes_nx_set_key, | ||
175 | .encrypt = ctr3686_aes_nx_crypt, | ||
176 | .decrypt = ctr3686_aes_nx_crypt, | ||
177 | } | ||
178 | }; | ||
diff --git a/drivers/crypto/nx/nx-aes-ecb.c b/drivers/crypto/nx/nx-aes-ecb.c new file mode 100644 index 000000000000..7b77bc2d1df4 --- /dev/null +++ b/drivers/crypto/nx/nx-aes-ecb.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /** | ||
2 | * AES ECB routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/aes.h> | ||
23 | #include <crypto/algapi.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/crypto.h> | ||
27 | #include <asm/vio.h> | ||
28 | |||
29 | #include "nx_csbcpb.h" | ||
30 | #include "nx.h" | ||
31 | |||
32 | |||
33 | static int ecb_aes_nx_set_key(struct crypto_tfm *tfm, | ||
34 | const u8 *in_key, | ||
35 | unsigned int key_len) | ||
36 | { | ||
37 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); | ||
38 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
39 | |||
40 | nx_ctx_init(nx_ctx, HCOP_FC_AES); | ||
41 | |||
42 | switch (key_len) { | ||
43 | case AES_KEYSIZE_128: | ||
44 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128); | ||
45 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128]; | ||
46 | break; | ||
47 | case AES_KEYSIZE_192: | ||
48 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192); | ||
49 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192]; | ||
50 | break; | ||
51 | case AES_KEYSIZE_256: | ||
52 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256); | ||
53 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256]; | ||
54 | break; | ||
55 | default: | ||
56 | return -EINVAL; | ||
57 | } | ||
58 | |||
59 | csbcpb->cpb.hdr.mode = NX_MODE_AES_ECB; | ||
60 | memcpy(csbcpb->cpb.aes_ecb.key, in_key, key_len); | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int ecb_aes_nx_crypt(struct blkcipher_desc *desc, | ||
66 | struct scatterlist *dst, | ||
67 | struct scatterlist *src, | ||
68 | unsigned int nbytes, | ||
69 | int enc) | ||
70 | { | ||
71 | struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm); | ||
72 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
73 | int rc; | ||
74 | |||
75 | if (nbytes > nx_ctx->ap->databytelen) | ||
76 | return -EINVAL; | ||
77 | |||
78 | if (enc) | ||
79 | NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT; | ||
80 | else | ||
81 | NX_CPB_FDM(csbcpb) &= ~NX_FDM_ENDE_ENCRYPT; | ||
82 | |||
83 | rc = nx_build_sg_lists(nx_ctx, desc, dst, src, nbytes, NULL); | ||
84 | if (rc) | ||
85 | goto out; | ||
86 | |||
87 | if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) { | ||
88 | rc = -EINVAL; | ||
89 | goto out; | ||
90 | } | ||
91 | |||
92 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
93 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
94 | if (rc) | ||
95 | goto out; | ||
96 | |||
97 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
98 | atomic64_add(csbcpb->csb.processed_byte_count, | ||
99 | &(nx_ctx->stats->aes_bytes)); | ||
100 | out: | ||
101 | return rc; | ||
102 | } | ||
103 | |||
104 | static int ecb_aes_nx_encrypt(struct blkcipher_desc *desc, | ||
105 | struct scatterlist *dst, | ||
106 | struct scatterlist *src, | ||
107 | unsigned int nbytes) | ||
108 | { | ||
109 | return ecb_aes_nx_crypt(desc, dst, src, nbytes, 1); | ||
110 | } | ||
111 | |||
112 | static int ecb_aes_nx_decrypt(struct blkcipher_desc *desc, | ||
113 | struct scatterlist *dst, | ||
114 | struct scatterlist *src, | ||
115 | unsigned int nbytes) | ||
116 | { | ||
117 | return ecb_aes_nx_crypt(desc, dst, src, nbytes, 0); | ||
118 | } | ||
119 | |||
120 | struct crypto_alg nx_ecb_aes_alg = { | ||
121 | .cra_name = "ecb(aes)", | ||
122 | .cra_driver_name = "ecb-aes-nx", | ||
123 | .cra_priority = 300, | ||
124 | .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, | ||
125 | .cra_blocksize = AES_BLOCK_SIZE, | ||
126 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
127 | .cra_type = &crypto_blkcipher_type, | ||
128 | .cra_module = THIS_MODULE, | ||
129 | .cra_list = LIST_HEAD_INIT(nx_ecb_aes_alg.cra_list), | ||
130 | .cra_init = nx_crypto_ctx_aes_ecb_init, | ||
131 | .cra_exit = nx_crypto_ctx_exit, | ||
132 | .cra_blkcipher = { | ||
133 | .min_keysize = AES_MIN_KEY_SIZE, | ||
134 | .max_keysize = AES_MAX_KEY_SIZE, | ||
135 | .setkey = ecb_aes_nx_set_key, | ||
136 | .encrypt = ecb_aes_nx_encrypt, | ||
137 | .decrypt = ecb_aes_nx_decrypt, | ||
138 | } | ||
139 | }; | ||
diff --git a/drivers/crypto/nx/nx-aes-gcm.c b/drivers/crypto/nx/nx-aes-gcm.c new file mode 100644 index 000000000000..9ab1c7341dac --- /dev/null +++ b/drivers/crypto/nx/nx-aes-gcm.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /** | ||
2 | * AES GCM routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/internal/aead.h> | ||
23 | #include <crypto/aes.h> | ||
24 | #include <crypto/algapi.h> | ||
25 | #include <crypto/scatterwalk.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/types.h> | ||
28 | #include <linux/crypto.h> | ||
29 | #include <asm/vio.h> | ||
30 | |||
31 | #include "nx_csbcpb.h" | ||
32 | #include "nx.h" | ||
33 | |||
34 | |||
35 | static int gcm_aes_nx_set_key(struct crypto_aead *tfm, | ||
36 | const u8 *in_key, | ||
37 | unsigned int key_len) | ||
38 | { | ||
39 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base); | ||
40 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
41 | struct nx_csbcpb *csbcpb_aead = nx_ctx->csbcpb_aead; | ||
42 | |||
43 | nx_ctx_init(nx_ctx, HCOP_FC_AES); | ||
44 | |||
45 | switch (key_len) { | ||
46 | case AES_KEYSIZE_128: | ||
47 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128); | ||
48 | NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_128); | ||
49 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128]; | ||
50 | break; | ||
51 | case AES_KEYSIZE_192: | ||
52 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192); | ||
53 | NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_192); | ||
54 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192]; | ||
55 | break; | ||
56 | case AES_KEYSIZE_256: | ||
57 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256); | ||
58 | NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_256); | ||
59 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256]; | ||
60 | break; | ||
61 | default: | ||
62 | return -EINVAL; | ||
63 | } | ||
64 | |||
65 | csbcpb->cpb.hdr.mode = NX_MODE_AES_GCM; | ||
66 | memcpy(csbcpb->cpb.aes_gcm.key, in_key, key_len); | ||
67 | |||
68 | csbcpb_aead->cpb.hdr.mode = NX_MODE_AES_GCA; | ||
69 | memcpy(csbcpb_aead->cpb.aes_gca.key, in_key, key_len); | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static int gcm4106_aes_nx_set_key(struct crypto_aead *tfm, | ||
75 | const u8 *in_key, | ||
76 | unsigned int key_len) | ||
77 | { | ||
78 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base); | ||
79 | char *nonce = nx_ctx->priv.gcm.nonce; | ||
80 | int rc; | ||
81 | |||
82 | if (key_len < 4) | ||
83 | return -EINVAL; | ||
84 | |||
85 | key_len -= 4; | ||
86 | |||
87 | rc = gcm_aes_nx_set_key(tfm, in_key, key_len); | ||
88 | if (rc) | ||
89 | goto out; | ||
90 | |||
91 | memcpy(nonce, in_key + key_len, 4); | ||
92 | out: | ||
93 | return rc; | ||
94 | } | ||
95 | |||
96 | static int gcm_aes_nx_setauthsize(struct crypto_aead *tfm, | ||
97 | unsigned int authsize) | ||
98 | { | ||
99 | if (authsize > crypto_aead_alg(tfm)->maxauthsize) | ||
100 | return -EINVAL; | ||
101 | |||
102 | crypto_aead_crt(tfm)->authsize = authsize; | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int gcm4106_aes_nx_setauthsize(struct crypto_aead *tfm, | ||
108 | unsigned int authsize) | ||
109 | { | ||
110 | switch (authsize) { | ||
111 | case 8: | ||
112 | case 12: | ||
113 | case 16: | ||
114 | break; | ||
115 | default: | ||
116 | return -EINVAL; | ||
117 | } | ||
118 | |||
119 | crypto_aead_crt(tfm)->authsize = authsize; | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int nx_gca(struct nx_crypto_ctx *nx_ctx, | ||
125 | struct aead_request *req, | ||
126 | u8 *out) | ||
127 | { | ||
128 | struct nx_csbcpb *csbcpb_aead = nx_ctx->csbcpb_aead; | ||
129 | int rc = -EINVAL; | ||
130 | struct scatter_walk walk; | ||
131 | struct nx_sg *nx_sg = nx_ctx->in_sg; | ||
132 | |||
133 | if (req->assoclen > nx_ctx->ap->databytelen) | ||
134 | goto out; | ||
135 | |||
136 | if (req->assoclen <= AES_BLOCK_SIZE) { | ||
137 | scatterwalk_start(&walk, req->assoc); | ||
138 | scatterwalk_copychunks(out, &walk, req->assoclen, | ||
139 | SCATTERWALK_FROM_SG); | ||
140 | scatterwalk_done(&walk, SCATTERWALK_FROM_SG, 0); | ||
141 | |||
142 | rc = 0; | ||
143 | goto out; | ||
144 | } | ||
145 | |||
146 | nx_sg = nx_walk_and_build(nx_sg, nx_ctx->ap->sglen, req->assoc, 0, | ||
147 | req->assoclen); | ||
148 | nx_ctx->op_aead.inlen = (nx_ctx->in_sg - nx_sg) * sizeof(struct nx_sg); | ||
149 | |||
150 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op_aead, | ||
151 | req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
152 | if (rc) | ||
153 | goto out; | ||
154 | |||
155 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
156 | atomic64_add(req->assoclen, &(nx_ctx->stats->aes_bytes)); | ||
157 | |||
158 | memcpy(out, csbcpb_aead->cpb.aes_gca.out_pat, AES_BLOCK_SIZE); | ||
159 | out: | ||
160 | return rc; | ||
161 | } | ||
162 | |||
163 | static int gcm_aes_nx_crypt(struct aead_request *req, int enc) | ||
164 | { | ||
165 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
166 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
167 | struct blkcipher_desc desc; | ||
168 | unsigned int nbytes = req->cryptlen; | ||
169 | int rc = -EINVAL; | ||
170 | |||
171 | if (nbytes > nx_ctx->ap->databytelen) | ||
172 | goto out; | ||
173 | |||
174 | desc.info = nx_ctx->priv.gcm.iv; | ||
175 | /* initialize the counter */ | ||
176 | *(u32 *)(desc.info + NX_GCM_CTR_OFFSET) = 1; | ||
177 | |||
178 | /* For scenarios where the input message is zero length, AES CTR mode | ||
179 | * may be used. Set the source data to be a single block (16B) of all | ||
180 | * zeros, and set the input IV value to be the same as the GMAC IV | ||
181 | * value. - nx_wb 4.8.1.3 */ | ||
182 | if (nbytes == 0) { | ||
183 | char src[AES_BLOCK_SIZE] = {}; | ||
184 | struct scatterlist sg; | ||
185 | |||
186 | desc.tfm = crypto_alloc_blkcipher("ctr(aes)", 0, 0); | ||
187 | if (IS_ERR(desc.tfm)) { | ||
188 | rc = -ENOMEM; | ||
189 | goto out; | ||
190 | } | ||
191 | |||
192 | crypto_blkcipher_setkey(desc.tfm, csbcpb->cpb.aes_gcm.key, | ||
193 | NX_CPB_KEY_SIZE(csbcpb) == NX_KS_AES_128 ? 16 : | ||
194 | NX_CPB_KEY_SIZE(csbcpb) == NX_KS_AES_192 ? 24 : 32); | ||
195 | |||
196 | sg_init_one(&sg, src, AES_BLOCK_SIZE); | ||
197 | if (enc) | ||
198 | crypto_blkcipher_encrypt_iv(&desc, req->dst, &sg, | ||
199 | AES_BLOCK_SIZE); | ||
200 | else | ||
201 | crypto_blkcipher_decrypt_iv(&desc, req->dst, &sg, | ||
202 | AES_BLOCK_SIZE); | ||
203 | crypto_free_blkcipher(desc.tfm); | ||
204 | |||
205 | rc = 0; | ||
206 | goto out; | ||
207 | } | ||
208 | |||
209 | desc.tfm = (struct crypto_blkcipher *)req->base.tfm; | ||
210 | |||
211 | csbcpb->cpb.aes_gcm.bit_length_aad = req->assoclen * 8; | ||
212 | |||
213 | if (req->assoclen) { | ||
214 | rc = nx_gca(nx_ctx, req, csbcpb->cpb.aes_gcm.in_pat_or_aad); | ||
215 | if (rc) | ||
216 | goto out; | ||
217 | } | ||
218 | |||
219 | if (enc) | ||
220 | NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT; | ||
221 | else | ||
222 | nbytes -= AES_BLOCK_SIZE; | ||
223 | |||
224 | csbcpb->cpb.aes_gcm.bit_length_data = nbytes * 8; | ||
225 | |||
226 | rc = nx_build_sg_lists(nx_ctx, &desc, req->dst, req->src, nbytes, | ||
227 | csbcpb->cpb.aes_gcm.iv_or_cnt); | ||
228 | if (rc) | ||
229 | goto out; | ||
230 | |||
231 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
232 | req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
233 | if (rc) | ||
234 | goto out; | ||
235 | |||
236 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
237 | atomic64_add(csbcpb->csb.processed_byte_count, | ||
238 | &(nx_ctx->stats->aes_bytes)); | ||
239 | |||
240 | if (enc) { | ||
241 | /* copy out the auth tag */ | ||
242 | scatterwalk_map_and_copy(csbcpb->cpb.aes_gcm.out_pat_or_mac, | ||
243 | req->dst, nbytes, | ||
244 | crypto_aead_authsize(crypto_aead_reqtfm(req)), | ||
245 | SCATTERWALK_TO_SG); | ||
246 | } else if (req->assoclen) { | ||
247 | u8 *itag = nx_ctx->priv.gcm.iauth_tag; | ||
248 | u8 *otag = csbcpb->cpb.aes_gcm.out_pat_or_mac; | ||
249 | |||
250 | scatterwalk_map_and_copy(itag, req->dst, nbytes, | ||
251 | crypto_aead_authsize(crypto_aead_reqtfm(req)), | ||
252 | SCATTERWALK_FROM_SG); | ||
253 | rc = memcmp(itag, otag, | ||
254 | crypto_aead_authsize(crypto_aead_reqtfm(req))) ? | ||
255 | -EBADMSG : 0; | ||
256 | } | ||
257 | out: | ||
258 | return rc; | ||
259 | } | ||
260 | |||
261 | static int gcm_aes_nx_encrypt(struct aead_request *req) | ||
262 | { | ||
263 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
264 | char *iv = nx_ctx->priv.gcm.iv; | ||
265 | |||
266 | memcpy(iv, req->iv, 12); | ||
267 | |||
268 | return gcm_aes_nx_crypt(req, 1); | ||
269 | } | ||
270 | |||
271 | static int gcm_aes_nx_decrypt(struct aead_request *req) | ||
272 | { | ||
273 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
274 | char *iv = nx_ctx->priv.gcm.iv; | ||
275 | |||
276 | memcpy(iv, req->iv, 12); | ||
277 | |||
278 | return gcm_aes_nx_crypt(req, 0); | ||
279 | } | ||
280 | |||
281 | static int gcm4106_aes_nx_encrypt(struct aead_request *req) | ||
282 | { | ||
283 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
284 | char *iv = nx_ctx->priv.gcm.iv; | ||
285 | char *nonce = nx_ctx->priv.gcm.nonce; | ||
286 | |||
287 | memcpy(iv, nonce, NX_GCM4106_NONCE_LEN); | ||
288 | memcpy(iv + NX_GCM4106_NONCE_LEN, req->iv, 8); | ||
289 | |||
290 | return gcm_aes_nx_crypt(req, 1); | ||
291 | } | ||
292 | |||
293 | static int gcm4106_aes_nx_decrypt(struct aead_request *req) | ||
294 | { | ||
295 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm); | ||
296 | char *iv = nx_ctx->priv.gcm.iv; | ||
297 | char *nonce = nx_ctx->priv.gcm.nonce; | ||
298 | |||
299 | memcpy(iv, nonce, NX_GCM4106_NONCE_LEN); | ||
300 | memcpy(iv + NX_GCM4106_NONCE_LEN, req->iv, 8); | ||
301 | |||
302 | return gcm_aes_nx_crypt(req, 0); | ||
303 | } | ||
304 | |||
305 | /* tell the block cipher walk routines that this is a stream cipher by | ||
306 | * setting cra_blocksize to 1. Even using blkcipher_walk_virt_block | ||
307 | * during encrypt/decrypt doesn't solve this problem, because it calls | ||
308 | * blkcipher_walk_done under the covers, which doesn't use walk->blocksize, | ||
309 | * but instead uses this tfm->blocksize. */ | ||
310 | struct crypto_alg nx_gcm_aes_alg = { | ||
311 | .cra_name = "gcm(aes)", | ||
312 | .cra_driver_name = "gcm-aes-nx", | ||
313 | .cra_priority = 300, | ||
314 | .cra_flags = CRYPTO_ALG_TYPE_AEAD, | ||
315 | .cra_blocksize = 1, | ||
316 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
317 | .cra_type = &crypto_aead_type, | ||
318 | .cra_module = THIS_MODULE, | ||
319 | .cra_list = LIST_HEAD_INIT(nx_gcm_aes_alg.cra_list), | ||
320 | .cra_init = nx_crypto_ctx_aes_gcm_init, | ||
321 | .cra_exit = nx_crypto_ctx_exit, | ||
322 | .cra_aead = { | ||
323 | .ivsize = AES_BLOCK_SIZE, | ||
324 | .maxauthsize = AES_BLOCK_SIZE, | ||
325 | .setkey = gcm_aes_nx_set_key, | ||
326 | .setauthsize = gcm_aes_nx_setauthsize, | ||
327 | .encrypt = gcm_aes_nx_encrypt, | ||
328 | .decrypt = gcm_aes_nx_decrypt, | ||
329 | } | ||
330 | }; | ||
331 | |||
332 | struct crypto_alg nx_gcm4106_aes_alg = { | ||
333 | .cra_name = "rfc4106(gcm(aes))", | ||
334 | .cra_driver_name = "rfc4106-gcm-aes-nx", | ||
335 | .cra_priority = 300, | ||
336 | .cra_flags = CRYPTO_ALG_TYPE_AEAD, | ||
337 | .cra_blocksize = 1, | ||
338 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
339 | .cra_type = &crypto_nivaead_type, | ||
340 | .cra_module = THIS_MODULE, | ||
341 | .cra_list = LIST_HEAD_INIT(nx_gcm4106_aes_alg.cra_list), | ||
342 | .cra_init = nx_crypto_ctx_aes_gcm_init, | ||
343 | .cra_exit = nx_crypto_ctx_exit, | ||
344 | .cra_aead = { | ||
345 | .ivsize = 8, | ||
346 | .maxauthsize = AES_BLOCK_SIZE, | ||
347 | .geniv = "seqiv", | ||
348 | .setkey = gcm4106_aes_nx_set_key, | ||
349 | .setauthsize = gcm4106_aes_nx_setauthsize, | ||
350 | .encrypt = gcm4106_aes_nx_encrypt, | ||
351 | .decrypt = gcm4106_aes_nx_decrypt, | ||
352 | } | ||
353 | }; | ||
diff --git a/drivers/crypto/nx/nx-aes-xcbc.c b/drivers/crypto/nx/nx-aes-xcbc.c new file mode 100644 index 000000000000..93923e4628c0 --- /dev/null +++ b/drivers/crypto/nx/nx-aes-xcbc.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /** | ||
2 | * AES XCBC routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/internal/hash.h> | ||
23 | #include <crypto/aes.h> | ||
24 | #include <crypto/algapi.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/crypto.h> | ||
28 | #include <asm/vio.h> | ||
29 | |||
30 | #include "nx_csbcpb.h" | ||
31 | #include "nx.h" | ||
32 | |||
33 | |||
34 | struct xcbc_state { | ||
35 | u8 state[AES_BLOCK_SIZE]; | ||
36 | unsigned int count; | ||
37 | u8 buffer[AES_BLOCK_SIZE]; | ||
38 | }; | ||
39 | |||
40 | static int nx_xcbc_set_key(struct crypto_shash *desc, | ||
41 | const u8 *in_key, | ||
42 | unsigned int key_len) | ||
43 | { | ||
44 | struct nx_crypto_ctx *nx_ctx = crypto_shash_ctx(desc); | ||
45 | |||
46 | switch (key_len) { | ||
47 | case AES_KEYSIZE_128: | ||
48 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128]; | ||
49 | break; | ||
50 | default: | ||
51 | return -EINVAL; | ||
52 | } | ||
53 | |||
54 | memcpy(nx_ctx->priv.xcbc.key, in_key, key_len); | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static int nx_xcbc_init(struct shash_desc *desc) | ||
60 | { | ||
61 | struct xcbc_state *sctx = shash_desc_ctx(desc); | ||
62 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
63 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
64 | struct nx_sg *out_sg; | ||
65 | |||
66 | nx_ctx_init(nx_ctx, HCOP_FC_AES); | ||
67 | |||
68 | memset(sctx, 0, sizeof *sctx); | ||
69 | |||
70 | NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128); | ||
71 | csbcpb->cpb.hdr.mode = NX_MODE_AES_XCBC_MAC; | ||
72 | |||
73 | memcpy(csbcpb->cpb.aes_xcbc.key, nx_ctx->priv.xcbc.key, AES_BLOCK_SIZE); | ||
74 | memset(nx_ctx->priv.xcbc.key, 0, sizeof *nx_ctx->priv.xcbc.key); | ||
75 | |||
76 | out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state, | ||
77 | AES_BLOCK_SIZE, nx_ctx->ap->sglen); | ||
78 | nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int nx_xcbc_update(struct shash_desc *desc, | ||
84 | const u8 *data, | ||
85 | unsigned int len) | ||
86 | { | ||
87 | struct xcbc_state *sctx = shash_desc_ctx(desc); | ||
88 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
89 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
90 | struct nx_sg *in_sg; | ||
91 | u32 to_process, leftover; | ||
92 | int rc = 0; | ||
93 | |||
94 | if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) { | ||
95 | /* we've hit the nx chip previously and we're updating again, | ||
96 | * so copy over the partial digest */ | ||
97 | memcpy(csbcpb->cpb.aes_xcbc.cv, | ||
98 | csbcpb->cpb.aes_xcbc.out_cv_mac, AES_BLOCK_SIZE); | ||
99 | } | ||
100 | |||
101 | /* 2 cases for total data len: | ||
102 | * 1: <= AES_BLOCK_SIZE: copy into state, return 0 | ||
103 | * 2: > AES_BLOCK_SIZE: process X blocks, copy in leftover | ||
104 | */ | ||
105 | if (len + sctx->count <= AES_BLOCK_SIZE) { | ||
106 | memcpy(sctx->buffer + sctx->count, data, len); | ||
107 | sctx->count += len; | ||
108 | goto out; | ||
109 | } | ||
110 | |||
111 | /* to_process: the AES_BLOCK_SIZE data chunk to process in this | ||
112 | * update */ | ||
113 | to_process = (sctx->count + len) & ~(AES_BLOCK_SIZE - 1); | ||
114 | leftover = (sctx->count + len) & (AES_BLOCK_SIZE - 1); | ||
115 | |||
116 | /* the hardware will not accept a 0 byte operation for this algorithm | ||
117 | * and the operation MUST be finalized to be correct. So if we happen | ||
118 | * to get an update that falls on a block sized boundary, we must | ||
119 | * save off the last block to finalize with later. */ | ||
120 | if (!leftover) { | ||
121 | to_process -= AES_BLOCK_SIZE; | ||
122 | leftover = AES_BLOCK_SIZE; | ||
123 | } | ||
124 | |||
125 | if (sctx->count) { | ||
126 | in_sg = nx_build_sg_list(nx_ctx->in_sg, sctx->buffer, | ||
127 | sctx->count, nx_ctx->ap->sglen); | ||
128 | in_sg = nx_build_sg_list(in_sg, (u8 *)data, | ||
129 | to_process - sctx->count, | ||
130 | nx_ctx->ap->sglen); | ||
131 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * | ||
132 | sizeof(struct nx_sg); | ||
133 | } else { | ||
134 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)data, to_process, | ||
135 | nx_ctx->ap->sglen); | ||
136 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * | ||
137 | sizeof(struct nx_sg); | ||
138 | } | ||
139 | |||
140 | NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; | ||
141 | |||
142 | if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) { | ||
143 | rc = -EINVAL; | ||
144 | goto out; | ||
145 | } | ||
146 | |||
147 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
148 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
149 | if (rc) | ||
150 | goto out; | ||
151 | |||
152 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
153 | |||
154 | /* copy the leftover back into the state struct */ | ||
155 | memcpy(sctx->buffer, data + len - leftover, leftover); | ||
156 | sctx->count = leftover; | ||
157 | |||
158 | /* everything after the first update is continuation */ | ||
159 | NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; | ||
160 | out: | ||
161 | return rc; | ||
162 | } | ||
163 | |||
164 | static int nx_xcbc_final(struct shash_desc *desc, u8 *out) | ||
165 | { | ||
166 | struct xcbc_state *sctx = shash_desc_ctx(desc); | ||
167 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
168 | struct nx_csbcpb *csbcpb = nx_ctx->csbcpb; | ||
169 | struct nx_sg *in_sg, *out_sg; | ||
170 | int rc = 0; | ||
171 | |||
172 | if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) { | ||
173 | /* we've hit the nx chip previously, now we're finalizing, | ||
174 | * so copy over the partial digest */ | ||
175 | memcpy(csbcpb->cpb.aes_xcbc.cv, | ||
176 | csbcpb->cpb.aes_xcbc.out_cv_mac, AES_BLOCK_SIZE); | ||
177 | } else if (sctx->count == 0) { | ||
178 | /* we've never seen an update, so this is a 0 byte op. The | ||
179 | * hardware cannot handle a 0 byte op, so just copy out the | ||
180 | * known 0 byte result. This is cheaper than allocating a | ||
181 | * software context to do a 0 byte op */ | ||
182 | u8 data[] = { 0x75, 0xf0, 0x25, 0x1d, 0x52, 0x8a, 0xc0, 0x1c, | ||
183 | 0x45, 0x73, 0xdf, 0xd5, 0x84, 0xd7, 0x9f, 0x29 }; | ||
184 | memcpy(out, data, sizeof(data)); | ||
185 | goto out; | ||
186 | } | ||
187 | |||
188 | /* final is represented by continuing the operation and indicating that | ||
189 | * this is not an intermediate operation */ | ||
190 | NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE; | ||
191 | |||
192 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buffer, | ||
193 | sctx->count, nx_ctx->ap->sglen); | ||
194 | out_sg = nx_build_sg_list(nx_ctx->out_sg, out, AES_BLOCK_SIZE, | ||
195 | nx_ctx->ap->sglen); | ||
196 | |||
197 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg); | ||
198 | nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg); | ||
199 | |||
200 | if (!nx_ctx->op.outlen) { | ||
201 | rc = -EINVAL; | ||
202 | goto out; | ||
203 | } | ||
204 | |||
205 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
206 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
207 | if (rc) | ||
208 | goto out; | ||
209 | |||
210 | atomic_inc(&(nx_ctx->stats->aes_ops)); | ||
211 | |||
212 | memcpy(out, csbcpb->cpb.aes_xcbc.out_cv_mac, AES_BLOCK_SIZE); | ||
213 | out: | ||
214 | return rc; | ||
215 | } | ||
216 | |||
217 | struct shash_alg nx_shash_aes_xcbc_alg = { | ||
218 | .digestsize = AES_BLOCK_SIZE, | ||
219 | .init = nx_xcbc_init, | ||
220 | .update = nx_xcbc_update, | ||
221 | .final = nx_xcbc_final, | ||
222 | .setkey = nx_xcbc_set_key, | ||
223 | .descsize = sizeof(struct xcbc_state), | ||
224 | .statesize = sizeof(struct xcbc_state), | ||
225 | .base = { | ||
226 | .cra_name = "xcbc(aes)", | ||
227 | .cra_driver_name = "xcbc-aes-nx", | ||
228 | .cra_priority = 300, | ||
229 | .cra_flags = CRYPTO_ALG_TYPE_SHASH, | ||
230 | .cra_blocksize = AES_BLOCK_SIZE, | ||
231 | .cra_module = THIS_MODULE, | ||
232 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
233 | .cra_init = nx_crypto_ctx_aes_xcbc_init, | ||
234 | .cra_exit = nx_crypto_ctx_exit, | ||
235 | } | ||
236 | }; | ||
diff --git a/drivers/crypto/nx/nx-sha256.c b/drivers/crypto/nx/nx-sha256.c new file mode 100644 index 000000000000..9767315f8c0b --- /dev/null +++ b/drivers/crypto/nx/nx-sha256.c | |||
@@ -0,0 +1,246 @@ | |||
1 | /** | ||
2 | * SHA-256 routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/internal/hash.h> | ||
23 | #include <crypto/sha.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <asm/vio.h> | ||
26 | |||
27 | #include "nx_csbcpb.h" | ||
28 | #include "nx.h" | ||
29 | |||
30 | |||
31 | static int nx_sha256_init(struct shash_desc *desc) | ||
32 | { | ||
33 | struct sha256_state *sctx = shash_desc_ctx(desc); | ||
34 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
35 | struct nx_sg *out_sg; | ||
36 | |||
37 | nx_ctx_init(nx_ctx, HCOP_FC_SHA); | ||
38 | |||
39 | memset(sctx, 0, sizeof *sctx); | ||
40 | |||
41 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_SHA256]; | ||
42 | |||
43 | NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA256); | ||
44 | out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state, | ||
45 | SHA256_DIGEST_SIZE, nx_ctx->ap->sglen); | ||
46 | nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg); | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int nx_sha256_update(struct shash_desc *desc, const u8 *data, | ||
52 | unsigned int len) | ||
53 | { | ||
54 | struct sha256_state *sctx = shash_desc_ctx(desc); | ||
55 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
56 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
57 | struct nx_sg *in_sg; | ||
58 | u64 to_process, leftover; | ||
59 | int rc = 0; | ||
60 | |||
61 | if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) { | ||
62 | /* we've hit the nx chip previously and we're updating again, | ||
63 | * so copy over the partial digest */ | ||
64 | memcpy(csbcpb->cpb.sha256.input_partial_digest, | ||
65 | csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE); | ||
66 | } | ||
67 | |||
68 | /* 2 cases for total data len: | ||
69 | * 1: <= SHA256_BLOCK_SIZE: copy into state, return 0 | ||
70 | * 2: > SHA256_BLOCK_SIZE: process X blocks, copy in leftover | ||
71 | */ | ||
72 | if (len + sctx->count <= SHA256_BLOCK_SIZE) { | ||
73 | memcpy(sctx->buf + sctx->count, data, len); | ||
74 | sctx->count += len; | ||
75 | goto out; | ||
76 | } | ||
77 | |||
78 | /* to_process: the SHA256_BLOCK_SIZE data chunk to process in this | ||
79 | * update */ | ||
80 | to_process = (sctx->count + len) & ~(SHA256_BLOCK_SIZE - 1); | ||
81 | leftover = (sctx->count + len) & (SHA256_BLOCK_SIZE - 1); | ||
82 | |||
83 | if (sctx->count) { | ||
84 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf, | ||
85 | sctx->count, nx_ctx->ap->sglen); | ||
86 | in_sg = nx_build_sg_list(in_sg, (u8 *)data, | ||
87 | to_process - sctx->count, | ||
88 | nx_ctx->ap->sglen); | ||
89 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * | ||
90 | sizeof(struct nx_sg); | ||
91 | } else { | ||
92 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)data, | ||
93 | to_process, nx_ctx->ap->sglen); | ||
94 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * | ||
95 | sizeof(struct nx_sg); | ||
96 | } | ||
97 | |||
98 | NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; | ||
99 | |||
100 | if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) { | ||
101 | rc = -EINVAL; | ||
102 | goto out; | ||
103 | } | ||
104 | |||
105 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
106 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
107 | if (rc) | ||
108 | goto out; | ||
109 | |||
110 | atomic_inc(&(nx_ctx->stats->sha256_ops)); | ||
111 | |||
112 | /* copy the leftover back into the state struct */ | ||
113 | memcpy(sctx->buf, data + len - leftover, leftover); | ||
114 | sctx->count = leftover; | ||
115 | |||
116 | csbcpb->cpb.sha256.message_bit_length += (u64) | ||
117 | (csbcpb->cpb.sha256.spbc * 8); | ||
118 | |||
119 | /* everything after the first update is continuation */ | ||
120 | NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; | ||
121 | out: | ||
122 | return rc; | ||
123 | } | ||
124 | |||
125 | static int nx_sha256_final(struct shash_desc *desc, u8 *out) | ||
126 | { | ||
127 | struct sha256_state *sctx = shash_desc_ctx(desc); | ||
128 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
129 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
130 | struct nx_sg *in_sg, *out_sg; | ||
131 | int rc; | ||
132 | |||
133 | if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) { | ||
134 | /* we've hit the nx chip previously, now we're finalizing, | ||
135 | * so copy over the partial digest */ | ||
136 | memcpy(csbcpb->cpb.sha256.input_partial_digest, | ||
137 | csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE); | ||
138 | } | ||
139 | |||
140 | /* final is represented by continuing the operation and indicating that | ||
141 | * this is not an intermediate operation */ | ||
142 | NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE; | ||
143 | |||
144 | csbcpb->cpb.sha256.message_bit_length += (u64)(sctx->count * 8); | ||
145 | |||
146 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf, | ||
147 | sctx->count, nx_ctx->ap->sglen); | ||
148 | out_sg = nx_build_sg_list(nx_ctx->out_sg, out, SHA256_DIGEST_SIZE, | ||
149 | nx_ctx->ap->sglen); | ||
150 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg); | ||
151 | nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg); | ||
152 | |||
153 | if (!nx_ctx->op.outlen) { | ||
154 | rc = -EINVAL; | ||
155 | goto out; | ||
156 | } | ||
157 | |||
158 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
159 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
160 | if (rc) | ||
161 | goto out; | ||
162 | |||
163 | atomic_inc(&(nx_ctx->stats->sha256_ops)); | ||
164 | |||
165 | atomic64_add(csbcpb->cpb.sha256.message_bit_length, | ||
166 | &(nx_ctx->stats->sha256_bytes)); | ||
167 | memcpy(out, csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE); | ||
168 | out: | ||
169 | return rc; | ||
170 | } | ||
171 | |||
172 | static int nx_sha256_export(struct shash_desc *desc, void *out) | ||
173 | { | ||
174 | struct sha256_state *sctx = shash_desc_ctx(desc); | ||
175 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
176 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
177 | struct sha256_state *octx = out; | ||
178 | |||
179 | octx->count = sctx->count + | ||
180 | (csbcpb->cpb.sha256.message_bit_length / 8); | ||
181 | memcpy(octx->buf, sctx->buf, sizeof(octx->buf)); | ||
182 | |||
183 | /* if no data has been processed yet, we need to export SHA256's | ||
184 | * initial data, in case this context gets imported into a software | ||
185 | * context */ | ||
186 | if (csbcpb->cpb.sha256.message_bit_length) | ||
187 | memcpy(octx->state, csbcpb->cpb.sha256.message_digest, | ||
188 | SHA256_DIGEST_SIZE); | ||
189 | else { | ||
190 | octx->state[0] = SHA256_H0; | ||
191 | octx->state[1] = SHA256_H1; | ||
192 | octx->state[2] = SHA256_H2; | ||
193 | octx->state[3] = SHA256_H3; | ||
194 | octx->state[4] = SHA256_H4; | ||
195 | octx->state[5] = SHA256_H5; | ||
196 | octx->state[6] = SHA256_H6; | ||
197 | octx->state[7] = SHA256_H7; | ||
198 | } | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int nx_sha256_import(struct shash_desc *desc, const void *in) | ||
204 | { | ||
205 | struct sha256_state *sctx = shash_desc_ctx(desc); | ||
206 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
207 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
208 | const struct sha256_state *ictx = in; | ||
209 | |||
210 | memcpy(sctx->buf, ictx->buf, sizeof(ictx->buf)); | ||
211 | |||
212 | sctx->count = ictx->count & 0x3f; | ||
213 | csbcpb->cpb.sha256.message_bit_length = (ictx->count & ~0x3f) * 8; | ||
214 | |||
215 | if (csbcpb->cpb.sha256.message_bit_length) { | ||
216 | memcpy(csbcpb->cpb.sha256.message_digest, ictx->state, | ||
217 | SHA256_DIGEST_SIZE); | ||
218 | |||
219 | NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; | ||
220 | NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; | ||
221 | } | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | struct shash_alg nx_shash_sha256_alg = { | ||
227 | .digestsize = SHA256_DIGEST_SIZE, | ||
228 | .init = nx_sha256_init, | ||
229 | .update = nx_sha256_update, | ||
230 | .final = nx_sha256_final, | ||
231 | .export = nx_sha256_export, | ||
232 | .import = nx_sha256_import, | ||
233 | .descsize = sizeof(struct sha256_state), | ||
234 | .statesize = sizeof(struct sha256_state), | ||
235 | .base = { | ||
236 | .cra_name = "sha256", | ||
237 | .cra_driver_name = "sha256-nx", | ||
238 | .cra_priority = 300, | ||
239 | .cra_flags = CRYPTO_ALG_TYPE_SHASH, | ||
240 | .cra_blocksize = SHA256_BLOCK_SIZE, | ||
241 | .cra_module = THIS_MODULE, | ||
242 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
243 | .cra_init = nx_crypto_ctx_sha_init, | ||
244 | .cra_exit = nx_crypto_ctx_exit, | ||
245 | } | ||
246 | }; | ||
diff --git a/drivers/crypto/nx/nx-sha512.c b/drivers/crypto/nx/nx-sha512.c new file mode 100644 index 000000000000..3177b8c3d5f1 --- /dev/null +++ b/drivers/crypto/nx/nx-sha512.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /** | ||
2 | * SHA-512 routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/internal/hash.h> | ||
23 | #include <crypto/sha.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <asm/vio.h> | ||
26 | |||
27 | #include "nx_csbcpb.h" | ||
28 | #include "nx.h" | ||
29 | |||
30 | |||
31 | static int nx_sha512_init(struct shash_desc *desc) | ||
32 | { | ||
33 | struct sha512_state *sctx = shash_desc_ctx(desc); | ||
34 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
35 | struct nx_sg *out_sg; | ||
36 | |||
37 | nx_ctx_init(nx_ctx, HCOP_FC_SHA); | ||
38 | |||
39 | memset(sctx, 0, sizeof *sctx); | ||
40 | |||
41 | nx_ctx->ap = &nx_ctx->props[NX_PROPS_SHA512]; | ||
42 | |||
43 | NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA512); | ||
44 | out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state, | ||
45 | SHA512_DIGEST_SIZE, nx_ctx->ap->sglen); | ||
46 | nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg); | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int nx_sha512_update(struct shash_desc *desc, const u8 *data, | ||
52 | unsigned int len) | ||
53 | { | ||
54 | struct sha512_state *sctx = shash_desc_ctx(desc); | ||
55 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
56 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
57 | struct nx_sg *in_sg; | ||
58 | u64 to_process, leftover, spbc_bits; | ||
59 | int rc = 0; | ||
60 | |||
61 | if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) { | ||
62 | /* we've hit the nx chip previously and we're updating again, | ||
63 | * so copy over the partial digest */ | ||
64 | memcpy(csbcpb->cpb.sha512.input_partial_digest, | ||
65 | csbcpb->cpb.sha512.message_digest, SHA512_DIGEST_SIZE); | ||
66 | } | ||
67 | |||
68 | /* 2 cases for total data len: | ||
69 | * 1: <= SHA512_BLOCK_SIZE: copy into state, return 0 | ||
70 | * 2: > SHA512_BLOCK_SIZE: process X blocks, copy in leftover | ||
71 | */ | ||
72 | if ((u64)len + sctx->count[0] <= SHA512_BLOCK_SIZE) { | ||
73 | memcpy(sctx->buf + sctx->count[0], data, len); | ||
74 | sctx->count[0] += len; | ||
75 | goto out; | ||
76 | } | ||
77 | |||
78 | /* to_process: the SHA512_BLOCK_SIZE data chunk to process in this | ||
79 | * update */ | ||
80 | to_process = (sctx->count[0] + len) & ~(SHA512_BLOCK_SIZE - 1); | ||
81 | leftover = (sctx->count[0] + len) & (SHA512_BLOCK_SIZE - 1); | ||
82 | |||
83 | if (sctx->count[0]) { | ||
84 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf, | ||
85 | sctx->count[0], nx_ctx->ap->sglen); | ||
86 | in_sg = nx_build_sg_list(in_sg, (u8 *)data, | ||
87 | to_process - sctx->count[0], | ||
88 | nx_ctx->ap->sglen); | ||
89 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * | ||
90 | sizeof(struct nx_sg); | ||
91 | } else { | ||
92 | in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)data, | ||
93 | to_process, nx_ctx->ap->sglen); | ||
94 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * | ||
95 | sizeof(struct nx_sg); | ||
96 | } | ||
97 | |||
98 | NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; | ||
99 | |||
100 | if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) { | ||
101 | rc = -EINVAL; | ||
102 | goto out; | ||
103 | } | ||
104 | |||
105 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
106 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
107 | if (rc) | ||
108 | goto out; | ||
109 | |||
110 | atomic_inc(&(nx_ctx->stats->sha512_ops)); | ||
111 | |||
112 | /* copy the leftover back into the state struct */ | ||
113 | memcpy(sctx->buf, data + len - leftover, leftover); | ||
114 | sctx->count[0] = leftover; | ||
115 | |||
116 | spbc_bits = csbcpb->cpb.sha512.spbc * 8; | ||
117 | csbcpb->cpb.sha512.message_bit_length_lo += spbc_bits; | ||
118 | if (csbcpb->cpb.sha512.message_bit_length_lo < spbc_bits) | ||
119 | csbcpb->cpb.sha512.message_bit_length_hi++; | ||
120 | |||
121 | /* everything after the first update is continuation */ | ||
122 | NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; | ||
123 | out: | ||
124 | return rc; | ||
125 | } | ||
126 | |||
127 | static int nx_sha512_final(struct shash_desc *desc, u8 *out) | ||
128 | { | ||
129 | struct sha512_state *sctx = shash_desc_ctx(desc); | ||
130 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
131 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
132 | struct nx_sg *in_sg, *out_sg; | ||
133 | u64 count0; | ||
134 | int rc; | ||
135 | |||
136 | if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) { | ||
137 | /* we've hit the nx chip previously, now we're finalizing, | ||
138 | * so copy over the partial digest */ | ||
139 | memcpy(csbcpb->cpb.sha512.input_partial_digest, | ||
140 | csbcpb->cpb.sha512.message_digest, SHA512_DIGEST_SIZE); | ||
141 | } | ||
142 | |||
143 | /* final is represented by continuing the operation and indicating that | ||
144 | * this is not an intermediate operation */ | ||
145 | NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE; | ||
146 | |||
147 | count0 = sctx->count[0] * 8; | ||
148 | |||
149 | csbcpb->cpb.sha512.message_bit_length_lo += count0; | ||
150 | if (csbcpb->cpb.sha512.message_bit_length_lo < count0) | ||
151 | csbcpb->cpb.sha512.message_bit_length_hi++; | ||
152 | |||
153 | in_sg = nx_build_sg_list(nx_ctx->in_sg, sctx->buf, sctx->count[0], | ||
154 | nx_ctx->ap->sglen); | ||
155 | out_sg = nx_build_sg_list(nx_ctx->out_sg, out, SHA512_DIGEST_SIZE, | ||
156 | nx_ctx->ap->sglen); | ||
157 | nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg); | ||
158 | nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg); | ||
159 | |||
160 | if (!nx_ctx->op.outlen) { | ||
161 | rc = -EINVAL; | ||
162 | goto out; | ||
163 | } | ||
164 | |||
165 | rc = nx_hcall_sync(nx_ctx, &nx_ctx->op, | ||
166 | desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP); | ||
167 | if (rc) | ||
168 | goto out; | ||
169 | |||
170 | atomic_inc(&(nx_ctx->stats->sha512_ops)); | ||
171 | atomic64_add(csbcpb->cpb.sha512.message_bit_length_lo, | ||
172 | &(nx_ctx->stats->sha512_bytes)); | ||
173 | |||
174 | memcpy(out, csbcpb->cpb.sha512.message_digest, SHA512_DIGEST_SIZE); | ||
175 | out: | ||
176 | return rc; | ||
177 | } | ||
178 | |||
179 | static int nx_sha512_export(struct shash_desc *desc, void *out) | ||
180 | { | ||
181 | struct sha512_state *sctx = shash_desc_ctx(desc); | ||
182 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
183 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
184 | struct sha512_state *octx = out; | ||
185 | |||
186 | /* move message_bit_length (128 bits) into count and convert its value | ||
187 | * to bytes */ | ||
188 | octx->count[0] = csbcpb->cpb.sha512.message_bit_length_lo >> 3 | | ||
189 | ((csbcpb->cpb.sha512.message_bit_length_hi & 7) << 61); | ||
190 | octx->count[1] = csbcpb->cpb.sha512.message_bit_length_hi >> 3; | ||
191 | |||
192 | octx->count[0] += sctx->count[0]; | ||
193 | if (octx->count[0] < sctx->count[0]) | ||
194 | octx->count[1]++; | ||
195 | |||
196 | memcpy(octx->buf, sctx->buf, sizeof(octx->buf)); | ||
197 | |||
198 | /* if no data has been processed yet, we need to export SHA512's | ||
199 | * initial data, in case this context gets imported into a software | ||
200 | * context */ | ||
201 | if (csbcpb->cpb.sha512.message_bit_length_hi || | ||
202 | csbcpb->cpb.sha512.message_bit_length_lo) | ||
203 | memcpy(octx->state, csbcpb->cpb.sha512.message_digest, | ||
204 | SHA512_DIGEST_SIZE); | ||
205 | else { | ||
206 | octx->state[0] = SHA512_H0; | ||
207 | octx->state[1] = SHA512_H1; | ||
208 | octx->state[2] = SHA512_H2; | ||
209 | octx->state[3] = SHA512_H3; | ||
210 | octx->state[4] = SHA512_H4; | ||
211 | octx->state[5] = SHA512_H5; | ||
212 | octx->state[6] = SHA512_H6; | ||
213 | octx->state[7] = SHA512_H7; | ||
214 | } | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int nx_sha512_import(struct shash_desc *desc, const void *in) | ||
220 | { | ||
221 | struct sha512_state *sctx = shash_desc_ctx(desc); | ||
222 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); | ||
223 | struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; | ||
224 | const struct sha512_state *ictx = in; | ||
225 | |||
226 | memcpy(sctx->buf, ictx->buf, sizeof(ictx->buf)); | ||
227 | sctx->count[0] = ictx->count[0] & 0x3f; | ||
228 | csbcpb->cpb.sha512.message_bit_length_lo = (ictx->count[0] & ~0x3f) | ||
229 | << 3; | ||
230 | csbcpb->cpb.sha512.message_bit_length_hi = ictx->count[1] << 3 | | ||
231 | ictx->count[0] >> 61; | ||
232 | |||
233 | if (csbcpb->cpb.sha512.message_bit_length_hi || | ||
234 | csbcpb->cpb.sha512.message_bit_length_lo) { | ||
235 | memcpy(csbcpb->cpb.sha512.message_digest, ictx->state, | ||
236 | SHA512_DIGEST_SIZE); | ||
237 | |||
238 | NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; | ||
239 | NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; | ||
240 | } | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | struct shash_alg nx_shash_sha512_alg = { | ||
246 | .digestsize = SHA512_DIGEST_SIZE, | ||
247 | .init = nx_sha512_init, | ||
248 | .update = nx_sha512_update, | ||
249 | .final = nx_sha512_final, | ||
250 | .export = nx_sha512_export, | ||
251 | .import = nx_sha512_import, | ||
252 | .descsize = sizeof(struct sha512_state), | ||
253 | .statesize = sizeof(struct sha512_state), | ||
254 | .base = { | ||
255 | .cra_name = "sha512", | ||
256 | .cra_driver_name = "sha512-nx", | ||
257 | .cra_priority = 300, | ||
258 | .cra_flags = CRYPTO_ALG_TYPE_SHASH, | ||
259 | .cra_blocksize = SHA512_BLOCK_SIZE, | ||
260 | .cra_module = THIS_MODULE, | ||
261 | .cra_ctxsize = sizeof(struct nx_crypto_ctx), | ||
262 | .cra_init = nx_crypto_ctx_sha_init, | ||
263 | .cra_exit = nx_crypto_ctx_exit, | ||
264 | } | ||
265 | }; | ||
diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c new file mode 100644 index 000000000000..d7f179cc2e98 --- /dev/null +++ b/drivers/crypto/nx/nx.c | |||
@@ -0,0 +1,716 @@ | |||
1 | /** | ||
2 | * Routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <crypto/internal/hash.h> | ||
23 | #include <crypto/hash.h> | ||
24 | #include <crypto/aes.h> | ||
25 | #include <crypto/sha.h> | ||
26 | #include <crypto/algapi.h> | ||
27 | #include <crypto/scatterwalk.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/moduleparam.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/crypto.h> | ||
33 | #include <linux/scatterlist.h> | ||
34 | #include <linux/device.h> | ||
35 | #include <linux/of.h> | ||
36 | #include <asm/pSeries_reconfig.h> | ||
37 | #include <asm/abs_addr.h> | ||
38 | #include <asm/hvcall.h> | ||
39 | #include <asm/vio.h> | ||
40 | |||
41 | #include "nx_csbcpb.h" | ||
42 | #include "nx.h" | ||
43 | |||
44 | |||
45 | /** | ||
46 | * nx_hcall_sync - make an H_COP_OP hcall for the passed in op structure | ||
47 | * | ||
48 | * @nx_ctx: the crypto context handle | ||
49 | * @op: PFO operation struct to pass in | ||
50 | * @may_sleep: flag indicating the request can sleep | ||
51 | * | ||
52 | * Make the hcall, retrying while the hardware is busy. If we cannot yield | ||
53 | * the thread, limit the number of retries to 10 here. | ||
54 | */ | ||
55 | int nx_hcall_sync(struct nx_crypto_ctx *nx_ctx, | ||
56 | struct vio_pfo_op *op, | ||
57 | u32 may_sleep) | ||
58 | { | ||
59 | int rc, retries = 10; | ||
60 | struct vio_dev *viodev = nx_driver.viodev; | ||
61 | |||
62 | atomic_inc(&(nx_ctx->stats->sync_ops)); | ||
63 | |||
64 | do { | ||
65 | rc = vio_h_cop_sync(viodev, op); | ||
66 | } while ((rc == -EBUSY && !may_sleep && retries--) || | ||
67 | (rc == -EBUSY && may_sleep && cond_resched())); | ||
68 | |||
69 | if (rc) { | ||
70 | dev_dbg(&viodev->dev, "vio_h_cop_sync failed: rc: %d " | ||
71 | "hcall rc: %ld\n", rc, op->hcall_err); | ||
72 | atomic_inc(&(nx_ctx->stats->errors)); | ||
73 | atomic_set(&(nx_ctx->stats->last_error), op->hcall_err); | ||
74 | atomic_set(&(nx_ctx->stats->last_error_pid), current->pid); | ||
75 | } | ||
76 | |||
77 | return rc; | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * nx_build_sg_list - build an NX scatter list describing a single buffer | ||
82 | * | ||
83 | * @sg_head: pointer to the first scatter list element to build | ||
84 | * @start_addr: pointer to the linear buffer | ||
85 | * @len: length of the data at @start_addr | ||
86 | * @sgmax: the largest number of scatter list elements we're allowed to create | ||
87 | * | ||
88 | * This function will start writing nx_sg elements at @sg_head and keep | ||
89 | * writing them until all of the data from @start_addr is described or | ||
90 | * until sgmax elements have been written. Scatter list elements will be | ||
91 | * created such that none of the elements describes a buffer that crosses a 4K | ||
92 | * boundary. | ||
93 | */ | ||
94 | struct nx_sg *nx_build_sg_list(struct nx_sg *sg_head, | ||
95 | u8 *start_addr, | ||
96 | unsigned int len, | ||
97 | u32 sgmax) | ||
98 | { | ||
99 | unsigned int sg_len = 0; | ||
100 | struct nx_sg *sg; | ||
101 | u64 sg_addr = (u64)start_addr; | ||
102 | u64 end_addr; | ||
103 | |||
104 | /* determine the start and end for this address range - slightly | ||
105 | * different if this is in VMALLOC_REGION */ | ||
106 | if (is_vmalloc_addr(start_addr)) | ||
107 | sg_addr = phys_to_abs(page_to_phys(vmalloc_to_page(start_addr))) | ||
108 | + offset_in_page(sg_addr); | ||
109 | else | ||
110 | sg_addr = virt_to_abs(sg_addr); | ||
111 | |||
112 | end_addr = sg_addr + len; | ||
113 | |||
114 | /* each iteration will write one struct nx_sg element and add the | ||
115 | * length of data described by that element to sg_len. Once @len bytes | ||
116 | * have been described (or @sgmax elements have been written), the | ||
117 | * loop ends. min_t is used to ensure @end_addr falls on the same page | ||
118 | * as sg_addr, if not, we need to create another nx_sg element for the | ||
119 | * data on the next page */ | ||
120 | for (sg = sg_head; sg_len < len; sg++) { | ||
121 | sg->addr = sg_addr; | ||
122 | sg_addr = min_t(u64, NX_PAGE_NUM(sg_addr + NX_PAGE_SIZE), end_addr); | ||
123 | sg->len = sg_addr - sg->addr; | ||
124 | sg_len += sg->len; | ||
125 | |||
126 | if ((sg - sg_head) == sgmax) { | ||
127 | pr_err("nx: scatter/gather list overflow, pid: %d\n", | ||
128 | current->pid); | ||
129 | return NULL; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | /* return the moved sg_head pointer */ | ||
134 | return sg; | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * nx_walk_and_build - walk a linux scatterlist and build an nx scatterlist | ||
139 | * | ||
140 | * @nx_dst: pointer to the first nx_sg element to write | ||
141 | * @sglen: max number of nx_sg entries we're allowed to write | ||
142 | * @sg_src: pointer to the source linux scatterlist to walk | ||
143 | * @start: number of bytes to fast-forward past at the beginning of @sg_src | ||
144 | * @src_len: number of bytes to walk in @sg_src | ||
145 | */ | ||
146 | struct nx_sg *nx_walk_and_build(struct nx_sg *nx_dst, | ||
147 | unsigned int sglen, | ||
148 | struct scatterlist *sg_src, | ||
149 | unsigned int start, | ||
150 | unsigned int src_len) | ||
151 | { | ||
152 | struct scatter_walk walk; | ||
153 | struct nx_sg *nx_sg = nx_dst; | ||
154 | unsigned int n, offset = 0, len = src_len; | ||
155 | char *dst; | ||
156 | |||
157 | /* we need to fast forward through @start bytes first */ | ||
158 | for (;;) { | ||
159 | scatterwalk_start(&walk, sg_src); | ||
160 | |||
161 | if (start < offset + sg_src->length) | ||
162 | break; | ||
163 | |||
164 | offset += sg_src->length; | ||
165 | sg_src = scatterwalk_sg_next(sg_src); | ||
166 | } | ||
167 | |||
168 | /* start - offset is the number of bytes to advance in the scatterlist | ||
169 | * element we're currently looking at */ | ||
170 | scatterwalk_advance(&walk, start - offset); | ||
171 | |||
172 | while (len && nx_sg) { | ||
173 | n = scatterwalk_clamp(&walk, len); | ||
174 | if (!n) { | ||
175 | scatterwalk_start(&walk, sg_next(walk.sg)); | ||
176 | n = scatterwalk_clamp(&walk, len); | ||
177 | } | ||
178 | dst = scatterwalk_map(&walk); | ||
179 | |||
180 | nx_sg = nx_build_sg_list(nx_sg, dst, n, sglen); | ||
181 | len -= n; | ||
182 | |||
183 | scatterwalk_unmap(dst); | ||
184 | scatterwalk_advance(&walk, n); | ||
185 | scatterwalk_done(&walk, SCATTERWALK_FROM_SG, len); | ||
186 | } | ||
187 | |||
188 | /* return the moved destination pointer */ | ||
189 | return nx_sg; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * nx_build_sg_lists - walk the input scatterlists and build arrays of NX | ||
194 | * scatterlists based on them. | ||
195 | * | ||
196 | * @nx_ctx: NX crypto context for the lists we're building | ||
197 | * @desc: the block cipher descriptor for the operation | ||
198 | * @dst: destination scatterlist | ||
199 | * @src: source scatterlist | ||
200 | * @nbytes: length of data described in the scatterlists | ||
201 | * @iv: destination for the iv data, if the algorithm requires it | ||
202 | * | ||
203 | * This is common code shared by all the AES algorithms. It uses the block | ||
204 | * cipher walk routines to traverse input and output scatterlists, building | ||
205 | * corresponding NX scatterlists | ||
206 | */ | ||
207 | int nx_build_sg_lists(struct nx_crypto_ctx *nx_ctx, | ||
208 | struct blkcipher_desc *desc, | ||
209 | struct scatterlist *dst, | ||
210 | struct scatterlist *src, | ||
211 | unsigned int nbytes, | ||
212 | u8 *iv) | ||
213 | { | ||
214 | struct nx_sg *nx_insg = nx_ctx->in_sg; | ||
215 | struct nx_sg *nx_outsg = nx_ctx->out_sg; | ||
216 | struct blkcipher_walk walk; | ||
217 | int rc; | ||
218 | |||
219 | blkcipher_walk_init(&walk, dst, src, nbytes); | ||
220 | rc = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); | ||
221 | if (rc) | ||
222 | goto out; | ||
223 | |||
224 | if (iv) | ||
225 | memcpy(iv, walk.iv, AES_BLOCK_SIZE); | ||
226 | |||
227 | while (walk.nbytes) { | ||
228 | nx_insg = nx_build_sg_list(nx_insg, walk.src.virt.addr, | ||
229 | walk.nbytes, nx_ctx->ap->sglen); | ||
230 | nx_outsg = nx_build_sg_list(nx_outsg, walk.dst.virt.addr, | ||
231 | walk.nbytes, nx_ctx->ap->sglen); | ||
232 | |||
233 | rc = blkcipher_walk_done(desc, &walk, 0); | ||
234 | if (rc) | ||
235 | break; | ||
236 | } | ||
237 | |||
238 | if (walk.nbytes) { | ||
239 | nx_insg = nx_build_sg_list(nx_insg, walk.src.virt.addr, | ||
240 | walk.nbytes, nx_ctx->ap->sglen); | ||
241 | nx_outsg = nx_build_sg_list(nx_outsg, walk.dst.virt.addr, | ||
242 | walk.nbytes, nx_ctx->ap->sglen); | ||
243 | |||
244 | rc = 0; | ||
245 | } | ||
246 | |||
247 | /* these lengths should be negative, which will indicate to phyp that | ||
248 | * the input and output parameters are scatterlists, not linear | ||
249 | * buffers */ | ||
250 | nx_ctx->op.inlen = (nx_ctx->in_sg - nx_insg) * sizeof(struct nx_sg); | ||
251 | nx_ctx->op.outlen = (nx_ctx->out_sg - nx_outsg) * sizeof(struct nx_sg); | ||
252 | out: | ||
253 | return rc; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * nx_ctx_init - initialize an nx_ctx's vio_pfo_op struct | ||
258 | * | ||
259 | * @nx_ctx: the nx context to initialize | ||
260 | * @function: the function code for the op | ||
261 | */ | ||
262 | void nx_ctx_init(struct nx_crypto_ctx *nx_ctx, unsigned int function) | ||
263 | { | ||
264 | memset(nx_ctx->kmem, 0, nx_ctx->kmem_len); | ||
265 | nx_ctx->csbcpb->csb.valid |= NX_CSB_VALID_BIT; | ||
266 | |||
267 | nx_ctx->op.flags = function; | ||
268 | nx_ctx->op.csbcpb = virt_to_abs(nx_ctx->csbcpb); | ||
269 | nx_ctx->op.in = virt_to_abs(nx_ctx->in_sg); | ||
270 | nx_ctx->op.out = virt_to_abs(nx_ctx->out_sg); | ||
271 | |||
272 | if (nx_ctx->csbcpb_aead) { | ||
273 | nx_ctx->csbcpb_aead->csb.valid |= NX_CSB_VALID_BIT; | ||
274 | |||
275 | nx_ctx->op_aead.flags = function; | ||
276 | nx_ctx->op_aead.csbcpb = virt_to_abs(nx_ctx->csbcpb_aead); | ||
277 | nx_ctx->op_aead.in = virt_to_abs(nx_ctx->in_sg); | ||
278 | nx_ctx->op_aead.out = virt_to_abs(nx_ctx->out_sg); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | static void nx_of_update_status(struct device *dev, | ||
283 | struct property *p, | ||
284 | struct nx_of *props) | ||
285 | { | ||
286 | if (!strncmp(p->value, "okay", p->length)) { | ||
287 | props->status = NX_WAITING; | ||
288 | props->flags |= NX_OF_FLAG_STATUS_SET; | ||
289 | } else { | ||
290 | dev_info(dev, "%s: status '%s' is not 'okay'\n", __func__, | ||
291 | (char *)p->value); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | static void nx_of_update_sglen(struct device *dev, | ||
296 | struct property *p, | ||
297 | struct nx_of *props) | ||
298 | { | ||
299 | if (p->length != sizeof(props->max_sg_len)) { | ||
300 | dev_err(dev, "%s: unexpected format for " | ||
301 | "ibm,max-sg-len property\n", __func__); | ||
302 | dev_dbg(dev, "%s: ibm,max-sg-len is %d bytes " | ||
303 | "long, expected %zd bytes\n", __func__, | ||
304 | p->length, sizeof(props->max_sg_len)); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | props->max_sg_len = *(u32 *)p->value; | ||
309 | props->flags |= NX_OF_FLAG_MAXSGLEN_SET; | ||
310 | } | ||
311 | |||
312 | static void nx_of_update_msc(struct device *dev, | ||
313 | struct property *p, | ||
314 | struct nx_of *props) | ||
315 | { | ||
316 | struct msc_triplet *trip; | ||
317 | struct max_sync_cop *msc; | ||
318 | unsigned int bytes_so_far, i, lenp; | ||
319 | |||
320 | msc = (struct max_sync_cop *)p->value; | ||
321 | lenp = p->length; | ||
322 | |||
323 | /* You can't tell if the data read in for this property is sane by its | ||
324 | * size alone. This is because there are sizes embedded in the data | ||
325 | * structure. The best we can do is check lengths as we parse and bail | ||
326 | * as soon as a length error is detected. */ | ||
327 | bytes_so_far = 0; | ||
328 | |||
329 | while ((bytes_so_far + sizeof(struct max_sync_cop)) <= lenp) { | ||
330 | bytes_so_far += sizeof(struct max_sync_cop); | ||
331 | |||
332 | trip = msc->trip; | ||
333 | |||
334 | for (i = 0; | ||
335 | ((bytes_so_far + sizeof(struct msc_triplet)) <= lenp) && | ||
336 | i < msc->triplets; | ||
337 | i++) { | ||
338 | if (msc->fc > NX_MAX_FC || msc->mode > NX_MAX_MODE) { | ||
339 | dev_err(dev, "unknown function code/mode " | ||
340 | "combo: %d/%d (ignored)\n", msc->fc, | ||
341 | msc->mode); | ||
342 | goto next_loop; | ||
343 | } | ||
344 | |||
345 | switch (trip->keybitlen) { | ||
346 | case 128: | ||
347 | case 160: | ||
348 | props->ap[msc->fc][msc->mode][0].databytelen = | ||
349 | trip->databytelen; | ||
350 | props->ap[msc->fc][msc->mode][0].sglen = | ||
351 | trip->sglen; | ||
352 | break; | ||
353 | case 192: | ||
354 | props->ap[msc->fc][msc->mode][1].databytelen = | ||
355 | trip->databytelen; | ||
356 | props->ap[msc->fc][msc->mode][1].sglen = | ||
357 | trip->sglen; | ||
358 | break; | ||
359 | case 256: | ||
360 | if (msc->fc == NX_FC_AES) { | ||
361 | props->ap[msc->fc][msc->mode][2]. | ||
362 | databytelen = trip->databytelen; | ||
363 | props->ap[msc->fc][msc->mode][2].sglen = | ||
364 | trip->sglen; | ||
365 | } else if (msc->fc == NX_FC_AES_HMAC || | ||
366 | msc->fc == NX_FC_SHA) { | ||
367 | props->ap[msc->fc][msc->mode][1]. | ||
368 | databytelen = trip->databytelen; | ||
369 | props->ap[msc->fc][msc->mode][1].sglen = | ||
370 | trip->sglen; | ||
371 | } else { | ||
372 | dev_warn(dev, "unknown function " | ||
373 | "code/key bit len combo" | ||
374 | ": (%u/256)\n", msc->fc); | ||
375 | } | ||
376 | break; | ||
377 | case 512: | ||
378 | props->ap[msc->fc][msc->mode][2].databytelen = | ||
379 | trip->databytelen; | ||
380 | props->ap[msc->fc][msc->mode][2].sglen = | ||
381 | trip->sglen; | ||
382 | break; | ||
383 | default: | ||
384 | dev_warn(dev, "unknown function code/key bit " | ||
385 | "len combo: (%u/%u)\n", msc->fc, | ||
386 | trip->keybitlen); | ||
387 | break; | ||
388 | } | ||
389 | next_loop: | ||
390 | bytes_so_far += sizeof(struct msc_triplet); | ||
391 | trip++; | ||
392 | } | ||
393 | |||
394 | msc = (struct max_sync_cop *)trip; | ||
395 | } | ||
396 | |||
397 | props->flags |= NX_OF_FLAG_MAXSYNCCOP_SET; | ||
398 | } | ||
399 | |||
400 | /** | ||
401 | * nx_of_init - read openFirmware values from the device tree | ||
402 | * | ||
403 | * @dev: device handle | ||
404 | * @props: pointer to struct to hold the properties values | ||
405 | * | ||
406 | * Called once at driver probe time, this function will read out the | ||
407 | * openFirmware properties we use at runtime. If all the OF properties are | ||
408 | * acceptable, when we exit this function props->flags will indicate that | ||
409 | * we're ready to register our crypto algorithms. | ||
410 | */ | ||
411 | static void nx_of_init(struct device *dev, struct nx_of *props) | ||
412 | { | ||
413 | struct device_node *base_node = dev->of_node; | ||
414 | struct property *p; | ||
415 | |||
416 | p = of_find_property(base_node, "status", NULL); | ||
417 | if (!p) | ||
418 | dev_info(dev, "%s: property 'status' not found\n", __func__); | ||
419 | else | ||
420 | nx_of_update_status(dev, p, props); | ||
421 | |||
422 | p = of_find_property(base_node, "ibm,max-sg-len", NULL); | ||
423 | if (!p) | ||
424 | dev_info(dev, "%s: property 'ibm,max-sg-len' not found\n", | ||
425 | __func__); | ||
426 | else | ||
427 | nx_of_update_sglen(dev, p, props); | ||
428 | |||
429 | p = of_find_property(base_node, "ibm,max-sync-cop", NULL); | ||
430 | if (!p) | ||
431 | dev_info(dev, "%s: property 'ibm,max-sync-cop' not found\n", | ||
432 | __func__); | ||
433 | else | ||
434 | nx_of_update_msc(dev, p, props); | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * nx_register_algs - register algorithms with the crypto API | ||
439 | * | ||
440 | * Called from nx_probe() | ||
441 | * | ||
442 | * If all OF properties are in an acceptable state, the driver flags will | ||
443 | * indicate that we're ready and we'll create our debugfs files and register | ||
444 | * out crypto algorithms. | ||
445 | */ | ||
446 | static int nx_register_algs(void) | ||
447 | { | ||
448 | int rc = -1; | ||
449 | |||
450 | if (nx_driver.of.flags != NX_OF_FLAG_MASK_READY) | ||
451 | goto out; | ||
452 | |||
453 | memset(&nx_driver.stats, 0, sizeof(struct nx_stats)); | ||
454 | |||
455 | rc = NX_DEBUGFS_INIT(&nx_driver); | ||
456 | if (rc) | ||
457 | goto out; | ||
458 | |||
459 | rc = crypto_register_alg(&nx_ecb_aes_alg); | ||
460 | if (rc) | ||
461 | goto out; | ||
462 | |||
463 | rc = crypto_register_alg(&nx_cbc_aes_alg); | ||
464 | if (rc) | ||
465 | goto out_unreg_ecb; | ||
466 | |||
467 | rc = crypto_register_alg(&nx_ctr_aes_alg); | ||
468 | if (rc) | ||
469 | goto out_unreg_cbc; | ||
470 | |||
471 | rc = crypto_register_alg(&nx_ctr3686_aes_alg); | ||
472 | if (rc) | ||
473 | goto out_unreg_ctr; | ||
474 | |||
475 | rc = crypto_register_alg(&nx_gcm_aes_alg); | ||
476 | if (rc) | ||
477 | goto out_unreg_ctr3686; | ||
478 | |||
479 | rc = crypto_register_alg(&nx_gcm4106_aes_alg); | ||
480 | if (rc) | ||
481 | goto out_unreg_gcm; | ||
482 | |||
483 | rc = crypto_register_alg(&nx_ccm_aes_alg); | ||
484 | if (rc) | ||
485 | goto out_unreg_gcm4106; | ||
486 | |||
487 | rc = crypto_register_alg(&nx_ccm4309_aes_alg); | ||
488 | if (rc) | ||
489 | goto out_unreg_ccm; | ||
490 | |||
491 | rc = crypto_register_shash(&nx_shash_sha256_alg); | ||
492 | if (rc) | ||
493 | goto out_unreg_ccm4309; | ||
494 | |||
495 | rc = crypto_register_shash(&nx_shash_sha512_alg); | ||
496 | if (rc) | ||
497 | goto out_unreg_s256; | ||
498 | |||
499 | rc = crypto_register_shash(&nx_shash_aes_xcbc_alg); | ||
500 | if (rc) | ||
501 | goto out_unreg_s512; | ||
502 | |||
503 | nx_driver.of.status = NX_OKAY; | ||
504 | |||
505 | goto out; | ||
506 | |||
507 | out_unreg_s512: | ||
508 | crypto_unregister_shash(&nx_shash_sha512_alg); | ||
509 | out_unreg_s256: | ||
510 | crypto_unregister_shash(&nx_shash_sha256_alg); | ||
511 | out_unreg_ccm4309: | ||
512 | crypto_unregister_alg(&nx_ccm4309_aes_alg); | ||
513 | out_unreg_ccm: | ||
514 | crypto_unregister_alg(&nx_ccm_aes_alg); | ||
515 | out_unreg_gcm4106: | ||
516 | crypto_unregister_alg(&nx_gcm4106_aes_alg); | ||
517 | out_unreg_gcm: | ||
518 | crypto_unregister_alg(&nx_gcm_aes_alg); | ||
519 | out_unreg_ctr3686: | ||
520 | crypto_unregister_alg(&nx_ctr3686_aes_alg); | ||
521 | out_unreg_ctr: | ||
522 | crypto_unregister_alg(&nx_ctr_aes_alg); | ||
523 | out_unreg_cbc: | ||
524 | crypto_unregister_alg(&nx_cbc_aes_alg); | ||
525 | out_unreg_ecb: | ||
526 | crypto_unregister_alg(&nx_ecb_aes_alg); | ||
527 | out: | ||
528 | return rc; | ||
529 | } | ||
530 | |||
531 | /** | ||
532 | * nx_crypto_ctx_init - create and initialize a crypto api context | ||
533 | * | ||
534 | * @nx_ctx: the crypto api context | ||
535 | * @fc: function code for the context | ||
536 | * @mode: the function code specific mode for this context | ||
537 | */ | ||
538 | static int nx_crypto_ctx_init(struct nx_crypto_ctx *nx_ctx, u32 fc, u32 mode) | ||
539 | { | ||
540 | if (nx_driver.of.status != NX_OKAY) { | ||
541 | pr_err("Attempt to initialize NX crypto context while device " | ||
542 | "is not available!\n"); | ||
543 | return -ENODEV; | ||
544 | } | ||
545 | |||
546 | /* we need an extra page for csbcpb_aead for these modes */ | ||
547 | if (mode == NX_MODE_AES_GCM || mode == NX_MODE_AES_CCM) | ||
548 | nx_ctx->kmem_len = (4 * NX_PAGE_SIZE) + | ||
549 | sizeof(struct nx_csbcpb); | ||
550 | else | ||
551 | nx_ctx->kmem_len = (3 * NX_PAGE_SIZE) + | ||
552 | sizeof(struct nx_csbcpb); | ||
553 | |||
554 | nx_ctx->kmem = kmalloc(nx_ctx->kmem_len, GFP_KERNEL); | ||
555 | if (!nx_ctx->kmem) | ||
556 | return -ENOMEM; | ||
557 | |||
558 | /* the csbcpb and scatterlists must be 4K aligned pages */ | ||
559 | nx_ctx->csbcpb = (struct nx_csbcpb *)(round_up((u64)nx_ctx->kmem, | ||
560 | (u64)NX_PAGE_SIZE)); | ||
561 | nx_ctx->in_sg = (struct nx_sg *)((u8 *)nx_ctx->csbcpb + NX_PAGE_SIZE); | ||
562 | nx_ctx->out_sg = (struct nx_sg *)((u8 *)nx_ctx->in_sg + NX_PAGE_SIZE); | ||
563 | |||
564 | if (mode == NX_MODE_AES_GCM || mode == NX_MODE_AES_CCM) | ||
565 | nx_ctx->csbcpb_aead = | ||
566 | (struct nx_csbcpb *)((u8 *)nx_ctx->out_sg + | ||
567 | NX_PAGE_SIZE); | ||
568 | |||
569 | /* give each context a pointer to global stats and their OF | ||
570 | * properties */ | ||
571 | nx_ctx->stats = &nx_driver.stats; | ||
572 | memcpy(nx_ctx->props, nx_driver.of.ap[fc][mode], | ||
573 | sizeof(struct alg_props) * 3); | ||
574 | |||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | /* entry points from the crypto tfm initializers */ | ||
579 | int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm) | ||
580 | { | ||
581 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES, | ||
582 | NX_MODE_AES_CCM); | ||
583 | } | ||
584 | |||
585 | int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm) | ||
586 | { | ||
587 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES, | ||
588 | NX_MODE_AES_GCM); | ||
589 | } | ||
590 | |||
591 | int nx_crypto_ctx_aes_ctr_init(struct crypto_tfm *tfm) | ||
592 | { | ||
593 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES, | ||
594 | NX_MODE_AES_CTR); | ||
595 | } | ||
596 | |||
597 | int nx_crypto_ctx_aes_cbc_init(struct crypto_tfm *tfm) | ||
598 | { | ||
599 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES, | ||
600 | NX_MODE_AES_CBC); | ||
601 | } | ||
602 | |||
603 | int nx_crypto_ctx_aes_ecb_init(struct crypto_tfm *tfm) | ||
604 | { | ||
605 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES, | ||
606 | NX_MODE_AES_ECB); | ||
607 | } | ||
608 | |||
609 | int nx_crypto_ctx_sha_init(struct crypto_tfm *tfm) | ||
610 | { | ||
611 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_SHA, NX_MODE_SHA); | ||
612 | } | ||
613 | |||
614 | int nx_crypto_ctx_aes_xcbc_init(struct crypto_tfm *tfm) | ||
615 | { | ||
616 | return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES, | ||
617 | NX_MODE_AES_XCBC_MAC); | ||
618 | } | ||
619 | |||
620 | /** | ||
621 | * nx_crypto_ctx_exit - destroy a crypto api context | ||
622 | * | ||
623 | * @tfm: the crypto transform pointer for the context | ||
624 | * | ||
625 | * As crypto API contexts are destroyed, this exit hook is called to free the | ||
626 | * memory associated with it. | ||
627 | */ | ||
628 | void nx_crypto_ctx_exit(struct crypto_tfm *tfm) | ||
629 | { | ||
630 | struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); | ||
631 | |||
632 | kzfree(nx_ctx->kmem); | ||
633 | nx_ctx->csbcpb = NULL; | ||
634 | nx_ctx->csbcpb_aead = NULL; | ||
635 | nx_ctx->in_sg = NULL; | ||
636 | nx_ctx->out_sg = NULL; | ||
637 | } | ||
638 | |||
639 | static int __devinit nx_probe(struct vio_dev *viodev, | ||
640 | const struct vio_device_id *id) | ||
641 | { | ||
642 | dev_dbg(&viodev->dev, "driver probed: %s resource id: 0x%x\n", | ||
643 | viodev->name, viodev->resource_id); | ||
644 | |||
645 | if (nx_driver.viodev) { | ||
646 | dev_err(&viodev->dev, "%s: Attempt to register more than one " | ||
647 | "instance of the hardware\n", __func__); | ||
648 | return -EINVAL; | ||
649 | } | ||
650 | |||
651 | nx_driver.viodev = viodev; | ||
652 | |||
653 | nx_of_init(&viodev->dev, &nx_driver.of); | ||
654 | |||
655 | return nx_register_algs(); | ||
656 | } | ||
657 | |||
658 | static int __devexit nx_remove(struct vio_dev *viodev) | ||
659 | { | ||
660 | dev_dbg(&viodev->dev, "entering nx_remove for UA 0x%x\n", | ||
661 | viodev->unit_address); | ||
662 | |||
663 | if (nx_driver.of.status == NX_OKAY) { | ||
664 | NX_DEBUGFS_FINI(&nx_driver); | ||
665 | |||
666 | crypto_unregister_alg(&nx_ccm_aes_alg); | ||
667 | crypto_unregister_alg(&nx_ccm4309_aes_alg); | ||
668 | crypto_unregister_alg(&nx_gcm_aes_alg); | ||
669 | crypto_unregister_alg(&nx_gcm4106_aes_alg); | ||
670 | crypto_unregister_alg(&nx_ctr_aes_alg); | ||
671 | crypto_unregister_alg(&nx_ctr3686_aes_alg); | ||
672 | crypto_unregister_alg(&nx_cbc_aes_alg); | ||
673 | crypto_unregister_alg(&nx_ecb_aes_alg); | ||
674 | crypto_unregister_shash(&nx_shash_sha256_alg); | ||
675 | crypto_unregister_shash(&nx_shash_sha512_alg); | ||
676 | crypto_unregister_shash(&nx_shash_aes_xcbc_alg); | ||
677 | } | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | |||
683 | /* module wide initialization/cleanup */ | ||
684 | static int __init nx_init(void) | ||
685 | { | ||
686 | return vio_register_driver(&nx_driver.viodriver); | ||
687 | } | ||
688 | |||
689 | static void __exit nx_fini(void) | ||
690 | { | ||
691 | vio_unregister_driver(&nx_driver.viodriver); | ||
692 | } | ||
693 | |||
694 | static struct vio_device_id nx_crypto_driver_ids[] __devinitdata = { | ||
695 | { "ibm,sym-encryption-v1", "ibm,sym-encryption" }, | ||
696 | { "", "" } | ||
697 | }; | ||
698 | MODULE_DEVICE_TABLE(vio, nx_crypto_driver_ids); | ||
699 | |||
700 | /* driver state structure */ | ||
701 | struct nx_crypto_driver nx_driver = { | ||
702 | .viodriver = { | ||
703 | .id_table = nx_crypto_driver_ids, | ||
704 | .probe = nx_probe, | ||
705 | .remove = nx_remove, | ||
706 | .name = NX_NAME, | ||
707 | }, | ||
708 | }; | ||
709 | |||
710 | module_init(nx_init); | ||
711 | module_exit(nx_fini); | ||
712 | |||
713 | MODULE_AUTHOR("Kent Yoder <yoder1@us.ibm.com>"); | ||
714 | MODULE_DESCRIPTION(NX_STRING); | ||
715 | MODULE_LICENSE("GPL"); | ||
716 | MODULE_VERSION(NX_VERSION); | ||
diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h new file mode 100644 index 000000000000..3232b182dd28 --- /dev/null +++ b/drivers/crypto/nx/nx.h | |||
@@ -0,0 +1,193 @@ | |||
1 | |||
2 | #ifndef __NX_H__ | ||
3 | #define __NX_H__ | ||
4 | |||
5 | #define NX_NAME "nx-crypto" | ||
6 | #define NX_STRING "IBM Power7+ Nest Accelerator Crypto Driver" | ||
7 | #define NX_VERSION "1.0" | ||
8 | |||
9 | static const char nx_driver_string[] = NX_STRING; | ||
10 | static const char nx_driver_version[] = NX_VERSION; | ||
11 | |||
12 | /* a scatterlist in the format PHYP is expecting */ | ||
13 | struct nx_sg { | ||
14 | u64 addr; | ||
15 | u32 rsvd; | ||
16 | u32 len; | ||
17 | } __attribute((packed)); | ||
18 | |||
19 | #define NX_PAGE_SIZE (4096) | ||
20 | #define NX_MAX_SG_ENTRIES (NX_PAGE_SIZE/(sizeof(struct nx_sg))) | ||
21 | |||
22 | enum nx_status { | ||
23 | NX_DISABLED, | ||
24 | NX_WAITING, | ||
25 | NX_OKAY | ||
26 | }; | ||
27 | |||
28 | /* msc_triplet and max_sync_cop are used only to assist in parsing the | ||
29 | * openFirmware property */ | ||
30 | struct msc_triplet { | ||
31 | u32 keybitlen; | ||
32 | u32 databytelen; | ||
33 | u32 sglen; | ||
34 | } __packed; | ||
35 | |||
36 | struct max_sync_cop { | ||
37 | u32 fc; | ||
38 | u32 mode; | ||
39 | u32 triplets; | ||
40 | struct msc_triplet trip[0]; | ||
41 | } __packed; | ||
42 | |||
43 | struct alg_props { | ||
44 | u32 databytelen; | ||
45 | u32 sglen; | ||
46 | }; | ||
47 | |||
48 | #define NX_OF_FLAG_MAXSGLEN_SET (1) | ||
49 | #define NX_OF_FLAG_STATUS_SET (2) | ||
50 | #define NX_OF_FLAG_MAXSYNCCOP_SET (4) | ||
51 | #define NX_OF_FLAG_MASK_READY (NX_OF_FLAG_MAXSGLEN_SET | \ | ||
52 | NX_OF_FLAG_STATUS_SET | \ | ||
53 | NX_OF_FLAG_MAXSYNCCOP_SET) | ||
54 | struct nx_of { | ||
55 | u32 flags; | ||
56 | u32 max_sg_len; | ||
57 | enum nx_status status; | ||
58 | struct alg_props ap[NX_MAX_FC][NX_MAX_MODE][3]; | ||
59 | }; | ||
60 | |||
61 | struct nx_stats { | ||
62 | atomic_t aes_ops; | ||
63 | atomic64_t aes_bytes; | ||
64 | atomic_t sha256_ops; | ||
65 | atomic64_t sha256_bytes; | ||
66 | atomic_t sha512_ops; | ||
67 | atomic64_t sha512_bytes; | ||
68 | |||
69 | atomic_t sync_ops; | ||
70 | |||
71 | atomic_t errors; | ||
72 | atomic_t last_error; | ||
73 | atomic_t last_error_pid; | ||
74 | }; | ||
75 | |||
76 | struct nx_debugfs { | ||
77 | struct dentry *dfs_root; | ||
78 | struct dentry *dfs_aes_ops, *dfs_aes_bytes; | ||
79 | struct dentry *dfs_sha256_ops, *dfs_sha256_bytes; | ||
80 | struct dentry *dfs_sha512_ops, *dfs_sha512_bytes; | ||
81 | struct dentry *dfs_errors, *dfs_last_error, *dfs_last_error_pid; | ||
82 | }; | ||
83 | |||
84 | struct nx_crypto_driver { | ||
85 | struct nx_stats stats; | ||
86 | struct nx_of of; | ||
87 | struct vio_dev *viodev; | ||
88 | struct vio_driver viodriver; | ||
89 | struct nx_debugfs dfs; | ||
90 | }; | ||
91 | |||
92 | #define NX_GCM4106_NONCE_LEN (4) | ||
93 | #define NX_GCM_CTR_OFFSET (12) | ||
94 | struct nx_gcm_priv { | ||
95 | u8 iv[16]; | ||
96 | u8 iauth_tag[16]; | ||
97 | u8 nonce[NX_GCM4106_NONCE_LEN]; | ||
98 | }; | ||
99 | |||
100 | #define NX_CCM_AES_KEY_LEN (16) | ||
101 | #define NX_CCM4309_AES_KEY_LEN (19) | ||
102 | #define NX_CCM4309_NONCE_LEN (3) | ||
103 | struct nx_ccm_priv { | ||
104 | u8 iv[16]; | ||
105 | u8 b0[16]; | ||
106 | u8 iauth_tag[16]; | ||
107 | u8 oauth_tag[16]; | ||
108 | u8 nonce[NX_CCM4309_NONCE_LEN]; | ||
109 | }; | ||
110 | |||
111 | struct nx_xcbc_priv { | ||
112 | u8 key[16]; | ||
113 | }; | ||
114 | |||
115 | struct nx_ctr_priv { | ||
116 | u8 iv[16]; | ||
117 | }; | ||
118 | |||
119 | struct nx_crypto_ctx { | ||
120 | void *kmem; /* unaligned, kmalloc'd buffer */ | ||
121 | size_t kmem_len; /* length of kmem */ | ||
122 | struct nx_csbcpb *csbcpb; /* aligned page given to phyp @ hcall time */ | ||
123 | struct vio_pfo_op op; /* operation struct with hcall parameters */ | ||
124 | struct nx_csbcpb *csbcpb_aead; /* secondary csbcpb used by AEAD algs */ | ||
125 | struct vio_pfo_op op_aead;/* operation struct for csbcpb_aead */ | ||
126 | |||
127 | struct nx_sg *in_sg; /* aligned pointer into kmem to an sg list */ | ||
128 | struct nx_sg *out_sg; /* aligned pointer into kmem to an sg list */ | ||
129 | |||
130 | struct alg_props *ap; /* pointer into props based on our key size */ | ||
131 | struct alg_props props[3];/* openFirmware properties for requests */ | ||
132 | struct nx_stats *stats; /* pointer into an nx_crypto_driver for stats | ||
133 | reporting */ | ||
134 | |||
135 | union { | ||
136 | struct nx_gcm_priv gcm; | ||
137 | struct nx_ccm_priv ccm; | ||
138 | struct nx_xcbc_priv xcbc; | ||
139 | struct nx_ctr_priv ctr; | ||
140 | } priv; | ||
141 | }; | ||
142 | |||
143 | /* prototypes */ | ||
144 | int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm); | ||
145 | int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm); | ||
146 | int nx_crypto_ctx_aes_xcbc_init(struct crypto_tfm *tfm); | ||
147 | int nx_crypto_ctx_aes_ctr_init(struct crypto_tfm *tfm); | ||
148 | int nx_crypto_ctx_aes_cbc_init(struct crypto_tfm *tfm); | ||
149 | int nx_crypto_ctx_aes_ecb_init(struct crypto_tfm *tfm); | ||
150 | int nx_crypto_ctx_sha_init(struct crypto_tfm *tfm); | ||
151 | void nx_crypto_ctx_exit(struct crypto_tfm *tfm); | ||
152 | void nx_ctx_init(struct nx_crypto_ctx *nx_ctx, unsigned int function); | ||
153 | int nx_hcall_sync(struct nx_crypto_ctx *ctx, struct vio_pfo_op *op, | ||
154 | u32 may_sleep); | ||
155 | struct nx_sg *nx_build_sg_list(struct nx_sg *, u8 *, unsigned int, u32); | ||
156 | int nx_build_sg_lists(struct nx_crypto_ctx *, struct blkcipher_desc *, | ||
157 | struct scatterlist *, struct scatterlist *, unsigned int, | ||
158 | u8 *); | ||
159 | struct nx_sg *nx_walk_and_build(struct nx_sg *, unsigned int, | ||
160 | struct scatterlist *, unsigned int, | ||
161 | unsigned int); | ||
162 | |||
163 | #ifdef CONFIG_DEBUG_FS | ||
164 | #define NX_DEBUGFS_INIT(drv) nx_debugfs_init(drv) | ||
165 | #define NX_DEBUGFS_FINI(drv) nx_debugfs_fini(drv) | ||
166 | |||
167 | int nx_debugfs_init(struct nx_crypto_driver *); | ||
168 | void nx_debugfs_fini(struct nx_crypto_driver *); | ||
169 | #else | ||
170 | #define NX_DEBUGFS_INIT(drv) (0) | ||
171 | #define NX_DEBUGFS_FINI(drv) (0) | ||
172 | #endif | ||
173 | |||
174 | #define NX_PAGE_NUM(x) ((u64)(x) & 0xfffffffffffff000ULL) | ||
175 | |||
176 | extern struct crypto_alg nx_cbc_aes_alg; | ||
177 | extern struct crypto_alg nx_ecb_aes_alg; | ||
178 | extern struct crypto_alg nx_gcm_aes_alg; | ||
179 | extern struct crypto_alg nx_gcm4106_aes_alg; | ||
180 | extern struct crypto_alg nx_ctr_aes_alg; | ||
181 | extern struct crypto_alg nx_ctr3686_aes_alg; | ||
182 | extern struct crypto_alg nx_ccm_aes_alg; | ||
183 | extern struct crypto_alg nx_ccm4309_aes_alg; | ||
184 | extern struct shash_alg nx_shash_aes_xcbc_alg; | ||
185 | extern struct shash_alg nx_shash_sha512_alg; | ||
186 | extern struct shash_alg nx_shash_sha256_alg; | ||
187 | |||
188 | extern struct nx_crypto_driver nx_driver; | ||
189 | |||
190 | #define SCATTERWALK_TO_SG 1 | ||
191 | #define SCATTERWALK_FROM_SG 0 | ||
192 | |||
193 | #endif | ||
diff --git a/drivers/crypto/nx/nx_csbcpb.h b/drivers/crypto/nx/nx_csbcpb.h new file mode 100644 index 000000000000..a304f956d6f8 --- /dev/null +++ b/drivers/crypto/nx/nx_csbcpb.h | |||
@@ -0,0 +1,205 @@ | |||
1 | |||
2 | #ifndef __NX_CSBCPB_H__ | ||
3 | #define __NX_CSBCPB_H__ | ||
4 | |||
5 | struct cop_symcpb_aes_ecb { | ||
6 | u8 key[32]; | ||
7 | u8 __rsvd[80]; | ||
8 | } __packed; | ||
9 | |||
10 | struct cop_symcpb_aes_cbc { | ||
11 | u8 iv[16]; | ||
12 | u8 key[32]; | ||
13 | u8 cv[16]; | ||
14 | u32 spbc; | ||
15 | u8 __rsvd[44]; | ||
16 | } __packed; | ||
17 | |||
18 | struct cop_symcpb_aes_gca { | ||
19 | u8 in_pat[16]; | ||
20 | u8 key[32]; | ||
21 | u8 out_pat[16]; | ||
22 | u32 spbc; | ||
23 | u8 __rsvd[44]; | ||
24 | } __packed; | ||
25 | |||
26 | struct cop_symcpb_aes_gcm { | ||
27 | u8 in_pat_or_aad[16]; | ||
28 | u8 iv_or_cnt[16]; | ||
29 | u64 bit_length_aad; | ||
30 | u64 bit_length_data; | ||
31 | u8 in_s0[16]; | ||
32 | u8 key[32]; | ||
33 | u8 __rsvd1[16]; | ||
34 | u8 out_pat_or_mac[16]; | ||
35 | u8 out_s0[16]; | ||
36 | u8 out_cnt[16]; | ||
37 | u32 spbc; | ||
38 | u8 __rsvd2[12]; | ||
39 | } __packed; | ||
40 | |||
41 | struct cop_symcpb_aes_ctr { | ||
42 | u8 iv[16]; | ||
43 | u8 key[32]; | ||
44 | u8 cv[16]; | ||
45 | u32 spbc; | ||
46 | u8 __rsvd2[44]; | ||
47 | } __packed; | ||
48 | |||
49 | struct cop_symcpb_aes_cca { | ||
50 | u8 b0[16]; | ||
51 | u8 b1[16]; | ||
52 | u8 key[16]; | ||
53 | u8 out_pat_or_b0[16]; | ||
54 | u32 spbc; | ||
55 | u8 __rsvd[44]; | ||
56 | } __packed; | ||
57 | |||
58 | struct cop_symcpb_aes_ccm { | ||
59 | u8 in_pat_or_b0[16]; | ||
60 | u8 iv_or_ctr[16]; | ||
61 | u8 in_s0[16]; | ||
62 | u8 key[16]; | ||
63 | u8 __rsvd1[48]; | ||
64 | u8 out_pat_or_mac[16]; | ||
65 | u8 out_s0[16]; | ||
66 | u8 out_ctr[16]; | ||
67 | u32 spbc; | ||
68 | u8 __rsvd2[12]; | ||
69 | } __packed; | ||
70 | |||
71 | struct cop_symcpb_aes_xcbc { | ||
72 | u8 cv[16]; | ||
73 | u8 key[16]; | ||
74 | u8 __rsvd1[16]; | ||
75 | u8 out_cv_mac[16]; | ||
76 | u32 spbc; | ||
77 | u8 __rsvd2[44]; | ||
78 | } __packed; | ||
79 | |||
80 | struct cop_symcpb_sha256 { | ||
81 | u64 message_bit_length; | ||
82 | u64 __rsvd1; | ||
83 | u8 input_partial_digest[32]; | ||
84 | u8 message_digest[32]; | ||
85 | u32 spbc; | ||
86 | u8 __rsvd2[44]; | ||
87 | } __packed; | ||
88 | |||
89 | struct cop_symcpb_sha512 { | ||
90 | u64 message_bit_length_hi; | ||
91 | u64 message_bit_length_lo; | ||
92 | u8 input_partial_digest[64]; | ||
93 | u8 __rsvd1[32]; | ||
94 | u8 message_digest[64]; | ||
95 | u32 spbc; | ||
96 | u8 __rsvd2[76]; | ||
97 | } __packed; | ||
98 | |||
99 | #define NX_FDM_INTERMEDIATE 0x01 | ||
100 | #define NX_FDM_CONTINUATION 0x02 | ||
101 | #define NX_FDM_ENDE_ENCRYPT 0x80 | ||
102 | |||
103 | #define NX_CPB_FDM(c) ((c)->cpb.hdr.fdm) | ||
104 | #define NX_CPB_KS_DS(c) ((c)->cpb.hdr.ks_ds) | ||
105 | |||
106 | #define NX_CPB_KEY_SIZE(c) (NX_CPB_KS_DS(c) >> 4) | ||
107 | #define NX_CPB_SET_KEY_SIZE(c, x) NX_CPB_KS_DS(c) |= ((x) << 4) | ||
108 | #define NX_CPB_SET_DIGEST_SIZE(c, x) NX_CPB_KS_DS(c) |= (x) | ||
109 | |||
110 | struct cop_symcpb_header { | ||
111 | u8 mode; | ||
112 | u8 fdm; | ||
113 | u8 ks_ds; | ||
114 | u8 pad_byte; | ||
115 | u8 __rsvd[12]; | ||
116 | } __packed; | ||
117 | |||
118 | struct cop_parameter_block { | ||
119 | struct cop_symcpb_header hdr; | ||
120 | union { | ||
121 | struct cop_symcpb_aes_ecb aes_ecb; | ||
122 | struct cop_symcpb_aes_cbc aes_cbc; | ||
123 | struct cop_symcpb_aes_gca aes_gca; | ||
124 | struct cop_symcpb_aes_gcm aes_gcm; | ||
125 | struct cop_symcpb_aes_cca aes_cca; | ||
126 | struct cop_symcpb_aes_ccm aes_ccm; | ||
127 | struct cop_symcpb_aes_ctr aes_ctr; | ||
128 | struct cop_symcpb_aes_xcbc aes_xcbc; | ||
129 | struct cop_symcpb_sha256 sha256; | ||
130 | struct cop_symcpb_sha512 sha512; | ||
131 | }; | ||
132 | } __packed; | ||
133 | |||
134 | #define NX_CSB_VALID_BIT 0x80 | ||
135 | |||
136 | /* co-processor status block */ | ||
137 | struct cop_status_block { | ||
138 | u8 valid; | ||
139 | u8 crb_seq_number; | ||
140 | u8 completion_code; | ||
141 | u8 completion_extension; | ||
142 | u32 processed_byte_count; | ||
143 | u64 address; | ||
144 | } __packed; | ||
145 | |||
146 | /* Nest accelerator workbook section 4.4 */ | ||
147 | struct nx_csbcpb { | ||
148 | unsigned char __rsvd[112]; | ||
149 | struct cop_status_block csb; | ||
150 | struct cop_parameter_block cpb; | ||
151 | } __packed; | ||
152 | |||
153 | /* nx_csbcpb related definitions */ | ||
154 | #define NX_MODE_AES_ECB 0 | ||
155 | #define NX_MODE_AES_CBC 1 | ||
156 | #define NX_MODE_AES_GMAC 2 | ||
157 | #define NX_MODE_AES_GCA 3 | ||
158 | #define NX_MODE_AES_GCM 4 | ||
159 | #define NX_MODE_AES_CCA 5 | ||
160 | #define NX_MODE_AES_CCM 6 | ||
161 | #define NX_MODE_AES_CTR 7 | ||
162 | #define NX_MODE_AES_XCBC_MAC 20 | ||
163 | #define NX_MODE_SHA 0 | ||
164 | #define NX_MODE_SHA_HMAC 1 | ||
165 | #define NX_MODE_AES_CBC_HMAC_ETA 8 | ||
166 | #define NX_MODE_AES_CBC_HMAC_ATE 9 | ||
167 | #define NX_MODE_AES_CBC_HMAC_EAA 10 | ||
168 | #define NX_MODE_AES_CTR_HMAC_ETA 12 | ||
169 | #define NX_MODE_AES_CTR_HMAC_ATE 13 | ||
170 | #define NX_MODE_AES_CTR_HMAC_EAA 14 | ||
171 | |||
172 | #define NX_FDM_CI_FULL 0 | ||
173 | #define NX_FDM_CI_FIRST 1 | ||
174 | #define NX_FDM_CI_LAST 2 | ||
175 | #define NX_FDM_CI_MIDDLE 3 | ||
176 | |||
177 | #define NX_FDM_PR_NONE 0 | ||
178 | #define NX_FDM_PR_PAD 1 | ||
179 | |||
180 | #define NX_KS_AES_128 1 | ||
181 | #define NX_KS_AES_192 2 | ||
182 | #define NX_KS_AES_256 3 | ||
183 | |||
184 | #define NX_DS_SHA256 2 | ||
185 | #define NX_DS_SHA512 3 | ||
186 | |||
187 | #define NX_FC_AES 0 | ||
188 | #define NX_FC_SHA 2 | ||
189 | #define NX_FC_AES_HMAC 6 | ||
190 | |||
191 | #define NX_MAX_FC (NX_FC_AES_HMAC + 1) | ||
192 | #define NX_MAX_MODE (NX_MODE_AES_XCBC_MAC + 1) | ||
193 | |||
194 | #define HCOP_FC_AES NX_FC_AES | ||
195 | #define HCOP_FC_SHA NX_FC_SHA | ||
196 | #define HCOP_FC_AES_HMAC NX_FC_AES_HMAC | ||
197 | |||
198 | /* indices into the array of algorithm properties */ | ||
199 | #define NX_PROPS_AES_128 0 | ||
200 | #define NX_PROPS_AES_192 1 | ||
201 | #define NX_PROPS_AES_256 2 | ||
202 | #define NX_PROPS_SHA256 1 | ||
203 | #define NX_PROPS_SHA512 2 | ||
204 | |||
205 | #endif | ||
diff --git a/drivers/crypto/nx/nx_debugfs.c b/drivers/crypto/nx/nx_debugfs.c new file mode 100644 index 000000000000..7ab2e8dcd9b4 --- /dev/null +++ b/drivers/crypto/nx/nx_debugfs.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /** | ||
2 | * debugfs routines supporting the Power 7+ Nest Accelerators driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 International Business Machines Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 only. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | * | ||
19 | * Author: Kent Yoder <yoder1@us.ibm.com> | ||
20 | */ | ||
21 | |||
22 | #include <linux/device.h> | ||
23 | #include <linux/kobject.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/crypto.h> | ||
29 | #include <crypto/hash.h> | ||
30 | #include <asm/vio.h> | ||
31 | |||
32 | #include "nx_csbcpb.h" | ||
33 | #include "nx.h" | ||
34 | |||
35 | #ifdef CONFIG_DEBUG_FS | ||
36 | |||
37 | /* | ||
38 | * debugfs | ||
39 | * | ||
40 | * For documentation on these attributes, please see: | ||
41 | * | ||
42 | * Documentation/ABI/testing/debugfs-pfo-nx-crypto | ||
43 | */ | ||
44 | |||
45 | int nx_debugfs_init(struct nx_crypto_driver *drv) | ||
46 | { | ||
47 | struct nx_debugfs *dfs = &drv->dfs; | ||
48 | |||
49 | dfs->dfs_root = debugfs_create_dir(NX_NAME, NULL); | ||
50 | |||
51 | dfs->dfs_aes_ops = | ||
52 | debugfs_create_u32("aes_ops", | ||
53 | S_IRUSR | S_IRGRP | S_IROTH, | ||
54 | dfs->dfs_root, (u32 *)&drv->stats.aes_ops); | ||
55 | dfs->dfs_sha256_ops = | ||
56 | debugfs_create_u32("sha256_ops", | ||
57 | S_IRUSR | S_IRGRP | S_IROTH, | ||
58 | dfs->dfs_root, | ||
59 | (u32 *)&drv->stats.sha256_ops); | ||
60 | dfs->dfs_sha512_ops = | ||
61 | debugfs_create_u32("sha512_ops", | ||
62 | S_IRUSR | S_IRGRP | S_IROTH, | ||
63 | dfs->dfs_root, | ||
64 | (u32 *)&drv->stats.sha512_ops); | ||
65 | dfs->dfs_aes_bytes = | ||
66 | debugfs_create_u64("aes_bytes", | ||
67 | S_IRUSR | S_IRGRP | S_IROTH, | ||
68 | dfs->dfs_root, | ||
69 | (u64 *)&drv->stats.aes_bytes); | ||
70 | dfs->dfs_sha256_bytes = | ||
71 | debugfs_create_u64("sha256_bytes", | ||
72 | S_IRUSR | S_IRGRP | S_IROTH, | ||
73 | dfs->dfs_root, | ||
74 | (u64 *)&drv->stats.sha256_bytes); | ||
75 | dfs->dfs_sha512_bytes = | ||
76 | debugfs_create_u64("sha512_bytes", | ||
77 | S_IRUSR | S_IRGRP | S_IROTH, | ||
78 | dfs->dfs_root, | ||
79 | (u64 *)&drv->stats.sha512_bytes); | ||
80 | dfs->dfs_errors = | ||
81 | debugfs_create_u32("errors", | ||
82 | S_IRUSR | S_IRGRP | S_IROTH, | ||
83 | dfs->dfs_root, (u32 *)&drv->stats.errors); | ||
84 | dfs->dfs_last_error = | ||
85 | debugfs_create_u32("last_error", | ||
86 | S_IRUSR | S_IRGRP | S_IROTH, | ||
87 | dfs->dfs_root, | ||
88 | (u32 *)&drv->stats.last_error); | ||
89 | dfs->dfs_last_error_pid = | ||
90 | debugfs_create_u32("last_error_pid", | ||
91 | S_IRUSR | S_IRGRP | S_IROTH, | ||
92 | dfs->dfs_root, | ||
93 | (u32 *)&drv->stats.last_error_pid); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | void | ||
98 | nx_debugfs_fini(struct nx_crypto_driver *drv) | ||
99 | { | ||
100 | debugfs_remove_recursive(drv->dfs.dfs_root); | ||
101 | } | ||
102 | |||
103 | #endif | ||
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 7b397c6f607e..31c47e18d83c 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) | |||
227 | return 0; | 227 | return 0; |
228 | } | 228 | } |
229 | 229 | ||
230 | static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, | ||
231 | struct pmac_i2c_bus *bus) | ||
232 | { | ||
233 | struct i2c_client *newdev; | ||
234 | struct device_node *node; | ||
235 | |||
236 | for_each_child_of_node(adap->dev.of_node, node) { | ||
237 | struct i2c_board_info info = {}; | ||
238 | struct dev_archdata dev_ad = {}; | ||
239 | const __be32 *reg; | ||
240 | char tmp[16]; | ||
241 | u32 addr; | ||
242 | int len; | ||
243 | |||
244 | /* Get address & channel */ | ||
245 | reg = of_get_property(node, "reg", &len); | ||
246 | if (!reg || (len < sizeof(int))) { | ||
247 | dev_err(&adap->dev, "i2c-powermac: invalid reg on %s\n", | ||
248 | node->full_name); | ||
249 | continue; | ||
250 | } | ||
251 | addr = be32_to_cpup(reg); | ||
252 | |||
253 | /* Multibus setup, check channel */ | ||
254 | if (!pmac_i2c_match_adapter(node, adap)) | ||
255 | continue; | ||
256 | |||
257 | dev_dbg(&adap->dev, "i2c-powermac: register %s\n", | ||
258 | node->full_name); | ||
259 | |||
260 | /* Make up a modalias. Note: we to _NOT_ want the standard | ||
261 | * i2c drivers to match with any of our powermac stuff | ||
262 | * unless they have been specifically modified to handle | ||
263 | * it on a case by case basis. For example, for thermal | ||
264 | * control, things like lm75 etc... shall match with their | ||
265 | * corresponding windfarm drivers, _NOT_ the generic ones, | ||
266 | * so we force a prefix of AAPL, onto the modalias to | ||
267 | * make that happen | ||
268 | */ | ||
269 | if (of_modalias_node(node, tmp, sizeof(tmp)) < 0) { | ||
270 | dev_err(&adap->dev, "i2c-powermac: modalias failure" | ||
271 | " on %s\n", node->full_name); | ||
272 | continue; | ||
273 | } | ||
274 | snprintf(info.type, sizeof(info.type), "MAC,%s", tmp); | ||
275 | |||
276 | /* Fill out the rest of the info structure */ | ||
277 | info.addr = (addr & 0xff) >> 1; | ||
278 | info.irq = irq_of_parse_and_map(node, 0); | ||
279 | info.of_node = of_node_get(node); | ||
280 | info.archdata = &dev_ad; | ||
281 | |||
282 | newdev = i2c_new_device(adap, &info); | ||
283 | if (!newdev) { | ||
284 | dev_err(&adap->dev, "i2c-powermac: Failure to register" | ||
285 | " %s\n", node->full_name); | ||
286 | of_node_put(node); | ||
287 | /* We do not dispose of the interrupt mapping on | ||
288 | * purpose. It's not necessary (interrupt cannot be | ||
289 | * re-used) and somebody else might have grabbed it | ||
290 | * via direct DT lookup so let's not bother | ||
291 | */ | ||
292 | continue; | ||
293 | } | ||
294 | } | ||
295 | } | ||
230 | 296 | ||
231 | static int __devinit i2c_powermac_probe(struct platform_device *dev) | 297 | static int __devinit i2c_powermac_probe(struct platform_device *dev) |
232 | { | 298 | { |
@@ -272,6 +338,7 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) | |||
272 | adapter->algo = &i2c_powermac_algorithm; | 338 | adapter->algo = &i2c_powermac_algorithm; |
273 | i2c_set_adapdata(adapter, bus); | 339 | i2c_set_adapdata(adapter, bus); |
274 | adapter->dev.parent = &dev->dev; | 340 | adapter->dev.parent = &dev->dev; |
341 | adapter->dev.of_node = dev->dev.of_node; | ||
275 | rc = i2c_add_adapter(adapter); | 342 | rc = i2c_add_adapter(adapter); |
276 | if (rc) { | 343 | if (rc) { |
277 | printk(KERN_ERR "i2c-powermac: Adapter %s registration " | 344 | printk(KERN_ERR "i2c-powermac: Adapter %s registration " |
@@ -281,33 +348,10 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) | |||
281 | 348 | ||
282 | printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); | 349 | printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); |
283 | 350 | ||
284 | if (!strncmp(basename, "uni-n", 5)) { | 351 | /* Cannot use of_i2c_register_devices() due to Apple device-tree |
285 | struct device_node *np; | 352 | * funkyness |
286 | const u32 *prop; | 353 | */ |
287 | struct i2c_board_info info; | 354 | i2c_powermac_register_devices(adapter, bus); |
288 | |||
289 | /* Instantiate I2C motion sensor if present */ | ||
290 | np = of_find_node_by_name(NULL, "accelerometer"); | ||
291 | if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") && | ||
292 | (prop = of_get_property(np, "reg", NULL))) { | ||
293 | int i2c_bus; | ||
294 | const char *tmp_bus; | ||
295 | |||
296 | /* look for bus either using "reg" or by path */ | ||
297 | tmp_bus = strstr(np->full_name, "/i2c-bus@"); | ||
298 | if (tmp_bus) | ||
299 | i2c_bus = *(tmp_bus + 9) - '0'; | ||
300 | else | ||
301 | i2c_bus = ((*prop) >> 8) & 0x0f; | ||
302 | |||
303 | if (pmac_i2c_get_channel(bus) == i2c_bus) { | ||
304 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
305 | info.addr = ((*prop) & 0xff) >> 1; | ||
306 | strlcpy(info.type, "ams", I2C_NAME_SIZE); | ||
307 | i2c_new_device(adapter, &info); | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | 355 | ||
312 | return rc; | 356 | return rc; |
313 | } | 357 | } |
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index fa51af11c6f1..a555da64224e 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig | |||
@@ -204,11 +204,14 @@ config THERM_ADT746X | |||
204 | better fan behaviour by default, and some manual control. | 204 | better fan behaviour by default, and some manual control. |
205 | 205 | ||
206 | config THERM_PM72 | 206 | config THERM_PM72 |
207 | tristate "Support for thermal management on PowerMac G5" | 207 | tristate "Support for thermal management on PowerMac G5 (AGP)" |
208 | depends on I2C && I2C_POWERMAC && PPC_PMAC64 | 208 | depends on I2C && I2C_POWERMAC && PPC_PMAC64 |
209 | default n | ||
209 | help | 210 | help |
210 | This driver provides thermostat and fan control for the desktop | 211 | This driver provides thermostat and fan control for the desktop |
211 | G5 machines. | 212 | G5 machines. |
213 | |||
214 | This is deprecated, use windfarm instead. | ||
212 | 215 | ||
213 | config WINDFARM | 216 | config WINDFARM |
214 | tristate "New PowerMac thermal control infrastructure" | 217 | tristate "New PowerMac thermal control infrastructure" |
@@ -221,6 +224,22 @@ config WINDFARM_PM81 | |||
221 | help | 224 | help |
222 | This driver provides thermal control for the iMacG5 | 225 | This driver provides thermal control for the iMacG5 |
223 | 226 | ||
227 | config WINDFARM_PM72 | ||
228 | tristate "Support for thermal management on PowerMac G5 (AGP)" | ||
229 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU | ||
230 | select I2C_POWERMAC | ||
231 | help | ||
232 | This driver provides thermal control for the PowerMac G5 | ||
233 | "AGP" variants (PowerMac 7,2 and 7,3) | ||
234 | |||
235 | config WINDFARM_RM31 | ||
236 | tristate "Support for thermal management on Xserve G5" | ||
237 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU | ||
238 | select I2C_POWERMAC | ||
239 | help | ||
240 | This driver provides thermal control for the Xserve G5 | ||
241 | (RackMac3,1) | ||
242 | |||
224 | config WINDFARM_PM91 | 243 | config WINDFARM_PM91 |
225 | tristate "Support for thermal management on PowerMac9,1" | 244 | tristate "Support for thermal management on PowerMac9,1" |
226 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU | 245 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU |
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index 6652a6ebb6fa..6753b65f8ede 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile | |||
@@ -29,6 +29,20 @@ obj-$(CONFIG_THERM_PM72) += therm_pm72.o | |||
29 | obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o | 29 | obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o |
30 | obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o | 30 | obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o |
31 | obj-$(CONFIG_WINDFARM) += windfarm_core.o | 31 | obj-$(CONFIG_WINDFARM) += windfarm_core.o |
32 | obj-$(CONFIG_WINDFARM_PM72) += windfarm_fcu_controls.o \ | ||
33 | windfarm_ad7417_sensor.o \ | ||
34 | windfarm_lm75_sensor.o \ | ||
35 | windfarm_max6690_sensor.o \ | ||
36 | windfarm_pid.o \ | ||
37 | windfarm_cpufreq_clamp.o \ | ||
38 | windfarm_pm72.o | ||
39 | obj-$(CONFIG_WINDFARM_RM31) += windfarm_fcu_controls.o \ | ||
40 | windfarm_ad7417_sensor.o \ | ||
41 | windfarm_lm75_sensor.o \ | ||
42 | windfarm_lm87_sensor.o \ | ||
43 | windfarm_pid.o \ | ||
44 | windfarm_cpufreq_clamp.o \ | ||
45 | windfarm_rm31.o | ||
32 | obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \ | 46 | obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \ |
33 | windfarm_smu_sensors.o \ | 47 | windfarm_smu_sensors.o \ |
34 | windfarm_lm75_sensor.o windfarm_pid.o \ | 48 | windfarm_lm75_sensor.o windfarm_pid.o \ |
diff --git a/drivers/macintosh/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c index abeecd27b484..978eda8d6678 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, | |||
65 | static int ams_i2c_remove(struct i2c_client *client); | 65 | static int ams_i2c_remove(struct i2c_client *client); |
66 | 66 | ||
67 | static const struct i2c_device_id ams_id[] = { | 67 | static const struct i2c_device_id ams_id[] = { |
68 | { "ams", 0 }, | 68 | { "MAC,accelerometer_1", 0 }, |
69 | { } | 69 | { } |
70 | }; | 70 | }; |
71 | MODULE_DEVICE_TABLE(i2c, ams_id); | 71 | MODULE_DEVICE_TABLE(i2c, ams_id); |
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index fc71723cbc48..f433521a6f9d 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}; | |||
47 | 47 | ||
48 | static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ | 48 | static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ |
49 | static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ | 49 | static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ |
50 | static const char *sensor_location[3]; | 50 | static const char *sensor_location[3] = { "?", "?", "?" }; |
51 | 51 | ||
52 | static int limit_adjust; | 52 | static int limit_adjust; |
53 | static int fan_speed = -1; | 53 | static int fan_speed = -1; |
@@ -79,18 +79,16 @@ struct thermostat { | |||
79 | int last_speed[2]; | 79 | int last_speed[2]; |
80 | int last_var[2]; | 80 | int last_var[2]; |
81 | int pwm_inv[2]; | 81 | int pwm_inv[2]; |
82 | struct task_struct *thread; | ||
83 | struct platform_device *pdev; | ||
84 | enum { | ||
85 | ADT7460, | ||
86 | ADT7467 | ||
87 | } type; | ||
82 | }; | 88 | }; |
83 | 89 | ||
84 | static enum {ADT7460, ADT7467} therm_type; | ||
85 | static int therm_bus, therm_address; | ||
86 | static struct platform_device * of_dev; | ||
87 | static struct thermostat* thermostat; | ||
88 | static struct task_struct *thread_therm = NULL; | ||
89 | |||
90 | static void write_both_fan_speed(struct thermostat *th, int speed); | 90 | static void write_both_fan_speed(struct thermostat *th, int speed); |
91 | static void write_fan_speed(struct thermostat *th, int speed, int fan); | 91 | static void write_fan_speed(struct thermostat *th, int speed, int fan); |
92 | static void thermostat_create_files(void); | ||
93 | static void thermostat_remove_files(void); | ||
94 | 92 | ||
95 | static int | 93 | static int |
96 | write_reg(struct thermostat* th, int reg, u8 data) | 94 | write_reg(struct thermostat* th, int reg, u8 data) |
@@ -126,66 +124,6 @@ read_reg(struct thermostat* th, int reg) | |||
126 | return data; | 124 | return data; |
127 | } | 125 | } |
128 | 126 | ||
129 | static struct i2c_driver thermostat_driver; | ||
130 | |||
131 | static int | ||
132 | attach_thermostat(struct i2c_adapter *adapter) | ||
133 | { | ||
134 | unsigned long bus_no; | ||
135 | struct i2c_board_info info; | ||
136 | struct i2c_client *client; | ||
137 | |||
138 | if (strncmp(adapter->name, "uni-n", 5)) | ||
139 | return -ENODEV; | ||
140 | bus_no = simple_strtoul(adapter->name + 6, NULL, 10); | ||
141 | if (bus_no != therm_bus) | ||
142 | return -ENODEV; | ||
143 | |||
144 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
145 | strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE); | ||
146 | info.addr = therm_address; | ||
147 | client = i2c_new_device(adapter, &info); | ||
148 | if (!client) | ||
149 | return -ENODEV; | ||
150 | |||
151 | /* | ||
152 | * Let i2c-core delete that device on driver removal. | ||
153 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
154 | */ | ||
155 | list_add_tail(&client->detected, &thermostat_driver.clients); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int | ||
160 | remove_thermostat(struct i2c_client *client) | ||
161 | { | ||
162 | struct thermostat *th = i2c_get_clientdata(client); | ||
163 | int i; | ||
164 | |||
165 | thermostat_remove_files(); | ||
166 | |||
167 | if (thread_therm != NULL) { | ||
168 | kthread_stop(thread_therm); | ||
169 | } | ||
170 | |||
171 | printk(KERN_INFO "adt746x: Putting max temperatures back from " | ||
172 | "%d, %d, %d to %d, %d, %d\n", | ||
173 | th->limits[0], th->limits[1], th->limits[2], | ||
174 | th->initial_limits[0], th->initial_limits[1], | ||
175 | th->initial_limits[2]); | ||
176 | |||
177 | for (i = 0; i < 3; i++) | ||
178 | write_reg(th, LIMIT_REG[i], th->initial_limits[i]); | ||
179 | |||
180 | write_both_fan_speed(th, -1); | ||
181 | |||
182 | thermostat = NULL; | ||
183 | |||
184 | kfree(th); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int read_fan_speed(struct thermostat *th, u8 addr) | 127 | static int read_fan_speed(struct thermostat *th, u8 addr) |
190 | { | 128 | { |
191 | u8 tmp[2]; | 129 | u8 tmp[2]; |
@@ -203,7 +141,7 @@ static int read_fan_speed(struct thermostat *th, u8 addr) | |||
203 | static void write_both_fan_speed(struct thermostat *th, int speed) | 141 | static void write_both_fan_speed(struct thermostat *th, int speed) |
204 | { | 142 | { |
205 | write_fan_speed(th, speed, 0); | 143 | write_fan_speed(th, speed, 0); |
206 | if (therm_type == ADT7460) | 144 | if (th->type == ADT7460) |
207 | write_fan_speed(th, speed, 1); | 145 | write_fan_speed(th, speed, 1); |
208 | } | 146 | } |
209 | 147 | ||
@@ -216,7 +154,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) | |||
216 | else if (speed < -1) | 154 | else if (speed < -1) |
217 | speed = 0; | 155 | speed = 0; |
218 | 156 | ||
219 | if (therm_type == ADT7467 && fan == 1) | 157 | if (th->type == ADT7467 && fan == 1) |
220 | return; | 158 | return; |
221 | 159 | ||
222 | if (th->last_speed[fan] != speed) { | 160 | if (th->last_speed[fan] != speed) { |
@@ -239,7 +177,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) | |||
239 | write_reg(th, FAN_SPD_SET[fan], speed); | 177 | write_reg(th, FAN_SPD_SET[fan], speed); |
240 | } else { | 178 | } else { |
241 | /* back to automatic */ | 179 | /* back to automatic */ |
242 | if(therm_type == ADT7460) { | 180 | if(th->type == ADT7460) { |
243 | manual = read_reg(th, | 181 | manual = read_reg(th, |
244 | MANUAL_MODE[fan]) & (~MANUAL_MASK); | 182 | MANUAL_MODE[fan]) & (~MANUAL_MASK); |
245 | manual &= ~INVERT_MASK; | 183 | manual &= ~INVERT_MASK; |
@@ -293,7 +231,7 @@ static void update_fans_speed (struct thermostat *th) | |||
293 | /* we don't care about local sensor, so we start at sensor 1 */ | 231 | /* we don't care about local sensor, so we start at sensor 1 */ |
294 | for (i = 1; i < 3; i++) { | 232 | for (i = 1; i < 3; i++) { |
295 | int started = 0; | 233 | int started = 0; |
296 | int fan_number = (therm_type == ADT7460 && i == 2); | 234 | int fan_number = (th->type == ADT7460 && i == 2); |
297 | int var = th->temps[i] - th->limits[i]; | 235 | int var = th->temps[i] - th->limits[i]; |
298 | 236 | ||
299 | if (var > -1) { | 237 | if (var > -1) { |
@@ -370,116 +308,22 @@ static int monitor_task(void *arg) | |||
370 | 308 | ||
371 | static void set_limit(struct thermostat *th, int i) | 309 | static void set_limit(struct thermostat *th, int i) |
372 | { | 310 | { |
373 | /* Set sensor1 limit higher to avoid powerdowns */ | 311 | /* Set sensor1 limit higher to avoid powerdowns */ |
374 | th->limits[i] = default_limits_chip[i] + limit_adjust; | 312 | th->limits[i] = default_limits_chip[i] + limit_adjust; |
375 | write_reg(th, LIMIT_REG[i], th->limits[i]); | 313 | write_reg(th, LIMIT_REG[i], th->limits[i]); |
376 | 314 | ||
377 | /* set our limits to normal */ | 315 | /* set our limits to normal */ |
378 | th->limits[i] = default_limits_local[i] + limit_adjust; | 316 | th->limits[i] = default_limits_local[i] + limit_adjust; |
379 | } | 317 | } |
380 | 318 | ||
381 | static int probe_thermostat(struct i2c_client *client, | 319 | #define BUILD_SHOW_FUNC_INT(name, data) \ |
382 | const struct i2c_device_id *id) | 320 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
383 | { | 321 | { \ |
384 | struct thermostat* th; | 322 | struct thermostat *th = dev_get_drvdata(dev); \ |
385 | int rc; | 323 | return sprintf(buf, "%d\n", data); \ |
386 | int i; | ||
387 | |||
388 | if (thermostat) | ||
389 | return 0; | ||
390 | |||
391 | th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); | ||
392 | if (!th) | ||
393 | return -ENOMEM; | ||
394 | |||
395 | i2c_set_clientdata(client, th); | ||
396 | th->clt = client; | ||
397 | |||
398 | rc = read_reg(th, CONFIG_REG); | ||
399 | if (rc < 0) { | ||
400 | dev_err(&client->dev, "Thermostat failed to read config!\n"); | ||
401 | kfree(th); | ||
402 | return -ENODEV; | ||
403 | } | ||
404 | |||
405 | /* force manual control to start the fan quieter */ | ||
406 | if (fan_speed == -1) | ||
407 | fan_speed = 64; | ||
408 | |||
409 | if(therm_type == ADT7460) { | ||
410 | printk(KERN_INFO "adt746x: ADT7460 initializing\n"); | ||
411 | /* The 7460 needs to be started explicitly */ | ||
412 | write_reg(th, CONFIG_REG, 1); | ||
413 | } else | ||
414 | printk(KERN_INFO "adt746x: ADT7467 initializing\n"); | ||
415 | |||
416 | for (i = 0; i < 3; i++) { | ||
417 | th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); | ||
418 | set_limit(th, i); | ||
419 | } | ||
420 | |||
421 | printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" | ||
422 | " to %d, %d, %d\n", | ||
423 | th->initial_limits[0], th->initial_limits[1], | ||
424 | th->initial_limits[2], th->limits[0], th->limits[1], | ||
425 | th->limits[2]); | ||
426 | |||
427 | thermostat = th; | ||
428 | |||
429 | /* record invert bit status because fw can corrupt it after suspend */ | ||
430 | th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; | ||
431 | th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; | ||
432 | |||
433 | /* be sure to really write fan speed the first time */ | ||
434 | th->last_speed[0] = -2; | ||
435 | th->last_speed[1] = -2; | ||
436 | th->last_var[0] = -80; | ||
437 | th->last_var[1] = -80; | ||
438 | |||
439 | if (fan_speed != -1) { | ||
440 | /* manual mode, stop fans */ | ||
441 | write_both_fan_speed(th, 0); | ||
442 | } else { | ||
443 | /* automatic mode */ | ||
444 | write_both_fan_speed(th, -1); | ||
445 | } | ||
446 | |||
447 | thread_therm = kthread_run(monitor_task, th, "kfand"); | ||
448 | |||
449 | if (thread_therm == ERR_PTR(-ENOMEM)) { | ||
450 | printk(KERN_INFO "adt746x: Kthread creation failed\n"); | ||
451 | thread_therm = NULL; | ||
452 | return -ENOMEM; | ||
453 | } | ||
454 | |||
455 | thermostat_create_files(); | ||
456 | |||
457 | return 0; | ||
458 | } | 324 | } |
459 | 325 | ||
460 | static const struct i2c_device_id therm_adt746x_id[] = { | 326 | #define BUILD_SHOW_FUNC_INT_LITE(name, data) \ |
461 | { "therm_adt746x", 0 }, | ||
462 | { } | ||
463 | }; | ||
464 | |||
465 | static struct i2c_driver thermostat_driver = { | ||
466 | .driver = { | ||
467 | .name = "therm_adt746x", | ||
468 | }, | ||
469 | .attach_adapter = attach_thermostat, | ||
470 | .probe = probe_thermostat, | ||
471 | .remove = remove_thermostat, | ||
472 | .id_table = therm_adt746x_id, | ||
473 | }; | ||
474 | |||
475 | /* | ||
476 | * Now, unfortunately, sysfs doesn't give us a nice void * we could | ||
477 | * pass around to the attribute functions, so we don't really have | ||
478 | * choice but implement a bunch of them... | ||
479 | * | ||
480 | * FIXME, it does now... | ||
481 | */ | ||
482 | #define BUILD_SHOW_FUNC_INT(name, data) \ | ||
483 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 327 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
484 | { \ | 328 | { \ |
485 | return sprintf(buf, "%d\n", data); \ | 329 | return sprintf(buf, "%d\n", data); \ |
@@ -494,22 +338,24 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch | |||
494 | #define BUILD_SHOW_FUNC_FAN(name, data) \ | 338 | #define BUILD_SHOW_FUNC_FAN(name, data) \ |
495 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 339 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
496 | { \ | 340 | { \ |
341 | struct thermostat *th = dev_get_drvdata(dev); \ | ||
497 | return sprintf(buf, "%d (%d rpm)\n", \ | 342 | return sprintf(buf, "%d (%d rpm)\n", \ |
498 | thermostat->last_speed[data], \ | 343 | th->last_speed[data], \ |
499 | read_fan_speed(thermostat, FAN_SPEED[data]) \ | 344 | read_fan_speed(th, FAN_SPEED[data]) \ |
500 | ); \ | 345 | ); \ |
501 | } | 346 | } |
502 | 347 | ||
503 | #define BUILD_STORE_FUNC_DEG(name, data) \ | 348 | #define BUILD_STORE_FUNC_DEG(name, data) \ |
504 | static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ | 349 | static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ |
505 | { \ | 350 | { \ |
351 | struct thermostat *th = dev_get_drvdata(dev); \ | ||
506 | int val; \ | 352 | int val; \ |
507 | int i; \ | 353 | int i; \ |
508 | val = simple_strtol(buf, NULL, 10); \ | 354 | val = simple_strtol(buf, NULL, 10); \ |
509 | printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ | 355 | printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ |
510 | limit_adjust = val; \ | 356 | limit_adjust = val; \ |
511 | for (i=0; i < 3; i++) \ | 357 | for (i=0; i < 3; i++) \ |
512 | set_limit(thermostat, i); \ | 358 | set_limit(th, i); \ |
513 | return n; \ | 359 | return n; \ |
514 | } | 360 | } |
515 | 361 | ||
@@ -525,20 +371,21 @@ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, c | |||
525 | return n; \ | 371 | return n; \ |
526 | } | 372 | } |
527 | 373 | ||
528 | BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1]))) | 374 | BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(th, TEMP_REG[1]))) |
529 | BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2]))) | 375 | BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(th, TEMP_REG[2]))) |
530 | BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1]) | 376 | BUILD_SHOW_FUNC_INT(sensor1_limit, th->limits[1]) |
531 | BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2]) | 377 | BUILD_SHOW_FUNC_INT(sensor2_limit, th->limits[2]) |
532 | BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) | 378 | BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) |
533 | BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) | 379 | BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) |
534 | 380 | ||
535 | BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed) | 381 | BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed) |
382 | BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) | ||
383 | |||
536 | BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) | 384 | BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) |
537 | BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) | 385 | BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) |
538 | 386 | ||
539 | BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) | 387 | BUILD_SHOW_FUNC_INT_LITE(limit_adjust, limit_adjust) |
540 | BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust) | 388 | BUILD_STORE_FUNC_DEG(limit_adjust, th) |
541 | BUILD_STORE_FUNC_DEG(limit_adjust, thermostat) | ||
542 | 389 | ||
543 | static DEVICE_ATTR(sensor1_temperature, S_IRUGO, | 390 | static DEVICE_ATTR(sensor1_temperature, S_IRUGO, |
544 | show_sensor1_temperature,NULL); | 391 | show_sensor1_temperature,NULL); |
@@ -564,53 +411,77 @@ static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO, | |||
564 | static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, | 411 | static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, |
565 | show_limit_adjust, store_limit_adjust); | 412 | show_limit_adjust, store_limit_adjust); |
566 | 413 | ||
567 | 414 | static void thermostat_create_files(struct thermostat *th) | |
568 | static int __init | ||
569 | thermostat_init(void) | ||
570 | { | 415 | { |
571 | struct device_node* np; | 416 | struct device_node *np = th->clt->dev.of_node; |
572 | const u32 *prop; | 417 | struct device *dev; |
573 | int i = 0, offset = 0; | 418 | int err; |
574 | 419 | ||
575 | np = of_find_node_by_name(NULL, "fan"); | 420 | /* To maintain ABI compatibility with userspace, create |
576 | if (!np) | 421 | * the old style platform driver and attach the attributes |
577 | return -ENODEV; | 422 | * to it here |
578 | if (of_device_is_compatible(np, "adt7460")) | 423 | */ |
579 | therm_type = ADT7460; | 424 | th->pdev = of_platform_device_create(np, "temperatures", NULL); |
580 | else if (of_device_is_compatible(np, "adt7467")) | 425 | if (!th->pdev) |
581 | therm_type = ADT7467; | 426 | return; |
582 | else { | 427 | dev = &th->pdev->dev; |
583 | of_node_put(np); | 428 | dev_set_drvdata(dev, th); |
584 | return -ENODEV; | 429 | err = device_create_file(dev, &dev_attr_sensor1_temperature); |
585 | } | 430 | err |= device_create_file(dev, &dev_attr_sensor2_temperature); |
431 | err |= device_create_file(dev, &dev_attr_sensor1_limit); | ||
432 | err |= device_create_file(dev, &dev_attr_sensor2_limit); | ||
433 | err |= device_create_file(dev, &dev_attr_sensor1_location); | ||
434 | err |= device_create_file(dev, &dev_attr_sensor2_location); | ||
435 | err |= device_create_file(dev, &dev_attr_limit_adjust); | ||
436 | err |= device_create_file(dev, &dev_attr_specified_fan_speed); | ||
437 | err |= device_create_file(dev, &dev_attr_sensor1_fan_speed); | ||
438 | if(th->type == ADT7460) | ||
439 | err |= device_create_file(dev, &dev_attr_sensor2_fan_speed); | ||
440 | if (err) | ||
441 | printk(KERN_WARNING | ||
442 | "Failed to create temperature attribute file(s).\n"); | ||
443 | } | ||
586 | 444 | ||
587 | prop = of_get_property(np, "hwsensor-params-version", NULL); | 445 | static void thermostat_remove_files(struct thermostat *th) |
588 | printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, | 446 | { |
589 | (*prop == 1)?"":"un"); | 447 | struct device *dev; |
590 | if (*prop != 1) { | ||
591 | of_node_put(np); | ||
592 | return -ENODEV; | ||
593 | } | ||
594 | 448 | ||
595 | prop = of_get_property(np, "reg", NULL); | 449 | if (!th->pdev) |
596 | if (!prop) { | 450 | return; |
597 | of_node_put(np); | 451 | dev = &th->pdev->dev; |
598 | return -ENODEV; | 452 | device_remove_file(dev, &dev_attr_sensor1_temperature); |
599 | } | 453 | device_remove_file(dev, &dev_attr_sensor2_temperature); |
454 | device_remove_file(dev, &dev_attr_sensor1_limit); | ||
455 | device_remove_file(dev, &dev_attr_sensor2_limit); | ||
456 | device_remove_file(dev, &dev_attr_sensor1_location); | ||
457 | device_remove_file(dev, &dev_attr_sensor2_location); | ||
458 | device_remove_file(dev, &dev_attr_limit_adjust); | ||
459 | device_remove_file(dev, &dev_attr_specified_fan_speed); | ||
460 | device_remove_file(dev, &dev_attr_sensor1_fan_speed); | ||
461 | if (th->type == ADT7460) | ||
462 | device_remove_file(dev, &dev_attr_sensor2_fan_speed); | ||
463 | of_device_unregister(th->pdev); | ||
600 | 464 | ||
601 | /* look for bus either by path or using "reg" */ | 465 | } |
602 | if (strstr(np->full_name, "/i2c-bus@") != NULL) { | ||
603 | const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9); | ||
604 | therm_bus = tmp_bus[0]-'0'; | ||
605 | } else { | ||
606 | therm_bus = ((*prop) >> 8) & 0x0f; | ||
607 | } | ||
608 | 466 | ||
609 | therm_address = ((*prop) & 0xff) >> 1; | 467 | static int probe_thermostat(struct i2c_client *client, |
468 | const struct i2c_device_id *id) | ||
469 | { | ||
470 | struct device_node *np = client->dev.of_node; | ||
471 | struct thermostat* th; | ||
472 | const __be32 *prop; | ||
473 | int i, rc, vers, offset = 0; | ||
610 | 474 | ||
611 | printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, " | 475 | if (!np) |
612 | "limit_adjust: %d, fan_speed: %d\n", | 476 | return -ENXIO; |
613 | therm_bus, therm_address, limit_adjust, fan_speed); | 477 | prop = of_get_property(np, "hwsensor-params-version", NULL); |
478 | if (!prop) | ||
479 | return -ENXIO; | ||
480 | vers = be32_to_cpup(prop); | ||
481 | printk(KERN_INFO "adt746x: version %d (%ssupported)\n", | ||
482 | vers, vers == 1 ? "" : "un"); | ||
483 | if (vers != 1) | ||
484 | return -ENXIO; | ||
614 | 485 | ||
615 | if (of_get_property(np, "hwsensor-location", NULL)) { | 486 | if (of_get_property(np, "hwsensor-location", NULL)) { |
616 | for (i = 0; i < 3; i++) { | 487 | for (i = 0; i < 3; i++) { |
@@ -623,72 +494,129 @@ thermostat_init(void) | |||
623 | printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); | 494 | printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); |
624 | offset += strlen(sensor_location[i]) + 1; | 495 | offset += strlen(sensor_location[i]) + 1; |
625 | } | 496 | } |
626 | } else { | ||
627 | sensor_location[0] = "?"; | ||
628 | sensor_location[1] = "?"; | ||
629 | sensor_location[2] = "?"; | ||
630 | } | 497 | } |
631 | 498 | ||
632 | of_dev = of_platform_device_create(np, "temperatures", NULL); | 499 | th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); |
633 | of_node_put(np); | 500 | if (!th) |
501 | return -ENOMEM; | ||
502 | |||
503 | i2c_set_clientdata(client, th); | ||
504 | th->clt = client; | ||
505 | th->type = id->driver_data; | ||
634 | 506 | ||
635 | if (of_dev == NULL) { | 507 | rc = read_reg(th, CONFIG_REG); |
636 | printk(KERN_ERR "Can't register temperatures device !\n"); | 508 | if (rc < 0) { |
509 | dev_err(&client->dev, "Thermostat failed to read config!\n"); | ||
510 | kfree(th); | ||
637 | return -ENODEV; | 511 | return -ENODEV; |
638 | } | 512 | } |
639 | 513 | ||
640 | #ifndef CONFIG_I2C_POWERMAC | 514 | /* force manual control to start the fan quieter */ |
641 | request_module("i2c-powermac"); | 515 | if (fan_speed == -1) |
642 | #endif | 516 | fan_speed = 64; |
517 | |||
518 | if (th->type == ADT7460) { | ||
519 | printk(KERN_INFO "adt746x: ADT7460 initializing\n"); | ||
520 | /* The 7460 needs to be started explicitly */ | ||
521 | write_reg(th, CONFIG_REG, 1); | ||
522 | } else | ||
523 | printk(KERN_INFO "adt746x: ADT7467 initializing\n"); | ||
643 | 524 | ||
644 | return i2c_add_driver(&thermostat_driver); | 525 | for (i = 0; i < 3; i++) { |
526 | th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); | ||
527 | set_limit(th, i); | ||
528 | } | ||
529 | |||
530 | printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" | ||
531 | " to %d, %d, %d\n", | ||
532 | th->initial_limits[0], th->initial_limits[1], | ||
533 | th->initial_limits[2], th->limits[0], th->limits[1], | ||
534 | th->limits[2]); | ||
535 | |||
536 | /* record invert bit status because fw can corrupt it after suspend */ | ||
537 | th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; | ||
538 | th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; | ||
539 | |||
540 | /* be sure to really write fan speed the first time */ | ||
541 | th->last_speed[0] = -2; | ||
542 | th->last_speed[1] = -2; | ||
543 | th->last_var[0] = -80; | ||
544 | th->last_var[1] = -80; | ||
545 | |||
546 | if (fan_speed != -1) { | ||
547 | /* manual mode, stop fans */ | ||
548 | write_both_fan_speed(th, 0); | ||
549 | } else { | ||
550 | /* automatic mode */ | ||
551 | write_both_fan_speed(th, -1); | ||
552 | } | ||
553 | |||
554 | th->thread = kthread_run(monitor_task, th, "kfand"); | ||
555 | if (th->thread == ERR_PTR(-ENOMEM)) { | ||
556 | printk(KERN_INFO "adt746x: Kthread creation failed\n"); | ||
557 | th->thread = NULL; | ||
558 | return -ENOMEM; | ||
559 | } | ||
560 | |||
561 | thermostat_create_files(th); | ||
562 | |||
563 | return 0; | ||
645 | } | 564 | } |
646 | 565 | ||
647 | static void thermostat_create_files(void) | 566 | static int remove_thermostat(struct i2c_client *client) |
648 | { | 567 | { |
649 | int err; | 568 | struct thermostat *th = i2c_get_clientdata(client); |
569 | int i; | ||
570 | |||
571 | thermostat_remove_files(th); | ||
650 | 572 | ||
651 | err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature); | 573 | if (th->thread != NULL) |
652 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature); | 574 | kthread_stop(th->thread); |
653 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit); | 575 | |
654 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit); | 576 | printk(KERN_INFO "adt746x: Putting max temperatures back from " |
655 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location); | 577 | "%d, %d, %d to %d, %d, %d\n", |
656 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location); | 578 | th->limits[0], th->limits[1], th->limits[2], |
657 | err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust); | 579 | th->initial_limits[0], th->initial_limits[1], |
658 | err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed); | 580 | th->initial_limits[2]); |
659 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); | 581 | |
660 | if(therm_type == ADT7460) | 582 | for (i = 0; i < 3; i++) |
661 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed); | 583 | write_reg(th, LIMIT_REG[i], th->initial_limits[i]); |
662 | if (err) | 584 | |
663 | printk(KERN_WARNING | 585 | write_both_fan_speed(th, -1); |
664 | "Failed to create temperature attribute file(s).\n"); | 586 | |
587 | kfree(th); | ||
588 | |||
589 | return 0; | ||
665 | } | 590 | } |
666 | 591 | ||
667 | static void thermostat_remove_files(void) | 592 | static const struct i2c_device_id therm_adt746x_id[] = { |
593 | { "MAC,adt7460", ADT7460 }, | ||
594 | { "MAC,adt7467", ADT7467 }, | ||
595 | { } | ||
596 | }; | ||
597 | MODULE_DEVICE_TABLE(i2c, therm_adt746x_id); | ||
598 | |||
599 | static struct i2c_driver thermostat_driver = { | ||
600 | .driver = { | ||
601 | .name = "therm_adt746x", | ||
602 | }, | ||
603 | .probe = probe_thermostat, | ||
604 | .remove = remove_thermostat, | ||
605 | .id_table = therm_adt746x_id, | ||
606 | }; | ||
607 | |||
608 | static int __init thermostat_init(void) | ||
668 | { | 609 | { |
669 | if (of_dev) { | 610 | #ifndef CONFIG_I2C_POWERMAC |
670 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature); | 611 | request_module("i2c-powermac"); |
671 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature); | 612 | #endif |
672 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit); | ||
673 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit); | ||
674 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_location); | ||
675 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_location); | ||
676 | device_remove_file(&of_dev->dev, &dev_attr_limit_adjust); | ||
677 | device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed); | ||
678 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); | ||
679 | |||
680 | if(therm_type == ADT7460) | ||
681 | device_remove_file(&of_dev->dev, | ||
682 | &dev_attr_sensor2_fan_speed); | ||
683 | 613 | ||
684 | } | 614 | return i2c_add_driver(&thermostat_driver); |
685 | } | 615 | } |
686 | 616 | ||
687 | static void __exit | 617 | static void __exit thermostat_exit(void) |
688 | thermostat_exit(void) | ||
689 | { | 618 | { |
690 | i2c_del_driver(&thermostat_driver); | 619 | i2c_del_driver(&thermostat_driver); |
691 | of_device_unregister(of_dev); | ||
692 | } | 620 | } |
693 | 621 | ||
694 | module_init(thermostat_init); | 622 | module_init(thermostat_init); |
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h index 7a2482cc26a7..028cdac2d33d 100644 --- a/drivers/macintosh/windfarm.h +++ b/drivers/macintosh/windfarm.h | |||
@@ -17,7 +17,7 @@ | |||
17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
18 | 18 | ||
19 | /* Display a 16.16 fixed point value */ | 19 | /* Display a 16.16 fixed point value */ |
20 | #define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16) | 20 | #define FIX32TOPRINT(f) (((s32)(f)) >> 16),(((((s32)(f)) & 0xffff) * 1000) >> 16) |
21 | 21 | ||
22 | /* | 22 | /* |
23 | * Control objects | 23 | * Control objects |
@@ -35,12 +35,13 @@ struct wf_control_ops { | |||
35 | }; | 35 | }; |
36 | 36 | ||
37 | struct wf_control { | 37 | struct wf_control { |
38 | struct list_head link; | 38 | struct list_head link; |
39 | struct wf_control_ops *ops; | 39 | const struct wf_control_ops *ops; |
40 | char *name; | 40 | const char *name; |
41 | int type; | 41 | int type; |
42 | struct kref ref; | 42 | struct kref ref; |
43 | struct device_attribute attr; | 43 | struct device_attribute attr; |
44 | void *priv; | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | #define WF_CONTROL_TYPE_GENERIC 0 | 47 | #define WF_CONTROL_TYPE_GENERIC 0 |
@@ -72,6 +73,26 @@ static inline int wf_control_set_min(struct wf_control *ct) | |||
72 | return ct->ops->set_value(ct, vmin); | 73 | return ct->ops->set_value(ct, vmin); |
73 | } | 74 | } |
74 | 75 | ||
76 | static inline int wf_control_set(struct wf_control *ct, s32 val) | ||
77 | { | ||
78 | return ct->ops->set_value(ct, val); | ||
79 | } | ||
80 | |||
81 | static inline int wf_control_get(struct wf_control *ct, s32 *val) | ||
82 | { | ||
83 | return ct->ops->get_value(ct, val); | ||
84 | } | ||
85 | |||
86 | static inline s32 wf_control_get_min(struct wf_control *ct) | ||
87 | { | ||
88 | return ct->ops->get_min(ct); | ||
89 | } | ||
90 | |||
91 | static inline s32 wf_control_get_max(struct wf_control *ct) | ||
92 | { | ||
93 | return ct->ops->get_max(ct); | ||
94 | } | ||
95 | |||
75 | /* | 96 | /* |
76 | * Sensor objects | 97 | * Sensor objects |
77 | */ | 98 | */ |
@@ -85,11 +106,12 @@ struct wf_sensor_ops { | |||
85 | }; | 106 | }; |
86 | 107 | ||
87 | struct wf_sensor { | 108 | struct wf_sensor { |
88 | struct list_head link; | 109 | struct list_head link; |
89 | struct wf_sensor_ops *ops; | 110 | const struct wf_sensor_ops *ops; |
90 | char *name; | 111 | const char *name; |
91 | struct kref ref; | 112 | struct kref ref; |
92 | struct device_attribute attr; | 113 | struct device_attribute attr; |
114 | void *priv; | ||
93 | }; | 115 | }; |
94 | 116 | ||
95 | /* Same lifetime rules as controls */ | 117 | /* Same lifetime rules as controls */ |
@@ -99,6 +121,11 @@ extern struct wf_sensor * wf_find_sensor(const char *name); | |||
99 | extern int wf_get_sensor(struct wf_sensor *sr); | 121 | extern int wf_get_sensor(struct wf_sensor *sr); |
100 | extern void wf_put_sensor(struct wf_sensor *sr); | 122 | extern void wf_put_sensor(struct wf_sensor *sr); |
101 | 123 | ||
124 | static inline int wf_sensor_get(struct wf_sensor *sr, s32 *val) | ||
125 | { | ||
126 | return sr->ops->get_value(sr, val); | ||
127 | } | ||
128 | |||
102 | /* For use by clients. Note that we are a bit racy here since | 129 | /* For use by clients. Note that we are a bit racy here since |
103 | * notifier_block doesn't have a module owner field. I may fix | 130 | * notifier_block doesn't have a module owner field. I may fix |
104 | * it one day ... | 131 | * it one day ... |
diff --git a/drivers/macintosh/windfarm_ad7417_sensor.c b/drivers/macintosh/windfarm_ad7417_sensor.c new file mode 100644 index 000000000000..ac3f243b9c5a --- /dev/null +++ b/drivers/macintosh/windfarm_ad7417_sensor.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. AD7417 sensors | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <asm/prom.h> | ||
18 | #include <asm/machdep.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/sections.h> | ||
21 | |||
22 | #include "windfarm.h" | ||
23 | #include "windfarm_mpu.h" | ||
24 | |||
25 | #define VERSION "1.0" | ||
26 | |||
27 | struct wf_ad7417_priv { | ||
28 | struct kref ref; | ||
29 | struct i2c_client *i2c; | ||
30 | u8 config; | ||
31 | u8 cpu; | ||
32 | const struct mpu_data *mpu; | ||
33 | struct wf_sensor sensors[5]; | ||
34 | struct mutex lock; | ||
35 | }; | ||
36 | |||
37 | static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value) | ||
38 | { | ||
39 | struct wf_ad7417_priv *pv = sr->priv; | ||
40 | u8 buf[2]; | ||
41 | s16 raw; | ||
42 | int rc; | ||
43 | |||
44 | *value = 0; | ||
45 | mutex_lock(&pv->lock); | ||
46 | |||
47 | /* Read temp register */ | ||
48 | buf[0] = 0; | ||
49 | rc = i2c_master_send(pv->i2c, buf, 1); | ||
50 | if (rc < 0) | ||
51 | goto error; | ||
52 | rc = i2c_master_recv(pv->i2c, buf, 2); | ||
53 | if (rc < 0) | ||
54 | goto error; | ||
55 | |||
56 | /* Read a a 16-bit signed value */ | ||
57 | raw = be16_to_cpup((__le16 *)buf); | ||
58 | |||
59 | /* Convert 8.8-bit to 16.16 fixed point */ | ||
60 | *value = ((s32)raw) << 8; | ||
61 | |||
62 | mutex_unlock(&pv->lock); | ||
63 | return 0; | ||
64 | |||
65 | error: | ||
66 | mutex_unlock(&pv->lock); | ||
67 | return -1; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Scaling factors for the AD7417 ADC converters (except | ||
72 | * for the CPU diode which is obtained from the EEPROM). | ||
73 | * Those values are obtained from the property list of | ||
74 | * the darwin driver | ||
75 | */ | ||
76 | #define ADC_12V_CURRENT_SCALE 0x0320 /* _AD2 */ | ||
77 | #define ADC_CPU_VOLTAGE_SCALE 0x00a0 /* _AD3 */ | ||
78 | #define ADC_CPU_CURRENT_SCALE 0x1f40 /* _AD4 */ | ||
79 | |||
80 | static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv, | ||
81 | int chan, s32 raw, s32 *value) | ||
82 | { | ||
83 | switch(chan) { | ||
84 | case 1: /* Diode */ | ||
85 | *value = (raw * (s32)pv->mpu->mdiode + | ||
86 | ((s32)pv->mpu->bdiode << 12)) >> 2; | ||
87 | break; | ||
88 | case 2: /* 12v current */ | ||
89 | *value = raw * ADC_12V_CURRENT_SCALE; | ||
90 | break; | ||
91 | case 3: /* core voltage */ | ||
92 | *value = raw * ADC_CPU_VOLTAGE_SCALE; | ||
93 | break; | ||
94 | case 4: /* core current */ | ||
95 | *value = raw * ADC_CPU_CURRENT_SCALE; | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value) | ||
101 | { | ||
102 | struct wf_ad7417_priv *pv = sr->priv; | ||
103 | int chan = sr - pv->sensors; | ||
104 | int i, rc; | ||
105 | u8 buf[2]; | ||
106 | u16 raw; | ||
107 | |||
108 | *value = 0; | ||
109 | mutex_lock(&pv->lock); | ||
110 | for (i = 0; i < 10; i++) { | ||
111 | /* Set channel */ | ||
112 | buf[0] = 1; | ||
113 | buf[1] = (pv->config & 0x1f) | (chan << 5); | ||
114 | rc = i2c_master_send(pv->i2c, buf, 2); | ||
115 | if (rc < 0) | ||
116 | goto error; | ||
117 | |||
118 | /* Wait for conversion */ | ||
119 | msleep(1); | ||
120 | |||
121 | /* Switch to data register */ | ||
122 | buf[0] = 4; | ||
123 | rc = i2c_master_send(pv->i2c, buf, 1); | ||
124 | if (rc < 0) | ||
125 | goto error; | ||
126 | |||
127 | /* Read result */ | ||
128 | rc = i2c_master_recv(pv->i2c, buf, 2); | ||
129 | if (rc < 0) | ||
130 | goto error; | ||
131 | |||
132 | /* Read a a 16-bit signed value */ | ||
133 | raw = be16_to_cpup((__le16 *)buf) >> 6; | ||
134 | wf_ad7417_adc_convert(pv, chan, raw, value); | ||
135 | |||
136 | dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]" | ||
137 | " raw value: 0x%x, conv to: 0x%08x\n", | ||
138 | chan, sr->name, raw, *value); | ||
139 | |||
140 | mutex_unlock(&pv->lock); | ||
141 | return 0; | ||
142 | |||
143 | error: | ||
144 | dev_dbg(&pv->i2c->dev, | ||
145 | "Error reading ADC, try %d...\n", i); | ||
146 | if (i < 9) | ||
147 | msleep(10); | ||
148 | } | ||
149 | mutex_unlock(&pv->lock); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | static void wf_ad7417_release(struct kref *ref) | ||
154 | { | ||
155 | struct wf_ad7417_priv *pv = container_of(ref, | ||
156 | struct wf_ad7417_priv, ref); | ||
157 | kfree(pv); | ||
158 | } | ||
159 | |||
160 | static void wf_ad7417_sensor_release(struct wf_sensor *sr) | ||
161 | { | ||
162 | struct wf_ad7417_priv *pv = sr->priv; | ||
163 | |||
164 | kfree(sr->name); | ||
165 | kref_put(&pv->ref, wf_ad7417_release); | ||
166 | } | ||
167 | |||
168 | static const struct wf_sensor_ops wf_ad7417_temp_ops = { | ||
169 | .get_value = wf_ad7417_temp_get, | ||
170 | .release = wf_ad7417_sensor_release, | ||
171 | .owner = THIS_MODULE, | ||
172 | }; | ||
173 | |||
174 | static const struct wf_sensor_ops wf_ad7417_adc_ops = { | ||
175 | .get_value = wf_ad7417_adc_get, | ||
176 | .release = wf_ad7417_sensor_release, | ||
177 | .owner = THIS_MODULE, | ||
178 | }; | ||
179 | |||
180 | static void __devinit wf_ad7417_add_sensor(struct wf_ad7417_priv *pv, | ||
181 | int index, const char *name, | ||
182 | const struct wf_sensor_ops *ops) | ||
183 | { | ||
184 | pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu); | ||
185 | pv->sensors[index].priv = pv; | ||
186 | pv->sensors[index].ops = ops; | ||
187 | if (!wf_register_sensor(&pv->sensors[index])) | ||
188 | kref_get(&pv->ref); | ||
189 | } | ||
190 | |||
191 | static void __devinit wf_ad7417_init_chip(struct wf_ad7417_priv *pv) | ||
192 | { | ||
193 | int rc; | ||
194 | u8 buf[2]; | ||
195 | u8 config = 0; | ||
196 | |||
197 | /* | ||
198 | * Read ADC the configuration register and cache it. We | ||
199 | * also make sure Config2 contains proper values, I've seen | ||
200 | * cases where we got stale grabage in there, thus preventing | ||
201 | * proper reading of conv. values | ||
202 | */ | ||
203 | |||
204 | /* Clear Config2 */ | ||
205 | buf[0] = 5; | ||
206 | buf[1] = 0; | ||
207 | i2c_master_send(pv->i2c, buf, 2); | ||
208 | |||
209 | /* Read & cache Config1 */ | ||
210 | buf[0] = 1; | ||
211 | rc = i2c_master_send(pv->i2c, buf, 1); | ||
212 | if (rc > 0) { | ||
213 | rc = i2c_master_recv(pv->i2c, buf, 1); | ||
214 | if (rc > 0) { | ||
215 | config = buf[0]; | ||
216 | |||
217 | dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n", | ||
218 | config); | ||
219 | |||
220 | /* Disable shutdown mode */ | ||
221 | config &= 0xfe; | ||
222 | buf[0] = 1; | ||
223 | buf[1] = config; | ||
224 | rc = i2c_master_send(pv->i2c, buf, 2); | ||
225 | } | ||
226 | } | ||
227 | if (rc <= 0) | ||
228 | dev_err(&pv->i2c->dev, "Error reading ADC config\n"); | ||
229 | |||
230 | pv->config = config; | ||
231 | } | ||
232 | |||
233 | static int __devinit wf_ad7417_probe(struct i2c_client *client, | ||
234 | const struct i2c_device_id *id) | ||
235 | { | ||
236 | struct wf_ad7417_priv *pv; | ||
237 | const struct mpu_data *mpu; | ||
238 | const char *loc; | ||
239 | int cpu_nr; | ||
240 | |||
241 | loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); | ||
242 | if (!loc) { | ||
243 | dev_warn(&client->dev, "Missing hwsensor-location property!\n"); | ||
244 | return -ENXIO; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Identify which CPU we belong to by looking at the first entry | ||
249 | * in the hwsensor-location list | ||
250 | */ | ||
251 | if (!strncmp(loc, "CPU A", 5)) | ||
252 | cpu_nr = 0; | ||
253 | else if (!strncmp(loc, "CPU B", 5)) | ||
254 | cpu_nr = 1; | ||
255 | else { | ||
256 | pr_err("wf_ad7417: Can't identify location %s\n", loc); | ||
257 | return -ENXIO; | ||
258 | } | ||
259 | mpu = wf_get_mpu(cpu_nr); | ||
260 | if (!mpu) { | ||
261 | dev_err(&client->dev, "Failed to retrieve MPU data\n"); | ||
262 | return -ENXIO; | ||
263 | } | ||
264 | |||
265 | pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL); | ||
266 | if (pv == NULL) | ||
267 | return -ENODEV; | ||
268 | |||
269 | kref_init(&pv->ref); | ||
270 | mutex_init(&pv->lock); | ||
271 | pv->i2c = client; | ||
272 | pv->cpu = cpu_nr; | ||
273 | pv->mpu = mpu; | ||
274 | dev_set_drvdata(&client->dev, pv); | ||
275 | |||
276 | /* Initialize the chip */ | ||
277 | wf_ad7417_init_chip(pv); | ||
278 | |||
279 | /* | ||
280 | * We cannot rely on Apple device-tree giving us child | ||
281 | * node with the names of the individual sensors so we | ||
282 | * just hard code what we know about them | ||
283 | */ | ||
284 | wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops); | ||
285 | wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops); | ||
286 | wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops); | ||
287 | wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops); | ||
288 | wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops); | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static int __devexit wf_ad7417_remove(struct i2c_client *client) | ||
294 | { | ||
295 | struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev); | ||
296 | int i; | ||
297 | |||
298 | /* Mark client detached */ | ||
299 | pv->i2c = NULL; | ||
300 | |||
301 | /* Release sensor */ | ||
302 | for (i = 0; i < 5; i++) | ||
303 | wf_unregister_sensor(&pv->sensors[i]); | ||
304 | |||
305 | kref_put(&pv->ref, wf_ad7417_release); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static const struct i2c_device_id wf_ad7417_id[] = { | ||
311 | { "MAC,ad7417", 0 }, | ||
312 | { } | ||
313 | }; | ||
314 | MODULE_DEVICE_TABLE(i2c, wf_ad7417_id); | ||
315 | |||
316 | static struct i2c_driver wf_ad7417_driver = { | ||
317 | .driver = { | ||
318 | .name = "wf_ad7417", | ||
319 | }, | ||
320 | .probe = wf_ad7417_probe, | ||
321 | .remove = wf_ad7417_remove, | ||
322 | .id_table = wf_ad7417_id, | ||
323 | }; | ||
324 | |||
325 | static int __devinit wf_ad7417_init(void) | ||
326 | { | ||
327 | /* This is only supported on these machines */ | ||
328 | if (!of_machine_is_compatible("PowerMac7,2") && | ||
329 | !of_machine_is_compatible("PowerMac7,3") && | ||
330 | !of_machine_is_compatible("RackMac3,1")) | ||
331 | return -ENODEV; | ||
332 | |||
333 | return i2c_add_driver(&wf_ad7417_driver); | ||
334 | } | ||
335 | |||
336 | static void __devexit wf_ad7417_exit(void) | ||
337 | { | ||
338 | i2c_del_driver(&wf_ad7417_driver); | ||
339 | } | ||
340 | |||
341 | module_init(wf_ad7417_init); | ||
342 | module_exit(wf_ad7417_exit); | ||
343 | |||
344 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
345 | MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs"); | ||
346 | MODULE_LICENSE("GPL"); | ||
347 | |||
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index ce8897933a84..3ee198b65843 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, | |||
164 | struct device_attribute *attr, char *buf) | 164 | struct device_attribute *attr, char *buf) |
165 | { | 165 | { |
166 | struct wf_control *ctrl = container_of(attr, struct wf_control, attr); | 166 | struct wf_control *ctrl = container_of(attr, struct wf_control, attr); |
167 | const char *typestr; | ||
167 | s32 val = 0; | 168 | s32 val = 0; |
168 | int err; | 169 | int err; |
169 | 170 | ||
170 | err = ctrl->ops->get_value(ctrl, &val); | 171 | err = ctrl->ops->get_value(ctrl, &val); |
171 | if (err < 0) | 172 | if (err < 0) { |
173 | if (err == -EFAULT) | ||
174 | return sprintf(buf, "<HW FAULT>\n"); | ||
172 | return err; | 175 | return err; |
173 | return sprintf(buf, "%d\n", val); | 176 | } |
177 | switch(ctrl->type) { | ||
178 | case WF_CONTROL_RPM_FAN: | ||
179 | typestr = " RPM"; | ||
180 | break; | ||
181 | case WF_CONTROL_PWM_FAN: | ||
182 | typestr = " %"; | ||
183 | break; | ||
184 | default: | ||
185 | typestr = ""; | ||
186 | } | ||
187 | return sprintf(buf, "%d%s\n", val, typestr); | ||
174 | } | 188 | } |
175 | 189 | ||
176 | /* This is really only for debugging... */ | 190 | /* This is really only for debugging... */ |
@@ -470,11 +484,6 @@ static int __init windfarm_core_init(void) | |||
470 | { | 484 | { |
471 | DBG("wf: core loaded\n"); | 485 | DBG("wf: core loaded\n"); |
472 | 486 | ||
473 | /* Don't register on old machines that use therm_pm72 for now */ | ||
474 | if (of_machine_is_compatible("PowerMac7,2") || | ||
475 | of_machine_is_compatible("PowerMac7,3") || | ||
476 | of_machine_is_compatible("RackMac3,1")) | ||
477 | return -ENODEV; | ||
478 | platform_device_register(&wf_platform_device); | 487 | platform_device_register(&wf_platform_device); |
479 | return 0; | 488 | return 0; |
480 | } | 489 | } |
diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c index 1a77a7c97d0e..72d1fdfe02a5 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) | |||
75 | { | 75 | { |
76 | struct wf_control *clamp; | 76 | struct wf_control *clamp; |
77 | 77 | ||
78 | /* Don't register on old machines that use therm_pm72 for now */ | ||
79 | if (of_machine_is_compatible("PowerMac7,2") || | ||
80 | of_machine_is_compatible("PowerMac7,3") || | ||
81 | of_machine_is_compatible("RackMac3,1")) | ||
82 | return -ENODEV; | ||
83 | |||
84 | clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); | 78 | clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); |
85 | if (clamp == NULL) | 79 | if (clamp == NULL) |
86 | return -ENOMEM; | 80 | return -ENOMEM; |
diff --git a/drivers/macintosh/windfarm_fcu_controls.c b/drivers/macintosh/windfarm_fcu_controls.c new file mode 100644 index 000000000000..b3411edb324b --- /dev/null +++ b/drivers/macintosh/windfarm_fcu_controls.c | |||
@@ -0,0 +1,613 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. FCU fan control | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | */ | ||
8 | #undef DEBUG | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/wait.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <asm/prom.h> | ||
19 | #include <asm/machdep.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/sections.h> | ||
22 | |||
23 | #include "windfarm.h" | ||
24 | #include "windfarm_mpu.h" | ||
25 | |||
26 | #define VERSION "1.0" | ||
27 | |||
28 | #ifdef DEBUG | ||
29 | #define DBG(args...) printk(args) | ||
30 | #else | ||
31 | #define DBG(args...) do { } while(0) | ||
32 | #endif | ||
33 | |||
34 | /* | ||
35 | * This option is "weird" :) Basically, if you define this to 1 | ||
36 | * the control loop for the RPMs fans (not PWMs) will apply the | ||
37 | * correction factor obtained from the PID to the actual RPM | ||
38 | * speed read from the FCU. | ||
39 | * | ||
40 | * If you define the below constant to 0, then it will be | ||
41 | * applied to the setpoint RPM speed, that is basically the | ||
42 | * speed we proviously "asked" for. | ||
43 | * | ||
44 | * I'm using 0 for now which is what therm_pm72 used to do and | ||
45 | * what Darwin -apparently- does based on observed behaviour. | ||
46 | */ | ||
47 | #define RPM_PID_USE_ACTUAL_SPEED 0 | ||
48 | |||
49 | /* Default min/max for pumps */ | ||
50 | #define CPU_PUMP_OUTPUT_MAX 3200 | ||
51 | #define CPU_PUMP_OUTPUT_MIN 1250 | ||
52 | |||
53 | #define FCU_FAN_RPM 0 | ||
54 | #define FCU_FAN_PWM 1 | ||
55 | |||
56 | struct wf_fcu_priv { | ||
57 | struct kref ref; | ||
58 | struct i2c_client *i2c; | ||
59 | struct mutex lock; | ||
60 | struct list_head fan_list; | ||
61 | int rpm_shift; | ||
62 | }; | ||
63 | |||
64 | struct wf_fcu_fan { | ||
65 | struct list_head link; | ||
66 | int id; | ||
67 | s32 min, max, target; | ||
68 | struct wf_fcu_priv *fcu_priv; | ||
69 | struct wf_control ctrl; | ||
70 | }; | ||
71 | |||
72 | static void wf_fcu_release(struct kref *ref) | ||
73 | { | ||
74 | struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref); | ||
75 | |||
76 | kfree(pv); | ||
77 | } | ||
78 | |||
79 | static void wf_fcu_fan_release(struct wf_control *ct) | ||
80 | { | ||
81 | struct wf_fcu_fan *fan = ct->priv; | ||
82 | |||
83 | kref_put(&fan->fcu_priv->ref, wf_fcu_release); | ||
84 | kfree(fan); | ||
85 | } | ||
86 | |||
87 | static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg, | ||
88 | unsigned char *buf, int nb) | ||
89 | { | ||
90 | int tries, nr, nw; | ||
91 | |||
92 | mutex_lock(&pv->lock); | ||
93 | |||
94 | buf[0] = reg; | ||
95 | tries = 0; | ||
96 | for (;;) { | ||
97 | nw = i2c_master_send(pv->i2c, buf, 1); | ||
98 | if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) | ||
99 | break; | ||
100 | msleep(10); | ||
101 | ++tries; | ||
102 | } | ||
103 | if (nw <= 0) { | ||
104 | pr_err("Failure writing address to FCU: %d", nw); | ||
105 | nr = nw; | ||
106 | goto bail; | ||
107 | } | ||
108 | tries = 0; | ||
109 | for (;;) { | ||
110 | nr = i2c_master_recv(pv->i2c, buf, nb); | ||
111 | if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100) | ||
112 | break; | ||
113 | msleep(10); | ||
114 | ++tries; | ||
115 | } | ||
116 | if (nr <= 0) | ||
117 | pr_err("wf_fcu: Failure reading data from FCU: %d", nw); | ||
118 | bail: | ||
119 | mutex_unlock(&pv->lock); | ||
120 | return nr; | ||
121 | } | ||
122 | |||
123 | static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg, | ||
124 | const unsigned char *ptr, int nb) | ||
125 | { | ||
126 | int tries, nw; | ||
127 | unsigned char buf[16]; | ||
128 | |||
129 | buf[0] = reg; | ||
130 | memcpy(buf+1, ptr, nb); | ||
131 | ++nb; | ||
132 | tries = 0; | ||
133 | for (;;) { | ||
134 | nw = i2c_master_send(pv->i2c, buf, nb); | ||
135 | if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) | ||
136 | break; | ||
137 | msleep(10); | ||
138 | ++tries; | ||
139 | } | ||
140 | if (nw < 0) | ||
141 | pr_err("wf_fcu: Failure writing to FCU: %d", nw); | ||
142 | return nw; | ||
143 | } | ||
144 | |||
145 | static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value) | ||
146 | { | ||
147 | struct wf_fcu_fan *fan = ct->priv; | ||
148 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
149 | int rc, shift = pv->rpm_shift; | ||
150 | unsigned char buf[2]; | ||
151 | |||
152 | if (value < fan->min) | ||
153 | value = fan->min; | ||
154 | if (value > fan->max) | ||
155 | value = fan->max; | ||
156 | |||
157 | fan->target = value; | ||
158 | |||
159 | buf[0] = value >> (8 - shift); | ||
160 | buf[1] = value << shift; | ||
161 | rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2); | ||
162 | if (rc < 0) | ||
163 | return -EIO; | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value) | ||
168 | { | ||
169 | struct wf_fcu_fan *fan = ct->priv; | ||
170 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
171 | int rc, reg_base, shift = pv->rpm_shift; | ||
172 | unsigned char failure; | ||
173 | unsigned char active; | ||
174 | unsigned char buf[2]; | ||
175 | |||
176 | rc = wf_fcu_read_reg(pv, 0xb, &failure, 1); | ||
177 | if (rc != 1) | ||
178 | return -EIO; | ||
179 | if ((failure & (1 << fan->id)) != 0) | ||
180 | return -EFAULT; | ||
181 | rc = wf_fcu_read_reg(pv, 0xd, &active, 1); | ||
182 | if (rc != 1) | ||
183 | return -EIO; | ||
184 | if ((active & (1 << fan->id)) == 0) | ||
185 | return -ENXIO; | ||
186 | |||
187 | /* Programmed value or real current speed */ | ||
188 | #if RPM_PID_USE_ACTUAL_SPEED | ||
189 | reg_base = 0x11; | ||
190 | #else | ||
191 | reg_base = 0x10; | ||
192 | #endif | ||
193 | rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2); | ||
194 | if (rc != 2) | ||
195 | return -EIO; | ||
196 | |||
197 | *value = (buf[0] << (8 - shift)) | buf[1] >> shift; | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value) | ||
203 | { | ||
204 | struct wf_fcu_fan *fan = ct->priv; | ||
205 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
206 | unsigned char buf[2]; | ||
207 | int rc; | ||
208 | |||
209 | if (value < fan->min) | ||
210 | value = fan->min; | ||
211 | if (value > fan->max) | ||
212 | value = fan->max; | ||
213 | |||
214 | fan->target = value; | ||
215 | |||
216 | value = (value * 2559) / 1000; | ||
217 | buf[0] = value; | ||
218 | rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1); | ||
219 | if (rc < 0) | ||
220 | return -EIO; | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value) | ||
225 | { | ||
226 | struct wf_fcu_fan *fan = ct->priv; | ||
227 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
228 | unsigned char failure; | ||
229 | unsigned char active; | ||
230 | unsigned char buf[2]; | ||
231 | int rc; | ||
232 | |||
233 | rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1); | ||
234 | if (rc != 1) | ||
235 | return -EIO; | ||
236 | if ((failure & (1 << fan->id)) != 0) | ||
237 | return -EFAULT; | ||
238 | rc = wf_fcu_read_reg(pv, 0x2d, &active, 1); | ||
239 | if (rc != 1) | ||
240 | return -EIO; | ||
241 | if ((active & (1 << fan->id)) == 0) | ||
242 | return -ENXIO; | ||
243 | |||
244 | rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1); | ||
245 | if (rc != 1) | ||
246 | return -EIO; | ||
247 | |||
248 | *value = (((s32)buf[0]) * 1000) / 2559; | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static s32 wf_fcu_fan_min(struct wf_control *ct) | ||
254 | { | ||
255 | struct wf_fcu_fan *fan = ct->priv; | ||
256 | |||
257 | return fan->min; | ||
258 | } | ||
259 | |||
260 | static s32 wf_fcu_fan_max(struct wf_control *ct) | ||
261 | { | ||
262 | struct wf_fcu_fan *fan = ct->priv; | ||
263 | |||
264 | return fan->max; | ||
265 | } | ||
266 | |||
267 | static const struct wf_control_ops wf_fcu_fan_rpm_ops = { | ||
268 | .set_value = wf_fcu_fan_set_rpm, | ||
269 | .get_value = wf_fcu_fan_get_rpm, | ||
270 | .get_min = wf_fcu_fan_min, | ||
271 | .get_max = wf_fcu_fan_max, | ||
272 | .release = wf_fcu_fan_release, | ||
273 | .owner = THIS_MODULE, | ||
274 | }; | ||
275 | |||
276 | static const struct wf_control_ops wf_fcu_fan_pwm_ops = { | ||
277 | .set_value = wf_fcu_fan_set_pwm, | ||
278 | .get_value = wf_fcu_fan_get_pwm, | ||
279 | .get_min = wf_fcu_fan_min, | ||
280 | .get_max = wf_fcu_fan_max, | ||
281 | .release = wf_fcu_fan_release, | ||
282 | .owner = THIS_MODULE, | ||
283 | }; | ||
284 | |||
285 | static void __devinit wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan) | ||
286 | { | ||
287 | const struct mpu_data *mpu = wf_get_mpu(0); | ||
288 | u16 pump_min = 0, pump_max = 0xffff; | ||
289 | u16 tmp[4]; | ||
290 | |||
291 | /* Try to fetch pumps min/max infos from eeprom */ | ||
292 | if (mpu) { | ||
293 | memcpy(&tmp, mpu->processor_part_num, 8); | ||
294 | if (tmp[0] != 0xffff && tmp[1] != 0xffff) { | ||
295 | pump_min = max(pump_min, tmp[0]); | ||
296 | pump_max = min(pump_max, tmp[1]); | ||
297 | } | ||
298 | if (tmp[2] != 0xffff && tmp[3] != 0xffff) { | ||
299 | pump_min = max(pump_min, tmp[2]); | ||
300 | pump_max = min(pump_max, tmp[3]); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | /* Double check the values, this _IS_ needed as the EEPROM on | ||
305 | * some dual 2.5Ghz G5s seem, at least, to have both min & max | ||
306 | * same to the same value ... (grrrr) | ||
307 | */ | ||
308 | if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) { | ||
309 | pump_min = CPU_PUMP_OUTPUT_MIN; | ||
310 | pump_max = CPU_PUMP_OUTPUT_MAX; | ||
311 | } | ||
312 | |||
313 | fan->min = pump_min; | ||
314 | fan->max = pump_max; | ||
315 | |||
316 | DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n", | ||
317 | fan->ctrl.name, pump_min, pump_max); | ||
318 | } | ||
319 | |||
320 | static void __devinit wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan) | ||
321 | { | ||
322 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
323 | const struct mpu_data *mpu0 = wf_get_mpu(0); | ||
324 | const struct mpu_data *mpu1 = wf_get_mpu(1); | ||
325 | |||
326 | /* Default */ | ||
327 | fan->min = 2400 >> pv->rpm_shift; | ||
328 | fan->max = 56000 >> pv->rpm_shift; | ||
329 | |||
330 | /* CPU fans have min/max in MPU */ | ||
331 | if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) { | ||
332 | fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); | ||
333 | fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); | ||
334 | goto bail; | ||
335 | } | ||
336 | if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) { | ||
337 | fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan); | ||
338 | fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan); | ||
339 | goto bail; | ||
340 | } | ||
341 | if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) { | ||
342 | fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan); | ||
343 | fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan); | ||
344 | goto bail; | ||
345 | } | ||
346 | if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) { | ||
347 | fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan); | ||
348 | fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan); | ||
349 | goto bail; | ||
350 | } | ||
351 | /* Rackmac variants, we just use mpu0 intake */ | ||
352 | if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) { | ||
353 | fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); | ||
354 | fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); | ||
355 | goto bail; | ||
356 | } | ||
357 | bail: | ||
358 | DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n", | ||
359 | fan->ctrl.name, fan->min, fan->max); | ||
360 | } | ||
361 | |||
362 | static void __devinit wf_fcu_add_fan(struct wf_fcu_priv *pv, | ||
363 | const char *name, | ||
364 | int type, int id) | ||
365 | { | ||
366 | struct wf_fcu_fan *fan; | ||
367 | |||
368 | fan = kzalloc(sizeof(*fan), GFP_KERNEL); | ||
369 | if (!fan) | ||
370 | return; | ||
371 | fan->fcu_priv = pv; | ||
372 | fan->id = id; | ||
373 | fan->ctrl.name = name; | ||
374 | fan->ctrl.priv = fan; | ||
375 | |||
376 | /* min/max is oddball but the code comes from | ||
377 | * therm_pm72 which seems to work so ... | ||
378 | */ | ||
379 | if (type == FCU_FAN_RPM) { | ||
380 | if (!strncmp(name, "cpu-pump", strlen("cpu-pump"))) | ||
381 | wf_fcu_get_pump_minmax(fan); | ||
382 | else | ||
383 | wf_fcu_get_rpmfan_minmax(fan); | ||
384 | fan->ctrl.type = WF_CONTROL_RPM_FAN; | ||
385 | fan->ctrl.ops = &wf_fcu_fan_rpm_ops; | ||
386 | } else { | ||
387 | fan->min = 10; | ||
388 | fan->max = 100; | ||
389 | fan->ctrl.type = WF_CONTROL_PWM_FAN; | ||
390 | fan->ctrl.ops = &wf_fcu_fan_pwm_ops; | ||
391 | } | ||
392 | |||
393 | if (wf_register_control(&fan->ctrl)) { | ||
394 | pr_err("wf_fcu: Failed to register fan %s\n", name); | ||
395 | kfree(fan); | ||
396 | return; | ||
397 | } | ||
398 | list_add(&fan->link, &pv->fan_list); | ||
399 | kref_get(&pv->ref); | ||
400 | } | ||
401 | |||
402 | static void __devinit wf_fcu_lookup_fans(struct wf_fcu_priv *pv) | ||
403 | { | ||
404 | /* Translation of device-tree location properties to | ||
405 | * windfarm fan names | ||
406 | */ | ||
407 | static const struct { | ||
408 | const char *dt_name; /* Device-tree name */ | ||
409 | const char *ct_name; /* Control name */ | ||
410 | } loc_trans[] = { | ||
411 | { "BACKSIDE", "backside-fan", }, | ||
412 | { "SYS CTRLR FAN", "backside-fan", }, | ||
413 | { "DRIVE BAY", "drive-bay-fan", }, | ||
414 | { "SLOT", "slots-fan", }, | ||
415 | { "PCI FAN", "slots-fan", }, | ||
416 | { "CPU A INTAKE", "cpu-front-fan-0", }, | ||
417 | { "CPU A EXHAUST", "cpu-rear-fan-0", }, | ||
418 | { "CPU B INTAKE", "cpu-front-fan-1", }, | ||
419 | { "CPU B EXHAUST", "cpu-rear-fan-1", }, | ||
420 | { "CPU A PUMP", "cpu-pump-0", }, | ||
421 | { "CPU B PUMP", "cpu-pump-1", }, | ||
422 | { "CPU A 1", "cpu-fan-a-0", }, | ||
423 | { "CPU A 2", "cpu-fan-b-0", }, | ||
424 | { "CPU A 3", "cpu-fan-c-0", }, | ||
425 | { "CPU B 1", "cpu-fan-a-1", }, | ||
426 | { "CPU B 2", "cpu-fan-b-1", }, | ||
427 | { "CPU B 3", "cpu-fan-c-1", }, | ||
428 | }; | ||
429 | struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node; | ||
430 | int i; | ||
431 | |||
432 | DBG("Looking up FCU controls in device-tree...\n"); | ||
433 | |||
434 | while ((np = of_get_next_child(fcu, np)) != NULL) { | ||
435 | int id, type = -1; | ||
436 | const char *loc; | ||
437 | const char *name; | ||
438 | const u32 *reg; | ||
439 | |||
440 | DBG(" control: %s, type: %s\n", np->name, np->type); | ||
441 | |||
442 | /* Detect control type */ | ||
443 | if (!strcmp(np->type, "fan-rpm-control") || | ||
444 | !strcmp(np->type, "fan-rpm")) | ||
445 | type = FCU_FAN_RPM; | ||
446 | if (!strcmp(np->type, "fan-pwm-control") || | ||
447 | !strcmp(np->type, "fan-pwm")) | ||
448 | type = FCU_FAN_PWM; | ||
449 | /* Only care about fans for now */ | ||
450 | if (type == -1) | ||
451 | continue; | ||
452 | |||
453 | /* Lookup for a matching location */ | ||
454 | loc = of_get_property(np, "location", NULL); | ||
455 | reg = of_get_property(np, "reg", NULL); | ||
456 | if (loc == NULL || reg == NULL) | ||
457 | continue; | ||
458 | DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); | ||
459 | |||
460 | for (i = 0; i < ARRAY_SIZE(loc_trans); i++) { | ||
461 | if (strncmp(loc, loc_trans[i].dt_name, | ||
462 | strlen(loc_trans[i].dt_name))) | ||
463 | continue; | ||
464 | name = loc_trans[i].ct_name; | ||
465 | |||
466 | DBG(" location match, name: %s\n", name); | ||
467 | |||
468 | if (type == FCU_FAN_RPM) | ||
469 | id = ((*reg) - 0x10) / 2; | ||
470 | else | ||
471 | id = ((*reg) - 0x30) / 2; | ||
472 | if (id > 7) { | ||
473 | pr_warning("wf_fcu: Can't parse " | ||
474 | "fan ID in device-tree for %s\n", | ||
475 | np->full_name); | ||
476 | break; | ||
477 | } | ||
478 | wf_fcu_add_fan(pv, name, type, id); | ||
479 | break; | ||
480 | } | ||
481 | } | ||
482 | } | ||
483 | |||
484 | static void __devinit wf_fcu_default_fans(struct wf_fcu_priv *pv) | ||
485 | { | ||
486 | /* We only support the default fans for PowerMac7,2 */ | ||
487 | if (!of_machine_is_compatible("PowerMac7,2")) | ||
488 | return; | ||
489 | |||
490 | wf_fcu_add_fan(pv, "backside-fan", FCU_FAN_PWM, 1); | ||
491 | wf_fcu_add_fan(pv, "drive-bay-fan", FCU_FAN_RPM, 2); | ||
492 | wf_fcu_add_fan(pv, "slots-fan", FCU_FAN_PWM, 2); | ||
493 | wf_fcu_add_fan(pv, "cpu-front-fan-0", FCU_FAN_RPM, 3); | ||
494 | wf_fcu_add_fan(pv, "cpu-rear-fan-0", FCU_FAN_RPM, 4); | ||
495 | wf_fcu_add_fan(pv, "cpu-front-fan-1", FCU_FAN_RPM, 5); | ||
496 | wf_fcu_add_fan(pv, "cpu-rear-fan-1", FCU_FAN_RPM, 6); | ||
497 | } | ||
498 | |||
499 | static int __devinit wf_fcu_init_chip(struct wf_fcu_priv *pv) | ||
500 | { | ||
501 | unsigned char buf = 0xff; | ||
502 | int rc; | ||
503 | |||
504 | rc = wf_fcu_write_reg(pv, 0xe, &buf, 1); | ||
505 | if (rc < 0) | ||
506 | return -EIO; | ||
507 | rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1); | ||
508 | if (rc < 0) | ||
509 | return -EIO; | ||
510 | rc = wf_fcu_read_reg(pv, 0, &buf, 1); | ||
511 | if (rc < 0) | ||
512 | return -EIO; | ||
513 | pv->rpm_shift = (buf == 1) ? 2 : 3; | ||
514 | |||
515 | pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n", | ||
516 | pv->rpm_shift); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int __devinit wf_fcu_probe(struct i2c_client *client, | ||
522 | const struct i2c_device_id *id) | ||
523 | { | ||
524 | struct wf_fcu_priv *pv; | ||
525 | |||
526 | pv = kzalloc(sizeof(*pv), GFP_KERNEL); | ||
527 | if (!pv) | ||
528 | return -ENOMEM; | ||
529 | |||
530 | kref_init(&pv->ref); | ||
531 | mutex_init(&pv->lock); | ||
532 | INIT_LIST_HEAD(&pv->fan_list); | ||
533 | pv->i2c = client; | ||
534 | |||
535 | /* | ||
536 | * First we must start the FCU which will query the | ||
537 | * shift value to apply to RPMs | ||
538 | */ | ||
539 | if (wf_fcu_init_chip(pv)) { | ||
540 | pr_err("wf_fcu: Initialization failed !\n"); | ||
541 | kfree(pv); | ||
542 | return -ENXIO; | ||
543 | } | ||
544 | |||
545 | /* First lookup fans in the device-tree */ | ||
546 | wf_fcu_lookup_fans(pv); | ||
547 | |||
548 | /* | ||
549 | * Older machines don't have the device-tree entries | ||
550 | * we are looking for, just hard code the list | ||
551 | */ | ||
552 | if (list_empty(&pv->fan_list)) | ||
553 | wf_fcu_default_fans(pv); | ||
554 | |||
555 | /* Still no fans ? FAIL */ | ||
556 | if (list_empty(&pv->fan_list)) { | ||
557 | pr_err("wf_fcu: Failed to find fans for your machine\n"); | ||
558 | kfree(pv); | ||
559 | return -ENODEV; | ||
560 | } | ||
561 | |||
562 | dev_set_drvdata(&client->dev, pv); | ||
563 | |||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static int __devexit wf_fcu_remove(struct i2c_client *client) | ||
568 | { | ||
569 | struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev); | ||
570 | struct wf_fcu_fan *fan; | ||
571 | |||
572 | while (!list_empty(&pv->fan_list)) { | ||
573 | fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link); | ||
574 | list_del(&fan->link); | ||
575 | wf_unregister_control(&fan->ctrl); | ||
576 | } | ||
577 | kref_put(&pv->ref, wf_fcu_release); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | static const struct i2c_device_id wf_fcu_id[] = { | ||
582 | { "MAC,fcu", 0 }, | ||
583 | { } | ||
584 | }; | ||
585 | MODULE_DEVICE_TABLE(i2c, wf_fcu_id); | ||
586 | |||
587 | static struct i2c_driver wf_fcu_driver = { | ||
588 | .driver = { | ||
589 | .name = "wf_fcu", | ||
590 | }, | ||
591 | .probe = wf_fcu_probe, | ||
592 | .remove = wf_fcu_remove, | ||
593 | .id_table = wf_fcu_id, | ||
594 | }; | ||
595 | |||
596 | static int __init wf_fcu_init(void) | ||
597 | { | ||
598 | return i2c_add_driver(&wf_fcu_driver); | ||
599 | } | ||
600 | |||
601 | static void __exit wf_fcu_exit(void) | ||
602 | { | ||
603 | i2c_del_driver(&wf_fcu_driver); | ||
604 | } | ||
605 | |||
606 | |||
607 | module_init(wf_fcu_init); | ||
608 | module_exit(wf_fcu_exit); | ||
609 | |||
610 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
611 | MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control"); | ||
612 | MODULE_LICENSE("GPL"); | ||
613 | |||
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 4d6a90a1372b..b0c2d3695b34 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c | |||
@@ -23,7 +23,7 @@ | |||
23 | 23 | ||
24 | #include "windfarm.h" | 24 | #include "windfarm.h" |
25 | 25 | ||
26 | #define VERSION "0.2" | 26 | #define VERSION "1.0" |
27 | 27 | ||
28 | #undef DEBUG | 28 | #undef DEBUG |
29 | 29 | ||
@@ -36,8 +36,8 @@ | |||
36 | struct wf_lm75_sensor { | 36 | struct wf_lm75_sensor { |
37 | int ds1775 : 1; | 37 | int ds1775 : 1; |
38 | int inited : 1; | 38 | int inited : 1; |
39 | struct i2c_client *i2c; | 39 | struct i2c_client *i2c; |
40 | struct wf_sensor sens; | 40 | struct wf_sensor sens; |
41 | }; | 41 | }; |
42 | #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) | 42 | #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) |
43 | 43 | ||
@@ -90,40 +90,19 @@ static struct wf_sensor_ops wf_lm75_ops = { | |||
90 | 90 | ||
91 | static int wf_lm75_probe(struct i2c_client *client, | 91 | static int wf_lm75_probe(struct i2c_client *client, |
92 | const struct i2c_device_id *id) | 92 | const struct i2c_device_id *id) |
93 | { | 93 | { |
94 | struct wf_lm75_sensor *lm; | 94 | struct wf_lm75_sensor *lm; |
95 | int rc; | 95 | int rc, ds1775 = id->driver_data; |
96 | 96 | const char *name, *loc; | |
97 | lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); | ||
98 | if (lm == NULL) | ||
99 | return -ENODEV; | ||
100 | |||
101 | lm->inited = 0; | ||
102 | lm->ds1775 = id->driver_data; | ||
103 | lm->i2c = client; | ||
104 | lm->sens.name = client->dev.platform_data; | ||
105 | lm->sens.ops = &wf_lm75_ops; | ||
106 | i2c_set_clientdata(client, lm); | ||
107 | |||
108 | rc = wf_register_sensor(&lm->sens); | ||
109 | if (rc) | ||
110 | kfree(lm); | ||
111 | |||
112 | return rc; | ||
113 | } | ||
114 | |||
115 | static struct i2c_driver wf_lm75_driver; | ||
116 | |||
117 | static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, | ||
118 | u8 addr, int ds1775, | ||
119 | const char *loc) | ||
120 | { | ||
121 | struct i2c_board_info info; | ||
122 | struct i2c_client *client; | ||
123 | char *name; | ||
124 | 97 | ||
125 | DBG("wf_lm75: creating %s device at address 0x%02x\n", | 98 | DBG("wf_lm75: creating %s device at address 0x%02x\n", |
126 | ds1775 ? "ds1775" : "lm75", addr); | 99 | ds1775 ? "ds1775" : "lm75", client->addr); |
100 | |||
101 | loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); | ||
102 | if (!loc) { | ||
103 | dev_warn(&client->dev, "Missing hwsensor-location property!\n"); | ||
104 | return -ENXIO; | ||
105 | } | ||
127 | 106 | ||
128 | /* Usual rant about sensor names not beeing very consistent in | 107 | /* Usual rant about sensor names not beeing very consistent in |
129 | * the device-tree, oh well ... | 108 | * the device-tree, oh well ... |
@@ -137,68 +116,31 @@ static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, | |||
137 | name = "optical-drive-temp"; | 116 | name = "optical-drive-temp"; |
138 | else if (!strcmp(loc, "HD Temp")) | 117 | else if (!strcmp(loc, "HD Temp")) |
139 | name = "hard-drive-temp"; | 118 | name = "hard-drive-temp"; |
119 | else if (!strcmp(loc, "PCI SLOTS")) | ||
120 | name = "slots-temp"; | ||
121 | else if (!strcmp(loc, "CPU A INLET")) | ||
122 | name = "cpu-inlet-temp-0"; | ||
123 | else if (!strcmp(loc, "CPU B INLET")) | ||
124 | name = "cpu-inlet-temp-1"; | ||
140 | else | 125 | else |
141 | goto fail; | 126 | return -ENXIO; |
142 | 127 | ||
143 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
144 | info.addr = (addr >> 1) & 0x7f; | ||
145 | info.platform_data = name; | ||
146 | strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE); | ||
147 | |||
148 | client = i2c_new_device(adapter, &info); | ||
149 | if (client == NULL) { | ||
150 | printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n", | ||
151 | ds1775 ? "ds1775" : "lm75", name); | ||
152 | goto fail; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * Let i2c-core delete that device on driver removal. | ||
157 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
158 | */ | ||
159 | list_add_tail(&client->detected, &wf_lm75_driver.clients); | ||
160 | return client; | ||
161 | fail: | ||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | static int wf_lm75_attach(struct i2c_adapter *adapter) | ||
166 | { | ||
167 | struct device_node *busnode, *dev; | ||
168 | struct pmac_i2c_bus *bus; | ||
169 | 128 | ||
170 | DBG("wf_lm75: adapter %s detected\n", adapter->name); | 129 | lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); |
171 | 130 | if (lm == NULL) | |
172 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
173 | if (bus == NULL) | ||
174 | return -ENODEV; | 131 | return -ENODEV; |
175 | busnode = pmac_i2c_get_bus_node(bus); | ||
176 | 132 | ||
177 | DBG("wf_lm75: bus found, looking for device...\n"); | 133 | lm->inited = 0; |
178 | 134 | lm->ds1775 = ds1775; | |
179 | /* Now look for lm75(s) in there */ | 135 | lm->i2c = client; |
180 | for (dev = NULL; | 136 | lm->sens.name = (char *)name; /* XXX fix constness in structure */ |
181 | (dev = of_get_next_child(busnode, dev)) != NULL;) { | 137 | lm->sens.ops = &wf_lm75_ops; |
182 | const char *loc = | 138 | i2c_set_clientdata(client, lm); |
183 | of_get_property(dev, "hwsensor-location", NULL); | ||
184 | u8 addr; | ||
185 | 139 | ||
186 | /* We must re-match the adapter in order to properly check | 140 | rc = wf_register_sensor(&lm->sens); |
187 | * the channel on multibus setups | 141 | if (rc) |
188 | */ | 142 | kfree(lm); |
189 | if (!pmac_i2c_match_adapter(dev, adapter)) | 143 | return rc; |
190 | continue; | ||
191 | addr = pmac_i2c_get_dev_addr(dev); | ||
192 | if (loc == NULL || addr == 0) | ||
193 | continue; | ||
194 | /* real lm75 */ | ||
195 | if (of_device_is_compatible(dev, "lm75")) | ||
196 | wf_lm75_create(adapter, addr, 0, loc); | ||
197 | /* ds1775 (compatible, better resolution */ | ||
198 | else if (of_device_is_compatible(dev, "ds1775")) | ||
199 | wf_lm75_create(adapter, addr, 1, loc); | ||
200 | } | ||
201 | return 0; | ||
202 | } | 144 | } |
203 | 145 | ||
204 | static int wf_lm75_remove(struct i2c_client *client) | 146 | static int wf_lm75_remove(struct i2c_client *client) |
@@ -217,16 +159,16 @@ static int wf_lm75_remove(struct i2c_client *client) | |||
217 | } | 159 | } |
218 | 160 | ||
219 | static const struct i2c_device_id wf_lm75_id[] = { | 161 | static const struct i2c_device_id wf_lm75_id[] = { |
220 | { "wf_lm75", 0 }, | 162 | { "MAC,lm75", 0 }, |
221 | { "wf_ds1775", 1 }, | 163 | { "MAC,ds1775", 1 }, |
222 | { } | 164 | { } |
223 | }; | 165 | }; |
166 | MODULE_DEVICE_TABLE(i2c, wf_lm75_id); | ||
224 | 167 | ||
225 | static struct i2c_driver wf_lm75_driver = { | 168 | static struct i2c_driver wf_lm75_driver = { |
226 | .driver = { | 169 | .driver = { |
227 | .name = "wf_lm75", | 170 | .name = "wf_lm75", |
228 | }, | 171 | }, |
229 | .attach_adapter = wf_lm75_attach, | ||
230 | .probe = wf_lm75_probe, | 172 | .probe = wf_lm75_probe, |
231 | .remove = wf_lm75_remove, | 173 | .remove = wf_lm75_remove, |
232 | .id_table = wf_lm75_id, | 174 | .id_table = wf_lm75_id, |
@@ -234,11 +176,6 @@ static struct i2c_driver wf_lm75_driver = { | |||
234 | 176 | ||
235 | static int __init wf_lm75_sensor_init(void) | 177 | static int __init wf_lm75_sensor_init(void) |
236 | { | 178 | { |
237 | /* Don't register on old machines that use therm_pm72 for now */ | ||
238 | if (of_machine_is_compatible("PowerMac7,2") || | ||
239 | of_machine_is_compatible("PowerMac7,3") || | ||
240 | of_machine_is_compatible("RackMac3,1")) | ||
241 | return -ENODEV; | ||
242 | return i2c_add_driver(&wf_lm75_driver); | 179 | return i2c_add_driver(&wf_lm75_driver); |
243 | } | 180 | } |
244 | 181 | ||
diff --git a/drivers/macintosh/windfarm_lm87_sensor.c b/drivers/macintosh/windfarm_lm87_sensor.c new file mode 100644 index 000000000000..c071aab79dd1 --- /dev/null +++ b/drivers/macintosh/windfarm_lm87_sensor.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. LM87 sensor | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/wait.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <asm/prom.h> | ||
19 | #include <asm/machdep.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/sections.h> | ||
22 | #include <asm/pmac_low_i2c.h> | ||
23 | |||
24 | #include "windfarm.h" | ||
25 | |||
26 | #define VERSION "1.0" | ||
27 | |||
28 | #undef DEBUG | ||
29 | |||
30 | #ifdef DEBUG | ||
31 | #define DBG(args...) printk(args) | ||
32 | #else | ||
33 | #define DBG(args...) do { } while(0) | ||
34 | #endif | ||
35 | |||
36 | struct wf_lm87_sensor { | ||
37 | struct i2c_client *i2c; | ||
38 | struct wf_sensor sens; | ||
39 | }; | ||
40 | #define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens) | ||
41 | |||
42 | |||
43 | static int wf_lm87_read_reg(struct i2c_client *chip, int reg) | ||
44 | { | ||
45 | int rc, tries = 0; | ||
46 | u8 buf; | ||
47 | |||
48 | for (;;) { | ||
49 | /* Set address */ | ||
50 | buf = (u8)reg; | ||
51 | rc = i2c_master_send(chip, &buf, 1); | ||
52 | if (rc <= 0) | ||
53 | goto error; | ||
54 | rc = i2c_master_recv(chip, &buf, 1); | ||
55 | if (rc <= 0) | ||
56 | goto error; | ||
57 | return (int)buf; | ||
58 | error: | ||
59 | DBG("wf_lm87: Error reading LM87, retrying...\n"); | ||
60 | if (++tries > 10) { | ||
61 | printk(KERN_ERR "wf_lm87: Error reading LM87 !\n"); | ||
62 | return -EIO; | ||
63 | } | ||
64 | msleep(10); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static int wf_lm87_get(struct wf_sensor *sr, s32 *value) | ||
69 | { | ||
70 | struct wf_lm87_sensor *lm = sr->priv; | ||
71 | s32 temp; | ||
72 | |||
73 | if (lm->i2c == NULL) | ||
74 | return -ENODEV; | ||
75 | |||
76 | #define LM87_INT_TEMP 0x27 | ||
77 | |||
78 | /* Read temperature register */ | ||
79 | temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP); | ||
80 | if (temp < 0) | ||
81 | return temp; | ||
82 | *value = temp << 16; | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static void wf_lm87_release(struct wf_sensor *sr) | ||
88 | { | ||
89 | struct wf_lm87_sensor *lm = wf_to_lm87(sr); | ||
90 | |||
91 | kfree(lm); | ||
92 | } | ||
93 | |||
94 | static struct wf_sensor_ops wf_lm87_ops = { | ||
95 | .get_value = wf_lm87_get, | ||
96 | .release = wf_lm87_release, | ||
97 | .owner = THIS_MODULE, | ||
98 | }; | ||
99 | |||
100 | static int wf_lm87_probe(struct i2c_client *client, | ||
101 | const struct i2c_device_id *id) | ||
102 | { | ||
103 | struct wf_lm87_sensor *lm; | ||
104 | const char *name = NULL, *loc; | ||
105 | struct device_node *np = NULL; | ||
106 | int rc; | ||
107 | |||
108 | /* | ||
109 | * The lm87 contains a whole pile of sensors, additionally, | ||
110 | * the Xserve G5 has several lm87's. However, for now we only | ||
111 | * care about the internal temperature sensor | ||
112 | */ | ||
113 | while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) { | ||
114 | if (strcmp(np->name, "int-temp")) | ||
115 | continue; | ||
116 | loc = of_get_property(np, "location", NULL); | ||
117 | if (!loc) | ||
118 | continue; | ||
119 | if (strstr(loc, "DIMM")) | ||
120 | name = "dimms-temp"; | ||
121 | else if (strstr(loc, "Processors")) | ||
122 | name = "between-cpus-temp"; | ||
123 | if (name) { | ||
124 | of_node_put(np); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | if (!name) { | ||
129 | pr_warning("wf_lm87: Unsupported sensor %s\n", | ||
130 | client->dev.of_node->full_name); | ||
131 | return -ENODEV; | ||
132 | } | ||
133 | |||
134 | lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL); | ||
135 | if (lm == NULL) | ||
136 | return -ENODEV; | ||
137 | |||
138 | lm->i2c = client; | ||
139 | lm->sens.name = name; | ||
140 | lm->sens.ops = &wf_lm87_ops; | ||
141 | lm->sens.priv = lm; | ||
142 | i2c_set_clientdata(client, lm); | ||
143 | |||
144 | rc = wf_register_sensor(&lm->sens); | ||
145 | if (rc) | ||
146 | kfree(lm); | ||
147 | return rc; | ||
148 | } | ||
149 | |||
150 | static int wf_lm87_remove(struct i2c_client *client) | ||
151 | { | ||
152 | struct wf_lm87_sensor *lm = i2c_get_clientdata(client); | ||
153 | |||
154 | DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name); | ||
155 | |||
156 | /* Mark client detached */ | ||
157 | lm->i2c = NULL; | ||
158 | |||
159 | /* release sensor */ | ||
160 | wf_unregister_sensor(&lm->sens); | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static const struct i2c_device_id wf_lm87_id[] = { | ||
166 | { "MAC,lm87cimt", 0 }, | ||
167 | { } | ||
168 | }; | ||
169 | MODULE_DEVICE_TABLE(i2c, wf_lm87_id); | ||
170 | |||
171 | static struct i2c_driver wf_lm87_driver = { | ||
172 | .driver = { | ||
173 | .name = "wf_lm87", | ||
174 | }, | ||
175 | .probe = wf_lm87_probe, | ||
176 | .remove = wf_lm87_remove, | ||
177 | .id_table = wf_lm87_id, | ||
178 | }; | ||
179 | |||
180 | static int __init wf_lm87_sensor_init(void) | ||
181 | { | ||
182 | /* We only support this on the Xserve */ | ||
183 | if (!of_machine_is_compatible("RackMac3,1")) | ||
184 | return -ENODEV; | ||
185 | |||
186 | return i2c_add_driver(&wf_lm87_driver); | ||
187 | } | ||
188 | |||
189 | static void __exit wf_lm87_sensor_exit(void) | ||
190 | { | ||
191 | i2c_del_driver(&wf_lm87_driver); | ||
192 | } | ||
193 | |||
194 | |||
195 | module_init(wf_lm87_sensor_init); | ||
196 | module_exit(wf_lm87_sensor_exit); | ||
197 | |||
198 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
199 | MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control"); | ||
200 | MODULE_LICENSE("GPL"); | ||
201 | |||
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index 8204113268f4..371b058d2f7d 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c | |||
@@ -16,7 +16,7 @@ | |||
16 | 16 | ||
17 | #include "windfarm.h" | 17 | #include "windfarm.h" |
18 | 18 | ||
19 | #define VERSION "0.2" | 19 | #define VERSION "1.0" |
20 | 20 | ||
21 | /* This currently only exports the external temperature sensor, | 21 | /* This currently only exports the external temperature sensor, |
22 | since that's all the control loops need. */ | 22 | since that's all the control loops need. */ |
@@ -64,9 +64,29 @@ static struct wf_sensor_ops wf_max6690_ops = { | |||
64 | static int wf_max6690_probe(struct i2c_client *client, | 64 | static int wf_max6690_probe(struct i2c_client *client, |
65 | const struct i2c_device_id *id) | 65 | const struct i2c_device_id *id) |
66 | { | 66 | { |
67 | const char *name, *loc; | ||
67 | struct wf_6690_sensor *max; | 68 | struct wf_6690_sensor *max; |
68 | int rc; | 69 | int rc; |
69 | 70 | ||
71 | loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); | ||
72 | if (!loc) { | ||
73 | dev_warn(&client->dev, "Missing hwsensor-location property!\n"); | ||
74 | return -ENXIO; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * We only expose the external temperature register for | ||
79 | * now as this is all we need for our control loops | ||
80 | */ | ||
81 | if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT")) | ||
82 | name = "backside-temp"; | ||
83 | else if (!strcmp(loc, "NB Ambient")) | ||
84 | name = "north-bridge-temp"; | ||
85 | else if (!strcmp(loc, "GPU Ambient")) | ||
86 | name = "gpu-temp"; | ||
87 | else | ||
88 | return -ENXIO; | ||
89 | |||
70 | max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); | 90 | max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); |
71 | if (max == NULL) { | 91 | if (max == NULL) { |
72 | printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " | 92 | printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " |
@@ -75,90 +95,16 @@ static int wf_max6690_probe(struct i2c_client *client, | |||
75 | } | 95 | } |
76 | 96 | ||
77 | max->i2c = client; | 97 | max->i2c = client; |
78 | max->sens.name = client->dev.platform_data; | 98 | max->sens.name = (char *)name; /* XXX fix constness in structure */ |
79 | max->sens.ops = &wf_max6690_ops; | 99 | max->sens.ops = &wf_max6690_ops; |
80 | i2c_set_clientdata(client, max); | 100 | i2c_set_clientdata(client, max); |
81 | 101 | ||
82 | rc = wf_register_sensor(&max->sens); | 102 | rc = wf_register_sensor(&max->sens); |
83 | if (rc) { | 103 | if (rc) |
84 | kfree(max); | 104 | kfree(max); |
85 | } | ||
86 | |||
87 | return rc; | 105 | return rc; |
88 | } | 106 | } |
89 | 107 | ||
90 | static struct i2c_driver wf_max6690_driver; | ||
91 | |||
92 | static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, | ||
93 | u8 addr, const char *loc) | ||
94 | { | ||
95 | struct i2c_board_info info; | ||
96 | struct i2c_client *client; | ||
97 | char *name; | ||
98 | |||
99 | if (!strcmp(loc, "BACKSIDE")) | ||
100 | name = "backside-temp"; | ||
101 | else if (!strcmp(loc, "NB Ambient")) | ||
102 | name = "north-bridge-temp"; | ||
103 | else if (!strcmp(loc, "GPU Ambient")) | ||
104 | name = "gpu-temp"; | ||
105 | else | ||
106 | goto fail; | ||
107 | |||
108 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
109 | info.addr = addr >> 1; | ||
110 | info.platform_data = name; | ||
111 | strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE); | ||
112 | |||
113 | client = i2c_new_device(adapter, &info); | ||
114 | if (client == NULL) { | ||
115 | printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n"); | ||
116 | goto fail; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Let i2c-core delete that device on driver removal. | ||
121 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
122 | */ | ||
123 | list_add_tail(&client->detected, &wf_max6690_driver.clients); | ||
124 | return client; | ||
125 | |||
126 | fail: | ||
127 | return NULL; | ||
128 | } | ||
129 | |||
130 | static int wf_max6690_attach(struct i2c_adapter *adapter) | ||
131 | { | ||
132 | struct device_node *busnode, *dev = NULL; | ||
133 | struct pmac_i2c_bus *bus; | ||
134 | const char *loc; | ||
135 | |||
136 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
137 | if (bus == NULL) | ||
138 | return -ENODEV; | ||
139 | busnode = pmac_i2c_get_bus_node(bus); | ||
140 | |||
141 | while ((dev = of_get_next_child(busnode, dev)) != NULL) { | ||
142 | u8 addr; | ||
143 | |||
144 | /* We must re-match the adapter in order to properly check | ||
145 | * the channel on multibus setups | ||
146 | */ | ||
147 | if (!pmac_i2c_match_adapter(dev, adapter)) | ||
148 | continue; | ||
149 | if (!of_device_is_compatible(dev, "max6690")) | ||
150 | continue; | ||
151 | addr = pmac_i2c_get_dev_addr(dev); | ||
152 | loc = of_get_property(dev, "hwsensor-location", NULL); | ||
153 | if (loc == NULL || addr == 0) | ||
154 | continue; | ||
155 | printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); | ||
156 | wf_max6690_create(adapter, addr, loc); | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int wf_max6690_remove(struct i2c_client *client) | 108 | static int wf_max6690_remove(struct i2c_client *client) |
163 | { | 109 | { |
164 | struct wf_6690_sensor *max = i2c_get_clientdata(client); | 110 | struct wf_6690_sensor *max = i2c_get_clientdata(client); |
@@ -170,15 +116,15 @@ static int wf_max6690_remove(struct i2c_client *client) | |||
170 | } | 116 | } |
171 | 117 | ||
172 | static const struct i2c_device_id wf_max6690_id[] = { | 118 | static const struct i2c_device_id wf_max6690_id[] = { |
173 | { "wf_max6690", 0 }, | 119 | { "MAC,max6690", 0 }, |
174 | { } | 120 | { } |
175 | }; | 121 | }; |
122 | MODULE_DEVICE_TABLE(i2c, wf_max6690_id); | ||
176 | 123 | ||
177 | static struct i2c_driver wf_max6690_driver = { | 124 | static struct i2c_driver wf_max6690_driver = { |
178 | .driver = { | 125 | .driver = { |
179 | .name = "wf_max6690", | 126 | .name = "wf_max6690", |
180 | }, | 127 | }, |
181 | .attach_adapter = wf_max6690_attach, | ||
182 | .probe = wf_max6690_probe, | 128 | .probe = wf_max6690_probe, |
183 | .remove = wf_max6690_remove, | 129 | .remove = wf_max6690_remove, |
184 | .id_table = wf_max6690_id, | 130 | .id_table = wf_max6690_id, |
@@ -186,11 +132,6 @@ static struct i2c_driver wf_max6690_driver = { | |||
186 | 132 | ||
187 | static int __init wf_max6690_sensor_init(void) | 133 | static int __init wf_max6690_sensor_init(void) |
188 | { | 134 | { |
189 | /* Don't register on old machines that use therm_pm72 for now */ | ||
190 | if (of_machine_is_compatible("PowerMac7,2") || | ||
191 | of_machine_is_compatible("PowerMac7,3") || | ||
192 | of_machine_is_compatible("RackMac3,1")) | ||
193 | return -ENODEV; | ||
194 | return i2c_add_driver(&wf_max6690_driver); | 135 | return i2c_add_driver(&wf_max6690_driver); |
195 | } | 136 | } |
196 | 137 | ||
diff --git a/drivers/macintosh/windfarm_mpu.h b/drivers/macintosh/windfarm_mpu.h new file mode 100644 index 000000000000..046edc8c2ec5 --- /dev/null +++ b/drivers/macintosh/windfarm_mpu.h | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | */ | ||
8 | |||
9 | #ifndef __WINDFARM_MPU_H | ||
10 | #define __WINDFARM_MPU_H | ||
11 | |||
12 | typedef unsigned short fu16; | ||
13 | typedef int fs32; | ||
14 | typedef short fs16; | ||
15 | |||
16 | /* Definition of the MPU data structure which contains per CPU | ||
17 | * calibration information (among others) for the G5 machines | ||
18 | */ | ||
19 | struct mpu_data | ||
20 | { | ||
21 | u8 signature; /* 0x00 - EEPROM sig. */ | ||
22 | u8 bytes_used; /* 0x01 - Bytes used in eeprom (160 ?) */ | ||
23 | u8 size; /* 0x02 - EEPROM size (256 ?) */ | ||
24 | u8 version; /* 0x03 - EEPROM version */ | ||
25 | u32 data_revision; /* 0x04 - Dataset revision */ | ||
26 | u8 processor_bin_code[3]; /* 0x08 - Processor BIN code */ | ||
27 | u8 bin_code_expansion; /* 0x0b - ??? (padding ?) */ | ||
28 | u8 processor_num; /* 0x0c - Number of CPUs on this MPU */ | ||
29 | u8 input_mul_bus_div; /* 0x0d - Clock input multiplier/bus divider */ | ||
30 | u8 reserved1[2]; /* 0x0e - */ | ||
31 | u32 input_clk_freq_high; /* 0x10 - Input clock frequency high */ | ||
32 | u8 cpu_nb_target_cycles; /* 0x14 - ??? */ | ||
33 | u8 cpu_statlat; /* 0x15 - ??? */ | ||
34 | u8 cpu_snooplat; /* 0x16 - ??? */ | ||
35 | u8 cpu_snoopacc; /* 0x17 - ??? */ | ||
36 | u8 nb_paamwin; /* 0x18 - ??? */ | ||
37 | u8 nb_statlat; /* 0x19 - ??? */ | ||
38 | u8 nb_snooplat; /* 0x1a - ??? */ | ||
39 | u8 nb_snoopwin; /* 0x1b - ??? */ | ||
40 | u8 api_bus_mode; /* 0x1c - ??? */ | ||
41 | u8 reserved2[3]; /* 0x1d - */ | ||
42 | u32 input_clk_freq_low; /* 0x20 - Input clock frequency low */ | ||
43 | u8 processor_card_slot; /* 0x24 - Processor card slot number */ | ||
44 | u8 reserved3[2]; /* 0x25 - */ | ||
45 | u8 padjmax; /* 0x27 - Max power adjustment (Not in OF!) */ | ||
46 | u8 ttarget; /* 0x28 - Target temperature */ | ||
47 | u8 tmax; /* 0x29 - Max temperature */ | ||
48 | u8 pmaxh; /* 0x2a - Max power */ | ||
49 | u8 tguardband; /* 0x2b - Guardband temp ??? Hist. len in OSX */ | ||
50 | fs32 pid_gp; /* 0x2c - PID proportional gain */ | ||
51 | fs32 pid_gr; /* 0x30 - PID reset gain */ | ||
52 | fs32 pid_gd; /* 0x34 - PID derivative gain */ | ||
53 | fu16 voph; /* 0x38 - Vop High */ | ||
54 | fu16 vopl; /* 0x3a - Vop Low */ | ||
55 | fs16 nactual_die; /* 0x3c - nActual Die */ | ||
56 | fs16 nactual_heatsink; /* 0x3e - nActual Heatsink */ | ||
57 | fs16 nactual_system; /* 0x40 - nActual System */ | ||
58 | u16 calibration_flags; /* 0x42 - Calibration flags */ | ||
59 | fu16 mdiode; /* 0x44 - Diode M value (scaling factor) */ | ||
60 | fs16 bdiode; /* 0x46 - Diode B value (offset) */ | ||
61 | fs32 theta_heat_sink; /* 0x48 - Theta heat sink */ | ||
62 | u16 rminn_intake_fan; /* 0x4c - Intake fan min RPM */ | ||
63 | u16 rmaxn_intake_fan; /* 0x4e - Intake fan max RPM */ | ||
64 | u16 rminn_exhaust_fan; /* 0x50 - Exhaust fan min RPM */ | ||
65 | u16 rmaxn_exhaust_fan; /* 0x52 - Exhaust fan max RPM */ | ||
66 | u8 processor_part_num[8]; /* 0x54 - Processor part number XX pumps min/max */ | ||
67 | u32 processor_lot_num; /* 0x5c - Processor lot number */ | ||
68 | u8 orig_card_sernum[0x10]; /* 0x60 - Card original serial number */ | ||
69 | u8 curr_card_sernum[0x10]; /* 0x70 - Card current serial number */ | ||
70 | u8 mlb_sernum[0x18]; /* 0x80 - MLB serial number */ | ||
71 | u32 checksum1; /* 0x98 - */ | ||
72 | u32 checksum2; /* 0x9c - */ | ||
73 | }; /* Total size = 0xa0 */ | ||
74 | |||
75 | static inline const struct mpu_data *wf_get_mpu(int cpu) | ||
76 | { | ||
77 | struct device_node *np; | ||
78 | char nodename[64]; | ||
79 | const void *data; | ||
80 | int len; | ||
81 | |||
82 | /* | ||
83 | * prom.c routine for finding a node by path is a bit brain dead | ||
84 | * and requires exact @xxx unit numbers. This is a bit ugly but | ||
85 | * will work for these machines | ||
86 | */ | ||
87 | sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0); | ||
88 | np = of_find_node_by_path(nodename); | ||
89 | if (!np) | ||
90 | return NULL; | ||
91 | data = of_get_property(np, "cpuid", &len); | ||
92 | of_node_put(np); | ||
93 | if (!data) | ||
94 | return NULL; | ||
95 | |||
96 | /* | ||
97 | * We are naughty, we have dropped the reference to the device | ||
98 | * node and still return a pointer to the content. We know we | ||
99 | * can do that though as this is only ever called on PowerMac | ||
100 | * which cannot remove those nodes | ||
101 | */ | ||
102 | return data; | ||
103 | } | ||
104 | |||
105 | #endif /* __WINDFARM_MPU_H */ | ||
diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c new file mode 100644 index 000000000000..84ac913d7e3a --- /dev/null +++ b/drivers/macintosh/windfarm_pm72.c | |||
@@ -0,0 +1,847 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. | ||
3 | * Control loops for PowerMac7,2 and 7,3 | ||
4 | * | ||
5 | * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. | ||
6 | * | ||
7 | * Use and redistribute under the terms of the GNU GPL v2. | ||
8 | */ | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/reboot.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include <asm/smu.h> | ||
17 | |||
18 | #include "windfarm.h" | ||
19 | #include "windfarm_pid.h" | ||
20 | #include "windfarm_mpu.h" | ||
21 | |||
22 | #define VERSION "1.0" | ||
23 | |||
24 | #undef DEBUG | ||
25 | #undef LOTSA_DEBUG | ||
26 | |||
27 | #ifdef DEBUG | ||
28 | #define DBG(args...) printk(args) | ||
29 | #else | ||
30 | #define DBG(args...) do { } while(0) | ||
31 | #endif | ||
32 | |||
33 | #ifdef LOTSA_DEBUG | ||
34 | #define DBG_LOTS(args...) printk(args) | ||
35 | #else | ||
36 | #define DBG_LOTS(args...) do { } while(0) | ||
37 | #endif | ||
38 | |||
39 | /* define this to force CPU overtemp to 60 degree, useful for testing | ||
40 | * the overtemp code | ||
41 | */ | ||
42 | #undef HACKED_OVERTEMP | ||
43 | |||
44 | /* We currently only handle 2 chips */ | ||
45 | #define NR_CHIPS 2 | ||
46 | #define NR_CPU_FANS 3 * NR_CHIPS | ||
47 | |||
48 | /* Controls and sensors */ | ||
49 | static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; | ||
50 | static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; | ||
51 | static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; | ||
52 | static struct wf_sensor *backside_temp; | ||
53 | static struct wf_sensor *drives_temp; | ||
54 | |||
55 | static struct wf_control *cpu_front_fans[NR_CHIPS]; | ||
56 | static struct wf_control *cpu_rear_fans[NR_CHIPS]; | ||
57 | static struct wf_control *cpu_pumps[NR_CHIPS]; | ||
58 | static struct wf_control *backside_fan; | ||
59 | static struct wf_control *drives_fan; | ||
60 | static struct wf_control *slots_fan; | ||
61 | static struct wf_control *cpufreq_clamp; | ||
62 | |||
63 | /* We keep a temperature history for average calculation of 180s */ | ||
64 | #define CPU_TEMP_HIST_SIZE 180 | ||
65 | |||
66 | /* Fixed speed for slot fan */ | ||
67 | #define SLOTS_FAN_DEFAULT_PWM 40 | ||
68 | |||
69 | /* Scale value for CPU intake fans */ | ||
70 | #define CPU_INTAKE_SCALE 0x0000f852 | ||
71 | |||
72 | /* PID loop state */ | ||
73 | static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; | ||
74 | static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; | ||
75 | static bool cpu_pid_combined; | ||
76 | static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; | ||
77 | static int cpu_thist_pt; | ||
78 | static s64 cpu_thist_total; | ||
79 | static s32 cpu_all_tmax = 100 << 16; | ||
80 | static struct wf_pid_state backside_pid; | ||
81 | static int backside_tick; | ||
82 | static struct wf_pid_state drives_pid; | ||
83 | static int drives_tick; | ||
84 | |||
85 | static int nr_chips; | ||
86 | static bool have_all_controls; | ||
87 | static bool have_all_sensors; | ||
88 | static bool started; | ||
89 | |||
90 | static int failure_state; | ||
91 | #define FAILURE_SENSOR 1 | ||
92 | #define FAILURE_FAN 2 | ||
93 | #define FAILURE_PERM 4 | ||
94 | #define FAILURE_LOW_OVERTEMP 8 | ||
95 | #define FAILURE_HIGH_OVERTEMP 16 | ||
96 | |||
97 | /* Overtemp values */ | ||
98 | #define LOW_OVER_AVERAGE 0 | ||
99 | #define LOW_OVER_IMMEDIATE (10 << 16) | ||
100 | #define LOW_OVER_CLEAR ((-10) << 16) | ||
101 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
102 | #define HIGH_OVER_AVERAGE (10 << 16) | ||
103 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
104 | |||
105 | |||
106 | static void cpu_max_all_fans(void) | ||
107 | { | ||
108 | int i; | ||
109 | |||
110 | /* We max all CPU fans in case of a sensor error. We also do the | ||
111 | * cpufreq clamping now, even if it's supposedly done later by the | ||
112 | * generic code anyway, we do it earlier here to react faster | ||
113 | */ | ||
114 | if (cpufreq_clamp) | ||
115 | wf_control_set_max(cpufreq_clamp); | ||
116 | for (i = 0; i < nr_chips; i++) { | ||
117 | if (cpu_front_fans[i]) | ||
118 | wf_control_set_max(cpu_front_fans[i]); | ||
119 | if (cpu_rear_fans[i]) | ||
120 | wf_control_set_max(cpu_rear_fans[i]); | ||
121 | if (cpu_pumps[i]) | ||
122 | wf_control_set_max(cpu_pumps[i]); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static int cpu_check_overtemp(s32 temp) | ||
127 | { | ||
128 | int new_state = 0; | ||
129 | s32 t_avg, t_old; | ||
130 | static bool first = true; | ||
131 | |||
132 | /* First check for immediate overtemps */ | ||
133 | if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { | ||
134 | new_state |= FAILURE_LOW_OVERTEMP; | ||
135 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
136 | printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" | ||
137 | " temperature !\n"); | ||
138 | } | ||
139 | if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { | ||
140 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
141 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
142 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
143 | " immediate CPU temperature !\n"); | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * The first time around, initialize the array with the first | ||
148 | * temperature reading | ||
149 | */ | ||
150 | if (first) { | ||
151 | int i; | ||
152 | |||
153 | cpu_thist_total = 0; | ||
154 | for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { | ||
155 | cpu_thist[i] = temp; | ||
156 | cpu_thist_total += temp; | ||
157 | } | ||
158 | first = false; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * We calculate a history of max temperatures and use that for the | ||
163 | * overtemp management | ||
164 | */ | ||
165 | t_old = cpu_thist[cpu_thist_pt]; | ||
166 | cpu_thist[cpu_thist_pt] = temp; | ||
167 | cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; | ||
168 | cpu_thist_total -= t_old; | ||
169 | cpu_thist_total += temp; | ||
170 | t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; | ||
171 | |||
172 | DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", | ||
173 | FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); | ||
174 | |||
175 | /* Now check for average overtemps */ | ||
176 | if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { | ||
177 | new_state |= FAILURE_LOW_OVERTEMP; | ||
178 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
179 | printk(KERN_ERR "windfarm: Overtemp due to average CPU" | ||
180 | " temperature !\n"); | ||
181 | } | ||
182 | if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { | ||
183 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
184 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
185 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
186 | " average CPU temperature !\n"); | ||
187 | } | ||
188 | |||
189 | /* Now handle overtemp conditions. We don't currently use the windfarm | ||
190 | * overtemp handling core as it's not fully suited to the needs of those | ||
191 | * new machine. This will be fixed later. | ||
192 | */ | ||
193 | if (new_state) { | ||
194 | /* High overtemp -> immediate shutdown */ | ||
195 | if (new_state & FAILURE_HIGH_OVERTEMP) | ||
196 | machine_power_off(); | ||
197 | if ((failure_state & new_state) != new_state) | ||
198 | cpu_max_all_fans(); | ||
199 | failure_state |= new_state; | ||
200 | } else if ((failure_state & FAILURE_LOW_OVERTEMP) && | ||
201 | (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { | ||
202 | printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); | ||
203 | failure_state &= ~FAILURE_LOW_OVERTEMP; | ||
204 | } | ||
205 | |||
206 | return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); | ||
207 | } | ||
208 | |||
209 | static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) | ||
210 | { | ||
211 | s32 dtemp, volts, amps; | ||
212 | int rc; | ||
213 | |||
214 | /* Get diode temperature */ | ||
215 | rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); | ||
216 | if (rc) { | ||
217 | DBG(" CPU%d: temp reading error !\n", cpu); | ||
218 | return -EIO; | ||
219 | } | ||
220 | DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); | ||
221 | *temp = dtemp; | ||
222 | |||
223 | /* Get voltage */ | ||
224 | rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); | ||
225 | if (rc) { | ||
226 | DBG(" CPU%d, volts reading error !\n", cpu); | ||
227 | return -EIO; | ||
228 | } | ||
229 | DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); | ||
230 | |||
231 | /* Get current */ | ||
232 | rc = wf_sensor_get(sens_cpu_amps[cpu], &s); | ||
233 | if (rc) { | ||
234 | DBG(" CPU%d, current reading error !\n", cpu); | ||
235 | return -EIO; | ||
236 | } | ||
237 | DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); | ||
238 | |||
239 | /* Calculate power */ | ||
240 | |||
241 | /* Scale voltage and current raw sensor values according to fixed scales | ||
242 | * obtained in Darwin and calculate power from I and V | ||
243 | */ | ||
244 | *power = (((u64)volts) * ((u64)amps)) >> 16; | ||
245 | |||
246 | DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); | ||
247 | |||
248 | return 0; | ||
249 | |||
250 | } | ||
251 | |||
252 | static void cpu_fans_tick_split(void) | ||
253 | { | ||
254 | int err, cpu; | ||
255 | s32 intake, temp, power, t_max = 0; | ||
256 | |||
257 | DBG_LOTS("* cpu fans_tick_split()\n"); | ||
258 | |||
259 | for (cpu = 0; cpu < nr_chips; ++cpu) { | ||
260 | struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; | ||
261 | |||
262 | /* Read current speed */ | ||
263 | wf_control_get(cpu_rear_fans[cpu], &sp->target); | ||
264 | |||
265 | DBG_LOTS(" CPU%d: cur_target = %d RPM\n", cpu, sp->target); | ||
266 | |||
267 | err = read_one_cpu_vals(cpu, &temp, &power); | ||
268 | if (err) { | ||
269 | failure_state |= FAILURE_SENSOR; | ||
270 | cpu_max_all_fans(); | ||
271 | return; | ||
272 | } | ||
273 | |||
274 | /* Keep track of highest temp */ | ||
275 | t_max = max(t_max, temp); | ||
276 | |||
277 | /* Handle possible overtemps */ | ||
278 | if (cpu_check_overtemp(t_max)) | ||
279 | return; | ||
280 | |||
281 | /* Run PID */ | ||
282 | wf_cpu_pid_run(sp, power, temp); | ||
283 | |||
284 | DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); | ||
285 | |||
286 | /* Apply result directly to exhaust fan */ | ||
287 | err = wf_control_set(cpu_rear_fans[cpu], sp->target); | ||
288 | if (err) { | ||
289 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
290 | cpu_rear_fans[cpu]->name, err); | ||
291 | failure_state |= FAILURE_FAN; | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | /* Scale result for intake fan */ | ||
296 | intake = (sp->target * CPU_INTAKE_SCALE) >> 16; | ||
297 | DBG_LOTS(" CPU%d: intake = %d RPM\n", cpu, intake); | ||
298 | err = wf_control_set(cpu_front_fans[cpu], intake); | ||
299 | if (err) { | ||
300 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
301 | cpu_front_fans[cpu]->name, err); | ||
302 | failure_state |= FAILURE_FAN; | ||
303 | break; | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void cpu_fans_tick_combined(void) | ||
309 | { | ||
310 | s32 temp0, power0, temp1, power1, t_max = 0; | ||
311 | s32 temp, power, intake, pump; | ||
312 | struct wf_control *pump0, *pump1; | ||
313 | struct wf_cpu_pid_state *sp = &cpu_pid[0]; | ||
314 | int err, cpu; | ||
315 | |||
316 | DBG_LOTS("* cpu fans_tick_combined()\n"); | ||
317 | |||
318 | /* Read current speed from cpu 0 */ | ||
319 | wf_control_get(cpu_rear_fans[0], &sp->target); | ||
320 | |||
321 | DBG_LOTS(" CPUs: cur_target = %d RPM\n", sp->target); | ||
322 | |||
323 | /* Read values for both CPUs */ | ||
324 | err = read_one_cpu_vals(0, &temp0, &power0); | ||
325 | if (err) { | ||
326 | failure_state |= FAILURE_SENSOR; | ||
327 | cpu_max_all_fans(); | ||
328 | return; | ||
329 | } | ||
330 | err = read_one_cpu_vals(1, &temp1, &power1); | ||
331 | if (err) { | ||
332 | failure_state |= FAILURE_SENSOR; | ||
333 | cpu_max_all_fans(); | ||
334 | return; | ||
335 | } | ||
336 | |||
337 | /* Keep track of highest temp */ | ||
338 | t_max = max(t_max, max(temp0, temp1)); | ||
339 | |||
340 | /* Handle possible overtemps */ | ||
341 | if (cpu_check_overtemp(t_max)) | ||
342 | return; | ||
343 | |||
344 | /* Use the max temp & power of both */ | ||
345 | temp = max(temp0, temp1); | ||
346 | power = max(power0, power1); | ||
347 | |||
348 | /* Run PID */ | ||
349 | wf_cpu_pid_run(sp, power, temp); | ||
350 | |||
351 | /* Scale result for intake fan */ | ||
352 | intake = (sp->target * CPU_INTAKE_SCALE) >> 16; | ||
353 | |||
354 | /* Same deal with pump speed */ | ||
355 | pump0 = cpu_pumps[0]; | ||
356 | pump1 = cpu_pumps[1]; | ||
357 | if (!pump0) { | ||
358 | pump0 = pump1; | ||
359 | pump1 = NULL; | ||
360 | } | ||
361 | pump = (sp->target * wf_control_get_max(pump0)) / | ||
362 | cpu_mpu_data[0]->rmaxn_exhaust_fan; | ||
363 | |||
364 | DBG_LOTS(" CPUs: target = %d RPM\n", sp->target); | ||
365 | DBG_LOTS(" CPUs: intake = %d RPM\n", intake); | ||
366 | DBG_LOTS(" CPUs: pump = %d RPM\n", pump); | ||
367 | |||
368 | for (cpu = 0; cpu < nr_chips; cpu++) { | ||
369 | err = wf_control_set(cpu_rear_fans[cpu], sp->target); | ||
370 | if (err) { | ||
371 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
372 | cpu_rear_fans[cpu]->name, err); | ||
373 | failure_state |= FAILURE_FAN; | ||
374 | } | ||
375 | err = wf_control_set(cpu_front_fans[cpu], intake); | ||
376 | if (err) { | ||
377 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
378 | cpu_front_fans[cpu]->name, err); | ||
379 | failure_state |= FAILURE_FAN; | ||
380 | } | ||
381 | err = 0; | ||
382 | if (cpu_pumps[cpu]) | ||
383 | err = wf_control_set(cpu_pumps[cpu], pump); | ||
384 | if (err) { | ||
385 | pr_warning("wf_pm72: Pump %s reports error %d\n", | ||
386 | cpu_pumps[cpu]->name, err); | ||
387 | failure_state |= FAILURE_FAN; | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | /* Implementation... */ | ||
393 | static int cpu_setup_pid(int cpu) | ||
394 | { | ||
395 | struct wf_cpu_pid_param pid; | ||
396 | const struct mpu_data *mpu = cpu_mpu_data[cpu]; | ||
397 | s32 tmax, ttarget, ptarget; | ||
398 | int fmin, fmax, hsize; | ||
399 | |||
400 | /* Get PID params from the appropriate MPU EEPROM */ | ||
401 | tmax = mpu->tmax << 16; | ||
402 | ttarget = mpu->ttarget << 16; | ||
403 | ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; | ||
404 | |||
405 | DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", | ||
406 | cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); | ||
407 | |||
408 | /* We keep a global tmax for overtemp calculations */ | ||
409 | if (tmax < cpu_all_tmax) | ||
410 | cpu_all_tmax = tmax; | ||
411 | |||
412 | /* Set PID min/max by using the rear fan min/max */ | ||
413 | fmin = wf_control_get_min(cpu_rear_fans[cpu]); | ||
414 | fmax = wf_control_get_max(cpu_rear_fans[cpu]); | ||
415 | DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); | ||
416 | |||
417 | /* History size */ | ||
418 | hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); | ||
419 | DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); | ||
420 | |||
421 | /* Initialize PID loop */ | ||
422 | pid.interval = 1; /* seconds */ | ||
423 | pid.history_len = hsize; | ||
424 | pid.gd = mpu->pid_gd; | ||
425 | pid.gp = mpu->pid_gp; | ||
426 | pid.gr = mpu->pid_gr; | ||
427 | pid.tmax = tmax; | ||
428 | pid.ttarget = ttarget; | ||
429 | pid.pmaxadj = ptarget; | ||
430 | pid.min = fmin; | ||
431 | pid.max = fmax; | ||
432 | |||
433 | wf_cpu_pid_init(&cpu_pid[cpu], &pid); | ||
434 | cpu_pid[cpu].target = 1000; | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | /* Backside/U3 fan */ | ||
440 | static struct wf_pid_param backside_u3_param = { | ||
441 | .interval = 5, | ||
442 | .history_len = 2, | ||
443 | .gd = 40 << 20, | ||
444 | .gp = 5 << 20, | ||
445 | .gr = 0, | ||
446 | .itarget = 65 << 16, | ||
447 | .additive = 1, | ||
448 | .min = 20, | ||
449 | .max = 100, | ||
450 | }; | ||
451 | |||
452 | static struct wf_pid_param backside_u3h_param = { | ||
453 | .interval = 5, | ||
454 | .history_len = 2, | ||
455 | .gd = 20 << 20, | ||
456 | .gp = 5 << 20, | ||
457 | .gr = 0, | ||
458 | .itarget = 75 << 16, | ||
459 | .additive = 1, | ||
460 | .min = 20, | ||
461 | .max = 100, | ||
462 | }; | ||
463 | |||
464 | static void backside_fan_tick(void) | ||
465 | { | ||
466 | s32 temp; | ||
467 | int speed; | ||
468 | int err; | ||
469 | |||
470 | if (!backside_fan || !backside_temp || !backside_tick) | ||
471 | return; | ||
472 | if (--backside_tick > 0) | ||
473 | return; | ||
474 | backside_tick = backside_pid.param.interval; | ||
475 | |||
476 | DBG_LOTS("* backside fans tick\n"); | ||
477 | |||
478 | /* Update fan speed from actual fans */ | ||
479 | err = wf_control_get(backside_fan, &speed); | ||
480 | if (!err) | ||
481 | backside_pid.target = speed; | ||
482 | |||
483 | err = wf_sensor_get(backside_temp, &temp); | ||
484 | if (err) { | ||
485 | printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n", | ||
486 | err); | ||
487 | failure_state |= FAILURE_SENSOR; | ||
488 | wf_control_set_max(backside_fan); | ||
489 | return; | ||
490 | } | ||
491 | speed = wf_pid_run(&backside_pid, temp); | ||
492 | |||
493 | DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", | ||
494 | FIX32TOPRINT(temp), speed); | ||
495 | |||
496 | err = wf_control_set(backside_fan, speed); | ||
497 | if (err) { | ||
498 | printk(KERN_WARNING "windfarm: backside fan error %d\n", err); | ||
499 | failure_state |= FAILURE_FAN; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | static void backside_setup_pid(void) | ||
504 | { | ||
505 | /* first time initialize things */ | ||
506 | s32 fmin = wf_control_get_min(backside_fan); | ||
507 | s32 fmax = wf_control_get_max(backside_fan); | ||
508 | struct wf_pid_param param; | ||
509 | struct device_node *u3; | ||
510 | int u3h = 1; /* conservative by default */ | ||
511 | |||
512 | u3 = of_find_node_by_path("/u3@0,f8000000"); | ||
513 | if (u3 != NULL) { | ||
514 | const u32 *vers = of_get_property(u3, "device-rev", NULL); | ||
515 | if (vers) | ||
516 | if (((*vers) & 0x3f) < 0x34) | ||
517 | u3h = 0; | ||
518 | of_node_put(u3); | ||
519 | } | ||
520 | |||
521 | param = u3h ? backside_u3h_param : backside_u3_param; | ||
522 | |||
523 | param.min = max(param.min, fmin); | ||
524 | param.max = min(param.max, fmax); | ||
525 | wf_pid_init(&backside_pid, ¶m); | ||
526 | backside_tick = 1; | ||
527 | |||
528 | pr_info("wf_pm72: Backside control loop started.\n"); | ||
529 | } | ||
530 | |||
531 | /* Drive bay fan */ | ||
532 | static const struct wf_pid_param drives_param = { | ||
533 | .interval = 5, | ||
534 | .history_len = 2, | ||
535 | .gd = 30 << 20, | ||
536 | .gp = 5 << 20, | ||
537 | .gr = 0, | ||
538 | .itarget = 40 << 16, | ||
539 | .additive = 1, | ||
540 | .min = 300, | ||
541 | .max = 4000, | ||
542 | }; | ||
543 | |||
544 | static void drives_fan_tick(void) | ||
545 | { | ||
546 | s32 temp; | ||
547 | int speed; | ||
548 | int err; | ||
549 | |||
550 | if (!drives_fan || !drives_temp || !drives_tick) | ||
551 | return; | ||
552 | if (--drives_tick > 0) | ||
553 | return; | ||
554 | drives_tick = drives_pid.param.interval; | ||
555 | |||
556 | DBG_LOTS("* drives fans tick\n"); | ||
557 | |||
558 | /* Update fan speed from actual fans */ | ||
559 | err = wf_control_get(drives_fan, &speed); | ||
560 | if (!err) | ||
561 | drives_pid.target = speed; | ||
562 | |||
563 | err = wf_sensor_get(drives_temp, &temp); | ||
564 | if (err) { | ||
565 | pr_warning("wf_pm72: drive bay temp sensor error %d\n", err); | ||
566 | failure_state |= FAILURE_SENSOR; | ||
567 | wf_control_set_max(drives_fan); | ||
568 | return; | ||
569 | } | ||
570 | speed = wf_pid_run(&drives_pid, temp); | ||
571 | |||
572 | DBG_LOTS("drives PID temp=%d.%.3d speed=%d\n", | ||
573 | FIX32TOPRINT(temp), speed); | ||
574 | |||
575 | err = wf_control_set(drives_fan, speed); | ||
576 | if (err) { | ||
577 | printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err); | ||
578 | failure_state |= FAILURE_FAN; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | static void drives_setup_pid(void) | ||
583 | { | ||
584 | /* first time initialize things */ | ||
585 | s32 fmin = wf_control_get_min(drives_fan); | ||
586 | s32 fmax = wf_control_get_max(drives_fan); | ||
587 | struct wf_pid_param param = drives_param; | ||
588 | |||
589 | param.min = max(param.min, fmin); | ||
590 | param.max = min(param.max, fmax); | ||
591 | wf_pid_init(&drives_pid, ¶m); | ||
592 | drives_tick = 1; | ||
593 | |||
594 | pr_info("wf_pm72: Drive bay control loop started.\n"); | ||
595 | } | ||
596 | |||
597 | static void set_fail_state(void) | ||
598 | { | ||
599 | cpu_max_all_fans(); | ||
600 | |||
601 | if (backside_fan) | ||
602 | wf_control_set_max(backside_fan); | ||
603 | if (slots_fan) | ||
604 | wf_control_set_max(slots_fan); | ||
605 | if (drives_fan) | ||
606 | wf_control_set_max(drives_fan); | ||
607 | } | ||
608 | |||
609 | static void pm72_tick(void) | ||
610 | { | ||
611 | int i, last_failure; | ||
612 | |||
613 | if (!started) { | ||
614 | started = 1; | ||
615 | printk(KERN_INFO "windfarm: CPUs control loops started.\n"); | ||
616 | for (i = 0; i < nr_chips; ++i) { | ||
617 | if (cpu_setup_pid(i) < 0) { | ||
618 | failure_state = FAILURE_PERM; | ||
619 | set_fail_state(); | ||
620 | break; | ||
621 | } | ||
622 | } | ||
623 | DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); | ||
624 | |||
625 | backside_setup_pid(); | ||
626 | drives_setup_pid(); | ||
627 | |||
628 | /* | ||
629 | * We don't have the right stuff to drive the PCI fan | ||
630 | * so we fix it to a default value | ||
631 | */ | ||
632 | wf_control_set(slots_fan, SLOTS_FAN_DEFAULT_PWM); | ||
633 | |||
634 | #ifdef HACKED_OVERTEMP | ||
635 | cpu_all_tmax = 60 << 16; | ||
636 | #endif | ||
637 | } | ||
638 | |||
639 | /* Permanent failure, bail out */ | ||
640 | if (failure_state & FAILURE_PERM) | ||
641 | return; | ||
642 | |||
643 | /* | ||
644 | * Clear all failure bits except low overtemp which will be eventually | ||
645 | * cleared by the control loop itself | ||
646 | */ | ||
647 | last_failure = failure_state; | ||
648 | failure_state &= FAILURE_LOW_OVERTEMP; | ||
649 | if (cpu_pid_combined) | ||
650 | cpu_fans_tick_combined(); | ||
651 | else | ||
652 | cpu_fans_tick_split(); | ||
653 | backside_fan_tick(); | ||
654 | drives_fan_tick(); | ||
655 | |||
656 | DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", | ||
657 | last_failure, failure_state); | ||
658 | |||
659 | /* Check for failures. Any failure causes cpufreq clamping */ | ||
660 | if (failure_state && last_failure == 0 && cpufreq_clamp) | ||
661 | wf_control_set_max(cpufreq_clamp); | ||
662 | if (failure_state == 0 && last_failure && cpufreq_clamp) | ||
663 | wf_control_set_min(cpufreq_clamp); | ||
664 | |||
665 | /* That's it for now, we might want to deal with other failures | ||
666 | * differently in the future though | ||
667 | */ | ||
668 | } | ||
669 | |||
670 | static void pm72_new_control(struct wf_control *ct) | ||
671 | { | ||
672 | bool all_controls; | ||
673 | bool had_pump = cpu_pumps[0] || cpu_pumps[1]; | ||
674 | |||
675 | if (!strcmp(ct->name, "cpu-front-fan-0")) | ||
676 | cpu_front_fans[0] = ct; | ||
677 | else if (!strcmp(ct->name, "cpu-front-fan-1")) | ||
678 | cpu_front_fans[1] = ct; | ||
679 | else if (!strcmp(ct->name, "cpu-rear-fan-0")) | ||
680 | cpu_rear_fans[0] = ct; | ||
681 | else if (!strcmp(ct->name, "cpu-rear-fan-1")) | ||
682 | cpu_rear_fans[1] = ct; | ||
683 | else if (!strcmp(ct->name, "cpu-pump-0")) | ||
684 | cpu_pumps[0] = ct; | ||
685 | else if (!strcmp(ct->name, "cpu-pump-1")) | ||
686 | cpu_pumps[1] = ct; | ||
687 | else if (!strcmp(ct->name, "backside-fan")) | ||
688 | backside_fan = ct; | ||
689 | else if (!strcmp(ct->name, "slots-fan")) | ||
690 | slots_fan = ct; | ||
691 | else if (!strcmp(ct->name, "drive-bay-fan")) | ||
692 | drives_fan = ct; | ||
693 | else if (!strcmp(ct->name, "cpufreq-clamp")) | ||
694 | cpufreq_clamp = ct; | ||
695 | |||
696 | all_controls = | ||
697 | cpu_front_fans[0] && | ||
698 | cpu_rear_fans[0] && | ||
699 | backside_fan && | ||
700 | slots_fan && | ||
701 | drives_fan; | ||
702 | if (nr_chips > 1) | ||
703 | all_controls &= | ||
704 | cpu_front_fans[1] && | ||
705 | cpu_rear_fans[1]; | ||
706 | have_all_controls = all_controls; | ||
707 | |||
708 | if ((cpu_pumps[0] || cpu_pumps[1]) && !had_pump) { | ||
709 | pr_info("wf_pm72: Liquid cooling pump(s) detected," | ||
710 | " using new algorithm !\n"); | ||
711 | cpu_pid_combined = true; | ||
712 | } | ||
713 | } | ||
714 | |||
715 | |||
716 | static void pm72_new_sensor(struct wf_sensor *sr) | ||
717 | { | ||
718 | bool all_sensors; | ||
719 | |||
720 | if (!strcmp(sr->name, "cpu-diode-temp-0")) | ||
721 | sens_cpu_temp[0] = sr; | ||
722 | else if (!strcmp(sr->name, "cpu-diode-temp-1")) | ||
723 | sens_cpu_temp[1] = sr; | ||
724 | else if (!strcmp(sr->name, "cpu-voltage-0")) | ||
725 | sens_cpu_volts[0] = sr; | ||
726 | else if (!strcmp(sr->name, "cpu-voltage-1")) | ||
727 | sens_cpu_volts[1] = sr; | ||
728 | else if (!strcmp(sr->name, "cpu-current-0")) | ||
729 | sens_cpu_amps[0] = sr; | ||
730 | else if (!strcmp(sr->name, "cpu-current-1")) | ||
731 | sens_cpu_amps[1] = sr; | ||
732 | else if (!strcmp(sr->name, "backside-temp")) | ||
733 | backside_temp = sr; | ||
734 | else if (!strcmp(sr->name, "hd-temp")) | ||
735 | drives_temp = sr; | ||
736 | |||
737 | all_sensors = | ||
738 | sens_cpu_temp[0] && | ||
739 | sens_cpu_volts[0] && | ||
740 | sens_cpu_amps[0] && | ||
741 | backside_temp && | ||
742 | drives_temp; | ||
743 | if (nr_chips > 1) | ||
744 | all_sensors &= | ||
745 | sens_cpu_temp[1] && | ||
746 | sens_cpu_volts[1] && | ||
747 | sens_cpu_amps[1]; | ||
748 | |||
749 | have_all_sensors = all_sensors; | ||
750 | } | ||
751 | |||
752 | static int pm72_wf_notify(struct notifier_block *self, | ||
753 | unsigned long event, void *data) | ||
754 | { | ||
755 | switch (event) { | ||
756 | case WF_EVENT_NEW_SENSOR: | ||
757 | pm72_new_sensor(data); | ||
758 | break; | ||
759 | case WF_EVENT_NEW_CONTROL: | ||
760 | pm72_new_control(data); | ||
761 | break; | ||
762 | case WF_EVENT_TICK: | ||
763 | if (have_all_controls && have_all_sensors) | ||
764 | pm72_tick(); | ||
765 | } | ||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | static struct notifier_block pm72_events = { | ||
770 | .notifier_call = pm72_wf_notify, | ||
771 | }; | ||
772 | |||
773 | static int wf_pm72_probe(struct platform_device *dev) | ||
774 | { | ||
775 | wf_register_client(&pm72_events); | ||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int __devexit wf_pm72_remove(struct platform_device *dev) | ||
780 | { | ||
781 | wf_unregister_client(&pm72_events); | ||
782 | |||
783 | /* should release all sensors and controls */ | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static struct platform_driver wf_pm72_driver = { | ||
788 | .probe = wf_pm72_probe, | ||
789 | .remove = wf_pm72_remove, | ||
790 | .driver = { | ||
791 | .name = "windfarm", | ||
792 | .owner = THIS_MODULE, | ||
793 | }, | ||
794 | }; | ||
795 | |||
796 | static int __init wf_pm72_init(void) | ||
797 | { | ||
798 | struct device_node *cpu; | ||
799 | int i; | ||
800 | |||
801 | if (!of_machine_is_compatible("PowerMac7,2") && | ||
802 | !of_machine_is_compatible("PowerMac7,3")) | ||
803 | return -ENODEV; | ||
804 | |||
805 | /* Count the number of CPU cores */ | ||
806 | nr_chips = 0; | ||
807 | for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) | ||
808 | ++nr_chips; | ||
809 | if (nr_chips > NR_CHIPS) | ||
810 | nr_chips = NR_CHIPS; | ||
811 | |||
812 | pr_info("windfarm: Initializing for desktop G5 with %d chips\n", | ||
813 | nr_chips); | ||
814 | |||
815 | /* Get MPU data for each CPU */ | ||
816 | for (i = 0; i < nr_chips; i++) { | ||
817 | cpu_mpu_data[i] = wf_get_mpu(i); | ||
818 | if (!cpu_mpu_data[i]) { | ||
819 | pr_err("wf_pm72: Failed to find MPU data for CPU %d\n", i); | ||
820 | return -ENXIO; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | #ifdef MODULE | ||
825 | request_module("windfarm_fcu_controls"); | ||
826 | request_module("windfarm_lm75_sensor"); | ||
827 | request_module("windfarm_ad7417_sensor"); | ||
828 | request_module("windfarm_max6690_sensor"); | ||
829 | request_module("windfarm_cpufreq_clamp"); | ||
830 | #endif /* MODULE */ | ||
831 | |||
832 | platform_driver_register(&wf_pm72_driver); | ||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | static void __exit wf_pm72_exit(void) | ||
837 | { | ||
838 | platform_driver_unregister(&wf_pm72_driver); | ||
839 | } | ||
840 | |||
841 | module_init(wf_pm72_init); | ||
842 | module_exit(wf_pm72_exit); | ||
843 | |||
844 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
845 | MODULE_DESCRIPTION("Thermal control for AGP PowerMac G5s"); | ||
846 | MODULE_LICENSE("GPL"); | ||
847 | MODULE_ALIAS("platform:windfarm"); | ||
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index fc13d0f2663b..990c87606be9 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) | |||
302 | pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; | 302 | pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; |
303 | pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; | 303 | pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; |
304 | pid_param.itarget = param->itarget; | 304 | pid_param.itarget = param->itarget; |
305 | pid_param.min = fan_system->ops->get_min(fan_system); | 305 | pid_param.min = wf_control_get_min(fan_system); |
306 | pid_param.max = fan_system->ops->get_max(fan_system); | 306 | pid_param.max = wf_control_get_max(fan_system); |
307 | if (fan_hd) { | 307 | if (fan_hd) { |
308 | pid_param.min = | 308 | pid_param.min = |
309 | max(pid_param.min,fan_hd->ops->get_min(fan_hd)); | 309 | max(pid_param.min, wf_control_get_min(fan_hd)); |
310 | pid_param.max = | 310 | pid_param.max = |
311 | min(pid_param.max,fan_hd->ops->get_max(fan_hd)); | 311 | min(pid_param.max, wf_control_get_max(fan_hd)); |
312 | } | 312 | } |
313 | wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); | 313 | wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); |
314 | 314 | ||
@@ -337,7 +337,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) | |||
337 | } | 337 | } |
338 | st->ticks = WF_SMU_SYS_FANS_INTERVAL; | 338 | st->ticks = WF_SMU_SYS_FANS_INTERVAL; |
339 | 339 | ||
340 | rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); | 340 | rc = wf_sensor_get(sensor_hd_temp, &temp); |
341 | if (rc) { | 341 | if (rc) { |
342 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", | 342 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", |
343 | rc); | 343 | rc); |
@@ -373,7 +373,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) | |||
373 | st->hd_setpoint = new_setpoint; | 373 | st->hd_setpoint = new_setpoint; |
374 | readjust: | 374 | readjust: |
375 | if (fan_system && wf_smu_failure_state == 0) { | 375 | if (fan_system && wf_smu_failure_state == 0) { |
376 | rc = fan_system->ops->set_value(fan_system, st->sys_setpoint); | 376 | rc = wf_control_set(fan_system, st->sys_setpoint); |
377 | if (rc) { | 377 | if (rc) { |
378 | printk(KERN_WARNING "windfarm: Sys fan error %d\n", | 378 | printk(KERN_WARNING "windfarm: Sys fan error %d\n", |
379 | rc); | 379 | rc); |
@@ -381,7 +381,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) | |||
381 | } | 381 | } |
382 | } | 382 | } |
383 | if (fan_hd && wf_smu_failure_state == 0) { | 383 | if (fan_hd && wf_smu_failure_state == 0) { |
384 | rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint); | 384 | rc = wf_control_set(fan_hd, st->hd_setpoint); |
385 | if (rc) { | 385 | if (rc) { |
386 | printk(KERN_WARNING "windfarm: HD fan error %d\n", | 386 | printk(KERN_WARNING "windfarm: HD fan error %d\n", |
387 | rc); | 387 | rc); |
@@ -447,8 +447,8 @@ static void wf_smu_create_cpu_fans(void) | |||
447 | pid_param.ttarget = tmax - tdelta; | 447 | pid_param.ttarget = tmax - tdelta; |
448 | pid_param.pmaxadj = maxpow - powadj; | 448 | pid_param.pmaxadj = maxpow - powadj; |
449 | 449 | ||
450 | pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); | 450 | pid_param.min = wf_control_get_min(fan_cpu_main); |
451 | pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); | 451 | pid_param.max = wf_control_get_max(fan_cpu_main); |
452 | 452 | ||
453 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); | 453 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); |
454 | 454 | ||
@@ -481,7 +481,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
481 | } | 481 | } |
482 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; | 482 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; |
483 | 483 | ||
484 | rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); | 484 | rc = wf_sensor_get(sensor_cpu_temp, &temp); |
485 | if (rc) { | 485 | if (rc) { |
486 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", | 486 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", |
487 | rc); | 487 | rc); |
@@ -489,7 +489,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
489 | return; | 489 | return; |
490 | } | 490 | } |
491 | 491 | ||
492 | rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); | 492 | rc = wf_sensor_get(sensor_cpu_power, &power); |
493 | if (rc) { | 493 | if (rc) { |
494 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", | 494 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", |
495 | rc); | 495 | rc); |
@@ -525,8 +525,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
525 | st->cpu_setpoint = new_setpoint; | 525 | st->cpu_setpoint = new_setpoint; |
526 | readjust: | 526 | readjust: |
527 | if (fan_cpu_main && wf_smu_failure_state == 0) { | 527 | if (fan_cpu_main && wf_smu_failure_state == 0) { |
528 | rc = fan_cpu_main->ops->set_value(fan_cpu_main, | 528 | rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); |
529 | st->cpu_setpoint); | ||
530 | if (rc) { | 529 | if (rc) { |
531 | printk(KERN_WARNING "windfarm: CPU main fan" | 530 | printk(KERN_WARNING "windfarm: CPU main fan" |
532 | " error %d\n", rc); | 531 | " error %d\n", rc); |
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index a9430ed4f36c..7653603cb00e 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) | |||
192 | pid_param.ttarget = tmax - tdelta; | 192 | pid_param.ttarget = tmax - tdelta; |
193 | pid_param.pmaxadj = maxpow - powadj; | 193 | pid_param.pmaxadj = maxpow - powadj; |
194 | 194 | ||
195 | pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); | 195 | pid_param.min = wf_control_get_min(fan_cpu_main); |
196 | pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); | 196 | pid_param.max = wf_control_get_max(fan_cpu_main); |
197 | 197 | ||
198 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); | 198 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); |
199 | 199 | ||
@@ -226,7 +226,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
226 | } | 226 | } |
227 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; | 227 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; |
228 | 228 | ||
229 | rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); | 229 | rc = wf_sensor_get(sensor_cpu_temp, &temp); |
230 | if (rc) { | 230 | if (rc) { |
231 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", | 231 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", |
232 | rc); | 232 | rc); |
@@ -234,7 +234,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
234 | return; | 234 | return; |
235 | } | 235 | } |
236 | 236 | ||
237 | rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); | 237 | rc = wf_sensor_get(sensor_cpu_power, &power); |
238 | if (rc) { | 238 | if (rc) { |
239 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", | 239 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", |
240 | rc); | 240 | rc); |
@@ -261,8 +261,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
261 | st->cpu_setpoint = new_setpoint; | 261 | st->cpu_setpoint = new_setpoint; |
262 | readjust: | 262 | readjust: |
263 | if (fan_cpu_main && wf_smu_failure_state == 0) { | 263 | if (fan_cpu_main && wf_smu_failure_state == 0) { |
264 | rc = fan_cpu_main->ops->set_value(fan_cpu_main, | 264 | rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); |
265 | st->cpu_setpoint); | ||
266 | if (rc) { | 265 | if (rc) { |
267 | printk(KERN_WARNING "windfarm: CPU main fan" | 266 | printk(KERN_WARNING "windfarm: CPU main fan" |
268 | " error %d\n", rc); | 267 | " error %d\n", rc); |
@@ -270,8 +269,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
270 | } | 269 | } |
271 | } | 270 | } |
272 | if (fan_cpu_second && wf_smu_failure_state == 0) { | 271 | if (fan_cpu_second && wf_smu_failure_state == 0) { |
273 | rc = fan_cpu_second->ops->set_value(fan_cpu_second, | 272 | rc = wf_control_set(fan_cpu_second, st->cpu_setpoint); |
274 | st->cpu_setpoint); | ||
275 | if (rc) { | 273 | if (rc) { |
276 | printk(KERN_WARNING "windfarm: CPU second fan" | 274 | printk(KERN_WARNING "windfarm: CPU second fan" |
277 | " error %d\n", rc); | 275 | " error %d\n", rc); |
@@ -279,8 +277,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
279 | } | 277 | } |
280 | } | 278 | } |
281 | if (fan_cpu_third && wf_smu_failure_state == 0) { | 279 | if (fan_cpu_third && wf_smu_failure_state == 0) { |
282 | rc = fan_cpu_main->ops->set_value(fan_cpu_third, | 280 | rc = wf_control_set(fan_cpu_third, st->cpu_setpoint); |
283 | st->cpu_setpoint); | ||
284 | if (rc) { | 281 | if (rc) { |
285 | printk(KERN_WARNING "windfarm: CPU third fan" | 282 | printk(KERN_WARNING "windfarm: CPU third fan" |
286 | " error %d\n", rc); | 283 | " error %d\n", rc); |
@@ -312,8 +309,8 @@ static void wf_smu_create_drive_fans(void) | |||
312 | 309 | ||
313 | /* Fill PID params */ | 310 | /* Fill PID params */ |
314 | param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN); | 311 | param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN); |
315 | param.min = fan_hd->ops->get_min(fan_hd); | 312 | param.min = wf_control_get_min(fan_hd); |
316 | param.max = fan_hd->ops->get_max(fan_hd); | 313 | param.max = wf_control_get_max(fan_hd); |
317 | wf_pid_init(&wf_smu_drive_fans->pid, ¶m); | 314 | wf_pid_init(&wf_smu_drive_fans->pid, ¶m); |
318 | 315 | ||
319 | DBG("wf: Drive Fan control initialized.\n"); | 316 | 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) | |||
338 | } | 335 | } |
339 | st->ticks = st->pid.param.interval; | 336 | st->ticks = st->pid.param.interval; |
340 | 337 | ||
341 | rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); | 338 | rc = wf_sensor_get(sensor_hd_temp, &temp); |
342 | if (rc) { | 339 | if (rc) { |
343 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", | 340 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", |
344 | rc); | 341 | rc); |
@@ -361,7 +358,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) | |||
361 | st->setpoint = new_setpoint; | 358 | st->setpoint = new_setpoint; |
362 | readjust: | 359 | readjust: |
363 | if (fan_hd && wf_smu_failure_state == 0) { | 360 | if (fan_hd && wf_smu_failure_state == 0) { |
364 | rc = fan_hd->ops->set_value(fan_hd, st->setpoint); | 361 | rc = wf_control_set(fan_hd, st->setpoint); |
365 | if (rc) { | 362 | if (rc) { |
366 | printk(KERN_WARNING "windfarm: HD fan error %d\n", | 363 | printk(KERN_WARNING "windfarm: HD fan error %d\n", |
367 | rc); | 364 | rc); |
@@ -393,8 +390,8 @@ static void wf_smu_create_slots_fans(void) | |||
393 | 390 | ||
394 | /* Fill PID params */ | 391 | /* Fill PID params */ |
395 | param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN); | 392 | param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN); |
396 | param.min = fan_slots->ops->get_min(fan_slots); | 393 | param.min = wf_control_get_min(fan_slots); |
397 | param.max = fan_slots->ops->get_max(fan_slots); | 394 | param.max = wf_control_get_max(fan_slots); |
398 | wf_pid_init(&wf_smu_slots_fans->pid, ¶m); | 395 | wf_pid_init(&wf_smu_slots_fans->pid, ¶m); |
399 | 396 | ||
400 | DBG("wf: Slots Fan control initialized.\n"); | 397 | 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) | |||
419 | } | 416 | } |
420 | st->ticks = st->pid.param.interval; | 417 | st->ticks = st->pid.param.interval; |
421 | 418 | ||
422 | rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power); | 419 | rc = wf_sensor_get(sensor_slots_power, &power); |
423 | if (rc) { | 420 | if (rc) { |
424 | printk(KERN_WARNING "windfarm: Slots power sensor error %d\n", | 421 | printk(KERN_WARNING "windfarm: Slots power sensor error %d\n", |
425 | rc); | 422 | rc); |
@@ -444,7 +441,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) | |||
444 | st->setpoint = new_setpoint; | 441 | st->setpoint = new_setpoint; |
445 | readjust: | 442 | readjust: |
446 | if (fan_slots && wf_smu_failure_state == 0) { | 443 | if (fan_slots && wf_smu_failure_state == 0) { |
447 | rc = fan_slots->ops->set_value(fan_slots, st->setpoint); | 444 | rc = wf_control_set(fan_slots, st->setpoint); |
448 | if (rc) { | 445 | if (rc) { |
449 | printk(KERN_WARNING "windfarm: Slots fan error %d\n", | 446 | printk(KERN_WARNING "windfarm: Slots fan error %d\n", |
450 | rc); | 447 | rc); |
diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c new file mode 100644 index 000000000000..3eca6d4b52fc --- /dev/null +++ b/drivers/macintosh/windfarm_rm31.c | |||
@@ -0,0 +1,740 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. | ||
3 | * Control loops for RackMack3,1 (Xserve G5) | ||
4 | * | ||
5 | * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. | ||
6 | * | ||
7 | * Use and redistribute under the terms of the GNU GPL v2. | ||
8 | */ | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/reboot.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include <asm/smu.h> | ||
17 | |||
18 | #include "windfarm.h" | ||
19 | #include "windfarm_pid.h" | ||
20 | #include "windfarm_mpu.h" | ||
21 | |||
22 | #define VERSION "1.0" | ||
23 | |||
24 | #undef DEBUG | ||
25 | #undef LOTSA_DEBUG | ||
26 | |||
27 | #ifdef DEBUG | ||
28 | #define DBG(args...) printk(args) | ||
29 | #else | ||
30 | #define DBG(args...) do { } while(0) | ||
31 | #endif | ||
32 | |||
33 | #ifdef LOTSA_DEBUG | ||
34 | #define DBG_LOTS(args...) printk(args) | ||
35 | #else | ||
36 | #define DBG_LOTS(args...) do { } while(0) | ||
37 | #endif | ||
38 | |||
39 | /* define this to force CPU overtemp to 60 degree, useful for testing | ||
40 | * the overtemp code | ||
41 | */ | ||
42 | #undef HACKED_OVERTEMP | ||
43 | |||
44 | /* We currently only handle 2 chips */ | ||
45 | #define NR_CHIPS 2 | ||
46 | #define NR_CPU_FANS 3 * NR_CHIPS | ||
47 | |||
48 | /* Controls and sensors */ | ||
49 | static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; | ||
50 | static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; | ||
51 | static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; | ||
52 | static struct wf_sensor *backside_temp; | ||
53 | static struct wf_sensor *slots_temp; | ||
54 | static struct wf_sensor *dimms_temp; | ||
55 | |||
56 | static struct wf_control *cpu_fans[NR_CHIPS][3]; | ||
57 | static struct wf_control *backside_fan; | ||
58 | static struct wf_control *slots_fan; | ||
59 | static struct wf_control *cpufreq_clamp; | ||
60 | |||
61 | /* We keep a temperature history for average calculation of 180s */ | ||
62 | #define CPU_TEMP_HIST_SIZE 180 | ||
63 | |||
64 | /* PID loop state */ | ||
65 | static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; | ||
66 | static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; | ||
67 | static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; | ||
68 | static int cpu_thist_pt; | ||
69 | static s64 cpu_thist_total; | ||
70 | static s32 cpu_all_tmax = 100 << 16; | ||
71 | static struct wf_pid_state backside_pid; | ||
72 | static int backside_tick; | ||
73 | static struct wf_pid_state slots_pid; | ||
74 | static int slots_tick; | ||
75 | static int slots_speed; | ||
76 | static struct wf_pid_state dimms_pid; | ||
77 | static int dimms_output_clamp; | ||
78 | |||
79 | static int nr_chips; | ||
80 | static bool have_all_controls; | ||
81 | static bool have_all_sensors; | ||
82 | static bool started; | ||
83 | |||
84 | static int failure_state; | ||
85 | #define FAILURE_SENSOR 1 | ||
86 | #define FAILURE_FAN 2 | ||
87 | #define FAILURE_PERM 4 | ||
88 | #define FAILURE_LOW_OVERTEMP 8 | ||
89 | #define FAILURE_HIGH_OVERTEMP 16 | ||
90 | |||
91 | /* Overtemp values */ | ||
92 | #define LOW_OVER_AVERAGE 0 | ||
93 | #define LOW_OVER_IMMEDIATE (10 << 16) | ||
94 | #define LOW_OVER_CLEAR ((-10) << 16) | ||
95 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
96 | #define HIGH_OVER_AVERAGE (10 << 16) | ||
97 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
98 | |||
99 | |||
100 | static void cpu_max_all_fans(void) | ||
101 | { | ||
102 | int i; | ||
103 | |||
104 | /* We max all CPU fans in case of a sensor error. We also do the | ||
105 | * cpufreq clamping now, even if it's supposedly done later by the | ||
106 | * generic code anyway, we do it earlier here to react faster | ||
107 | */ | ||
108 | if (cpufreq_clamp) | ||
109 | wf_control_set_max(cpufreq_clamp); | ||
110 | for (i = 0; i < nr_chips; i++) { | ||
111 | if (cpu_fans[i][0]) | ||
112 | wf_control_set_max(cpu_fans[i][0]); | ||
113 | if (cpu_fans[i][1]) | ||
114 | wf_control_set_max(cpu_fans[i][1]); | ||
115 | if (cpu_fans[i][2]) | ||
116 | wf_control_set_max(cpu_fans[i][2]); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | static int cpu_check_overtemp(s32 temp) | ||
121 | { | ||
122 | int new_state = 0; | ||
123 | s32 t_avg, t_old; | ||
124 | static bool first = true; | ||
125 | |||
126 | /* First check for immediate overtemps */ | ||
127 | if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { | ||
128 | new_state |= FAILURE_LOW_OVERTEMP; | ||
129 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
130 | printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" | ||
131 | " temperature !\n"); | ||
132 | } | ||
133 | if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { | ||
134 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
135 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
136 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
137 | " immediate CPU temperature !\n"); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * The first time around, initialize the array with the first | ||
142 | * temperature reading | ||
143 | */ | ||
144 | if (first) { | ||
145 | int i; | ||
146 | |||
147 | cpu_thist_total = 0; | ||
148 | for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { | ||
149 | cpu_thist[i] = temp; | ||
150 | cpu_thist_total += temp; | ||
151 | } | ||
152 | first = false; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * We calculate a history of max temperatures and use that for the | ||
157 | * overtemp management | ||
158 | */ | ||
159 | t_old = cpu_thist[cpu_thist_pt]; | ||
160 | cpu_thist[cpu_thist_pt] = temp; | ||
161 | cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; | ||
162 | cpu_thist_total -= t_old; | ||
163 | cpu_thist_total += temp; | ||
164 | t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; | ||
165 | |||
166 | DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", | ||
167 | FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); | ||
168 | |||
169 | /* Now check for average overtemps */ | ||
170 | if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { | ||
171 | new_state |= FAILURE_LOW_OVERTEMP; | ||
172 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
173 | printk(KERN_ERR "windfarm: Overtemp due to average CPU" | ||
174 | " temperature !\n"); | ||
175 | } | ||
176 | if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { | ||
177 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
178 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
179 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
180 | " average CPU temperature !\n"); | ||
181 | } | ||
182 | |||
183 | /* Now handle overtemp conditions. We don't currently use the windfarm | ||
184 | * overtemp handling core as it's not fully suited to the needs of those | ||
185 | * new machine. This will be fixed later. | ||
186 | */ | ||
187 | if (new_state) { | ||
188 | /* High overtemp -> immediate shutdown */ | ||
189 | if (new_state & FAILURE_HIGH_OVERTEMP) | ||
190 | machine_power_off(); | ||
191 | if ((failure_state & new_state) != new_state) | ||
192 | cpu_max_all_fans(); | ||
193 | failure_state |= new_state; | ||
194 | } else if ((failure_state & FAILURE_LOW_OVERTEMP) && | ||
195 | (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { | ||
196 | printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); | ||
197 | failure_state &= ~FAILURE_LOW_OVERTEMP; | ||
198 | } | ||
199 | |||
200 | return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); | ||
201 | } | ||
202 | |||
203 | static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) | ||
204 | { | ||
205 | s32 dtemp, volts, amps; | ||
206 | int rc; | ||
207 | |||
208 | /* Get diode temperature */ | ||
209 | rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); | ||
210 | if (rc) { | ||
211 | DBG(" CPU%d: temp reading error !\n", cpu); | ||
212 | return -EIO; | ||
213 | } | ||
214 | DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); | ||
215 | *temp = dtemp; | ||
216 | |||
217 | /* Get voltage */ | ||
218 | rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); | ||
219 | if (rc) { | ||
220 | DBG(" CPU%d, volts reading error !\n", cpu); | ||
221 | return -EIO; | ||
222 | } | ||
223 | DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); | ||
224 | |||
225 | /* Get current */ | ||
226 | rc = wf_sensor_get(sens_cpu_amps[cpu], &s); | ||
227 | if (rc) { | ||
228 | DBG(" CPU%d, current reading error !\n", cpu); | ||
229 | return -EIO; | ||
230 | } | ||
231 | DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); | ||
232 | |||
233 | /* Calculate power */ | ||
234 | |||
235 | /* Scale voltage and current raw sensor values according to fixed scales | ||
236 | * obtained in Darwin and calculate power from I and V | ||
237 | */ | ||
238 | *power = (((u64)volts) * ((u64)amps)) >> 16; | ||
239 | |||
240 | DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); | ||
241 | |||
242 | return 0; | ||
243 | |||
244 | } | ||
245 | |||
246 | static void cpu_fans_tick(void) | ||
247 | { | ||
248 | int err, cpu, i; | ||
249 | s32 speed, temp, power, t_max = 0; | ||
250 | |||
251 | DBG_LOTS("* cpu fans_tick_split()\n"); | ||
252 | |||
253 | for (cpu = 0; cpu < nr_chips; ++cpu) { | ||
254 | struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; | ||
255 | |||
256 | /* Read current speed */ | ||
257 | wf_control_get(cpu_fans[cpu][0], &sp->target); | ||
258 | |||
259 | err = read_one_cpu_vals(cpu, &temp, &power); | ||
260 | if (err) { | ||
261 | failure_state |= FAILURE_SENSOR; | ||
262 | cpu_max_all_fans(); | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | /* Keep track of highest temp */ | ||
267 | t_max = max(t_max, temp); | ||
268 | |||
269 | /* Handle possible overtemps */ | ||
270 | if (cpu_check_overtemp(t_max)) | ||
271 | return; | ||
272 | |||
273 | /* Run PID */ | ||
274 | wf_cpu_pid_run(sp, power, temp); | ||
275 | |||
276 | DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); | ||
277 | |||
278 | /* Apply DIMMs clamp */ | ||
279 | speed = max(sp->target, dimms_output_clamp); | ||
280 | |||
281 | /* Apply result to all cpu fans */ | ||
282 | for (i = 0; i < 3; i++) { | ||
283 | err = wf_control_set(cpu_fans[cpu][i], speed); | ||
284 | if (err) { | ||
285 | pr_warning("wf_rm31: Fan %s reports error %d\n", | ||
286 | cpu_fans[cpu][i]->name, err); | ||
287 | failure_state |= FAILURE_FAN; | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* Implementation... */ | ||
294 | static int cpu_setup_pid(int cpu) | ||
295 | { | ||
296 | struct wf_cpu_pid_param pid; | ||
297 | const struct mpu_data *mpu = cpu_mpu_data[cpu]; | ||
298 | s32 tmax, ttarget, ptarget; | ||
299 | int fmin, fmax, hsize; | ||
300 | |||
301 | /* Get PID params from the appropriate MPU EEPROM */ | ||
302 | tmax = mpu->tmax << 16; | ||
303 | ttarget = mpu->ttarget << 16; | ||
304 | ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; | ||
305 | |||
306 | DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", | ||
307 | cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); | ||
308 | |||
309 | /* We keep a global tmax for overtemp calculations */ | ||
310 | if (tmax < cpu_all_tmax) | ||
311 | cpu_all_tmax = tmax; | ||
312 | |||
313 | /* Set PID min/max by using the rear fan min/max */ | ||
314 | fmin = wf_control_get_min(cpu_fans[cpu][0]); | ||
315 | fmax = wf_control_get_max(cpu_fans[cpu][0]); | ||
316 | DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); | ||
317 | |||
318 | /* History size */ | ||
319 | hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); | ||
320 | DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); | ||
321 | |||
322 | /* Initialize PID loop */ | ||
323 | pid.interval = 1; /* seconds */ | ||
324 | pid.history_len = hsize; | ||
325 | pid.gd = mpu->pid_gd; | ||
326 | pid.gp = mpu->pid_gp; | ||
327 | pid.gr = mpu->pid_gr; | ||
328 | pid.tmax = tmax; | ||
329 | pid.ttarget = ttarget; | ||
330 | pid.pmaxadj = ptarget; | ||
331 | pid.min = fmin; | ||
332 | pid.max = fmax; | ||
333 | |||
334 | wf_cpu_pid_init(&cpu_pid[cpu], &pid); | ||
335 | cpu_pid[cpu].target = 4000; | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* Backside/U3 fan */ | ||
341 | static struct wf_pid_param backside_param = { | ||
342 | .interval = 1, | ||
343 | .history_len = 2, | ||
344 | .gd = 0x00500000, | ||
345 | .gp = 0x0004cccc, | ||
346 | .gr = 0, | ||
347 | .itarget = 70 << 16, | ||
348 | .additive = 0, | ||
349 | .min = 20, | ||
350 | .max = 100, | ||
351 | }; | ||
352 | |||
353 | /* DIMMs temperature (clamp the backside fan) */ | ||
354 | static struct wf_pid_param dimms_param = { | ||
355 | .interval = 1, | ||
356 | .history_len = 20, | ||
357 | .gd = 0, | ||
358 | .gp = 0, | ||
359 | .gr = 0x06553600, | ||
360 | .itarget = 50 << 16, | ||
361 | .additive = 0, | ||
362 | .min = 4000, | ||
363 | .max = 14000, | ||
364 | }; | ||
365 | |||
366 | static void backside_fan_tick(void) | ||
367 | { | ||
368 | s32 temp, dtemp; | ||
369 | int speed, dspeed, fan_min; | ||
370 | int err; | ||
371 | |||
372 | if (!backside_fan || !backside_temp || !dimms_temp || !backside_tick) | ||
373 | return; | ||
374 | if (--backside_tick > 0) | ||
375 | return; | ||
376 | backside_tick = backside_pid.param.interval; | ||
377 | |||
378 | DBG_LOTS("* backside fans tick\n"); | ||
379 | |||
380 | /* Update fan speed from actual fans */ | ||
381 | err = wf_control_get(backside_fan, &speed); | ||
382 | if (!err) | ||
383 | backside_pid.target = speed; | ||
384 | |||
385 | err = wf_sensor_get(backside_temp, &temp); | ||
386 | if (err) { | ||
387 | printk(KERN_WARNING "windfarm: U3 temp sensor error %d\n", | ||
388 | err); | ||
389 | failure_state |= FAILURE_SENSOR; | ||
390 | wf_control_set_max(backside_fan); | ||
391 | return; | ||
392 | } | ||
393 | speed = wf_pid_run(&backside_pid, temp); | ||
394 | |||
395 | DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", | ||
396 | FIX32TOPRINT(temp), speed); | ||
397 | |||
398 | err = wf_sensor_get(dimms_temp, &dtemp); | ||
399 | if (err) { | ||
400 | printk(KERN_WARNING "windfarm: DIMMs temp sensor error %d\n", | ||
401 | err); | ||
402 | failure_state |= FAILURE_SENSOR; | ||
403 | wf_control_set_max(backside_fan); | ||
404 | return; | ||
405 | } | ||
406 | dspeed = wf_pid_run(&dimms_pid, dtemp); | ||
407 | dimms_output_clamp = dspeed; | ||
408 | |||
409 | fan_min = (dspeed * 100) / 14000; | ||
410 | fan_min = max(fan_min, backside_param.min); | ||
411 | speed = max(speed, fan_min); | ||
412 | |||
413 | err = wf_control_set(backside_fan, speed); | ||
414 | if (err) { | ||
415 | printk(KERN_WARNING "windfarm: backside fan error %d\n", err); | ||
416 | failure_state |= FAILURE_FAN; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | static void backside_setup_pid(void) | ||
421 | { | ||
422 | /* first time initialize things */ | ||
423 | s32 fmin = wf_control_get_min(backside_fan); | ||
424 | s32 fmax = wf_control_get_max(backside_fan); | ||
425 | struct wf_pid_param param; | ||
426 | |||
427 | param = backside_param; | ||
428 | param.min = max(param.min, fmin); | ||
429 | param.max = min(param.max, fmax); | ||
430 | wf_pid_init(&backside_pid, ¶m); | ||
431 | |||
432 | param = dimms_param; | ||
433 | wf_pid_init(&dimms_pid, ¶m); | ||
434 | |||
435 | backside_tick = 1; | ||
436 | |||
437 | pr_info("wf_rm31: Backside control loop started.\n"); | ||
438 | } | ||
439 | |||
440 | /* Slots fan */ | ||
441 | static const struct wf_pid_param slots_param = { | ||
442 | .interval = 5, | ||
443 | .history_len = 2, | ||
444 | .gd = 30 << 20, | ||
445 | .gp = 5 << 20, | ||
446 | .gr = 0, | ||
447 | .itarget = 40 << 16, | ||
448 | .additive = 1, | ||
449 | .min = 300, | ||
450 | .max = 4000, | ||
451 | }; | ||
452 | |||
453 | static void slots_fan_tick(void) | ||
454 | { | ||
455 | s32 temp; | ||
456 | int speed; | ||
457 | int err; | ||
458 | |||
459 | if (!slots_fan || !slots_temp || !slots_tick) | ||
460 | return; | ||
461 | if (--slots_tick > 0) | ||
462 | return; | ||
463 | slots_tick = slots_pid.param.interval; | ||
464 | |||
465 | DBG_LOTS("* slots fans tick\n"); | ||
466 | |||
467 | err = wf_sensor_get(slots_temp, &temp); | ||
468 | if (err) { | ||
469 | pr_warning("wf_rm31: slots temp sensor error %d\n", err); | ||
470 | failure_state |= FAILURE_SENSOR; | ||
471 | wf_control_set_max(slots_fan); | ||
472 | return; | ||
473 | } | ||
474 | speed = wf_pid_run(&slots_pid, temp); | ||
475 | |||
476 | DBG_LOTS("slots PID temp=%d.%.3d speed=%d\n", | ||
477 | FIX32TOPRINT(temp), speed); | ||
478 | |||
479 | slots_speed = speed; | ||
480 | err = wf_control_set(slots_fan, speed); | ||
481 | if (err) { | ||
482 | printk(KERN_WARNING "windfarm: slots bay fan error %d\n", err); | ||
483 | failure_state |= FAILURE_FAN; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void slots_setup_pid(void) | ||
488 | { | ||
489 | /* first time initialize things */ | ||
490 | s32 fmin = wf_control_get_min(slots_fan); | ||
491 | s32 fmax = wf_control_get_max(slots_fan); | ||
492 | struct wf_pid_param param = slots_param; | ||
493 | |||
494 | param.min = max(param.min, fmin); | ||
495 | param.max = min(param.max, fmax); | ||
496 | wf_pid_init(&slots_pid, ¶m); | ||
497 | slots_tick = 1; | ||
498 | |||
499 | pr_info("wf_rm31: Slots control loop started.\n"); | ||
500 | } | ||
501 | |||
502 | static void set_fail_state(void) | ||
503 | { | ||
504 | cpu_max_all_fans(); | ||
505 | |||
506 | if (backside_fan) | ||
507 | wf_control_set_max(backside_fan); | ||
508 | if (slots_fan) | ||
509 | wf_control_set_max(slots_fan); | ||
510 | } | ||
511 | |||
512 | static void rm31_tick(void) | ||
513 | { | ||
514 | int i, last_failure; | ||
515 | |||
516 | if (!started) { | ||
517 | started = 1; | ||
518 | printk(KERN_INFO "windfarm: CPUs control loops started.\n"); | ||
519 | for (i = 0; i < nr_chips; ++i) { | ||
520 | if (cpu_setup_pid(i) < 0) { | ||
521 | failure_state = FAILURE_PERM; | ||
522 | set_fail_state(); | ||
523 | break; | ||
524 | } | ||
525 | } | ||
526 | DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); | ||
527 | |||
528 | backside_setup_pid(); | ||
529 | slots_setup_pid(); | ||
530 | |||
531 | #ifdef HACKED_OVERTEMP | ||
532 | cpu_all_tmax = 60 << 16; | ||
533 | #endif | ||
534 | } | ||
535 | |||
536 | /* Permanent failure, bail out */ | ||
537 | if (failure_state & FAILURE_PERM) | ||
538 | return; | ||
539 | |||
540 | /* | ||
541 | * Clear all failure bits except low overtemp which will be eventually | ||
542 | * cleared by the control loop itself | ||
543 | */ | ||
544 | last_failure = failure_state; | ||
545 | failure_state &= FAILURE_LOW_OVERTEMP; | ||
546 | backside_fan_tick(); | ||
547 | slots_fan_tick(); | ||
548 | |||
549 | /* We do CPUs last because they can be clamped high by | ||
550 | * DIMM temperature | ||
551 | */ | ||
552 | cpu_fans_tick(); | ||
553 | |||
554 | DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", | ||
555 | last_failure, failure_state); | ||
556 | |||
557 | /* Check for failures. Any failure causes cpufreq clamping */ | ||
558 | if (failure_state && last_failure == 0 && cpufreq_clamp) | ||
559 | wf_control_set_max(cpufreq_clamp); | ||
560 | if (failure_state == 0 && last_failure && cpufreq_clamp) | ||
561 | wf_control_set_min(cpufreq_clamp); | ||
562 | |||
563 | /* That's it for now, we might want to deal with other failures | ||
564 | * differently in the future though | ||
565 | */ | ||
566 | } | ||
567 | |||
568 | static void rm31_new_control(struct wf_control *ct) | ||
569 | { | ||
570 | bool all_controls; | ||
571 | |||
572 | if (!strcmp(ct->name, "cpu-fan-a-0")) | ||
573 | cpu_fans[0][0] = ct; | ||
574 | else if (!strcmp(ct->name, "cpu-fan-b-0")) | ||
575 | cpu_fans[0][1] = ct; | ||
576 | else if (!strcmp(ct->name, "cpu-fan-c-0")) | ||
577 | cpu_fans[0][2] = ct; | ||
578 | else if (!strcmp(ct->name, "cpu-fan-a-1")) | ||
579 | cpu_fans[1][0] = ct; | ||
580 | else if (!strcmp(ct->name, "cpu-fan-b-1")) | ||
581 | cpu_fans[1][1] = ct; | ||
582 | else if (!strcmp(ct->name, "cpu-fan-c-1")) | ||
583 | cpu_fans[1][2] = ct; | ||
584 | else if (!strcmp(ct->name, "backside-fan")) | ||
585 | backside_fan = ct; | ||
586 | else if (!strcmp(ct->name, "slots-fan")) | ||
587 | slots_fan = ct; | ||
588 | else if (!strcmp(ct->name, "cpufreq-clamp")) | ||
589 | cpufreq_clamp = ct; | ||
590 | |||
591 | all_controls = | ||
592 | cpu_fans[0][0] && | ||
593 | cpu_fans[0][1] && | ||
594 | cpu_fans[0][2] && | ||
595 | backside_fan && | ||
596 | slots_fan; | ||
597 | if (nr_chips > 1) | ||
598 | all_controls &= | ||
599 | cpu_fans[1][0] && | ||
600 | cpu_fans[1][1] && | ||
601 | cpu_fans[1][2]; | ||
602 | have_all_controls = all_controls; | ||
603 | } | ||
604 | |||
605 | |||
606 | static void rm31_new_sensor(struct wf_sensor *sr) | ||
607 | { | ||
608 | bool all_sensors; | ||
609 | |||
610 | if (!strcmp(sr->name, "cpu-diode-temp-0")) | ||
611 | sens_cpu_temp[0] = sr; | ||
612 | else if (!strcmp(sr->name, "cpu-diode-temp-1")) | ||
613 | sens_cpu_temp[1] = sr; | ||
614 | else if (!strcmp(sr->name, "cpu-voltage-0")) | ||
615 | sens_cpu_volts[0] = sr; | ||
616 | else if (!strcmp(sr->name, "cpu-voltage-1")) | ||
617 | sens_cpu_volts[1] = sr; | ||
618 | else if (!strcmp(sr->name, "cpu-current-0")) | ||
619 | sens_cpu_amps[0] = sr; | ||
620 | else if (!strcmp(sr->name, "cpu-current-1")) | ||
621 | sens_cpu_amps[1] = sr; | ||
622 | else if (!strcmp(sr->name, "backside-temp")) | ||
623 | backside_temp = sr; | ||
624 | else if (!strcmp(sr->name, "slots-temp")) | ||
625 | slots_temp = sr; | ||
626 | else if (!strcmp(sr->name, "dimms-temp")) | ||
627 | dimms_temp = sr; | ||
628 | |||
629 | all_sensors = | ||
630 | sens_cpu_temp[0] && | ||
631 | sens_cpu_volts[0] && | ||
632 | sens_cpu_amps[0] && | ||
633 | backside_temp && | ||
634 | slots_temp && | ||
635 | dimms_temp; | ||
636 | if (nr_chips > 1) | ||
637 | all_sensors &= | ||
638 | sens_cpu_temp[1] && | ||
639 | sens_cpu_volts[1] && | ||
640 | sens_cpu_amps[1]; | ||
641 | |||
642 | have_all_sensors = all_sensors; | ||
643 | } | ||
644 | |||
645 | static int rm31_wf_notify(struct notifier_block *self, | ||
646 | unsigned long event, void *data) | ||
647 | { | ||
648 | switch (event) { | ||
649 | case WF_EVENT_NEW_SENSOR: | ||
650 | rm31_new_sensor(data); | ||
651 | break; | ||
652 | case WF_EVENT_NEW_CONTROL: | ||
653 | rm31_new_control(data); | ||
654 | break; | ||
655 | case WF_EVENT_TICK: | ||
656 | if (have_all_controls && have_all_sensors) | ||
657 | rm31_tick(); | ||
658 | } | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static struct notifier_block rm31_events = { | ||
663 | .notifier_call = rm31_wf_notify, | ||
664 | }; | ||
665 | |||
666 | static int wf_rm31_probe(struct platform_device *dev) | ||
667 | { | ||
668 | wf_register_client(&rm31_events); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int __devexit wf_rm31_remove(struct platform_device *dev) | ||
673 | { | ||
674 | wf_unregister_client(&rm31_events); | ||
675 | |||
676 | /* should release all sensors and controls */ | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | static struct platform_driver wf_rm31_driver = { | ||
681 | .probe = wf_rm31_probe, | ||
682 | .remove = wf_rm31_remove, | ||
683 | .driver = { | ||
684 | .name = "windfarm", | ||
685 | .owner = THIS_MODULE, | ||
686 | }, | ||
687 | }; | ||
688 | |||
689 | static int __init wf_rm31_init(void) | ||
690 | { | ||
691 | struct device_node *cpu; | ||
692 | int i; | ||
693 | |||
694 | if (!of_machine_is_compatible("RackMac3,1")) | ||
695 | return -ENODEV; | ||
696 | |||
697 | /* Count the number of CPU cores */ | ||
698 | nr_chips = 0; | ||
699 | for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) | ||
700 | ++nr_chips; | ||
701 | if (nr_chips > NR_CHIPS) | ||
702 | nr_chips = NR_CHIPS; | ||
703 | |||
704 | pr_info("windfarm: Initializing for desktop G5 with %d chips\n", | ||
705 | nr_chips); | ||
706 | |||
707 | /* Get MPU data for each CPU */ | ||
708 | for (i = 0; i < nr_chips; i++) { | ||
709 | cpu_mpu_data[i] = wf_get_mpu(i); | ||
710 | if (!cpu_mpu_data[i]) { | ||
711 | pr_err("wf_rm31: Failed to find MPU data for CPU %d\n", i); | ||
712 | return -ENXIO; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | #ifdef MODULE | ||
717 | request_module("windfarm_fcu_controls"); | ||
718 | request_module("windfarm_lm75_sensor"); | ||
719 | request_module("windfarm_lm87_sensor"); | ||
720 | request_module("windfarm_ad7417_sensor"); | ||
721 | request_module("windfarm_max6690_sensor"); | ||
722 | request_module("windfarm_cpufreq_clamp"); | ||
723 | #endif /* MODULE */ | ||
724 | |||
725 | platform_driver_register(&wf_rm31_driver); | ||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static void __exit wf_rm31_exit(void) | ||
730 | { | ||
731 | platform_driver_unregister(&wf_rm31_driver); | ||
732 | } | ||
733 | |||
734 | module_init(wf_rm31_init); | ||
735 | module_exit(wf_rm31_exit); | ||
736 | |||
737 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
738 | MODULE_DESCRIPTION("Thermal control for Xserve G5"); | ||
739 | MODULE_LICENSE("GPL"); | ||
740 | MODULE_ALIAS("platform:windfarm"); | ||
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index 3c2be5193fd5..c155a54e8638 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, | |||
172 | 172 | ||
173 | fct->fan_type = pwm_fan; | 173 | fct->fan_type = pwm_fan; |
174 | fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN; | 174 | fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN; |
175 | sysfs_attr_init(&fct->ctrl.attr.attr); | ||
176 | 175 | ||
177 | /* We use the name & location here the same way we do for SMU sensors, | 176 | /* We use the name & location here the same way we do for SMU sensors, |
178 | * see the comment in windfarm_smu_sensors.c. The locations are a bit | 177 | * 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 65a8ff3e1f8e..426e810233d7 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c | |||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | #include "windfarm.h" | 21 | #include "windfarm.h" |
22 | 22 | ||
23 | #define VERSION "0.2" | 23 | #define VERSION "1.0" |
24 | 24 | ||
25 | #define DEBUG | 25 | #define DEBUG |
26 | 26 | ||
@@ -34,11 +34,12 @@ | |||
34 | #define MAX_AGE msecs_to_jiffies(800) | 34 | #define MAX_AGE msecs_to_jiffies(800) |
35 | 35 | ||
36 | struct wf_sat { | 36 | struct wf_sat { |
37 | struct kref ref; | ||
37 | int nr; | 38 | int nr; |
38 | atomic_t refcnt; | ||
39 | struct mutex mutex; | 39 | struct mutex mutex; |
40 | unsigned long last_read; /* jiffies when cache last updated */ | 40 | unsigned long last_read; /* jiffies when cache last updated */ |
41 | u8 cache[16]; | 41 | u8 cache[16]; |
42 | struct list_head sensors; | ||
42 | struct i2c_client *i2c; | 43 | struct i2c_client *i2c; |
43 | struct device_node *node; | 44 | struct device_node *node; |
44 | }; | 45 | }; |
@@ -46,11 +47,12 @@ struct wf_sat { | |||
46 | static struct wf_sat *sats[2]; | 47 | static struct wf_sat *sats[2]; |
47 | 48 | ||
48 | struct wf_sat_sensor { | 49 | struct wf_sat_sensor { |
49 | int index; | 50 | struct list_head link; |
50 | int index2; /* used for power sensors */ | 51 | int index; |
51 | int shift; | 52 | int index2; /* used for power sensors */ |
52 | struct wf_sat *sat; | 53 | int shift; |
53 | struct wf_sensor sens; | 54 | struct wf_sat *sat; |
55 | struct wf_sensor sens; | ||
54 | }; | 56 | }; |
55 | 57 | ||
56 | #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) | 58 | #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) | |||
142 | return 0; | 144 | return 0; |
143 | } | 145 | } |
144 | 146 | ||
145 | static int wf_sat_get(struct wf_sensor *sr, s32 *value) | 147 | static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value) |
146 | { | 148 | { |
147 | struct wf_sat_sensor *sens = wf_to_sat(sr); | 149 | struct wf_sat_sensor *sens = wf_to_sat(sr); |
148 | struct wf_sat *sat = sens->sat; | 150 | struct wf_sat *sat = sens->sat; |
@@ -175,62 +177,34 @@ static int wf_sat_get(struct wf_sensor *sr, s32 *value) | |||
175 | return err; | 177 | return err; |
176 | } | 178 | } |
177 | 179 | ||
178 | static void wf_sat_release(struct wf_sensor *sr) | 180 | static void wf_sat_release(struct kref *ref) |
181 | { | ||
182 | struct wf_sat *sat = container_of(ref, struct wf_sat, ref); | ||
183 | |||
184 | if (sat->nr >= 0) | ||
185 | sats[sat->nr] = NULL; | ||
186 | kfree(sat); | ||
187 | } | ||
188 | |||
189 | static void wf_sat_sensor_release(struct wf_sensor *sr) | ||
179 | { | 190 | { |
180 | struct wf_sat_sensor *sens = wf_to_sat(sr); | 191 | struct wf_sat_sensor *sens = wf_to_sat(sr); |
181 | struct wf_sat *sat = sens->sat; | 192 | struct wf_sat *sat = sens->sat; |
182 | 193 | ||
183 | if (atomic_dec_and_test(&sat->refcnt)) { | ||
184 | if (sat->nr >= 0) | ||
185 | sats[sat->nr] = NULL; | ||
186 | kfree(sat); | ||
187 | } | ||
188 | kfree(sens); | 194 | kfree(sens); |
195 | kref_put(&sat->ref, wf_sat_release); | ||
189 | } | 196 | } |
190 | 197 | ||
191 | static struct wf_sensor_ops wf_sat_ops = { | 198 | static struct wf_sensor_ops wf_sat_ops = { |
192 | .get_value = wf_sat_get, | 199 | .get_value = wf_sat_sensor_get, |
193 | .release = wf_sat_release, | 200 | .release = wf_sat_sensor_release, |
194 | .owner = THIS_MODULE, | 201 | .owner = THIS_MODULE, |
195 | }; | 202 | }; |
196 | 203 | ||
197 | static struct i2c_driver wf_sat_driver; | ||
198 | |||
199 | static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) | ||
200 | { | ||
201 | struct i2c_board_info info; | ||
202 | struct i2c_client *client; | ||
203 | const u32 *reg; | ||
204 | u8 addr; | ||
205 | |||
206 | reg = of_get_property(dev, "reg", NULL); | ||
207 | if (reg == NULL) | ||
208 | return; | ||
209 | addr = *reg; | ||
210 | DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); | ||
211 | |||
212 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
213 | info.addr = (addr >> 1) & 0x7f; | ||
214 | info.platform_data = dev; | ||
215 | strlcpy(info.type, "wf_sat", I2C_NAME_SIZE); | ||
216 | |||
217 | client = i2c_new_device(adapter, &info); | ||
218 | if (client == NULL) { | ||
219 | printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Let i2c-core delete that device on driver removal. | ||
225 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
226 | */ | ||
227 | list_add_tail(&client->detected, &wf_sat_driver.clients); | ||
228 | } | ||
229 | |||
230 | static int wf_sat_probe(struct i2c_client *client, | 204 | static int wf_sat_probe(struct i2c_client *client, |
231 | const struct i2c_device_id *id) | 205 | const struct i2c_device_id *id) |
232 | { | 206 | { |
233 | struct device_node *dev = client->dev.platform_data; | 207 | struct device_node *dev = client->dev.of_node; |
234 | struct wf_sat *sat; | 208 | struct wf_sat *sat; |
235 | struct wf_sat_sensor *sens; | 209 | struct wf_sat_sensor *sens; |
236 | const u32 *reg; | 210 | const u32 *reg; |
@@ -246,9 +220,10 @@ static int wf_sat_probe(struct i2c_client *client, | |||
246 | return -ENOMEM; | 220 | return -ENOMEM; |
247 | sat->nr = -1; | 221 | sat->nr = -1; |
248 | sat->node = of_node_get(dev); | 222 | sat->node = of_node_get(dev); |
249 | atomic_set(&sat->refcnt, 0); | 223 | kref_init(&sat->ref); |
250 | mutex_init(&sat->mutex); | 224 | mutex_init(&sat->mutex); |
251 | sat->i2c = client; | 225 | sat->i2c = client; |
226 | INIT_LIST_HEAD(&sat->sensors); | ||
252 | i2c_set_clientdata(client, sat); | 227 | i2c_set_clientdata(client, sat); |
253 | 228 | ||
254 | vsens[0] = vsens[1] = -1; | 229 | vsens[0] = vsens[1] = -1; |
@@ -310,14 +285,15 @@ static int wf_sat_probe(struct i2c_client *client, | |||
310 | sens->index2 = -1; | 285 | sens->index2 = -1; |
311 | sens->shift = shift; | 286 | sens->shift = shift; |
312 | sens->sat = sat; | 287 | sens->sat = sat; |
313 | atomic_inc(&sat->refcnt); | ||
314 | sens->sens.ops = &wf_sat_ops; | 288 | sens->sens.ops = &wf_sat_ops; |
315 | sens->sens.name = (char *) (sens + 1); | 289 | sens->sens.name = (char *) (sens + 1); |
316 | snprintf(sens->sens.name, 16, "%s-%d", name, cpu); | 290 | snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu); |
317 | 291 | ||
318 | if (wf_register_sensor(&sens->sens)) { | 292 | if (wf_register_sensor(&sens->sens)) |
319 | atomic_dec(&sat->refcnt); | ||
320 | kfree(sens); | 293 | kfree(sens); |
294 | else { | ||
295 | list_add(&sens->link, &sat->sensors); | ||
296 | kref_get(&sat->ref); | ||
321 | } | 297 | } |
322 | } | 298 | } |
323 | 299 | ||
@@ -336,14 +312,15 @@ static int wf_sat_probe(struct i2c_client *client, | |||
336 | sens->index2 = isens[core]; | 312 | sens->index2 = isens[core]; |
337 | sens->shift = 0; | 313 | sens->shift = 0; |
338 | sens->sat = sat; | 314 | sens->sat = sat; |
339 | atomic_inc(&sat->refcnt); | ||
340 | sens->sens.ops = &wf_sat_ops; | 315 | sens->sens.ops = &wf_sat_ops; |
341 | sens->sens.name = (char *) (sens + 1); | 316 | sens->sens.name = (char *) (sens + 1); |
342 | snprintf(sens->sens.name, 16, "cpu-power-%d", cpu); | 317 | snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu); |
343 | 318 | ||
344 | if (wf_register_sensor(&sens->sens)) { | 319 | if (wf_register_sensor(&sens->sens)) |
345 | atomic_dec(&sat->refcnt); | ||
346 | kfree(sens); | 320 | kfree(sens); |
321 | else { | ||
322 | list_add(&sens->link, &sat->sensors); | ||
323 | kref_get(&sat->ref); | ||
347 | } | 324 | } |
348 | } | 325 | } |
349 | 326 | ||
@@ -353,42 +330,35 @@ static int wf_sat_probe(struct i2c_client *client, | |||
353 | return 0; | 330 | return 0; |
354 | } | 331 | } |
355 | 332 | ||
356 | static int wf_sat_attach(struct i2c_adapter *adapter) | ||
357 | { | ||
358 | struct device_node *busnode, *dev = NULL; | ||
359 | struct pmac_i2c_bus *bus; | ||
360 | |||
361 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
362 | if (bus == NULL) | ||
363 | return -ENODEV; | ||
364 | busnode = pmac_i2c_get_bus_node(bus); | ||
365 | |||
366 | while ((dev = of_get_next_child(busnode, dev)) != NULL) | ||
367 | if (of_device_is_compatible(dev, "smu-sat")) | ||
368 | wf_sat_create(adapter, dev); | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int wf_sat_remove(struct i2c_client *client) | 333 | static int wf_sat_remove(struct i2c_client *client) |
373 | { | 334 | { |
374 | struct wf_sat *sat = i2c_get_clientdata(client); | 335 | struct wf_sat *sat = i2c_get_clientdata(client); |
336 | struct wf_sat_sensor *sens; | ||
375 | 337 | ||
376 | /* XXX TODO */ | 338 | /* release sensors */ |
377 | 339 | while(!list_empty(&sat->sensors)) { | |
340 | sens = list_first_entry(&sat->sensors, | ||
341 | struct wf_sat_sensor, link); | ||
342 | list_del(&sens->link); | ||
343 | wf_unregister_sensor(&sens->sens); | ||
344 | } | ||
378 | sat->i2c = NULL; | 345 | sat->i2c = NULL; |
346 | i2c_set_clientdata(client, NULL); | ||
347 | kref_put(&sat->ref, wf_sat_release); | ||
348 | |||
379 | return 0; | 349 | return 0; |
380 | } | 350 | } |
381 | 351 | ||
382 | static const struct i2c_device_id wf_sat_id[] = { | 352 | static const struct i2c_device_id wf_sat_id[] = { |
383 | { "wf_sat", 0 }, | 353 | { "MAC,smu-sat", 0 }, |
384 | { } | 354 | { } |
385 | }; | 355 | }; |
356 | MODULE_DEVICE_TABLE(i2c, wf_sat_id); | ||
386 | 357 | ||
387 | static struct i2c_driver wf_sat_driver = { | 358 | static struct i2c_driver wf_sat_driver = { |
388 | .driver = { | 359 | .driver = { |
389 | .name = "wf_smu_sat", | 360 | .name = "wf_smu_sat", |
390 | }, | 361 | }, |
391 | .attach_adapter = wf_sat_attach, | ||
392 | .probe = wf_sat_probe, | 362 | .probe = wf_sat_probe, |
393 | .remove = wf_sat_remove, | 363 | .remove = wf_sat_remove, |
394 | .id_table = wf_sat_id, | 364 | .id_table = wf_sat_id, |
@@ -399,15 +369,13 @@ static int __init sat_sensors_init(void) | |||
399 | return i2c_add_driver(&wf_sat_driver); | 369 | return i2c_add_driver(&wf_sat_driver); |
400 | } | 370 | } |
401 | 371 | ||
402 | #if 0 /* uncomment when module_exit() below is uncommented */ | ||
403 | static void __exit sat_sensors_exit(void) | 372 | static void __exit sat_sensors_exit(void) |
404 | { | 373 | { |
405 | i2c_del_driver(&wf_sat_driver); | 374 | i2c_del_driver(&wf_sat_driver); |
406 | } | 375 | } |
407 | #endif | ||
408 | 376 | ||
409 | module_init(sat_sensors_init); | 377 | module_init(sat_sensors_init); |
410 | /*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */ | 378 | module_exit(sat_sensors_exit); |
411 | 379 | ||
412 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); | 380 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); |
413 | MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); | 381 | MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); |
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index a409fa050a1a..93d0a8b7718a 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, | |||
338 | mutex_unlock(&ps3av->mutex); | 338 | mutex_unlock(&ps3av->mutex); |
339 | return 0; | 339 | return 0; |
340 | 340 | ||
341 | err: | 341 | err: |
342 | mutex_unlock(&ps3av->mutex); | 342 | mutex_unlock(&ps3av->mutex); |
343 | printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); | 343 | printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); |
344 | return res; | 344 | return res; |
@@ -477,7 +477,6 @@ int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) | |||
477 | 477 | ||
478 | return 0; | 478 | return 0; |
479 | } | 479 | } |
480 | |||
481 | EXPORT_SYMBOL_GPL(ps3av_set_audio_mode); | 480 | EXPORT_SYMBOL_GPL(ps3av_set_audio_mode); |
482 | 481 | ||
483 | static int ps3av_set_videomode(void) | 482 | static int ps3av_set_videomode(void) |
@@ -501,7 +500,7 @@ static void ps3av_set_videomode_packet(u32 id) | |||
501 | 500 | ||
502 | video_mode = &video_mode_table[id & PS3AV_MODE_MASK]; | 501 | video_mode = &video_mode_table[id & PS3AV_MODE_MASK]; |
503 | 502 | ||
504 | avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ | 503 | avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ |
505 | avb_param.num_of_audio_pkt = 0; | 504 | avb_param.num_of_audio_pkt = 0; |
506 | avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi + | 505 | avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi + |
507 | ps3av->av_hw_conf.num_of_avmulti; | 506 | ps3av->av_hw_conf.num_of_avmulti; |
@@ -521,7 +520,7 @@ static void ps3av_set_videomode_packet(u32 id) | |||
521 | #ifndef PS3AV_HDMI_YUV | 520 | #ifndef PS3AV_HDMI_YUV |
522 | if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || | 521 | if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || |
523 | ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) | 522 | ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) |
524 | av_video_cs = RGB8; /* use RGB for HDMI */ | 523 | av_video_cs = RGB8; /* use RGB for HDMI */ |
525 | #endif | 524 | #endif |
526 | len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], | 525 | len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], |
527 | ps3av->av_port[i], | 526 | ps3av->av_port[i], |
@@ -590,8 +589,8 @@ static void ps3avd(struct work_struct *work) | |||
590 | #define SHIFT_VESA 8 | 589 | #define SHIFT_VESA 8 |
591 | 590 | ||
592 | static const struct { | 591 | static const struct { |
593 | unsigned mask : 19; | 592 | unsigned mask:19; |
594 | unsigned id : 4; | 593 | unsigned id:4; |
595 | } ps3av_preferred_modes[] = { | 594 | } ps3av_preferred_modes[] = { |
596 | { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA }, | 595 | { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA }, |
597 | { PS3AV_RESBIT_1920x1080P << SHIFT_60, PS3AV_MODE_1080P60 }, | 596 | { 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) | |||
667 | return id; | 666 | return id; |
668 | } | 667 | } |
669 | 668 | ||
670 | static void ps3av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info *monitor_info) | 669 | static void ps3av_monitor_info_dump( |
670 | const struct ps3av_pkt_av_get_monitor_info *monitor_info) | ||
671 | { | 671 | { |
672 | const struct ps3av_info_monitor *info = &monitor_info->info; | 672 | const struct ps3av_info_monitor *info = &monitor_info->info; |
673 | const struct ps3av_info_audio *audio = info->audio; | 673 | 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 * | |||
717 | 717 | ||
718 | /* audio block */ | 718 | /* audio block */ |
719 | for (i = 0; i < info->num_of_audio_block; i++) { | 719 | for (i = 0; i < info->num_of_audio_block; i++) { |
720 | pr_debug("audio[%d] type: %02x max_ch: %02x fs: %02x sbit: " | 720 | pr_debug( |
721 | "%02x\n", | 721 | "audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n", |
722 | i, audio->type, audio->max_num_of_ch, audio->fs, | 722 | i, audio->type, audio->max_num_of_ch, audio->fs, |
723 | audio->sbit); | 723 | audio->sbit); |
724 | audio++; | 724 | audio++; |
@@ -870,21 +870,18 @@ int ps3av_set_video_mode(int id) | |||
870 | 870 | ||
871 | return 0; | 871 | return 0; |
872 | } | 872 | } |
873 | |||
874 | EXPORT_SYMBOL_GPL(ps3av_set_video_mode); | 873 | EXPORT_SYMBOL_GPL(ps3av_set_video_mode); |
875 | 874 | ||
876 | int ps3av_get_auto_mode(void) | 875 | int ps3av_get_auto_mode(void) |
877 | { | 876 | { |
878 | return ps3av_auto_videomode(&ps3av->av_hw_conf); | 877 | return ps3av_auto_videomode(&ps3av->av_hw_conf); |
879 | } | 878 | } |
880 | |||
881 | EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); | 879 | EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); |
882 | 880 | ||
883 | int ps3av_get_mode(void) | 881 | int ps3av_get_mode(void) |
884 | { | 882 | { |
885 | return ps3av ? ps3av->ps3av_mode : 0; | 883 | return ps3av ? ps3av->ps3av_mode : 0; |
886 | } | 884 | } |
887 | |||
888 | EXPORT_SYMBOL_GPL(ps3av_get_mode); | 885 | EXPORT_SYMBOL_GPL(ps3av_get_mode); |
889 | 886 | ||
890 | /* get resolution by video_mode */ | 887 | /* get resolution by video_mode */ |
@@ -902,7 +899,6 @@ int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres) | |||
902 | *yres = video_mode_table[id].y; | 899 | *yres = video_mode_table[id].y; |
903 | return 0; | 900 | return 0; |
904 | } | 901 | } |
905 | |||
906 | EXPORT_SYMBOL_GPL(ps3av_video_mode2res); | 902 | EXPORT_SYMBOL_GPL(ps3av_video_mode2res); |
907 | 903 | ||
908 | /* mute */ | 904 | /* mute */ |
@@ -911,7 +907,6 @@ int ps3av_video_mute(int mute) | |||
911 | return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON | 907 | return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON |
912 | : PS3AV_CMD_MUTE_OFF); | 908 | : PS3AV_CMD_MUTE_OFF); |
913 | } | 909 | } |
914 | |||
915 | EXPORT_SYMBOL_GPL(ps3av_video_mute); | 910 | EXPORT_SYMBOL_GPL(ps3av_video_mute); |
916 | 911 | ||
917 | /* mute analog output only */ | 912 | /* mute analog output only */ |
@@ -935,7 +930,6 @@ int ps3av_audio_mute(int mute) | |||
935 | return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON | 930 | return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON |
936 | : PS3AV_CMD_MUTE_OFF); | 931 | : PS3AV_CMD_MUTE_OFF); |
937 | } | 932 | } |
938 | |||
939 | EXPORT_SYMBOL_GPL(ps3av_audio_mute); | 933 | EXPORT_SYMBOL_GPL(ps3av_audio_mute); |
940 | 934 | ||
941 | static int __devinit ps3av_probe(struct ps3_system_bus_device *dev) | 935 | static int __devinit ps3av_probe(struct ps3_system_bus_device *dev) |