Эх сурвалжийг харах

Merge 'akpm' patch series

* Merge akpm patch series: (122 commits)
  drivers/connector/cn_proc.c: remove unused local
  Documentation/SubmitChecklist: add RCU debug config options
  reiserfs: use hweight_long()
  reiserfs: use proper little-endian bitops
  pnpacpi: register disabled resources
  drivers/rtc/rtc-tegra.c: properly initialize spinlock
  drivers/rtc/rtc-twl.c: check return value of twl_rtc_write_u8() in twl_rtc_set_time()
  drivers/rtc: add support for Qualcomm PMIC8xxx RTC
  drivers/rtc/rtc-s3c.c: support clock gating
  drivers/rtc/rtc-mpc5121.c: add support for RTC on MPC5200
  init: skip calibration delay if previously done
  misc/eeprom: add eeprom access driver for digsy_mtc board
  misc/eeprom: add driver for microwire 93xx46 EEPROMs
  checkpatch.pl: update $logFunctions
  checkpatch: make utf-8 test --strict
  checkpatch.pl: add ability to ignore various messages
  checkpatch: add a "prefer __aligned" check
  checkpatch: validate signature styles and To: and Cc: lines
  checkpatch: add __rcu as a sparse modifier
  checkpatch: suggest using min_t or max_t
  ...

Did this as a merge because of (trivial) conflicts in
 - Documentation/feature-removal-schedule.txt
 - arch/xtensa/include/asm/uaccess.h
that were just easier to fix up in the merge than in the patch series.
Linus Torvalds 14 жил өмнө
parent
commit
45b583b10a
100 өөрчлөгдсөн 3703 нэмэгдсэн , 263 устгасан
  1. 1 1
      Documentation/ABI/obsolete/proc-pid-oom_adj
  2. 21 0
      Documentation/ABI/testing/sysfs-bus-i2c-devices-fsa9480
  3. 2 2
      Documentation/SubmitChecklist
  4. 1 17
      Documentation/feature-removal-schedule.txt
  5. 1 1
      MAINTAINERS
  6. 0 1
      arch/alpha/kernel/process.c
  7. 83 1
      arch/arm/mach-ux500/board-u5500.c
  8. 6 4
      arch/cris/arch-v32/mach-a3/pinmux.c
  9. 0 1
      arch/cris/include/arch-v10/arch/processor.h
  10. 0 1
      arch/cris/include/arch-v32/arch/processor.h
  11. 0 2
      arch/h8300/include/asm/processor.h
  12. 0 1
      arch/m32r/include/asm/processor.h
  13. 0 4
      arch/m68k/include/asm/processor.h
  14. 1 1
      arch/m68k/kernel/process_mm.c
  15. 1 1
      arch/m68k/kernel/process_no.c
  16. 1 0
      arch/powerpc/kernel/rtas.c
  17. 1 0
      arch/powerpc/kernel/rtas_flash.c
  18. 1 4
      arch/sparc/Kconfig
  19. 59 17
      arch/sparc/include/asm/pgalloc_64.h
  20. 11 4
      arch/sparc/include/asm/pgtable_64.h
  21. 1 1
      arch/sparc/mm/Makefile
  22. 181 0
      arch/sparc/mm/gup.c
  23. 11 0
      arch/sparc/mm/tsb.c
  24. 2 0
      arch/um/Kconfig.x86
  25. 1 2
      arch/um/drivers/chan_kern.c
  26. 5 1
      arch/um/drivers/cow_user.c
  27. 1 1
      arch/um/drivers/harddog_user.c
  28. 1 2
      arch/um/drivers/line.c
  29. 12 0
      arch/um/drivers/net_kern.c
  30. 4 1
      arch/um/drivers/net_user.c
  31. 1 2
      arch/um/drivers/slip_user.c
  32. 4 6
      arch/um/include/asm/delay.h
  33. 0 1
      arch/um/kernel/exec.c
  34. 1 2
      arch/um/kernel/reboot.c
  35. 3 1
      arch/um/os-Linux/Makefile
  36. 1 6
      arch/um/os-Linux/elf_aux.c
  37. 2 2
      arch/um/os-Linux/helper.c
  38. 4 0
      arch/um/os-Linux/main.c
  39. 4 2
      arch/um/os-Linux/mem.c
  40. 5 0
      arch/um/os-Linux/user_syms.c
  41. 1 1
      arch/um/sys-i386/Makefile
  42. 2 0
      arch/um/sys-i386/asm/elf.h
  43. 45 14
      arch/um/sys-i386/delay.c
  44. 62 0
      arch/um/sys-i386/mem.c
  45. 3 1
      arch/um/sys-x86_64/Makefile
  46. 10 0
      arch/um/sys-x86_64/asm/elf.h
  47. 42 12
      arch/um/sys-x86_64/delay.c
  48. 21 11
      arch/um/sys-x86_64/mem.c
  49. 4 22
      arch/um/sys-x86_64/shared/sysdep/vm-flags.h
  50. 90 0
      arch/um/sys-x86_64/vdso/Makefile
  51. 10 0
      arch/um/sys-x86_64/vdso/checkundef.sh
  52. 71 0
      arch/um/sys-x86_64/vdso/um_vdso.c
  53. 64 0
      arch/um/sys-x86_64/vdso/vdso-layout.lds.S
  54. 12 0
      arch/um/sys-x86_64/vdso/vdso-note.S
  55. 10 0
      arch/um/sys-x86_64/vdso/vdso.S
  56. 32 0
      arch/um/sys-x86_64/vdso/vdso.lds.S
  57. 74 0
      arch/um/sys-x86_64/vdso/vma.c
  58. 1 0
      arch/xtensa/include/asm/uaccess.h
  59. 3 0
      arch/xtensa/kernel/ptrace.c
  60. 1 2
      drivers/ata/sata_dwc_460ex.c
  61. 0 1
      drivers/connector/cn_proc.c
  62. 2 2
      drivers/eisa/pci_eisa.c
  63. 3 0
      drivers/firmware/sigma.c
  64. 3 7
      drivers/gpu/drm/drm_scatter.c
  65. 1 2
      drivers/gpu/drm/radeon/radeon_mem.c
  66. 1 2
      drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
  67. 3 5
      drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
  68. 1 2
      drivers/gpu/vga/vgaarb.c
  69. 1 0
      drivers/leds/Kconfig
  70. 53 14
      drivers/leds/leds-lm3530.c
  71. 2 2
      drivers/leds/leds-lp5521.c
  72. 7 5
      drivers/leds/leds-sunfire.c
  73. 10 2
      drivers/misc/Kconfig
  74. 1 0
      drivers/misc/Makefile
  75. 25 0
      drivers/misc/eeprom/Kconfig
  76. 2 0
      drivers/misc/eeprom/Makefile
  77. 85 0
      drivers/misc/eeprom/digsy_mtc_eeprom.c
  78. 410 0
      drivers/misc/eeprom/eeprom_93xx46.c
  79. 557 0
      drivers/misc/fsa9480.c
  80. 4 1
      drivers/misc/pch_phub.c
  81. 1 0
      drivers/mmc/core/core.c
  82. 27 25
      drivers/pnp/pnpacpi/rsparser.c
  83. 12 2
      drivers/rtc/Kconfig
  84. 1 0
      drivers/rtc/Makefile
  85. 73 8
      drivers/rtc/rtc-mpc5121.c
  86. 550 0
      drivers/rtc/rtc-pm8xxx.c
  87. 26 1
      drivers/rtc/rtc-s3c.c
  88. 1 1
      drivers/rtc/rtc-tegra.c
  89. 1 1
      drivers/rtc/rtc-twl.c
  90. 8 0
      drivers/video/backlight/Kconfig
  91. 1 0
      drivers/video/backlight/Makefile
  92. 1 2
      drivers/video/backlight/adp8860_bl.c
  93. 646 0
      drivers/video/backlight/ams369fg06.c
  94. 12 5
      drivers/video/backlight/ld9040.c
  95. 7 4
      drivers/video/backlight/s6e63m0.c
  96. 30 0
      drivers/xen/Kconfig
  97. 137 2
      drivers/xen/balloon.c
  98. 1 1
      fs/hugetlbfs/inode.c
  99. 3 4
      fs/proc/base.c
  100. 2 6
      fs/reiserfs/bitmap.c

+ 1 - 1
Documentation/ABI/obsolete/proc-pid-oom_adj

@@ -14,7 +14,7 @@ Why:	/proc/<pid>/oom_adj allows userspace to influence the oom killer's
 
 
 	A much more powerful interface, /proc/<pid>/oom_score_adj, was
 	A much more powerful interface, /proc/<pid>/oom_score_adj, was
 	introduced with the oom killer rewrite that allows users to increase or
 	introduced with the oom killer rewrite that allows users to increase or
-	decrease the badness() score linearly.  This interface will replace
+	decrease the badness score linearly.  This interface will replace
 	/proc/<pid>/oom_adj.
 	/proc/<pid>/oom_adj.
 
 
 	A warning will be emitted to the kernel log if an application uses this
 	A warning will be emitted to the kernel log if an application uses this

+ 21 - 0
Documentation/ABI/testing/sysfs-bus-i2c-devices-fsa9480

@@ -0,0 +1,21 @@
+What:		/sys/bus/i2c/devices/.../device
+Date:		February 2011
+Contact:	Minkyu Kang <mk7.kang@samsung.com>
+Description:
+		show what device is attached
+		NONE - no device
+		USB - USB device is attached
+		UART - UART is attached
+		CHARGER - Charger is attaced
+		JIG - JIG is attached
+
+What:		/sys/bus/i2c/devices/.../switch
+Date:		February 2011
+Contact:	Minkyu Kang <mk7.kang@samsung.com>
+Description:
+		show or set the state of manual switch
+		VAUDIO - switch to VAUDIO path
+		UART - switch to UART path
+		AUDIO - switch to AUDIO path
+		DHOST - switch to DHOST path
+		AUTO - switch automatically by device

+ 2 - 2
Documentation/SubmitChecklist

@@ -53,8 +53,8 @@ kernel patches.
 
 
 12: Has been tested with CONFIG_PREEMPT, CONFIG_DEBUG_PREEMPT,
 12: Has been tested with CONFIG_PREEMPT, CONFIG_DEBUG_PREEMPT,
     CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES,
     CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES,
-    CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_ATOMIC_SLEEP all simultaneously
-    enabled.
+    CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_ATOMIC_SLEEP, CONFIG_PROVE_RCU
+    and CONFIG_DEBUG_OBJECTS_RCU_HEAD all simultaneously enabled.
 
 
 13: Has been build- and runtime tested with and without CONFIG_SMP and
 13: Has been build- and runtime tested with and without CONFIG_SMP and
     CONFIG_PREEMPT.
     CONFIG_PREEMPT.

+ 1 - 17
Documentation/feature-removal-schedule.txt

@@ -184,7 +184,7 @@ Why:	/proc/<pid>/oom_adj allows userspace to influence the oom killer's
 
 
 	A much more powerful interface, /proc/<pid>/oom_score_adj, was
 	A much more powerful interface, /proc/<pid>/oom_score_adj, was
 	introduced with the oom killer rewrite that allows users to increase or
 	introduced with the oom killer rewrite that allows users to increase or
-	decrease the badness() score linearly.  This interface will replace
+	decrease the badness score linearly.  This interface will replace
 	/proc/<pid>/oom_adj.
 	/proc/<pid>/oom_adj.
 
 
 	A warning will be emitted to the kernel log if an application uses this
 	A warning will be emitted to the kernel log if an application uses this
@@ -518,22 +518,6 @@ Files:	net/netfilter/xt_connlimit.c
 
 
 ----------------------------
 ----------------------------
 
 
-What:	noswapaccount kernel command line parameter
-When:	3.0
-Why:	The original implementation of memsw feature enabled by
-	CONFIG_CGROUP_MEM_RES_CTLR_SWAP could be disabled by the noswapaccount
-	kernel parameter (introduced in 2.6.29-rc1). Later on, this decision
-	turned out to be not ideal because we cannot have the feature compiled
-	in and disabled by default and let only interested to enable it
-	(e.g. general distribution kernels might need it). Therefore we have
-	added swapaccount[=0|1] parameter (introduced in 2.6.37) which provides
-	the both possibilities. If we remove noswapaccount we will have
-	less command line parameters with the same functionality and we
-	can also cleanup the parameter handling a bit ().
-Who:	Michal Hocko <mhocko@suse.cz>
-
-----------------------------
-
 What:	ipt_addrtype match include file
 What:	ipt_addrtype match include file
 When:	2012
 When:	2012
 Why:	superseded by xt_addrtype
 Why:	superseded by xt_addrtype

+ 1 - 1
MAINTAINERS

@@ -3012,7 +3012,7 @@ F:	kernel/hrtimer.c
 F:	kernel/time/clockevents.c
 F:	kernel/time/clockevents.c
 F:	kernel/time/tick*.*
 F:	kernel/time/tick*.*
 F:	kernel/time/timer_*.c
 F:	kernel/time/timer_*.c
-F:	include/linux/clockevents.h
+F:	include/linux/clockchips.h
 F:	include/linux/hrtimer.h
 F:	include/linux/hrtimer.h
 
 
 HIGH-SPEED SCC DRIVER FOR AX.25
 HIGH-SPEED SCC DRIVER FOR AX.25

+ 0 - 1
arch/alpha/kernel/process.c

@@ -200,7 +200,6 @@ show_regs(struct pt_regs *regs)
 void
 void
 start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
 start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
 {
 {
-	set_fs(USER_DS);
 	regs->pc = pc;
 	regs->pc = pc;
 	regs->ps = 8;
 	regs->ps = 8;
 	wrusp(sp);
 	wrusp(sp);

+ 83 - 1
arch/arm/mach-ux500/board-u5500.c

@@ -10,16 +10,97 @@
 #include <linux/amba/bus.h>
 #include <linux/amba/bus.h>
 #include <linux/gpio.h>
 #include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/irq.h>
+#include <linux/i2c.h>
 
 
 #include <asm/mach/arch.h>
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 #include <asm/mach-types.h>
 
 
+#include <plat/pincfg.h>
+#include <plat/i2c.h>
+
 #include <mach/hardware.h>
 #include <mach/hardware.h>
 #include <mach/devices.h>
 #include <mach/devices.h>
 #include <mach/setup.h>
 #include <mach/setup.h>
 
 
+#include "pins-db5500.h"
 #include "devices-db5500.h"
 #include "devices-db5500.h"
+#include <linux/led-lm3530.h>
+
+/*
+ * GPIO
+ */
+
+static pin_cfg_t u5500_pins[] = {
+	/* I2C */
+	GPIO218_I2C2_SCL        | PIN_INPUT_PULLUP,
+	GPIO219_I2C2_SDA        | PIN_INPUT_PULLUP,
+
+	/* DISPLAY_ENABLE */
+	GPIO226_GPIO        | PIN_OUTPUT_LOW,
+
+	/* Backlight Enbale */
+	GPIO224_GPIO        | PIN_OUTPUT_HIGH,
+};
+/*
+ * I2C
+ */
+
+#define U5500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, _sm) \
+static struct nmk_i2c_controller u5500_i2c##id##_data = { \
+	/*				\
+	 * slave data setup time, which is	\
+	 * 250 ns,100ns,10ns which is 14,6,2	\
+	 * respectively for a 48 Mhz	\
+	 * i2c clock			\
+	 */				\
+	.slsu		= _slsu,	\
+	/* Tx FIFO threshold */		\
+	.tft		= _tft,		\
+	/* Rx FIFO threshold */		\
+	.rft		= _rft,		\
+	/* std. mode operation */	\
+	.clk_freq	= clk,		\
+	.sm		= _sm,		\
+}
+/*
+ * The board uses TODO <3> i2c controllers, initialize all of
+ * them with slave data setup time of 250 ns,
+ * Tx & Rx FIFO threshold values as 1 and standard
+ * mode of operation
+ */
+
+U5500_I2C_CONTROLLER(2,	0xe, 1, 1, 400000, I2C_FREQ_MODE_FAST);
+
+static struct lm3530_platform_data u5500_als_platform_data = {
+	.mode = LM3530_BL_MODE_MANUAL,
+	.als_input_mode = LM3530_INPUT_ALS1,
+	.max_current = LM3530_FS_CURR_26mA,
+	.pwm_pol_hi = true,
+	.als_avrg_time = LM3530_ALS_AVRG_TIME_512ms,
+	.brt_ramp_law = 1,      /* Linear */
+	.brt_ramp_fall = LM3530_RAMP_TIME_8s,
+	.brt_ramp_rise = LM3530_RAMP_TIME_8s,
+	.als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm,
+	.als2_resistor_sel = LM3530_ALS_IMPD_Z,
+	.als_vmin = 730,	/* mV */
+	.als_vmax = 1020,	/* mV */
+	.brt_val = 0x7F,	/* Max brightness */
+};
 
 
+
+static struct i2c_board_info __initdata u5500_i2c2_devices[] = {
+	{
+		/* Backlight */
+		I2C_BOARD_INFO("lm3530-led", 0x36),
+		.platform_data = &u5500_als_platform_data,
+	},
+};
+
+static void __init u5500_i2c_init(void)
+{
+	db5500_add_i2c2(&u5500_i2c2_data);
+	i2c_register_board_info(2, ARRAY_AND_SIZE(u5500_i2c2_devices));
+}
 static void __init u5500_uart_init(void)
 static void __init u5500_uart_init(void)
 {
 {
 	db5500_add_uart0(NULL);
 	db5500_add_uart0(NULL);
@@ -30,7 +111,8 @@ static void __init u5500_uart_init(void)
 static void __init u5500_init_machine(void)
 static void __init u5500_init_machine(void)
 {
 {
 	u5500_init_devices();
 	u5500_init_devices();
-
+	nmk_config_pins(u5500_pins, ARRAY_SIZE(u5500_pins));
+	u5500_i2c_init();
 	u5500_sdi_init();
 	u5500_sdi_init();
 	u5500_uart_init();
 	u5500_uart_init();
 }
 }

+ 6 - 4
arch/cris/arch-v32/mach-a3/pinmux.c

@@ -85,6 +85,8 @@ crisv32_pinmux_alloc_fixed(enum fixed_function function)
 	int ret = -EINVAL;
 	int ret = -EINVAL;
 	char saved[sizeof pins];
 	char saved[sizeof pins];
 	unsigned long flags;
 	unsigned long flags;
+	reg_pinmux_rw_hwprot hwprot;
+	reg_clkgen_rw_clk_ctrl clk_ctrl;
 
 
 	spin_lock_irqsave(&pinmux_lock, flags);
 	spin_lock_irqsave(&pinmux_lock, flags);
 
 
@@ -93,9 +95,8 @@ crisv32_pinmux_alloc_fixed(enum fixed_function function)
 
 
 	crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
 	crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
 
 
-	reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
-	reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen,
-		rw_clk_ctrl);
+	hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
+	clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
 
 
 	switch (function) {
 	switch (function) {
 	case pinmux_eth:
 	case pinmux_eth:
@@ -262,6 +263,7 @@ crisv32_pinmux_dealloc_fixed(enum fixed_function function)
 	int ret = -EINVAL;
 	int ret = -EINVAL;
 	char saved[sizeof pins];
 	char saved[sizeof pins];
 	unsigned long flags;
 	unsigned long flags;
+	reg_pinmux_rw_hwprot hwprot;
 
 
 	spin_lock_irqsave(&pinmux_lock, flags);
 	spin_lock_irqsave(&pinmux_lock, flags);
 
 
@@ -270,7 +272,7 @@ crisv32_pinmux_dealloc_fixed(enum fixed_function function)
 
 
 	crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
 	crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
 
 
-	reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
+	hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
 
 
 	switch (function) {
 	switch (function) {
 	case pinmux_eth:
 	case pinmux_eth:

+ 0 - 1
arch/cris/include/arch-v10/arch/processor.h

@@ -53,7 +53,6 @@ struct thread_struct {
  */
  */
 
 
 #define start_thread(regs, ip, usp) do { \
 #define start_thread(regs, ip, usp) do { \
-	set_fs(USER_DS);      \
 	regs->irp = ip;       \
 	regs->irp = ip;       \
 	regs->dccr |= 1 << U_DCCR_BITNR; \
 	regs->dccr |= 1 << U_DCCR_BITNR; \
 	wrusp(usp);           \
 	wrusp(usp);           \

+ 0 - 1
arch/cris/include/arch-v32/arch/processor.h

@@ -47,7 +47,6 @@ struct thread_struct {
  */
  */
 #define start_thread(regs, ip, usp) \
 #define start_thread(regs, ip, usp) \
 do { \
 do { \
-	set_fs(USER_DS); \
 	regs->erp = ip; \
 	regs->erp = ip; \
 	regs->ccs |= 1 << (U_CCS_BITNR + CCS_SHIFT); \
 	regs->ccs |= 1 << (U_CCS_BITNR + CCS_SHIFT); \
 	wrusp(usp); \
 	wrusp(usp); \

+ 0 - 2
arch/h8300/include/asm/processor.h

@@ -81,7 +81,6 @@ struct thread_struct {
 #if defined(__H8300H__)
 #if defined(__H8300H__)
 #define start_thread(_regs, _pc, _usp)			        \
 #define start_thread(_regs, _pc, _usp)			        \
 do {							        \
 do {							        \
-	set_fs(USER_DS);           /* reads from user space */  \
   	(_regs)->pc = (_pc);				        \
   	(_regs)->pc = (_pc);				        \
 	(_regs)->ccr = 0x00;	   /* clear all flags */        \
 	(_regs)->ccr = 0x00;	   /* clear all flags */        \
 	(_regs)->er5 = current->mm->start_data;	/* GOT base */  \
 	(_regs)->er5 = current->mm->start_data;	/* GOT base */  \
@@ -91,7 +90,6 @@ do {							        \
 #if defined(__H8300S__)
 #if defined(__H8300S__)
 #define start_thread(_regs, _pc, _usp)			        \
 #define start_thread(_regs, _pc, _usp)			        \
 do {							        \
 do {							        \
-	set_fs(USER_DS);           /* reads from user space */  \
 	(_regs)->pc = (_pc);				        \
 	(_regs)->pc = (_pc);				        \
 	(_regs)->ccr = 0x00;	   /* clear kernel flag */      \
 	(_regs)->ccr = 0x00;	   /* clear kernel flag */      \
 	(_regs)->exr = 0x78;       /* enable all interrupts */  \
 	(_regs)->exr = 0x78;       /* enable all interrupts */  \

+ 0 - 1
arch/m32r/include/asm/processor.h

@@ -106,7 +106,6 @@ struct thread_struct {
 
 
 #define start_thread(regs, new_pc, new_spu) 				\
 #define start_thread(regs, new_pc, new_spu) 				\
 	do {								\
 	do {								\
-		set_fs(USER_DS); 					\
 		regs->psw = (regs->psw | USERPS_BPSW) & 0x0000FFFFUL;	\
 		regs->psw = (regs->psw | USERPS_BPSW) & 0x0000FFFFUL;	\
 		regs->bpc = new_pc;					\
 		regs->bpc = new_pc;					\
 		regs->spu = new_spu;					\
 		regs->spu = new_spu;					\

+ 0 - 4
arch/m68k/include/asm/processor.h

@@ -105,9 +105,6 @@ struct thread_struct {
 static inline void start_thread(struct pt_regs * regs, unsigned long pc,
 static inline void start_thread(struct pt_regs * regs, unsigned long pc,
 				unsigned long usp)
 				unsigned long usp)
 {
 {
-	/* reads from user space */
-	set_fs(USER_DS);
-
 	regs->pc = pc;
 	regs->pc = pc;
 	regs->sr &= ~0x2000;
 	regs->sr &= ~0x2000;
 	wrusp(usp);
 	wrusp(usp);
@@ -129,7 +126,6 @@ extern int handle_kernel_fault(struct pt_regs *regs);
 
 
 #define start_thread(_regs, _pc, _usp)                  \
 #define start_thread(_regs, _pc, _usp)                  \
 do {                                                    \
 do {                                                    \
-	set_fs(USER_DS); /* reads from user space */    \
 	(_regs)->pc = (_pc);                            \
 	(_regs)->pc = (_pc);                            \
 	((struct switch_stack *)(_regs))[-1].a6 = 0;    \
 	((struct switch_stack *)(_regs))[-1].a6 = 0;    \
 	reformat(_regs);                                \
 	reformat(_regs);                                \

+ 1 - 1
arch/m68k/kernel/process_mm.c

@@ -185,7 +185,7 @@ EXPORT_SYMBOL(kernel_thread);
 void flush_thread(void)
 void flush_thread(void)
 {
 {
 	unsigned long zero = 0;
 	unsigned long zero = 0;
-	set_fs(USER_DS);
+
 	current->thread.fs = __USER_DS;
 	current->thread.fs = __USER_DS;
 	if (!FPU_IS_EMU)
 	if (!FPU_IS_EMU)
 		asm volatile (".chip 68k/68881\n\t"
 		asm volatile (".chip 68k/68881\n\t"

+ 1 - 1
arch/m68k/kernel/process_no.c

@@ -158,7 +158,7 @@ void flush_thread(void)
 #ifdef CONFIG_FPU
 #ifdef CONFIG_FPU
 	unsigned long zero = 0;
 	unsigned long zero = 0;
 #endif
 #endif
-	set_fs(USER_DS);
+
 	current->thread.fs = __USER_DS;
 	current->thread.fs = __USER_DS;
 #ifdef CONFIG_FPU
 #ifdef CONFIG_FPU
 	if (!FPU_IS_EMU)
 	if (!FPU_IS_EMU)

+ 1 - 0
arch/powerpc/kernel/rtas.c

@@ -24,6 +24,7 @@
 #include <linux/cpumask.h>
 #include <linux/cpumask.h>
 #include <linux/memblock.h>
 #include <linux/memblock.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/reboot.h>
 
 
 #include <asm/prom.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>

+ 1 - 0
arch/powerpc/kernel/rtas_flash.c

@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/proc_fs.h>
 #include <linux/proc_fs.h>
+#include <linux/reboot.h>
 #include <asm/delay.h>
 #include <asm/delay.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <asm/rtas.h>
 #include <asm/rtas.h>

+ 1 - 4
arch/sparc/Kconfig

@@ -41,6 +41,7 @@ config SPARC64
 	select HAVE_FUNCTION_TRACE_MCOUNT_TEST
 	select HAVE_FUNCTION_TRACE_MCOUNT_TEST
 	select HAVE_KRETPROBES
 	select HAVE_KRETPROBES
 	select HAVE_KPROBES
 	select HAVE_KPROBES
+	select HAVE_RCU_TABLE_FREE if SMP
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK
 	select HAVE_SYSCALL_WRAPPERS
 	select HAVE_SYSCALL_WRAPPERS
 	select HAVE_DYNAMIC_FTRACE
 	select HAVE_DYNAMIC_FTRACE
@@ -81,10 +82,6 @@ config IOMMU_HELPER
 	bool
 	bool
 	default y if SPARC64
 	default y if SPARC64
 
 
-config QUICKLIST
-	bool
-	default y if SPARC64
-
 config STACKTRACE_SUPPORT
 config STACKTRACE_SUPPORT
 	bool
 	bool
 	default y if SPARC64
 	default y if SPARC64

+ 59 - 17
arch/sparc/include/asm/pgalloc_64.h

@@ -5,7 +5,6 @@
 #include <linux/sched.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/quicklist.h>
 
 
 #include <asm/spitfire.h>
 #include <asm/spitfire.h>
 #include <asm/cpudata.h>
 #include <asm/cpudata.h>
@@ -14,71 +13,114 @@
 
 
 /* Page table allocation/freeing. */
 /* Page table allocation/freeing. */
 
 
+extern struct kmem_cache *pgtable_cache;
+
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
 {
-	return quicklist_alloc(0, GFP_KERNEL, NULL);
+	return kmem_cache_alloc(pgtable_cache, GFP_KERNEL);
 }
 }
 
 
 static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 {
 {
-	quicklist_free(0, NULL, pgd);
+	kmem_cache_free(pgtable_cache, pgd);
 }
 }
 
 
 #define pud_populate(MM, PUD, PMD)	pud_set(PUD, PMD)
 #define pud_populate(MM, PUD, PMD)	pud_set(PUD, PMD)
 
 
 static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
 static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
 {
 {
-	return quicklist_alloc(0, GFP_KERNEL, NULL);
+	return kmem_cache_alloc(pgtable_cache,
+				GFP_KERNEL|__GFP_REPEAT);
 }
 }
 
 
 static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
 static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
 {
 {
-	quicklist_free(0, NULL, pmd);
+	kmem_cache_free(pgtable_cache, pmd);
 }
 }
 
 
 static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
 static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
 					  unsigned long address)
 					  unsigned long address)
 {
 {
-	return quicklist_alloc(0, GFP_KERNEL, NULL);
+	return (pte_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
 }
 }
 
 
 static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
 static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
 					unsigned long address)
 					unsigned long address)
 {
 {
 	struct page *page;
 	struct page *page;
-	void *pg;
+	pte_t *pte;
 
 
-	pg = quicklist_alloc(0, GFP_KERNEL, NULL);
-	if (!pg)
+	pte = pte_alloc_one_kernel(mm, address);
+	if (!pte)
 		return NULL;
 		return NULL;
-	page = virt_to_page(pg);
+	page = virt_to_page(pte);
 	pgtable_page_ctor(page);
 	pgtable_page_ctor(page);
 	return page;
 	return page;
 }
 }
 
 
 static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 {
 {
-	quicklist_free(0, NULL, pte);
+	free_page((unsigned long)pte);
 }
 }
 
 
 static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage)
 static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage)
 {
 {
 	pgtable_page_dtor(ptepage);
 	pgtable_page_dtor(ptepage);
-	quicklist_free_page(0, NULL, ptepage);
+	__free_page(ptepage);
 }
 }
 
 
-
 #define pmd_populate_kernel(MM, PMD, PTE)	pmd_set(PMD, PTE)
 #define pmd_populate_kernel(MM, PMD, PTE)	pmd_set(PMD, PTE)
 #define pmd_populate(MM,PMD,PTE_PAGE)		\
 #define pmd_populate(MM,PMD,PTE_PAGE)		\
 	pmd_populate_kernel(MM,PMD,page_address(PTE_PAGE))
 	pmd_populate_kernel(MM,PMD,page_address(PTE_PAGE))
 #define pmd_pgtable(pmd) pmd_page(pmd)
 #define pmd_pgtable(pmd) pmd_page(pmd)
 
 
-static inline void check_pgt_cache(void)
+#define check_pgt_cache()	do { } while (0)
+
+static inline void pgtable_free(void *table, bool is_page)
+{
+	if (is_page)
+		free_page((unsigned long)table);
+	else
+		kmem_cache_free(pgtable_cache, table);
+}
+
+#ifdef CONFIG_SMP
+
+struct mmu_gather;
+extern void tlb_remove_table(struct mmu_gather *, void *);
+
+static inline void pgtable_free_tlb(struct mmu_gather *tlb, void *table, bool is_page)
+{
+	unsigned long pgf = (unsigned long)table;
+	if (is_page)
+		pgf |= 0x1UL;
+	tlb_remove_table(tlb, (void *)pgf);
+}
+
+static inline void __tlb_remove_table(void *_table)
+{
+	void *table = (void *)((unsigned long)_table & ~0x1UL);
+	bool is_page = false;
+
+	if ((unsigned long)_table & 0x1UL)
+		is_page = true;
+	pgtable_free(table, is_page);
+}
+#else /* CONFIG_SMP */
+static inline void pgtable_free_tlb(struct mmu_gather *tlb, void *table, bool is_page)
 {
 {
-	quicklist_trim(0, NULL, 25, 16);
+	pgtable_free(table, is_page);
+}
+#endif /* !CONFIG_SMP */
+
+static inline void __pte_free_tlb(struct mmu_gather *tlb, struct page *ptepage,
+				  unsigned long address)
+{
+	pgtable_page_dtor(ptepage);
+	pgtable_free_tlb(tlb, page_address(ptepage), true);
 }
 }
 
 
-#define __pte_free_tlb(tlb, pte, addr)	pte_free((tlb)->mm, pte)
-#define __pmd_free_tlb(tlb, pmd, addr)	pmd_free((tlb)->mm, pmd)
+#define __pmd_free_tlb(tlb, pmd, addr)		      \
+	pgtable_free_tlb(tlb, pmd, false)
 
 
 #endif /* _SPARC64_PGALLOC_H */
 #endif /* _SPARC64_PGALLOC_H */

+ 11 - 4
arch/sparc/include/asm/pgtable_64.h

@@ -95,6 +95,10 @@
 /* PTE bits which are the same in SUN4U and SUN4V format.  */
 /* PTE bits which are the same in SUN4U and SUN4V format.  */
 #define _PAGE_VALID	  _AC(0x8000000000000000,UL) /* Valid TTE            */
 #define _PAGE_VALID	  _AC(0x8000000000000000,UL) /* Valid TTE            */
 #define _PAGE_R	  	  _AC(0x8000000000000000,UL) /* Keep ref bit uptodate*/
 #define _PAGE_R	  	  _AC(0x8000000000000000,UL) /* Keep ref bit uptodate*/
+#define _PAGE_SPECIAL     _AC(0x0200000000000000,UL) /* Special page         */
+
+/* Advertise support for _PAGE_SPECIAL */
+#define __HAVE_ARCH_PTE_SPECIAL
 
 
 /* SUN4U pte bits... */
 /* SUN4U pte bits... */
 #define _PAGE_SZ4MB_4U	  _AC(0x6000000000000000,UL) /* 4MB Page             */
 #define _PAGE_SZ4MB_4U	  _AC(0x6000000000000000,UL) /* 4MB Page             */
@@ -104,6 +108,7 @@
 #define _PAGE_NFO_4U	  _AC(0x1000000000000000,UL) /* No Fault Only        */
 #define _PAGE_NFO_4U	  _AC(0x1000000000000000,UL) /* No Fault Only        */
 #define _PAGE_IE_4U	  _AC(0x0800000000000000,UL) /* Invert Endianness    */
 #define _PAGE_IE_4U	  _AC(0x0800000000000000,UL) /* Invert Endianness    */
 #define _PAGE_SOFT2_4U	  _AC(0x07FC000000000000,UL) /* Software bits, set 2 */
 #define _PAGE_SOFT2_4U	  _AC(0x07FC000000000000,UL) /* Software bits, set 2 */
+#define _PAGE_SPECIAL_4U  _AC(0x0200000000000000,UL) /* Special page         */
 #define _PAGE_RES1_4U	  _AC(0x0002000000000000,UL) /* Reserved             */
 #define _PAGE_RES1_4U	  _AC(0x0002000000000000,UL) /* Reserved             */
 #define _PAGE_SZ32MB_4U	  _AC(0x0001000000000000,UL) /* (Panther) 32MB page  */
 #define _PAGE_SZ32MB_4U	  _AC(0x0001000000000000,UL) /* (Panther) 32MB page  */
 #define _PAGE_SZ256MB_4U  _AC(0x2001000000000000,UL) /* (Panther) 256MB page */
 #define _PAGE_SZ256MB_4U  _AC(0x2001000000000000,UL) /* (Panther) 256MB page */
@@ -133,6 +138,7 @@
 #define _PAGE_ACCESSED_4V _AC(0x1000000000000000,UL) /* Accessed (ref'd)     */
 #define _PAGE_ACCESSED_4V _AC(0x1000000000000000,UL) /* Accessed (ref'd)     */
 #define _PAGE_READ_4V	  _AC(0x0800000000000000,UL) /* Readable SW Bit      */
 #define _PAGE_READ_4V	  _AC(0x0800000000000000,UL) /* Readable SW Bit      */
 #define _PAGE_WRITE_4V	  _AC(0x0400000000000000,UL) /* Writable SW Bit      */
 #define _PAGE_WRITE_4V	  _AC(0x0400000000000000,UL) /* Writable SW Bit      */
+#define _PAGE_SPECIAL_4V  _AC(0x0200000000000000,UL) /* Special page         */
 #define _PAGE_PADDR_4V	  _AC(0x00FFFFFFFFFFE000,UL) /* paddr[55:13]         */
 #define _PAGE_PADDR_4V	  _AC(0x00FFFFFFFFFFE000,UL) /* paddr[55:13]         */
 #define _PAGE_IE_4V	  _AC(0x0000000000001000,UL) /* Invert Endianness    */
 #define _PAGE_IE_4V	  _AC(0x0000000000001000,UL) /* Invert Endianness    */
 #define _PAGE_E_4V	  _AC(0x0000000000000800,UL) /* side-Effect          */
 #define _PAGE_E_4V	  _AC(0x0000000000000800,UL) /* side-Effect          */
@@ -302,10 +308,10 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t prot)
 	: "=r" (mask), "=r" (tmp)
 	: "=r" (mask), "=r" (tmp)
 	: "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_4U |
 	: "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_4U |
 	       _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_PRESENT_4U |
 	       _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_PRESENT_4U |
-	       _PAGE_SZBITS_4U),
+	       _PAGE_SZBITS_4U | _PAGE_SPECIAL),
 	  "i" (_PAGE_PADDR_4V | _PAGE_MODIFIED_4V | _PAGE_ACCESSED_4V |
 	  "i" (_PAGE_PADDR_4V | _PAGE_MODIFIED_4V | _PAGE_ACCESSED_4V |
 	       _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_PRESENT_4V |
 	       _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_PRESENT_4V |
-	       _PAGE_SZBITS_4V));
+	       _PAGE_SZBITS_4V | _PAGE_SPECIAL));
 
 
 	return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask));
 	return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask));
 }
 }
@@ -502,6 +508,7 @@ static inline pte_t pte_mkyoung(pte_t pte)
 
 
 static inline pte_t pte_mkspecial(pte_t pte)
 static inline pte_t pte_mkspecial(pte_t pte)
 {
 {
+	pte_val(pte) |= _PAGE_SPECIAL;
 	return pte;
 	return pte;
 }
 }
 
 
@@ -607,9 +614,9 @@ static inline unsigned long pte_present(pte_t pte)
 	return val;
 	return val;
 }
 }
 
 
-static inline int pte_special(pte_t pte)
+static inline unsigned long pte_special(pte_t pte)
 {
 {
-	return 0;
+	return pte_val(pte) & _PAGE_SPECIAL;
 }
 }
 
 
 #define pmd_set(pmdp, ptep)	\
 #define pmd_set(pmdp, ptep)	\

+ 1 - 1
arch/sparc/mm/Makefile

@@ -4,7 +4,7 @@
 asflags-y := -ansi
 asflags-y := -ansi
 ccflags-y := -Werror
 ccflags-y := -Werror
 
 
-obj-$(CONFIG_SPARC64)   += ultra.o tlb.o tsb.o
+obj-$(CONFIG_SPARC64)   += ultra.o tlb.o tsb.o gup.o
 obj-y                   += fault_$(BITS).o
 obj-y                   += fault_$(BITS).o
 obj-y                   += init_$(BITS).o
 obj-y                   += init_$(BITS).o
 obj-$(CONFIG_SPARC32)   += loadmmu.o
 obj-$(CONFIG_SPARC32)   += loadmmu.o

+ 181 - 0
arch/sparc/mm/gup.c

@@ -0,0 +1,181 @@
+/*
+ * Lockless get_user_pages_fast for sparc, cribbed from powerpc
+ *
+ * Copyright (C) 2008 Nick Piggin
+ * Copyright (C) 2008 Novell Inc.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/vmstat.h>
+#include <linux/pagemap.h>
+#include <linux/rwsem.h>
+#include <asm/pgtable.h>
+
+/*
+ * The performance critical leaf functions are made noinline otherwise gcc
+ * inlines everything into a single function which results in too much
+ * register pressure.
+ */
+static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
+		unsigned long end, int write, struct page **pages, int *nr)
+{
+	unsigned long mask, result;
+	pte_t *ptep;
+
+	if (tlb_type == hypervisor) {
+		result = _PAGE_PRESENT_4V|_PAGE_P_4V;
+		if (write)
+			result |= _PAGE_WRITE_4V;
+	} else {
+		result = _PAGE_PRESENT_4U|_PAGE_P_4U;
+		if (write)
+			result |= _PAGE_WRITE_4U;
+	}
+	mask = result | _PAGE_SPECIAL;
+
+	ptep = pte_offset_kernel(&pmd, addr);
+	do {
+		struct page *page, *head;
+		pte_t pte = *ptep;
+
+		if ((pte_val(pte) & mask) != result)
+			return 0;
+		VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
+
+		/* The hugepage case is simplified on sparc64 because
+		 * we encode the sub-page pfn offsets into the
+		 * hugepage PTEs.  We could optimize this in the future
+		 * use page_cache_add_speculative() for the hugepage case.
+		 */
+		page = pte_page(pte);
+		head = compound_head(page);
+		if (!page_cache_get_speculative(head))
+			return 0;
+		if (unlikely(pte_val(pte) != pte_val(*ptep))) {
+			put_page(head);
+			return 0;
+		}
+
+		pages[*nr] = page;
+		(*nr)++;
+	} while (ptep++, addr += PAGE_SIZE, addr != end);
+
+	return 1;
+}
+
+static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
+		int write, struct page **pages, int *nr)
+{
+	unsigned long next;
+	pmd_t *pmdp;
+
+	pmdp = pmd_offset(&pud, addr);
+	do {
+		pmd_t pmd = *pmdp;
+
+		next = pmd_addr_end(addr, end);
+		if (pmd_none(pmd))
+			return 0;
+		if (!gup_pte_range(pmd, addr, next, write, pages, nr))
+			return 0;
+	} while (pmdp++, addr = next, addr != end);
+
+	return 1;
+}
+
+static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end,
+		int write, struct page **pages, int *nr)
+{
+	unsigned long next;
+	pud_t *pudp;
+
+	pudp = pud_offset(&pgd, addr);
+	do {
+		pud_t pud = *pudp;
+
+		next = pud_addr_end(addr, end);
+		if (pud_none(pud))
+			return 0;
+		if (!gup_pmd_range(pud, addr, next, write, pages, nr))
+			return 0;
+	} while (pudp++, addr = next, addr != end);
+
+	return 1;
+}
+
+int get_user_pages_fast(unsigned long start, int nr_pages, int write,
+			struct page **pages)
+{
+	struct mm_struct *mm = current->mm;
+	unsigned long addr, len, end;
+	unsigned long next;
+	pgd_t *pgdp;
+	int nr = 0;
+
+	start &= PAGE_MASK;
+	addr = start;
+	len = (unsigned long) nr_pages << PAGE_SHIFT;
+	end = start + len;
+
+	/*
+	 * XXX: batch / limit 'nr', to avoid large irq off latency
+	 * needs some instrumenting to determine the common sizes used by
+	 * important workloads (eg. DB2), and whether limiting the batch size
+	 * will decrease performance.
+	 *
+	 * It seems like we're in the clear for the moment. Direct-IO is
+	 * the main guy that batches up lots of get_user_pages, and even
+	 * they are limited to 64-at-a-time which is not so many.
+	 */
+	/*
+	 * This doesn't prevent pagetable teardown, but does prevent
+	 * the pagetables from being freed on sparc.
+	 *
+	 * So long as we atomically load page table pointers versus teardown,
+	 * we can follow the address down to the the page and take a ref on it.
+	 */
+	local_irq_disable();
+
+	pgdp = pgd_offset(mm, addr);
+	do {
+		pgd_t pgd = *pgdp;
+
+		next = pgd_addr_end(addr, end);
+		if (pgd_none(pgd))
+			goto slow;
+		if (!gup_pud_range(pgd, addr, next, write, pages, &nr))
+			goto slow;
+	} while (pgdp++, addr = next, addr != end);
+
+	local_irq_enable();
+
+	VM_BUG_ON(nr != (end - start) >> PAGE_SHIFT);
+	return nr;
+
+	{
+		int ret;
+
+slow:
+		local_irq_enable();
+
+		/* Try to get the remaining pages with get_user_pages */
+		start += nr << PAGE_SHIFT;
+		pages += nr;
+
+		down_read(&mm->mmap_sem);
+		ret = get_user_pages(current, mm, start,
+			(end - start) >> PAGE_SHIFT, write, 0, pages, NULL);
+		up_read(&mm->mmap_sem);
+
+		/* Have to be a bit careful with return values */
+		if (nr > 0) {
+			if (ret < 0)
+				ret = nr;
+			else
+				ret += nr;
+		}
+
+		return ret;
+	}
+}

+ 11 - 0
arch/sparc/mm/tsb.c

@@ -236,6 +236,8 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsign
 	}
 	}
 }
 }
 
 
+struct kmem_cache *pgtable_cache __read_mostly;
+
 static struct kmem_cache *tsb_caches[8] __read_mostly;
 static struct kmem_cache *tsb_caches[8] __read_mostly;
 
 
 static const char *tsb_cache_names[8] = {
 static const char *tsb_cache_names[8] = {
@@ -253,6 +255,15 @@ void __init pgtable_cache_init(void)
 {
 {
 	unsigned long i;
 	unsigned long i;
 
 
+	pgtable_cache = kmem_cache_create("pgtable_cache",
+					  PAGE_SIZE, PAGE_SIZE,
+					  0,
+					  _clear_page);
+	if (!pgtable_cache) {
+		prom_printf("pgtable_cache_init(): Could not create!\n");
+		prom_halt();
+	}
+
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
 		unsigned long size = 8192 << i;
 		unsigned long size = 8192 << i;
 		const char *name = tsb_cache_names[i];
 		const char *name = tsb_cache_names[i];

+ 2 - 0
arch/um/Kconfig.x86

@@ -1,3 +1,5 @@
+mainmenu "User Mode Linux/$SUBARCH $KERNELVERSION Kernel Configuration"
+
 source "arch/um/Kconfig.common"
 source "arch/um/Kconfig.common"
 
 
 menu "UML-specific options"
 menu "UML-specific options"

+ 1 - 2
arch/um/drivers/chan_kern.c

@@ -543,11 +543,10 @@ int parse_chan_pair(char *str, struct line *line, int device,
 		    const struct chan_opts *opts, char **error_out)
 		    const struct chan_opts *opts, char **error_out)
 {
 {
 	struct list_head *chans = &line->chan_list;
 	struct list_head *chans = &line->chan_list;
-	struct chan *new, *chan;
+	struct chan *new;
 	char *in, *out;
 	char *in, *out;
 
 
 	if (!list_empty(chans)) {
 	if (!list_empty(chans)) {
-		chan = list_entry(chans->next, struct chan, list);
 		free_chan(chans, 0);
 		free_chan(chans, 0);
 		INIT_LIST_HEAD(chans);
 		INIT_LIST_HEAD(chans);
 	}
 	}

+ 5 - 1
arch/um/drivers/cow_user.c

@@ -186,7 +186,11 @@ static int absolutize(char *to, int size, char *from)
 		strcat(to, "/");
 		strcat(to, "/");
 		strcat(to, from);
 		strcat(to, from);
 	}
 	}
-	chdir(save_cwd);
+	if (chdir(save_cwd)) {
+		cow_printf("absolutize : Can't cd to '%s' - "
+			   "errno = %d\n", save_cwd, errno);
+		return -1;
+	}
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 1
arch/um/drivers/harddog_user.c

@@ -32,7 +32,7 @@ int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock)
 {
 {
 	struct dog_data data;
 	struct dog_data data;
 	int in_fds[2], out_fds[2], pid, n, err;
 	int in_fds[2], out_fds[2], pid, n, err;
-	char pid_buf[sizeof("nnnnn\0")], c;
+	char pid_buf[sizeof("nnnnnnn\0")], c;
 	char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL };
 	char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL };
 	char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL,
 	char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL,
 				  NULL };
 				  NULL };

+ 1 - 2
arch/um/drivers/line.c

@@ -176,10 +176,9 @@ void line_flush_buffer(struct tty_struct *tty)
 {
 {
 	struct line *line = tty->driver_data;
 	struct line *line = tty->driver_data;
 	unsigned long flags;
 	unsigned long flags;
-	int err;
 
 
 	spin_lock_irqsave(&line->lock, flags);
 	spin_lock_irqsave(&line->lock, flags);
-	err = flush_buffer(line);
+	flush_buffer(line);
 	spin_unlock_irqrestore(&line->lock, flags);
 	spin_unlock_irqrestore(&line->lock, flags);
 }
 }
 
 

+ 12 - 0
arch/um/drivers/net_kern.c

@@ -262,6 +262,15 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void uml_net_poll_controller(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	uml_net_interrupt(dev->irq, dev);
+	enable_irq(dev->irq);
+}
+#endif
+
 static void uml_net_get_drvinfo(struct net_device *dev,
 static void uml_net_get_drvinfo(struct net_device *dev,
 				struct ethtool_drvinfo *info)
 				struct ethtool_drvinfo *info)
 {
 {
@@ -364,6 +373,9 @@ static const struct net_device_ops uml_netdev_ops = {
 	.ndo_set_mac_address	= eth_mac_addr,
 	.ndo_set_mac_address	= eth_mac_addr,
 	.ndo_change_mtu 	= uml_net_change_mtu,
 	.ndo_change_mtu 	= uml_net_change_mtu,
 	.ndo_validate_addr	= eth_validate_addr,
 	.ndo_validate_addr	= eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = uml_net_poll_controller,
+#endif
 };
 };
 
 
 /*
 /*

+ 4 - 1
arch/um/drivers/net_user.c

@@ -228,7 +228,10 @@ static void change(char *dev, char *what, unsigned char *addr,
 		       "buffer\n");
 		       "buffer\n");
 
 
 	pid = change_tramp(argv, output, output_len);
 	pid = change_tramp(argv, output, output_len);
-	if (pid < 0) return;
+	if (pid < 0) {
+		kfree(output);
+		return;
+	}
 
 
 	if (output != NULL) {
 	if (output != NULL) {
 		printk("%s", output);
 		printk("%s", output);

+ 1 - 2
arch/um/drivers/slip_user.c

@@ -102,7 +102,7 @@ static int slip_tramp(char **argv, int fd)
 		       "buffer\n");
 		       "buffer\n");
 		os_kill_process(pid, 1);
 		os_kill_process(pid, 1);
 		err = -ENOMEM;
 		err = -ENOMEM;
-		goto out_free;
+		goto out_close;
 	}
 	}
 
 
 	close(fds[1]);
 	close(fds[1]);
@@ -112,7 +112,6 @@ static int slip_tramp(char **argv, int fd)
 	err = helper_wait(pid);
 	err = helper_wait(pid);
 	close(fds[0]);
 	close(fds[0]);
 
 
-out_free:
 	kfree(output);
 	kfree(output);
 	return err;
 	return err;
 
 

+ 4 - 6
arch/um/include/asm/delay.h

@@ -1,20 +1,18 @@
 #ifndef __UM_DELAY_H
 #ifndef __UM_DELAY_H
 #define __UM_DELAY_H
 #define __UM_DELAY_H
 
 
-#define MILLION 1000000
-
 /* Undefined on purpose */
 /* Undefined on purpose */
 extern void __bad_udelay(void);
 extern void __bad_udelay(void);
+extern void __bad_ndelay(void);
 
 
 extern void __udelay(unsigned long usecs);
 extern void __udelay(unsigned long usecs);
+extern void __ndelay(unsigned long usecs);
 extern void __delay(unsigned long loops);
 extern void __delay(unsigned long loops);
 
 
 #define udelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
 #define udelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
 	__bad_udelay() : __udelay(n))
 	__bad_udelay() : __udelay(n))
 
 
-/* It appears that ndelay is not used at all for UML, and has never been
- * implemented. */
-extern void __unimplemented_ndelay(void);
-#define ndelay(n) __unimplemented_ndelay()
+#define ndelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
+	__bad_ndelay() : __ndelay(n))
 
 
 #endif
 #endif

+ 0 - 1
arch/um/kernel/exec.c

@@ -38,7 +38,6 @@ void flush_thread(void)
 
 
 void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
 void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
 {
 {
-	set_fs(USER_DS);
 	PT_REGS_IP(regs) = eip;
 	PT_REGS_IP(regs) = eip;
 	PT_REGS_SP(regs) = esp;
 	PT_REGS_SP(regs) = esp;
 }
 }

+ 1 - 2
arch/um/kernel/reboot.c

@@ -20,9 +20,8 @@ static void kill_off_processes(void)
 		os_kill_ptraced_process(userspace_pid[0], 1);
 		os_kill_ptraced_process(userspace_pid[0], 1);
 	else {
 	else {
 		struct task_struct *p;
 		struct task_struct *p;
-		int pid, me;
+		int pid;
 
 
-		me = os_getpid();
 		for_each_process(p) {
 		for_each_process(p) {
 			if (p->mm == NULL)
 			if (p->mm == NULL)
 				continue;
 				continue;

+ 3 - 1
arch/um/os-Linux/Makefile

@@ -3,10 +3,12 @@
 # Licensed under the GPL
 # Licensed under the GPL
 #
 #
 
 
-obj-y = aio.o elf_aux.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
+obj-y = aio.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
 	registers.o sigio.o signal.o start_up.o time.o tty.o uaccess.o \
 	registers.o sigio.o signal.o start_up.o time.o tty.o uaccess.o \
 	umid.o tls.o user_syms.o util.o drivers/ sys-$(SUBARCH)/ skas/
 	umid.o tls.o user_syms.o util.o drivers/ sys-$(SUBARCH)/ skas/
 
 
+obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o
+
 USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
 USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
 	main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
 	main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
 	tty.o tls.o uaccess.o umid.o util.o
 	tty.o tls.o uaccess.o umid.o util.o

+ 1 - 6
arch/um/os-Linux/elf_aux.c

@@ -14,16 +14,11 @@
 #include "mem_user.h"
 #include "mem_user.h"
 #include <kern_constants.h>
 #include <kern_constants.h>
 
 
-/* Use the one from the kernel - the host may miss it, if having old headers. */
-#if UM_ELF_CLASS == UM_ELFCLASS32
 typedef Elf32_auxv_t elf_auxv_t;
 typedef Elf32_auxv_t elf_auxv_t;
-#else
-typedef Elf64_auxv_t elf_auxv_t;
-#endif
 
 
 /* These are initialized very early in boot and never changed */
 /* These are initialized very early in boot and never changed */
 char * elf_aux_platform;
 char * elf_aux_platform;
-long elf_aux_hwcap;
+extern long elf_aux_hwcap;
 unsigned long vsyscall_ehdr;
 unsigned long vsyscall_ehdr;
 unsigned long vsyscall_end;
 unsigned long vsyscall_end;
 unsigned long __kernel_vsyscall;
 unsigned long __kernel_vsyscall;

+ 2 - 2
arch/um/os-Linux/helper.c

@@ -28,14 +28,14 @@ static int helper_child(void *arg)
 {
 {
 	struct helper_data *data = arg;
 	struct helper_data *data = arg;
 	char **argv = data->argv;
 	char **argv = data->argv;
-	int err;
+	int err, ret;
 
 
 	if (data->pre_exec != NULL)
 	if (data->pre_exec != NULL)
 		(*data->pre_exec)(data->pre_data);
 		(*data->pre_exec)(data->pre_data);
 	err = execvp_noalloc(data->buf, argv[0], argv);
 	err = execvp_noalloc(data->buf, argv[0], argv);
 
 
 	/* If the exec succeeds, we don't get here */
 	/* If the exec succeeds, we don't get here */
-	write(data->fd, &err, sizeof(err));
+	CATCH_EINTR(ret = write(data->fd, &err, sizeof(err)));
 
 
 	return 0;
 	return 0;
 }
 }

+ 4 - 0
arch/um/os-Linux/main.c

@@ -21,6 +21,8 @@
 #define STACKSIZE (8 * 1024 * 1024)
 #define STACKSIZE (8 * 1024 * 1024)
 #define THREAD_NAME_LEN (256)
 #define THREAD_NAME_LEN (256)
 
 
+long elf_aux_hwcap;
+
 static void set_stklim(void)
 static void set_stklim(void)
 {
 {
 	struct rlimit lim;
 	struct rlimit lim;
@@ -143,7 +145,9 @@ int __init main(int argc, char **argv, char **envp)
 	install_fatal_handler(SIGINT);
 	install_fatal_handler(SIGINT);
 	install_fatal_handler(SIGTERM);
 	install_fatal_handler(SIGTERM);
 
 
+#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
 	scan_elf_aux(envp);
 	scan_elf_aux(envp);
+#endif
 
 
 	do_uml_initcalls();
 	do_uml_initcalls();
 	ret = linux_main(argc, argv);
 	ret = linux_main(argc, argv);

+ 4 - 2
arch/um/os-Linux/mem.c

@@ -176,7 +176,7 @@ static int __init make_tempfile(const char *template, char **out_tempname,
 
 
 	find_tempdir();
 	find_tempdir();
 	if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
 	if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
-		return -1;
+		goto out;
 
 
 	if (template[0] != '/')
 	if (template[0] != '/')
 		strcpy(tempname, tempdir);
 		strcpy(tempname, tempdir);
@@ -191,13 +191,15 @@ static int __init make_tempfile(const char *template, char **out_tempname,
 	}
 	}
 	if (do_unlink && (unlink(tempname) < 0)) {
 	if (do_unlink && (unlink(tempname) < 0)) {
 		perror("unlink");
 		perror("unlink");
-		goto out;
+		goto close;
 	}
 	}
 	if (out_tempname) {
 	if (out_tempname) {
 		*out_tempname = tempname;
 		*out_tempname = tempname;
 	} else
 	} else
 		free(tempname);
 		free(tempname);
 	return fd;
 	return fd;
+close:
+	close(fd);
 out:
 out:
 	free(tempname);
 	free(tempname);
 	return -1;
 	return -1;

+ 5 - 0
arch/um/os-Linux/user_syms.c

@@ -113,3 +113,8 @@ EXPORT_SYMBOL(__stack_smash_handler);
 
 
 extern long __guard __attribute__((weak));
 extern long __guard __attribute__((weak));
 EXPORT_SYMBOL(__guard);
 EXPORT_SYMBOL(__guard);
+
+#ifdef _FORTIFY_SOURCE
+extern int __sprintf_chk(char *str, int flag, size_t strlen, const char *format);
+EXPORT_SYMBOL(__sprintf_chk);
+#endif

+ 1 - 1
arch/um/sys-i386/Makefile

@@ -4,7 +4,7 @@
 
 
 obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
 obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
 	ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \
 	ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \
-	sys_call_table.o tls.o atomic64_cx8_32.o
+	sys_call_table.o tls.o atomic64_cx8_32.o mem.o
 
 
 obj-$(CONFIG_BINFMT_ELF) += elfcore.o
 obj-$(CONFIG_BINFMT_ELF) += elfcore.o
 
 

+ 2 - 0
arch/um/sys-i386/asm/elf.h

@@ -105,6 +105,8 @@ extern unsigned long __kernel_vsyscall;
 #define FIXADDR_USER_START      VSYSCALL_BASE
 #define FIXADDR_USER_START      VSYSCALL_BASE
 #define FIXADDR_USER_END        VSYSCALL_END
 #define FIXADDR_USER_END        VSYSCALL_END
 
 
+#define __HAVE_ARCH_GATE_AREA 1
+
 /*
 /*
  * Architecture-neutral AT_ values in 0-17, leave some room
  * Architecture-neutral AT_ values in 0-17, leave some room
  * for more of them, start the x86-specific ones at 32.
  * for more of them, start the x86-specific ones at 32.

+ 45 - 14
arch/um/sys-i386/delay.c

@@ -1,29 +1,60 @@
+/*
+ * Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
+ * Mostly copied from arch/x86/lib/delay.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <asm/param.h>
 #include <asm/param.h>
 
 
-void __delay(unsigned long time)
+void __delay(unsigned long loops)
 {
 {
-	/* Stolen from the i386 __loop_delay */
-	int d0;
-	__asm__ __volatile__(
-		"\tjmp 1f\n"
+	asm volatile(
+		"test %0,%0\n"
+		"jz 3f\n"
+		"jmp 1f\n"
+
 		".align 16\n"
 		".align 16\n"
-		"1:\tjmp 2f\n"
+		"1: jmp 2f\n"
+
 		".align 16\n"
 		".align 16\n"
-		"2:\tdecl %0\n\tjns 2b"
-		:"=&a" (d0)
-		:"0" (time));
+		"2: dec %0\n"
+		" jnz 2b\n"
+		"3: dec %0\n"
+
+		: /* we don't need output */
+		: "a" (loops)
+	);
 }
 }
+EXPORT_SYMBOL(__delay);
 
 
-void __udelay(unsigned long usecs)
+inline void __const_udelay(unsigned long xloops)
 {
 {
-	int i, n;
+	int d0;
 
 
-	n = (loops_per_jiffy * HZ * usecs) / MILLION;
-        for(i=0;i<n;i++)
-                cpu_relax();
+	xloops *= 4;
+	asm("mull %%edx"
+		: "=d" (xloops), "=&a" (d0)
+		: "1" (xloops), "0"
+		(loops_per_jiffy * (HZ/4)));
+
+	__delay(++xloops);
 }
 }
+EXPORT_SYMBOL(__const_udelay);
 
 
+void __udelay(unsigned long usecs)
+{
+	__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
+}
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__udelay);
+
+void __ndelay(unsigned long nsecs)
+{
+	__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
+}
+EXPORT_SYMBOL(__ndelay);

+ 62 - 0
arch/um/sys-i386/mem.c

@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mm.h>
+#include <asm/page.h>
+#include <asm/mman.h>
+
+static struct vm_area_struct gate_vma;
+
+static int __init gate_vma_init(void)
+{
+	if (!FIXADDR_USER_START)
+		return 0;
+
+	gate_vma.vm_mm = NULL;
+	gate_vma.vm_start = FIXADDR_USER_START;
+	gate_vma.vm_end = FIXADDR_USER_END;
+	gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
+	gate_vma.vm_page_prot = __P101;
+
+	/*
+	 * Make sure the vDSO gets into every core dump.
+	 * Dumping its contents makes post-mortem fully interpretable later
+	 * without matching up the same kernel and hardware config to see
+	 * what PC values meant.
+	 */
+	gate_vma.vm_flags |= VM_ALWAYSDUMP;
+
+	return 0;
+}
+__initcall(gate_vma_init);
+
+struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+	return FIXADDR_USER_START ? &gate_vma : NULL;
+}
+
+int in_gate_area_no_mm(unsigned long addr)
+{
+	if (!FIXADDR_USER_START)
+		return 0;
+
+	if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
+		return 1;
+
+	return 0;
+}
+
+int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+	struct vm_area_struct *vma = get_gate_vma(mm);
+
+	if (!vma)
+		return 0;
+
+	return (addr >= vma->vm_start) && (addr < vma->vm_end);
+}

+ 3 - 1
arch/um/sys-x86_64/Makefile

@@ -4,10 +4,12 @@
 # Licensed under the GPL
 # Licensed under the GPL
 #
 #
 
 
-obj-y = bug.o bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \
+obj-y = bug.o bugs.o delay.o fault.o ldt.o ptrace.o ptrace_user.o mem.o \
 	setjmp.o signal.o stub.o stub_segv.o syscalls.o syscall_table.o \
 	setjmp.o signal.o stub.o stub_segv.o syscalls.o syscall_table.o \
 	sysrq.o ksyms.o tls.o
 	sysrq.o ksyms.o tls.o
 
 
+obj-y += vdso/
+
 subarch-obj-y = lib/csum-partial_64.o lib/memcpy_64.o lib/thunk_64.o \
 subarch-obj-y = lib/csum-partial_64.o lib/memcpy_64.o lib/thunk_64.o \
 		lib/rwsem.o
 		lib/rwsem.o
 subarch-obj-$(CONFIG_MODULES) += kernel/module.o
 subarch-obj-$(CONFIG_MODULES) += kernel/module.o

+ 10 - 0
arch/um/sys-x86_64/asm/elf.h

@@ -119,4 +119,14 @@ extern long elf_aux_hwcap;
 
 
 #define SET_PERSONALITY(ex) do ; while(0)
 #define SET_PERSONALITY(ex) do ; while(0)
 
 
+#define __HAVE_ARCH_GATE_AREA 1
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+struct linux_binprm;
+extern int arch_setup_additional_pages(struct linux_binprm *bprm,
+	int uses_interp);
+
+extern unsigned long um_vdso_addr;
+#define AT_SYSINFO_EHDR 33
+#define ARCH_DLINFO	NEW_AUX_ENT(AT_SYSINFO_EHDR, um_vdso_addr)
+
 #endif
 #endif

+ 42 - 12
arch/um/sys-x86_64/delay.c

@@ -1,30 +1,60 @@
 /*
 /*
- * Copyright 2003 PathScale, Inc.
- * Copied from arch/x86_64
+ * Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
+ * Mostly copied from arch/x86/lib/delay.c
  *
  *
- * Licensed under the GPL
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  */
  */
 
 
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
-#include <asm/processor.h>
 #include <asm/param.h>
 #include <asm/param.h>
 
 
 void __delay(unsigned long loops)
 void __delay(unsigned long loops)
 {
 {
-	unsigned long i;
+	asm volatile(
+		"test %0,%0\n"
+		"jz 3f\n"
+		"jmp 1f\n"
 
 
-        for(i = 0; i < loops; i++)
-                cpu_relax();
+		".align 16\n"
+		"1: jmp 2f\n"
+
+		".align 16\n"
+		"2: dec %0\n"
+		" jnz 2b\n"
+		"3: dec %0\n"
+
+		: /* we don't need output */
+		: "a" (loops)
+	);
 }
 }
+EXPORT_SYMBOL(__delay);
 
 
-void __udelay(unsigned long usecs)
+inline void __const_udelay(unsigned long xloops)
 {
 {
-	unsigned long i, n;
+	int d0;
 
 
-	n = (loops_per_jiffy * HZ * usecs) / MILLION;
-        for(i=0;i<n;i++)
-                cpu_relax();
+	xloops *= 4;
+	asm("mull %%edx"
+		: "=d" (xloops), "=&a" (d0)
+		: "1" (xloops), "0"
+		(loops_per_jiffy * (HZ/4)));
+
+	__delay(++xloops);
 }
 }
+EXPORT_SYMBOL(__const_udelay);
 
 
+void __udelay(unsigned long usecs)
+{
+	__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
+}
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__udelay);
+
+void __ndelay(unsigned long nsecs)
+{
+	__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
+}
+EXPORT_SYMBOL(__ndelay);

+ 21 - 11
arch/um/sys-x86_64/mem.c

@@ -1,16 +1,26 @@
-/*
- * Copyright 2003 PathScale, Inc.
- *
- * Licensed under the GPL
- */
-
 #include "linux/mm.h"
 #include "linux/mm.h"
 #include "asm/page.h"
 #include "asm/page.h"
 #include "asm/mman.h"
 #include "asm/mman.h"
 
 
-unsigned long vm_stack_flags = __VM_STACK_FLAGS;
-unsigned long vm_stack_flags32 = __VM_STACK_FLAGS;
-unsigned long vm_data_default_flags = __VM_DATA_DEFAULT_FLAGS;
-unsigned long vm_data_default_flags32 = __VM_DATA_DEFAULT_FLAGS;
-unsigned long vm_force_exec32 = PROT_EXEC;
+const char *arch_vma_name(struct vm_area_struct *vma)
+{
+	if (vma->vm_mm && vma->vm_start == um_vdso_addr)
+		return "[vdso]";
+
+	return NULL;
+}
+
+struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+	return NULL;
+}
+
+int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+	return 0;
+}
 
 
+int in_gate_area_no_mm(unsigned long addr)
+{
+	return 0;
+}

+ 4 - 22
arch/um/sys-x86_64/shared/sysdep/vm-flags.h

@@ -7,27 +7,9 @@
 #ifndef __VM_FLAGS_X86_64_H
 #ifndef __VM_FLAGS_X86_64_H
 #define __VM_FLAGS_X86_64_H
 #define __VM_FLAGS_X86_64_H
 
 
-#define __VM_DATA_DEFAULT_FLAGS	(VM_READ | VM_WRITE | VM_EXEC | \
-				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
-#define __VM_STACK_FLAGS 	(VM_GROWSDOWN | VM_READ | VM_WRITE | \
-				 VM_EXEC | VM_MAYREAD | VM_MAYWRITE | \
-				 VM_MAYEXEC)
-
-extern unsigned long vm_stack_flags, vm_stack_flags32;
-extern unsigned long vm_data_default_flags, vm_data_default_flags32;
-extern unsigned long vm_force_exec32;
-
-#ifdef TIF_IA32
-#define VM_DATA_DEFAULT_FLAGS \
-	(test_thread_flag(TIF_IA32) ? vm_data_default_flags32 : \
-	  vm_data_default_flags)
-
-#define VM_STACK_DEFAULT_FLAGS \
-	(test_thread_flag(TIF_IA32) ? vm_stack_flags32 : vm_stack_flags)
-#endif
-
-#define VM_DATA_DEFAULT_FLAGS vm_data_default_flags
-
-#define VM_STACK_DEFAULT_FLAGS vm_stack_flags
+#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
+	VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+#define VM_STACK_DEFAULT_FLAGS (VM_GROWSDOWN | VM_READ | VM_WRITE | \
+	VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
 
 
 #endif
 #endif

+ 90 - 0
arch/um/sys-x86_64/vdso/Makefile

@@ -0,0 +1,90 @@
+#
+# Building vDSO images for x86.
+#
+
+VDSO64-y		:= y
+
+vdso-install-$(VDSO64-y)	+= vdso.so
+
+
+# files to link into the vdso
+vobjs-y := vdso-note.o um_vdso.o
+
+# files to link into kernel
+obj-$(VDSO64-y)			+= vdso.o vma.o
+
+vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
+
+$(obj)/vdso.o: $(obj)/vdso.so
+
+targets += vdso.so vdso.so.dbg vdso.lds $(vobjs-y)
+
+export CPPFLAGS_vdso.lds += -P -C
+
+VDSO_LDFLAGS_vdso.lds = -m64 -Wl,-soname=linux-vdso.so.1 \
+       -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096
+
+$(obj)/vdso.o: $(src)/vdso.S $(obj)/vdso.so
+
+$(obj)/vdso.so.dbg: $(src)/vdso.lds $(vobjs) FORCE
+	$(call if_changed,vdso)
+
+$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: $(obj)/%.so.dbg FORCE
+	$(call if_changed,objcopy)
+
+#
+# Don't omit frame pointers for ease of userspace debugging, but do
+# optimize sibling calls.
+#
+CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \
+       $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector) \
+       -fno-omit-frame-pointer -foptimize-sibling-calls
+
+$(vobjs): KBUILD_CFLAGS += $(CFL)
+
+#
+# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
+#
+CFLAGS_REMOVE_vdso-note.o = -pg
+CFLAGS_REMOVE_um_vdso.o = -pg
+
+targets += vdso-syms.lds
+obj-$(VDSO64-y)			+= vdso-syms.lds
+
+#
+# Match symbols in the DSO that look like VDSO*; produce a file of constants.
+#
+sed-vdsosym := -e 's/^00*/0/' \
+	-e 's/^\([0-9a-fA-F]*\) . \(VDSO[a-zA-Z0-9_]*\)$$/\2 = 0x\1;/p'
+quiet_cmd_vdsosym = VDSOSYM $@
+define cmd_vdsosym
+	$(NM) $< | LC_ALL=C sed -n $(sed-vdsosym) | LC_ALL=C sort > $@
+endef
+
+$(obj)/%-syms.lds: $(obj)/%.so.dbg FORCE
+	$(call if_changed,vdsosym)
+
+#
+# The DSO images are built using a special linker script.
+#
+quiet_cmd_vdso = VDSO    $@
+      cmd_vdso = $(CC) -nostdlib -o $@ \
+		       $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \
+		       -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) && \
+		 sh $(srctree)/$(src)/checkundef.sh '$(NM)' '$@'
+
+VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+GCOV_PROFILE := n
+
+#
+# Install the unstripped copy of vdso*.so listed in $(vdso-install-y).
+#
+quiet_cmd_vdso_install = INSTALL $@
+      cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+$(vdso-install-y): %.so: $(obj)/%.so.dbg FORCE
+	@mkdir -p $(MODLIB)/vdso
+	$(call cmd,vdso_install)
+
+PHONY += vdso_install $(vdso-install-y)
+vdso_install: $(vdso-install-y)

+ 10 - 0
arch/um/sys-x86_64/vdso/checkundef.sh

@@ -0,0 +1,10 @@
+#!/bin/sh
+nm="$1"
+file="$2"
+$nm "$file" | grep '^ *U' > /dev/null 2>&1
+if [ $? -eq 1 ]; then
+    exit 0
+else
+    echo "$file: undefined symbols found" >&2
+    exit 1
+fi

+ 71 - 0
arch/um/sys-x86_64/vdso/um_vdso.c

@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This vDSO turns all calls into a syscall so that UML can trap them.
+ */
+
+
+/* Disable profiling for userspace code */
+#define DISABLE_BRANCH_PROFILING
+
+#include <linux/time.h>
+#include <linux/getcpu.h>
+#include <asm/unistd.h>
+
+int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
+{
+	long ret;
+
+	asm("syscall" : "=a" (ret) :
+		"0" (__NR_clock_gettime), "D" (clock), "S" (ts) : "memory");
+
+	return ret;
+}
+int clock_gettime(clockid_t, struct timespec *)
+	__attribute__((weak, alias("__vdso_clock_gettime")));
+
+int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+	long ret;
+
+	asm("syscall" : "=a" (ret) :
+		"0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory");
+
+	return ret;
+}
+int gettimeofday(struct timeval *, struct timezone *)
+	__attribute__((weak, alias("__vdso_gettimeofday")));
+
+time_t __vdso_time(time_t *t)
+{
+	long secs;
+
+	asm volatile("syscall"
+		: "=a" (secs)
+		: "0" (__NR_time), "D" (t) : "cc", "r11", "cx", "memory");
+
+	return secs;
+}
+int time(time_t *t) __attribute__((weak, alias("__vdso_time")));
+
+long
+__vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
+{
+	/*
+	 * UML does not support SMP, we can cheat here. :)
+	 */
+
+	if (cpu)
+		*cpu = 0;
+	if (node)
+		*node = 0;
+
+	return 0;
+}
+
+long getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
+	__attribute__((weak, alias("__vdso_getcpu")));

+ 64 - 0
arch/um/sys-x86_64/vdso/vdso-layout.lds.S

@@ -0,0 +1,64 @@
+/*
+ * Linker script for vDSO.  This is an ELF shared object prelinked to
+ * its virtual address, and with only one read-only segment.
+ * This script controls its layout.
+ */
+
+SECTIONS
+{
+	. = VDSO_PRELINK + SIZEOF_HEADERS;
+
+	.hash		: { *(.hash) }			:text
+	.gnu.hash	: { *(.gnu.hash) }
+	.dynsym		: { *(.dynsym) }
+	.dynstr		: { *(.dynstr) }
+	.gnu.version	: { *(.gnu.version) }
+	.gnu.version_d	: { *(.gnu.version_d) }
+	.gnu.version_r	: { *(.gnu.version_r) }
+
+	.note		: { *(.note.*) }		:text	:note
+
+	.eh_frame_hdr	: { *(.eh_frame_hdr) }		:text	:eh_frame_hdr
+	.eh_frame	: { KEEP (*(.eh_frame)) }	:text
+
+	.dynamic	: { *(.dynamic) }		:text	:dynamic
+
+	.rodata		: { *(.rodata*) }		:text
+	.data		: {
+	      *(.data*)
+	      *(.sdata*)
+	      *(.got.plt) *(.got)
+	      *(.gnu.linkonce.d.*)
+	      *(.bss*)
+	      *(.dynbss*)
+	      *(.gnu.linkonce.b.*)
+	}
+
+	.altinstructions	: { *(.altinstructions) }
+	.altinstr_replacement	: { *(.altinstr_replacement) }
+
+	/*
+	 * Align the actual code well away from the non-instruction data.
+	 * This is the best thing for the I-cache.
+	 */
+	. = ALIGN(0x100);
+
+	.text		: { *(.text*) }			:text	=0x90909090
+}
+
+/*
+ * Very old versions of ld do not recognize this name token; use the constant.
+ */
+#define PT_GNU_EH_FRAME	0x6474e550
+
+/*
+ * We must supply the ELF program headers explicitly to get just one
+ * PT_LOAD segment, and set the flags explicitly to make segments read-only.
+ */
+PHDRS
+{
+	text		PT_LOAD		FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+	dynamic		PT_DYNAMIC	FLAGS(4);		/* PF_R */
+	note		PT_NOTE		FLAGS(4);		/* PF_R */
+	eh_frame_hdr	PT_GNU_EH_FRAME;
+}

+ 12 - 0
arch/um/sys-x86_64/vdso/vdso-note.S

@@ -0,0 +1,12 @@
+/*
+ * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
+ * Here we can supply some information useful to userland.
+ */
+
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/elfnote.h>
+
+ELFNOTE_START(Linux, 0, "a")
+	.long LINUX_VERSION_CODE
+ELFNOTE_END

+ 10 - 0
arch/um/sys-x86_64/vdso/vdso.S

@@ -0,0 +1,10 @@
+#include <linux/init.h>
+
+__INITDATA
+
+	.globl vdso_start, vdso_end
+vdso_start:
+	.incbin "arch/um/sys-x86_64/vdso/vdso.so"
+vdso_end:
+
+__FINIT

+ 32 - 0
arch/um/sys-x86_64/vdso/vdso.lds.S

@@ -0,0 +1,32 @@
+/*
+ * Linker script for 64-bit vDSO.
+ * We #include the file to define the layout details.
+ * Here we only choose the prelinked virtual address.
+ *
+ * This file defines the version script giving the user-exported symbols in
+ * the DSO.  We can define local symbols here called VDSO* to make their
+ * values visible using the asm-x86/vdso.h macros from the kernel proper.
+ */
+
+#define VDSO_PRELINK 0xffffffffff700000
+#include "vdso-layout.lds.S"
+
+/*
+ * This controls what userland symbols we export from the vDSO.
+ */
+VERSION {
+	LINUX_2.6 {
+	global:
+		clock_gettime;
+		__vdso_clock_gettime;
+		gettimeofday;
+		__vdso_gettimeofday;
+		getcpu;
+		__vdso_getcpu;
+		time;
+		__vdso_time;
+	local: *;
+	};
+}
+
+VDSO64_PRELINK = VDSO_PRELINK;

+ 74 - 0
arch/um/sys-x86_64/vdso/vma.c

@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/page.h>
+#include <linux/init.h>
+
+unsigned int __read_mostly vdso_enabled = 1;
+unsigned long um_vdso_addr;
+
+extern unsigned long task_size;
+extern char vdso_start[], vdso_end[];
+
+static struct page **vdsop;
+
+static int __init init_vdso(void)
+{
+	struct page *um_vdso;
+
+	BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
+
+	um_vdso_addr = task_size - PAGE_SIZE;
+
+	vdsop = kmalloc(GFP_KERNEL, sizeof(struct page *));
+	if (!vdsop)
+		goto oom;
+
+	um_vdso = alloc_page(GFP_KERNEL);
+	if (!um_vdso) {
+		kfree(vdsop);
+
+		goto oom;
+	}
+
+	copy_page(page_address(um_vdso), vdso_start);
+	*vdsop = um_vdso;
+
+	return 0;
+
+oom:
+	printk(KERN_ERR "Cannot allocate vdso\n");
+	vdso_enabled = 0;
+
+	return -ENOMEM;
+}
+subsys_initcall(init_vdso);
+
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+{
+	int err;
+	struct mm_struct *mm = current->mm;
+
+	if (!vdso_enabled)
+		return 0;
+
+	down_write(&mm->mmap_sem);
+
+	err = install_special_mapping(mm, um_vdso_addr, PAGE_SIZE,
+		VM_READ|VM_EXEC|
+		VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC|
+		VM_ALWAYSDUMP,
+		vdsop);
+
+	up_write(&mm->mmap_sem);
+
+	return err;
+}

+ 1 - 0
arch/xtensa/include/asm/uaccess.h

@@ -17,6 +17,7 @@
 #define _XTENSA_UACCESS_H
 #define _XTENSA_UACCESS_H
 
 
 #include <linux/errno.h>
 #include <linux/errno.h>
+#include <linux/prefetch.h>
 #include <asm/types.h>
 #include <asm/types.h>
 
 
 #define VERIFY_READ    0
 #define VERIFY_READ    0

+ 3 - 0
arch/xtensa/kernel/ptrace.c

@@ -147,6 +147,9 @@ int ptrace_setxregs(struct task_struct *child, void __user *uregs)
 	elf_xtregs_t *xtregs = uregs;
 	elf_xtregs_t *xtregs = uregs;
 	int ret = 0;
 	int ret = 0;
 
 
+	if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
+		return -EFAULT;
+
 #if XTENSA_HAVE_COPROCESSORS
 #if XTENSA_HAVE_COPROCESSORS
 	/* Flush all coprocessors before we overwrite them. */
 	/* Flush all coprocessors before we overwrite them. */
 	coprocessor_flush_all(ti);
 	coprocessor_flush_all(ti);

+ 1 - 2
drivers/ata/sata_dwc_460ex.c

@@ -1642,13 +1642,12 @@ static int sata_dwc_probe(struct platform_device *ofdev)
 	const struct ata_port_info *ppi[] = { &pi, NULL };
 	const struct ata_port_info *ppi[] = { &pi, NULL };
 
 
 	/* Allocate DWC SATA device */
 	/* Allocate DWC SATA device */
-	hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
+	hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
 	if (hsdev == NULL) {
 	if (hsdev == NULL) {
 		dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
 		dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error;
 		goto error;
 	}
 	}
-	memset(hsdev, 0, sizeof(*hsdev));
 
 
 	/* Ioremap SATA registers */
 	/* Ioremap SATA registers */
 	base = of_iomap(ofdev->dev.of_node, 0);
 	base = of_iomap(ofdev->dev.of_node, 0);

+ 0 - 1
drivers/connector/cn_proc.c

@@ -173,7 +173,6 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
 	struct proc_event *ev;
 	struct proc_event *ev;
 	struct timespec ts;
 	struct timespec ts;
 	__u8 buffer[CN_PROC_MSG_SIZE];
 	__u8 buffer[CN_PROC_MSG_SIZE];
-	struct task_struct *tracer;
 
 
 	if (atomic_read(&proc_event_num_listeners) < 1)
 	if (atomic_read(&proc_event_num_listeners) < 1)
 		return;
 		return;

+ 2 - 2
drivers/eisa/pci_eisa.c

@@ -45,13 +45,13 @@ static int __init pci_eisa_init(struct pci_dev *pdev,
 	return 0;
 	return 0;
 }
 }
 
 
-static struct pci_device_id pci_eisa_pci_tbl[] = {
+static struct pci_device_id __initdata pci_eisa_pci_tbl[] = {
 	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
 	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
 	  PCI_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
 	  PCI_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
 	{ 0, }
 	{ 0, }
 };
 };
 
 
-static struct pci_driver pci_eisa_driver = {
+static struct pci_driver __initdata pci_eisa_driver = {
 	.name		= "pci_eisa",
 	.name		= "pci_eisa",
 	.id_table	= pci_eisa_pci_tbl,
 	.id_table	= pci_eisa_pci_tbl,
 	.probe		= pci_eisa_init,
 	.probe		= pci_eisa_init,

+ 3 - 0
drivers/firmware/sigma.c

@@ -11,6 +11,7 @@
 #include <linux/firmware.h>
 #include <linux/firmware.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/i2c.h>
 #include <linux/i2c.h>
+#include <linux/module.h>
 #include <linux/sigma.h>
 #include <linux/sigma.h>
 
 
 /* Return: 0==OK, <0==error, =1 ==no more actions */
 /* Return: 0==OK, <0==error, =1 ==no more actions */
@@ -113,3 +114,5 @@ int process_sigma_firmware(struct i2c_client *client, const char *name)
 	return ret;
 	return ret;
 }
 }
 EXPORT_SYMBOL(process_sigma_firmware);
 EXPORT_SYMBOL(process_sigma_firmware);
+
+MODULE_LICENSE("GPL");

+ 3 - 7
drivers/gpu/drm/drm_scatter.c

@@ -83,30 +83,26 @@ int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request)
 	if (dev->sg)
 	if (dev->sg)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 	if (!entry)
 	if (!entry)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(entry, 0, sizeof(*entry));
 	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
 	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
 	DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
 	DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
 
 
 	entry->pages = pages;
 	entry->pages = pages;
-	entry->pagelist = kmalloc(pages * sizeof(*entry->pagelist), GFP_KERNEL);
+	entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL);
 	if (!entry->pagelist) {
 	if (!entry->pagelist) {
 		kfree(entry);
 		kfree(entry);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	memset(entry->pagelist, 0, pages * sizeof(*entry->pagelist));
-
-	entry->busaddr = kmalloc(pages * sizeof(*entry->busaddr), GFP_KERNEL);
+	entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
 	if (!entry->busaddr) {
 	if (!entry->busaddr) {
 		kfree(entry->pagelist);
 		kfree(entry->pagelist);
 		kfree(entry);
 		kfree(entry);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
-	memset((void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr));
 
 
 	entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
 	entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
 	if (!entry->virtual) {
 	if (!entry->virtual) {

+ 1 - 2
drivers/gpu/drm/radeon/radeon_mem.c

@@ -139,7 +139,7 @@ static int init_heap(struct mem_block **heap, int start, int size)
 	if (!blocks)
 	if (!blocks)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
+	*heap = kzalloc(sizeof(**heap), GFP_KERNEL);
 	if (!*heap) {
 	if (!*heap) {
 		kfree(blocks);
 		kfree(blocks);
 		return -ENOMEM;
 		return -ENOMEM;
@@ -150,7 +150,6 @@ static int init_heap(struct mem_block **heap, int start, int size)
 	blocks->file_priv = NULL;
 	blocks->file_priv = NULL;
 	blocks->next = blocks->prev = *heap;
 	blocks->next = blocks->prev = *heap;
 
 
-	memset(*heap, 0, sizeof(**heap));
 	(*heap)->file_priv = (struct drm_file *) - 1;
 	(*heap)->file_priv = (struct drm_file *) - 1;
 	(*heap)->next = (*heap)->prev = blocks;
 	(*heap)->next = (*heap)->prev = blocks;
 	return 0;
 	return 0;

+ 1 - 2
drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c

@@ -585,11 +585,10 @@ int vmw_overlay_init(struct vmw_private *dev_priv)
 		return -ENOSYS;
 		return -ENOSYS;
 	}
 	}
 
 
-	overlay = kmalloc(sizeof(*overlay), GFP_KERNEL);
+	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
 	if (!overlay)
 	if (!overlay)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memset(overlay, 0, sizeof(*overlay));
 	mutex_init(&overlay->mutex);
 	mutex_init(&overlay->mutex);
 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
 	for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
 		overlay->stream[i].buf = NULL;
 		overlay->stream[i].buf = NULL;

+ 3 - 5
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c

@@ -612,11 +612,9 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
 	    srf->sizes[0].height == 64 &&
 	    srf->sizes[0].height == 64 &&
 	    srf->format == SVGA3D_A8R8G8B8) {
 	    srf->format == SVGA3D_A8R8G8B8) {
 
 
-		srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
-		/* clear the image */
-		if (srf->snooper.image) {
-			memset(srf->snooper.image, 0x00, 64 * 64 * 4);
-		} else {
+		/* allocate image area and clear it */
+		srf->snooper.image = kzalloc(64 * 64 * 4, GFP_KERNEL);
+		if (!srf->snooper.image) {
 			DRM_ERROR("Failed to allocate cursor_image\n");
 			DRM_ERROR("Failed to allocate cursor_image\n");
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 			goto out_err1;
 			goto out_err1;

+ 1 - 2
drivers/gpu/vga/vgaarb.c

@@ -1171,10 +1171,9 @@ static int vga_arb_open(struct inode *inode, struct file *file)
 
 
 	pr_debug("%s\n", __func__);
 	pr_debug("%s\n", __func__);
 
 
-	priv = kmalloc(sizeof(struct vga_arb_private), GFP_KERNEL);
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (priv == NULL)
 	if (priv == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
-	memset(priv, 0, sizeof(*priv));
 	spin_lock_init(&priv->lock);
 	spin_lock_init(&priv->lock);
 	file->private_data = priv;
 	file->private_data = priv;
 
 

+ 1 - 0
drivers/leds/Kconfig

@@ -365,6 +365,7 @@ config LEDS_NS2
 config LEDS_NETXBIG
 config LEDS_NETXBIG
 	tristate "LED support for Big Network series LEDs"
 	tristate "LED support for Big Network series LEDs"
 	depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
 	depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
+	depends on LEDS_CLASS
 	default y
 	default y
 	help
 	help
 	  This option enable support for LEDs found on the LaCie 2Big
 	  This option enable support for LEDs found on the LaCie 2Big

+ 53 - 14
drivers/leds/leds-lm3530.c

@@ -68,17 +68,16 @@
 #define LM3530_ALS2_IMP_SHIFT		(4)
 #define LM3530_ALS2_IMP_SHIFT		(4)
 
 
 /* Zone Boundary Register defaults */
 /* Zone Boundary Register defaults */
-#define LM3530_DEF_ZB_0			(0x33)
-#define LM3530_DEF_ZB_1			(0x66)
-#define LM3530_DEF_ZB_2			(0x99)
-#define LM3530_DEF_ZB_3			(0xCC)
+#define LM3530_ALS_ZB_MAX		(4)
+#define LM3530_ALS_WINDOW_mV		(1000)
+#define LM3530_ALS_OFFSET_mV		(4)
 
 
 /* Zone Target Register defaults */
 /* Zone Target Register defaults */
-#define LM3530_DEF_ZT_0			(0x19)
-#define LM3530_DEF_ZT_1			(0x33)
+#define LM3530_DEF_ZT_0			(0x7F)
+#define LM3530_DEF_ZT_1			(0x66)
 #define LM3530_DEF_ZT_2			(0x4C)
 #define LM3530_DEF_ZT_2			(0x4C)
-#define LM3530_DEF_ZT_3			(0x66)
-#define LM3530_DEF_ZT_4			(0x7F)
+#define LM3530_DEF_ZT_3			(0x33)
+#define LM3530_DEF_ZT_4			(0x19)
 
 
 struct lm3530_mode_map {
 struct lm3530_mode_map {
 	const char *mode;
 	const char *mode;
@@ -150,6 +149,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
 	u8 als_imp_sel = 0;
 	u8 als_imp_sel = 0;
 	u8 brightness;
 	u8 brightness;
 	u8 reg_val[LM3530_REG_MAX];
 	u8 reg_val[LM3530_REG_MAX];
+	u8 zones[LM3530_ALS_ZB_MAX];
+	u32 als_vmin, als_vmax, als_vstep;
 	struct lm3530_platform_data *pltfm = drvdata->pdata;
 	struct lm3530_platform_data *pltfm = drvdata->pdata;
 	struct i2c_client *client = drvdata->client;
 	struct i2c_client *client = drvdata->client;
 
 
@@ -161,6 +162,26 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
 		gen_config |= (LM3530_ENABLE_I2C);
 		gen_config |= (LM3530_ENABLE_I2C);
 
 
 	if (drvdata->mode == LM3530_BL_MODE_ALS) {
 	if (drvdata->mode == LM3530_BL_MODE_ALS) {
+		if (pltfm->als_vmax == 0) {
+			pltfm->als_vmin = als_vmin = 0;
+			pltfm->als_vmin = als_vmax = LM3530_ALS_WINDOW_mV;
+		}
+
+		als_vmin = pltfm->als_vmin;
+		als_vmax = pltfm->als_vmax;
+
+		if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
+			pltfm->als_vmax = als_vmax =
+				als_vmin + LM3530_ALS_WINDOW_mV;
+
+		/* n zone boundary makes n+1 zones */
+		als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
+
+		for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
+			zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
+					als_vstep + (i * als_vstep)) * LED_FULL)
+					/ 1000;
+
 		als_config =
 		als_config =
 			(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
 			(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
 			(LM3530_ENABLE_ALS) |
 			(LM3530_ENABLE_ALS) |
@@ -169,6 +190,7 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
 		als_imp_sel =
 		als_imp_sel =
 			(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
 			(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
 			(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
 			(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
+
 	}
 	}
 
 
 	if (drvdata->mode == LM3530_BL_MODE_PWM)
 	if (drvdata->mode == LM3530_BL_MODE_PWM)
@@ -190,10 +212,10 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
 	reg_val[3] = 0x00;		/* LM3530_ALS_ZONE_REG */
 	reg_val[3] = 0x00;		/* LM3530_ALS_ZONE_REG */
 	reg_val[4] = als_imp_sel;	/* LM3530_ALS_IMP_SELECT */
 	reg_val[4] = als_imp_sel;	/* LM3530_ALS_IMP_SELECT */
 	reg_val[5] = brightness;	/* LM3530_BRT_CTRL_REG */
 	reg_val[5] = brightness;	/* LM3530_BRT_CTRL_REG */
-	reg_val[6] = LM3530_DEF_ZB_0;	/* LM3530_ALS_ZB0_REG */
-	reg_val[7] = LM3530_DEF_ZB_1;	/* LM3530_ALS_ZB1_REG */
-	reg_val[8] = LM3530_DEF_ZB_2;	/* LM3530_ALS_ZB2_REG */
-	reg_val[9] = LM3530_DEF_ZB_3;	/* LM3530_ALS_ZB3_REG */
+	reg_val[6] = zones[0];		/* LM3530_ALS_ZB0_REG */
+	reg_val[7] = zones[1];		/* LM3530_ALS_ZB1_REG */
+	reg_val[8] = zones[2];		/* LM3530_ALS_ZB2_REG */
+	reg_val[9] = zones[3];		/* LM3530_ALS_ZB3_REG */
 	reg_val[10] = LM3530_DEF_ZT_0;	/* LM3530_ALS_Z0T_REG */
 	reg_val[10] = LM3530_DEF_ZT_0;	/* LM3530_ALS_Z0T_REG */
 	reg_val[11] = LM3530_DEF_ZT_1;	/* LM3530_ALS_Z1T_REG */
 	reg_val[11] = LM3530_DEF_ZT_1;	/* LM3530_ALS_Z1T_REG */
 	reg_val[12] = LM3530_DEF_ZT_2;	/* LM3530_ALS_Z2T_REG */
 	reg_val[12] = LM3530_DEF_ZT_2;	/* LM3530_ALS_Z2T_REG */
@@ -265,6 +287,24 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
 	}
 	}
 }
 }
 
 
+static ssize_t lm3530_mode_get(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = container_of(
+					dev->parent, struct i2c_client, dev);
+	struct lm3530_data *drvdata = i2c_get_clientdata(client);
+	int i, len = 0;
+
+	for (i = 0; i < ARRAY_SIZE(mode_map); i++)
+		if (drvdata->mode == mode_map[i].mode_val)
+			len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
+		else
+			len += sprintf(buf + len, "%s ", mode_map[i].mode);
+
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
 
 
 static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
 static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
 				   *attr, const char *buf, size_t size)
 				   *attr, const char *buf, size_t size)
@@ -298,8 +338,7 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
 
 
 	return sizeof(drvdata->mode);
 	return sizeof(drvdata->mode);
 }
 }
-
-static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
+static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
 
 
 static int __devinit lm3530_probe(struct i2c_client *client,
 static int __devinit lm3530_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 			   const struct i2c_device_id *id)

+ 2 - 2
drivers/leds/leds-lp5521.c

@@ -744,7 +744,7 @@ fail1:
 	return ret;
 	return ret;
 }
 }
 
 
-static int lp5521_remove(struct i2c_client *client)
+static int __devexit lp5521_remove(struct i2c_client *client)
 {
 {
 	struct lp5521_chip *chip = i2c_get_clientdata(client);
 	struct lp5521_chip *chip = i2c_get_clientdata(client);
 	int i;
 	int i;
@@ -775,7 +775,7 @@ static struct i2c_driver lp5521_driver = {
 		.name	= "lp5521",
 		.name	= "lp5521",
 	},
 	},
 	.probe		= lp5521_probe,
 	.probe		= lp5521_probe,
-	.remove		= lp5521_remove,
+	.remove		= __devexit_p(lp5521_remove),
 	.id_table	= lp5521_id,
 	.id_table	= lp5521_id,
 };
 };
 
 

+ 7 - 5
drivers/leds/leds-sunfire.c

@@ -127,17 +127,19 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
 					       struct led_type *types)
 					       struct led_type *types)
 {
 {
 	struct sunfire_drvdata *p;
 	struct sunfire_drvdata *p;
-	int i, err = -EINVAL;
+	int i, err;
 
 
 	if (pdev->num_resources != 1) {
 	if (pdev->num_resources != 1) {
 		printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
 		printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
 		       pdev->num_resources);
 		       pdev->num_resources);
+		err = -EINVAL;
 		goto out;
 		goto out;
 	}
 	}
 
 
 	p = kzalloc(sizeof(*p), GFP_KERNEL);
 	p = kzalloc(sizeof(*p), GFP_KERNEL);
 	if (!p) {
 	if (!p) {
 		printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
 		printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
+		err = -ENOMEM;
 		goto out;
 		goto out;
 	}
 	}
 
 
@@ -160,14 +162,14 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
 
 
 	dev_set_drvdata(&pdev->dev, p);
 	dev_set_drvdata(&pdev->dev, p);
 
 
-	err = 0;
-out:
-	return err;
+	return 0;
 
 
 out_unregister_led_cdevs:
 out_unregister_led_cdevs:
 	for (i--; i >= 0; i--)
 	for (i--; i >= 0; i--)
 		led_classdev_unregister(&p->leds[i].led_cdev);
 		led_classdev_unregister(&p->leds[i].led_cdev);
-	goto out;
+	kfree(p);
+out:
+	return err;
 }
 }
 
 
 static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
 static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)

+ 10 - 2
drivers/misc/Kconfig

@@ -245,8 +245,7 @@ config SGI_XP
 
 
 config CS5535_MFGPT
 config CS5535_MFGPT
 	tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
 	tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
-	depends on PCI
-	depends on X86
+	depends on PCI && X86 && MFD_CS5535
 	default n
 	default n
 	help
 	help
 	  This driver provides access to MFGPT functionality for other
 	  This driver provides access to MFGPT functionality for other
@@ -490,6 +489,15 @@ config PCH_PHUB
 	  To compile this driver as a module, choose M here: the module will
 	  To compile this driver as a module, choose M here: the module will
 	  be called pch_phub.
 	  be called pch_phub.
 
 
+config USB_SWITCH_FSA9480
+	tristate "FSA9480 USB Switch"
+	depends on I2C
+	help
+	  The FSA9480 is a USB port accessory detector and switch.
+	  The FSA9480 is fully controlled using I2C and enables USB data,
+	  stereo and mono audio, video, microphone and UART data to use
+	  a common connector port.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
 source "drivers/misc/cb710/Kconfig"

+ 1 - 0
drivers/misc/Makefile

@@ -46,3 +46,4 @@ obj-y				+= ti-st/
 obj-$(CONFIG_AB8500_PWM)	+= ab8500-pwm.o
 obj-$(CONFIG_AB8500_PWM)	+= ab8500-pwm.o
 obj-y				+= lis3lv02d/
 obj-y				+= lis3lv02d/
 obj-y				+= carma/
 obj-y				+= carma/
+obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o

+ 25 - 0
drivers/misc/eeprom/Kconfig

@@ -70,4 +70,29 @@ config EEPROM_93CX6
 
 
 	  If unsure, say N.
 	  If unsure, say N.
 
 
+config EEPROM_93XX46
+	tristate "Microwire EEPROM 93XX46 support"
+	depends on SPI && SYSFS
+	help
+	  Driver for the microwire EEPROM chipsets 93xx46x. The driver
+	  supports both read and write commands and also the command to
+	  erase the whole EEPROM.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called eeprom_93xx46.
+
+	  If unsure, say N.
+
+config EEPROM_DIGSY_MTC_CFG
+	bool "DigsyMTC display configuration EEPROMs device"
+	depends on PPC_MPC5200_GPIO && GPIOLIB && SPI_GPIO
+	help
+	  This option enables access to display configuration EEPROMs
+	  on digsy_mtc board. You have to additionally select Microwire
+	  EEPROM 93XX46 driver. sysfs entries will be created for that
+	  EEPROM allowing to read/write the configuration data or to
+	  erase the whole EEPROM.
+
+	  If unsure, say N.
+
 endmenu
 endmenu

+ 2 - 0
drivers/misc/eeprom/Makefile

@@ -3,3 +3,5 @@ obj-$(CONFIG_EEPROM_AT25)	+= at25.o
 obj-$(CONFIG_EEPROM_LEGACY)	+= eeprom.o
 obj-$(CONFIG_EEPROM_LEGACY)	+= eeprom.o
 obj-$(CONFIG_EEPROM_MAX6875)	+= max6875.o
 obj-$(CONFIG_EEPROM_MAX6875)	+= max6875.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
 obj-$(CONFIG_EEPROM_93CX6)	+= eeprom_93cx6.o
+obj-$(CONFIG_EEPROM_93XX46)	+= eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o

+ 85 - 0
drivers/misc/eeprom/digsy_mtc_eeprom.c

@@ -0,0 +1,85 @@
+/*
+ * EEPROMs access control driver for display configuration EEPROMs
+ * on DigsyMTC board.
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_gpio.h>
+#include <linux/eeprom_93xx46.h>
+
+#define GPIO_EEPROM_CLK		216
+#define GPIO_EEPROM_CS		210
+#define GPIO_EEPROM_DI		217
+#define GPIO_EEPROM_DO		249
+#define GPIO_EEPROM_OE		255
+#define EE_SPI_BUS_NUM	1
+
+static void digsy_mtc_op_prepare(void *p)
+{
+	/* enable */
+	gpio_set_value(GPIO_EEPROM_OE, 0);
+}
+
+static void digsy_mtc_op_finish(void *p)
+{
+	/* disable */
+	gpio_set_value(GPIO_EEPROM_OE, 1);
+}
+
+struct eeprom_93xx46_platform_data digsy_mtc_eeprom_data = {
+	.flags		= EE_ADDR8,
+	.prepare	= digsy_mtc_op_prepare,
+	.finish		= digsy_mtc_op_finish,
+};
+
+static struct spi_gpio_platform_data eeprom_spi_gpio_data = {
+	.sck		= GPIO_EEPROM_CLK,
+	.mosi		= GPIO_EEPROM_DI,
+	.miso		= GPIO_EEPROM_DO,
+	.num_chipselect	= 1,
+};
+
+static struct platform_device digsy_mtc_eeprom = {
+	.name	= "spi_gpio",
+	.id	= EE_SPI_BUS_NUM,
+	.dev	= {
+		.platform_data	= &eeprom_spi_gpio_data,
+	},
+};
+
+static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = {
+	{
+		.modalias		= "93xx46",
+		.max_speed_hz		= 1000000,
+		.bus_num		= EE_SPI_BUS_NUM,
+		.chip_select		= 0,
+		.mode			= SPI_MODE_0,
+		.controller_data	= (void *)GPIO_EEPROM_CS,
+		.platform_data		= &digsy_mtc_eeprom_data,
+	},
+};
+
+static int __init digsy_mtc_eeprom_devices_init(void)
+{
+	int ret;
+
+	ret = gpio_request_one(GPIO_EEPROM_OE, GPIOF_OUT_INIT_HIGH,
+				"93xx46 EEPROMs OE");
+	if (ret) {
+		pr_err("can't request gpio %d\n", GPIO_EEPROM_OE);
+		return ret;
+	}
+	spi_register_board_info(digsy_mtc_eeprom_info,
+				ARRAY_SIZE(digsy_mtc_eeprom_info));
+	return platform_device_register(&digsy_mtc_eeprom);
+}
+device_initcall(digsy_mtc_eeprom_devices_init);

+ 410 - 0
drivers/misc/eeprom/eeprom_93xx46.c

@@ -0,0 +1,410 @@
+/*
+ * Driver for 93xx46 EEPROMs
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/eeprom_93xx46.h>
+
+#define OP_START	0x4
+#define OP_WRITE	(OP_START | 0x1)
+#define OP_READ		(OP_START | 0x2)
+#define ADDR_EWDS	0x00
+#define ADDR_ERAL	0x20
+#define ADDR_EWEN	0x30
+
+struct eeprom_93xx46_dev {
+	struct spi_device *spi;
+	struct eeprom_93xx46_platform_data *pdata;
+	struct bin_attribute bin;
+	struct mutex lock;
+	int addrlen;
+};
+
+static ssize_t
+eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj,
+		       struct bin_attribute *bin_attr,
+		       char *buf, loff_t off, size_t count)
+{
+	struct eeprom_93xx46_dev *edev;
+	struct device *dev;
+	struct spi_message m;
+	struct spi_transfer t[2];
+	int bits, ret;
+	u16 cmd_addr;
+
+	dev = container_of(kobj, struct device, kobj);
+	edev = dev_get_drvdata(dev);
+
+	if (unlikely(off >= edev->bin.size))
+		return 0;
+	if ((off + count) > edev->bin.size)
+		count = edev->bin.size - off;
+	if (unlikely(!count))
+		return count;
+
+	cmd_addr = OP_READ << edev->addrlen;
+
+	if (edev->addrlen == 7) {
+		cmd_addr |= off & 0x7f;
+		bits = 10;
+	} else {
+		cmd_addr |= off & 0x3f;
+		bits = 9;
+	}
+
+	dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
+		cmd_addr, edev->spi->max_speed_hz);
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof(t));
+
+	t[0].tx_buf = (char *)&cmd_addr;
+	t[0].len = 2;
+	t[0].bits_per_word = bits;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].rx_buf = buf;
+	t[1].len = count;
+	t[1].bits_per_word = 8;
+	spi_message_add_tail(&t[1], &m);
+
+	mutex_lock(&edev->lock);
+
+	if (edev->pdata->prepare)
+		edev->pdata->prepare(edev);
+
+	ret = spi_sync(edev->spi, &m);
+	/* have to wait at least Tcsl ns */
+	ndelay(250);
+	if (ret) {
+		dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
+			count, (int)off, ret);
+	}
+
+	if (edev->pdata->finish)
+		edev->pdata->finish(edev);
+
+	mutex_unlock(&edev->lock);
+	return ret ? : count;
+}
+
+static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
+{
+	struct spi_message m;
+	struct spi_transfer t;
+	int bits, ret;
+	u16 cmd_addr;
+
+	cmd_addr = OP_START << edev->addrlen;
+	if (edev->addrlen == 7) {
+		cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
+		bits = 10;
+	} else {
+		cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
+		bits = 9;
+	}
+
+	dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr);
+
+	spi_message_init(&m);
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = &cmd_addr;
+	t.len = 2;
+	t.bits_per_word = bits;
+	spi_message_add_tail(&t, &m);
+
+	mutex_lock(&edev->lock);
+
+	if (edev->pdata->prepare)
+		edev->pdata->prepare(edev);
+
+	ret = spi_sync(edev->spi, &m);
+	/* have to wait at least Tcsl ns */
+	ndelay(250);
+	if (ret)
+		dev_err(&edev->spi->dev, "erase/write %sable error %d\n",
+			is_on ? "en" : "dis", ret);
+
+	if (edev->pdata->finish)
+		edev->pdata->finish(edev);
+
+	mutex_unlock(&edev->lock);
+	return ret;
+}
+
+static ssize_t
+eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
+			 const char *buf, unsigned off)
+{
+	struct spi_message m;
+	struct spi_transfer t[2];
+	int bits, data_len, ret;
+	u16 cmd_addr;
+
+	cmd_addr = OP_WRITE << edev->addrlen;
+
+	if (edev->addrlen == 7) {
+		cmd_addr |= off & 0x7f;
+		bits = 10;
+		data_len = 1;
+	} else {
+		cmd_addr |= off & 0x3f;
+		bits = 9;
+		data_len = 2;
+	}
+
+	dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr);
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof(t));
+
+	t[0].tx_buf = (char *)&cmd_addr;
+	t[0].len = 2;
+	t[0].bits_per_word = bits;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].tx_buf = buf;
+	t[1].len = data_len;
+	t[1].bits_per_word = 8;
+	spi_message_add_tail(&t[1], &m);
+
+	ret = spi_sync(edev->spi, &m);
+	/* have to wait program cycle time Twc ms */
+	mdelay(6);
+	return ret;
+}
+
+static ssize_t
+eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
+			struct bin_attribute *bin_attr,
+			char *buf, loff_t off, size_t count)
+{
+	struct eeprom_93xx46_dev *edev;
+	struct device *dev;
+	int i, ret, step = 1;
+
+	dev = container_of(kobj, struct device, kobj);
+	edev = dev_get_drvdata(dev);
+
+	if (unlikely(off >= edev->bin.size))
+		return 0;
+	if ((off + count) > edev->bin.size)
+		count = edev->bin.size - off;
+	if (unlikely(!count))
+		return count;
+
+	/* only write even number of bytes on 16-bit devices */
+	if (edev->addrlen == 6) {
+		step = 2;
+		count &= ~1;
+	}
+
+	/* erase/write enable */
+	ret = eeprom_93xx46_ew(edev, 1);
+	if (ret)
+		return ret;
+
+	mutex_lock(&edev->lock);
+
+	if (edev->pdata->prepare)
+		edev->pdata->prepare(edev);
+
+	for (i = 0; i < count; i += step) {
+		ret = eeprom_93xx46_write_word(edev, &buf[i], off + i);
+		if (ret) {
+			dev_err(&edev->spi->dev, "write failed at %d: %d\n",
+				(int)off + i, ret);
+			break;
+		}
+	}
+
+	if (edev->pdata->finish)
+		edev->pdata->finish(edev);
+
+	mutex_unlock(&edev->lock);
+
+	/* erase/write disable */
+	eeprom_93xx46_ew(edev, 0);
+	return ret ? : count;
+}
+
+static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
+{
+	struct eeprom_93xx46_platform_data *pd = edev->pdata;
+	struct spi_message m;
+	struct spi_transfer t;
+	int bits, ret;
+	u16 cmd_addr;
+
+	cmd_addr = OP_START << edev->addrlen;
+	if (edev->addrlen == 7) {
+		cmd_addr |= ADDR_ERAL << 1;
+		bits = 10;
+	} else {
+		cmd_addr |= ADDR_ERAL;
+		bits = 9;
+	}
+
+	spi_message_init(&m);
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = &cmd_addr;
+	t.len = 2;
+	t.bits_per_word = bits;
+	spi_message_add_tail(&t, &m);
+
+	mutex_lock(&edev->lock);
+
+	if (edev->pdata->prepare)
+		edev->pdata->prepare(edev);
+
+	ret = spi_sync(edev->spi, &m);
+	if (ret)
+		dev_err(&edev->spi->dev, "erase error %d\n", ret);
+	/* have to wait erase cycle time Tec ms */
+	mdelay(6);
+
+	if (pd->finish)
+		pd->finish(edev);
+
+	mutex_unlock(&edev->lock);
+	return ret;
+}
+
+static ssize_t eeprom_93xx46_store_erase(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev);
+	int erase = 0, ret;
+
+	sscanf(buf, "%d", &erase);
+	if (erase) {
+		ret = eeprom_93xx46_ew(edev, 1);
+		if (ret)
+			return ret;
+		ret = eeprom_93xx46_eral(edev);
+		if (ret)
+			return ret;
+		ret = eeprom_93xx46_ew(edev, 0);
+		if (ret)
+			return ret;
+	}
+	return count;
+}
+static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
+
+static int __devinit eeprom_93xx46_probe(struct spi_device *spi)
+{
+	struct eeprom_93xx46_platform_data *pd;
+	struct eeprom_93xx46_dev *edev;
+	int err;
+
+	pd = spi->dev.platform_data;
+	if (!pd) {
+		dev_err(&spi->dev, "missing platform data\n");
+		return -ENODEV;
+	}
+
+	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+	if (!edev)
+		return -ENOMEM;
+
+	if (pd->flags & EE_ADDR8)
+		edev->addrlen = 7;
+	else if (pd->flags & EE_ADDR16)
+		edev->addrlen = 6;
+	else {
+		dev_err(&spi->dev, "unspecified address type\n");
+		err = -EINVAL;
+		goto fail;
+	}
+
+	mutex_init(&edev->lock);
+
+	edev->spi = spi_dev_get(spi);
+	edev->pdata = pd;
+
+	sysfs_bin_attr_init(&edev->bin);
+	edev->bin.attr.name = "eeprom";
+	edev->bin.attr.mode = S_IRUSR;
+	edev->bin.read = eeprom_93xx46_bin_read;
+	edev->bin.size = 128;
+	if (!(pd->flags & EE_READONLY)) {
+		edev->bin.write = eeprom_93xx46_bin_write;
+		edev->bin.attr.mode |= S_IWUSR;
+	}
+
+	err = sysfs_create_bin_file(&spi->dev.kobj, &edev->bin);
+	if (err)
+		goto fail;
+
+	dev_info(&spi->dev, "%d-bit eeprom %s\n",
+		(pd->flags & EE_ADDR8) ? 8 : 16,
+		(pd->flags & EE_READONLY) ? "(readonly)" : "");
+
+	if (!(pd->flags & EE_READONLY)) {
+		if (device_create_file(&spi->dev, &dev_attr_erase))
+			dev_err(&spi->dev, "can't create erase interface\n");
+	}
+
+	dev_set_drvdata(&spi->dev, edev);
+	return 0;
+fail:
+	kfree(edev);
+	return err;
+}
+
+static int __devexit eeprom_93xx46_remove(struct spi_device *spi)
+{
+	struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev);
+
+	if (!(edev->pdata->flags & EE_READONLY))
+		device_remove_file(&spi->dev, &dev_attr_erase);
+
+	sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
+	dev_set_drvdata(&spi->dev, NULL);
+	kfree(edev);
+	return 0;
+}
+
+static struct spi_driver eeprom_93xx46_driver = {
+	.driver = {
+		.name	= "93xx46",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= eeprom_93xx46_probe,
+	.remove		= __devexit_p(eeprom_93xx46_remove),
+};
+
+static int __init eeprom_93xx46_init(void)
+{
+	return spi_register_driver(&eeprom_93xx46_driver);
+}
+module_init(eeprom_93xx46_init);
+
+static void __exit eeprom_93xx46_exit(void)
+{
+	spi_unregister_driver(&eeprom_93xx46_driver);
+}
+module_exit(eeprom_93xx46_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_ALIAS("spi:93xx46");

+ 557 - 0
drivers/misc/fsa9480.c

@@ -0,0 +1,557 @@
+/*
+ * fsa9480.c - FSA9480 micro USB switch device driver
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Wonguk Jeong <wonguk.jeong@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_data/fsa9480.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+/* FSA9480 I2C registers */
+#define FSA9480_REG_DEVID		0x01
+#define FSA9480_REG_CTRL		0x02
+#define FSA9480_REG_INT1		0x03
+#define FSA9480_REG_INT2		0x04
+#define FSA9480_REG_INT1_MASK		0x05
+#define FSA9480_REG_INT2_MASK		0x06
+#define FSA9480_REG_ADC			0x07
+#define FSA9480_REG_TIMING1		0x08
+#define FSA9480_REG_TIMING2		0x09
+#define FSA9480_REG_DEV_T1		0x0a
+#define FSA9480_REG_DEV_T2		0x0b
+#define FSA9480_REG_BTN1		0x0c
+#define FSA9480_REG_BTN2		0x0d
+#define FSA9480_REG_CK			0x0e
+#define FSA9480_REG_CK_INT1		0x0f
+#define FSA9480_REG_CK_INT2		0x10
+#define FSA9480_REG_CK_INTMASK1		0x11
+#define FSA9480_REG_CK_INTMASK2		0x12
+#define FSA9480_REG_MANSW1		0x13
+#define FSA9480_REG_MANSW2		0x14
+
+/* Control */
+#define CON_SWITCH_OPEN		(1 << 4)
+#define CON_RAW_DATA		(1 << 3)
+#define CON_MANUAL_SW		(1 << 2)
+#define CON_WAIT		(1 << 1)
+#define CON_INT_MASK		(1 << 0)
+#define CON_MASK		(CON_SWITCH_OPEN | CON_RAW_DATA | \
+				CON_MANUAL_SW | CON_WAIT)
+
+/* Device Type 1 */
+#define DEV_USB_OTG		(1 << 7)
+#define DEV_DEDICATED_CHG	(1 << 6)
+#define DEV_USB_CHG		(1 << 5)
+#define DEV_CAR_KIT		(1 << 4)
+#define DEV_UART		(1 << 3)
+#define DEV_USB			(1 << 2)
+#define DEV_AUDIO_2		(1 << 1)
+#define DEV_AUDIO_1		(1 << 0)
+
+#define DEV_T1_USB_MASK		(DEV_USB_OTG | DEV_USB)
+#define DEV_T1_UART_MASK	(DEV_UART)
+#define DEV_T1_CHARGER_MASK	(DEV_DEDICATED_CHG | DEV_USB_CHG)
+
+/* Device Type 2 */
+#define DEV_AV			(1 << 6)
+#define DEV_TTY			(1 << 5)
+#define DEV_PPD			(1 << 4)
+#define DEV_JIG_UART_OFF	(1 << 3)
+#define DEV_JIG_UART_ON		(1 << 2)
+#define DEV_JIG_USB_OFF		(1 << 1)
+#define DEV_JIG_USB_ON		(1 << 0)
+
+#define DEV_T2_USB_MASK		(DEV_JIG_USB_OFF | DEV_JIG_USB_ON)
+#define DEV_T2_UART_MASK	(DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
+#define DEV_T2_JIG_MASK		(DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \
+				DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
+
+/*
+ * Manual Switch
+ * D- [7:5] / D+ [4:2]
+ * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
+ */
+#define SW_VAUDIO		((4 << 5) | (4 << 2))
+#define SW_UART			((3 << 5) | (3 << 2))
+#define SW_AUDIO		((2 << 5) | (2 << 2))
+#define SW_DHOST		((1 << 5) | (1 << 2))
+#define SW_AUTO			((0 << 5) | (0 << 2))
+
+/* Interrupt 1 */
+#define INT_DETACH		(1 << 1)
+#define INT_ATTACH		(1 << 0)
+
+struct fsa9480_usbsw {
+	struct i2c_client		*client;
+	struct fsa9480_platform_data	*pdata;
+	int				dev1;
+	int				dev2;
+	int				mansw;
+};
+
+static struct fsa9480_usbsw *chip;
+
+static int fsa9480_write_reg(struct i2c_client *client,
+		int reg, int value)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, value);
+
+	if (ret < 0)
+		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int fsa9480_read_reg(struct i2c_client *client, int reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+
+	if (ret < 0)
+		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int fsa9480_read_irq(struct i2c_client *client, int *value)
+{
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(client,
+			FSA9480_REG_INT1, 2, (u8 *)value);
+	*value &= 0xffff;
+
+	if (ret < 0)
+		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+	return ret;
+}
+
+static void fsa9480_set_switch(const char *buf)
+{
+	struct fsa9480_usbsw *usbsw = chip;
+	struct i2c_client *client = usbsw->client;
+	unsigned int value;
+	unsigned int path = 0;
+
+	value = fsa9480_read_reg(client, FSA9480_REG_CTRL);
+
+	if (!strncmp(buf, "VAUDIO", 6)) {
+		path = SW_VAUDIO;
+		value &= ~CON_MANUAL_SW;
+	} else if (!strncmp(buf, "UART", 4)) {
+		path = SW_UART;
+		value &= ~CON_MANUAL_SW;
+	} else if (!strncmp(buf, "AUDIO", 5)) {
+		path = SW_AUDIO;
+		value &= ~CON_MANUAL_SW;
+	} else if (!strncmp(buf, "DHOST", 5)) {
+		path = SW_DHOST;
+		value &= ~CON_MANUAL_SW;
+	} else if (!strncmp(buf, "AUTO", 4)) {
+		path = SW_AUTO;
+		value |= CON_MANUAL_SW;
+	} else {
+		printk(KERN_ERR "Wrong command\n");
+		return;
+	}
+
+	usbsw->mansw = path;
+	fsa9480_write_reg(client, FSA9480_REG_MANSW1, path);
+	fsa9480_write_reg(client, FSA9480_REG_CTRL, value);
+}
+
+static ssize_t fsa9480_get_switch(char *buf)
+{
+	struct fsa9480_usbsw *usbsw = chip;
+	struct i2c_client *client = usbsw->client;
+	unsigned int value;
+
+	value = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
+
+	if (value == SW_VAUDIO)
+		return sprintf(buf, "VAUDIO\n");
+	else if (value == SW_UART)
+		return sprintf(buf, "UART\n");
+	else if (value == SW_AUDIO)
+		return sprintf(buf, "AUDIO\n");
+	else if (value == SW_DHOST)
+		return sprintf(buf, "DHOST\n");
+	else if (value == SW_AUTO)
+		return sprintf(buf, "AUTO\n");
+	else
+		return sprintf(buf, "%x", value);
+}
+
+static ssize_t fsa9480_show_device(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
+	struct i2c_client *client = usbsw->client;
+	int dev1, dev2;
+
+	dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
+	dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
+
+	if (!dev1 && !dev2)
+		return sprintf(buf, "NONE\n");
+
+	/* USB */
+	if (dev1 & DEV_T1_USB_MASK || dev2 & DEV_T2_USB_MASK)
+		return sprintf(buf, "USB\n");
+
+	/* UART */
+	if (dev1 & DEV_T1_UART_MASK || dev2 & DEV_T2_UART_MASK)
+		return sprintf(buf, "UART\n");
+
+	/* CHARGER */
+	if (dev1 & DEV_T1_CHARGER_MASK)
+		return sprintf(buf, "CHARGER\n");
+
+	/* JIG */
+	if (dev2 & DEV_T2_JIG_MASK)
+		return sprintf(buf, "JIG\n");
+
+	return sprintf(buf, "UNKNOWN\n");
+}
+
+static ssize_t fsa9480_show_manualsw(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return fsa9480_get_switch(buf);
+
+}
+
+static ssize_t fsa9480_set_manualsw(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	fsa9480_set_switch(buf);
+
+	return count;
+}
+
+static DEVICE_ATTR(device, S_IRUGO, fsa9480_show_device, NULL);
+static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR,
+		fsa9480_show_manualsw, fsa9480_set_manualsw);
+
+static struct attribute *fsa9480_attributes[] = {
+	&dev_attr_device.attr,
+	&dev_attr_switch.attr,
+	NULL
+};
+
+static const struct attribute_group fsa9480_group = {
+	.attrs = fsa9480_attributes,
+};
+
+static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw, int intr)
+{
+	int val1, val2, ctrl;
+	struct fsa9480_platform_data *pdata = usbsw->pdata;
+	struct i2c_client *client = usbsw->client;
+
+	val1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
+	val2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
+	ctrl = fsa9480_read_reg(client, FSA9480_REG_CTRL);
+
+	dev_info(&client->dev, "intr: 0x%x, dev1: 0x%x, dev2: 0x%x\n",
+			intr, val1, val2);
+
+	if (!intr)
+		goto out;
+
+	if (intr & INT_ATTACH) {	/* Attached */
+		/* USB */
+		if (val1 & DEV_T1_USB_MASK || val2 & DEV_T2_USB_MASK) {
+			if (pdata->usb_cb)
+				pdata->usb_cb(FSA9480_ATTACHED);
+
+			if (usbsw->mansw) {
+				fsa9480_write_reg(client,
+					FSA9480_REG_MANSW1, usbsw->mansw);
+			}
+		}
+
+		/* UART */
+		if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) {
+			if (pdata->uart_cb)
+				pdata->uart_cb(FSA9480_ATTACHED);
+
+			if (!(ctrl & CON_MANUAL_SW)) {
+				fsa9480_write_reg(client,
+					FSA9480_REG_MANSW1, SW_UART);
+			}
+		}
+
+		/* CHARGER */
+		if (val1 & DEV_T1_CHARGER_MASK) {
+			if (pdata->charger_cb)
+				pdata->charger_cb(FSA9480_ATTACHED);
+		}
+
+		/* JIG */
+		if (val2 & DEV_T2_JIG_MASK) {
+			if (pdata->jig_cb)
+				pdata->jig_cb(FSA9480_ATTACHED);
+		}
+	} else if (intr & INT_DETACH) {	/* Detached */
+		/* USB */
+		if (usbsw->dev1 & DEV_T1_USB_MASK ||
+			usbsw->dev2 & DEV_T2_USB_MASK) {
+			if (pdata->usb_cb)
+				pdata->usb_cb(FSA9480_DETACHED);
+		}
+
+		/* UART */
+		if (usbsw->dev1 & DEV_T1_UART_MASK ||
+			usbsw->dev2 & DEV_T2_UART_MASK) {
+			if (pdata->uart_cb)
+				pdata->uart_cb(FSA9480_DETACHED);
+		}
+
+		/* CHARGER */
+		if (usbsw->dev1 & DEV_T1_CHARGER_MASK) {
+			if (pdata->charger_cb)
+				pdata->charger_cb(FSA9480_DETACHED);
+		}
+
+		/* JIG */
+		if (usbsw->dev2 & DEV_T2_JIG_MASK) {
+			if (pdata->jig_cb)
+				pdata->jig_cb(FSA9480_DETACHED);
+		}
+	}
+
+	usbsw->dev1 = val1;
+	usbsw->dev2 = val2;
+
+out:
+	ctrl &= ~CON_INT_MASK;
+	fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
+}
+
+static irqreturn_t fsa9480_irq_handler(int irq, void *data)
+{
+	struct fsa9480_usbsw *usbsw = data;
+	struct i2c_client *client = usbsw->client;
+	int intr;
+
+	/* clear interrupt */
+	fsa9480_read_irq(client, &intr);
+
+	/* device detection */
+	fsa9480_detect_dev(usbsw, intr);
+
+	return IRQ_HANDLED;
+}
+
+static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw)
+{
+	struct fsa9480_platform_data *pdata = usbsw->pdata;
+	struct i2c_client *client = usbsw->client;
+	int ret;
+	int intr;
+	unsigned int ctrl = CON_MASK;
+
+	/* clear interrupt */
+	fsa9480_read_irq(client, &intr);
+
+	/* unmask interrupt (attach/detach only) */
+	fsa9480_write_reg(client, FSA9480_REG_INT1_MASK, 0xfc);
+	fsa9480_write_reg(client, FSA9480_REG_INT2_MASK, 0x1f);
+
+	usbsw->mansw = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
+
+	if (usbsw->mansw)
+		ctrl &= ~CON_MANUAL_SW;	/* Manual Switching Mode */
+
+	fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
+
+	if (pdata && pdata->cfg_gpio)
+		pdata->cfg_gpio();
+
+	if (client->irq) {
+		ret = request_threaded_irq(client->irq, NULL,
+				fsa9480_irq_handler,
+				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				"fsa9480 micro USB", usbsw);
+		if (ret) {
+			dev_err(&client->dev, "failed to reqeust IRQ\n");
+			return ret;
+		}
+
+		device_init_wakeup(&client->dev, pdata->wakeup);
+	}
+
+	return 0;
+}
+
+static int __devinit fsa9480_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+	struct fsa9480_usbsw *usbsw;
+	int ret = 0;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EIO;
+
+	usbsw = kzalloc(sizeof(struct fsa9480_usbsw), GFP_KERNEL);
+	if (!usbsw) {
+		dev_err(&client->dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	usbsw->client = client;
+	usbsw->pdata = client->dev.platform_data;
+
+	chip = usbsw;
+
+	i2c_set_clientdata(client, usbsw);
+
+	ret = fsa9480_irq_init(usbsw);
+	if (ret)
+		goto fail1;
+
+	ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group);
+	if (ret) {
+		dev_err(&client->dev,
+				"failed to create fsa9480 attribute group\n");
+		goto fail2;
+	}
+
+	/* ADC Detect Time: 500ms */
+	fsa9480_write_reg(client, FSA9480_REG_TIMING1, 0x6);
+
+	if (chip->pdata->reset_cb)
+		chip->pdata->reset_cb();
+
+	/* device detection */
+	fsa9480_detect_dev(usbsw, INT_ATTACH);
+
+	pm_runtime_set_active(&client->dev);
+
+	return 0;
+
+fail2:
+	if (client->irq)
+		free_irq(client->irq, NULL);
+fail1:
+	i2c_set_clientdata(client, NULL);
+	kfree(usbsw);
+	return ret;
+}
+
+static int __devexit fsa9480_remove(struct i2c_client *client)
+{
+	struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+	if (client->irq)
+		free_irq(client->irq, NULL);
+	i2c_set_clientdata(client, NULL);
+
+	sysfs_remove_group(&client->dev.kobj, &fsa9480_group);
+	device_init_wakeup(&client->dev, 0);
+	kfree(usbsw);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int fsa9480_suspend(struct i2c_client *client, pm_message_t state)
+{
+	struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+	struct fsa9480_platform_data *pdata = usbsw->pdata;
+
+	if (device_may_wakeup(&client->dev) && client->irq)
+		enable_irq_wake(client->irq);
+
+	if (pdata->usb_power)
+		pdata->usb_power(0);
+
+	return 0;
+}
+
+static int fsa9480_resume(struct i2c_client *client)
+{
+	struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+	int dev1, dev2;
+
+	if (device_may_wakeup(&client->dev) && client->irq)
+		disable_irq_wake(client->irq);
+
+	/*
+	 * Clear Pending interrupt. Note that detect_dev does what
+	 * the interrupt handler does. So, we don't miss pending and
+	 * we reenable interrupt if there is one.
+	 */
+	fsa9480_read_reg(client, FSA9480_REG_INT1);
+	fsa9480_read_reg(client, FSA9480_REG_INT2);
+
+	dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
+	dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
+
+	/* device detection */
+	fsa9480_detect_dev(usbsw, (dev1 || dev2) ? INT_ATTACH : INT_DETACH);
+
+	return 0;
+}
+
+#else
+
+#define fsa9480_suspend NULL
+#define fsa9480_resume NULL
+
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id fsa9480_id[] = {
+	{"fsa9480", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, fsa9480_id);
+
+static struct i2c_driver fsa9480_i2c_driver = {
+	.driver = {
+		.name = "fsa9480",
+	},
+	.probe = fsa9480_probe,
+	.remove = __devexit_p(fsa9480_remove),
+	.resume = fsa9480_resume,
+	.suspend = fsa9480_suspend,
+	.id_table = fsa9480_id,
+};
+
+static int __init fsa9480_init(void)
+{
+	return i2c_add_driver(&fsa9480_i2c_driver);
+}
+module_init(fsa9480_init);
+
+static void __exit fsa9480_exit(void)
+{
+	i2c_del_driver(&fsa9480_i2c_driver);
+}
+module_exit(fsa9480_exit);
+
+MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
+MODULE_DESCRIPTION("FSA9480 USB Switch driver");
+MODULE_LICENSE("GPL");

+ 4 - 1
drivers/misc/pch_phub.c

@@ -686,6 +686,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
 	}
 	}
 
 
 	if (id->driver_data == 1) { /* EG20T PCH */
 	if (id->driver_data == 1) { /* EG20T PCH */
+		const char *board_name;
+
 		retval = sysfs_create_file(&pdev->dev.kobj,
 		retval = sysfs_create_file(&pdev->dev.kobj,
 					   &dev_attr_pch_mac.attr);
 					   &dev_attr_pch_mac.attr);
 		if (retval)
 		if (retval)
@@ -701,7 +703,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
 					       CLKCFG_CANCLK_MASK);
 					       CLKCFG_CANCLK_MASK);
 
 
 		/* quirk for CM-iTC board */
 		/* quirk for CM-iTC board */
-		if (strstr(dmi_get_system_info(DMI_BOARD_NAME), "CM-iTC"))
+		board_name = dmi_get_system_info(DMI_BOARD_NAME);
+		if (board_name && strstr(board_name, "CM-iTC"))
 			pch_phub_read_modify_write_reg(chip,
 			pch_phub_read_modify_write_reg(chip,
 						(unsigned int)CLKCFG_REG_OFFSET,
 						(unsigned int)CLKCFG_REG_OFFSET,
 						CLKCFG_UART_48MHZ | CLKCFG_BAUDDIV |
 						CLKCFG_UART_48MHZ | CLKCFG_BAUDDIV |

+ 1 - 0
drivers/mmc/core/core.c

@@ -23,6 +23,7 @@
 #include <linux/log2.h>
 #include <linux/log2.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
+#include <linux/suspend.h>
 
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/host.h>

+ 27 - 25
drivers/pnp/pnpacpi/rsparser.c

@@ -509,15 +509,15 @@ static __init void pnpacpi_parse_dma_option(struct pnp_dev *dev,
 					    struct acpi_resource_dma *p)
 					    struct acpi_resource_dma *p)
 {
 {
 	int i;
 	int i;
-	unsigned char map = 0, flags;
+	unsigned char map = 0, flags = 0;
 
 
 	if (p->channel_count == 0)
 	if (p->channel_count == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	for (i = 0; i < p->channel_count; i++)
 	for (i = 0; i < p->channel_count; i++)
 		map |= 1 << p->channels[i];
 		map |= 1 << p->channels[i];
 
 
-	flags = dma_flags(dev, p->type, p->bus_master, p->transfer);
+	flags |= dma_flags(dev, p->type, p->bus_master, p->transfer);
 	pnp_register_dma_resource(dev, option_flags, map, flags);
 	pnp_register_dma_resource(dev, option_flags, map, flags);
 }
 }
 
 
@@ -527,17 +527,17 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev,
 {
 {
 	int i;
 	int i;
 	pnp_irq_mask_t map;
 	pnp_irq_mask_t map;
-	unsigned char flags;
+	unsigned char flags = 0;
 
 
 	if (p->interrupt_count == 0)
 	if (p->interrupt_count == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	bitmap_zero(map.bits, PNP_IRQ_NR);
 	bitmap_zero(map.bits, PNP_IRQ_NR);
 	for (i = 0; i < p->interrupt_count; i++)
 	for (i = 0; i < p->interrupt_count; i++)
 		if (p->interrupts[i])
 		if (p->interrupts[i])
 			__set_bit(p->interrupts[i], map.bits);
 			__set_bit(p->interrupts[i], map.bits);
 
 
-	flags = irq_flags(p->triggering, p->polarity, p->sharable);
+	flags |= irq_flags(p->triggering, p->polarity, p->sharable);
 	pnp_register_irq_resource(dev, option_flags, &map, flags);
 	pnp_register_irq_resource(dev, option_flags, &map, flags);
 }
 }
 
 
@@ -547,10 +547,10 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
 {
 {
 	int i;
 	int i;
 	pnp_irq_mask_t map;
 	pnp_irq_mask_t map;
-	unsigned char flags;
+	unsigned char flags = 0;
 
 
 	if (p->interrupt_count == 0)
 	if (p->interrupt_count == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	bitmap_zero(map.bits, PNP_IRQ_NR);
 	bitmap_zero(map.bits, PNP_IRQ_NR);
 	for (i = 0; i < p->interrupt_count; i++) {
 	for (i = 0; i < p->interrupt_count; i++) {
@@ -564,7 +564,7 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
 		}
 		}
 	}
 	}
 
 
-	flags = irq_flags(p->triggering, p->polarity, p->sharable);
+	flags |= irq_flags(p->triggering, p->polarity, p->sharable);
 	pnp_register_irq_resource(dev, option_flags, &map, flags);
 	pnp_register_irq_resource(dev, option_flags, &map, flags);
 }
 }
 
 
@@ -575,10 +575,10 @@ static __init void pnpacpi_parse_port_option(struct pnp_dev *dev,
 	unsigned char flags = 0;
 	unsigned char flags = 0;
 
 
 	if (io->address_length == 0)
 	if (io->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	if (io->io_decode == ACPI_DECODE_16)
 	if (io->io_decode == ACPI_DECODE_16)
-		flags = IORESOURCE_IO_16BIT_ADDR;
+		flags |= IORESOURCE_IO_16BIT_ADDR;
 	pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum,
 	pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum,
 				   io->alignment, io->address_length, flags);
 				   io->alignment, io->address_length, flags);
 }
 }
@@ -587,11 +587,13 @@ static __init void pnpacpi_parse_fixed_port_option(struct pnp_dev *dev,
 					unsigned int option_flags,
 					unsigned int option_flags,
 					struct acpi_resource_fixed_io *io)
 					struct acpi_resource_fixed_io *io)
 {
 {
+	unsigned char flags = 0;
+
 	if (io->address_length == 0)
 	if (io->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	pnp_register_port_resource(dev, option_flags, io->address, io->address,
 	pnp_register_port_resource(dev, option_flags, io->address, io->address,
-				   0, io->address_length, IORESOURCE_IO_FIXED);
+				   0, io->address_length, flags | IORESOURCE_IO_FIXED);
 }
 }
 
 
 static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
 static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
@@ -601,10 +603,10 @@ static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
 	unsigned char flags = 0;
 	unsigned char flags = 0;
 
 
 	if (p->address_length == 0)
 	if (p->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	if (p->write_protect == ACPI_READ_WRITE_MEMORY)
 	if (p->write_protect == ACPI_READ_WRITE_MEMORY)
-		flags = IORESOURCE_MEM_WRITEABLE;
+		flags |= IORESOURCE_MEM_WRITEABLE;
 	pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
 	pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
 				  p->alignment, p->address_length, flags);
 				  p->alignment, p->address_length, flags);
 }
 }
@@ -616,10 +618,10 @@ static __init void pnpacpi_parse_mem32_option(struct pnp_dev *dev,
 	unsigned char flags = 0;
 	unsigned char flags = 0;
 
 
 	if (p->address_length == 0)
 	if (p->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	if (p->write_protect == ACPI_READ_WRITE_MEMORY)
 	if (p->write_protect == ACPI_READ_WRITE_MEMORY)
-		flags = IORESOURCE_MEM_WRITEABLE;
+		flags |= IORESOURCE_MEM_WRITEABLE;
 	pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
 	pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
 				  p->alignment, p->address_length, flags);
 				  p->alignment, p->address_length, flags);
 }
 }
@@ -631,10 +633,10 @@ static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_dev *dev,
 	unsigned char flags = 0;
 	unsigned char flags = 0;
 
 
 	if (p->address_length == 0)
 	if (p->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	if (p->write_protect == ACPI_READ_WRITE_MEMORY)
 	if (p->write_protect == ACPI_READ_WRITE_MEMORY)
-		flags = IORESOURCE_MEM_WRITEABLE;
+		flags |= IORESOURCE_MEM_WRITEABLE;
 	pnp_register_mem_resource(dev, option_flags, p->address, p->address,
 	pnp_register_mem_resource(dev, option_flags, p->address, p->address,
 				  0, p->address_length, flags);
 				  0, p->address_length, flags);
 }
 }
@@ -655,18 +657,18 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev,
 	}
 	}
 
 
 	if (p->address_length == 0)
 	if (p->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	if (p->resource_type == ACPI_MEMORY_RANGE) {
 	if (p->resource_type == ACPI_MEMORY_RANGE) {
 		if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
 		if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
-			flags = IORESOURCE_MEM_WRITEABLE;
+			flags |= IORESOURCE_MEM_WRITEABLE;
 		pnp_register_mem_resource(dev, option_flags, p->minimum,
 		pnp_register_mem_resource(dev, option_flags, p->minimum,
 					  p->minimum, 0, p->address_length,
 					  p->minimum, 0, p->address_length,
 					  flags);
 					  flags);
 	} else if (p->resource_type == ACPI_IO_RANGE)
 	} else if (p->resource_type == ACPI_IO_RANGE)
 		pnp_register_port_resource(dev, option_flags, p->minimum,
 		pnp_register_port_resource(dev, option_flags, p->minimum,
 					   p->minimum, 0, p->address_length,
 					   p->minimum, 0, p->address_length,
-					   IORESOURCE_IO_FIXED);
+					   flags | IORESOURCE_IO_FIXED);
 }
 }
 
 
 static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
 static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
@@ -677,18 +679,18 @@ static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
 	unsigned char flags = 0;
 	unsigned char flags = 0;
 
 
 	if (p->address_length == 0)
 	if (p->address_length == 0)
-		return;
+		flags |= IORESOURCE_DISABLED;
 
 
 	if (p->resource_type == ACPI_MEMORY_RANGE) {
 	if (p->resource_type == ACPI_MEMORY_RANGE) {
 		if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
 		if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
-			flags = IORESOURCE_MEM_WRITEABLE;
+			flags |= IORESOURCE_MEM_WRITEABLE;
 		pnp_register_mem_resource(dev, option_flags, p->minimum,
 		pnp_register_mem_resource(dev, option_flags, p->minimum,
 					  p->minimum, 0, p->address_length,
 					  p->minimum, 0, p->address_length,
 					  flags);
 					  flags);
 	} else if (p->resource_type == ACPI_IO_RANGE)
 	} else if (p->resource_type == ACPI_IO_RANGE)
 		pnp_register_port_resource(dev, option_flags, p->minimum,
 		pnp_register_port_resource(dev, option_flags, p->minimum,
 					   p->minimum, 0, p->address_length,
 					   p->minimum, 0, p->address_length,
-					   IORESOURCE_IO_FIXED);
+					   flags | IORESOURCE_IO_FIXED);
 }
 }
 
 
 struct acpipnp_parse_option_s {
 struct acpipnp_parse_option_s {

+ 12 - 2
drivers/rtc/Kconfig

@@ -1006,10 +1006,10 @@ config RTC_DRV_MC13XXX
 
 
 config RTC_DRV_MPC5121
 config RTC_DRV_MPC5121
 	tristate "Freescale MPC5121 built-in RTC"
 	tristate "Freescale MPC5121 built-in RTC"
-	depends on PPC_MPC512x && RTC_CLASS
+	depends on PPC_MPC512x || PPC_MPC52xx
 	help
 	help
 	  If you say yes here you will get support for the
 	  If you say yes here you will get support for the
-	  built-in RTC MPC5121.
+	  built-in RTC on MPC5121 or on MPC5200.
 
 
 	  This driver can also be built as a module. If so, the module
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-mpc5121.
 	  will be called rtc-mpc5121.
@@ -1034,6 +1034,16 @@ config RTC_DRV_LPC32XX
 	  This driver can also be buillt as a module. If so, the module
 	  This driver can also be buillt as a module. If so, the module
 	  will be called rtc-lpc32xx.
 	  will be called rtc-lpc32xx.
 
 
+config RTC_DRV_PM8XXX
+	tristate "Qualcomm PMIC8XXX RTC"
+	depends on MFD_PM8XXX
+	help
+	  If you say yes here you get support for the
+	  Qualcomm PMIC8XXX RTC.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc-pm8xxx.
+
 config RTC_DRV_TEGRA
 config RTC_DRV_TEGRA
 	tristate "NVIDIA Tegra Internal RTC driver"
 	tristate "NVIDIA Tegra Internal RTC driver"
 	depends on RTC_CLASS && ARCH_TEGRA
 	depends on RTC_CLASS && ARCH_TEGRA

+ 1 - 0
drivers/rtc/Makefile

@@ -77,6 +77,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123)	+= rtc-pcf2123.o
 obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
 obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o
 obj-$(CONFIG_RTC_DRV_PL030)	+= rtc-pl030.o
 obj-$(CONFIG_RTC_DRV_PL030)	+= rtc-pl030.o
 obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o
 obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o
+obj-$(CONFIG_RTC_DRV_PM8XXX)	+= rtc-pm8xxx.o
 obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
 obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o

+ 73 - 8
drivers/rtc/rtc-mpc5121.c

@@ -3,6 +3,7 @@
  *
  *
  * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
  * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
  * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
  * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright 2011, Dmitry Eremin-Solenikov
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * it under the terms of the GNU General Public License version 2 as
@@ -145,6 +146,55 @@ static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	return 0;
 	return 0;
 }
 }
 
 
+static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+	int tmp;
+
+	tm->tm_sec = in_8(&regs->second);
+	tm->tm_min = in_8(&regs->minute);
+
+	/* 12 hour format? */
+	if (in_8(&regs->hour) & 0x20)
+		tm->tm_hour = (in_8(&regs->hour) >> 1) +
+			(in_8(&regs->hour) & 1 ? 12 : 0);
+	else
+		tm->tm_hour = in_8(&regs->hour);
+
+	tmp = in_8(&regs->wday_mday);
+	tm->tm_mday = tmp & 0x1f;
+	tm->tm_mon = in_8(&regs->month) - 1;
+	tm->tm_year = in_be16(&regs->year) - 1900;
+	tm->tm_wday = (tmp >> 5) % 7;
+	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+	tm->tm_isdst = 0;
+
+	return 0;
+}
+
+static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+	struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+	mpc5121_rtc_update_smh(regs, tm);
+
+	/* date */
+	out_8(&regs->month_set, tm->tm_mon + 1);
+	out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
+	out_8(&regs->date_set, tm->tm_mday);
+	out_be16(&regs->year_set, tm->tm_year + 1900);
+
+	/* set date sequence */
+	out_8(&regs->set_date, 0x1);
+	out_8(&regs->set_date, 0x3);
+	out_8(&regs->set_date, 0x1);
+	out_8(&regs->set_date, 0x0);
+
+	return 0;
+}
+
 static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
 {
 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 	struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
@@ -248,11 +298,18 @@ static const struct rtc_class_ops mpc5121_rtc_ops = {
 	.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
 	.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
 };
 };
 
 
+static const struct rtc_class_ops mpc5200_rtc_ops = {
+	.read_time = mpc5200_rtc_read_time,
+	.set_time = mpc5200_rtc_set_time,
+	.read_alarm = mpc5121_rtc_read_alarm,
+	.set_alarm = mpc5121_rtc_set_alarm,
+	.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
+};
+
 static int __devinit mpc5121_rtc_probe(struct platform_device *op)
 static int __devinit mpc5121_rtc_probe(struct platform_device *op)
 {
 {
 	struct mpc5121_rtc_data *rtc;
 	struct mpc5121_rtc_data *rtc;
 	int err = 0;
 	int err = 0;
-	u32 ka;
 
 
 	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
 	rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
 	if (!rtc)
 	if (!rtc)
@@ -287,15 +344,22 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op)
 		goto out_dispose2;
 		goto out_dispose2;
 	}
 	}
 
 
-	ka = in_be32(&rtc->regs->keep_alive);
-	if (ka & 0x02) {
-		dev_warn(&op->dev,
-			"mpc5121-rtc: Battery or oscillator failure!\n");
-		out_be32(&rtc->regs->keep_alive, ka);
+	if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
+		u32 ka;
+		ka = in_be32(&rtc->regs->keep_alive);
+		if (ka & 0x02) {
+			dev_warn(&op->dev,
+				"mpc5121-rtc: Battery or oscillator failure!\n");
+			out_be32(&rtc->regs->keep_alive, ka);
+		}
+
+		rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
+						&mpc5121_rtc_ops, THIS_MODULE);
+	} else {
+		rtc->rtc = rtc_device_register("mpc5200-rtc", &op->dev,
+						&mpc5200_rtc_ops, THIS_MODULE);
 	}
 	}
 
 
-	rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
-					&mpc5121_rtc_ops, THIS_MODULE);
 	if (IS_ERR(rtc->rtc)) {
 	if (IS_ERR(rtc->rtc)) {
 		err = PTR_ERR(rtc->rtc);
 		err = PTR_ERR(rtc->rtc);
 		goto out_free_irq;
 		goto out_free_irq;
@@ -340,6 +404,7 @@ static int __devexit mpc5121_rtc_remove(struct platform_device *op)
 
 
 static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
 static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
 	{ .compatible = "fsl,mpc5121-rtc", },
 	{ .compatible = "fsl,mpc5121-rtc", },
+	{ .compatible = "fsl,mpc5200-rtc", },
 	{},
 	{},
 };
 };
 
 

+ 550 - 0
drivers/rtc/rtc-pm8xxx.c

@@ -0,0 +1,550 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/rtc.h>
+
+
+/* RTC Register offsets from RTC CTRL REG */
+#define PM8XXX_ALARM_CTRL_OFFSET	0x01
+#define PM8XXX_RTC_WRITE_OFFSET		0x02
+#define PM8XXX_RTC_READ_OFFSET		0x06
+#define PM8XXX_ALARM_RW_OFFSET		0x0A
+
+/* RTC_CTRL register bit fields */
+#define PM8xxx_RTC_ENABLE		BIT(7)
+#define PM8xxx_RTC_ALARM_ENABLE		BIT(1)
+#define PM8xxx_RTC_ALARM_CLEAR		BIT(0)
+
+#define NUM_8_BIT_RTC_REGS		0x4
+
+/**
+ * struct pm8xxx_rtc -  rtc driver internal structure
+ * @rtc:		rtc device for this driver.
+ * @rtc_alarm_irq:	rtc alarm irq number.
+ * @rtc_base:		address of rtc control register.
+ * @rtc_read_base:	base address of read registers.
+ * @rtc_write_base:	base address of write registers.
+ * @alarm_rw_base:	base address of alarm registers.
+ * @ctrl_reg:		rtc control register.
+ * @rtc_dev:		device structure.
+ * @ctrl_reg_lock:	spinlock protecting access to ctrl_reg.
+ */
+struct pm8xxx_rtc {
+	struct rtc_device *rtc;
+	int rtc_alarm_irq;
+	int rtc_base;
+	int rtc_read_base;
+	int rtc_write_base;
+	int alarm_rw_base;
+	u8  ctrl_reg;
+	struct device *rtc_dev;
+	spinlock_t ctrl_reg_lock;
+};
+
+/*
+ * The RTC registers need to be read/written one byte at a time. This is a
+ * hardware limitation.
+ */
+static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
+		int base, int count)
+{
+	int i, rc;
+	struct device *parent = rtc_dd->rtc_dev->parent;
+
+	for (i = 0; i < count; i++) {
+		rc = pm8xxx_readb(parent, base + i, &rtc_val[i]);
+		if (rc < 0) {
+			dev_err(rtc_dd->rtc_dev, "PMIC read failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
+		int base, int count)
+{
+	int i, rc;
+	struct device *parent = rtc_dd->rtc_dev->parent;
+
+	for (i = 0; i < count; i++) {
+		rc = pm8xxx_writeb(parent, base + i, rtc_val[i]);
+		if (rc < 0) {
+			dev_err(rtc_dd->rtc_dev, "PMIC write failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Steps to write the RTC registers.
+ * 1. Disable alarm if enabled.
+ * 2. Write 0x00 to LSB.
+ * 3. Write Byte[1], Byte[2], Byte[3] then Byte[0].
+ * 4. Enable alarm if disabled in step 1.
+ */
+static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc, i;
+	unsigned long secs, irq_flags;
+	u8 value[NUM_8_BIT_RTC_REGS], reg = 0, alarm_enabled = 0, ctrl_reg;
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rtc_tm_to_time(tm, &secs);
+
+	for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
+		value[i] = secs & 0xFF;
+		secs >>= 8;
+	}
+
+	dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
+
+	spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+	ctrl_reg = rtc_dd->ctrl_reg;
+
+	if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) {
+		alarm_enabled = 1;
+		ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+		rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
+				1);
+		if (rc < 0) {
+			dev_err(dev, "Write to RTC control register "
+								"failed\n");
+			goto rtc_rw_fail;
+		}
+		rtc_dd->ctrl_reg = ctrl_reg;
+	} else
+		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+	/* Write 0 to Byte[0] */
+	reg = 0;
+	rc = pm8xxx_write_wrapper(rtc_dd, &reg, rtc_dd->rtc_write_base, 1);
+	if (rc < 0) {
+		dev_err(dev, "Write to RTC write data register failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Write Byte[1], Byte[2], Byte[3] */
+	rc = pm8xxx_write_wrapper(rtc_dd, value + 1,
+					rtc_dd->rtc_write_base + 1, 3);
+	if (rc < 0) {
+		dev_err(dev, "Write to RTC write data register failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Write Byte[0] */
+	rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->rtc_write_base, 1);
+	if (rc < 0) {
+		dev_err(dev, "Write to RTC write data register failed\n");
+		goto rtc_rw_fail;
+	}
+
+	if (alarm_enabled) {
+		ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
+		rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
+									1);
+		if (rc < 0) {
+			dev_err(dev, "Write to RTC control register "
+								"failed\n");
+			goto rtc_rw_fail;
+		}
+		rtc_dd->ctrl_reg = ctrl_reg;
+	}
+
+rtc_rw_fail:
+	if (alarm_enabled)
+		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+	return rc;
+}
+
+static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	u8 value[NUM_8_BIT_RTC_REGS], reg;
+	unsigned long secs;
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->rtc_read_base,
+							NUM_8_BIT_RTC_REGS);
+	if (rc < 0) {
+		dev_err(dev, "RTC read data register failed\n");
+		return rc;
+	}
+
+	/*
+	 * Read the LSB again and check if there has been a carry over.
+	 * If there is, redo the read operation.
+	 */
+	rc = pm8xxx_read_wrapper(rtc_dd, &reg, rtc_dd->rtc_read_base, 1);
+	if (rc < 0) {
+		dev_err(dev, "RTC read data register failed\n");
+		return rc;
+	}
+
+	if (unlikely(reg < value[0])) {
+		rc = pm8xxx_read_wrapper(rtc_dd, value,
+				rtc_dd->rtc_read_base, NUM_8_BIT_RTC_REGS);
+		if (rc < 0) {
+			dev_err(dev, "RTC read data register failed\n");
+			return rc;
+		}
+	}
+
+	secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+
+	rtc_time_to_tm(secs, tm);
+
+	rc = rtc_valid_tm(tm);
+	if (rc < 0) {
+		dev_err(dev, "Invalid time read from RTC\n");
+		return rc;
+	}
+
+	dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
+				secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
+				tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+	return 0;
+}
+
+static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	int rc, i;
+	u8 value[NUM_8_BIT_RTC_REGS], ctrl_reg;
+	unsigned long secs, irq_flags;
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rtc_tm_to_time(&alarm->time, &secs);
+
+	for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
+		value[i] = secs & 0xFF;
+		secs >>= 8;
+	}
+
+	spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+	rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
+							NUM_8_BIT_RTC_REGS);
+	if (rc < 0) {
+		dev_err(dev, "Write to RTC ALARM register failed\n");
+		goto rtc_rw_fail;
+	}
+
+	ctrl_reg = rtc_dd->ctrl_reg;
+	ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
+					(ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
+
+	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
+	if (rc < 0) {
+		dev_err(dev, "Write to RTC control register failed\n");
+		goto rtc_rw_fail;
+	}
+
+	rtc_dd->ctrl_reg = ctrl_reg;
+
+	dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+				alarm->time.tm_hour, alarm->time.tm_min,
+				alarm->time.tm_sec, alarm->time.tm_mday,
+				alarm->time.tm_mon, alarm->time.tm_year);
+rtc_rw_fail:
+	spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+	return rc;
+}
+
+static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	int rc;
+	u8 value[NUM_8_BIT_RTC_REGS];
+	unsigned long secs;
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
+			NUM_8_BIT_RTC_REGS);
+	if (rc < 0) {
+		dev_err(dev, "RTC alarm time read failed\n");
+		return rc;
+	}
+
+	secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+
+	rtc_time_to_tm(secs, &alarm->time);
+
+	rc = rtc_valid_tm(&alarm->time);
+	if (rc < 0) {
+		dev_err(dev, "Invalid alarm time read from RTC\n");
+		return rc;
+	}
+
+	dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+				alarm->time.tm_hour, alarm->time.tm_min,
+				alarm->time.tm_sec, alarm->time.tm_mday,
+				alarm->time.tm_mon, alarm->time.tm_year);
+
+	return 0;
+}
+
+static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+	int rc;
+	unsigned long irq_flags;
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+	u8 ctrl_reg;
+
+	spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+	ctrl_reg = rtc_dd->ctrl_reg;
+	ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
+				(ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
+
+	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
+	if (rc < 0) {
+		dev_err(dev, "Write to RTC control register failed\n");
+		goto rtc_rw_fail;
+	}
+
+	rtc_dd->ctrl_reg = ctrl_reg;
+
+rtc_rw_fail:
+	spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+	return rc;
+}
+
+static struct rtc_class_ops pm8xxx_rtc_ops = {
+	.read_time	= pm8xxx_rtc_read_time,
+	.set_alarm	= pm8xxx_rtc_set_alarm,
+	.read_alarm	= pm8xxx_rtc_read_alarm,
+	.alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
+{
+	struct pm8xxx_rtc *rtc_dd = dev_id;
+	u8 ctrl_reg;
+	int rc;
+	unsigned long irq_flags;
+
+	rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
+
+	spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+	/* Clear the alarm enable bit */
+	ctrl_reg = rtc_dd->ctrl_reg;
+	ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
+
+	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
+	if (rc < 0) {
+		spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+		dev_err(rtc_dd->rtc_dev, "Write to RTC control register "
+								"failed\n");
+		goto rtc_alarm_handled;
+	}
+
+	rtc_dd->ctrl_reg = ctrl_reg;
+	spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+	/* Clear RTC alarm register */
+	rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
+						PM8XXX_ALARM_CTRL_OFFSET, 1);
+	if (rc < 0) {
+		dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read "
+								"failed\n");
+		goto rtc_alarm_handled;
+	}
+
+	ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR;
+	rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
+						PM8XXX_ALARM_CTRL_OFFSET, 1);
+	if (rc < 0)
+		dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register"
+								" failed\n");
+
+rtc_alarm_handled:
+	return IRQ_HANDLED;
+}
+
+static int __devinit pm8xxx_rtc_probe(struct platform_device *pdev)
+{
+	int rc;
+	u8 ctrl_reg;
+	bool rtc_write_enable = false;
+	struct pm8xxx_rtc *rtc_dd;
+	struct resource *rtc_resource;
+	const struct pm8xxx_rtc_platform_data *pdata =
+						dev_get_platdata(&pdev->dev);
+
+	if (pdata != NULL)
+		rtc_write_enable = pdata->rtc_write_enable;
+
+	rtc_dd = kzalloc(sizeof(*rtc_dd), GFP_KERNEL);
+	if (rtc_dd == NULL) {
+		dev_err(&pdev->dev, "Unable to allocate memory!\n");
+		return -ENOMEM;
+	}
+
+	/* Initialise spinlock to protect RTC control register */
+	spin_lock_init(&rtc_dd->ctrl_reg_lock);
+
+	rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
+	if (rtc_dd->rtc_alarm_irq < 0) {
+		dev_err(&pdev->dev, "Alarm IRQ resource absent!\n");
+		rc = -ENXIO;
+		goto fail_rtc_enable;
+	}
+
+	rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO,
+							"pmic_rtc_base");
+	if (!(rtc_resource && rtc_resource->start)) {
+		dev_err(&pdev->dev, "RTC IO resource absent!\n");
+		rc = -ENXIO;
+		goto fail_rtc_enable;
+	}
+
+	rtc_dd->rtc_base = rtc_resource->start;
+
+	/* Setup RTC register addresses */
+	rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET;
+	rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET;
+	rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET;
+
+	rtc_dd->rtc_dev = &pdev->dev;
+
+	/* Check if the RTC is on, else turn it on */
+	rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "RTC control register read failed!\n");
+		goto fail_rtc_enable;
+	}
+
+	if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
+		ctrl_reg |= PM8xxx_RTC_ENABLE;
+		rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
+									1);
+		if (rc < 0) {
+			dev_err(&pdev->dev, "Write to RTC control register "
+								"failed\n");
+			goto fail_rtc_enable;
+		}
+	}
+
+	rtc_dd->ctrl_reg = ctrl_reg;
+	if (rtc_write_enable == true)
+		pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time;
+
+	platform_set_drvdata(pdev, rtc_dd);
+
+	/* Register the RTC device */
+	rtc_dd->rtc = rtc_device_register("pm8xxx_rtc", &pdev->dev,
+				&pm8xxx_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc_dd->rtc)) {
+		dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
+					__func__, PTR_ERR(rtc_dd->rtc));
+		rc = PTR_ERR(rtc_dd->rtc);
+		goto fail_rtc_enable;
+	}
+
+	/* Request the alarm IRQ */
+	rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
+				 pm8xxx_alarm_trigger, IRQF_TRIGGER_RISING,
+				 "pm8xxx_rtc_alarm", rtc_dd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
+		goto fail_req_irq;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	dev_dbg(&pdev->dev, "Probe success !!\n");
+
+	return 0;
+
+fail_req_irq:
+	rtc_device_unregister(rtc_dd->rtc);
+fail_rtc_enable:
+	platform_set_drvdata(pdev, NULL);
+	kfree(rtc_dd);
+	return rc;
+}
+
+static int __devexit pm8xxx_rtc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
+	rtc_device_unregister(rtc_dd->rtc);
+	platform_set_drvdata(pdev, NULL);
+	kfree(rtc_dd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm8xxx_rtc_resume(struct device *dev)
+{
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(rtc_dd->rtc_alarm_irq);
+
+	return 0;
+}
+
+static int pm8xxx_rtc_suspend(struct device *dev)
+{
+	struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(rtc_dd->rtc_alarm_irq);
+
+	return 0;
+}
+#endif
+
+SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume);
+
+static struct platform_driver pm8xxx_rtc_driver = {
+	.probe		= pm8xxx_rtc_probe,
+	.remove		= __devexit_p(pm8xxx_rtc_remove),
+	.driver	= {
+		.name	= PM8XXX_RTC_DEV_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &pm8xxx_rtc_pm_ops,
+	},
+};
+
+static int __init pm8xxx_rtc_init(void)
+{
+	return platform_driver_register(&pm8xxx_rtc_driver);
+}
+module_init(pm8xxx_rtc_init);
+
+static void __exit pm8xxx_rtc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_rtc_driver);
+}
+module_exit(pm8xxx_rtc_exit);
+
+MODULE_ALIAS("platform:rtc-pm8xxx");
+MODULE_DESCRIPTION("PMIC8xxx RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");

+ 26 - 1
drivers/rtc/rtc-s3c.c

@@ -57,11 +57,13 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
 {
 {
 	struct rtc_device *rdev = id;
 	struct rtc_device *rdev = id;
 
 
+	clk_enable(rtc_clk);
 	rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
 	rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
 
 
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 		writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
 		writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
 
 
+	clk_disable(rtc_clk);
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -69,11 +71,13 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
 {
 {
 	struct rtc_device *rdev = id;
 	struct rtc_device *rdev = id;
 
 
+	clk_enable(rtc_clk);
 	rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
 	rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
 
 
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 		writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
 		writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
 
 
+	clk_disable(rtc_clk);
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -84,12 +88,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
 
 
 	pr_debug("%s: aie=%d\n", __func__, enabled);
 	pr_debug("%s: aie=%d\n", __func__, enabled);
 
 
+	clk_enable(rtc_clk);
 	tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
 	tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
 
 
 	if (enabled)
 	if (enabled)
 		tmp |= S3C2410_RTCALM_ALMEN;
 		tmp |= S3C2410_RTCALM_ALMEN;
 
 
 	writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
 	writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
+	clk_disable(rtc_clk);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -103,6 +109,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
 	if (!is_power_of_2(freq))
 	if (!is_power_of_2(freq))
 		return -EINVAL;
 		return -EINVAL;
 
 
+	clk_enable(rtc_clk);
 	spin_lock_irq(&s3c_rtc_pie_lock);
 	spin_lock_irq(&s3c_rtc_pie_lock);
 
 
 	if (s3c_rtc_cpu_type == TYPE_S3C2410) {
 	if (s3c_rtc_cpu_type == TYPE_S3C2410) {
@@ -114,6 +121,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
 
 
 	writel(tmp, s3c_rtc_base + S3C2410_TICNT);
 	writel(tmp, s3c_rtc_base + S3C2410_TICNT);
 	spin_unlock_irq(&s3c_rtc_pie_lock);
 	spin_unlock_irq(&s3c_rtc_pie_lock);
+	clk_disable(rtc_clk);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -125,6 +133,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
 	unsigned int have_retried = 0;
 	unsigned int have_retried = 0;
 	void __iomem *base = s3c_rtc_base;
 	void __iomem *base = s3c_rtc_base;
 
 
+	clk_enable(rtc_clk);
  retry_get_time:
  retry_get_time:
 	rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);
 	rtc_tm->tm_min  = readb(base + S3C2410_RTCMIN);
 	rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
 	rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
@@ -157,6 +166,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
 	rtc_tm->tm_year += 100;
 	rtc_tm->tm_year += 100;
 	rtc_tm->tm_mon -= 1;
 	rtc_tm->tm_mon -= 1;
 
 
+	clk_disable(rtc_clk);
 	return rtc_valid_tm(rtc_tm);
 	return rtc_valid_tm(rtc_tm);
 }
 }
 
 
@@ -165,6 +175,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
 	void __iomem *base = s3c_rtc_base;
 	void __iomem *base = s3c_rtc_base;
 	int year = tm->tm_year - 100;
 	int year = tm->tm_year - 100;
 
 
+	clk_enable(rtc_clk);
 	pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
 	pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
 		 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
 		 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
 		 tm->tm_hour, tm->tm_min, tm->tm_sec);
 		 tm->tm_hour, tm->tm_min, tm->tm_sec);
@@ -182,6 +193,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
 	writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
 	writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
 	writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
 	writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
 	writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
 	writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
+	clk_disable(rtc_clk);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -192,6 +204,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	void __iomem *base = s3c_rtc_base;
 	void __iomem *base = s3c_rtc_base;
 	unsigned int alm_en;
 	unsigned int alm_en;
 
 
+	clk_enable(rtc_clk);
 	alm_tm->tm_sec  = readb(base + S3C2410_ALMSEC);
 	alm_tm->tm_sec  = readb(base + S3C2410_ALMSEC);
 	alm_tm->tm_min  = readb(base + S3C2410_ALMMIN);
 	alm_tm->tm_min  = readb(base + S3C2410_ALMMIN);
 	alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
 	alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
@@ -243,6 +256,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	else
 	else
 		alm_tm->tm_year = -1;
 		alm_tm->tm_year = -1;
 
 
+	clk_disable(rtc_clk);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -252,6 +266,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 	void __iomem *base = s3c_rtc_base;
 	void __iomem *base = s3c_rtc_base;
 	unsigned int alrm_en;
 	unsigned int alrm_en;
 
 
+	clk_enable(rtc_clk);
 	pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
 	pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
 		 alrm->enabled,
 		 alrm->enabled,
 		 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
 		 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
@@ -282,6 +297,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 
 
 	s3c_rtc_setaie(dev, alrm->enabled);
 	s3c_rtc_setaie(dev, alrm->enabled);
 
 
+	clk_disable(rtc_clk);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -289,6 +305,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
 {
 {
 	unsigned int ticnt;
 	unsigned int ticnt;
 
 
+	clk_enable(rtc_clk);
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
 		ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
 		ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
 		ticnt &= S3C64XX_RTCCON_TICEN;
 		ticnt &= S3C64XX_RTCCON_TICEN;
@@ -298,6 +315,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
 	}
 	}
 
 
 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
 	seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt  ? "yes" : "no");
+	clk_disable(rtc_clk);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -360,6 +378,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
 	if (s3c_rtc_base == NULL)
 	if (s3c_rtc_base == NULL)
 		return;
 		return;
 
 
+	clk_enable(rtc_clk);
 	if (!en) {
 	if (!en) {
 		tmp = readw(base + S3C2410_RTCCON);
 		tmp = readw(base + S3C2410_RTCCON);
 		if (s3c_rtc_cpu_type == TYPE_S3C64XX)
 		if (s3c_rtc_cpu_type == TYPE_S3C64XX)
@@ -399,6 +418,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
 				base + S3C2410_RTCCON);
 				base + S3C2410_RTCCON);
 		}
 		}
 	}
 	}
+	clk_disable(rtc_clk);
 }
 }
 
 
 static int __devexit s3c_rtc_remove(struct platform_device *dev)
 static int __devexit s3c_rtc_remove(struct platform_device *dev)
@@ -410,7 +430,6 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
 
 
 	s3c_rtc_setaie(&dev->dev, 0);
 	s3c_rtc_setaie(&dev->dev, 0);
 
 
-	clk_disable(rtc_clk);
 	clk_put(rtc_clk);
 	clk_put(rtc_clk);
 	rtc_clk = NULL;
 	rtc_clk = NULL;
 
 
@@ -529,6 +548,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
 
 
 	s3c_rtc_setfreq(&pdev->dev, 1);
 	s3c_rtc_setfreq(&pdev->dev, 1);
 
 
+	clk_disable(rtc_clk);
+
 	return 0;
 	return 0;
 
 
  err_nortc:
  err_nortc:
@@ -554,6 +575,7 @@ static int ticnt_save, ticnt_en_save;
 
 
 static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
 static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
 {
 {
+	clk_enable(rtc_clk);
 	/* save TICNT for anyone using periodic interrupts */
 	/* save TICNT for anyone using periodic interrupts */
 	ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
 	ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
@@ -568,6 +590,7 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
 		else
 		else
 			dev_err(&pdev->dev, "enable_irq_wake failed\n");
 			dev_err(&pdev->dev, "enable_irq_wake failed\n");
 	}
 	}
+	clk_disable(rtc_clk);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -576,6 +599,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
 {
 {
 	unsigned int tmp;
 	unsigned int tmp;
 
 
+	clk_enable(rtc_clk);
 	s3c_rtc_enable(pdev, 1);
 	s3c_rtc_enable(pdev, 1);
 	writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
 	writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
 	if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
@@ -587,6 +611,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
 		disable_irq_wake(s3c_rtc_alarmno);
 		disable_irq_wake(s3c_rtc_alarmno);
 		wake_en = false;
 		wake_en = false;
 	}
 	}
+	clk_disable(rtc_clk);
 
 
 	return 0;
 	return 0;
 }
 }

+ 1 - 1
drivers/rtc/rtc-tegra.c

@@ -343,7 +343,7 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
 
 
 	/* set context info. */
 	/* set context info. */
 	info->pdev = pdev;
 	info->pdev = pdev;
-	info->tegra_rtc_lock = __SPIN_LOCK_UNLOCKED(info->tegra_rtc_lock);
+	spin_lock_init(&info->tegra_rtc_lock);
 
 
 	platform_set_drvdata(pdev, info);
 	platform_set_drvdata(pdev, info);
 
 

+ 1 - 1
drivers/rtc/rtc-twl.c

@@ -275,7 +275,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
 		goto out;
 		goto out;
 
 
 	save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
 	save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
-	twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+	ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
 	if (ret < 0)
 	if (ret < 0)
 		goto out;
 		goto out;
 
 

+ 8 - 0
drivers/video/backlight/Kconfig

@@ -117,6 +117,14 @@ config LCD_LD9040
 	  If you have an LD9040 Panel, say Y to enable its
 	  If you have an LD9040 Panel, say Y to enable its
 	  control driver.
 	  control driver.
 
 
+config LCD_AMS369FG06
+	tristate "AMS369FG06 AMOLED LCD Driver"
+	depends on SPI && BACKLIGHT_CLASS_DEVICE
+	default n
+	help
+	  If you have an AMS369FG06 AMOLED Panel, say Y to enable its
+	  LCD control driver.
+
 endif # LCD_CLASS_DEVICE
 endif # LCD_CLASS_DEVICE
 
 
 #
 #

+ 1 - 0
drivers/video/backlight/Makefile

@@ -13,6 +13,7 @@ obj-$(CONFIG_LCD_TDO24M)	   += tdo24m.o
 obj-$(CONFIG_LCD_TOSA)		   += tosa_lcd.o
 obj-$(CONFIG_LCD_TOSA)		   += tosa_lcd.o
 obj-$(CONFIG_LCD_S6E63M0)	+= s6e63m0.o
 obj-$(CONFIG_LCD_S6E63M0)	+= s6e63m0.o
 obj-$(CONFIG_LCD_LD9040)	+= ld9040.o
 obj-$(CONFIG_LCD_LD9040)	+= ld9040.o
+obj-$(CONFIG_LCD_AMS369FG06)	+= ams369fg06.o
 
 
 obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
 obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
 obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o
 obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o

+ 1 - 2
drivers/video/backlight/adp8860_bl.c

@@ -722,8 +722,7 @@ static int __devinit adp8860_probe(struct i2c_client *client,
 		goto out2;
 		goto out2;
 	}
 	}
 
 
-	bl->props.max_brightness =
-		bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
+	bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
 
 
 	data->bl = bl;
 	data->bl = bl;
 
 

+ 646 - 0
drivers/video/backlight/ams369fg06.c

@@ -0,0 +1,646 @@
+/*
+ * ams369fg06 AMOLED LCD panel driver.
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han  <jg1.han@samsung.com>
+ *
+ * Derived from drivers/video/s6e63m0.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/wait.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/lcd.h>
+#include <linux/backlight.h>
+
+#define SLEEPMSEC		0x1000
+#define ENDDEF			0x2000
+#define	DEFMASK			0xFF00
+#define COMMAND_ONLY		0xFE
+#define DATA_ONLY		0xFF
+
+#define MAX_GAMMA_LEVEL		5
+#define GAMMA_TABLE_COUNT	21
+
+#define MIN_BRIGHTNESS		0
+#define MAX_BRIGHTNESS		255
+#define DEFAULT_BRIGHTNESS	150
+
+struct ams369fg06 {
+	struct device			*dev;
+	struct spi_device		*spi;
+	unsigned int			power;
+	struct lcd_device		*ld;
+	struct backlight_device		*bd;
+	struct lcd_platform_data	*lcd_pd;
+};
+
+static const unsigned short seq_display_on[] = {
+	0x14, 0x03,
+	ENDDEF, 0x0000
+};
+
+static const unsigned short seq_display_off[] = {
+	0x14, 0x00,
+	ENDDEF, 0x0000
+};
+
+static const unsigned short seq_stand_by_on[] = {
+	0x1D, 0xA1,
+	SLEEPMSEC, 200,
+	ENDDEF, 0x0000
+};
+
+static const unsigned short seq_stand_by_off[] = {
+	0x1D, 0xA0,
+	SLEEPMSEC, 250,
+	ENDDEF, 0x0000
+};
+
+static const unsigned short seq_setting[] = {
+	0x31, 0x08,
+	0x32, 0x14,
+	0x30, 0x02,
+	0x27, 0x01,
+	0x12, 0x08,
+	0x13, 0x08,
+	0x15, 0x00,
+	0x16, 0x00,
+
+	0xef, 0xd0,
+	DATA_ONLY, 0xe8,
+
+	0x39, 0x44,
+	0x40, 0x00,
+	0x41, 0x3f,
+	0x42, 0x2a,
+	0x43, 0x27,
+	0x44, 0x27,
+	0x45, 0x1f,
+	0x46, 0x44,
+	0x50, 0x00,
+	0x51, 0x00,
+	0x52, 0x17,
+	0x53, 0x24,
+	0x54, 0x26,
+	0x55, 0x1f,
+	0x56, 0x43,
+	0x60, 0x00,
+	0x61, 0x3f,
+	0x62, 0x2a,
+	0x63, 0x25,
+	0x64, 0x24,
+	0x65, 0x1b,
+	0x66, 0x5c,
+
+	0x17, 0x22,
+	0x18, 0x33,
+	0x19, 0x03,
+	0x1a, 0x01,
+	0x22, 0xa4,
+	0x23, 0x00,
+	0x26, 0xa0,
+
+	0x1d, 0xa0,
+	SLEEPMSEC, 300,
+
+	0x14, 0x03,
+
+	ENDDEF, 0x0000
+};
+
+/* gamma value: 2.2 */
+static const unsigned int ams369fg06_22_250[] = {
+	0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
+	0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
+	0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
+};
+
+static const unsigned int ams369fg06_22_200[] = {
+	0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
+	0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
+	0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
+};
+
+static const unsigned int ams369fg06_22_150[] = {
+	0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
+	0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
+	0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
+};
+
+static const unsigned int ams369fg06_22_100[] = {
+	0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
+	0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
+	0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
+};
+
+static const unsigned int ams369fg06_22_50[] = {
+	0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
+	0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
+	0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
+};
+
+struct ams369fg06_gamma {
+	unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
+};
+
+static struct ams369fg06_gamma gamma_table = {
+	.gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
+	.gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
+	.gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
+	.gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
+	.gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
+};
+
+static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
+{
+	u16 buf[1];
+	struct spi_message msg;
+
+	struct spi_transfer xfer = {
+		.len		= 2,
+		.tx_buf		= buf,
+	};
+
+	buf[0] = (addr << 8) | data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	return spi_sync(lcd->spi, &msg);
+}
+
+static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
+	unsigned char command)
+{
+	int ret = 0;
+
+	if (address != DATA_ONLY)
+		ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
+	if (command != COMMAND_ONLY)
+		ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
+
+	return ret;
+}
+
+static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
+	const unsigned short *wbuf)
+{
+	int ret = 0, i = 0;
+
+	while ((wbuf[i] & DEFMASK) != ENDDEF) {
+		if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
+			ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
+			if (ret)
+				break;
+		} else
+			mdelay(wbuf[i+1]);
+		i += 2;
+	}
+
+	return ret;
+}
+
+static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
+	const unsigned int *gamma)
+{
+	unsigned int i = 0;
+	int ret = 0;
+
+	for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
+		ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
+		ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
+		ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
+		if (ret) {
+			dev_err(lcd->dev, "failed to set gamma table.\n");
+			goto gamma_err;
+		}
+	}
+
+gamma_err:
+	return ret;
+}
+
+static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
+{
+	int ret = 0;
+	int gamma = 0;
+
+	if ((brightness >= 0) && (brightness <= 50))
+		gamma = 0;
+	else if ((brightness > 50) && (brightness <= 100))
+		gamma = 1;
+	else if ((brightness > 100) && (brightness <= 150))
+		gamma = 2;
+	else if ((brightness > 150) && (brightness <= 200))
+		gamma = 3;
+	else if ((brightness > 200) && (brightness <= 255))
+		gamma = 4;
+
+	ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
+
+	return ret;
+}
+
+static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
+{
+	int ret, i;
+	static const unsigned short *init_seq[] = {
+		seq_setting,
+		seq_stand_by_off,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
+{
+	int ret, i;
+	static const unsigned short *init_seq[] = {
+		seq_stand_by_off,
+		seq_display_on,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
+{
+	int ret, i;
+
+	static const unsigned short *init_seq[] = {
+		seq_display_off,
+		seq_stand_by_on,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int ams369fg06_power_is_on(int power)
+{
+	return ((power) <= FB_BLANK_NORMAL);
+}
+
+static int ams369fg06_power_on(struct ams369fg06 *lcd)
+{
+	int ret = 0;
+	struct lcd_platform_data *pd = NULL;
+	struct backlight_device *bd = NULL;
+
+	pd = lcd->lcd_pd;
+	if (!pd) {
+		dev_err(lcd->dev, "platform data is NULL.\n");
+		return -EFAULT;
+	}
+
+	bd = lcd->bd;
+	if (!bd) {
+		dev_err(lcd->dev, "backlight device is NULL.\n");
+		return -EFAULT;
+	}
+
+	if (!pd->power_on) {
+		dev_err(lcd->dev, "power_on is NULL.\n");
+		return -EFAULT;
+	} else {
+		pd->power_on(lcd->ld, 1);
+		mdelay(pd->power_on_delay);
+	}
+
+	if (!pd->reset) {
+		dev_err(lcd->dev, "reset is NULL.\n");
+		return -EFAULT;
+	} else {
+		pd->reset(lcd->ld);
+		mdelay(pd->reset_delay);
+	}
+
+	ret = ams369fg06_ldi_init(lcd);
+	if (ret) {
+		dev_err(lcd->dev, "failed to initialize ldi.\n");
+		return ret;
+	}
+
+	ret = ams369fg06_ldi_enable(lcd);
+	if (ret) {
+		dev_err(lcd->dev, "failed to enable ldi.\n");
+		return ret;
+	}
+
+	/* set brightness to current value after power on or resume. */
+	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
+	if (ret) {
+		dev_err(lcd->dev, "lcd gamma setting failed.\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ams369fg06_power_off(struct ams369fg06 *lcd)
+{
+	int ret = 0;
+	struct lcd_platform_data *pd = NULL;
+
+	pd = lcd->lcd_pd;
+	if (!pd) {
+		dev_err(lcd->dev, "platform data is NULL\n");
+		return -EFAULT;
+	}
+
+	ret = ams369fg06_ldi_disable(lcd);
+	if (ret) {
+		dev_err(lcd->dev, "lcd setting failed.\n");
+		return -EIO;
+	}
+
+	mdelay(pd->power_off_delay);
+
+	if (!pd->power_on) {
+		dev_err(lcd->dev, "power_on is NULL.\n");
+		return -EFAULT;
+	} else
+		pd->power_on(lcd->ld, 0);
+
+	return 0;
+}
+
+static int ams369fg06_power(struct ams369fg06 *lcd, int power)
+{
+	int ret = 0;
+
+	if (ams369fg06_power_is_on(power) &&
+		!ams369fg06_power_is_on(lcd->power))
+		ret = ams369fg06_power_on(lcd);
+	else if (!ams369fg06_power_is_on(power) &&
+		ams369fg06_power_is_on(lcd->power))
+		ret = ams369fg06_power_off(lcd);
+
+	if (!ret)
+		lcd->power = power;
+
+	return ret;
+}
+
+static int ams369fg06_get_power(struct lcd_device *ld)
+{
+	struct ams369fg06 *lcd = lcd_get_data(ld);
+
+	return lcd->power;
+}
+
+static int ams369fg06_set_power(struct lcd_device *ld, int power)
+{
+	struct ams369fg06 *lcd = lcd_get_data(ld);
+
+	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
+		power != FB_BLANK_NORMAL) {
+		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
+		return -EINVAL;
+	}
+
+	return ams369fg06_power(lcd, power);
+}
+
+static int ams369fg06_get_brightness(struct backlight_device *bd)
+{
+	return bd->props.brightness;
+}
+
+static int ams369fg06_set_brightness(struct backlight_device *bd)
+{
+	int ret = 0;
+	int brightness = bd->props.brightness;
+	struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev);
+
+	if (brightness < MIN_BRIGHTNESS ||
+		brightness > bd->props.max_brightness) {
+		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
+			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
+		return -EINVAL;
+	}
+
+	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
+	if (ret) {
+		dev_err(&bd->dev, "lcd brightness setting failed.\n");
+		return -EIO;
+	}
+
+	return ret;
+}
+
+static struct lcd_ops ams369fg06_lcd_ops = {
+	.get_power = ams369fg06_get_power,
+	.set_power = ams369fg06_set_power,
+};
+
+static const struct backlight_ops ams369fg06_backlight_ops = {
+	.get_brightness = ams369fg06_get_brightness,
+	.update_status = ams369fg06_set_brightness,
+};
+
+static int __devinit ams369fg06_probe(struct spi_device *spi)
+{
+	int ret = 0;
+	struct ams369fg06 *lcd = NULL;
+	struct lcd_device *ld = NULL;
+	struct backlight_device *bd = NULL;
+	struct backlight_properties props;
+
+	lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL);
+	if (!lcd)
+		return -ENOMEM;
+
+	/* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
+	spi->bits_per_word = 16;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "spi setup failed.\n");
+		goto out_free_lcd;
+	}
+
+	lcd->spi = spi;
+	lcd->dev = &spi->dev;
+
+	lcd->lcd_pd = spi->dev.platform_data;
+	if (!lcd->lcd_pd) {
+		dev_err(&spi->dev, "platform data is NULL\n");
+		goto out_free_lcd;
+	}
+
+	ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
+		&ams369fg06_lcd_ops);
+	if (IS_ERR(ld)) {
+		ret = PTR_ERR(ld);
+		goto out_free_lcd;
+	}
+
+	lcd->ld = ld;
+
+	memset(&props, 0, sizeof(struct backlight_properties));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = MAX_BRIGHTNESS;
+
+	bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
+		&ams369fg06_backlight_ops, &props);
+	if (IS_ERR(bd)) {
+		ret =  PTR_ERR(bd);
+		goto out_lcd_unregister;
+	}
+
+	bd->props.brightness = DEFAULT_BRIGHTNESS;
+	lcd->bd = bd;
+
+	if (!lcd->lcd_pd->lcd_enabled) {
+		/*
+		 * if lcd panel was off from bootloader then
+		 * current lcd status is powerdown and then
+		 * it enables lcd panel.
+		 */
+		lcd->power = FB_BLANK_POWERDOWN;
+
+		ams369fg06_power(lcd, FB_BLANK_UNBLANK);
+	} else
+		lcd->power = FB_BLANK_UNBLANK;
+
+	dev_set_drvdata(&spi->dev, lcd);
+
+	dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
+
+	return 0;
+
+out_lcd_unregister:
+	lcd_device_unregister(ld);
+out_free_lcd:
+	kfree(lcd);
+	return ret;
+}
+
+static int __devexit ams369fg06_remove(struct spi_device *spi)
+{
+	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+	ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+	backlight_device_unregister(lcd->bd);
+	lcd_device_unregister(lcd->ld);
+	kfree(lcd);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM)
+static unsigned int before_power;
+
+static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+	int ret = 0;
+	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
+
+	before_power = lcd->power;
+
+	/*
+	 * when lcd panel is suspend, lcd panel becomes off
+	 * regardless of status.
+	 */
+	ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+
+	return ret;
+}
+
+static int ams369fg06_resume(struct spi_device *spi)
+{
+	int ret = 0;
+	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+	/*
+	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
+	 * (at that time, before_power is FB_BLANK_UNBLANK) then
+	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
+	 */
+	if (before_power == FB_BLANK_UNBLANK)
+		lcd->power = FB_BLANK_POWERDOWN;
+
+	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
+
+	ret = ams369fg06_power(lcd, before_power);
+
+	return ret;
+}
+#else
+#define ams369fg06_suspend	NULL
+#define ams369fg06_resume	NULL
+#endif
+
+static void ams369fg06_shutdown(struct spi_device *spi)
+{
+	struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
+
+	ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver ams369fg06_driver = {
+	.driver = {
+		.name	= "ams369fg06",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ams369fg06_probe,
+	.remove		= __devexit_p(ams369fg06_remove),
+	.shutdown	= ams369fg06_shutdown,
+	.suspend	= ams369fg06_suspend,
+	.resume		= ams369fg06_resume,
+};
+
+static int __init ams369fg06_init(void)
+{
+	return spi_register_driver(&ams369fg06_driver);
+}
+
+static void __exit ams369fg06_exit(void)
+{
+	spi_unregister_driver(&ams369fg06_driver);
+}
+
+module_init(ams369fg06_init);
+module_exit(ams369fg06_exit);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("ams369fg06 LCD Driver");
+MODULE_LICENSE("GPL");

+ 12 - 5
drivers/video/backlight/ld9040.c

@@ -668,6 +668,7 @@ static int ld9040_probe(struct spi_device *spi)
 	struct ld9040 *lcd = NULL;
 	struct ld9040 *lcd = NULL;
 	struct lcd_device *ld = NULL;
 	struct lcd_device *ld = NULL;
 	struct backlight_device *bd = NULL;
 	struct backlight_device *bd = NULL;
+	struct backlight_properties props;
 
 
 	lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
 	lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
 	if (!lcd)
 	if (!lcd)
@@ -699,14 +700,17 @@ static int ld9040_probe(struct spi_device *spi)
 
 
 	lcd->ld = ld;
 	lcd->ld = ld;
 
 
+	memset(&props, 0, sizeof(struct backlight_properties));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = MAX_BRIGHTNESS;
+
 	bd = backlight_device_register("ld9040-bl", &spi->dev,
 	bd = backlight_device_register("ld9040-bl", &spi->dev,
-		lcd, &ld9040_backlight_ops, NULL);
-	if (IS_ERR(ld)) {
-		ret = PTR_ERR(ld);
-		goto out_free_lcd;
+		lcd, &ld9040_backlight_ops, &props);
+	if (IS_ERR(bd)) {
+		ret = PTR_ERR(bd);
+		goto out_unregister_lcd;
 	}
 	}
 
 
-	bd->props.max_brightness = MAX_BRIGHTNESS;
 	bd->props.brightness = MAX_BRIGHTNESS;
 	bd->props.brightness = MAX_BRIGHTNESS;
 	lcd->bd = bd;
 	lcd->bd = bd;
 
 
@@ -731,6 +735,8 @@ static int ld9040_probe(struct spi_device *spi)
 	dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
 	dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
 	return 0;
 	return 0;
 
 
+out_unregister_lcd:
+	lcd_device_unregister(lcd->ld);
 out_free_lcd:
 out_free_lcd:
 	kfree(lcd);
 	kfree(lcd);
 	return ret;
 	return ret;
@@ -741,6 +747,7 @@ static int __devexit ld9040_remove(struct spi_device *spi)
 	struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
 	struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
 
 
 	ld9040_power(lcd, FB_BLANK_POWERDOWN);
 	ld9040_power(lcd, FB_BLANK_POWERDOWN);
+	backlight_device_unregister(lcd->bd);
 	lcd_device_unregister(lcd->ld);
 	lcd_device_unregister(lcd->ld);
 	kfree(lcd);
 	kfree(lcd);
 
 

+ 7 - 4
drivers/video/backlight/s6e63m0.c

@@ -738,6 +738,7 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
 	struct s6e63m0 *lcd = NULL;
 	struct s6e63m0 *lcd = NULL;
 	struct lcd_device *ld = NULL;
 	struct lcd_device *ld = NULL;
 	struct backlight_device *bd = NULL;
 	struct backlight_device *bd = NULL;
+	struct backlight_properties props;
 
 
 	lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
 	lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
 	if (!lcd)
 	if (!lcd)
@@ -769,16 +770,18 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
 
 
 	lcd->ld = ld;
 	lcd->ld = ld;
 
 
+	memset(&props, 0, sizeof(struct backlight_properties));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = MAX_BRIGHTNESS;
+
 	bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
 	bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
-		&s6e63m0_backlight_ops, NULL);
+		&s6e63m0_backlight_ops, &props);
 	if (IS_ERR(bd)) {
 	if (IS_ERR(bd)) {
 		ret =  PTR_ERR(bd);
 		ret =  PTR_ERR(bd);
 		goto out_lcd_unregister;
 		goto out_lcd_unregister;
 	}
 	}
 
 
-	bd->props.max_brightness = MAX_BRIGHTNESS;
 	bd->props.brightness = MAX_BRIGHTNESS;
 	bd->props.brightness = MAX_BRIGHTNESS;
-	bd->props.type = BACKLIGHT_RAW;
 	lcd->bd = bd;
 	lcd->bd = bd;
 
 
 	/*
 	/*
@@ -840,7 +843,7 @@ static int __devexit s6e63m0_remove(struct spi_device *spi)
 }
 }
 
 
 #if defined(CONFIG_PM)
 #if defined(CONFIG_PM)
-unsigned int before_power;
+static unsigned int before_power;
 
 
 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
 static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
 {
 {

+ 30 - 0
drivers/xen/Kconfig

@@ -26,6 +26,36 @@ config XEN_SELFBALLOONING
 	  kernel boot parameter.  Note that systems without a sufficiently
 	  kernel boot parameter.  Note that systems without a sufficiently
 	  large swap device should not enable self-ballooning.
 	  large swap device should not enable self-ballooning.
 
 
+config XEN_BALLOON_MEMORY_HOTPLUG
+	bool "Memory hotplug support for Xen balloon driver"
+	default n
+	depends on XEN_BALLOON && MEMORY_HOTPLUG
+	help
+	  Memory hotplug support for Xen balloon driver allows expanding memory
+	  available for the system above limit declared at system startup.
+	  It is very useful on critical systems which require long
+	  run without rebooting.
+
+	  Memory could be hotplugged in following steps:
+
+	    1) dom0: xl mem-max <domU> <maxmem>
+	       where <maxmem> is >= requested memory size,
+
+	    2) dom0: xl mem-set <domU> <memory>
+	       where <memory> is requested memory size; alternatively memory
+	       could be added by writing proper value to
+	       /sys/devices/system/xen_memory/xen_memory0/target or
+	       /sys/devices/system/xen_memory/xen_memory0/target_kb on dumU,
+
+	    3) domU: for i in /sys/devices/system/memory/memory*/state; do \
+	               [ "`cat "$i"`" = offline ] && echo online > "$i"; done
+
+	  Memory could be onlined automatically on domU by adding following line to udev rules:
+
+	  SUBSYSTEM=="memory", ACTION=="add", RUN+="/bin/sh -c '[ -f /sys$devpath/state ] && echo online > /sys$devpath/state'"
+
+	  In that case step 3 should be omitted.
+
 config XEN_SCRUB_PAGES
 config XEN_SCRUB_PAGES
 	bool "Scrub pages before returning them to system"
 	bool "Scrub pages before returning them to system"
 	depends on XEN_BALLOON
 	depends on XEN_BALLOON

+ 137 - 2
drivers/xen/balloon.c

@@ -4,6 +4,12 @@
  * Copyright (c) 2003, B Dragovic
  * Copyright (c) 2003, B Dragovic
  * Copyright (c) 2003-2004, M Williamson, K Fraser
  * Copyright (c) 2003-2004, M Williamson, K Fraser
  * Copyright (c) 2005 Dan M. Smith, IBM Corporation
  * Copyright (c) 2005 Dan M. Smith, IBM Corporation
+ * Copyright (c) 2010 Daniel Kiper
+ *
+ * Memory hotplug support was written by Daniel Kiper. Work on
+ * it was sponsored by Google under Google Summer of Code 2010
+ * program. Jeremy Fitzhardinge from Citrix was the mentor for
+ * this project.
  *
  *
  * This program is free software; you can redistribute it and/or
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License version 2
  * modify it under the terms of the GNU General Public License version 2
@@ -40,6 +46,9 @@
 #include <linux/mutex.h>
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/gfp.h>
 #include <linux/gfp.h>
+#include <linux/notifier.h>
+#include <linux/memory.h>
+#include <linux/memory_hotplug.h>
 
 
 #include <asm/page.h>
 #include <asm/page.h>
 #include <asm/pgalloc.h>
 #include <asm/pgalloc.h>
@@ -194,6 +203,87 @@ static enum bp_state update_schedule(enum bp_state state)
 	return BP_EAGAIN;
 	return BP_EAGAIN;
 }
 }
 
 
+#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
+static long current_credit(void)
+{
+	return balloon_stats.target_pages - balloon_stats.current_pages -
+		balloon_stats.hotplug_pages;
+}
+
+static bool balloon_is_inflated(void)
+{
+	if (balloon_stats.balloon_low || balloon_stats.balloon_high ||
+			balloon_stats.balloon_hotplug)
+		return true;
+	else
+		return false;
+}
+
+/*
+ * reserve_additional_memory() adds memory region of size >= credit above
+ * max_pfn. New region is section aligned and size is modified to be multiple
+ * of section size. Those features allow optimal use of address space and
+ * establish proper alignment when this function is called first time after
+ * boot (last section not fully populated at boot time contains unused memory
+ * pages with PG_reserved bit not set; online_pages_range() does not allow page
+ * onlining in whole range if first onlined page does not have PG_reserved
+ * bit set). Real size of added memory is established at page onlining stage.
+ */
+
+static enum bp_state reserve_additional_memory(long credit)
+{
+	int nid, rc;
+	u64 hotplug_start_paddr;
+	unsigned long balloon_hotplug = credit;
+
+	hotplug_start_paddr = PFN_PHYS(SECTION_ALIGN_UP(max_pfn));
+	balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION);
+	nid = memory_add_physaddr_to_nid(hotplug_start_paddr);
+
+	rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT);
+
+	if (rc) {
+		pr_info("xen_balloon: %s: add_memory() failed: %i\n", __func__, rc);
+		return BP_EAGAIN;
+	}
+
+	balloon_hotplug -= credit;
+
+	balloon_stats.hotplug_pages += credit;
+	balloon_stats.balloon_hotplug = balloon_hotplug;
+
+	return BP_DONE;
+}
+
+static void xen_online_page(struct page *page)
+{
+	__online_page_set_limits(page);
+
+	mutex_lock(&balloon_mutex);
+
+	__balloon_append(page);
+
+	if (balloon_stats.hotplug_pages)
+		--balloon_stats.hotplug_pages;
+	else
+		--balloon_stats.balloon_hotplug;
+
+	mutex_unlock(&balloon_mutex);
+}
+
+static int xen_memory_notifier(struct notifier_block *nb, unsigned long val, void *v)
+{
+	if (val == MEM_ONLINE)
+		schedule_delayed_work(&balloon_worker, 0);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xen_memory_nb = {
+	.notifier_call = xen_memory_notifier,
+	.priority = 0
+};
+#else
 static long current_credit(void)
 static long current_credit(void)
 {
 {
 	unsigned long target = balloon_stats.target_pages;
 	unsigned long target = balloon_stats.target_pages;
@@ -206,6 +296,21 @@ static long current_credit(void)
 	return target - balloon_stats.current_pages;
 	return target - balloon_stats.current_pages;
 }
 }
 
 
+static bool balloon_is_inflated(void)
+{
+	if (balloon_stats.balloon_low || balloon_stats.balloon_high)
+		return true;
+	else
+		return false;
+}
+
+static enum bp_state reserve_additional_memory(long credit)
+{
+	balloon_stats.target_pages = balloon_stats.current_pages;
+	return BP_DONE;
+}
+#endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */
+
 static enum bp_state increase_reservation(unsigned long nr_pages)
 static enum bp_state increase_reservation(unsigned long nr_pages)
 {
 {
 	int rc;
 	int rc;
@@ -217,6 +322,15 @@ static enum bp_state increase_reservation(unsigned long nr_pages)
 		.domid        = DOMID_SELF
 		.domid        = DOMID_SELF
 	};
 	};
 
 
+#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
+	if (!balloon_stats.balloon_low && !balloon_stats.balloon_high) {
+		nr_pages = min(nr_pages, balloon_stats.balloon_hotplug);
+		balloon_stats.hotplug_pages += nr_pages;
+		balloon_stats.balloon_hotplug -= nr_pages;
+		return BP_DONE;
+	}
+#endif
+
 	if (nr_pages > ARRAY_SIZE(frame_list))
 	if (nr_pages > ARRAY_SIZE(frame_list))
 		nr_pages = ARRAY_SIZE(frame_list);
 		nr_pages = ARRAY_SIZE(frame_list);
 
 
@@ -279,6 +393,15 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
 		.domid        = DOMID_SELF
 		.domid        = DOMID_SELF
 	};
 	};
 
 
+#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
+	if (balloon_stats.hotplug_pages) {
+		nr_pages = min(nr_pages, balloon_stats.hotplug_pages);
+		balloon_stats.hotplug_pages -= nr_pages;
+		balloon_stats.balloon_hotplug += nr_pages;
+		return BP_DONE;
+	}
+#endif
+
 	if (nr_pages > ARRAY_SIZE(frame_list))
 	if (nr_pages > ARRAY_SIZE(frame_list))
 		nr_pages = ARRAY_SIZE(frame_list);
 		nr_pages = ARRAY_SIZE(frame_list);
 
 
@@ -340,8 +463,12 @@ static void balloon_process(struct work_struct *work)
 	do {
 	do {
 		credit = current_credit();
 		credit = current_credit();
 
 
-		if (credit > 0)
-			state = increase_reservation(credit);
+		if (credit > 0) {
+			if (balloon_is_inflated())
+				state = increase_reservation(credit);
+			else
+				state = reserve_additional_memory(credit);
+		}
 
 
 		if (credit < 0)
 		if (credit < 0)
 			state = decrease_reservation(-credit, GFP_BALLOON);
 			state = decrease_reservation(-credit, GFP_BALLOON);
@@ -448,6 +575,14 @@ static int __init balloon_init(void)
 	balloon_stats.retry_count = 1;
 	balloon_stats.retry_count = 1;
 	balloon_stats.max_retry_count = RETRY_UNLIMITED;
 	balloon_stats.max_retry_count = RETRY_UNLIMITED;
 
 
+#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
+	balloon_stats.hotplug_pages = 0;
+	balloon_stats.balloon_hotplug = 0;
+
+	set_online_page_callback(&xen_online_page);
+	register_memory_notifier(&xen_memory_nb);
+#endif
+
 	/*
 	/*
 	 * Initialise the balloon with excess memory space.  We need
 	 * Initialise the balloon with excess memory space.  We need
 	 * to make sure we don't add memory which doesn't exist or
 	 * to make sure we don't add memory which doesn't exist or

+ 1 - 1
fs/hugetlbfs/inode.c

@@ -94,7 +94,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
 	vma->vm_flags |= VM_HUGETLB | VM_RESERVED;
 	vma->vm_flags |= VM_HUGETLB | VM_RESERVED;
 	vma->vm_ops = &hugetlb_vm_ops;
 	vma->vm_ops = &hugetlb_vm_ops;
 
 
-	if (vma->vm_pgoff & ~(huge_page_mask(h) >> PAGE_SHIFT))
+	if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	vma_len = (loff_t)(vma->vm_end - vma->vm_start);
 	vma_len = (loff_t)(vma->vm_end - vma->vm_start);

+ 3 - 4
fs/proc/base.c

@@ -1118,10 +1118,9 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
 	 * Warn that /proc/pid/oom_adj is deprecated, see
 	 * Warn that /proc/pid/oom_adj is deprecated, see
 	 * Documentation/feature-removal-schedule.txt.
 	 * Documentation/feature-removal-schedule.txt.
 	 */
 	 */
-	printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, "
-			"please use /proc/%d/oom_score_adj instead.\n",
-			current->comm, task_pid_nr(current),
-			task_pid_nr(task), task_pid_nr(task));
+	WARN_ONCE(1, "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
+		  current->comm, task_pid_nr(current), task_pid_nr(task),
+		  task_pid_nr(task));
 	task->signal->oom_adj = oom_adjust;
 	task->signal->oom_adj = oom_adjust;
 	/*
 	/*
 	 * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum
 	 * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum

+ 2 - 6
fs/reiserfs/bitmap.c

@@ -214,7 +214,7 @@ static int scan_bitmap_block(struct reiserfs_transaction_handle *th,
 					}
 					}
 					/* otherwise we clear all bit were set ... */
 					/* otherwise we clear all bit were set ... */
 					while (--i >= *beg)
 					while (--i >= *beg)
-						reiserfs_test_and_clear_le_bit
+						reiserfs_clear_le_bit
 						    (i, bh->b_data);
 						    (i, bh->b_data);
 					reiserfs_restore_prepared_buffer(s, bh);
 					reiserfs_restore_prepared_buffer(s, bh);
 					*beg = org;
 					*beg = org;
@@ -1222,15 +1222,11 @@ void reiserfs_cache_bitmap_metadata(struct super_block *sb,
 	info->free_count = 0;
 	info->free_count = 0;
 
 
 	while (--cur >= (unsigned long *)bh->b_data) {
 	while (--cur >= (unsigned long *)bh->b_data) {
-		int i;
-
 		/* 0 and ~0 are special, we can optimize for them */
 		/* 0 and ~0 are special, we can optimize for them */
 		if (*cur == 0)
 		if (*cur == 0)
 			info->free_count += BITS_PER_LONG;
 			info->free_count += BITS_PER_LONG;
 		else if (*cur != ~0L)	/* A mix, investigate */
 		else if (*cur != ~0L)	/* A mix, investigate */
-			for (i = BITS_PER_LONG - 1; i >= 0; i--)
-				if (!reiserfs_test_le_bit(i, cur))
-					info->free_count++;
+			info->free_count += BITS_PER_LONG - hweight_long(*cur);
 	}
 	}
 }
 }
 
 

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно