mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-24 15:56:49 +00:00
goldfish: R.I.P.
It is broken and it is not maintained by anyone since long time. Signed-off-by: Gabor Juhos <juhosg@openwrt.org> SVN-Revision: 34766
This commit is contained in:
parent
979bc5536d
commit
db831511d2
@ -1,25 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2006-2009 OpenWrt.org
|
|
||||||
#
|
|
||||||
# This is free software, licensed under the GNU General Public License v2.
|
|
||||||
# See /LICENSE for more information.
|
|
||||||
#
|
|
||||||
include $(TOPDIR)/rules.mk
|
|
||||||
|
|
||||||
ARCH:=arm
|
|
||||||
BOARD:=goldfish
|
|
||||||
BOARDNAME:=Goldfish (Android Emulator)
|
|
||||||
FEATURES:=broken
|
|
||||||
CFLAGS:=-O2 -pipe -march=armv5te -mtune=xscale -fno-caller-saves
|
|
||||||
|
|
||||||
LINUX_VERSION:=2.6.30.10
|
|
||||||
|
|
||||||
DEVICE_TYPE=phone
|
|
||||||
|
|
||||||
define Target/Description
|
|
||||||
Android emulator virtual platform
|
|
||||||
endef
|
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/target.mk
|
|
||||||
|
|
||||||
$(eval $(call BuildTarget))
|
|
@ -1,199 +0,0 @@
|
|||||||
CONFIG_ALIGNMENT_TRAP=y
|
|
||||||
# CONFIG_APM_EMULATION is not set
|
|
||||||
CONFIG_ARCH_GOLDFISH=y
|
|
||||||
# CONFIG_ARCH_HAS_HOLES_MEMORYMODEL is not set
|
|
||||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
|
|
||||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
|
|
||||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
|
||||||
CONFIG_ARM=y
|
|
||||||
CONFIG_ARM_THUMB=y
|
|
||||||
# CONFIG_BACKTRACE_SELF_TEST is not set
|
|
||||||
CONFIG_BATTERY_GOLDFISH=y
|
|
||||||
# CONFIG_BINARY_PRINTF is not set
|
|
||||||
CONFIG_BITREVERSE=y
|
|
||||||
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
|
|
||||||
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
|
|
||||||
# CONFIG_BOOT_PRINTK_DELAY is not set
|
|
||||||
CONFIG_CMDLINE="console=/dev/ttyS0 root=mtdblock0 rootdelay=1 rootfstype=yaffs2"
|
|
||||||
# CONFIG_CONSOLE_EARLYSUSPEND is not set
|
|
||||||
CONFIG_CONSOLE_TRANSLATIONS=y
|
|
||||||
CONFIG_CPU_32=y
|
|
||||||
CONFIG_CPU_32v5=y
|
|
||||||
CONFIG_CPU_ABRT_EV5TJ=y
|
|
||||||
CONFIG_CPU_ARM926T=y
|
|
||||||
# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
|
|
||||||
CONFIG_CPU_CACHE_VIVT=y
|
|
||||||
CONFIG_CPU_COPY_V4WB=y
|
|
||||||
CONFIG_CPU_CP15=y
|
|
||||||
CONFIG_CPU_CP15_MMU=y
|
|
||||||
# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
|
|
||||||
# CONFIG_CPU_ICACHE_DISABLE is not set
|
|
||||||
CONFIG_CPU_PABRT_NOIFAR=y
|
|
||||||
CONFIG_CPU_TLB_V4WBI=y
|
|
||||||
CONFIG_CRYPTO_AEAD2=y
|
|
||||||
CONFIG_CRYPTO_BLKCIPHER2=y
|
|
||||||
CONFIG_CRYPTO_HASH2=y
|
|
||||||
CONFIG_CRYPTO_MANAGER2=y
|
|
||||||
CONFIG_CRYPTO_RNG2=y
|
|
||||||
CONFIG_CRYPTO_WORKQUEUE=y
|
|
||||||
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
|
|
||||||
CONFIG_DEBUG_BUGVERBOSE=y
|
|
||||||
# CONFIG_DEBUG_DEVRES is not set
|
|
||||||
# CONFIG_DEBUG_DRIVER is not set
|
|
||||||
# CONFIG_DEBUG_ERRORS is not set
|
|
||||||
# CONFIG_DEBUG_INFO is not set
|
|
||||||
CONFIG_DEBUG_KERNEL=y
|
|
||||||
# CONFIG_DEBUG_KOBJECT is not set
|
|
||||||
# CONFIG_DEBUG_LIST is not set
|
|
||||||
# CONFIG_DEBUG_LL is not set
|
|
||||||
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
|
|
||||||
# CONFIG_DEBUG_LOCK_ALLOC is not set
|
|
||||||
# CONFIG_DEBUG_MUTEXES is not set
|
|
||||||
# CONFIG_DEBUG_NOTIFIERS is not set
|
|
||||||
# CONFIG_DEBUG_OBJECTS is not set
|
|
||||||
# CONFIG_DEBUG_RT_MUTEXES is not set
|
|
||||||
# CONFIG_DEBUG_SG is not set
|
|
||||||
# CONFIG_DEBUG_SHIRQ is not set
|
|
||||||
# CONFIG_DEBUG_SLAB is not set
|
|
||||||
# CONFIG_DEBUG_SPINLOCK is not set
|
|
||||||
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
|
|
||||||
# CONFIG_DEBUG_STACK_USAGE is not set
|
|
||||||
# CONFIG_DEBUG_USER is not set
|
|
||||||
# CONFIG_DEBUG_VM is not set
|
|
||||||
# CONFIG_DEBUG_WRITECOUNT is not set
|
|
||||||
# CONFIG_DETECT_HUNG_TASK is not set
|
|
||||||
CONFIG_DETECT_SOFTLOCKUP=y
|
|
||||||
CONFIG_DEVKMEM=y
|
|
||||||
# CONFIG_DM9000 is not set
|
|
||||||
CONFIG_DUMMY_CONSOLE=y
|
|
||||||
CONFIG_EARLYSUSPEND=y
|
|
||||||
# CONFIG_FAULT_INJECTION is not set
|
|
||||||
CONFIG_FB=y
|
|
||||||
CONFIG_FB_CFB_COPYAREA=y
|
|
||||||
CONFIG_FB_CFB_FILLRECT=y
|
|
||||||
CONFIG_FB_CFB_IMAGEBLIT=y
|
|
||||||
CONFIG_FB_EARLYSUSPEND=y
|
|
||||||
CONFIG_FB_GOLDFISH=y
|
|
||||||
CONFIG_FB_MODE_HELPERS=y
|
|
||||||
CONFIG_FB_TILEBLITTING=y
|
|
||||||
# CONFIG_FIRMWARE_EDID is not set
|
|
||||||
# CONFIG_FONTS is not set
|
|
||||||
CONFIG_FONT_8x16=y
|
|
||||||
CONFIG_FONT_8x8=y
|
|
||||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
|
||||||
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
|
|
||||||
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
|
|
||||||
CONFIG_FRAME_POINTER=y
|
|
||||||
CONFIG_FREEZER=y
|
|
||||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
|
||||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
|
||||||
CONFIG_GENERIC_FIND_LAST_BIT=y
|
|
||||||
# CONFIG_GENERIC_GPIO is not set
|
|
||||||
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
|
|
||||||
CONFIG_GOLDFISH_TTY=y
|
|
||||||
# CONFIG_HAMRADIO is not set
|
|
||||||
CONFIG_HARDIRQS_SW_RESEND=y
|
|
||||||
CONFIG_HAS_DMA=y
|
|
||||||
CONFIG_HAS_EARLYSUSPEND=y
|
|
||||||
CONFIG_HAS_IOMEM=y
|
|
||||||
CONFIG_HAS_IOPORT=y
|
|
||||||
CONFIG_HAS_WAKELOCK=y
|
|
||||||
CONFIG_HAVE_AOUT=y
|
|
||||||
CONFIG_HAVE_ARCH_KGDB=y
|
|
||||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
|
||||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
|
||||||
CONFIG_HAVE_IDE=y
|
|
||||||
CONFIG_HAVE_KPROBES=y
|
|
||||||
CONFIG_HAVE_KRETPROBES=y
|
|
||||||
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
|
|
||||||
CONFIG_HAVE_MLOCK=y
|
|
||||||
CONFIG_HAVE_OPROFILE=y
|
|
||||||
CONFIG_HID=y
|
|
||||||
CONFIG_HID_SUPPORT=y
|
|
||||||
CONFIG_HW_CONSOLE=y
|
|
||||||
# CONFIG_HW_RANDOM is not set
|
|
||||||
CONFIG_INITRAMFS_SOURCE=""
|
|
||||||
CONFIG_INPUT=y
|
|
||||||
CONFIG_INPUT_EVDEV=y
|
|
||||||
CONFIG_INPUT_GPIO=y
|
|
||||||
CONFIG_INPUT_KEYBOARD=y
|
|
||||||
CONFIG_INPUT_KEYRESET=y
|
|
||||||
CONFIG_INPUT_MOUSEDEV=y
|
|
||||||
CONFIG_INPUT_MOUSEDEV_PSAUX=y
|
|
||||||
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
|
|
||||||
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
|
|
||||||
CONFIG_INPUT_TOUCHSCREEN=y
|
|
||||||
# CONFIG_JFFS2_CMODE_PRIORITY is not set
|
|
||||||
CONFIG_JFFS2_CMODE_SIZE=y
|
|
||||||
# CONFIG_KEYBOARD_ATKBD is not set
|
|
||||||
CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
|
|
||||||
# CONFIG_KEYBOARD_LKKBD is not set
|
|
||||||
# CONFIG_KEYBOARD_NEWTON is not set
|
|
||||||
# CONFIG_KEYBOARD_STOWAWAY is not set
|
|
||||||
# CONFIG_KEYBOARD_SUNKBD is not set
|
|
||||||
# CONFIG_KEYBOARD_XTKBD is not set
|
|
||||||
# CONFIG_KGDB is not set
|
|
||||||
# CONFIG_LOCK_STAT is not set
|
|
||||||
# CONFIG_LOGO is not set
|
|
||||||
CONFIG_MACH_GOLDFISH=y
|
|
||||||
# CONFIG_MMC_GOLDFISH is not set
|
|
||||||
# CONFIG_MTD_CFI is not set
|
|
||||||
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
|
|
||||||
CONFIG_MTD_CONCAT=y
|
|
||||||
CONFIG_MTD_GOLDFISH_NAND=y
|
|
||||||
CONFIG_MTD_NAND=y
|
|
||||||
CONFIG_MTD_UBI=y
|
|
||||||
CONFIG_MTD_UBI_BEB_RESERVE=1
|
|
||||||
# CONFIG_MTD_UBI_DEBUG is not set
|
|
||||||
CONFIG_MTD_UBI_GLUEBI=y
|
|
||||||
CONFIG_MTD_UBI_WL_THRESHOLD=4096
|
|
||||||
# CONFIG_NETDEV_1000 is not set
|
|
||||||
# CONFIG_NO_IOPORT is not set
|
|
||||||
# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set
|
|
||||||
# CONFIG_OUTER_CACHE is not set
|
|
||||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
|
||||||
CONFIG_PAGE_OFFSET=0xC0000000
|
|
||||||
# CONFIG_PAGE_POISONING is not set
|
|
||||||
# CONFIG_PCI_SYSCALL is not set
|
|
||||||
# CONFIG_PDA_POWER is not set
|
|
||||||
CONFIG_PM=y
|
|
||||||
# CONFIG_PM_DEBUG is not set
|
|
||||||
CONFIG_PM_SLEEP=y
|
|
||||||
CONFIG_POWER_SUPPLY=y
|
|
||||||
# CONFIG_PROVE_LOCKING is not set
|
|
||||||
CONFIG_QEMU_TRACE=y
|
|
||||||
# CONFIG_RCU_TORTURE_TEST is not set
|
|
||||||
# CONFIG_RD_LZMA is not set
|
|
||||||
CONFIG_RTC_CLASS=y
|
|
||||||
# CONFIG_RTC_DRV_CMOS is not set
|
|
||||||
CONFIG_RTC_DRV_GOLDFISH=y
|
|
||||||
# CONFIG_RT_MUTEX_TESTER is not set
|
|
||||||
# CONFIG_SCHEDSTATS is not set
|
|
||||||
CONFIG_SCHED_DEBUG=y
|
|
||||||
# CONFIG_SCSI_DMA is not set
|
|
||||||
# CONFIG_SERIAL_8250 is not set
|
|
||||||
# CONFIG_SLOW_WORK is not set
|
|
||||||
CONFIG_SMC91X=y
|
|
||||||
CONFIG_SPLIT_PTLOCK_CPUS=4096
|
|
||||||
CONFIG_SUSPEND=y
|
|
||||||
CONFIG_SUSPEND_FREEZER=y
|
|
||||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
|
||||||
# CONFIG_TIMER_STATS is not set
|
|
||||||
CONFIG_TRACING_SUPPORT=y
|
|
||||||
# CONFIG_UBIFS_FS is not set
|
|
||||||
CONFIG_UEVENT_HELPER_PATH=""
|
|
||||||
CONFIG_UID16=y
|
|
||||||
CONFIG_USER_WAKELOCK=y
|
|
||||||
CONFIG_VECTORS_BASE=0xffff0000
|
|
||||||
CONFIG_VFP=y
|
|
||||||
# CONFIG_VGA_CONSOLE is not set
|
|
||||||
CONFIG_VIDEO_OUTPUT_CONTROL=y
|
|
||||||
CONFIG_VT=y
|
|
||||||
CONFIG_VT_CONSOLE=y
|
|
||||||
CONFIG_VT_HW_CONSOLE_BINDING=y
|
|
||||||
CONFIG_WAKELOCK=y
|
|
||||||
CONFIG_WAKELOCK_STAT=y
|
|
||||||
# CONFIG_WATCHDOG is not set
|
|
||||||
CONFIG_ZBOOT_ROM_BSS=0
|
|
||||||
CONFIG_ZBOOT_ROM_TEXT=0
|
|
||||||
CONFIG_ZONE_DMA_FLAG=0
|
|
@ -1,40 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2006-2010 OpenWrt.org
|
|
||||||
#
|
|
||||||
# This is free software, licensed under the GNU General Public License v2.
|
|
||||||
# See /LICENSE for more information.
|
|
||||||
#
|
|
||||||
include $(TOPDIR)/rules.mk
|
|
||||||
include $(INCLUDE_DIR)/image.mk
|
|
||||||
|
|
||||||
JFFS2_BLOCKSIZE=124k
|
|
||||||
JFFS2OPTS += -n -s 2048
|
|
||||||
|
|
||||||
define Image/BuildKernel
|
|
||||||
$(TARGET_CROSS)objcopy -O binary -R .note -R .comment -S \
|
|
||||||
$(LINUX_DIR)/arch/arm/boot/compressed/vmlinux $(BIN_DIR)/$(IMG_PREFIX)-kernel.bin
|
|
||||||
$(if $(CONFIG_TARGET_ROOTFS_INITRAMFS), \
|
|
||||||
$(CP) $(LINUX_DIR)/usr/initramfs_data.cpio.gz, \
|
|
||||||
gzip -c < $(LINUX_DIR)/usr/initramfs_data.cpio > \
|
|
||||||
) $(BIN_DIR)/$(IMG_PREFIX)-ramdisk.bin
|
|
||||||
$(CP) ./run-emulator.sh $(BIN_DIR)/
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Image/Build/jffs2-124k
|
|
||||||
$(CP) ./ubinize.cfg $(KDIR)/
|
|
||||||
(cd $(KDIR); \
|
|
||||||
ubinize \
|
|
||||||
-o $(BIN_DIR)/$(IMG_PREFIX)-$(1).img \
|
|
||||||
-p 128KiB -m 2KiB -s 2KiB ubinize.cfg; \
|
|
||||||
)
|
|
||||||
nand_ecc \
|
|
||||||
$(BIN_DIR)/$(IMG_PREFIX)-$(1).img \
|
|
||||||
$(BIN_DIR)/$(IMG_PREFIX)-system.bin
|
|
||||||
endef
|
|
||||||
|
|
||||||
define Image/Build
|
|
||||||
$(call Image/Build/$(1),$(1))
|
|
||||||
endef
|
|
||||||
|
|
||||||
|
|
||||||
$(eval $(call BuildImage))
|
|
@ -1,3 +0,0 @@
|
|||||||
PREFIX=openwrt-goldfish-
|
|
||||||
touch ${PREFIX}data.bin
|
|
||||||
./goldfish-qemu/emulator -skindir ./goldfish-qemu/skins -skin HVGA -sysdir . -ramdisk ${PREFIX}ramdisk.bin -kernel ${PREFIX}kernel.bin -system ${PREFIX}system.bin -data ${PREFIX}data.bin -show-kernel -partition-size 100 -qemu -append "console=ttyS0 ubi.mtd=0 root=/dev/mtdblock3 rootfstype=jffs2 bootdelay=1 init=/etc/preinit" -net nic,vlan=0,model=smc91c111 -net user,vlan=0,hostname=OpenWrt
|
|
@ -1,14 +0,0 @@
|
|||||||
[rootfs]
|
|
||||||
# Volume mode (other option is static)
|
|
||||||
mode=ubi
|
|
||||||
# Source image
|
|
||||||
image=root.jffs2-124k
|
|
||||||
# Volume ID in UBI image
|
|
||||||
vol_id=0
|
|
||||||
# Allow for dynamic resize
|
|
||||||
vol_type=dynamic
|
|
||||||
# Volume name
|
|
||||||
vol_name=rootfs
|
|
||||||
# Autoresize volume at first mount
|
|
||||||
vol_flags=autoresize
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
|||||||
From 0c61b75f9da1a0889959a0f9bd0b8b63f936ddf3 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Tony Lindgren <tony@atomide.com>
|
|
||||||
Date: Mon, 9 May 2005 14:10:26 -0700
|
|
||||||
Subject: [PATCH 042/134] ARM: Make low-level printk work
|
|
||||||
|
|
||||||
Makes low-level printk work.
|
|
||||||
|
|
||||||
Signed-off-by: Tony Lindgren <tony@atomide.com>
|
|
||||||
---
|
|
||||||
kernel/printk.c | 8 ++++++++
|
|
||||||
1 files changed, 8 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/printk.c
|
|
||||||
+++ b/kernel/printk.c
|
|
||||||
@@ -45,6 +45,10 @@ void asmlinkage __attribute__((weak)) ea
|
|
||||||
|
|
||||||
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
|
|
||||||
|
|
||||||
+#ifdef CONFIG_DEBUG_LL
|
|
||||||
+extern void printascii(char *);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/* printk's without a loglevel use this.. */
|
|
||||||
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
|
|
||||||
|
|
||||||
@@ -687,6 +691,10 @@ asmlinkage int vprintk(const char *fmt,
|
|
||||||
sizeof(printk_buf) - printed_len, fmt, args);
|
|
||||||
|
|
||||||
|
|
||||||
+#ifdef CONFIG_DEBUG_LL
|
|
||||||
+ printascii(printk_buf);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Copy the output into log_buf. If the caller didn't provide
|
|
||||||
* appropriate log level tags, we insert them here
|
|
@ -1,66 +0,0 @@
|
|||||||
From 3742e6638bdb7325c6432e2a145ad985ee47d052 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Mon, 26 Jan 2009 19:13:47 -0800
|
|
||||||
Subject: [PATCH 052/134] lowmemorykiller: Only iterate over process list when needed.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Use NR_ACTIVE plus NR_INACTIVE as a size estimate for our fake cache
|
|
||||||
instead the sum of rss. Neither method is accurate.
|
|
||||||
|
|
||||||
Also skip the process scan, if the amount of memory available is above
|
|
||||||
the largest threshold set.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/staging/android/lowmemorykiller.c | 35 +++++++++++++++++-----------
|
|
||||||
1 files changed, 21 insertions(+), 14 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/staging/android/lowmemorykiller.c
|
|
||||||
+++ b/drivers/staging/android/lowmemorykiller.c
|
|
||||||
@@ -71,23 +71,30 @@ static int lowmem_shrink(int nr_to_scan,
|
|
||||||
}
|
|
||||||
if(nr_to_scan > 0)
|
|
||||||
lowmem_print(3, "lowmem_shrink %d, %x, ofree %d, ma %d\n", nr_to_scan, gfp_mask, other_free, min_adj);
|
|
||||||
+ rem = global_page_state(NR_ACTIVE) + global_page_state(NR_INACTIVE);
|
|
||||||
+ if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
|
|
||||||
+ lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
|
|
||||||
+ return rem;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
read_lock(&tasklist_lock);
|
|
||||||
for_each_process(p) {
|
|
||||||
- if(p->oomkilladj >= 0 && p->mm) {
|
|
||||||
- tasksize = get_mm_rss(p->mm);
|
|
||||||
- if(nr_to_scan > 0 && tasksize > 0 && p->oomkilladj >= min_adj) {
|
|
||||||
- if(selected == NULL ||
|
|
||||||
- p->oomkilladj > selected->oomkilladj ||
|
|
||||||
- (p->oomkilladj == selected->oomkilladj &&
|
|
||||||
- tasksize > selected_tasksize)) {
|
|
||||||
- selected = p;
|
|
||||||
- selected_tasksize = tasksize;
|
|
||||||
- lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
|
|
||||||
- p->pid, p->comm, p->oomkilladj, tasksize);
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- rem += tasksize;
|
|
||||||
+ if (p->oomkilladj < min_adj || !p->mm)
|
|
||||||
+ continue;
|
|
||||||
+ tasksize = get_mm_rss(p->mm);
|
|
||||||
+ if (tasksize <= 0)
|
|
||||||
+ continue;
|
|
||||||
+ if (selected) {
|
|
||||||
+ if (p->oomkilladj < selected->oomkilladj)
|
|
||||||
+ continue;
|
|
||||||
+ if (p->oomkilladj == selected->oomkilladj &&
|
|
||||||
+ tasksize <= selected_tasksize)
|
|
||||||
+ continue;
|
|
||||||
}
|
|
||||||
+ selected = p;
|
|
||||||
+ selected_tasksize = tasksize;
|
|
||||||
+ lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
|
|
||||||
+ p->pid, p->comm, p->oomkilladj, tasksize);
|
|
||||||
}
|
|
||||||
if(selected != NULL) {
|
|
||||||
lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
|
|
@ -1,48 +0,0 @@
|
|||||||
From f82da10dcae73652a6f0355e4398b4be1af17e6b Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Mon, 26 Jan 2009 19:22:19 -0800
|
|
||||||
Subject: [PATCH 053/134] lowmemorykiller: Don't count free space unless it meets the specified limit by itself
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
This allows processes to be killed when the kernel evict cache pages in
|
|
||||||
an attempt to get more contiguous free memory.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/staging/android/lowmemorykiller.c | 13 +++++++++----
|
|
||||||
1 files changed, 9 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/staging/android/lowmemorykiller.c
|
|
||||||
+++ b/drivers/staging/android/lowmemorykiller.c
|
|
||||||
@@ -58,20 +58,25 @@ static int lowmem_shrink(int nr_to_scan,
|
|
||||||
int min_adj = OOM_ADJUST_MAX + 1;
|
|
||||||
int selected_tasksize = 0;
|
|
||||||
int array_size = ARRAY_SIZE(lowmem_adj);
|
|
||||||
- int other_free = global_page_state(NR_FREE_PAGES) + global_page_state(NR_FILE_PAGES);
|
|
||||||
+ int other_free = global_page_state(NR_FREE_PAGES);
|
|
||||||
+ int other_file = global_page_state(NR_FILE_PAGES);
|
|
||||||
if(lowmem_adj_size < array_size)
|
|
||||||
array_size = lowmem_adj_size;
|
|
||||||
if(lowmem_minfree_size < array_size)
|
|
||||||
array_size = lowmem_minfree_size;
|
|
||||||
for(i = 0; i < array_size; i++) {
|
|
||||||
- if(other_free < lowmem_minfree[i]) {
|
|
||||||
+ if (other_free < lowmem_minfree[i] &&
|
|
||||||
+ other_file < lowmem_minfree[i]) {
|
|
||||||
min_adj = lowmem_adj[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(nr_to_scan > 0)
|
|
||||||
- lowmem_print(3, "lowmem_shrink %d, %x, ofree %d, ma %d\n", nr_to_scan, gfp_mask, other_free, min_adj);
|
|
||||||
- rem = global_page_state(NR_ACTIVE) + global_page_state(NR_INACTIVE);
|
|
||||||
+ lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", nr_to_scan, gfp_mask, other_free, other_file, min_adj);
|
|
||||||
+ rem = global_page_state(NR_ACTIVE_ANON) +
|
|
||||||
+ global_page_state(NR_ACTIVE_FILE) +
|
|
||||||
+ global_page_state(NR_INACTIVE_ANON) +
|
|
||||||
+ global_page_state(NR_INACTIVE_FILE);
|
|
||||||
if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
|
|
||||||
lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
|
|
||||||
return rem;
|
|
@ -1,415 +0,0 @@
|
|||||||
From e4c9c5d7d6d5deb124083678fe5d839d3133f60a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mike Lockwood <lockwood@android.com>
|
|
||||||
Date: Mon, 12 Jan 2009 13:25:05 -0500
|
|
||||||
Subject: [PATCH 054/134] timed_gpio: Separate timed_output class into a separate driver.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Signed-off-by: Mike Lockwood <lockwood@android.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/staging/android/Kconfig | 6 ++-
|
|
||||||
drivers/staging/android/Makefile | 1 +
|
|
||||||
drivers/staging/android/timed_gpio.c | 98 +++++++++++--------------
|
|
||||||
drivers/staging/android/timed_gpio.h | 4 +-
|
|
||||||
drivers/staging/android/timed_output.c | 121 ++++++++++++++++++++++++++++++++
|
|
||||||
drivers/staging/android/timed_output.h | 37 ++++++++++
|
|
||||||
6 files changed, 210 insertions(+), 57 deletions(-)
|
|
||||||
create mode 100644 drivers/staging/android/timed_output.c
|
|
||||||
create mode 100644 drivers/staging/android/timed_output.h
|
|
||||||
|
|
||||||
--- a/drivers/staging/android/Kconfig
|
|
||||||
+++ b/drivers/staging/android/Kconfig
|
|
||||||
@@ -73,9 +73,13 @@ config ANDROID_RAM_CONSOLE_EARLY_SIZE
|
|
||||||
default 0
|
|
||||||
depends on ANDROID_RAM_CONSOLE_EARLY_INIT
|
|
||||||
|
|
||||||
+config ANDROID_TIMED_OUTPUT
|
|
||||||
+ bool "Timed output class driver"
|
|
||||||
+ default y
|
|
||||||
+
|
|
||||||
config ANDROID_TIMED_GPIO
|
|
||||||
tristate "Android timed gpio driver"
|
|
||||||
- depends on GENERIC_GPIO
|
|
||||||
+ depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT
|
|
||||||
default n
|
|
||||||
|
|
||||||
config ANDROID_LOW_MEMORY_KILLER
|
|
||||||
--- a/drivers/staging/android/Makefile
|
|
||||||
+++ b/drivers/staging/android/Makefile
|
|
||||||
@@ -1,5 +1,6 @@
|
|
||||||
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
|
|
||||||
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
|
|
||||||
obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
|
|
||||||
+obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
|
|
||||||
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
|
|
||||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
|
||||||
--- a/drivers/staging/android/timed_gpio.c
|
|
||||||
+++ b/drivers/staging/android/timed_gpio.c
|
|
||||||
@@ -20,13 +20,12 @@
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/gpio.h>
|
|
||||||
|
|
||||||
+#include "timed_output.h"
|
|
||||||
#include "timed_gpio.h"
|
|
||||||
|
|
||||||
|
|
||||||
-static struct class *timed_gpio_class;
|
|
||||||
-
|
|
||||||
struct timed_gpio_data {
|
|
||||||
- struct device *dev;
|
|
||||||
+ struct timed_output_dev dev;
|
|
||||||
struct hrtimer timer;
|
|
||||||
spinlock_t lock;
|
|
||||||
unsigned gpio;
|
|
||||||
@@ -36,70 +35,62 @@ struct timed_gpio_data {
|
|
||||||
|
|
||||||
static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
|
|
||||||
{
|
|
||||||
- struct timed_gpio_data *gpio_data = container_of(timer, struct timed_gpio_data, timer);
|
|
||||||
+ struct timed_gpio_data *data =
|
|
||||||
+ container_of(timer, struct timed_gpio_data, timer);
|
|
||||||
|
|
||||||
- gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? 1 : 0);
|
|
||||||
+ gpio_direction_output(data->gpio, data->active_low ? 1 : 0);
|
|
||||||
return HRTIMER_NORESTART;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
||||||
+static int gpio_get_time(struct timed_output_dev *dev)
|
|
||||||
{
|
|
||||||
- struct timed_gpio_data *gpio_data = dev_get_drvdata(dev);
|
|
||||||
- int remaining;
|
|
||||||
+ struct timed_gpio_data *data =
|
|
||||||
+ container_of(dev, struct timed_gpio_data, dev);
|
|
||||||
|
|
||||||
- if (hrtimer_active(&gpio_data->timer)) {
|
|
||||||
- ktime_t r = hrtimer_get_remaining(&gpio_data->timer);
|
|
||||||
+ if (hrtimer_active(&data->timer)) {
|
|
||||||
+ ktime_t r = hrtimer_get_remaining(&data->timer);
|
|
||||||
struct timeval t = ktime_to_timeval(r);
|
|
||||||
- remaining = t.tv_sec * 1000 + t.tv_usec / 1000;
|
|
||||||
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
|
|
||||||
} else
|
|
||||||
- remaining = 0;
|
|
||||||
-
|
|
||||||
- return sprintf(buf, "%d\n", remaining);
|
|
||||||
+ return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static ssize_t gpio_enable_store(
|
|
||||||
- struct device *dev, struct device_attribute *attr,
|
|
||||||
- const char *buf, size_t size)
|
|
||||||
+static void gpio_enable(struct timed_output_dev *dev, int value)
|
|
||||||
{
|
|
||||||
- struct timed_gpio_data *gpio_data = dev_get_drvdata(dev);
|
|
||||||
- int value;
|
|
||||||
+ struct timed_gpio_data *data =
|
|
||||||
+ container_of(dev, struct timed_gpio_data, dev);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
- sscanf(buf, "%d", &value);
|
|
||||||
-
|
|
||||||
- spin_lock_irqsave(&gpio_data->lock, flags);
|
|
||||||
+ spin_lock_irqsave(&data->lock, flags);
|
|
||||||
|
|
||||||
/* cancel previous timer and set GPIO according to value */
|
|
||||||
- hrtimer_cancel(&gpio_data->timer);
|
|
||||||
- gpio_direction_output(gpio_data->gpio, gpio_data->active_low ? !value : !!value);
|
|
||||||
+ hrtimer_cancel(&data->timer);
|
|
||||||
+ gpio_direction_output(data->gpio, data->active_low ? !value : !!value);
|
|
||||||
|
|
||||||
if (value > 0) {
|
|
||||||
- if (value > gpio_data->max_timeout)
|
|
||||||
- value = gpio_data->max_timeout;
|
|
||||||
+ if (value > data->max_timeout)
|
|
||||||
+ value = data->max_timeout;
|
|
||||||
|
|
||||||
- hrtimer_start(&gpio_data->timer,
|
|
||||||
- ktime_set(value / 1000, (value % 1000) * 1000000),
|
|
||||||
- HRTIMER_MODE_REL);
|
|
||||||
+ hrtimer_start(&data->timer,
|
|
||||||
+ ktime_set(value / 1000, (value % 1000) * 1000000),
|
|
||||||
+ HRTIMER_MODE_REL);
|
|
||||||
}
|
|
||||||
|
|
||||||
- spin_unlock_irqrestore(&gpio_data->lock, flags);
|
|
||||||
-
|
|
||||||
- return size;
|
|
||||||
+ spin_unlock_irqrestore(&data->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
-static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, gpio_enable_show, gpio_enable_store);
|
|
||||||
-
|
|
||||||
static int timed_gpio_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
|
|
||||||
struct timed_gpio *cur_gpio;
|
|
||||||
struct timed_gpio_data *gpio_data, *gpio_dat;
|
|
||||||
- int i, ret = 0;
|
|
||||||
+ int i, j, ret = 0;
|
|
||||||
|
|
||||||
if (!pdata)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
- gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, GFP_KERNEL);
|
|
||||||
+ gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios,
|
|
||||||
+ GFP_KERNEL);
|
|
||||||
if (!gpio_data)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
@@ -107,23 +98,26 @@ static int timed_gpio_probe(struct platf
|
|
||||||
cur_gpio = &pdata->gpios[i];
|
|
||||||
gpio_dat = &gpio_data[i];
|
|
||||||
|
|
||||||
- hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
||||||
+ hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,
|
|
||||||
+ HRTIMER_MODE_REL);
|
|
||||||
gpio_dat->timer.function = gpio_timer_func;
|
|
||||||
spin_lock_init(&gpio_dat->lock);
|
|
||||||
|
|
||||||
+ gpio_dat->dev.name = cur_gpio->name;
|
|
||||||
+ gpio_dat->dev.get_time = gpio_get_time;
|
|
||||||
+ gpio_dat->dev.enable = gpio_enable;
|
|
||||||
+ ret = timed_output_dev_register(&gpio_dat->dev);
|
|
||||||
+ if (ret < 0) {
|
|
||||||
+ for (j = 0; j < i; j++)
|
|
||||||
+ timed_output_dev_unregister(&gpio_data[i].dev);
|
|
||||||
+ kfree(gpio_data);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
gpio_dat->gpio = cur_gpio->gpio;
|
|
||||||
gpio_dat->max_timeout = cur_gpio->max_timeout;
|
|
||||||
gpio_dat->active_low = cur_gpio->active_low;
|
|
||||||
gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low);
|
|
||||||
-
|
|
||||||
- gpio_dat->dev = device_create(timed_gpio_class, &pdev->dev, 0, "%s", cur_gpio->name);
|
|
||||||
- if (unlikely(IS_ERR(gpio_dat->dev)))
|
|
||||||
- return PTR_ERR(gpio_dat->dev);
|
|
||||||
-
|
|
||||||
- dev_set_drvdata(gpio_dat->dev, gpio_dat);
|
|
||||||
- ret = device_create_file(gpio_dat->dev, &dev_attr_enable);
|
|
||||||
- if (ret)
|
|
||||||
- return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, gpio_data);
|
|
||||||
@@ -137,10 +131,8 @@ static int timed_gpio_remove(struct plat
|
|
||||||
struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
- for (i = 0; i < pdata->num_gpios; i++) {
|
|
||||||
- device_remove_file(gpio_data[i].dev, &dev_attr_enable);
|
|
||||||
- device_unregister(gpio_data[i].dev);
|
|
||||||
- }
|
|
||||||
+ for (i = 0; i < pdata->num_gpios; i++)
|
|
||||||
+ timed_output_dev_unregister(&gpio_data[i].dev);
|
|
||||||
|
|
||||||
kfree(gpio_data);
|
|
||||||
|
|
||||||
@@ -151,22 +143,18 @@ static struct platform_driver timed_gpio
|
|
||||||
.probe = timed_gpio_probe,
|
|
||||||
.remove = timed_gpio_remove,
|
|
||||||
.driver = {
|
|
||||||
- .name = "timed-gpio",
|
|
||||||
+ .name = TIMED_GPIO_NAME,
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init timed_gpio_init(void)
|
|
||||||
{
|
|
||||||
- timed_gpio_class = class_create(THIS_MODULE, "timed_output");
|
|
||||||
- if (IS_ERR(timed_gpio_class))
|
|
||||||
- return PTR_ERR(timed_gpio_class);
|
|
||||||
return platform_driver_register(&timed_gpio_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit timed_gpio_exit(void)
|
|
||||||
{
|
|
||||||
- class_destroy(timed_gpio_class);
|
|
||||||
platform_driver_unregister(&timed_gpio_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
--- a/drivers/staging/android/timed_gpio.h
|
|
||||||
+++ b/drivers/staging/android/timed_gpio.h
|
|
||||||
@@ -16,10 +16,12 @@
|
|
||||||
#ifndef _LINUX_TIMED_GPIO_H
|
|
||||||
#define _LINUX_TIMED_GPIO_H
|
|
||||||
|
|
||||||
+#define TIMED_GPIO_NAME "timed-gpio"
|
|
||||||
+
|
|
||||||
struct timed_gpio {
|
|
||||||
const char *name;
|
|
||||||
unsigned gpio;
|
|
||||||
- int max_timeout;
|
|
||||||
+ int max_timeout;
|
|
||||||
u8 active_low;
|
|
||||||
};
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/staging/android/timed_output.c
|
|
||||||
@@ -0,0 +1,121 @@
|
|
||||||
+/* drivers/misc/timed_output.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2009 Google, Inc.
|
|
||||||
+ * Author: Mike Lockwood <lockwood@android.com>
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/types.h>
|
|
||||||
+#include <linux/device.h>
|
|
||||||
+#include <linux/fs.h>
|
|
||||||
+#include <linux/err.h>
|
|
||||||
+
|
|
||||||
+#include "timed_output.h"
|
|
||||||
+
|
|
||||||
+static struct class *timed_output_class;
|
|
||||||
+static atomic_t device_count;
|
|
||||||
+
|
|
||||||
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
|
|
||||||
+ char *buf)
|
|
||||||
+{
|
|
||||||
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
|
|
||||||
+ int remaining = tdev->get_time(tdev);
|
|
||||||
+
|
|
||||||
+ return sprintf(buf, "%d\n", remaining);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t enable_store(
|
|
||||||
+ struct device *dev, struct device_attribute *attr,
|
|
||||||
+ const char *buf, size_t size)
|
|
||||||
+{
|
|
||||||
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
|
|
||||||
+ int value;
|
|
||||||
+
|
|
||||||
+ sscanf(buf, "%d", &value);
|
|
||||||
+ tdev->enable(tdev, value);
|
|
||||||
+
|
|
||||||
+ return size;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
|
|
||||||
+
|
|
||||||
+static int create_timed_output_class(void)
|
|
||||||
+{
|
|
||||||
+ if (!timed_output_class) {
|
|
||||||
+ timed_output_class = class_create(THIS_MODULE, "timed_output");
|
|
||||||
+ if (IS_ERR(timed_output_class))
|
|
||||||
+ return PTR_ERR(timed_output_class);
|
|
||||||
+ atomic_set(&device_count, 0);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+int timed_output_dev_register(struct timed_output_dev *tdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ ret = create_timed_output_class();
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ tdev->index = atomic_inc_return(&device_count);
|
|
||||||
+ tdev->dev = device_create(timed_output_class, NULL,
|
|
||||||
+ MKDEV(0, tdev->index), NULL, tdev->name);
|
|
||||||
+ if (IS_ERR(tdev->dev))
|
|
||||||
+ return PTR_ERR(tdev->dev);
|
|
||||||
+
|
|
||||||
+ ret = device_create_file(tdev->dev, &dev_attr_enable);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ goto err_create_file;
|
|
||||||
+
|
|
||||||
+ dev_set_drvdata(tdev->dev, tdev);
|
|
||||||
+ tdev->state = 0;
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_create_file:
|
|
||||||
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
|
|
||||||
+ printk(KERN_ERR "timed_output: Failed to register driver %s\n",
|
|
||||||
+ tdev->name);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(timed_output_dev_register);
|
|
||||||
+
|
|
||||||
+void timed_output_dev_unregister(struct timed_output_dev *tdev)
|
|
||||||
+{
|
|
||||||
+ device_remove_file(tdev->dev, &dev_attr_enable);
|
|
||||||
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
|
|
||||||
+ dev_set_drvdata(tdev->dev, NULL);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
|
|
||||||
+
|
|
||||||
+static int __init timed_output_init(void)
|
|
||||||
+{
|
|
||||||
+ return create_timed_output_class();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit timed_output_exit(void)
|
|
||||||
+{
|
|
||||||
+ class_destroy(timed_output_class);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(timed_output_init);
|
|
||||||
+module_exit(timed_output_exit);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
|
||||||
+MODULE_DESCRIPTION("timed output class driver");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/staging/android/timed_output.h
|
|
||||||
@@ -0,0 +1,37 @@
|
|
||||||
+/* include/linux/timed_output.h
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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.
|
|
||||||
+ *
|
|
||||||
+*/
|
|
||||||
+
|
|
||||||
+#ifndef _LINUX_TIMED_OUTPUT_H
|
|
||||||
+#define _LINUX_TIMED_OUTPUT_H
|
|
||||||
+
|
|
||||||
+struct timed_output_dev {
|
|
||||||
+ const char *name;
|
|
||||||
+
|
|
||||||
+ /* enable the output and set the timer */
|
|
||||||
+ void (*enable)(struct timed_output_dev *sdev, int timeout);
|
|
||||||
+
|
|
||||||
+ /* returns the current number of milliseconds remaining on the timer */
|
|
||||||
+ int (*get_time)(struct timed_output_dev *sdev);
|
|
||||||
+
|
|
||||||
+ /* private data */
|
|
||||||
+ struct device *dev;
|
|
||||||
+ int index;
|
|
||||||
+ int state;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+extern int timed_output_dev_register(struct timed_output_dev *dev);
|
|
||||||
+extern void timed_output_dev_unregister(struct timed_output_dev *dev);
|
|
||||||
+
|
|
||||||
+#endif
|
|
@ -1,62 +0,0 @@
|
|||||||
From d620f695290e4ffb1586420ba1dbbb5b2c8c075d Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Tue, 17 Feb 2009 14:51:02 -0800
|
|
||||||
Subject: [PATCH 055/134] mm: Add min_free_order_shift tunable.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
By default the kernel tries to keep half as much memory free at each
|
|
||||||
order as it does for one order below. This can be too agressive when
|
|
||||||
running without swap.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
kernel/sysctl.c | 9 +++++++++
|
|
||||||
mm/page_alloc.c | 3 ++-
|
|
||||||
2 files changed, 11 insertions(+), 1 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/sysctl.c
|
|
||||||
+++ b/kernel/sysctl.c
|
|
||||||
@@ -77,6 +77,7 @@ extern int suid_dumpable;
|
|
||||||
extern char core_pattern[];
|
|
||||||
extern int pid_max;
|
|
||||||
extern int min_free_kbytes;
|
|
||||||
+extern int min_free_order_shift;
|
|
||||||
extern int pid_max_min, pid_max_max;
|
|
||||||
extern int sysctl_drop_caches;
|
|
||||||
extern int percpu_pagelist_fraction;
|
|
||||||
@@ -1138,6 +1139,14 @@ static struct ctl_table vm_table[] = {
|
|
||||||
.extra1 = &zero,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
+ .ctl_name = CTL_UNNUMBERED,
|
|
||||||
+ .procname = "min_free_order_shift",
|
|
||||||
+ .data = &min_free_order_shift,
|
|
||||||
+ .maxlen = sizeof(min_free_order_shift),
|
|
||||||
+ .mode = 0644,
|
|
||||||
+ .proc_handler = &proc_dointvec
|
|
||||||
+ },
|
|
||||||
+ {
|
|
||||||
.ctl_name = VM_PERCPU_PAGELIST_FRACTION,
|
|
||||||
.procname = "percpu_pagelist_fraction",
|
|
||||||
.data = &percpu_pagelist_fraction,
|
|
||||||
--- a/mm/page_alloc.c
|
|
||||||
+++ b/mm/page_alloc.c
|
|
||||||
@@ -119,6 +119,7 @@ static char * const zone_names[MAX_NR_ZO
|
|
||||||
};
|
|
||||||
|
|
||||||
int min_free_kbytes = 1024;
|
|
||||||
+int min_free_order_shift = 1;
|
|
||||||
|
|
||||||
unsigned long __meminitdata nr_kernel_pages;
|
|
||||||
unsigned long __meminitdata nr_all_pages;
|
|
||||||
@@ -1258,7 +1259,7 @@ int zone_watermark_ok(struct zone *z, in
|
|
||||||
free_pages -= z->free_area[o].nr_free << o;
|
|
||||||
|
|
||||||
/* Require fewer higher order pages to be free */
|
|
||||||
- min >>= 1;
|
|
||||||
+ min >>= min_free_order_shift;
|
|
||||||
|
|
||||||
if (free_pages <= min)
|
|
||||||
return 0;
|
|
@ -1,45 +0,0 @@
|
|||||||
From a4eb204a8029320c2dd748daf4f51fd48d337c3d Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Wed, 18 Mar 2009 17:27:31 -0700
|
|
||||||
Subject: [PATCH 056/134] mm: Check if any page in a pageblock is reserved before marking it MIGRATE_RESERVE
|
|
||||||
|
|
||||||
This fixes a problem where the first pageblock got marked MIGRATE_RESERVE even
|
|
||||||
though it only had a few free pages. This in turn caused no contiguous memory
|
|
||||||
to be reserved and frequent kswapd wakeups that emptied the caches to get more
|
|
||||||
contiguous memory.
|
|
||||||
---
|
|
||||||
mm/page_alloc.c | 16 +++++++++++++++-
|
|
||||||
1 files changed, 15 insertions(+), 1 deletions(-)
|
|
||||||
|
|
||||||
--- a/mm/page_alloc.c
|
|
||||||
+++ b/mm/page_alloc.c
|
|
||||||
@@ -2570,6 +2570,20 @@ static inline unsigned long wait_table_b
|
|
||||||
#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
|
|
||||||
|
|
||||||
/*
|
|
||||||
+ * Check if a pageblock contains reserved pages
|
|
||||||
+ */
|
|
||||||
+static int pageblock_is_reserved(unsigned long start_pfn)
|
|
||||||
+{
|
|
||||||
+ unsigned long end_pfn = start_pfn + pageblock_nr_pages;
|
|
||||||
+ unsigned long pfn;
|
|
||||||
+
|
|
||||||
+ for (pfn = start_pfn; pfn < end_pfn; pfn++)
|
|
||||||
+ if (PageReserved(pfn_to_page(pfn)))
|
|
||||||
+ return 1;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
* Mark a number of pageblocks as MIGRATE_RESERVE. The number
|
|
||||||
* of blocks reserved is based on zone->pages_min. The memory within the
|
|
||||||
* reserve will tend to store contiguous free pages. Setting min_free_kbytes
|
|
||||||
@@ -2598,7 +2612,7 @@ static void setup_zone_migrate_reserve(s
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Blocks with reserved pages will never free, skip them. */
|
|
||||||
- if (PageReserved(page))
|
|
||||||
+ if (pageblock_is_reserved(pfn))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
block_migratetype = get_pageblock_migratetype(page);
|
|
@ -1,43 +0,0 @@
|
|||||||
From 10276fd993c6e4c92d8086a6ccd0c9e0ff53b053 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Wed, 10 Dec 2008 20:06:28 -0800
|
|
||||||
Subject: [PATCH 058/134] sched: Enable might_sleep before initializing drivers.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
This allows detection of init bugs in built-in drivers.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
kernel/sched.c | 14 ++++++++++++--
|
|
||||||
1 files changed, 12 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/sched.c
|
|
||||||
+++ b/kernel/sched.c
|
|
||||||
@@ -9065,13 +9065,23 @@ void __init sched_init(void)
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_SPINLOCK_SLEEP
|
|
||||||
+static int __might_sleep_init_called;
|
|
||||||
+int __init __might_sleep_init(void)
|
|
||||||
+{
|
|
||||||
+ __might_sleep_init_called = 1;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+early_initcall(__might_sleep_init);
|
|
||||||
+
|
|
||||||
void __might_sleep(char *file, int line)
|
|
||||||
{
|
|
||||||
#ifdef in_atomic
|
|
||||||
static unsigned long prev_jiffy; /* ratelimiting */
|
|
||||||
|
|
||||||
- if ((!in_atomic() && !irqs_disabled()) ||
|
|
||||||
- system_state != SYSTEM_RUNNING || oops_in_progress)
|
|
||||||
+ if ((!in_atomic() && !irqs_disabled()) || oops_in_progress)
|
|
||||||
+ return;
|
|
||||||
+ if (system_state != SYSTEM_RUNNING &&
|
|
||||||
+ (!__might_sleep_init_called || system_state != SYSTEM_BOOTING))
|
|
||||||
return;
|
|
||||||
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
|
|
||||||
return;
|
|
@ -1,35 +0,0 @@
|
|||||||
From d52bcb0a807dde87057f330dbe95aa6d221a4b85 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Mon, 9 Apr 2007 17:13:02 +0700
|
|
||||||
Subject: [PATCH 060/134] [ARM] Add code to prevent system calls from being restarted muliple times before returning from the kernel.
|
|
||||||
|
|
||||||
Fixes crashes with thumb syscalls.
|
|
||||||
---
|
|
||||||
arch/arm/kernel/signal.c | 9 +++++++++
|
|
||||||
1 files changed, 9 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/arch/arm/kernel/signal.c
|
|
||||||
+++ b/arch/arm/kernel/signal.c
|
|
||||||
@@ -499,6 +499,14 @@ setup_rt_frame(int usig, struct k_sigact
|
|
||||||
|
|
||||||
static inline void restart_syscall(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
+ if (regs->ARM_ORIG_r0 == -ERESTARTNOHAND ||
|
|
||||||
+ regs->ARM_ORIG_r0 == -ERESTARTSYS ||
|
|
||||||
+ regs->ARM_ORIG_r0 == -ERESTARTNOINTR ||
|
|
||||||
+ regs->ARM_ORIG_r0 == -ERESTART_RESTARTBLOCK) {
|
|
||||||
+ /* the syscall cannot be safely restarted, return -EINTR instead */
|
|
||||||
+ regs->ARM_r0 = -EINTR;
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
regs->ARM_r0 = regs->ARM_ORIG_r0;
|
|
||||||
regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
|
|
||||||
}
|
|
||||||
@@ -631,6 +639,7 @@ static void do_signal(struct pt_regs *re
|
|
||||||
*/
|
|
||||||
if (syscall) {
|
|
||||||
if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
|
|
||||||
+ regs->ARM_r0 = -EAGAIN; /* prevent multiple restarts */
|
|
||||||
if (thumb_mode(regs)) {
|
|
||||||
regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
|
|
||||||
regs->ARM_pc -= 2;
|
|
@ -1,50 +0,0 @@
|
|||||||
From 784b170a69906c48a688a9ffa7512fc858f8836c Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Sun, 30 Mar 2008 21:36:29 -0700
|
|
||||||
Subject: [PATCH 061/134] [ARM] Save thread registers in coredumps
|
|
||||||
|
|
||||||
Signed-off-by: Brian Swetland <swetland@google.com>
|
|
||||||
---
|
|
||||||
arch/arm/include/asm/elf.h | 6 ++++++
|
|
||||||
arch/arm/kernel/process.c | 10 ++++++++++
|
|
||||||
2 files changed, 16 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/arch/arm/include/asm/elf.h
|
|
||||||
+++ b/arch/arm/include/asm/elf.h
|
|
||||||
@@ -86,6 +86,10 @@ extern char elf_platform[];
|
|
||||||
|
|
||||||
struct elf32_hdr;
|
|
||||||
|
|
||||||
+struct task_struct;
|
|
||||||
+
|
|
||||||
+extern int dump_task_regs (struct task_struct *, elf_gregset_t *);
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* This is used to ensure we don't load something for the wrong architecture.
|
|
||||||
*/
|
|
||||||
@@ -113,4 +117,6 @@ extern int arm_elf_read_implies_exec(con
|
|
||||||
extern void elf_set_personality(const struct elf32_hdr *);
|
|
||||||
#define SET_PERSONALITY(ex) elf_set_personality(&(ex))
|
|
||||||
|
|
||||||
+#define ELF_CORE_COPY_TASK_REGS(tsk, elf_regs) dump_task_regs(tsk, elf_regs)
|
|
||||||
+
|
|
||||||
#endif
|
|
||||||
--- a/arch/arm/kernel/process.c
|
|
||||||
+++ b/arch/arm/kernel/process.c
|
|
||||||
@@ -337,6 +337,16 @@ int dump_fpu (struct pt_regs *regs, stru
|
|
||||||
EXPORT_SYMBOL(dump_fpu);
|
|
||||||
|
|
||||||
/*
|
|
||||||
+ * Capture the user space registers if the task is not running (in user space)
|
|
||||||
+ */
|
|
||||||
+int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
|
|
||||||
+{
|
|
||||||
+ struct pt_regs ptregs = *task_pt_regs(tsk);
|
|
||||||
+ elf_core_copy_regs(regs, &ptregs);
|
|
||||||
+ return 1;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
* Shuffle the argument into the correct register before calling the
|
|
||||||
* thread function. r1 is the thread argument, r2 is the pointer to
|
|
||||||
* the thread function, and r3 points to the exit function.
|
|
@ -1,104 +0,0 @@
|
|||||||
From e93bd714c424d9fe907cb1d8ec5ce784767a3b16 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Fri, 26 Sep 2008 22:10:56 -0700
|
|
||||||
Subject: [PATCH 064/134] PM: Add wake lock api.
|
|
||||||
|
|
||||||
---
|
|
||||||
include/linux/wakelock.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 files changed, 91 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100755 include/linux/wakelock.h
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/linux/wakelock.h
|
|
||||||
@@ -0,0 +1,91 @@
|
|
||||||
+/* include/linux/wakelock.h
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2007-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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.
|
|
||||||
+ *
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef _LINUX_WAKELOCK_H
|
|
||||||
+#define _LINUX_WAKELOCK_H
|
|
||||||
+
|
|
||||||
+#include <linux/list.h>
|
|
||||||
+#include <linux/ktime.h>
|
|
||||||
+
|
|
||||||
+/* A wake_lock prevents the system from entering suspend or other low power
|
|
||||||
+ * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock
|
|
||||||
+ * prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low power
|
|
||||||
+ * states that cause large interrupt latencies or that disable a set of
|
|
||||||
+ * interrupts will not entered from idle until the wake_locks are released.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ WAKE_LOCK_SUSPEND, /* Prevent suspend */
|
|
||||||
+ WAKE_LOCK_IDLE, /* Prevent low power idle */
|
|
||||||
+ WAKE_LOCK_TYPE_COUNT
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct wake_lock {
|
|
||||||
+#ifdef CONFIG_HAS_WAKELOCK
|
|
||||||
+ struct list_head link;
|
|
||||||
+ int flags;
|
|
||||||
+ const char *name;
|
|
||||||
+ unsigned long expires;
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ struct {
|
|
||||||
+ int count;
|
|
||||||
+ int expire_count;
|
|
||||||
+ int wakeup_count;
|
|
||||||
+ ktime_t total_time;
|
|
||||||
+ ktime_t prevent_suspend_time;
|
|
||||||
+ ktime_t max_time;
|
|
||||||
+ ktime_t last_time;
|
|
||||||
+ } stat;
|
|
||||||
+#endif
|
|
||||||
+#endif
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_HAS_WAKELOCK
|
|
||||||
+
|
|
||||||
+void wake_lock_init(struct wake_lock *lock, int type, const char *name);
|
|
||||||
+void wake_lock_destroy(struct wake_lock *lock);
|
|
||||||
+void wake_lock(struct wake_lock *lock);
|
|
||||||
+void wake_lock_timeout(struct wake_lock *lock, long timeout);
|
|
||||||
+void wake_unlock(struct wake_lock *lock);
|
|
||||||
+
|
|
||||||
+/* wake_lock_active returns a non-zero value if the wake_lock is currently
|
|
||||||
+ * locked. If the wake_lock has a timeout, it does not check the timeout
|
|
||||||
+ * but if the timeout had aready been checked it will return 0.
|
|
||||||
+ */
|
|
||||||
+int wake_lock_active(struct wake_lock *lock);
|
|
||||||
+
|
|
||||||
+/* has_wake_lock returns 0 if no wake locks of the specified type are active,
|
|
||||||
+ * and non-zero if one or more wake locks are held. Specifically it returns
|
|
||||||
+ * -1 if one or more wake locks with no timeout are active or the
|
|
||||||
+ * number of jiffies until all active wake locks time out.
|
|
||||||
+ */
|
|
||||||
+long has_wake_lock(int type);
|
|
||||||
+
|
|
||||||
+#else
|
|
||||||
+
|
|
||||||
+static inline void wake_lock_init(struct wake_lock *lock, int type,
|
|
||||||
+ const char *name) {}
|
|
||||||
+static inline void wake_lock_destroy(struct wake_lock *lock) {}
|
|
||||||
+static inline void wake_lock(struct wake_lock *lock) {}
|
|
||||||
+static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) {}
|
|
||||||
+static inline void wake_unlock(struct wake_lock *lock) {}
|
|
||||||
+
|
|
||||||
+static inline int wake_lock_active(struct wake_lock *lock) { return 0; }
|
|
||||||
+static inline long has_wake_lock(int type) { return 0; }
|
|
||||||
+
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+#endif
|
|
||||||
+
|
|
@ -1,69 +0,0 @@
|
|||||||
From 4f76252afe98fd017894e61c296bc61836e67a4a Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Fri, 26 Sep 2008 22:10:56 -0700
|
|
||||||
Subject: [PATCH 065/134] PM: Add early suspend api.
|
|
||||||
|
|
||||||
---
|
|
||||||
include/linux/earlysuspend.h | 56 ++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 files changed, 56 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100755 include/linux/earlysuspend.h
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/linux/earlysuspend.h
|
|
||||||
@@ -0,0 +1,56 @@
|
|
||||||
+/* include/linux/earlysuspend.h
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2007-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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.
|
|
||||||
+ *
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef _LINUX_EARLYSUSPEND_H
|
|
||||||
+#define _LINUX_EARLYSUSPEND_H
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
||||||
+#include <linux/list.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+/* The early_suspend structure defines suspend and resume hooks to be called
|
|
||||||
+ * when the user visible sleep state of the system changes, and a level to
|
|
||||||
+ * control the order. They can be used to turn off the screen and input
|
|
||||||
+ * devices that are not used for wakeup.
|
|
||||||
+ * Suspend handlers are called in low to high level order, resume handlers are
|
|
||||||
+ * called in the opposite order. If, when calling register_early_suspend,
|
|
||||||
+ * the suspend handlers have already been called without a matching call to the
|
|
||||||
+ * resume handlers, the suspend handler will be called directly from
|
|
||||||
+ * register_early_suspend. This direct call can violate the normal level order.
|
|
||||||
+ */
|
|
||||||
+enum {
|
|
||||||
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
|
|
||||||
+ EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
|
|
||||||
+ EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
|
|
||||||
+};
|
|
||||||
+struct early_suspend {
|
|
||||||
+#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
||||||
+ struct list_head link;
|
|
||||||
+ int level;
|
|
||||||
+ void (*suspend)(struct early_suspend *h);
|
|
||||||
+ void (*resume)(struct early_suspend *h);
|
|
||||||
+#endif
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
||||||
+void register_early_suspend(struct early_suspend *handler);
|
|
||||||
+void unregister_early_suspend(struct early_suspend *handler);
|
|
||||||
+#else
|
|
||||||
+#define register_early_suspend(handler) do { } while (0)
|
|
||||||
+#define unregister_early_suspend(handler) do { } while (0)
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+#endif
|
|
||||||
+
|
|
@ -1,673 +0,0 @@
|
|||||||
From eef7f012d480142a3eccff91e0ae73ecb13220b3 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Tue, 9 Sep 2008 22:14:34 -0700
|
|
||||||
Subject: [PATCH 066/134] PM: Implement wakelock api.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
PM: wakelock: Replace expire work with a timer
|
|
||||||
|
|
||||||
The expire work function did not work in the normal case.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
kernel/power/Kconfig | 19 ++
|
|
||||||
kernel/power/Makefile | 1 +
|
|
||||||
kernel/power/power.h | 7 +
|
|
||||||
kernel/power/wakelock.c | 598 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
4 files changed, 625 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 kernel/power/wakelock.c
|
|
||||||
|
|
||||||
--- a/kernel/power/Kconfig
|
|
||||||
+++ b/kernel/power/Kconfig
|
|
||||||
@@ -116,6 +116,25 @@ config SUSPEND_FREEZER
|
|
||||||
|
|
||||||
Turning OFF this setting is NOT recommended! If in doubt, say Y.
|
|
||||||
|
|
||||||
+config HAS_WAKELOCK
|
|
||||||
+ bool
|
|
||||||
+
|
|
||||||
+config WAKELOCK
|
|
||||||
+ bool "Wake lock"
|
|
||||||
+ depends on PM && RTC_CLASS
|
|
||||||
+ default n
|
|
||||||
+ select HAS_WAKELOCK
|
|
||||||
+ ---help---
|
|
||||||
+ Enable wakelocks. When user space request a sleep state the
|
|
||||||
+ sleep request will be delayed until no wake locks are held.
|
|
||||||
+
|
|
||||||
+config WAKELOCK_STAT
|
|
||||||
+ bool "Wake lock stats"
|
|
||||||
+ depends on WAKELOCK
|
|
||||||
+ default y
|
|
||||||
+ ---help---
|
|
||||||
+ Report wake lock stats in /proc/wakelocks
|
|
||||||
+
|
|
||||||
config HIBERNATION
|
|
||||||
bool "Hibernation (aka 'suspend to disk')"
|
|
||||||
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
|
|
||||||
--- a/kernel/power/Makefile
|
|
||||||
+++ b/kernel/power/Makefile
|
|
||||||
@@ -6,6 +6,7 @@ endif
|
|
||||||
obj-$(CONFIG_PM) += main.o
|
|
||||||
obj-$(CONFIG_PM_SLEEP) += console.o
|
|
||||||
obj-$(CONFIG_FREEZER) += process.o
|
|
||||||
+obj-$(CONFIG_WAKELOCK) += wakelock.o
|
|
||||||
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
|
|
||||||
--- a/kernel/power/power.h
|
|
||||||
+++ b/kernel/power/power.h
|
|
||||||
@@ -223,3 +223,10 @@ static inline void suspend_thaw_processe
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_WAKELOCK
|
|
||||||
+/* kernel/power/wakelock.c */
|
|
||||||
+extern struct workqueue_struct *suspend_work_queue;
|
|
||||||
+extern struct wake_lock main_wake_lock;
|
|
||||||
+extern suspend_state_t requested_suspend_state;
|
|
||||||
+#endif
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/kernel/power/wakelock.c
|
|
||||||
@@ -0,0 +1,598 @@
|
|
||||||
+/* kernel/power/wakelock.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2005-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/platform_device.h>
|
|
||||||
+#include <linux/rtc.h>
|
|
||||||
+#include <linux/suspend.h>
|
|
||||||
+#include <linux/syscalls.h> /* sys_sync */
|
|
||||||
+#include <linux/wakelock.h>
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+#include <linux/proc_fs.h>
|
|
||||||
+#endif
|
|
||||||
+#include "power.h"
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ DEBUG_EXIT_SUSPEND = 1U << 0,
|
|
||||||
+ DEBUG_WAKEUP = 1U << 1,
|
|
||||||
+ DEBUG_SUSPEND = 1U << 2,
|
|
||||||
+ DEBUG_EXPIRE = 1U << 3,
|
|
||||||
+ DEBUG_WAKE_LOCK = 1U << 4,
|
|
||||||
+};
|
|
||||||
+static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP;
|
|
||||||
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
|
||||||
+
|
|
||||||
+#define WAKE_LOCK_TYPE_MASK (0x0f)
|
|
||||||
+#define WAKE_LOCK_INITIALIZED (1U << 8)
|
|
||||||
+#define WAKE_LOCK_ACTIVE (1U << 9)
|
|
||||||
+#define WAKE_LOCK_AUTO_EXPIRE (1U << 10)
|
|
||||||
+#define WAKE_LOCK_PREVENTING_SUSPEND (1U << 11)
|
|
||||||
+
|
|
||||||
+static DEFINE_SPINLOCK(list_lock);
|
|
||||||
+static LIST_HEAD(inactive_locks);
|
|
||||||
+static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
|
|
||||||
+static int current_event_num;
|
|
||||||
+struct workqueue_struct *suspend_work_queue;
|
|
||||||
+struct wake_lock main_wake_lock;
|
|
||||||
+suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
|
|
||||||
+static struct wake_lock unknown_wakeup;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+static struct wake_lock deleted_wake_locks;
|
|
||||||
+static ktime_t last_sleep_time_update;
|
|
||||||
+static int wait_for_wakeup;
|
|
||||||
+
|
|
||||||
+int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)
|
|
||||||
+{
|
|
||||||
+ struct timespec ts;
|
|
||||||
+ struct timespec kt;
|
|
||||||
+ struct timespec tomono;
|
|
||||||
+ struct timespec delta;
|
|
||||||
+ unsigned long seq;
|
|
||||||
+ long timeout;
|
|
||||||
+
|
|
||||||
+ if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
|
|
||||||
+ return 0;
|
|
||||||
+ do {
|
|
||||||
+ seq = read_seqbegin(&xtime_lock);
|
|
||||||
+ timeout = lock->expires - jiffies;
|
|
||||||
+ if (timeout > 0)
|
|
||||||
+ return 0;
|
|
||||||
+ kt = current_kernel_time();
|
|
||||||
+ tomono = wall_to_monotonic;
|
|
||||||
+ } while (read_seqretry(&xtime_lock, seq));
|
|
||||||
+ jiffies_to_timespec(-timeout, &delta);
|
|
||||||
+ set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,
|
|
||||||
+ kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);
|
|
||||||
+ *expire_time = timespec_to_ktime(ts);
|
|
||||||
+ return 1;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int print_lock_stat(char *buf, struct wake_lock *lock)
|
|
||||||
+{
|
|
||||||
+ int lock_count = lock->stat.count;
|
|
||||||
+ int expire_count = lock->stat.expire_count;
|
|
||||||
+ ktime_t active_time = ktime_set(0, 0);
|
|
||||||
+ ktime_t total_time = lock->stat.total_time;
|
|
||||||
+ ktime_t max_time = lock->stat.max_time;
|
|
||||||
+ ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
|
|
||||||
+ if (lock->flags & WAKE_LOCK_ACTIVE) {
|
|
||||||
+ ktime_t now, add_time;
|
|
||||||
+ int expired = get_expired_time(lock, &now);
|
|
||||||
+ if (!expired)
|
|
||||||
+ now = ktime_get();
|
|
||||||
+ add_time = ktime_sub(now, lock->stat.last_time);
|
|
||||||
+ lock_count++;
|
|
||||||
+ if (!expired)
|
|
||||||
+ active_time = add_time;
|
|
||||||
+ else
|
|
||||||
+ expire_count++;
|
|
||||||
+ total_time = ktime_add(total_time, add_time);
|
|
||||||
+ if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND)
|
|
||||||
+ prevent_suspend_time = ktime_add(prevent_suspend_time,
|
|
||||||
+ ktime_sub(now, last_sleep_time_update));
|
|
||||||
+ if (add_time.tv64 > max_time.tv64)
|
|
||||||
+ max_time = add_time;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return sprintf(buf, "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t"
|
|
||||||
+ "%lld\n", lock->name, lock_count, expire_count,
|
|
||||||
+ lock->stat.wakeup_count, ktime_to_ns(active_time),
|
|
||||||
+ ktime_to_ns(total_time),
|
|
||||||
+ ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),
|
|
||||||
+ ktime_to_ns(lock->stat.last_time));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int wakelocks_read_proc(char *page, char **start, off_t off,
|
|
||||||
+ int count, int *eof, void *data)
|
|
||||||
+{
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ struct wake_lock *lock;
|
|
||||||
+ int len = 0;
|
|
||||||
+ char *p = page;
|
|
||||||
+ int type;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+
|
|
||||||
+ p += sprintf(p, "name\tcount\texpire_count\twake_count\tactive_since"
|
|
||||||
+ "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
|
|
||||||
+ list_for_each_entry(lock, &inactive_locks, link) {
|
|
||||||
+ p += print_lock_stat(p, lock);
|
|
||||||
+ }
|
|
||||||
+ for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
|
|
||||||
+ list_for_each_entry(lock, &active_wake_locks[type], link)
|
|
||||||
+ p += print_lock_stat(p, lock);
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+
|
|
||||||
+ *start = page + off;
|
|
||||||
+
|
|
||||||
+ len = p - page;
|
|
||||||
+ if (len > off)
|
|
||||||
+ len -= off;
|
|
||||||
+ else
|
|
||||||
+ len = 0;
|
|
||||||
+
|
|
||||||
+ return len < count ? len : count;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void wake_unlock_stat_locked(struct wake_lock *lock, int expired)
|
|
||||||
+{
|
|
||||||
+ ktime_t duration;
|
|
||||||
+ ktime_t now;
|
|
||||||
+ if (!(lock->flags & WAKE_LOCK_ACTIVE))
|
|
||||||
+ return;
|
|
||||||
+ if (get_expired_time(lock, &now))
|
|
||||||
+ expired = 1;
|
|
||||||
+ else
|
|
||||||
+ now = ktime_get();
|
|
||||||
+ lock->stat.count++;
|
|
||||||
+ if (expired)
|
|
||||||
+ lock->stat.expire_count++;
|
|
||||||
+ duration = ktime_sub(now, lock->stat.last_time);
|
|
||||||
+ lock->stat.total_time = ktime_add(lock->stat.total_time, duration);
|
|
||||||
+ if (ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time))
|
|
||||||
+ lock->stat.max_time = duration;
|
|
||||||
+ lock->stat.last_time = ktime_get();
|
|
||||||
+ if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
|
|
||||||
+ duration = ktime_sub(now, last_sleep_time_update);
|
|
||||||
+ lock->stat.prevent_suspend_time = ktime_add(
|
|
||||||
+ lock->stat.prevent_suspend_time, duration);
|
|
||||||
+ lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void update_sleep_wait_stats_locked(int done)
|
|
||||||
+{
|
|
||||||
+ struct wake_lock *lock;
|
|
||||||
+ ktime_t now, etime, elapsed, add;
|
|
||||||
+ int expired;
|
|
||||||
+
|
|
||||||
+ now = ktime_get();
|
|
||||||
+ elapsed = ktime_sub(now, last_sleep_time_update);
|
|
||||||
+ list_for_each_entry(lock, &active_wake_locks[WAKE_LOCK_SUSPEND], link) {
|
|
||||||
+ expired = get_expired_time(lock, &etime);
|
|
||||||
+ if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
|
|
||||||
+ if (expired)
|
|
||||||
+ add = ktime_sub(etime, last_sleep_time_update);
|
|
||||||
+ else
|
|
||||||
+ add = elapsed;
|
|
||||||
+ lock->stat.prevent_suspend_time = ktime_add(
|
|
||||||
+ lock->stat.prevent_suspend_time, add);
|
|
||||||
+ }
|
|
||||||
+ if (done || expired)
|
|
||||||
+ lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
|
|
||||||
+ else
|
|
||||||
+ lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
|
|
||||||
+ }
|
|
||||||
+ last_sleep_time_update = now;
|
|
||||||
+}
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static void expire_wake_lock(struct wake_lock *lock)
|
|
||||||
+{
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ wake_unlock_stat_locked(lock, 1);
|
|
||||||
+#endif
|
|
||||||
+ lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
|
|
||||||
+ list_del(&lock->link);
|
|
||||||
+ list_add(&lock->link, &inactive_locks);
|
|
||||||
+ if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
|
|
||||||
+ pr_info("expired wake lock %s\n", lock->name);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void print_active_locks(int type)
|
|
||||||
+{
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ struct wake_lock *lock;
|
|
||||||
+
|
|
||||||
+ BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ list_for_each_entry(lock, &active_wake_locks[type], link) {
|
|
||||||
+ if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
|
|
||||||
+ long timeout = lock->expires - jiffies;
|
|
||||||
+ if (timeout <= 0)
|
|
||||||
+ pr_info("wake lock %s, expired\n", lock->name);
|
|
||||||
+ else
|
|
||||||
+ pr_info("active wake lock %s, time left %ld\n",
|
|
||||||
+ lock->name, timeout);
|
|
||||||
+ } else
|
|
||||||
+ pr_info("active wake lock %s\n", lock->name);
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static long has_wake_lock_locked(int type)
|
|
||||||
+{
|
|
||||||
+ struct wake_lock *lock, *n;
|
|
||||||
+ long max_timeout = 0;
|
|
||||||
+
|
|
||||||
+ BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
|
|
||||||
+ list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
|
|
||||||
+ if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
|
|
||||||
+ long timeout = lock->expires - jiffies;
|
|
||||||
+ if (timeout <= 0)
|
|
||||||
+ expire_wake_lock(lock);
|
|
||||||
+ else if (timeout > max_timeout)
|
|
||||||
+ max_timeout = timeout;
|
|
||||||
+ } else
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+ return max_timeout;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+long has_wake_lock(int type)
|
|
||||||
+{
|
|
||||||
+ long ret;
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ ret = has_wake_lock_locked(type);
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void suspend(struct work_struct *work)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ int entry_event_num;
|
|
||||||
+
|
|
||||||
+ if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("suspend: abort suspend\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ entry_event_num = current_event_num;
|
|
||||||
+ sys_sync();
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("suspend: enter suspend\n");
|
|
||||||
+ ret = pm_suspend(requested_suspend_state);
|
|
||||||
+ if (debug_mask & DEBUG_EXIT_SUSPEND) {
|
|
||||||
+ struct timespec ts;
|
|
||||||
+ struct rtc_time tm;
|
|
||||||
+ getnstimeofday(&ts);
|
|
||||||
+ rtc_time_to_tm(ts.tv_sec, &tm);
|
|
||||||
+ pr_info("suspend: exit suspend, ret = %d "
|
|
||||||
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
|
|
||||||
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
||||||
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
|
|
||||||
+ }
|
|
||||||
+ if (current_event_num == entry_event_num) {
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("suspend: pm_suspend returned with no event\n");
|
|
||||||
+ wake_lock_timeout(&unknown_wakeup, HZ / 2);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+static DECLARE_WORK(suspend_work, suspend);
|
|
||||||
+
|
|
||||||
+static void expire_wake_locks(unsigned long data)
|
|
||||||
+{
|
|
||||||
+ long has_lock;
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ if (debug_mask & DEBUG_EXPIRE)
|
|
||||||
+ pr_info("expire_wake_locks: start\n");
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ print_active_locks(WAKE_LOCK_SUSPEND);
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
|
|
||||||
+ if (debug_mask & DEBUG_EXPIRE)
|
|
||||||
+ pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
|
|
||||||
+ if (has_lock == 0)
|
|
||||||
+ queue_work(suspend_work_queue, &suspend_work);
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
|
|
||||||
+
|
|
||||||
+static int power_suspend_late(struct platform_device *pdev, pm_message_t state)
|
|
||||||
+{
|
|
||||||
+ int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ wait_for_wakeup = 1;
|
|
||||||
+#endif
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("power_suspend_late return %d\n", ret);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver power_driver = {
|
|
||||||
+ .driver.name = "power",
|
|
||||||
+ .suspend_late = power_suspend_late,
|
|
||||||
+};
|
|
||||||
+static struct platform_device power_device = {
|
|
||||||
+ .name = "power",
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+void wake_lock_init(struct wake_lock *lock, int type, const char *name)
|
|
||||||
+{
|
|
||||||
+ unsigned long irqflags = 0;
|
|
||||||
+
|
|
||||||
+ if (name)
|
|
||||||
+ lock->name = name;
|
|
||||||
+ BUG_ON(!lock->name);
|
|
||||||
+
|
|
||||||
+ if (debug_mask & DEBUG_WAKE_LOCK)
|
|
||||||
+ pr_info("wake_lock_init name=%s\n", lock->name);
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ lock->stat.count = 0;
|
|
||||||
+ lock->stat.expire_count = 0;
|
|
||||||
+ lock->stat.wakeup_count = 0;
|
|
||||||
+ lock->stat.total_time = ktime_set(0, 0);
|
|
||||||
+ lock->stat.prevent_suspend_time = ktime_set(0, 0);
|
|
||||||
+ lock->stat.max_time = ktime_set(0, 0);
|
|
||||||
+ lock->stat.last_time = ktime_set(0, 0);
|
|
||||||
+#endif
|
|
||||||
+ lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
|
|
||||||
+
|
|
||||||
+ INIT_LIST_HEAD(&lock->link);
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ list_add(&lock->link, &inactive_locks);
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(wake_lock_init);
|
|
||||||
+
|
|
||||||
+void wake_lock_destroy(struct wake_lock *lock)
|
|
||||||
+{
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ if (debug_mask & DEBUG_WAKE_LOCK)
|
|
||||||
+ pr_info("wake_lock_destroy name=%s\n", lock->name);
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ lock->flags &= ~WAKE_LOCK_INITIALIZED;
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ if (lock->stat.count) {
|
|
||||||
+ deleted_wake_locks.stat.count += lock->stat.count;
|
|
||||||
+ deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
|
|
||||||
+ deleted_wake_locks.stat.total_time =
|
|
||||||
+ ktime_add(deleted_wake_locks.stat.total_time,
|
|
||||||
+ lock->stat.total_time);
|
|
||||||
+ deleted_wake_locks.stat.prevent_suspend_time =
|
|
||||||
+ ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
|
|
||||||
+ lock->stat.prevent_suspend_time);
|
|
||||||
+ deleted_wake_locks.stat.max_time =
|
|
||||||
+ ktime_add(deleted_wake_locks.stat.max_time,
|
|
||||||
+ lock->stat.max_time);
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
+ list_del(&lock->link);
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(wake_lock_destroy);
|
|
||||||
+
|
|
||||||
+static void wake_lock_internal(
|
|
||||||
+ struct wake_lock *lock, long timeout, int has_timeout)
|
|
||||||
+{
|
|
||||||
+ int type;
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ long expire_in;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ type = lock->flags & WAKE_LOCK_TYPE_MASK;
|
|
||||||
+ BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
|
|
||||||
+ BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
|
|
||||||
+ if (debug_mask & DEBUG_WAKEUP)
|
|
||||||
+ pr_info("wakeup wake lock: %s\n", lock->name);
|
|
||||||
+ wait_for_wakeup = 0;
|
|
||||||
+ lock->stat.wakeup_count++;
|
|
||||||
+ }
|
|
||||||
+ if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
|
|
||||||
+ (long)(lock->expires - jiffies) <= 0) {
|
|
||||||
+ wake_unlock_stat_locked(lock, 0);
|
|
||||||
+ lock->stat.last_time = ktime_get();
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
+ if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
|
|
||||||
+ lock->flags |= WAKE_LOCK_ACTIVE;
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ lock->stat.last_time = ktime_get();
|
|
||||||
+#endif
|
|
||||||
+ }
|
|
||||||
+ list_del(&lock->link);
|
|
||||||
+ if (has_timeout) {
|
|
||||||
+ if (debug_mask & DEBUG_WAKE_LOCK)
|
|
||||||
+ pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
|
|
||||||
+ lock->name, type, timeout / HZ,
|
|
||||||
+ (timeout % HZ) * MSEC_PER_SEC / HZ);
|
|
||||||
+ lock->expires = jiffies + timeout;
|
|
||||||
+ lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
|
|
||||||
+ list_add_tail(&lock->link, &active_wake_locks[type]);
|
|
||||||
+ } else {
|
|
||||||
+ if (debug_mask & DEBUG_WAKE_LOCK)
|
|
||||||
+ pr_info("wake_lock: %s, type %d\n", lock->name, type);
|
|
||||||
+ lock->expires = LONG_MAX;
|
|
||||||
+ lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
|
|
||||||
+ list_add(&lock->link, &active_wake_locks[type]);
|
|
||||||
+ }
|
|
||||||
+ if (type == WAKE_LOCK_SUSPEND) {
|
|
||||||
+ current_event_num++;
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ if (lock == &main_wake_lock)
|
|
||||||
+ update_sleep_wait_stats_locked(1);
|
|
||||||
+ else if (!wake_lock_active(&main_wake_lock))
|
|
||||||
+ update_sleep_wait_stats_locked(0);
|
|
||||||
+#endif
|
|
||||||
+ if (has_timeout)
|
|
||||||
+ expire_in = has_wake_lock_locked(type);
|
|
||||||
+ else
|
|
||||||
+ expire_in = -1;
|
|
||||||
+ if (expire_in > 0) {
|
|
||||||
+ if (debug_mask & DEBUG_EXPIRE)
|
|
||||||
+ pr_info("wake_lock: %s, start expire timer, "
|
|
||||||
+ "%ld\n", lock->name, expire_in);
|
|
||||||
+ mod_timer(&expire_timer, jiffies + expire_in);
|
|
||||||
+ } else {
|
|
||||||
+ if (del_timer(&expire_timer))
|
|
||||||
+ if (debug_mask & DEBUG_EXPIRE)
|
|
||||||
+ pr_info("wake_lock: %s, stop expire timer\n",
|
|
||||||
+ lock->name);
|
|
||||||
+ if (expire_in == 0)
|
|
||||||
+ queue_work(suspend_work_queue, &suspend_work);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void wake_lock(struct wake_lock *lock)
|
|
||||||
+{
|
|
||||||
+ wake_lock_internal(lock, 0, 0);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(wake_lock);
|
|
||||||
+
|
|
||||||
+void wake_lock_timeout(struct wake_lock *lock, long timeout)
|
|
||||||
+{
|
|
||||||
+ wake_lock_internal(lock, timeout, 1);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(wake_lock_timeout);
|
|
||||||
+
|
|
||||||
+void wake_unlock(struct wake_lock *lock)
|
|
||||||
+{
|
|
||||||
+ int type;
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ spin_lock_irqsave(&list_lock, irqflags);
|
|
||||||
+ type = lock->flags & WAKE_LOCK_TYPE_MASK;
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ wake_unlock_stat_locked(lock, 0);
|
|
||||||
+#endif
|
|
||||||
+ if (debug_mask & DEBUG_WAKE_LOCK)
|
|
||||||
+ pr_info("wake_unlock: %s\n", lock->name);
|
|
||||||
+ lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
|
|
||||||
+ list_del(&lock->link);
|
|
||||||
+ list_add(&lock->link, &inactive_locks);
|
|
||||||
+ if (type == WAKE_LOCK_SUSPEND) {
|
|
||||||
+ long has_lock = has_wake_lock_locked(type);
|
|
||||||
+ if (has_lock > 0) {
|
|
||||||
+ if (debug_mask & DEBUG_EXPIRE)
|
|
||||||
+ pr_info("wake_unlock: %s, start expire timer, "
|
|
||||||
+ "%ld\n", lock->name, has_lock);
|
|
||||||
+ mod_timer(&expire_timer, jiffies + has_lock);
|
|
||||||
+ } else {
|
|
||||||
+ if (del_timer(&expire_timer))
|
|
||||||
+ if (debug_mask & DEBUG_EXPIRE)
|
|
||||||
+ pr_info("wake_unlock: %s, stop expire "
|
|
||||||
+ "timer\n", lock->name);
|
|
||||||
+ if (has_lock == 0)
|
|
||||||
+ queue_work(suspend_work_queue, &suspend_work);
|
|
||||||
+ }
|
|
||||||
+ if (lock == &main_wake_lock) {
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ print_active_locks(WAKE_LOCK_SUSPEND);
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ update_sleep_wait_stats_locked(0);
|
|
||||||
+#endif
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&list_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(wake_unlock);
|
|
||||||
+
|
|
||||||
+int wake_lock_active(struct wake_lock *lock)
|
|
||||||
+{
|
|
||||||
+ return !!(lock->flags & WAKE_LOCK_ACTIVE);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(wake_lock_active);
|
|
||||||
+
|
|
||||||
+static int __init wakelocks_init(void)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
|
|
||||||
+ INIT_LIST_HEAD(&active_wake_locks[i]);
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
|
|
||||||
+ "deleted_wake_locks");
|
|
||||||
+#endif
|
|
||||||
+ wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
|
|
||||||
+ wake_lock(&main_wake_lock);
|
|
||||||
+ wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
|
|
||||||
+
|
|
||||||
+ ret = platform_device_register(&power_device);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("wakelocks_init: platform_device_register failed\n");
|
|
||||||
+ goto err_platform_device_register;
|
|
||||||
+ }
|
|
||||||
+ ret = platform_driver_register(&power_driver);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("wakelocks_init: platform_driver_register failed\n");
|
|
||||||
+ goto err_platform_driver_register;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ suspend_work_queue = create_singlethread_workqueue("suspend");
|
|
||||||
+ if (suspend_work_queue == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_suspend_work_queue;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ create_proc_read_entry("wakelocks", S_IRUGO, NULL,
|
|
||||||
+ wakelocks_read_proc, NULL);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_suspend_work_queue:
|
|
||||||
+ platform_driver_unregister(&power_driver);
|
|
||||||
+err_platform_driver_register:
|
|
||||||
+ platform_device_unregister(&power_device);
|
|
||||||
+err_platform_device_register:
|
|
||||||
+ wake_lock_destroy(&unknown_wakeup);
|
|
||||||
+ wake_lock_destroy(&main_wake_lock);
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ wake_lock_destroy(&deleted_wake_locks);
|
|
||||||
+#endif
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit wakelocks_exit(void)
|
|
||||||
+{
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ remove_proc_entry("wakelocks", NULL);
|
|
||||||
+#endif
|
|
||||||
+ destroy_workqueue(suspend_work_queue);
|
|
||||||
+ platform_driver_unregister(&power_driver);
|
|
||||||
+ platform_device_unregister(&power_device);
|
|
||||||
+ wake_lock_destroy(&unknown_wakeup);
|
|
||||||
+ wake_lock_destroy(&main_wake_lock);
|
|
||||||
+#ifdef CONFIG_WAKELOCK_STAT
|
|
||||||
+ wake_lock_destroy(&deleted_wake_locks);
|
|
||||||
+#endif
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+core_initcall(wakelocks_init);
|
|
||||||
+module_exit(wakelocks_exit);
|
|
@ -1,244 +0,0 @@
|
|||||||
From 2d4a25785ad1aa51bad967543e1ab20e9ed4a046 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Tue, 7 Oct 2008 20:48:01 -0700
|
|
||||||
Subject: [PATCH 067/134] PM: Implement early suspend api
|
|
||||||
|
|
||||||
---
|
|
||||||
kernel/power/Kconfig | 12 +++
|
|
||||||
kernel/power/Makefile | 1 +
|
|
||||||
kernel/power/earlysuspend.c | 178 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
kernel/power/power.h | 6 ++
|
|
||||||
4 files changed, 197 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 kernel/power/earlysuspend.c
|
|
||||||
|
|
||||||
--- a/kernel/power/Kconfig
|
|
||||||
+++ b/kernel/power/Kconfig
|
|
||||||
@@ -119,6 +119,9 @@ config SUSPEND_FREEZER
|
|
||||||
config HAS_WAKELOCK
|
|
||||||
bool
|
|
||||||
|
|
||||||
+config HAS_EARLYSUSPEND
|
|
||||||
+ bool
|
|
||||||
+
|
|
||||||
config WAKELOCK
|
|
||||||
bool "Wake lock"
|
|
||||||
depends on PM && RTC_CLASS
|
|
||||||
@@ -135,6 +138,15 @@ config WAKELOCK_STAT
|
|
||||||
---help---
|
|
||||||
Report wake lock stats in /proc/wakelocks
|
|
||||||
|
|
||||||
+config EARLYSUSPEND
|
|
||||||
+ bool "Early suspend"
|
|
||||||
+ depends on WAKELOCK
|
|
||||||
+ default y
|
|
||||||
+ select HAS_EARLYSUSPEND
|
|
||||||
+ ---help---
|
|
||||||
+ Call early suspend handlers when the user requested sleep state
|
|
||||||
+ changes.
|
|
||||||
+
|
|
||||||
config HIBERNATION
|
|
||||||
bool "Hibernation (aka 'suspend to disk')"
|
|
||||||
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
|
|
||||||
--- a/kernel/power/Makefile
|
|
||||||
+++ b/kernel/power/Makefile
|
|
||||||
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM) += main.o
|
|
||||||
obj-$(CONFIG_PM_SLEEP) += console.o
|
|
||||||
obj-$(CONFIG_FREEZER) += process.o
|
|
||||||
obj-$(CONFIG_WAKELOCK) += wakelock.o
|
|
||||||
+obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
|
|
||||||
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/kernel/power/earlysuspend.c
|
|
||||||
@@ -0,0 +1,178 @@
|
|
||||||
+/* kernel/power/earlysuspend.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2005-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/earlysuspend.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/mutex.h>
|
|
||||||
+#include <linux/rtc.h>
|
|
||||||
+#include <linux/syscalls.h> /* sys_sync */
|
|
||||||
+#include <linux/wakelock.h>
|
|
||||||
+#include <linux/workqueue.h>
|
|
||||||
+
|
|
||||||
+#include "power.h"
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ DEBUG_USER_STATE = 1U << 0,
|
|
||||||
+ DEBUG_SUSPEND = 1U << 2,
|
|
||||||
+};
|
|
||||||
+static int debug_mask = DEBUG_USER_STATE;
|
|
||||||
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
|
||||||
+
|
|
||||||
+static DEFINE_MUTEX(early_suspend_lock);
|
|
||||||
+static LIST_HEAD(early_suspend_handlers);
|
|
||||||
+static void early_suspend(struct work_struct *work);
|
|
||||||
+static void late_resume(struct work_struct *work);
|
|
||||||
+static DECLARE_WORK(early_suspend_work, early_suspend);
|
|
||||||
+static DECLARE_WORK(late_resume_work, late_resume);
|
|
||||||
+static DEFINE_SPINLOCK(state_lock);
|
|
||||||
+enum {
|
|
||||||
+ SUSPEND_REQUESTED = 0x1,
|
|
||||||
+ SUSPENDED = 0x2,
|
|
||||||
+ SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
|
|
||||||
+};
|
|
||||||
+static int state;
|
|
||||||
+
|
|
||||||
+void register_early_suspend(struct early_suspend *handler)
|
|
||||||
+{
|
|
||||||
+ struct list_head *pos;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&early_suspend_lock);
|
|
||||||
+ list_for_each(pos, &early_suspend_handlers) {
|
|
||||||
+ struct early_suspend *e;
|
|
||||||
+ e = list_entry(pos, struct early_suspend, link);
|
|
||||||
+ if (e->level > handler->level)
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ list_add_tail(&handler->link, pos);
|
|
||||||
+ if ((state & SUSPENDED) && handler->suspend)
|
|
||||||
+ handler->suspend(handler);
|
|
||||||
+ mutex_unlock(&early_suspend_lock);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(register_early_suspend);
|
|
||||||
+
|
|
||||||
+void unregister_early_suspend(struct early_suspend *handler)
|
|
||||||
+{
|
|
||||||
+ mutex_lock(&early_suspend_lock);
|
|
||||||
+ list_del(&handler->link);
|
|
||||||
+ mutex_unlock(&early_suspend_lock);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(unregister_early_suspend);
|
|
||||||
+
|
|
||||||
+static void early_suspend(struct work_struct *work)
|
|
||||||
+{
|
|
||||||
+ struct early_suspend *pos;
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ int abort = 0;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&early_suspend_lock);
|
|
||||||
+ spin_lock_irqsave(&state_lock, irqflags);
|
|
||||||
+ if (state == SUSPEND_REQUESTED)
|
|
||||||
+ state |= SUSPENDED;
|
|
||||||
+ else
|
|
||||||
+ abort = 1;
|
|
||||||
+ spin_unlock_irqrestore(&state_lock, irqflags);
|
|
||||||
+
|
|
||||||
+ if (abort) {
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("early_suspend: abort, state %d\n", state);
|
|
||||||
+ mutex_unlock(&early_suspend_lock);
|
|
||||||
+ goto abort;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("early_suspend: call handlers\n");
|
|
||||||
+ list_for_each_entry(pos, &early_suspend_handlers, link) {
|
|
||||||
+ if (pos->suspend != NULL)
|
|
||||||
+ pos->suspend(pos);
|
|
||||||
+ }
|
|
||||||
+ mutex_unlock(&early_suspend_lock);
|
|
||||||
+
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("early_suspend: sync\n");
|
|
||||||
+
|
|
||||||
+ sys_sync();
|
|
||||||
+abort:
|
|
||||||
+ spin_lock_irqsave(&state_lock, irqflags);
|
|
||||||
+ if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
|
|
||||||
+ wake_unlock(&main_wake_lock);
|
|
||||||
+ spin_unlock_irqrestore(&state_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void late_resume(struct work_struct *work)
|
|
||||||
+{
|
|
||||||
+ struct early_suspend *pos;
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ int abort = 0;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&early_suspend_lock);
|
|
||||||
+ spin_lock_irqsave(&state_lock, irqflags);
|
|
||||||
+ if (state == SUSPENDED)
|
|
||||||
+ state &= ~SUSPENDED;
|
|
||||||
+ else
|
|
||||||
+ abort = 1;
|
|
||||||
+ spin_unlock_irqrestore(&state_lock, irqflags);
|
|
||||||
+
|
|
||||||
+ if (abort) {
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("late_resume: abort, state %d\n", state);
|
|
||||||
+ goto abort;
|
|
||||||
+ }
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("late_resume: call handlers\n");
|
|
||||||
+ list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
|
|
||||||
+ if (pos->resume != NULL)
|
|
||||||
+ pos->resume(pos);
|
|
||||||
+ if (debug_mask & DEBUG_SUSPEND)
|
|
||||||
+ pr_info("late_resume: done\n");
|
|
||||||
+abort:
|
|
||||||
+ mutex_unlock(&early_suspend_lock);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void request_suspend_state(suspend_state_t new_state)
|
|
||||||
+{
|
|
||||||
+ unsigned long irqflags;
|
|
||||||
+ int old_sleep;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&state_lock, irqflags);
|
|
||||||
+ old_sleep = state & SUSPEND_REQUESTED;
|
|
||||||
+ if (debug_mask & DEBUG_USER_STATE) {
|
|
||||||
+ struct timespec ts;
|
|
||||||
+ struct rtc_time tm;
|
|
||||||
+ getnstimeofday(&ts);
|
|
||||||
+ rtc_time_to_tm(ts.tv_sec, &tm);
|
|
||||||
+ pr_info("request_suspend_state: %s (%d->%d) at %lld "
|
|
||||||
+ "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
|
|
||||||
+ new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
|
|
||||||
+ requested_suspend_state, new_state,
|
|
||||||
+ ktime_to_ns(ktime_get()),
|
|
||||||
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
||||||
+ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
|
|
||||||
+ }
|
|
||||||
+ if (!old_sleep && new_state != PM_SUSPEND_ON) {
|
|
||||||
+ state |= SUSPEND_REQUESTED;
|
|
||||||
+ queue_work(suspend_work_queue, &early_suspend_work);
|
|
||||||
+ } else if (old_sleep && new_state == PM_SUSPEND_ON) {
|
|
||||||
+ state &= ~SUSPEND_REQUESTED;
|
|
||||||
+ wake_lock(&main_wake_lock);
|
|
||||||
+ queue_work(suspend_work_queue, &late_resume_work);
|
|
||||||
+ }
|
|
||||||
+ requested_suspend_state = new_state;
|
|
||||||
+ spin_unlock_irqrestore(&state_lock, irqflags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+suspend_state_t get_suspend_state(void)
|
|
||||||
+{
|
|
||||||
+ return requested_suspend_state;
|
|
||||||
+}
|
|
||||||
--- a/kernel/power/power.h
|
|
||||||
+++ b/kernel/power/power.h
|
|
||||||
@@ -230,3 +230,9 @@ extern struct workqueue_struct *suspend_
|
|
||||||
extern struct wake_lock main_wake_lock;
|
|
||||||
extern suspend_state_t requested_suspend_state;
|
|
||||||
#endif
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_EARLYSUSPEND
|
|
||||||
+/* kernel/power/earlysuspend.c */
|
|
||||||
+void request_suspend_state(suspend_state_t state);
|
|
||||||
+suspend_state_t get_suspend_state(void);
|
|
||||||
+#endif
|
|
@ -1,52 +0,0 @@
|
|||||||
From 7b236e69bb4403f20fbdef81907d46b4d32d4af8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Thu, 9 Oct 2008 19:17:11 -0700
|
|
||||||
Subject: [PATCH 068/134] PM: Enable early suspend through /sys/power/state
|
|
||||||
|
|
||||||
If EARLYSUSPEND is enabled then writes to /sys/power/state no longer
|
|
||||||
blocks, and the kernel will try to enter the requested state every
|
|
||||||
time no wakelocks are held. Write "on" to resume normal operation.
|
|
||||||
---
|
|
||||||
kernel/power/main.c | 14 ++++++++++++++
|
|
||||||
1 files changed, 14 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/power/main.c
|
|
||||||
+++ b/kernel/power/main.c
|
|
||||||
@@ -408,6 +408,9 @@ static void suspend_finish(void)
|
|
||||||
|
|
||||||
|
|
||||||
static const char * const pm_states[PM_SUSPEND_MAX] = {
|
|
||||||
+#ifdef CONFIG_EARLYSUSPEND
|
|
||||||
+ [PM_SUSPEND_ON] = "on",
|
|
||||||
+#endif
|
|
||||||
[PM_SUSPEND_STANDBY] = "standby",
|
|
||||||
[PM_SUSPEND_MEM] = "mem",
|
|
||||||
};
|
|
||||||
@@ -525,7 +528,11 @@ static ssize_t state_store(struct kobjec
|
|
||||||
const char *buf, size_t n)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_SUSPEND
|
|
||||||
+#ifdef CONFIG_EARLYSUSPEND
|
|
||||||
+ suspend_state_t state = PM_SUSPEND_ON;
|
|
||||||
+#else
|
|
||||||
suspend_state_t state = PM_SUSPEND_STANDBY;
|
|
||||||
+#endif
|
|
||||||
const char * const *s;
|
|
||||||
#endif
|
|
||||||
char *p;
|
|
||||||
@@ -547,8 +554,15 @@ static ssize_t state_store(struct kobjec
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (state < PM_SUSPEND_MAX && *s)
|
|
||||||
+#ifdef CONFIG_EARLYSUSPEND
|
|
||||||
+ if (state == PM_SUSPEND_ON || valid_state(state)) {
|
|
||||||
+ error = 0;
|
|
||||||
+ request_suspend_state(state);
|
|
||||||
+ }
|
|
||||||
+#else
|
|
||||||
error = enter_state(state);
|
|
||||||
#endif
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
Exit:
|
|
||||||
return error ? error : n;
|
|
@ -1,314 +0,0 @@
|
|||||||
From 48e1af2bdd11204f11b3770a6c8d3eee64aee2e8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Thu, 9 Oct 2008 21:01:46 -0700
|
|
||||||
Subject: [PATCH 069/134] PM: Add user-space wake lock api.
|
|
||||||
|
|
||||||
This adds /sys/power/wake_lock and /sys/power/wake_unlock.
|
|
||||||
Writing a string to wake_lock creates a wake lock the
|
|
||||||
first time is sees a string and locks it. Optionally, the
|
|
||||||
string can be followed by a timeout.
|
|
||||||
To unlock the wake lock, write the same string to wake_unlock.
|
|
||||||
---
|
|
||||||
kernel/power/Kconfig | 10 ++
|
|
||||||
kernel/power/Makefile | 1 +
|
|
||||||
kernel/power/main.c | 9 ++
|
|
||||||
kernel/power/power.h | 11 ++
|
|
||||||
kernel/power/userwakelock.c | 218 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
5 files changed, 249 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 kernel/power/userwakelock.c
|
|
||||||
|
|
||||||
--- a/kernel/power/Kconfig
|
|
||||||
+++ b/kernel/power/Kconfig
|
|
||||||
@@ -138,6 +138,16 @@ config WAKELOCK_STAT
|
|
||||||
---help---
|
|
||||||
Report wake lock stats in /proc/wakelocks
|
|
||||||
|
|
||||||
+config USER_WAKELOCK
|
|
||||||
+ bool "Userspace wake locks"
|
|
||||||
+ depends on WAKELOCK
|
|
||||||
+ default y
|
|
||||||
+ ---help---
|
|
||||||
+ User-space wake lock api. Write "lockname" or "lockname timeout"
|
|
||||||
+ to /sys/power/wake_lock lock and if needed create a wake lock.
|
|
||||||
+ Write "lockname" to /sys/power/wake_unlock to unlock a user wake
|
|
||||||
+ lock.
|
|
||||||
+
|
|
||||||
config EARLYSUSPEND
|
|
||||||
bool "Early suspend"
|
|
||||||
depends on WAKELOCK
|
|
||||||
--- a/kernel/power/Makefile
|
|
||||||
+++ b/kernel/power/Makefile
|
|
||||||
@@ -7,6 +7,7 @@ obj-$(CONFIG_PM) += main.o
|
|
||||||
obj-$(CONFIG_PM_SLEEP) += console.o
|
|
||||||
obj-$(CONFIG_FREEZER) += process.o
|
|
||||||
obj-$(CONFIG_WAKELOCK) += wakelock.o
|
|
||||||
+obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
|
|
||||||
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
|
|
||||||
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
|
|
||||||
|
|
||||||
--- a/kernel/power/main.c
|
|
||||||
+++ b/kernel/power/main.c
|
|
||||||
@@ -595,6 +595,11 @@ pm_trace_store(struct kobject *kobj, str
|
|
||||||
power_attr(pm_trace);
|
|
||||||
#endif /* CONFIG_PM_TRACE */
|
|
||||||
|
|
||||||
+#ifdef CONFIG_USER_WAKELOCK
|
|
||||||
+power_attr(wake_lock);
|
|
||||||
+power_attr(wake_unlock);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
static struct attribute * g[] = {
|
|
||||||
&state_attr.attr,
|
|
||||||
#ifdef CONFIG_PM_TRACE
|
|
||||||
@@ -603,6 +608,10 @@ static struct attribute * g[] = {
|
|
||||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
|
|
||||||
&pm_test_attr.attr,
|
|
||||||
#endif
|
|
||||||
+#ifdef CONFIG_USER_WAKELOCK
|
|
||||||
+ &wake_lock_attr.attr,
|
|
||||||
+ &wake_unlock_attr.attr,
|
|
||||||
+#endif
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
--- a/kernel/power/power.h
|
|
||||||
+++ b/kernel/power/power.h
|
|
||||||
@@ -231,6 +231,17 @@ extern struct wake_lock main_wake_lock;
|
|
||||||
extern suspend_state_t requested_suspend_state;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+#ifdef CONFIG_USER_WAKELOCK
|
|
||||||
+ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
||||||
+ char *buf);
|
|
||||||
+ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
||||||
+ const char *buf, size_t n);
|
|
||||||
+ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
||||||
+ char *buf);
|
|
||||||
+ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
||||||
+ const char *buf, size_t n);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#ifdef CONFIG_EARLYSUSPEND
|
|
||||||
/* kernel/power/earlysuspend.c */
|
|
||||||
void request_suspend_state(suspend_state_t state);
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/kernel/power/userwakelock.c
|
|
||||||
@@ -0,0 +1,218 @@
|
|
||||||
+/* kernel/power/userwakelock.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2005-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/ctype.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/wakelock.h>
|
|
||||||
+
|
|
||||||
+#include "power.h"
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ DEBUG_FAILURE = BIT(0),
|
|
||||||
+ DEBUG_ERROR = BIT(1),
|
|
||||||
+ DEBUG_NEW = BIT(2),
|
|
||||||
+ DEBUG_ACCESS = BIT(3),
|
|
||||||
+ DEBUG_LOOKUP = BIT(4),
|
|
||||||
+};
|
|
||||||
+static int debug_mask = DEBUG_FAILURE;
|
|
||||||
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
|
||||||
+
|
|
||||||
+static DEFINE_MUTEX(tree_lock);
|
|
||||||
+
|
|
||||||
+struct user_wake_lock {
|
|
||||||
+ struct rb_node node;
|
|
||||||
+ struct wake_lock wake_lock;
|
|
||||||
+ char name[0];
|
|
||||||
+};
|
|
||||||
+struct rb_root user_wake_locks;
|
|
||||||
+
|
|
||||||
+static struct user_wake_lock *lookup_wake_lock_name(
|
|
||||||
+ const char *buf, int allocate, long *timeoutptr)
|
|
||||||
+{
|
|
||||||
+ struct rb_node **p = &user_wake_locks.rb_node;
|
|
||||||
+ struct rb_node *parent = NULL;
|
|
||||||
+ struct user_wake_lock *l;
|
|
||||||
+ int diff;
|
|
||||||
+ u64 timeout;
|
|
||||||
+ int name_len;
|
|
||||||
+ const char *arg;
|
|
||||||
+
|
|
||||||
+ /* Find length of lock name and start of optional timeout string */
|
|
||||||
+ arg = buf;
|
|
||||||
+ while (*arg && !isspace(*arg))
|
|
||||||
+ arg++;
|
|
||||||
+ name_len = arg - buf;
|
|
||||||
+ if (!name_len)
|
|
||||||
+ goto bad_arg;
|
|
||||||
+ while (isspace(*arg))
|
|
||||||
+ arg++;
|
|
||||||
+
|
|
||||||
+ /* Process timeout string */
|
|
||||||
+ if (timeoutptr && *arg) {
|
|
||||||
+ timeout = simple_strtoull(arg, (char **)&arg, 0);
|
|
||||||
+ while (isspace(*arg))
|
|
||||||
+ arg++;
|
|
||||||
+ if (*arg)
|
|
||||||
+ goto bad_arg;
|
|
||||||
+ /* convert timeout from nanoseconds to jiffies > 0 */
|
|
||||||
+ timeout += (NSEC_PER_SEC / HZ) - 1;
|
|
||||||
+ do_div(timeout, (NSEC_PER_SEC / HZ));
|
|
||||||
+ if (timeout <= 0)
|
|
||||||
+ timeout = 1;
|
|
||||||
+ *timeoutptr = timeout;
|
|
||||||
+ } else if (*arg)
|
|
||||||
+ goto bad_arg;
|
|
||||||
+ else if (timeoutptr)
|
|
||||||
+ *timeoutptr = 0;
|
|
||||||
+
|
|
||||||
+ /* Lookup wake lock in rbtree */
|
|
||||||
+ while (*p) {
|
|
||||||
+ parent = *p;
|
|
||||||
+ l = rb_entry(parent, struct user_wake_lock, node);
|
|
||||||
+ diff = strncmp(buf, l->name, name_len);
|
|
||||||
+ if (!diff && l->name[name_len])
|
|
||||||
+ diff = -1;
|
|
||||||
+ if (debug_mask & DEBUG_ERROR)
|
|
||||||
+ pr_info("lookup_wake_lock_name: compare %.*s %s %d\n",
|
|
||||||
+ name_len, buf, l->name, diff);
|
|
||||||
+
|
|
||||||
+ if (diff < 0)
|
|
||||||
+ p = &(*p)->rb_left;
|
|
||||||
+ else if (diff > 0)
|
|
||||||
+ p = &(*p)->rb_right;
|
|
||||||
+ else
|
|
||||||
+ return l;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Allocate and add new wakelock to rbtree */
|
|
||||||
+ if (!allocate) {
|
|
||||||
+ if (debug_mask & DEBUG_ERROR)
|
|
||||||
+ pr_info("lookup_wake_lock_name: %.*s not found\n",
|
|
||||||
+ name_len, buf);
|
|
||||||
+ return ERR_PTR(-EINVAL);
|
|
||||||
+ }
|
|
||||||
+ l = kzalloc(sizeof(*l) + name_len + 1, GFP_KERNEL);
|
|
||||||
+ if (l == NULL) {
|
|
||||||
+ if (debug_mask & DEBUG_FAILURE)
|
|
||||||
+ pr_err("lookup_wake_lock_name: failed to allocate "
|
|
||||||
+ "memory for %.*s\n", name_len, buf);
|
|
||||||
+ return ERR_PTR(-ENOMEM);
|
|
||||||
+ }
|
|
||||||
+ memcpy(l->name, buf, name_len);
|
|
||||||
+ if (debug_mask & DEBUG_NEW)
|
|
||||||
+ pr_info("lookup_wake_lock_name: new wake lock %s\n", l->name);
|
|
||||||
+ wake_lock_init(&l->wake_lock, WAKE_LOCK_SUSPEND, l->name);
|
|
||||||
+ rb_link_node(&l->node, parent, p);
|
|
||||||
+ rb_insert_color(&l->node, &user_wake_locks);
|
|
||||||
+ return l;
|
|
||||||
+
|
|
||||||
+bad_arg:
|
|
||||||
+ if (debug_mask & DEBUG_ERROR)
|
|
||||||
+ pr_info("lookup_wake_lock_name: wake lock, %.*s, bad arg, %s\n",
|
|
||||||
+ name_len, buf, arg);
|
|
||||||
+ return ERR_PTR(-EINVAL);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+ssize_t wake_lock_show(
|
|
||||||
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
||||||
+{
|
|
||||||
+ char *s = buf;
|
|
||||||
+ char *end = buf + PAGE_SIZE;
|
|
||||||
+ struct rb_node *n;
|
|
||||||
+ struct user_wake_lock *l;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&tree_lock);
|
|
||||||
+
|
|
||||||
+ for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
|
|
||||||
+ l = rb_entry(n, struct user_wake_lock, node);
|
|
||||||
+ if (wake_lock_active(&l->wake_lock))
|
|
||||||
+ s += scnprintf(s, end - s, "%s ", l->name);
|
|
||||||
+ }
|
|
||||||
+ s += scnprintf(s, end - s, "\n");
|
|
||||||
+
|
|
||||||
+ mutex_unlock(&tree_lock);
|
|
||||||
+ return (s - buf);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+ssize_t wake_lock_store(
|
|
||||||
+ struct kobject *kobj, struct kobj_attribute *attr,
|
|
||||||
+ const char *buf, size_t n)
|
|
||||||
+{
|
|
||||||
+ long timeout;
|
|
||||||
+ struct user_wake_lock *l;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&tree_lock);
|
|
||||||
+ l = lookup_wake_lock_name(buf, 1, &timeout);
|
|
||||||
+ if (IS_ERR(l)) {
|
|
||||||
+ n = PTR_ERR(l);
|
|
||||||
+ goto bad_name;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (debug_mask & DEBUG_ACCESS)
|
|
||||||
+ pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
|
|
||||||
+
|
|
||||||
+ if (timeout)
|
|
||||||
+ wake_lock_timeout(&l->wake_lock, timeout);
|
|
||||||
+ else
|
|
||||||
+ wake_lock(&l->wake_lock);
|
|
||||||
+bad_name:
|
|
||||||
+ mutex_unlock(&tree_lock);
|
|
||||||
+ return n;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ssize_t wake_unlock_show(
|
|
||||||
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
||||||
+{
|
|
||||||
+ char *s = buf;
|
|
||||||
+ char *end = buf + PAGE_SIZE;
|
|
||||||
+ struct rb_node *n;
|
|
||||||
+ struct user_wake_lock *l;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&tree_lock);
|
|
||||||
+
|
|
||||||
+ for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {
|
|
||||||
+ l = rb_entry(n, struct user_wake_lock, node);
|
|
||||||
+ if (!wake_lock_active(&l->wake_lock))
|
|
||||||
+ s += scnprintf(s, end - s, "%s ", l->name);
|
|
||||||
+ }
|
|
||||||
+ s += scnprintf(s, end - s, "\n");
|
|
||||||
+
|
|
||||||
+ mutex_unlock(&tree_lock);
|
|
||||||
+ return (s - buf);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+ssize_t wake_unlock_store(
|
|
||||||
+ struct kobject *kobj, struct kobj_attribute *attr,
|
|
||||||
+ const char *buf, size_t n)
|
|
||||||
+{
|
|
||||||
+ struct user_wake_lock *l;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&tree_lock);
|
|
||||||
+ l = lookup_wake_lock_name(buf, 0, NULL);
|
|
||||||
+ if (IS_ERR(l)) {
|
|
||||||
+ n = PTR_ERR(l);
|
|
||||||
+ goto not_found;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (debug_mask & DEBUG_ACCESS)
|
|
||||||
+ pr_info("wake_unlock_store: %s\n", l->name);
|
|
||||||
+
|
|
||||||
+ wake_unlock(&l->wake_lock);
|
|
||||||
+not_found:
|
|
||||||
+ mutex_unlock(&tree_lock);
|
|
||||||
+ return n;
|
|
||||||
+}
|
|
||||||
+
|
|
@ -1,64 +0,0 @@
|
|||||||
From 3f8e407c418205252f00615da4d0182e8c960344 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Tue, 14 Oct 2008 16:02:39 -0700
|
|
||||||
Subject: [PATCH 070/134] PM: wakelock: Abort task freezing if a wake lock is held.
|
|
||||||
|
|
||||||
Avoids a problem where the device sometimes hangs for 20 seconds
|
|
||||||
before the screen is turned on.
|
|
||||||
---
|
|
||||||
kernel/power/process.c | 23 ++++++++++++++++++-----
|
|
||||||
1 files changed, 18 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/power/process.c
|
|
||||||
+++ b/kernel/power/process.c
|
|
||||||
@@ -13,6 +13,7 @@
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/syscalls.h>
|
|
||||||
#include <linux/freezer.h>
|
|
||||||
+#include <linux/wakelock.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Timeout for stopping processes
|
|
||||||
@@ -36,6 +37,7 @@ static int try_to_freeze_tasks(bool sig_
|
|
||||||
struct timeval start, end;
|
|
||||||
u64 elapsed_csecs64;
|
|
||||||
unsigned int elapsed_csecs;
|
|
||||||
+ unsigned int wakeup = 0;
|
|
||||||
|
|
||||||
do_gettimeofday(&start);
|
|
||||||
|
|
||||||
@@ -62,6 +64,10 @@ static int try_to_freeze_tasks(bool sig_
|
|
||||||
} while_each_thread(g, p);
|
|
||||||
read_unlock(&tasklist_lock);
|
|
||||||
yield(); /* Yield is okay here */
|
|
||||||
+ if (todo && has_wake_lock(WAKE_LOCK_SUSPEND)) {
|
|
||||||
+ wakeup = 1;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
if (time_after(jiffies, end_time))
|
|
||||||
break;
|
|
||||||
} while (todo);
|
|
||||||
@@ -77,11 +83,18 @@ static int try_to_freeze_tasks(bool sig_
|
|
||||||
* and caller must call thaw_processes() if something fails),
|
|
||||||
* but it cleans up leftover PF_FREEZE requests.
|
|
||||||
*/
|
|
||||||
- printk("\n");
|
|
||||||
- printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
|
|
||||||
- "(%d tasks refusing to freeze):\n",
|
|
||||||
- elapsed_csecs / 100, elapsed_csecs % 100, todo);
|
|
||||||
- show_state();
|
|
||||||
+ if(wakeup) {
|
|
||||||
+ printk("\n");
|
|
||||||
+ printk(KERN_ERR "Freezing of %s aborted\n",
|
|
||||||
+ sig_only ? "user space " : "tasks ");
|
|
||||||
+ }
|
|
||||||
+ else {
|
|
||||||
+ printk("\n");
|
|
||||||
+ printk(KERN_ERR "Freezing of tasks failed after %d.%02d seconds "
|
|
||||||
+ "(%d tasks refusing to freeze):\n",
|
|
||||||
+ elapsed_csecs / 100, elapsed_csecs % 100, todo);
|
|
||||||
+ show_state();
|
|
||||||
+ }
|
|
||||||
read_lock(&tasklist_lock);
|
|
||||||
do_each_thread(g, p) {
|
|
||||||
task_lock(p);
|
|
@ -1,133 +0,0 @@
|
|||||||
From 0083b2afe89dea4a06bfd967b164318ad0bccd11 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Wed, 15 Oct 2008 17:52:20 -0700
|
|
||||||
Subject: [PATCH 071/134] PM: earlysuspend: Add console switch when user requested sleep state changes.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
kernel/power/Kconfig | 17 ++++++++
|
|
||||||
kernel/power/Makefile | 1 +
|
|
||||||
kernel/power/consoleearlysuspend.c | 78 ++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 96 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 kernel/power/consoleearlysuspend.c
|
|
||||||
|
|
||||||
--- a/kernel/power/Kconfig
|
|
||||||
+++ b/kernel/power/Kconfig
|
|
||||||
@@ -157,6 +157,23 @@ config EARLYSUSPEND
|
|
||||||
Call early suspend handlers when the user requested sleep state
|
|
||||||
changes.
|
|
||||||
|
|
||||||
+choice
|
|
||||||
+ prompt "User-space screen access"
|
|
||||||
+ default CONSOLE_EARLYSUSPEND
|
|
||||||
+ depends on HAS_EARLYSUSPEND
|
|
||||||
+
|
|
||||||
+ config NO_USER_SPACE_SCREEN_ACCESS_CONTROL
|
|
||||||
+ bool "None"
|
|
||||||
+
|
|
||||||
+ config CONSOLE_EARLYSUSPEND
|
|
||||||
+ bool "Console switch on early-suspend"
|
|
||||||
+ depends on HAS_EARLYSUSPEND && VT
|
|
||||||
+ ---help---
|
|
||||||
+ Register early suspend handler to perform a console switch to
|
|
||||||
+ when user-space should stop drawing to the screen and a switch
|
|
||||||
+ back when it should resume.
|
|
||||||
+endchoice
|
|
||||||
+
|
|
||||||
config HIBERNATION
|
|
||||||
bool "Hibernation (aka 'suspend to disk')"
|
|
||||||
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
|
|
||||||
--- a/kernel/power/Makefile
|
|
||||||
+++ b/kernel/power/Makefile
|
|
||||||
@@ -9,6 +9,7 @@ obj-$(CONFIG_FREEZER) += process.o
|
|
||||||
obj-$(CONFIG_WAKELOCK) += wakelock.o
|
|
||||||
obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
|
|
||||||
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
|
|
||||||
+obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o
|
|
||||||
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/kernel/power/consoleearlysuspend.c
|
|
||||||
@@ -0,0 +1,78 @@
|
|
||||||
+/* kernel/power/consoleearlysuspend.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2005-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/console.h>
|
|
||||||
+#include <linux/earlysuspend.h>
|
|
||||||
+#include <linux/kbd_kern.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/vt_kern.h>
|
|
||||||
+#include <linux/wait.h>
|
|
||||||
+
|
|
||||||
+#define EARLY_SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
|
|
||||||
+
|
|
||||||
+static int orig_fgconsole;
|
|
||||||
+static void console_early_suspend(struct early_suspend *h)
|
|
||||||
+{
|
|
||||||
+ acquire_console_sem();
|
|
||||||
+ orig_fgconsole = fg_console;
|
|
||||||
+ if (vc_allocate(EARLY_SUSPEND_CONSOLE))
|
|
||||||
+ goto err;
|
|
||||||
+ if (set_console(EARLY_SUSPEND_CONSOLE))
|
|
||||||
+ goto err;
|
|
||||||
+ release_console_sem();
|
|
||||||
+
|
|
||||||
+ if (vt_waitactive(EARLY_SUSPEND_CONSOLE))
|
|
||||||
+ pr_warning("console_early_suspend: Can't switch VCs.\n");
|
|
||||||
+ return;
|
|
||||||
+err:
|
|
||||||
+ pr_warning("console_early_suspend: Can't set console\n");
|
|
||||||
+ release_console_sem();
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void console_late_resume(struct early_suspend *h)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ acquire_console_sem();
|
|
||||||
+ ret = set_console(orig_fgconsole);
|
|
||||||
+ release_console_sem();
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_warning("console_late_resume: Can't set console.\n");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (vt_waitactive(orig_fgconsole))
|
|
||||||
+ pr_warning("console_late_resume: Can't switch VCs.\n");
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct early_suspend console_early_suspend_desc = {
|
|
||||||
+ .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
|
|
||||||
+ .suspend = console_early_suspend,
|
|
||||||
+ .resume = console_late_resume,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init console_early_suspend_init(void)
|
|
||||||
+{
|
|
||||||
+ register_early_suspend(&console_early_suspend_desc);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit console_early_suspend_exit(void)
|
|
||||||
+{
|
|
||||||
+ unregister_early_suspend(&console_early_suspend_desc);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(console_early_suspend_init);
|
|
||||||
+module_exit(console_early_suspend_exit);
|
|
||||||
+
|
|
@ -1,217 +0,0 @@
|
|||||||
From ba801aa66e869f1f9f9d3a1610a2165baa024e4b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rebecca Schultz <rschultz@google.com>
|
|
||||||
Date: Thu, 17 Jul 2008 18:14:55 -0700
|
|
||||||
Subject: [PATCH 072/134] PM: earlysuspend: Removing dependence on console.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Rather than signaling a full update of the display from userspace via a
|
|
||||||
console switch, this patch introduces 2 files int /sys/power,
|
|
||||||
wait_for_fb_sleep and wait_for_fb_wake. Reading these files will block
|
|
||||||
until the requested state has been entered. When a read from
|
|
||||||
wait_for_fb_sleep returns userspace should stop drawing. When
|
|
||||||
wait_for_fb_wake returns, it should do a full update. If either are called
|
|
||||||
when the fb driver is already in the requested state, they will return
|
|
||||||
immediately.
|
|
||||||
|
|
||||||
Signed-off-by: Rebecca Schultz <rschultz@google.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
kernel/power/Kconfig | 9 +++
|
|
||||||
kernel/power/Makefile | 1 +
|
|
||||||
kernel/power/fbearlysuspend.c | 153 +++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 163 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 kernel/power/fbearlysuspend.c
|
|
||||||
|
|
||||||
--- a/kernel/power/Kconfig
|
|
||||||
+++ b/kernel/power/Kconfig
|
|
||||||
@@ -159,6 +159,7 @@ config EARLYSUSPEND
|
|
||||||
|
|
||||||
choice
|
|
||||||
prompt "User-space screen access"
|
|
||||||
+ default FB_EARLYSUSPEND if !FRAMEBUFFER_CONSOLE
|
|
||||||
default CONSOLE_EARLYSUSPEND
|
|
||||||
depends on HAS_EARLYSUSPEND
|
|
||||||
|
|
||||||
@@ -172,6 +173,14 @@ choice
|
|
||||||
Register early suspend handler to perform a console switch to
|
|
||||||
when user-space should stop drawing to the screen and a switch
|
|
||||||
back when it should resume.
|
|
||||||
+
|
|
||||||
+ config FB_EARLYSUSPEND
|
|
||||||
+ bool "Sysfs interface"
|
|
||||||
+ depends on HAS_EARLYSUSPEND
|
|
||||||
+ ---help---
|
|
||||||
+ Register early suspend handler that notifies and waits for
|
|
||||||
+ user-space through sysfs when user-space should stop drawing
|
|
||||||
+ to the screen and notifies user-space when it should resume.
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
config HIBERNATION
|
|
||||||
--- a/kernel/power/Makefile
|
|
||||||
+++ b/kernel/power/Makefile
|
|
||||||
@@ -10,6 +10,7 @@ obj-$(CONFIG_WAKELOCK) += wakelock.o
|
|
||||||
obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
|
|
||||||
obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
|
|
||||||
obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o
|
|
||||||
+obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o
|
|
||||||
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/kernel/power/fbearlysuspend.c
|
|
||||||
@@ -0,0 +1,153 @@
|
|
||||||
+/* kernel/power/fbearlysuspend.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2005-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/earlysuspend.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/wait.h>
|
|
||||||
+
|
|
||||||
+#include "power.h"
|
|
||||||
+
|
|
||||||
+static wait_queue_head_t fb_state_wq;
|
|
||||||
+static DEFINE_SPINLOCK(fb_state_lock);
|
|
||||||
+static enum {
|
|
||||||
+ FB_STATE_STOPPED_DRAWING,
|
|
||||||
+ FB_STATE_REQUEST_STOP_DRAWING,
|
|
||||||
+ FB_STATE_DRAWING_OK,
|
|
||||||
+} fb_state;
|
|
||||||
+
|
|
||||||
+/* tell userspace to stop drawing, wait for it to stop */
|
|
||||||
+static void stop_drawing_early_suspend(struct early_suspend *h)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
|
|
||||||
+ fb_state = FB_STATE_REQUEST_STOP_DRAWING;
|
|
||||||
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
|
|
||||||
+
|
|
||||||
+ wake_up_all(&fb_state_wq);
|
|
||||||
+ ret = wait_event_timeout(fb_state_wq,
|
|
||||||
+ fb_state == FB_STATE_STOPPED_DRAWING,
|
|
||||||
+ HZ);
|
|
||||||
+ if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING))
|
|
||||||
+ pr_warning("stop_drawing_early_suspend: timeout waiting for "
|
|
||||||
+ "userspace to stop drawing\n");
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* tell userspace to start drawing */
|
|
||||||
+static void start_drawing_late_resume(struct early_suspend *h)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
|
|
||||||
+ fb_state = FB_STATE_DRAWING_OK;
|
|
||||||
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
|
|
||||||
+ wake_up(&fb_state_wq);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct early_suspend stop_drawing_early_suspend_desc = {
|
|
||||||
+ .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
|
|
||||||
+ .suspend = stop_drawing_early_suspend,
|
|
||||||
+ .resume = start_drawing_late_resume,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static ssize_t wait_for_fb_sleep_show(struct kobject *kobj,
|
|
||||||
+ struct kobj_attribute *attr, char *buf)
|
|
||||||
+{
|
|
||||||
+ char *s = buf;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ ret = wait_event_interruptible(fb_state_wq,
|
|
||||||
+ fb_state != FB_STATE_DRAWING_OK);
|
|
||||||
+ if (ret && fb_state == FB_STATE_DRAWING_OK)
|
|
||||||
+ return ret;
|
|
||||||
+ else
|
|
||||||
+ s += sprintf(buf, "sleeping");
|
|
||||||
+ return s - buf;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t wait_for_fb_wake_show(struct kobject *kobj,
|
|
||||||
+ struct kobj_attribute *attr, char *buf)
|
|
||||||
+{
|
|
||||||
+ char *s = buf;
|
|
||||||
+ int ret;
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&fb_state_lock, irq_flags);
|
|
||||||
+ if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) {
|
|
||||||
+ fb_state = FB_STATE_STOPPED_DRAWING;
|
|
||||||
+ wake_up(&fb_state_wq);
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&fb_state_lock, irq_flags);
|
|
||||||
+
|
|
||||||
+ ret = wait_event_interruptible(fb_state_wq,
|
|
||||||
+ fb_state == FB_STATE_DRAWING_OK);
|
|
||||||
+ if (ret && fb_state != FB_STATE_DRAWING_OK)
|
|
||||||
+ return ret;
|
|
||||||
+ else
|
|
||||||
+ s += sprintf(buf, "awake");
|
|
||||||
+
|
|
||||||
+ return s - buf;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define power_ro_attr(_name) \
|
|
||||||
+static struct kobj_attribute _name##_attr = { \
|
|
||||||
+ .attr = { \
|
|
||||||
+ .name = __stringify(_name), \
|
|
||||||
+ .mode = 0444, \
|
|
||||||
+ }, \
|
|
||||||
+ .show = _name##_show, \
|
|
||||||
+ .store = NULL, \
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+power_ro_attr(wait_for_fb_sleep);
|
|
||||||
+power_ro_attr(wait_for_fb_wake);
|
|
||||||
+
|
|
||||||
+static struct attribute *g[] = {
|
|
||||||
+ &wait_for_fb_sleep_attr.attr,
|
|
||||||
+ &wait_for_fb_wake_attr.attr,
|
|
||||||
+ NULL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct attribute_group attr_group = {
|
|
||||||
+ .attrs = g,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init android_power_init(void)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ init_waitqueue_head(&fb_state_wq);
|
|
||||||
+ fb_state = FB_STATE_DRAWING_OK;
|
|
||||||
+
|
|
||||||
+ ret = sysfs_create_group(power_kobj, &attr_group);
|
|
||||||
+ if (ret) {
|
|
||||||
+ pr_err("android_power_init: sysfs_create_group failed\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ register_early_suspend(&stop_drawing_early_suspend_desc);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit android_power_exit(void)
|
|
||||||
+{
|
|
||||||
+ unregister_early_suspend(&stop_drawing_early_suspend_desc);
|
|
||||||
+ sysfs_remove_group(power_kobj, &attr_group);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(android_power_init);
|
|
||||||
+module_exit(android_power_exit);
|
|
||||||
+
|
|
@ -1,132 +0,0 @@
|
|||||||
From 2df5996350da51bcdee798c00ae1f2415fd9ad20 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Robert Love <rlove@google.com>
|
|
||||||
Date: Mon, 12 May 2008 17:08:29 -0400
|
|
||||||
Subject: [PATCH 081/134] net: socket ioctl to reset connections matching local address
|
|
||||||
|
|
||||||
Introduce a new socket ioctl, SIOCKILLADDR, that nukes all sockets
|
|
||||||
bound to the same local address. This is useful in situations with
|
|
||||||
dynamic IPs, to kill stuck connections.
|
|
||||||
|
|
||||||
Signed-off-by: Brian Swetland <swetland@google.com>
|
|
||||||
|
|
||||||
net: fix tcp_v4_nuke_addr
|
|
||||||
|
|
||||||
Signed-off-by: Dima Zavin <dima@android.com>
|
|
||||||
---
|
|
||||||
include/linux/sockios.h | 1 +
|
|
||||||
include/net/tcp.h | 2 ++
|
|
||||||
net/ipv4/af_inet.c | 1 +
|
|
||||||
net/ipv4/devinet.c | 9 ++++++++-
|
|
||||||
net/ipv4/tcp_ipv4.c | 31 +++++++++++++++++++++++++++++++
|
|
||||||
5 files changed, 43 insertions(+), 1 deletions(-)
|
|
||||||
|
|
||||||
--- a/include/linux/sockios.h
|
|
||||||
+++ b/include/linux/sockios.h
|
|
||||||
@@ -65,6 +65,7 @@
|
|
||||||
#define SIOCDIFADDR 0x8936 /* delete PA address */
|
|
||||||
#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */
|
|
||||||
#define SIOCGIFCOUNT 0x8938 /* get number of devices */
|
|
||||||
+#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */
|
|
||||||
|
|
||||||
#define SIOCGIFBR 0x8940 /* Bridging support */
|
|
||||||
#define SIOCSIFBR 0x8941 /* Set bridging options */
|
|
||||||
--- a/include/net/tcp.h
|
|
||||||
+++ b/include/net/tcp.h
|
|
||||||
@@ -1381,6 +1381,8 @@ extern struct sk_buff **tcp4_gro_receive
|
|
||||||
extern int tcp_gro_complete(struct sk_buff *skb);
|
|
||||||
extern int tcp4_gro_complete(struct sk_buff *skb);
|
|
||||||
|
|
||||||
+extern void tcp_v4_nuke_addr(__u32 saddr);
|
|
||||||
+
|
|
||||||
#ifdef CONFIG_PROC_FS
|
|
||||||
extern int tcp4_proc_init(void);
|
|
||||||
extern void tcp4_proc_exit(void);
|
|
||||||
--- a/net/ipv4/af_inet.c
|
|
||||||
+++ b/net/ipv4/af_inet.c
|
|
||||||
@@ -825,6 +825,7 @@ int inet_ioctl(struct socket *sock, unsi
|
|
||||||
case SIOCSIFPFLAGS:
|
|
||||||
case SIOCGIFPFLAGS:
|
|
||||||
case SIOCSIFFLAGS:
|
|
||||||
+ case SIOCKILLADDR:
|
|
||||||
err = devinet_ioctl(net, cmd, (void __user *)arg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
--- a/net/ipv4/devinet.c
|
|
||||||
+++ b/net/ipv4/devinet.c
|
|
||||||
@@ -57,6 +57,7 @@
|
|
||||||
|
|
||||||
#include <net/arp.h>
|
|
||||||
#include <net/ip.h>
|
|
||||||
+#include <net/tcp.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <net/ip_fib.h>
|
|
||||||
#include <net/rtnetlink.h>
|
|
||||||
@@ -631,6 +632,7 @@ int devinet_ioctl(struct net *net, unsig
|
|
||||||
case SIOCSIFBRDADDR: /* Set the broadcast address */
|
|
||||||
case SIOCSIFDSTADDR: /* Set the destination address */
|
|
||||||
case SIOCSIFNETMASK: /* Set the netmask for the interface */
|
|
||||||
+ case SIOCKILLADDR: /* Nuke all sockets on this address */
|
|
||||||
ret = -EACCES;
|
|
||||||
if (!capable(CAP_NET_ADMIN))
|
|
||||||
goto out;
|
|
||||||
@@ -680,7 +682,8 @@ int devinet_ioctl(struct net *net, unsig
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = -EADDRNOTAVAIL;
|
|
||||||
- if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
|
|
||||||
+ if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS
|
|
||||||
+ && cmd != SIOCKILLADDR)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
@@ -804,6 +807,10 @@ int devinet_ioctl(struct net *net, unsig
|
|
||||||
inet_insert_ifa(ifa);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
+ case SIOCKILLADDR: /* Nuke all connections on this address */
|
|
||||||
+ ret = 0;
|
|
||||||
+ tcp_v4_nuke_addr(sin->sin_addr.s_addr);
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
done:
|
|
||||||
rtnl_unlock();
|
|
||||||
--- a/net/ipv4/tcp_ipv4.c
|
|
||||||
+++ b/net/ipv4/tcp_ipv4.c
|
|
||||||
@@ -1845,6 +1845,37 @@ void tcp_v4_destroy_sock(struct sock *sk
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(tcp_v4_destroy_sock);
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * tcp_v4_nuke_addr - destroy all sockets on the given local address
|
|
||||||
+ */
|
|
||||||
+void tcp_v4_nuke_addr(__u32 saddr)
|
|
||||||
+{
|
|
||||||
+ unsigned int bucket;
|
|
||||||
+
|
|
||||||
+ for (bucket = 0; bucket < tcp_hashinfo.ehash_size; bucket++) {
|
|
||||||
+ struct hlist_nulls_node *node;
|
|
||||||
+ struct sock *sk;
|
|
||||||
+ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket);
|
|
||||||
+
|
|
||||||
+ spin_lock_bh(lock);
|
|
||||||
+ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
|
|
||||||
+ struct inet_sock *inet = inet_sk(sk);
|
|
||||||
+
|
|
||||||
+ if (inet->rcv_saddr != saddr)
|
|
||||||
+ continue;
|
|
||||||
+ if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
|
|
||||||
+ continue;
|
|
||||||
+ if (sock_flag(sk, SOCK_DEAD))
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ sk->sk_err = ETIMEDOUT;
|
|
||||||
+ sk->sk_error_report(sk);
|
|
||||||
+ tcp_done(sk);
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_bh(lock);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
#ifdef CONFIG_PROC_FS
|
|
||||||
/* Proc filesystem TCP sock list dumping. */
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
|||||||
From 75090d40ffbd9b4863238e1d62b43f8598da5e5e Mon Sep 17 00:00:00 2001
|
|
||||||
From: Robert Love <rlove@google.com>
|
|
||||||
Date: Thu, 31 Jul 2008 11:12:44 -0400
|
|
||||||
Subject: [PATCH 082/134] sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size
|
|
||||||
|
|
||||||
Add a family of knobs to /sys/kernel/ipv4 for controlling the TCP window size:
|
|
||||||
|
|
||||||
tcp_wmem_min
|
|
||||||
tcp_wmem_def
|
|
||||||
tcp_wmem_max
|
|
||||||
tcp_rmem_min
|
|
||||||
tcp_rmem_def
|
|
||||||
tcp_rmem_max
|
|
||||||
|
|
||||||
This six values mirror the sysctl knobs in /proc/sys/net/ipv4/tcp_wmem and
|
|
||||||
/proc/sys/net/ipv4/tcp_rmem.
|
|
||||||
|
|
||||||
Sysfs, unlike sysctl, allows us to set and manage the files' permissions and
|
|
||||||
owners.
|
|
||||||
|
|
||||||
Signed-off-by: Robert Love <rlove@google.com>
|
|
||||||
---
|
|
||||||
net/ipv4/Makefile | 1 +
|
|
||||||
net/ipv4/sysfs_net_ipv4.c | 88 +++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 89 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 net/ipv4/sysfs_net_ipv4.c
|
|
||||||
|
|
||||||
--- a/net/ipv4/Makefile
|
|
||||||
+++ b/net/ipv4/Makefile
|
|
||||||
@@ -14,6 +14,7 @@ obj-y := route.o inetpeer.o protocol
|
|
||||||
inet_fragment.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
|
|
||||||
+obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o
|
|
||||||
obj-$(CONFIG_IP_FIB_HASH) += fib_hash.o
|
|
||||||
obj-$(CONFIG_IP_FIB_TRIE) += fib_trie.o
|
|
||||||
obj-$(CONFIG_PROC_FS) += proc.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/net/ipv4/sysfs_net_ipv4.c
|
|
||||||
@@ -0,0 +1,88 @@
|
|
||||||
+/*
|
|
||||||
+ * net/ipv4/sysfs_net_ipv4.c
|
|
||||||
+ *
|
|
||||||
+ * sysfs-based networking knobs (so we can, unlike with sysctl, control perms)
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * Robert Love <rlove@google.com>
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/kobject.h>
|
|
||||||
+#include <linux/string.h>
|
|
||||||
+#include <linux/sysfs.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <net/tcp.h>
|
|
||||||
+
|
|
||||||
+#define CREATE_IPV4_FILE(_name, _var) \
|
|
||||||
+static ssize_t _name##_show(struct kobject *kobj, \
|
|
||||||
+ struct kobj_attribute *attr, char *buf) \
|
|
||||||
+{ \
|
|
||||||
+ return sprintf(buf, "%d\n", _var); \
|
|
||||||
+} \
|
|
||||||
+static ssize_t _name##_store(struct kobject *kobj, \
|
|
||||||
+ struct kobj_attribute *attr, \
|
|
||||||
+ const char *buf, size_t count) \
|
|
||||||
+{ \
|
|
||||||
+ int val, ret; \
|
|
||||||
+ ret = sscanf(buf, "%d", &val); \
|
|
||||||
+ if (ret != 1) \
|
|
||||||
+ return -EINVAL; \
|
|
||||||
+ if (val < 0) \
|
|
||||||
+ return -EINVAL; \
|
|
||||||
+ _var = val; \
|
|
||||||
+ return count; \
|
|
||||||
+} \
|
|
||||||
+static struct kobj_attribute _name##_attr = \
|
|
||||||
+ __ATTR(_name, 0644, _name##_show, _name##_store)
|
|
||||||
+
|
|
||||||
+CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]);
|
|
||||||
+CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]);
|
|
||||||
+CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]);
|
|
||||||
+
|
|
||||||
+CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]);
|
|
||||||
+CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]);
|
|
||||||
+CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]);
|
|
||||||
+
|
|
||||||
+static struct attribute *ipv4_attrs[] = {
|
|
||||||
+ &tcp_wmem_min_attr.attr,
|
|
||||||
+ &tcp_wmem_def_attr.attr,
|
|
||||||
+ &tcp_wmem_max_attr.attr,
|
|
||||||
+ &tcp_rmem_min_attr.attr,
|
|
||||||
+ &tcp_rmem_def_attr.attr,
|
|
||||||
+ &tcp_rmem_max_attr.attr,
|
|
||||||
+ NULL
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct attribute_group ipv4_attr_group = {
|
|
||||||
+ .attrs = ipv4_attrs,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static __init int sysfs_ipv4_init(void)
|
|
||||||
+{
|
|
||||||
+ struct kobject *ipv4_kobject;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj);
|
|
||||||
+ if (!ipv4_kobject)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group);
|
|
||||||
+ if (ret) {
|
|
||||||
+ kobject_put(ipv4_kobject);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+subsys_initcall(sysfs_ipv4_init);
|
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@
|
|||||||
From 0d1077627288e51b5604f270eae6cf9f5ccd3871 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Fri, 17 Oct 2008 15:20:55 -0700
|
|
||||||
Subject: [PATCH 087/134] Input: Hold wake lock while event queue is not empty.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Allows userspace code to process input events while
|
|
||||||
the device appears to be asleep.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/input/evdev.c | 7 +++++++
|
|
||||||
1 files changed, 7 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/input/evdev.c
|
|
||||||
+++ b/drivers/input/evdev.c
|
|
||||||
@@ -19,6 +19,7 @@
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <linux/major.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
+#include <linux/wakelock.h>
|
|
||||||
#include "input-compat.h"
|
|
||||||
|
|
||||||
struct evdev {
|
|
||||||
@@ -43,6 +44,7 @@ struct evdev_client {
|
|
||||||
struct fasync_struct *fasync;
|
|
||||||
struct evdev *evdev;
|
|
||||||
struct list_head node;
|
|
||||||
+ struct wake_lock wake_lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct evdev *evdev_table[EVDEV_MINORS];
|
|
||||||
@@ -55,6 +57,7 @@ static void evdev_pass_event(struct evde
|
|
||||||
* Interrupts are disabled, just acquire the lock
|
|
||||||
*/
|
|
||||||
spin_lock(&client->buffer_lock);
|
|
||||||
+ wake_lock_timeout(&client->wake_lock, 5 * HZ);
|
|
||||||
client->buffer[client->head++] = *event;
|
|
||||||
client->head &= EVDEV_BUFFER_SIZE - 1;
|
|
||||||
spin_unlock(&client->buffer_lock);
|
|
||||||
@@ -233,6 +236,7 @@ static int evdev_release(struct inode *i
|
|
||||||
mutex_unlock(&evdev->mutex);
|
|
||||||
|
|
||||||
evdev_detach_client(evdev, client);
|
|
||||||
+ wake_lock_destroy(&client->wake_lock);
|
|
||||||
kfree(client);
|
|
||||||
|
|
||||||
evdev_close_device(evdev);
|
|
||||||
@@ -269,6 +273,7 @@ static int evdev_open(struct inode *inod
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_init(&client->buffer_lock);
|
|
||||||
+ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, "evdev");
|
|
||||||
client->evdev = evdev;
|
|
||||||
evdev_attach_client(evdev, client);
|
|
||||||
|
|
||||||
@@ -332,6 +337,8 @@ static int evdev_fetch_next_event(struct
|
|
||||||
if (have_event) {
|
|
||||||
*event = client->buffer[client->tail++];
|
|
||||||
client->tail &= EVDEV_BUFFER_SIZE - 1;
|
|
||||||
+ if (client->head == client->tail)
|
|
||||||
+ wake_unlock(&client->wake_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irq(&client->buffer_lock);
|
|
@ -1,31 +0,0 @@
|
|||||||
From 474db0e1425f8da03a7d49ebba1602efa503c73f Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Fri, 17 Oct 2008 15:28:08 -0700
|
|
||||||
Subject: [PATCH 088/134] Input: Use monotonic time for event time stamps.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Since wall time can jump backwards, it cannot be used to determine if one
|
|
||||||
event occured before another or for how long a key was pressed.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/input/evdev.c | 5 ++++-
|
|
||||||
1 files changed, 4 insertions(+), 1 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/input/evdev.c
|
|
||||||
+++ b/drivers/input/evdev.c
|
|
||||||
@@ -74,8 +74,11 @@ static void evdev_event(struct input_han
|
|
||||||
struct evdev *evdev = handle->private;
|
|
||||||
struct evdev_client *client;
|
|
||||||
struct input_event event;
|
|
||||||
+ struct timespec ts;
|
|
||||||
|
|
||||||
- do_gettimeofday(&event.time);
|
|
||||||
+ ktime_get_ts(&ts);
|
|
||||||
+ event.time.tv_sec = ts.tv_sec;
|
|
||||||
+ event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
|
|
||||||
event.type = type;
|
|
||||||
event.code = code;
|
|
||||||
event.value = value;
|
|
@ -1,313 +0,0 @@
|
|||||||
From eb92150c047e76960ecf8989b9037fd34b43a4af Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Fri, 21 Nov 2008 21:47:23 -0800
|
|
||||||
Subject: [PATCH 090/134] input: Add keyreset driver.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Add a platform device in the board file to specify a reset key-combo.
|
|
||||||
The first time the key-combo is detected a work function that syncs
|
|
||||||
the filesystems is scheduled. If all the keys are released and then
|
|
||||||
pressed again, it calls panic. Reboot on panic should be set for
|
|
||||||
this to work.
|
|
||||||
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/input/Kconfig | 9 ++
|
|
||||||
drivers/input/Makefile | 1 +
|
|
||||||
drivers/input/keyreset.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
include/linux/keyreset.h | 27 ++++++
|
|
||||||
4 files changed, 266 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/input/keyreset.c
|
|
||||||
create mode 100644 include/linux/keyreset.h
|
|
||||||
|
|
||||||
--- a/drivers/input/Kconfig
|
|
||||||
+++ b/drivers/input/Kconfig
|
|
||||||
@@ -149,6 +149,15 @@ config INPUT_APMPOWER
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called apm-power.
|
|
||||||
|
|
||||||
+config INPUT_KEYRESET
|
|
||||||
+ tristate "Reset key"
|
|
||||||
+ depends on INPUT
|
|
||||||
+ ---help---
|
|
||||||
+ Say Y here if you want to reboot when some keys are pressed;
|
|
||||||
+
|
|
||||||
+ To compile this driver as a module, choose M here: the
|
|
||||||
+ module will be called keyreset.
|
|
||||||
+
|
|
||||||
config XEN_KBDDEV_FRONTEND
|
|
||||||
tristate "Xen virtual keyboard and mouse support"
|
|
||||||
depends on XEN_FBDEV_FRONTEND
|
|
||||||
--- a/drivers/input/Makefile
|
|
||||||
+++ b/drivers/input/Makefile
|
|
||||||
@@ -23,5 +23,6 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touch
|
|
||||||
obj-$(CONFIG_INPUT_MISC) += misc/
|
|
||||||
|
|
||||||
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
|
|
||||||
+obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/input/keyreset.c
|
|
||||||
@@ -0,0 +1,229 @@
|
|
||||||
+/* drivers/input/keyreset.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/input.h>
|
|
||||||
+#include <linux/keyreset.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/reboot.h>
|
|
||||||
+#include <linux/sched.h>
|
|
||||||
+#include <linux/syscalls.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct keyreset_state {
|
|
||||||
+ struct input_handler input_handler;
|
|
||||||
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
|
|
||||||
+ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)];
|
|
||||||
+ unsigned long key[BITS_TO_LONGS(KEY_CNT)];
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+ int key_down_target;
|
|
||||||
+ int key_down;
|
|
||||||
+ int key_up;
|
|
||||||
+ int restart_disabled;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+int restart_requested;
|
|
||||||
+static void deferred_restart(struct work_struct *dummy)
|
|
||||||
+{
|
|
||||||
+ restart_requested = 2;
|
|
||||||
+ sys_sync();
|
|
||||||
+ restart_requested = 3;
|
|
||||||
+ kernel_restart(NULL);
|
|
||||||
+}
|
|
||||||
+static DECLARE_WORK(restart_work, deferred_restart);
|
|
||||||
+
|
|
||||||
+static void keyreset_event(struct input_handle *handle, unsigned int type,
|
|
||||||
+ unsigned int code, int value)
|
|
||||||
+{
|
|
||||||
+ unsigned long flags;
|
|
||||||
+ struct keyreset_state *state = handle->private;
|
|
||||||
+
|
|
||||||
+ if (type != EV_KEY)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (code >= KEY_MAX)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (!test_bit(code, state->keybit))
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&state->lock, flags);
|
|
||||||
+ if (!test_bit(code, state->key) == !value)
|
|
||||||
+ goto done;
|
|
||||||
+ __change_bit(code, state->key);
|
|
||||||
+ if (test_bit(code, state->upbit)) {
|
|
||||||
+ if (value) {
|
|
||||||
+ state->restart_disabled = 1;
|
|
||||||
+ state->key_up++;
|
|
||||||
+ } else
|
|
||||||
+ state->key_up--;
|
|
||||||
+ } else {
|
|
||||||
+ if (value)
|
|
||||||
+ state->key_down++;
|
|
||||||
+ else
|
|
||||||
+ state->key_down--;
|
|
||||||
+ }
|
|
||||||
+ if (state->key_down == 0 && state->key_up == 0)
|
|
||||||
+ state->restart_disabled = 0;
|
|
||||||
+
|
|
||||||
+ pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value,
|
|
||||||
+ state->key_down, state->key_up, state->restart_disabled);
|
|
||||||
+
|
|
||||||
+ if (value && !state->restart_disabled &&
|
|
||||||
+ state->key_down == state->key_down_target) {
|
|
||||||
+ state->restart_disabled = 1;
|
|
||||||
+ if (restart_requested)
|
|
||||||
+ panic("keyboard reset failed, %d", restart_requested);
|
|
||||||
+ pr_info("keyboard reset\n");
|
|
||||||
+ schedule_work(&restart_work);
|
|
||||||
+ restart_requested = 1;
|
|
||||||
+ }
|
|
||||||
+done:
|
|
||||||
+ spin_unlock_irqrestore(&state->lock, flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int keyreset_connect(struct input_handler *handler,
|
|
||||||
+ struct input_dev *dev,
|
|
||||||
+ const struct input_device_id *id)
|
|
||||||
+{
|
|
||||||
+ int i;
|
|
||||||
+ int ret;
|
|
||||||
+ struct input_handle *handle;
|
|
||||||
+ struct keyreset_state *state =
|
|
||||||
+ container_of(handler, struct keyreset_state, input_handler);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < KEY_MAX; i++) {
|
|
||||||
+ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit))
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ if (i == KEY_MAX)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
|
|
||||||
+ if (!handle)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ handle->dev = dev;
|
|
||||||
+ handle->handler = handler;
|
|
||||||
+ handle->name = "keyreset";
|
|
||||||
+ handle->private = state;
|
|
||||||
+
|
|
||||||
+ ret = input_register_handle(handle);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_input_register_handle;
|
|
||||||
+
|
|
||||||
+ ret = input_open_device(handle);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_input_open_device;
|
|
||||||
+
|
|
||||||
+ pr_info("using input dev %s for key reset\n", dev->name);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_input_open_device:
|
|
||||||
+ input_unregister_handle(handle);
|
|
||||||
+err_input_register_handle:
|
|
||||||
+ kfree(handle);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void keyreset_disconnect(struct input_handle *handle)
|
|
||||||
+{
|
|
||||||
+ input_close_device(handle);
|
|
||||||
+ input_unregister_handle(handle);
|
|
||||||
+ kfree(handle);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct input_device_id keyreset_ids[] = {
|
|
||||||
+ {
|
|
||||||
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
|
|
||||||
+ .evbit = { BIT_MASK(EV_KEY) },
|
|
||||||
+ },
|
|
||||||
+ { },
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(input, keyreset_ids);
|
|
||||||
+
|
|
||||||
+static int keyreset_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ int key, *keyp;
|
|
||||||
+ struct keyreset_state *state;
|
|
||||||
+ struct keyreset_platform_data *pdata = pdev->dev.platform_data;
|
|
||||||
+
|
|
||||||
+ if (!pdata)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
||||||
+ if (!state)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ spin_lock_init(&state->lock);
|
|
||||||
+ keyp = pdata->keys_down;
|
|
||||||
+ while ((key = *keyp++)) {
|
|
||||||
+ if (key >= KEY_MAX)
|
|
||||||
+ continue;
|
|
||||||
+ state->key_down_target++;
|
|
||||||
+ __set_bit(key, state->keybit);
|
|
||||||
+ }
|
|
||||||
+ if (pdata->keys_up) {
|
|
||||||
+ keyp = pdata->keys_up;
|
|
||||||
+ while ((key = *keyp++)) {
|
|
||||||
+ if (key >= KEY_MAX)
|
|
||||||
+ continue;
|
|
||||||
+ __set_bit(key, state->keybit);
|
|
||||||
+ __set_bit(key, state->upbit);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ state->input_handler.event = keyreset_event;
|
|
||||||
+ state->input_handler.connect = keyreset_connect;
|
|
||||||
+ state->input_handler.disconnect = keyreset_disconnect;
|
|
||||||
+ state->input_handler.name = KEYRESET_NAME;
|
|
||||||
+ state->input_handler.id_table = keyreset_ids;
|
|
||||||
+ ret = input_register_handler(&state->input_handler);
|
|
||||||
+ if (ret) {
|
|
||||||
+ kfree(state);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+ platform_set_drvdata(pdev, state);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+int keyreset_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct keyreset_state *state = platform_get_drvdata(pdev);
|
|
||||||
+ input_unregister_handler(&state->input_handler);
|
|
||||||
+ kfree(state);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct platform_driver keyreset_driver = {
|
|
||||||
+ .driver.name = KEYRESET_NAME,
|
|
||||||
+ .probe = keyreset_probe,
|
|
||||||
+ .remove = keyreset_remove,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init keyreset_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&keyreset_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit keyreset_exit(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_unregister(&keyreset_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(keyreset_init);
|
|
||||||
+module_exit(keyreset_exit);
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/linux/keyreset.h
|
|
||||||
@@ -0,0 +1,27 @@
|
|
||||||
+/*
|
|
||||||
+ * include/linux/keyreset.h - platform data structure for resetkeys driver
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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.
|
|
||||||
+ *
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef _LINUX_KEYRESET_H
|
|
||||||
+#define _LINUX_KEYRESET_H
|
|
||||||
+
|
|
||||||
+#define KEYRESET_NAME "keyreset"
|
|
||||||
+
|
|
||||||
+struct keyreset_platform_data {
|
|
||||||
+ int *keys_up;
|
|
||||||
+ int keys_down[]; /* 0 terminated */
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#endif /* _LINUX_KEYRESET_H */
|
|
@ -1,21 +0,0 @@
|
|||||||
From 2cae40a6a76e9e440f146ce0402aed40a7c127ca Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@google.com>
|
|
||||||
Date: Mon, 10 Nov 2008 16:29:50 -0800
|
|
||||||
Subject: [PATCH 093/134] mmc: Add status IRQ and status callback function to mmc platform data
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@google.com>
|
|
||||||
---
|
|
||||||
arch/arm/include/asm/mach/mmc.h | 2 ++
|
|
||||||
1 files changed, 2 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/arch/arm/include/asm/mach/mmc.h
|
|
||||||
+++ b/arch/arm/include/asm/mach/mmc.h
|
|
||||||
@@ -10,6 +10,8 @@ struct mmc_platform_data {
|
|
||||||
unsigned int ocr_mask; /* available voltages */
|
|
||||||
u32 (*translate_vdd)(struct device *, unsigned int);
|
|
||||||
unsigned int (*status)(struct device *);
|
|
||||||
+ unsigned int status_irq;
|
|
||||||
+ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||||||
From 66983857a54479d76ce6dbd8399e1276698bd3c5 Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@google.com>
|
|
||||||
Date: Tue, 11 Nov 2008 09:35:36 -0800
|
|
||||||
Subject: [PATCH 094/134] mmc: sd: Add new CONFIG_MMC_PARANOID_SD_INIT for enabling retries during SD detection
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@google.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/core/Kconfig | 8 ++++++++
|
|
||||||
drivers/mmc/core/sd.c | 24 ++++++++++++++++++++++--
|
|
||||||
2 files changed, 30 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/mmc/core/Kconfig
|
|
||||||
+++ b/drivers/mmc/core/Kconfig
|
|
||||||
@@ -14,3 +14,11 @@ config MMC_UNSAFE_RESUME
|
|
||||||
This option is usually just for embedded systems which use
|
|
||||||
a MMC/SD card for rootfs. Most people should say N here.
|
|
||||||
|
|
||||||
+config MMC_PARANOID_SD_INIT
|
|
||||||
+ bool "Enable paranoid SD card initialization (EXPERIMENTAL)"
|
|
||||||
+ help
|
|
||||||
+ If you say Y here, the MMC layer will be extra paranoid
|
|
||||||
+ about re-trying SD init requests. This can be a useful
|
|
||||||
+ work-around for buggy controllers and hardware. Enable
|
|
||||||
+ if you are experiencing issues with SD detection.
|
|
||||||
+
|
|
||||||
--- a/drivers/mmc/core/sd.c
|
|
||||||
+++ b/drivers/mmc/core/sd.c
|
|
||||||
@@ -336,7 +336,9 @@ static int mmc_sd_init_card(struct mmc_h
|
|
||||||
int err;
|
|
||||||
u32 cid[4];
|
|
||||||
unsigned int max_dtr;
|
|
||||||
-
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ int retries;
|
|
||||||
+#endif
|
|
||||||
BUG_ON(!host);
|
|
||||||
WARN_ON(!host->claimed);
|
|
||||||
|
|
||||||
@@ -439,11 +441,29 @@ static int mmc_sd_init_card(struct mmc_h
|
|
||||||
err = mmc_decode_scr(card);
|
|
||||||
if (err < 0)
|
|
||||||
goto free_card;
|
|
||||||
-
|
|
||||||
/*
|
|
||||||
* Fetch switch information from card.
|
|
||||||
*/
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ for (retries = 1; retries <= 3; retries++) {
|
|
||||||
+ err = mmc_read_switch(card);
|
|
||||||
+ if (!err) {
|
|
||||||
+ if (retries > 1) {
|
|
||||||
+ printk(KERN_WARNING
|
|
||||||
+ "%s: recovered\n",
|
|
||||||
+ mmc_hostname(host));
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ } else {
|
|
||||||
+ printk(KERN_WARNING
|
|
||||||
+ "%s: read switch failed (attempt %d)\n",
|
|
||||||
+ mmc_hostname(host), retries);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+#else
|
|
||||||
err = mmc_read_switch(card);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
if (err)
|
|
||||||
goto free_card;
|
|
||||||
}
|
|
@ -1,274 +0,0 @@
|
|||||||
From 62d51f0d0d0b3c758b4505c3855b61288b8e90aa Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@android.com>
|
|
||||||
Date: Mon, 14 Apr 2008 15:22:49 -0700
|
|
||||||
Subject: [PATCH 095/134] mmc: Add concept of an 'embedded' SDIO device.
|
|
||||||
|
|
||||||
This is required to support chips which use SDIO for signaling/
|
|
||||||
communication but do not implement the various card enumeration registers
|
|
||||||
as required for full SD / SDIO cards.
|
|
||||||
|
|
||||||
mmc: sdio: Fix bug where we're freeing the CIS tables we never allocated when using EMBEDDED_SDIO
|
|
||||||
mmc: Add max_blksize to embedded SDIO data
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@google.com>
|
|
||||||
---
|
|
||||||
arch/arm/include/asm/mach/mmc.h | 10 ++++++
|
|
||||||
drivers/mmc/core/Kconfig | 10 +++++-
|
|
||||||
drivers/mmc/core/core.c | 16 +++++++++
|
|
||||||
drivers/mmc/core/sdio.c | 67 ++++++++++++++++++++++++++++++++-------
|
|
||||||
drivers/mmc/core/sdio_bus.c | 13 +++++++-
|
|
||||||
include/linux/mmc/host.h | 17 ++++++++++
|
|
||||||
include/linux/mmc/sdio_func.h | 8 +++++
|
|
||||||
7 files changed, 127 insertions(+), 14 deletions(-)
|
|
||||||
|
|
||||||
--- a/arch/arm/include/asm/mach/mmc.h
|
|
||||||
+++ b/arch/arm/include/asm/mach/mmc.h
|
|
||||||
@@ -5,12 +5,22 @@
|
|
||||||
#define ASMARM_MACH_MMC_H
|
|
||||||
|
|
||||||
#include <linux/mmc/host.h>
|
|
||||||
+#include <linux/mmc/card.h>
|
|
||||||
+#include <linux/mmc/sdio_func.h>
|
|
||||||
+
|
|
||||||
+struct embedded_sdio_data {
|
|
||||||
+ struct sdio_cis cis;
|
|
||||||
+ struct sdio_cccr cccr;
|
|
||||||
+ struct sdio_embedded_func *funcs;
|
|
||||||
+ int num_funcs;
|
|
||||||
+};
|
|
||||||
|
|
||||||
struct mmc_platform_data {
|
|
||||||
unsigned int ocr_mask; /* available voltages */
|
|
||||||
u32 (*translate_vdd)(struct device *, unsigned int);
|
|
||||||
unsigned int (*status)(struct device *);
|
|
||||||
unsigned int status_irq;
|
|
||||||
+ struct embedded_sdio_data *embedded_sdio;
|
|
||||||
int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
--- a/drivers/mmc/core/Kconfig
|
|
||||||
+++ b/drivers/mmc/core/Kconfig
|
|
||||||
@@ -14,11 +14,19 @@ config MMC_UNSAFE_RESUME
|
|
||||||
This option is usually just for embedded systems which use
|
|
||||||
a MMC/SD card for rootfs. Most people should say N here.
|
|
||||||
|
|
||||||
+config MMC_EMBEDDED_SDIO
|
|
||||||
+ boolean "MMC embedded SDIO device support (EXPERIMENTAL)"
|
|
||||||
+ depends on EXPERIMENTAL
|
|
||||||
+ help
|
|
||||||
+ If you say Y here, support will be added for embedded SDIO
|
|
||||||
+ devices which do not contain the necessary enumeration
|
|
||||||
+ support in hardware to be properly detected.
|
|
||||||
+
|
|
||||||
config MMC_PARANOID_SD_INIT
|
|
||||||
bool "Enable paranoid SD card initialization (EXPERIMENTAL)"
|
|
||||||
+ depends on EXPERIMENTAL
|
|
||||||
help
|
|
||||||
If you say Y here, the MMC layer will be extra paranoid
|
|
||||||
about re-trying SD init requests. This can be a useful
|
|
||||||
work-around for buggy controllers and hardware. Enable
|
|
||||||
if you are experiencing issues with SD detection.
|
|
||||||
-
|
|
||||||
--- a/drivers/mmc/core/core.c
|
|
||||||
+++ b/drivers/mmc/core/core.c
|
|
||||||
@@ -1011,6 +1011,22 @@ EXPORT_SYMBOL(mmc_resume_host);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+void mmc_set_embedded_sdio_data(struct mmc_host *host,
|
|
||||||
+ struct sdio_cis *cis,
|
|
||||||
+ struct sdio_cccr *cccr,
|
|
||||||
+ struct sdio_embedded_func *funcs,
|
|
||||||
+ int num_funcs)
|
|
||||||
+{
|
|
||||||
+ host->embedded_sdio_data.cis = cis;
|
|
||||||
+ host->embedded_sdio_data.cccr = cccr;
|
|
||||||
+ host->embedded_sdio_data.funcs = funcs;
|
|
||||||
+ host->embedded_sdio_data.num_funcs = num_funcs;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
static int __init mmc_init(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
--- a/drivers/mmc/core/sdio.c
|
|
||||||
+++ b/drivers/mmc/core/sdio.c
|
|
||||||
@@ -24,6 +24,10 @@
|
|
||||||
#include "sdio_ops.h"
|
|
||||||
#include "sdio_cis.h"
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+#include <linux/mmc/sdio_ids.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
static int sdio_read_fbr(struct sdio_func *func)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
@@ -314,6 +318,11 @@ int mmc_attach_sdio(struct mmc_host *hos
|
|
||||||
*/
|
|
||||||
funcs = (ocr & 0x70000000) >> 28;
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ if (host->embedded_sdio_data.funcs)
|
|
||||||
+ funcs = host->embedded_sdio_data.num_funcs;
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Allocate card structure.
|
|
||||||
*/
|
|
||||||
@@ -351,17 +360,33 @@ int mmc_attach_sdio(struct mmc_host *hos
|
|
||||||
/*
|
|
||||||
* Read the common registers.
|
|
||||||
*/
|
|
||||||
- err = sdio_read_cccr(card);
|
|
||||||
- if (err)
|
|
||||||
- goto remove;
|
|
||||||
|
|
||||||
- /*
|
|
||||||
- * Read the common CIS tuples.
|
|
||||||
- */
|
|
||||||
- err = sdio_read_common_cis(card);
|
|
||||||
- if (err)
|
|
||||||
- goto remove;
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ if (host->embedded_sdio_data.cccr)
|
|
||||||
+ memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr));
|
|
||||||
+ else {
|
|
||||||
+#endif
|
|
||||||
+ err = sdio_read_cccr(card);
|
|
||||||
+ if (err)
|
|
||||||
+ goto remove;
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ if (host->embedded_sdio_data.cis)
|
|
||||||
+ memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis));
|
|
||||||
+ else {
|
|
||||||
+#endif
|
|
||||||
+ /*
|
|
||||||
+ * Read the common CIS tuples.
|
|
||||||
+ */
|
|
||||||
+ err = sdio_read_common_cis(card);
|
|
||||||
+ if (err)
|
|
||||||
+ goto remove;
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
/*
|
|
||||||
* Switch to high-speed (if supported).
|
|
||||||
*/
|
|
||||||
@@ -395,9 +420,27 @@ int mmc_attach_sdio(struct mmc_host *hos
|
|
||||||
* Initialize (but don't add) all present functions.
|
|
||||||
*/
|
|
||||||
for (i = 0;i < funcs;i++) {
|
|
||||||
- err = sdio_init_func(host->card, i + 1);
|
|
||||||
- if (err)
|
|
||||||
- goto remove;
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ if (host->embedded_sdio_data.funcs) {
|
|
||||||
+ struct sdio_func *tmp;
|
|
||||||
+
|
|
||||||
+ tmp = sdio_alloc_func(host->card);
|
|
||||||
+ if (IS_ERR(tmp))
|
|
||||||
+ goto remove;
|
|
||||||
+ tmp->num = (i + 1);
|
|
||||||
+ card->sdio_func[i] = tmp;
|
|
||||||
+ tmp->class = host->embedded_sdio_data.funcs[i].f_class;
|
|
||||||
+ tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize;
|
|
||||||
+ tmp->vendor = card->cis.vendor;
|
|
||||||
+ tmp->device = card->cis.device;
|
|
||||||
+ } else {
|
|
||||||
+#endif
|
|
||||||
+ err = sdio_init_func(host->card, i + 1);
|
|
||||||
+ if (err)
|
|
||||||
+ goto remove;
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
mmc_release_host(host);
|
|
||||||
--- a/drivers/mmc/core/sdio_bus.c
|
|
||||||
+++ b/drivers/mmc/core/sdio_bus.c
|
|
||||||
@@ -20,6 +20,10 @@
|
|
||||||
#include "sdio_cis.h"
|
|
||||||
#include "sdio_bus.h"
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+#include <linux/mmc/host.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
|
|
||||||
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
|
|
||||||
|
|
||||||
@@ -202,7 +206,14 @@ static void sdio_release_func(struct dev
|
|
||||||
{
|
|
||||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
|
||||||
|
|
||||||
- sdio_free_func_cis(func);
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ /*
|
|
||||||
+ * If this device is embedded then we never allocated
|
|
||||||
+ * cis tables for this func
|
|
||||||
+ */
|
|
||||||
+ if (!func->card->host->embedded_sdio_data.funcs)
|
|
||||||
+#endif
|
|
||||||
+ sdio_free_func_cis(func);
|
|
||||||
|
|
||||||
if (func->info)
|
|
||||||
kfree(func->info);
|
|
||||||
--- a/include/linux/mmc/host.h
|
|
||||||
+++ b/include/linux/mmc/host.h
|
|
||||||
@@ -161,6 +161,15 @@ struct mmc_host {
|
|
||||||
|
|
||||||
struct dentry *debugfs_root;
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+ struct {
|
|
||||||
+ struct sdio_cis *cis;
|
|
||||||
+ struct sdio_cccr *cccr;
|
|
||||||
+ struct sdio_embedded_func *funcs;
|
|
||||||
+ int num_funcs;
|
|
||||||
+ } embedded_sdio_data;
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -169,6 +178,14 @@ extern int mmc_add_host(struct mmc_host
|
|
||||||
extern void mmc_remove_host(struct mmc_host *);
|
|
||||||
extern void mmc_free_host(struct mmc_host *);
|
|
||||||
|
|
||||||
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
|
|
||||||
+extern void mmc_set_embedded_sdio_data(struct mmc_host *host,
|
|
||||||
+ struct sdio_cis *cis,
|
|
||||||
+ struct sdio_cccr *cccr,
|
|
||||||
+ struct sdio_embedded_func *funcs,
|
|
||||||
+ int num_funcs);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
static inline void *mmc_priv(struct mmc_host *host)
|
|
||||||
{
|
|
||||||
return (void *)host->private;
|
|
||||||
--- a/include/linux/mmc/sdio_func.h
|
|
||||||
+++ b/include/linux/mmc/sdio_func.h
|
|
||||||
@@ -21,6 +21,14 @@ struct sdio_func;
|
|
||||||
typedef void (sdio_irq_handler_t)(struct sdio_func *);
|
|
||||||
|
|
||||||
/*
|
|
||||||
+ * Structure used to hold embedded SDIO device data from platform layer
|
|
||||||
+ */
|
|
||||||
+struct sdio_embedded_func {
|
|
||||||
+ uint8_t f_class;
|
|
||||||
+ uint32_t f_maxblksize;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
* SDIO function CIS tuple (unknown to the core)
|
|
||||||
*/
|
|
||||||
struct sdio_func_tuple {
|
|
@ -1,74 +0,0 @@
|
|||||||
From 0b9879757f15d4ac99e032647cb3ee26393031b0 Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@android.com>
|
|
||||||
Date: Thu, 15 May 2008 09:15:37 -0700
|
|
||||||
Subject: [PATCH 097/134] mmc: Add new API call 'sdio_reset_comm' for resetting communication with an SDIO device
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@android.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/core/sdio.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 files changed, 58 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/mmc/core/sdio.c
|
|
||||||
+++ b/drivers/mmc/core/sdio.c
|
|
||||||
@@ -482,3 +482,61 @@ err:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
+int sdio_reset_comm(struct mmc_card *card)
|
|
||||||
+{
|
|
||||||
+ struct mmc_host *host = card->host;
|
|
||||||
+ u32 ocr;
|
|
||||||
+ int err;
|
|
||||||
+
|
|
||||||
+ printk("%s():\n", __func__);
|
|
||||||
+ mmc_go_idle(host);
|
|
||||||
+
|
|
||||||
+ mmc_set_clock(host, host->f_min);
|
|
||||||
+
|
|
||||||
+ err = mmc_send_io_op_cond(host, 0, &ocr);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ host->ocr = mmc_select_voltage(host, ocr);
|
|
||||||
+ if (!host->ocr) {
|
|
||||||
+ err = -EINVAL;
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ err = mmc_send_io_op_cond(host, host->ocr, &ocr);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ if (mmc_host_is_spi(host)) {
|
|
||||||
+ err = mmc_spi_set_crc(host, use_spi_crc);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!mmc_host_is_spi(host)) {
|
|
||||||
+ err = mmc_send_relative_addr(host, &card->rca);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err;
|
|
||||||
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
|
||||||
+ }
|
|
||||||
+ if (!mmc_host_is_spi(host)) {
|
|
||||||
+ err = mmc_select_card(card);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ mmc_set_clock(host, card->cis.max_dtr);
|
|
||||||
+ err = sdio_enable_wide(card);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+ err:
|
|
||||||
+ printk("%s: Error resetting SDIO communications (%d)\n",
|
|
||||||
+ mmc_hostname(host), err);
|
|
||||||
+ mmc_release_host(host);
|
|
||||||
+ return err;
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(sdio_reset_comm);
|
|
||||||
+
|
|
||||||
+
|
|
@ -1,44 +0,0 @@
|
|||||||
From bd8df907ee8e95143d1bb7ec01ee0e4b6f7e1b79 Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@android.com>
|
|
||||||
Date: Mon, 1 Dec 2008 08:52:34 -0800
|
|
||||||
Subject: [PATCH 098/134] mmc: sd: When resuming, try a little harder to init the card
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@android.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/core/sd.c | 19 +++++++++++++++++++
|
|
||||||
1 files changed, 19 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/mmc/core/sd.c
|
|
||||||
+++ b/drivers/mmc/core/sd.c
|
|
||||||
@@ -607,12 +607,31 @@ static void mmc_sd_suspend(struct mmc_ho
|
|
||||||
static void mmc_sd_resume(struct mmc_host *host)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ int retries;
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
BUG_ON(!host);
|
|
||||||
BUG_ON(!host->card);
|
|
||||||
|
|
||||||
mmc_claim_host(host);
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ retries = 5;
|
|
||||||
+ while (retries) {
|
|
||||||
+ err = mmc_sd_init_card(host, host->ocr, host->card);
|
|
||||||
+
|
|
||||||
+ if (err) {
|
|
||||||
+ printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
|
|
||||||
+ mmc_hostname(host), err, retries);
|
|
||||||
+ mdelay(5);
|
|
||||||
+ retries--;
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+#else
|
|
||||||
err = mmc_sd_init_card(host, host->ocr, host->card);
|
|
||||||
+#endif
|
|
||||||
mmc_release_host(host);
|
|
||||||
|
|
||||||
if (err) {
|
|
@ -1,104 +0,0 @@
|
|||||||
From 5ca694dfd981a371e9b18cdd4a89c002ffaccbc5 Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@android.com>
|
|
||||||
Date: Wed, 3 Dec 2008 10:22:59 -0800
|
|
||||||
Subject: [PATCH 099/134] mmc: mmcblk: Add new feature 'CONFIG_MMC_BLOCK_PARANOID_RESUME'
|
|
||||||
|
|
||||||
With this feature enabled, mmcblk will check the card-status before issuing
|
|
||||||
a transaction *only* after being resumed. This protectes us from issuing
|
|
||||||
transactions before the sdcard is ready (which can occur if the host driver
|
|
||||||
deferrs mmc_resume_host() to reduce resume latency)
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@android.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/card/Kconfig | 7 +++++++
|
|
||||||
drivers/mmc/card/block.c | 3 +++
|
|
||||||
drivers/mmc/card/queue.c | 28 ++++++++++++++++++++++++++++
|
|
||||||
drivers/mmc/card/queue.h | 3 +++
|
|
||||||
4 files changed, 41 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/mmc/card/Kconfig
|
|
||||||
+++ b/drivers/mmc/card/Kconfig
|
|
||||||
@@ -32,6 +32,13 @@ config MMC_BLOCK_BOUNCE
|
|
||||||
|
|
||||||
If unsure, say Y here.
|
|
||||||
|
|
||||||
+config MMC_BLOCK_PARANOID_RESUME
|
|
||||||
+ bool "Check card status on resume"
|
|
||||||
+ depends on MMC_BLOCK
|
|
||||||
+ default y
|
|
||||||
+ help
|
|
||||||
+ Nohelp
|
|
||||||
+
|
|
||||||
config SDIO_UART
|
|
||||||
tristate "SDIO UART/GPS class support"
|
|
||||||
help
|
|
||||||
--- a/drivers/mmc/card/block.c
|
|
||||||
+++ b/drivers/mmc/card/block.c
|
|
||||||
@@ -643,6 +643,9 @@ static int mmc_blk_resume(struct mmc_car
|
|
||||||
|
|
||||||
if (md) {
|
|
||||||
mmc_blk_set_blksize(md, card);
|
|
||||||
+#ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME
|
|
||||||
+ md->queue.check_status = 1;
|
|
||||||
+#endif
|
|
||||||
mmc_queue_resume(&md->queue);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
--- a/drivers/mmc/card/queue.c
|
|
||||||
+++ b/drivers/mmc/card/queue.c
|
|
||||||
@@ -14,7 +14,9 @@
|
|
||||||
#include <linux/freezer.h>
|
|
||||||
#include <linux/kthread.h>
|
|
||||||
#include <linux/scatterlist.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
|
|
||||||
+#include <linux/mmc/mmc.h>
|
|
||||||
#include <linux/mmc/card.h>
|
|
||||||
#include <linux/mmc/host.h>
|
|
||||||
#include "queue.h"
|
|
||||||
@@ -70,7 +72,33 @@ static int mmc_queue_thread(void *d)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
+#ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME
|
|
||||||
+ if (mq->check_status) {
|
|
||||||
+ struct mmc_command cmd;
|
|
||||||
|
|
||||||
+ do {
|
|
||||||
+ int err;
|
|
||||||
+
|
|
||||||
+ cmd.opcode = MMC_SEND_STATUS;
|
|
||||||
+ cmd.arg = mq->card->rca << 16;
|
|
||||||
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
||||||
+
|
|
||||||
+ mmc_claim_host(mq->card->host);
|
|
||||||
+ err = mmc_wait_for_cmd(mq->card->host, &cmd, 5);
|
|
||||||
+ mmc_release_host(mq->card->host);
|
|
||||||
+
|
|
||||||
+ if (err) {
|
|
||||||
+ printk(KERN_ERR "%s: failed to get status (%d)\n",
|
|
||||||
+ __func__, err);
|
|
||||||
+ msleep(5);
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ printk(KERN_DEBUG "%s: status 0x%.8x\n", __func__, cmd.resp[0]);
|
|
||||||
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
|
|
||||||
+ (R1_CURRENT_STATE(cmd.resp[0]) == 7));
|
|
||||||
+ mq->check_status = 0;
|
|
||||||
+ }
|
|
||||||
+#endif
|
|
||||||
mq->issue_fn(mq, req);
|
|
||||||
} while (1);
|
|
||||||
up(&mq->thread_sem);
|
|
||||||
--- a/drivers/mmc/card/queue.h
|
|
||||||
+++ b/drivers/mmc/card/queue.h
|
|
||||||
@@ -17,6 +17,9 @@ struct mmc_queue {
|
|
||||||
char *bounce_buf;
|
|
||||||
struct scatterlist *bounce_sg;
|
|
||||||
unsigned int bounce_sg_len;
|
|
||||||
+#ifdef CONFIG_MMC_BLOCK_PARANOID_RESUME
|
|
||||||
+ int check_status;
|
|
||||||
+#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
|
|
@ -1,91 +0,0 @@
|
|||||||
From 39e3d37b1f7194277b8a3ea3536a67ec2d24491e Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@android.com>
|
|
||||||
Date: Thu, 4 Dec 2008 11:18:00 -0800
|
|
||||||
Subject: [PATCH 100/134] mmc: sd: Add retries in re-detection
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@android.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/core/sd.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
|
|
||||||
1 files changed, 43 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/mmc/core/sd.c
|
|
||||||
+++ b/drivers/mmc/core/sd.c
|
|
||||||
@@ -558,18 +558,37 @@ static void mmc_sd_remove(struct mmc_hos
|
|
||||||
*/
|
|
||||||
static void mmc_sd_detect(struct mmc_host *host)
|
|
||||||
{
|
|
||||||
- int err;
|
|
||||||
+ int err = 0;
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ int retries = 5;
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
BUG_ON(!host);
|
|
||||||
BUG_ON(!host->card);
|
|
||||||
-
|
|
||||||
+
|
|
||||||
mmc_claim_host(host);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Just check if our card has been removed.
|
|
||||||
*/
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ while(retries) {
|
|
||||||
+ err = mmc_send_status(host->card, NULL);
|
|
||||||
+ printk("%s(%s): err = %d\n", __func__, mmc_hostname(host), err);
|
|
||||||
+ if (err) {
|
|
||||||
+ retries--;
|
|
||||||
+ udelay(5);
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ if (!retries) {
|
|
||||||
+ printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",
|
|
||||||
+ __func__, mmc_hostname(host), err);
|
|
||||||
+ }
|
|
||||||
+#else
|
|
||||||
err = mmc_send_status(host->card, NULL);
|
|
||||||
-
|
|
||||||
+#endif
|
|
||||||
mmc_release_host(host);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
@@ -664,6 +683,9 @@ static const struct mmc_bus_ops mmc_sd_o
|
|
||||||
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ int retries;
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
BUG_ON(!host);
|
|
||||||
WARN_ON(!host->claimed);
|
|
||||||
@@ -712,9 +734,27 @@ int mmc_attach_sd(struct mmc_host *host,
|
|
||||||
/*
|
|
||||||
* Detect and init the card.
|
|
||||||
*/
|
|
||||||
+#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
+ retries = 5;
|
|
||||||
+ while (retries) {
|
|
||||||
+ err = mmc_sd_init_card(host, host->ocr, NULL);
|
|
||||||
+ if (err) {
|
|
||||||
+ retries--;
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!retries) {
|
|
||||||
+ printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n",
|
|
||||||
+ mmc_hostname(host), err);
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+#else
|
|
||||||
err = mmc_sd_init_card(host, host->ocr, NULL);
|
|
||||||
if (err)
|
|
||||||
goto err;
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
mmc_release_host(host);
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
|||||||
From 4d3a01ba872f67bae704e37bf888c38fa83be759 Mon Sep 17 00:00:00 2001
|
|
||||||
From: San Mehat <san@android.com>
|
|
||||||
Date: Sat, 21 Mar 2009 18:48:05 -0700
|
|
||||||
Subject: [PATCH 105/134] block: block_dump: Add number of sectors to debug output
|
|
||||||
|
|
||||||
Signed-off-by: San Mehat <san@android.com>
|
|
||||||
---
|
|
||||||
block/blk-core.c | 5 +++--
|
|
||||||
1 files changed, 3 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
--- a/block/blk-core.c
|
|
||||||
+++ b/block/blk-core.c
|
|
||||||
@@ -1558,11 +1558,12 @@ void submit_bio(int rw, struct bio *bio)
|
|
||||||
|
|
||||||
if (unlikely(block_dump)) {
|
|
||||||
char b[BDEVNAME_SIZE];
|
|
||||||
- printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",
|
|
||||||
+ printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)\n",
|
|
||||||
current->comm, task_pid_nr(current),
|
|
||||||
(rw & WRITE) ? "WRITE" : "READ",
|
|
||||||
(unsigned long long)bio->bi_sector,
|
|
||||||
- bdevname(bio->bi_bdev, b));
|
|
||||||
+ bdevname(bio->bi_bdev, b),
|
|
||||||
+ count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
From 700cfcab7eee4a822be3fe0c8cfd77caac10bfed Mon Sep 17 00:00:00 2001
|
|
||||||
From: Dima Zavin <dima@android.com>
|
|
||||||
Date: Wed, 25 Mar 2009 21:29:45 -0700
|
|
||||||
Subject: [PATCH 108/134] mmc: sd: Remove debugging printk
|
|
||||||
|
|
||||||
Signed-off-by: Dima Zavin <dima@android.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/core/sd.c | 1 -
|
|
||||||
1 files changed, 0 insertions(+), 1 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/mmc/core/sd.c
|
|
||||||
+++ b/drivers/mmc/core/sd.c
|
|
||||||
@@ -574,7 +574,6 @@ static void mmc_sd_detect(struct mmc_hos
|
|
||||||
#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
|
||||||
while(retries) {
|
|
||||||
err = mmc_send_status(host->card, NULL);
|
|
||||||
- printk("%s(%s): err = %d\n", __func__, mmc_hostname(host), err);
|
|
||||||
if (err) {
|
|
||||||
retries--;
|
|
||||||
udelay(5);
|
|
@ -1,58 +0,0 @@
|
|||||||
From fca2519ebf25db61da132932eb3fffeaa1974f43 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Thu, 15 Jan 2009 19:07:27 -0800
|
|
||||||
Subject: [PATCH 109/134] Revert "printk: remove unused code from kernel/printk.c"
|
|
||||||
|
|
||||||
This reverts commit acff181d3574244e651913df77332e897b88bff4.
|
|
||||||
---
|
|
||||||
kernel/printk.c | 39 +++++++++++++++++++++++++++++++++++++++
|
|
||||||
1 files changed, 39 insertions(+), 0 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/printk.c
|
|
||||||
+++ b/kernel/printk.c
|
|
||||||
@@ -255,6 +255,45 @@ static inline void boot_delay_msec(void)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
+ * Return the number of unread characters in the log buffer.
|
|
||||||
+ */
|
|
||||||
+static int log_buf_get_len(void)
|
|
||||||
+{
|
|
||||||
+ return logged_chars;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Copy a range of characters from the log buffer.
|
|
||||||
+ */
|
|
||||||
+int log_buf_copy(char *dest, int idx, int len)
|
|
||||||
+{
|
|
||||||
+ int ret, max;
|
|
||||||
+ bool took_lock = false;
|
|
||||||
+
|
|
||||||
+ if (!oops_in_progress) {
|
|
||||||
+ spin_lock_irq(&logbuf_lock);
|
|
||||||
+ took_lock = true;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ max = log_buf_get_len();
|
|
||||||
+ if (idx < 0 || idx >= max) {
|
|
||||||
+ ret = -1;
|
|
||||||
+ } else {
|
|
||||||
+ if (len > max)
|
|
||||||
+ len = max;
|
|
||||||
+ ret = len;
|
|
||||||
+ idx += (log_end - max);
|
|
||||||
+ while (len-- > 0)
|
|
||||||
+ dest[len] = LOG_BUF(idx + len);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (took_lock)
|
|
||||||
+ spin_unlock_irq(&logbuf_lock);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
* Commands to do_syslog:
|
|
||||||
*
|
|
||||||
* 0 -- Close the log. Currently a NOP.
|
|
@ -1,24 +0,0 @@
|
|||||||
From e500cd364086cfc018b100d08c97b2fdf229d029 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Mon, 14 Apr 2008 21:35:25 -0700
|
|
||||||
Subject: [PATCH 110/134] printk: Fix log_buf_copy termination.
|
|
||||||
|
|
||||||
If idx was non-zero and the log had wrapped, len did not get truncated
|
|
||||||
to stop at the last byte written to the log.
|
|
||||||
---
|
|
||||||
kernel/printk.c | 4 ++--
|
|
||||||
1 files changed, 2 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
--- a/kernel/printk.c
|
|
||||||
+++ b/kernel/printk.c
|
|
||||||
@@ -279,8 +279,8 @@ int log_buf_copy(char *dest, int idx, in
|
|
||||||
if (idx < 0 || idx >= max) {
|
|
||||||
ret = -1;
|
|
||||||
} else {
|
|
||||||
- if (len > max)
|
|
||||||
- len = max;
|
|
||||||
+ if (len > max - idx)
|
|
||||||
+ len = max - idx;
|
|
||||||
ret = len;
|
|
||||||
idx += (log_end - max);
|
|
||||||
while (len-- > 0)
|
|
File diff suppressed because it is too large
Load Diff
@ -1,404 +0,0 @@
|
|||||||
From aefe5257bf3ed9bf950ad962a63e5315566c2f97 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mike Lockwood <lockwood@google.com>
|
|
||||||
Date: Mon, 23 Jul 2007 09:31:54 -0400
|
|
||||||
Subject: [PATCH 120/134] [ARM] goldfish: Add audio driver for goldfish.
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
---
|
|
||||||
arch/arm/mach-goldfish/Makefile | 2 +-
|
|
||||||
arch/arm/mach-goldfish/audio.c | 379 +++++++++++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 380 insertions(+), 1 deletions(-)
|
|
||||||
create mode 100644 arch/arm/mach-goldfish/audio.c
|
|
||||||
|
|
||||||
--- a/arch/arm/mach-goldfish/Makefile
|
|
||||||
+++ b/arch/arm/mach-goldfish/Makefile
|
|
||||||
@@ -4,6 +4,6 @@
|
|
||||||
|
|
||||||
# Object file lists.
|
|
||||||
|
|
||||||
-obj-y := pdev_bus.o timer.o
|
|
||||||
+obj-y := pdev_bus.o timer.o audio.o
|
|
||||||
obj-$(CONFIG_MACH_GOLDFISH) += board-goldfish.o
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/arch/arm/mach-goldfish/audio.c
|
|
||||||
@@ -0,0 +1,379 @@
|
|
||||||
+/* arch/arm/mach-goldfish/audio.c
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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/miscdevice.h>
|
|
||||||
+#include <linux/fs.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+#include <linux/pci.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+
|
|
||||||
+#include <asm/types.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+#include <asm/uaccess.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Google, Inc.");
|
|
||||||
+MODULE_DESCRIPTION("Android QEMU Audio Driver");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
+MODULE_VERSION("1.0");
|
|
||||||
+
|
|
||||||
+struct goldfish_audio {
|
|
||||||
+ uint32_t reg_base;
|
|
||||||
+ int irq;
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+ wait_queue_head_t wait;
|
|
||||||
+
|
|
||||||
+ char __iomem *buffer_virt; /* combined buffer virtual address */
|
|
||||||
+ unsigned long buffer_phys; /* combined buffer physical address */
|
|
||||||
+
|
|
||||||
+ char __iomem *write_buffer1; /* write buffer 1 virtual address */
|
|
||||||
+ char __iomem *write_buffer2; /* write buffer 2 virtual address */
|
|
||||||
+ char __iomem *read_buffer; /* read buffer virtual address */
|
|
||||||
+ int buffer_status;
|
|
||||||
+ int read_supported; /* true if we have audio input support */
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/* We will allocate two read buffers and two write buffers.
|
|
||||||
+ Having two read buffers facilitate stereo -> mono conversion.
|
|
||||||
+ Having two write buffers facilitate interleaved IO.
|
|
||||||
+*/
|
|
||||||
+#define READ_BUFFER_SIZE 16384
|
|
||||||
+#define WRITE_BUFFER_SIZE 16384
|
|
||||||
+#define COMBINED_BUFFER_SIZE ((2 * READ_BUFFER_SIZE) + (2 * WRITE_BUFFER_SIZE))
|
|
||||||
+
|
|
||||||
+#define GOLDFISH_AUDIO_READ(data, addr) (readl(data->reg_base + addr))
|
|
||||||
+#define GOLDFISH_AUDIO_WRITE(data, addr, x) (writel(x, data->reg_base + addr))
|
|
||||||
+
|
|
||||||
+/* temporary variable used between goldfish_audio_probe() and goldfish_audio_open() */
|
|
||||||
+static struct goldfish_audio* audio_data;
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ /* audio status register */
|
|
||||||
+ AUDIO_INT_STATUS = 0x00,
|
|
||||||
+ /* set this to enable IRQ */
|
|
||||||
+ AUDIO_INT_ENABLE = 0x04,
|
|
||||||
+ /* set these to specify buffer addresses */
|
|
||||||
+ AUDIO_SET_WRITE_BUFFER_1 = 0x08,
|
|
||||||
+ AUDIO_SET_WRITE_BUFFER_2 = 0x0C,
|
|
||||||
+ /* set number of bytes in buffer to write */
|
|
||||||
+ AUDIO_WRITE_BUFFER_1 = 0x10,
|
|
||||||
+ AUDIO_WRITE_BUFFER_2 = 0x14,
|
|
||||||
+
|
|
||||||
+ /* true if audio input is supported */
|
|
||||||
+ AUDIO_READ_SUPPORTED = 0x18,
|
|
||||||
+ /* buffer to use for audio input */
|
|
||||||
+ AUDIO_SET_READ_BUFFER = 0x1C,
|
|
||||||
+
|
|
||||||
+ /* driver writes number of bytes to read */
|
|
||||||
+ AUDIO_START_READ = 0x20,
|
|
||||||
+
|
|
||||||
+ /* number of bytes available in read buffer */
|
|
||||||
+ AUDIO_READ_BUFFER_AVAILABLE = 0x24,
|
|
||||||
+
|
|
||||||
+ /* AUDIO_INT_STATUS bits */
|
|
||||||
+
|
|
||||||
+ /* this bit set when it is safe to write more bytes to the buffer */
|
|
||||||
+ AUDIO_INT_WRITE_BUFFER_1_EMPTY = 1U << 0,
|
|
||||||
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY = 1U << 1,
|
|
||||||
+ AUDIO_INT_READ_BUFFER_FULL = 1U << 2,
|
|
||||||
+
|
|
||||||
+ AUDIO_INT_MASK = AUDIO_INT_WRITE_BUFFER_1_EMPTY |
|
|
||||||
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY |
|
|
||||||
+ AUDIO_INT_READ_BUFFER_FULL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static atomic_t open_count = ATOMIC_INIT(0);
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static ssize_t goldfish_audio_read(struct file *fp, char __user *buf,
|
|
||||||
+ size_t count, loff_t *pos)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_audio* data = fp->private_data;
|
|
||||||
+ int length;
|
|
||||||
+ int result = 0;
|
|
||||||
+
|
|
||||||
+ if (!data->read_supported)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ while (count > 0) {
|
|
||||||
+ length = (count > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : count);
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(data, AUDIO_START_READ, length);
|
|
||||||
+
|
|
||||||
+ wait_event_interruptible(data->wait, (data->buffer_status & AUDIO_INT_READ_BUFFER_FULL));
|
|
||||||
+
|
|
||||||
+ length = GOLDFISH_AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE);
|
|
||||||
+
|
|
||||||
+ /* copy data to user space */
|
|
||||||
+ if (copy_to_user(buf, data->read_buffer, length))
|
|
||||||
+ {
|
|
||||||
+ printk("copy_from_user failed!\n");
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ result += length;
|
|
||||||
+ buf += length;
|
|
||||||
+ count -= length;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return result;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t goldfish_audio_write(struct file *fp, const char __user *buf,
|
|
||||||
+ size_t count, loff_t *pos)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_audio* data = fp->private_data;
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ ssize_t result = 0;
|
|
||||||
+ char __iomem *kbuf;
|
|
||||||
+
|
|
||||||
+ while (count > 0)
|
|
||||||
+ {
|
|
||||||
+ ssize_t copy = count;
|
|
||||||
+ if (copy > WRITE_BUFFER_SIZE)
|
|
||||||
+ copy = WRITE_BUFFER_SIZE;
|
|
||||||
+ wait_event_interruptible(data->wait,
|
|
||||||
+ (data->buffer_status & (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY)));
|
|
||||||
+
|
|
||||||
+ if ((data->buffer_status & AUDIO_INT_WRITE_BUFFER_1_EMPTY) != 0) {
|
|
||||||
+ kbuf = data->write_buffer1;
|
|
||||||
+ } else {
|
|
||||||
+ kbuf = data->write_buffer2;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* copy from user space to the appropriate buffer */
|
|
||||||
+ if (copy_from_user(kbuf, buf, copy))
|
|
||||||
+ {
|
|
||||||
+ printk("copy_from_user failed!\n");
|
|
||||||
+ result = -EFAULT;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ spin_lock_irqsave(&data->lock, irq_flags);
|
|
||||||
+
|
|
||||||
+ /* clear the buffer empty flag, and signal the emulator to start writing the buffer */
|
|
||||||
+ if (kbuf == data->write_buffer1) {
|
|
||||||
+ data->buffer_status &= ~AUDIO_INT_WRITE_BUFFER_1_EMPTY;
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(data, AUDIO_WRITE_BUFFER_1, copy);
|
|
||||||
+ } else {
|
|
||||||
+ data->buffer_status &= ~AUDIO_INT_WRITE_BUFFER_2_EMPTY;
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(data, AUDIO_WRITE_BUFFER_2, copy);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ spin_unlock_irqrestore(&data->lock, irq_flags);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ buf += copy;
|
|
||||||
+ result += copy;
|
|
||||||
+ count -= copy;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return result;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_audio_open(struct inode *ip, struct file *fp)
|
|
||||||
+{
|
|
||||||
+ if (!audio_data)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+
|
|
||||||
+ if (atomic_inc_return(&open_count) == 1)
|
|
||||||
+ {
|
|
||||||
+ fp->private_data = audio_data;
|
|
||||||
+ audio_data->buffer_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(audio_data, AUDIO_INT_ENABLE, AUDIO_INT_MASK);
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ atomic_dec(&open_count);
|
|
||||||
+ return -EBUSY;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_audio_release(struct inode *ip, struct file* fp)
|
|
||||||
+{
|
|
||||||
+ atomic_dec(&open_count);
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(audio_data, AUDIO_INT_ENABLE, 0);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_audio_ioctl(struct inode* ip, struct file* fp, unsigned int cmd, unsigned long arg)
|
|
||||||
+{
|
|
||||||
+ /* temporary workaround, until we switch to the ALSA API */
|
|
||||||
+ if (cmd == 315)
|
|
||||||
+ return -1;
|
|
||||||
+ else
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t
|
|
||||||
+goldfish_audio_interrupt(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ struct goldfish_audio *data = dev_id;
|
|
||||||
+ uint32_t status;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&data->lock, irq_flags);
|
|
||||||
+
|
|
||||||
+ /* read buffer status flags */
|
|
||||||
+ status = GOLDFISH_AUDIO_READ(data, AUDIO_INT_STATUS);
|
|
||||||
+ status &= AUDIO_INT_MASK;
|
|
||||||
+ /* if buffers are newly empty, wake up blocked goldfish_audio_write() call */
|
|
||||||
+ if(status) {
|
|
||||||
+ data->buffer_status = status;
|
|
||||||
+ wake_up(&data->wait);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ spin_unlock_irqrestore(&data->lock, irq_flags);
|
|
||||||
+ return status ? IRQ_HANDLED : IRQ_NONE;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* file operations for /dev/eac */
|
|
||||||
+static struct file_operations goldfish_audio_fops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .read = goldfish_audio_read,
|
|
||||||
+ .write = goldfish_audio_write,
|
|
||||||
+ .open = goldfish_audio_open,
|
|
||||||
+ .release = goldfish_audio_release,
|
|
||||||
+ .ioctl = goldfish_audio_ioctl,
|
|
||||||
+
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct miscdevice goldfish_audio_device = {
|
|
||||||
+ .minor = MISC_DYNAMIC_MINOR,
|
|
||||||
+ .name = "eac",
|
|
||||||
+ .fops = &goldfish_audio_fops,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int goldfish_audio_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct resource *r;
|
|
||||||
+ struct goldfish_audio *data;
|
|
||||||
+ dma_addr_t buf_addr;
|
|
||||||
+
|
|
||||||
+printk("goldfish_audio_probe\n");
|
|
||||||
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
|
|
||||||
+ if(data == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_data_alloc_failed;
|
|
||||||
+ }
|
|
||||||
+ spin_lock_init(&data->lock);
|
|
||||||
+ init_waitqueue_head(&data->wait);
|
|
||||||
+ platform_set_drvdata(pdev, data);
|
|
||||||
+
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if(r == NULL) {
|
|
||||||
+ printk("platform_get_resource failed\n");
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_io_base;
|
|
||||||
+ }
|
|
||||||
+ data->reg_base = IO_ADDRESS(r->start - IO_START);
|
|
||||||
+
|
|
||||||
+ data->irq = platform_get_irq(pdev, 0);
|
|
||||||
+ if(data->irq < 0) {
|
|
||||||
+ printk("platform_get_irq failed\n");
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_irq;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ data->buffer_virt = dma_alloc_writecombine(&pdev->dev, COMBINED_BUFFER_SIZE,
|
|
||||||
+ &buf_addr, GFP_KERNEL);
|
|
||||||
+ if(data->buffer_virt == 0) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_alloc_write_buffer_failed;
|
|
||||||
+ }
|
|
||||||
+ data->buffer_phys = buf_addr;
|
|
||||||
+ data->write_buffer1 = data->buffer_virt;
|
|
||||||
+ data->write_buffer2 = data->buffer_virt + WRITE_BUFFER_SIZE;
|
|
||||||
+ data->read_buffer = data->buffer_virt + 2 * WRITE_BUFFER_SIZE;
|
|
||||||
+
|
|
||||||
+ ret = request_irq(data->irq, goldfish_audio_interrupt, IRQF_SHARED, pdev->name, data);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_request_irq_failed;
|
|
||||||
+
|
|
||||||
+ if((ret = misc_register(&goldfish_audio_device)))
|
|
||||||
+ {
|
|
||||||
+ printk("misc_register returned %d in goldfish_audio_init\n", ret);
|
|
||||||
+ goto err_misc_register_failed;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(data, AUDIO_SET_WRITE_BUFFER_1, buf_addr);
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(data, AUDIO_SET_WRITE_BUFFER_2, buf_addr + WRITE_BUFFER_SIZE);
|
|
||||||
+
|
|
||||||
+ data->read_supported = GOLDFISH_AUDIO_READ(data, AUDIO_READ_SUPPORTED);
|
|
||||||
+ if (data->read_supported)
|
|
||||||
+ GOLDFISH_AUDIO_WRITE(data, AUDIO_SET_READ_BUFFER, buf_addr + 2 * WRITE_BUFFER_SIZE);
|
|
||||||
+
|
|
||||||
+ audio_data = data;
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_misc_register_failed:
|
|
||||||
+err_request_irq_failed:
|
|
||||||
+ dma_free_writecombine(&pdev->dev, COMBINED_BUFFER_SIZE, data->buffer_virt, data->buffer_phys);
|
|
||||||
+err_alloc_write_buffer_failed:
|
|
||||||
+err_no_irq:
|
|
||||||
+err_no_io_base:
|
|
||||||
+ kfree(data);
|
|
||||||
+err_data_alloc_failed:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_audio_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_audio *data = platform_get_drvdata(pdev);
|
|
||||||
+
|
|
||||||
+ misc_deregister(&goldfish_audio_device);
|
|
||||||
+ free_irq(data->irq, data);
|
|
||||||
+ dma_free_writecombine(&pdev->dev, COMBINED_BUFFER_SIZE, data->buffer_virt, data->buffer_phys);
|
|
||||||
+ kfree(data);
|
|
||||||
+ audio_data = NULL;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_audio_driver = {
|
|
||||||
+ .probe = goldfish_audio_probe,
|
|
||||||
+ .remove = goldfish_audio_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish_audio"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_audio_init(void)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ ret = platform_driver_register(&goldfish_audio_driver);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ {
|
|
||||||
+ printk("platform_driver_register returned %d\n", ret);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit goldfish_audio_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&goldfish_audio_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(goldfish_audio_init);
|
|
||||||
+module_exit(goldfish_audio_exit);
|
|
@ -1,72 +0,0 @@
|
|||||||
From f0266de1899f043daff3b472b8fdd6e30012cb25 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@android.com>
|
|
||||||
Date: Wed, 9 Jan 2008 11:46:33 -0800
|
|
||||||
Subject: [PATCH 121/134] [ARM] goldfish: Implement suspend as wait-for-interrupt.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
arch/arm/mach-goldfish/Makefile | 2 +-
|
|
||||||
arch/arm/mach-goldfish/pm.c | 43 +++++++++++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 44 insertions(+), 1 deletions(-)
|
|
||||||
create mode 100644 arch/arm/mach-goldfish/pm.c
|
|
||||||
|
|
||||||
--- a/arch/arm/mach-goldfish/Makefile
|
|
||||||
+++ b/arch/arm/mach-goldfish/Makefile
|
|
||||||
@@ -4,6 +4,6 @@
|
|
||||||
|
|
||||||
# Object file lists.
|
|
||||||
|
|
||||||
-obj-y := pdev_bus.o timer.o audio.o
|
|
||||||
+obj-y := pdev_bus.o timer.o audio.o pm.o
|
|
||||||
obj-$(CONFIG_MACH_GOLDFISH) += board-goldfish.o
|
|
||||||
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/arch/arm/mach-goldfish/pm.c
|
|
||||||
@@ -0,0 +1,43 @@
|
|
||||||
+/* arch/arm/mach-msm/pm.c
|
|
||||||
+ *
|
|
||||||
+ * Goldfish Power Management Routines
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2007 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/kernel.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/pm.h>
|
|
||||||
+#include <linux/suspend.h>
|
|
||||||
+#include <mach/system.h>
|
|
||||||
+
|
|
||||||
+static int goldfish_pm_enter(suspend_state_t state)
|
|
||||||
+{
|
|
||||||
+ arch_idle();
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_suspend_ops goldfish_pm_ops = {
|
|
||||||
+ .enter = goldfish_pm_enter,
|
|
||||||
+ .valid = suspend_valid_only_mem,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_pm_init(void)
|
|
||||||
+{
|
|
||||||
+ suspend_set_ops(&goldfish_pm_ops);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+__initcall(goldfish_pm_init);
|
|
||||||
+
|
|
@ -1,368 +0,0 @@
|
|||||||
From 4ff5a10b94c0c41088c8bbfa5be1ebab3822371b Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
|
|
||||||
Date: Fri, 29 Jun 2007 21:41:20 -0700
|
|
||||||
Subject: [PATCH 122/134] [ARM] goldfish: tty: Adding tty driver for goldfish.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/char/Kconfig | 6 +
|
|
||||||
drivers/char/Makefile | 1 +
|
|
||||||
drivers/char/goldfish_tty.c | 323 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 330 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/char/goldfish_tty.c
|
|
||||||
|
|
||||||
--- a/drivers/char/Kconfig
|
|
||||||
+++ b/drivers/char/Kconfig
|
|
||||||
@@ -1106,6 +1106,12 @@ config DEVPORT
|
|
||||||
depends on ISA || PCI
|
|
||||||
default y
|
|
||||||
|
|
||||||
+config GOLDFISH_TTY
|
|
||||||
+ tristate "Goldfish TTY Driver"
|
|
||||||
+ default n
|
|
||||||
+ help
|
|
||||||
+ TTY driver for Goldfish Virtual Platform.
|
|
||||||
+
|
|
||||||
source "drivers/s390/char/Kconfig"
|
|
||||||
|
|
||||||
endmenu
|
|
||||||
--- a/drivers/char/Makefile
|
|
||||||
+++ b/drivers/char/Makefile
|
|
||||||
@@ -98,6 +98,7 @@ obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o
|
|
||||||
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
|
|
||||||
obj-$(CONFIG_GPIO_TB0219) += tb0219.o
|
|
||||||
obj-$(CONFIG_TELCLOCK) += tlclk.o
|
|
||||||
+obj-$(CONFIG_GOLDFISH_TTY) += goldfish_tty.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_MWAVE) += mwave/
|
|
||||||
obj-$(CONFIG_AGP) += agp/
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/char/goldfish_tty.c
|
|
||||||
@@ -0,0 +1,323 @@
|
|
||||||
+/* drivers/char/goldfish_tty.c
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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/console.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/tty.h>
|
|
||||||
+#include <linux/tty_flip.h>
|
|
||||||
+
|
|
||||||
+#include <mach/hardware.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ GOLDFISH_TTY_PUT_CHAR = 0x00,
|
|
||||||
+ GOLDFISH_TTY_BYTES_READY = 0x04,
|
|
||||||
+ GOLDFISH_TTY_CMD = 0x08,
|
|
||||||
+
|
|
||||||
+ GOLDFISH_TTY_DATA_PTR = 0x10,
|
|
||||||
+ GOLDFISH_TTY_DATA_LEN = 0x14,
|
|
||||||
+
|
|
||||||
+ GOLDFISH_TTY_CMD_INT_DISABLE = 0,
|
|
||||||
+ GOLDFISH_TTY_CMD_INT_ENABLE = 1,
|
|
||||||
+ GOLDFISH_TTY_CMD_WRITE_BUFFER = 2,
|
|
||||||
+ GOLDFISH_TTY_CMD_READ_BUFFER = 3,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct goldfish_tty {
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+ uint32_t base;
|
|
||||||
+ uint32_t irq;
|
|
||||||
+ int opencount;
|
|
||||||
+ struct tty_struct *tty;
|
|
||||||
+ struct console console;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static DEFINE_MUTEX(goldfish_tty_lock);
|
|
||||||
+static struct tty_driver *goldfish_tty_driver;
|
|
||||||
+static uint32_t goldfish_tty_line_count = 8;
|
|
||||||
+static uint32_t goldfish_tty_current_line_count;
|
|
||||||
+static struct goldfish_tty *goldfish_ttys;
|
|
||||||
+
|
|
||||||
+static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ struct goldfish_tty *qtty = &goldfish_ttys[line];
|
|
||||||
+ uint32_t base = qtty->base;
|
|
||||||
+ spin_lock_irqsave(&qtty->lock, irq_flags);
|
|
||||||
+ writel(buf, base + GOLDFISH_TTY_DATA_PTR);
|
|
||||||
+ writel(count, base + GOLDFISH_TTY_DATA_LEN);
|
|
||||||
+ writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD);
|
|
||||||
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ struct platform_device *pdev = dev_id;
|
|
||||||
+ struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
|
|
||||||
+ uint32_t base = qtty->base;
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ unsigned char *buf;
|
|
||||||
+ uint32_t count;
|
|
||||||
+
|
|
||||||
+ count = readl(base + GOLDFISH_TTY_BYTES_READY);
|
|
||||||
+ if(count == 0) {
|
|
||||||
+ return IRQ_NONE;
|
|
||||||
+ }
|
|
||||||
+ count = tty_prepare_flip_string(qtty->tty, &buf, count);
|
|
||||||
+ spin_lock_irqsave(&qtty->lock, irq_flags);
|
|
||||||
+ writel(buf, base + GOLDFISH_TTY_DATA_PTR);
|
|
||||||
+ writel(count, base + GOLDFISH_TTY_DATA_LEN);
|
|
||||||
+ writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD);
|
|
||||||
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
|
|
||||||
+ tty_schedule_flip(qtty->tty);
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_tty_open(struct tty_struct * tty, struct file * filp)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
|
|
||||||
+
|
|
||||||
+ mutex_lock(&goldfish_tty_lock);
|
|
||||||
+ if(qtty->tty == NULL || qtty->tty == tty) {
|
|
||||||
+ if(qtty->opencount++ == 0) {
|
|
||||||
+ qtty->tty = tty;
|
|
||||||
+ writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD);
|
|
||||||
+ }
|
|
||||||
+ ret = 0;
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ ret = -EBUSY;
|
|
||||||
+ mutex_unlock(&goldfish_tty_lock);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void goldfish_tty_close(struct tty_struct * tty, struct file * filp)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
|
|
||||||
+
|
|
||||||
+ mutex_lock(&goldfish_tty_lock);
|
|
||||||
+ if(qtty->tty == tty) {
|
|
||||||
+ if(--qtty->opencount == 0) {
|
|
||||||
+ writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD);
|
|
||||||
+ qtty->tty = NULL;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ mutex_unlock(&goldfish_tty_lock);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
|
|
||||||
+{
|
|
||||||
+ goldfish_tty_do_write(tty->index, buf, count);
|
|
||||||
+ return count;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_tty_write_room(struct tty_struct *tty)
|
|
||||||
+{
|
|
||||||
+ return 0x10000;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
|
|
||||||
+ uint32_t base = qtty->base;
|
|
||||||
+ return readl(base + GOLDFISH_TTY_BYTES_READY);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count)
|
|
||||||
+{
|
|
||||||
+ goldfish_tty_do_write(co->index, b, count);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index)
|
|
||||||
+{
|
|
||||||
+ *index = c->index;
|
|
||||||
+ return goldfish_tty_driver;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_tty_console_setup(struct console *co, char *options)
|
|
||||||
+{
|
|
||||||
+ if((unsigned)co->index > goldfish_tty_line_count)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ if(goldfish_ttys[co->index].base == 0)
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct tty_operations goldfish_tty_ops = {
|
|
||||||
+ .open = goldfish_tty_open,
|
|
||||||
+ .close = goldfish_tty_close,
|
|
||||||
+ .write = goldfish_tty_write,
|
|
||||||
+ .write_room = goldfish_tty_write_room,
|
|
||||||
+ .chars_in_buffer = goldfish_tty_chars_in_buffer,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __devinit goldfish_tty_create_driver(void)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct tty_driver *tty;
|
|
||||||
+
|
|
||||||
+ goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL);
|
|
||||||
+ if(goldfish_ttys == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_alloc_goldfish_ttys_failed;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ tty = alloc_tty_driver(goldfish_tty_line_count);
|
|
||||||
+ if(tty == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_alloc_tty_driver_failed;
|
|
||||||
+ }
|
|
||||||
+ tty->driver_name = "goldfish";
|
|
||||||
+ tty->name = "ttyS";
|
|
||||||
+ tty->type = TTY_DRIVER_TYPE_SERIAL;
|
|
||||||
+ tty->subtype = SERIAL_TYPE_NORMAL;
|
|
||||||
+ tty->init_termios = tty_std_termios;
|
|
||||||
+ tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
|
||||||
+ tty_set_operations(tty, &goldfish_tty_ops);
|
|
||||||
+ ret = tty_register_driver(tty);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_tty_register_driver_failed;
|
|
||||||
+
|
|
||||||
+ goldfish_tty_driver = tty;
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_tty_register_driver_failed:
|
|
||||||
+ put_tty_driver(tty);
|
|
||||||
+err_alloc_tty_driver_failed:
|
|
||||||
+ kfree(goldfish_ttys);
|
|
||||||
+ goldfish_ttys = NULL;
|
|
||||||
+err_alloc_goldfish_ttys_failed:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void goldfish_tty_delete_driver(void)
|
|
||||||
+{
|
|
||||||
+ tty_unregister_driver(goldfish_tty_driver);
|
|
||||||
+ put_tty_driver(goldfish_tty_driver);
|
|
||||||
+ goldfish_tty_driver = NULL;
|
|
||||||
+ kfree(goldfish_ttys);
|
|
||||||
+ goldfish_ttys = NULL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int __devinit goldfish_tty_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ int i;
|
|
||||||
+ struct resource *r;
|
|
||||||
+ struct device *ttydev;
|
|
||||||
+ uint32_t base;
|
|
||||||
+ uint32_t irq;
|
|
||||||
+
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if(r == NULL)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ base = IO_ADDRESS(r->start - IO_START);
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
||||||
+ if(r == NULL)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ irq = r->start;
|
|
||||||
+
|
|
||||||
+ if(pdev->id >= goldfish_tty_line_count)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&goldfish_tty_lock);
|
|
||||||
+ if(goldfish_tty_current_line_count == 0) {
|
|
||||||
+ ret = goldfish_tty_create_driver();
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_create_driver_failed;
|
|
||||||
+ }
|
|
||||||
+ goldfish_tty_current_line_count++;
|
|
||||||
+
|
|
||||||
+ spin_lock_init(&goldfish_ttys[pdev->id].lock);
|
|
||||||
+ goldfish_ttys[pdev->id].base = base;
|
|
||||||
+ goldfish_ttys[pdev->id].irq = irq;
|
|
||||||
+
|
|
||||||
+ writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
|
|
||||||
+
|
|
||||||
+ ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, "goldfish_tty", pdev);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_request_irq_failed;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ ttydev = tty_register_device(goldfish_tty_driver, pdev->id, NULL);
|
|
||||||
+ if(IS_ERR(ttydev)) {
|
|
||||||
+ ret = PTR_ERR(ttydev);
|
|
||||||
+ goto err_tty_register_device_failed;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ strcpy(goldfish_ttys[pdev->id].console.name, "ttyS");
|
|
||||||
+ goldfish_ttys[pdev->id].console.write = goldfish_tty_console_write;
|
|
||||||
+ goldfish_ttys[pdev->id].console.device = goldfish_tty_console_device;
|
|
||||||
+ goldfish_ttys[pdev->id].console.setup = goldfish_tty_console_setup;
|
|
||||||
+ goldfish_ttys[pdev->id].console.flags = CON_PRINTBUFFER;
|
|
||||||
+ goldfish_ttys[pdev->id].console.index = pdev->id;
|
|
||||||
+ register_console(&goldfish_ttys[pdev->id].console);
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ mutex_unlock(&goldfish_tty_lock);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ tty_unregister_device(goldfish_tty_driver, i);
|
|
||||||
+err_tty_register_device_failed:
|
|
||||||
+ free_irq(irq, pdev);
|
|
||||||
+err_request_irq_failed:
|
|
||||||
+ goldfish_tty_current_line_count--;
|
|
||||||
+ if(goldfish_tty_current_line_count == 0) {
|
|
||||||
+ goldfish_tty_delete_driver();
|
|
||||||
+ }
|
|
||||||
+err_create_driver_failed:
|
|
||||||
+ mutex_unlock(&goldfish_tty_lock);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int __devexit goldfish_tty_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ mutex_lock(&goldfish_tty_lock);
|
|
||||||
+ unregister_console(&goldfish_ttys[pdev->id].console);
|
|
||||||
+ tty_unregister_device(goldfish_tty_driver, pdev->id);
|
|
||||||
+ goldfish_ttys[pdev->id].base = 0;
|
|
||||||
+ free_irq(goldfish_ttys[pdev->id].irq, pdev);
|
|
||||||
+ goldfish_tty_current_line_count--;
|
|
||||||
+ if(goldfish_tty_current_line_count == 0) {
|
|
||||||
+ goldfish_tty_delete_driver();
|
|
||||||
+ }
|
|
||||||
+ mutex_unlock(&goldfish_tty_lock);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_tty_platform_driver = {
|
|
||||||
+ .probe = goldfish_tty_probe,
|
|
||||||
+ .remove = __devexit_p(goldfish_tty_remove),
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish_tty"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_tty_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&goldfish_tty_platform_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit goldfish_tty_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&goldfish_tty_platform_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(goldfish_tty_init);
|
|
||||||
+module_exit(goldfish_tty_exit);
|
|
@ -1,234 +0,0 @@
|
|||||||
From 226fd290dc2d052e5cd437cc8464e1424e7ebf2c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Brian Swetland <swetland@google.com>
|
|
||||||
Date: Sun, 13 Aug 2006 21:50:14 +0700
|
|
||||||
Subject: [PATCH 123/134] [ARM] goldfish: events: Add event driver for goldfish
|
|
||||||
|
|
||||||
this device is a direct pipe from "hardware" to the input
|
|
||||||
event subsystem, allowing us to avoid having to route
|
|
||||||
"keypad" style events through an AT keyboard driver (gross!)
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
---
|
|
||||||
drivers/input/keyboard/Kconfig | 5 +
|
|
||||||
drivers/input/keyboard/Makefile | 1 +
|
|
||||||
drivers/input/keyboard/goldfish_events.c | 190 ++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 196 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/input/keyboard/goldfish_events.c
|
|
||||||
|
|
||||||
--- a/drivers/input/keyboard/Kconfig
|
|
||||||
+++ b/drivers/input/keyboard/Kconfig
|
|
||||||
@@ -304,6 +304,11 @@ config KEYBOARD_GPIO
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called gpio-keys.
|
|
||||||
|
|
||||||
+config KEYBOARD_GOLDFISH_EVENTS
|
|
||||||
+ tristate "Generic Input Event device for Goldfish"
|
|
||||||
+ help
|
|
||||||
+ no help
|
|
||||||
+
|
|
||||||
config KEYBOARD_MAPLE
|
|
||||||
tristate "Maple bus keyboard"
|
|
||||||
depends on SH_DREAMCAST && MAPLE
|
|
||||||
--- a/drivers/input/keyboard/Makefile
|
|
||||||
+++ b/drivers/input/keyboard/Makefile
|
|
||||||
@@ -23,6 +23,7 @@ obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x
|
|
||||||
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
|
||||||
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
|
||||||
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
|
||||||
+obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
|
|
||||||
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
|
||||||
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
|
|
||||||
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/input/keyboard/goldfish_events.c
|
|
||||||
@@ -0,0 +1,190 @@
|
|
||||||
+/* drivers/input/keyboard/goldfish-events.c
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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/interrupt.h>
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+#include <linux/input.h>
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+
|
|
||||||
+#include <mach/hardware.h>
|
|
||||||
+#include <asm/irq.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ REG_READ = 0x00,
|
|
||||||
+ REG_SET_PAGE = 0x00,
|
|
||||||
+ REG_LEN = 0x04,
|
|
||||||
+ REG_DATA = 0x08,
|
|
||||||
+
|
|
||||||
+ PAGE_NAME = 0x00000,
|
|
||||||
+ PAGE_EVBITS = 0x10000,
|
|
||||||
+ PAGE_ABSDATA = 0x20000 | EV_ABS,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct event_dev {
|
|
||||||
+ struct input_dev *input;
|
|
||||||
+ int irq;
|
|
||||||
+ unsigned addr;
|
|
||||||
+ char name[0];
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static irqreturn_t events_interrupt(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ struct event_dev *edev = dev_id;
|
|
||||||
+ unsigned type, code, value;
|
|
||||||
+
|
|
||||||
+ type = __raw_readl(edev->addr + REG_READ);
|
|
||||||
+ code = __raw_readl(edev->addr + REG_READ);
|
|
||||||
+ value = __raw_readl(edev->addr + REG_READ);
|
|
||||||
+
|
|
||||||
+ input_event(edev->input, type, code, value);
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void events_import_bits(struct event_dev *edev, unsigned long bits[], unsigned type, size_t count)
|
|
||||||
+{
|
|
||||||
+ int i, j;
|
|
||||||
+ size_t size;
|
|
||||||
+ uint8_t val;
|
|
||||||
+ unsigned addr = edev->addr;
|
|
||||||
+ __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
|
|
||||||
+ size = __raw_readl(addr + REG_LEN) * 8;
|
|
||||||
+ if (size < count)
|
|
||||||
+ count = size;
|
|
||||||
+ addr = addr + REG_DATA;
|
|
||||||
+ for (i = 0; i < count; i += 8) {
|
|
||||||
+ val = __raw_readb(addr++);
|
|
||||||
+ for (j = 0; j < 8; j++)
|
|
||||||
+ if(val & 1 << j)
|
|
||||||
+ set_bit(i + j, bits);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int events_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct input_dev *input_dev;
|
|
||||||
+ struct event_dev *edev = NULL;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ unsigned keymapnamelen;
|
|
||||||
+ int i;
|
|
||||||
+ int count;
|
|
||||||
+ int irq;
|
|
||||||
+ unsigned addr;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ printk("*** events probe ***\n");
|
|
||||||
+
|
|
||||||
+ input_dev = input_allocate_device();
|
|
||||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if(!input_dev || !res) goto fail;
|
|
||||||
+
|
|
||||||
+ addr = (unsigned) ioremap(res->start, 4096);
|
|
||||||
+ irq = platform_get_irq(pdev, 0);
|
|
||||||
+
|
|
||||||
+ printk("events_probe() addr=0x%08x irq=%d\n", addr, irq);
|
|
||||||
+
|
|
||||||
+ if(!addr) goto fail;
|
|
||||||
+ if(irq < 0) goto fail;
|
|
||||||
+
|
|
||||||
+ __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
|
|
||||||
+ keymapnamelen = __raw_readl(addr + REG_LEN);
|
|
||||||
+
|
|
||||||
+ edev = kzalloc(sizeof(struct event_dev) + keymapnamelen + 1, GFP_KERNEL);
|
|
||||||
+ if (!edev) goto fail;
|
|
||||||
+
|
|
||||||
+ edev->input = input_dev;
|
|
||||||
+ edev->addr = addr;
|
|
||||||
+ edev->irq = irq;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < keymapnamelen; i++) {
|
|
||||||
+ edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
|
|
||||||
+ }
|
|
||||||
+ printk("events_probe() keymap=%s\n", edev->name);
|
|
||||||
+
|
|
||||||
+ events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
|
|
||||||
+ events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
|
|
||||||
+
|
|
||||||
+ __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
|
|
||||||
+ count = __raw_readl(addr + REG_LEN) / (4 * 4);
|
|
||||||
+ if (count > ABS_MAX)
|
|
||||||
+ count = ABS_MAX;
|
|
||||||
+ for(i = 0; i < count; i++) {
|
|
||||||
+ int val[4];
|
|
||||||
+ int j;
|
|
||||||
+ if (!test_bit(i, input_dev->absbit))
|
|
||||||
+ continue;
|
|
||||||
+ for(j = 0; j < ARRAY_SIZE(val); j++)
|
|
||||||
+ val[j] = __raw_readl(edev->addr + REG_DATA + (i * ARRAY_SIZE(val) + j) * 4);
|
|
||||||
+ input_set_abs_params(input_dev, i, val[0], val[1], val[2], val[3]);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, edev);
|
|
||||||
+
|
|
||||||
+ input_dev->name = edev->name;
|
|
||||||
+ input_set_drvdata(input_dev, edev);
|
|
||||||
+
|
|
||||||
+ ret = input_register_device(input_dev);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto fail;
|
|
||||||
+
|
|
||||||
+ if(request_irq(edev->irq, events_interrupt, 0,
|
|
||||||
+ "goldfish-events-keypad", edev) < 0) {
|
|
||||||
+ input_unregister_device(input_dev);
|
|
||||||
+ kfree(edev);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+fail:
|
|
||||||
+ kfree(edev);
|
|
||||||
+ input_free_device(input_dev);
|
|
||||||
+
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver events_driver = {
|
|
||||||
+ .probe = events_probe,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish_events",
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __devinit events_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&events_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static void __exit events_exit(void)
|
|
||||||
+{
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(events_init);
|
|
||||||
+module_exit(events_exit);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Brian Swetland");
|
|
||||||
+MODULE_DESCRIPTION("Goldfish Event Device");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
@ -1,626 +0,0 @@
|
|||||||
From 2eccfcf4c5c50b412a0859a345d8d28fc043956b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mike Lockwood <lockwood@android.com>
|
|
||||||
Date: Thu, 7 Feb 2008 07:47:30 -0500
|
|
||||||
Subject: [PATCH 124/134] [ARM] goldfish: mmc: goldfish MMC driver building and runnning in 2.6.27.
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
---
|
|
||||||
drivers/mmc/host/Kconfig | 8 +
|
|
||||||
drivers/mmc/host/Makefile | 1 +
|
|
||||||
drivers/mmc/host/goldfish.c | 583 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 592 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/mmc/host/goldfish.c
|
|
||||||
|
|
||||||
--- a/drivers/mmc/host/Kconfig
|
|
||||||
+++ b/drivers/mmc/host/Kconfig
|
|
||||||
@@ -200,6 +200,14 @@ config MMC_MVSDIO
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called mvsdio.
|
|
||||||
|
|
||||||
+config MMC_GOLDFISH
|
|
||||||
+ tristate "goldfish qemu Multimedia Card Interface support"
|
|
||||||
+ depends on ARCH_GOLDFISH
|
|
||||||
+ help
|
|
||||||
+ This selects the Goldfish Multimedia card Interface emulation.
|
|
||||||
+
|
|
||||||
+ If unsure, say N.
|
|
||||||
+
|
|
||||||
config MMC_SPI
|
|
||||||
tristate "MMC/SD/SDIO over SPI"
|
|
||||||
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
|
|
||||||
--- a/drivers/mmc/host/Makefile
|
|
||||||
+++ b/drivers/mmc/host/Makefile
|
|
||||||
@@ -26,6 +26,7 @@ obj-$(CONFIG_MMC_SPI) += mmc_spi.o
|
|
||||||
ifeq ($(CONFIG_OF),y)
|
|
||||||
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
|
|
||||||
endif
|
|
||||||
+obj-$(CONFIG_MMC_GOLDFISH) += goldfish.o
|
|
||||||
obj-$(CONFIG_MMC_S3C) += s3cmci.o
|
|
||||||
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
|
|
||||||
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/mmc/host/goldfish.c
|
|
||||||
@@ -0,0 +1,583 @@
|
|
||||||
+/*
|
|
||||||
+ * linux/drivers/media/mmc/goldfish.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright 2007, Google Inc.
|
|
||||||
+ *
|
|
||||||
+ * based on omap.c driver, which was
|
|
||||||
+ * Copyright (C) 2004 Nokia Corporation
|
|
||||||
+ * Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola@nokia.com>
|
|
||||||
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
|
|
||||||
+ * Other hacks (DMA, SD, etc) by David Brownell
|
|
||||||
+ *
|
|
||||||
+ * 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/platform_device.h>
|
|
||||||
+#include <linux/major.h>
|
|
||||||
+
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+#include <linux/pci.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/fs.h>
|
|
||||||
+#include <linux/errno.h>
|
|
||||||
+#include <linux/hdreg.h>
|
|
||||||
+#include <linux/kdev_t.h>
|
|
||||||
+#include <linux/blkdev.h>
|
|
||||||
+#include <linux/mutex.h>
|
|
||||||
+#include <linux/scatterlist.h>
|
|
||||||
+#include <linux/mmc/mmc.h>
|
|
||||||
+#include <linux/mmc/sdio.h>
|
|
||||||
+#include <linux/mmc/host.h>
|
|
||||||
+#include <linux/mmc/card.h>
|
|
||||||
+
|
|
||||||
+#include <linux/moduleparam.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/ioport.h>
|
|
||||||
+#include <linux/dma-mapping.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
+#include <linux/timer.h>
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+#include <asm/irq.h>
|
|
||||||
+#include <asm/scatterlist.h>
|
|
||||||
+#include <asm/mach-types.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+#include <asm/types.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+#include <asm/uaccess.h>
|
|
||||||
+
|
|
||||||
+#define DRIVER_NAME "goldfish_mmc"
|
|
||||||
+
|
|
||||||
+#define BUFFER_SIZE 16384
|
|
||||||
+
|
|
||||||
+#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr))
|
|
||||||
+#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr))
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ /* status register */
|
|
||||||
+ MMC_INT_STATUS = 0x00,
|
|
||||||
+ /* set this to enable IRQ */
|
|
||||||
+ MMC_INT_ENABLE = 0x04,
|
|
||||||
+ /* set this to specify buffer address */
|
|
||||||
+ MMC_SET_BUFFER = 0x08,
|
|
||||||
+
|
|
||||||
+ /* MMC command number */
|
|
||||||
+ MMC_CMD = 0x0C,
|
|
||||||
+
|
|
||||||
+ /* MMC argument */
|
|
||||||
+ MMC_ARG = 0x10,
|
|
||||||
+
|
|
||||||
+ /* MMC response (or R2 bits 0 - 31) */
|
|
||||||
+ MMC_RESP_0 = 0x14,
|
|
||||||
+
|
|
||||||
+ /* MMC R2 response bits 32 - 63 */
|
|
||||||
+ MMC_RESP_1 = 0x18,
|
|
||||||
+
|
|
||||||
+ /* MMC R2 response bits 64 - 95 */
|
|
||||||
+ MMC_RESP_2 = 0x1C,
|
|
||||||
+
|
|
||||||
+ /* MMC R2 response bits 96 - 127 */
|
|
||||||
+ MMC_RESP_3 = 0x20,
|
|
||||||
+
|
|
||||||
+ MMC_BLOCK_LENGTH = 0x24,
|
|
||||||
+ MMC_BLOCK_COUNT = 0x28,
|
|
||||||
+
|
|
||||||
+ /* MMC state flags */
|
|
||||||
+ MMC_STATE = 0x2C,
|
|
||||||
+
|
|
||||||
+ /* MMC_INT_STATUS bits */
|
|
||||||
+
|
|
||||||
+ MMC_STAT_END_OF_CMD = 1U << 0,
|
|
||||||
+ MMC_STAT_END_OF_DATA = 1U << 1,
|
|
||||||
+ MMC_STAT_STATE_CHANGE = 1U << 2,
|
|
||||||
+
|
|
||||||
+ /* MMC_STATE bits */
|
|
||||||
+ MMC_STATE_INSERTED = 1U << 0,
|
|
||||||
+ MMC_STATE_READ_ONLY = 1U << 1,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Command types
|
|
||||||
+ */
|
|
||||||
+#define OMAP_MMC_CMDTYPE_BC 0
|
|
||||||
+#define OMAP_MMC_CMDTYPE_BCR 1
|
|
||||||
+#define OMAP_MMC_CMDTYPE_AC 2
|
|
||||||
+#define OMAP_MMC_CMDTYPE_ADTC 3
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct goldfish_mmc_host {
|
|
||||||
+ struct mmc_request * mrq;
|
|
||||||
+ struct mmc_command * cmd;
|
|
||||||
+ struct mmc_data * data;
|
|
||||||
+ struct mmc_host * mmc;
|
|
||||||
+ struct device * dev;
|
|
||||||
+ unsigned char id; /* 16xx chips have 2 MMC blocks */
|
|
||||||
+ void __iomem *virt_base;
|
|
||||||
+ unsigned int phys_base;
|
|
||||||
+ int irq;
|
|
||||||
+ unsigned char bus_mode;
|
|
||||||
+ unsigned char hw_bus_mode;
|
|
||||||
+
|
|
||||||
+ unsigned int sg_len;
|
|
||||||
+ unsigned dma_done:1;
|
|
||||||
+ unsigned dma_in_use:1;
|
|
||||||
+
|
|
||||||
+ struct work_struct switch_work;
|
|
||||||
+ int switch_last_state;
|
|
||||||
+
|
|
||||||
+ uint32_t reg_base;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static inline int
|
|
||||||
+goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t
|
|
||||||
+goldfish_mmc_show_cover_switch(struct device *dev,
|
|
||||||
+ struct device_attribute *attr, char *buf)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_mmc_host *host = dev_get_drvdata(dev);
|
|
||||||
+
|
|
||||||
+ return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" :
|
|
||||||
+ "closed");
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL);
|
|
||||||
+
|
|
||||||
+static void
|
|
||||||
+goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)
|
|
||||||
+{
|
|
||||||
+ u32 cmdreg;
|
|
||||||
+ u32 resptype;
|
|
||||||
+ u32 cmdtype;
|
|
||||||
+
|
|
||||||
+ host->cmd = cmd;
|
|
||||||
+
|
|
||||||
+ resptype = 0;
|
|
||||||
+ cmdtype = 0;
|
|
||||||
+
|
|
||||||
+ /* Our hardware needs to know exact type */
|
|
||||||
+ switch (mmc_resp_type(cmd)) {
|
|
||||||
+ case MMC_RSP_NONE:
|
|
||||||
+ break;
|
|
||||||
+ case MMC_RSP_R1:
|
|
||||||
+ case MMC_RSP_R1B:
|
|
||||||
+ /* resp 1, 1b, 6, 7 */
|
|
||||||
+ resptype = 1;
|
|
||||||
+ break;
|
|
||||||
+ case MMC_RSP_R2:
|
|
||||||
+ resptype = 2;
|
|
||||||
+ break;
|
|
||||||
+ case MMC_RSP_R3:
|
|
||||||
+ resptype = 3;
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd));
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) {
|
|
||||||
+ cmdtype = OMAP_MMC_CMDTYPE_ADTC;
|
|
||||||
+ } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) {
|
|
||||||
+ cmdtype = OMAP_MMC_CMDTYPE_BC;
|
|
||||||
+ } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) {
|
|
||||||
+ cmdtype = OMAP_MMC_CMDTYPE_BCR;
|
|
||||||
+ } else {
|
|
||||||
+ cmdtype = OMAP_MMC_CMDTYPE_AC;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
|
|
||||||
+
|
|
||||||
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
|
||||||
+ cmdreg |= 1 << 6;
|
|
||||||
+
|
|
||||||
+ if (cmd->flags & MMC_RSP_BUSY)
|
|
||||||
+ cmdreg |= 1 << 11;
|
|
||||||
+
|
|
||||||
+ if (host->data && !(host->data->flags & MMC_DATA_WRITE))
|
|
||||||
+ cmdreg |= 1 << 15;
|
|
||||||
+
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg);
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void
|
|
||||||
+goldfish_mmc_xfer_done(struct goldfish_mmc_host *host, struct mmc_data *data)
|
|
||||||
+{
|
|
||||||
+ if (host->dma_in_use) {
|
|
||||||
+ enum dma_data_direction dma_data_dir;
|
|
||||||
+
|
|
||||||
+ if (data->flags & MMC_DATA_WRITE)
|
|
||||||
+ dma_data_dir = DMA_TO_DEVICE;
|
|
||||||
+ else
|
|
||||||
+ dma_data_dir = DMA_FROM_DEVICE;
|
|
||||||
+
|
|
||||||
+ if (dma_data_dir == DMA_FROM_DEVICE) {
|
|
||||||
+ // we don't really have DMA, so we need to copy from our platform driver buffer
|
|
||||||
+ uint8_t* dest = (uint8_t *)sg_virt(data->sg);
|
|
||||||
+ memcpy(dest, host->virt_base, data->sg->length);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ host->data->bytes_xfered += data->sg->length;
|
|
||||||
+
|
|
||||||
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, dma_data_dir);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ host->data = NULL;
|
|
||||||
+ host->sg_len = 0;
|
|
||||||
+
|
|
||||||
+ /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
|
|
||||||
+ * dozens of requests until the card finishes writing data.
|
|
||||||
+ * It'd be cheaper to just wait till an EOFB interrupt arrives...
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+ if (!data->stop) {
|
|
||||||
+ host->mrq = NULL;
|
|
||||||
+ mmc_request_done(host->mmc, data->mrq);
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ goldfish_mmc_start_command(host, data->stop);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void
|
|
||||||
+goldfish_mmc_end_of_data(struct goldfish_mmc_host *host, struct mmc_data *data)
|
|
||||||
+{
|
|
||||||
+ if (!host->dma_in_use) {
|
|
||||||
+ goldfish_mmc_xfer_done(host, data);
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ if (host->dma_done)
|
|
||||||
+ goldfish_mmc_xfer_done(host, data);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void
|
|
||||||
+goldfish_mmc_cmd_done(struct goldfish_mmc_host *host, struct mmc_command *cmd)
|
|
||||||
+{
|
|
||||||
+ host->cmd = NULL;
|
|
||||||
+ if (cmd->flags & MMC_RSP_PRESENT) {
|
|
||||||
+ if (cmd->flags & MMC_RSP_136) {
|
|
||||||
+ /* response type 2 */
|
|
||||||
+ cmd->resp[3] =
|
|
||||||
+ GOLDFISH_MMC_READ(host, MMC_RESP_0);
|
|
||||||
+ cmd->resp[2] =
|
|
||||||
+ GOLDFISH_MMC_READ(host, MMC_RESP_1);
|
|
||||||
+ cmd->resp[1] =
|
|
||||||
+ GOLDFISH_MMC_READ(host, MMC_RESP_2);
|
|
||||||
+ cmd->resp[0] =
|
|
||||||
+ GOLDFISH_MMC_READ(host, MMC_RESP_3);
|
|
||||||
+ } else {
|
|
||||||
+ /* response types 1, 1b, 3, 4, 5, 6 */
|
|
||||||
+ cmd->resp[0] =
|
|
||||||
+ GOLDFISH_MMC_READ(host, MMC_RESP_0);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (host->data == NULL || cmd->error) {
|
|
||||||
+ host->mrq = NULL;
|
|
||||||
+ mmc_request_done(host->mmc, cmd->mrq);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_mmc_host * host = (struct goldfish_mmc_host *)dev_id;
|
|
||||||
+ u16 status;
|
|
||||||
+ int end_command;
|
|
||||||
+ int end_transfer;
|
|
||||||
+ int transfer_error;
|
|
||||||
+ int state_changed;
|
|
||||||
+
|
|
||||||
+ if (host->cmd == NULL && host->data == NULL) {
|
|
||||||
+ status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
|
|
||||||
+ dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
|
|
||||||
+ if (status != 0) {
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
|
|
||||||
+ }
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ end_command = 0;
|
|
||||||
+ end_transfer = 0;
|
|
||||||
+ transfer_error = 0;
|
|
||||||
+ state_changed = 0;
|
|
||||||
+
|
|
||||||
+ while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
|
|
||||||
+
|
|
||||||
+ if (status & MMC_STAT_END_OF_CMD) {
|
|
||||||
+ end_command = 1;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (status & MMC_STAT_END_OF_DATA) {
|
|
||||||
+ end_transfer = 1;
|
|
||||||
+ }
|
|
||||||
+ if (status & MMC_STAT_STATE_CHANGE) {
|
|
||||||
+ state_changed = 1;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (end_command) {
|
|
||||||
+ goldfish_mmc_cmd_done(host, host->cmd);
|
|
||||||
+ }
|
|
||||||
+ if (transfer_error)
|
|
||||||
+ goldfish_mmc_xfer_done(host, host->data);
|
|
||||||
+ else if (end_transfer) {
|
|
||||||
+ host->dma_done = 1;
|
|
||||||
+ goldfish_mmc_end_of_data(host, host->data);
|
|
||||||
+ }
|
|
||||||
+ if (state_changed) {
|
|
||||||
+ schedule_work(&host->switch_work);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static void goldfish_mmc_switch_handler(struct work_struct *work)
|
|
||||||
+{
|
|
||||||
+/*
|
|
||||||
+ struct goldfish_mmc_host *host = container_of(work, struct goldfish_mmc_host, switch_work);
|
|
||||||
+ struct mmc_card *card;
|
|
||||||
+ static int complained = 0;
|
|
||||||
+ int cards = 0, cover_open;
|
|
||||||
+
|
|
||||||
+ cover_open = goldfish_mmc_cover_is_open(host);
|
|
||||||
+ if (cover_open != host->switch_last_state) {
|
|
||||||
+ kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
|
|
||||||
+ host->switch_last_state = cover_open;
|
|
||||||
+ }
|
|
||||||
+ mmc_detect_change(host->mmc, 0);
|
|
||||||
+ list_for_each_entry(card, &host->mmc->cards, node) {
|
|
||||||
+ if (mmc_card_present(card))
|
|
||||||
+ cards++;
|
|
||||||
+ }
|
|
||||||
+ if (goldfish_mmc_cover_is_open(host)) {
|
|
||||||
+ if (!complained) {
|
|
||||||
+ dev_info(mmc_dev(host->mmc), "cover is open\n");
|
|
||||||
+ complained = 1;
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ complained = 0;
|
|
||||||
+ }
|
|
||||||
+*/
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static void
|
|
||||||
+goldfish_mmc_prepare_data(struct goldfish_mmc_host *host, struct mmc_request *req)
|
|
||||||
+{
|
|
||||||
+ struct mmc_data *data = req->data;
|
|
||||||
+ int block_size;
|
|
||||||
+ unsigned sg_len;
|
|
||||||
+ enum dma_data_direction dma_data_dir;
|
|
||||||
+
|
|
||||||
+ host->data = data;
|
|
||||||
+ if (data == NULL) {
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
|
|
||||||
+ host->dma_in_use = 0;
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ block_size = data->blksz;
|
|
||||||
+
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
|
|
||||||
+
|
|
||||||
+ /* cope with calling layer confusion; it issues "single
|
|
||||||
+ * block" writes using multi-block scatterlists.
|
|
||||||
+ */
|
|
||||||
+ sg_len = (data->blocks == 1) ? 1 : data->sg_len;
|
|
||||||
+
|
|
||||||
+ if (data->flags & MMC_DATA_WRITE)
|
|
||||||
+ dma_data_dir = DMA_TO_DEVICE;
|
|
||||||
+ else
|
|
||||||
+ dma_data_dir = DMA_FROM_DEVICE;
|
|
||||||
+
|
|
||||||
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
||||||
+ sg_len, dma_data_dir);
|
|
||||||
+ host->dma_done = 0;
|
|
||||||
+ host->dma_in_use = 1;
|
|
||||||
+
|
|
||||||
+ if (dma_data_dir == DMA_TO_DEVICE) {
|
|
||||||
+ // we don't really have DMA, so we need to copy to our platform driver buffer
|
|
||||||
+ const uint8_t* src = (uint8_t *)sg_virt(data->sg);
|
|
||||||
+ memcpy(host->virt_base, src, data->sg->length);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
|
|
||||||
+
|
|
||||||
+ WARN_ON(host->mrq != NULL);
|
|
||||||
+
|
|
||||||
+ host->mrq = req;
|
|
||||||
+ goldfish_mmc_prepare_data(host, req);
|
|
||||||
+ goldfish_mmc_start_command(host, req->cmd);
|
|
||||||
+
|
|
||||||
+ /* this is to avoid accidentally being detected as an SDIO card in mmc_attach_sdio() */
|
|
||||||
+ if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
|
|
||||||
+ req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR)) {
|
|
||||||
+ req->cmd->error = -EINVAL;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
|
|
||||||
+
|
|
||||||
+ host->bus_mode = ios->bus_mode;
|
|
||||||
+ host->hw_bus_mode = host->bus_mode;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_mmc_get_ro(struct mmc_host *mmc)
|
|
||||||
+{
|
|
||||||
+ uint32_t state;
|
|
||||||
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
|
|
||||||
+
|
|
||||||
+ state = GOLDFISH_MMC_READ(host, MMC_STATE);
|
|
||||||
+ return ((state & MMC_STATE_READ_ONLY) != 0);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct mmc_host_ops goldfish_mmc_ops = {
|
|
||||||
+ .request = goldfish_mmc_request,
|
|
||||||
+ .set_ios = goldfish_mmc_set_ios,
|
|
||||||
+ .get_ro = goldfish_mmc_get_ro,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_mmc_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct mmc_host *mmc;
|
|
||||||
+ struct goldfish_mmc_host *host = NULL;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ int ret = 0;
|
|
||||||
+ int irq;
|
|
||||||
+ dma_addr_t buf_addr;
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ irq = platform_get_irq(pdev, 0);
|
|
||||||
+ if (res == NULL || irq < 0)
|
|
||||||
+ return -ENXIO;
|
|
||||||
+
|
|
||||||
+ mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
|
|
||||||
+ if (mmc == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_alloc_host_failed;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ host = mmc_priv(mmc);
|
|
||||||
+ host->mmc = mmc;
|
|
||||||
+ host->reg_base = IO_ADDRESS(res->start - IO_START);
|
|
||||||
+ host->virt_base = dma_alloc_writecombine(&pdev->dev, BUFFER_SIZE,
|
|
||||||
+ &buf_addr, GFP_KERNEL);
|
|
||||||
+ if(host->virt_base == 0) {
|
|
||||||
+ ret = -EBUSY;
|
|
||||||
+ goto dma_alloc_failed;
|
|
||||||
+ }
|
|
||||||
+ host->phys_base = buf_addr;
|
|
||||||
+
|
|
||||||
+ host->id = pdev->id;
|
|
||||||
+ host->irq = irq;
|
|
||||||
+
|
|
||||||
+ mmc->ops = &goldfish_mmc_ops;
|
|
||||||
+ mmc->f_min = 400000;
|
|
||||||
+ mmc->f_max = 24000000;
|
|
||||||
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
||||||
+ mmc->caps = MMC_CAP_4_BIT_DATA;
|
|
||||||
+
|
|
||||||
+ /* Use scatterlist DMA to reduce per-transfer costs.
|
|
||||||
+ * NOTE max_seg_size assumption that small blocks aren't
|
|
||||||
+ * normally used (except e.g. for reading SD registers).
|
|
||||||
+ */
|
|
||||||
+ mmc->max_phys_segs = 32;
|
|
||||||
+ mmc->max_hw_segs = 32;
|
|
||||||
+ mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
|
|
||||||
+ mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
|
|
||||||
+ mmc->max_req_size = BUFFER_SIZE;
|
|
||||||
+ mmc->max_seg_size = mmc->max_req_size;
|
|
||||||
+
|
|
||||||
+ ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_request_irq_failed;
|
|
||||||
+
|
|
||||||
+ host->dev = &pdev->dev;
|
|
||||||
+ platform_set_drvdata(pdev, host);
|
|
||||||
+
|
|
||||||
+ ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
|
|
||||||
+ if (ret)
|
|
||||||
+ dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n");
|
|
||||||
+
|
|
||||||
+ mmc_add_host(mmc);
|
|
||||||
+
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
|
|
||||||
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
|
|
||||||
+ MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA | MMC_STAT_STATE_CHANGE
|
|
||||||
+ );
|
|
||||||
+
|
|
||||||
+ // we start with the card present
|
|
||||||
+ kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
|
|
||||||
+ mmc_detect_change(host->mmc, 0);
|
|
||||||
+
|
|
||||||
+ INIT_WORK(&host->switch_work, goldfish_mmc_switch_handler);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_request_irq_failed:
|
|
||||||
+ dma_free_writecombine(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
|
|
||||||
+dma_alloc_failed:
|
|
||||||
+ mmc_free_host(host->mmc);
|
|
||||||
+err_alloc_host_failed:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_mmc_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, NULL);
|
|
||||||
+
|
|
||||||
+ BUG_ON(host == NULL);
|
|
||||||
+
|
|
||||||
+ mmc_remove_host(host->mmc);
|
|
||||||
+ free_irq(host->irq, host);
|
|
||||||
+ dma_free_writecombine(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
|
|
||||||
+ mmc_free_host(host->mmc);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_mmc_driver = {
|
|
||||||
+ .probe = goldfish_mmc_probe,
|
|
||||||
+ .remove = goldfish_mmc_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = DRIVER_NAME,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_mmc_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&goldfish_mmc_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit goldfish_mmc_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&goldfish_mmc_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(goldfish_mmc_init);
|
|
||||||
+module_exit(goldfish_mmc_exit);
|
|
||||||
+
|
|
@ -1,521 +0,0 @@
|
|||||||
From bed297dad6283a5926962c1c59f95ad641824630 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
|
|
||||||
Date: Fri, 29 Jun 2007 22:20:07 -0700
|
|
||||||
Subject: [PATCH 125/134] [ARM] goldfish: NAND: Add nand driver for goldfish.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/mtd/devices/Kconfig | 5 +
|
|
||||||
drivers/mtd/devices/Makefile | 1 +
|
|
||||||
drivers/mtd/devices/goldfish_nand.c | 418 +++++++++++++++++++++++++++++++
|
|
||||||
drivers/mtd/devices/goldfish_nand_reg.h | 58 +++++
|
|
||||||
4 files changed, 482 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/mtd/devices/goldfish_nand.c
|
|
||||||
create mode 100644 drivers/mtd/devices/goldfish_nand_reg.h
|
|
||||||
|
|
||||||
--- a/drivers/mtd/devices/Kconfig
|
|
||||||
+++ b/drivers/mtd/devices/Kconfig
|
|
||||||
@@ -297,5 +297,10 @@ config MTD_DOCPROBE_55AA
|
|
||||||
LinuxBIOS or if you need to recover a DiskOnChip Millennium on which
|
|
||||||
you have managed to wipe the first block.
|
|
||||||
|
|
||||||
+config MTD_GOLDFISH_NAND
|
|
||||||
+ tristate "Goldfish NAND device"
|
|
||||||
+ help
|
|
||||||
+ none
|
|
||||||
+
|
|
||||||
endmenu
|
|
||||||
|
|
||||||
--- a/drivers/mtd/devices/Makefile
|
|
||||||
+++ b/drivers/mtd/devices/Makefile
|
|
||||||
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
|
|
||||||
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
|
|
||||||
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
|
|
||||||
obj-$(CONFIG_MTD_M25P80) += m25p80.o
|
|
||||||
+obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/mtd/devices/goldfish_nand.c
|
|
||||||
@@ -0,0 +1,418 @@
|
|
||||||
+/* drivers/mtd/devices/goldfish_nand.c
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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 <asm/div64.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <linux/ioport.h>
|
|
||||||
+#include <linux/vmalloc.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/mtd/compatmac.h>
|
|
||||||
+#include <linux/mtd/mtd.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+
|
|
||||||
+#include "goldfish_nand_reg.h"
|
|
||||||
+
|
|
||||||
+struct goldfish_nand {
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+ unsigned char __iomem *base;
|
|
||||||
+ size_t mtd_count;
|
|
||||||
+ struct mtd_info mtd[0];
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static uint32_t goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
|
|
||||||
+ uint64_t addr, uint32_t len, void *ptr)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_nand *nand = mtd->priv;
|
|
||||||
+ uint32_t rv;
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ unsigned char __iomem *base = nand->base;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&nand->lock, irq_flags);
|
|
||||||
+ writel(mtd - nand->mtd, base + NAND_DEV);
|
|
||||||
+ writel((uint32_t)(addr >> 32), base + NAND_ADDR_HIGH);
|
|
||||||
+ writel((uint32_t)addr, base + NAND_ADDR_LOW);
|
|
||||||
+ writel(len, base + NAND_TRANSFER_SIZE);
|
|
||||||
+ writel(ptr, base + NAND_DATA);
|
|
||||||
+ writel(cmd, base + NAND_COMMAND);
|
|
||||||
+ rv = readl(base + NAND_RESULT);
|
|
||||||
+ spin_unlock_irqrestore(&nand->lock, irq_flags);
|
|
||||||
+ return rv;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|
||||||
+{
|
|
||||||
+ loff_t ofs = instr->addr;
|
|
||||||
+ uint32_t len = instr->len;
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if (ofs + len > mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ rem = do_div(ofs, mtd->writesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ if(len % mtd->writesize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ if(goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
|
|
||||||
+ printk("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size "
|
|
||||||
+ "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
|
|
||||||
+ return -EIO;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ instr->state = MTD_ERASE_DONE;
|
|
||||||
+ mtd_erase_callback(instr);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size "
|
|
||||||
+ "%llx, erase_size %x\n", ofs, len, mtd->size, mtd->erasesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
|
|
||||||
+ struct mtd_oob_ops *ops)
|
|
||||||
+{
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if(ofs + ops->len > mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ if(ops->datbuf && ops->len && ops->len != mtd->writesize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ if(ops->ooblen + ops->ooboffs > mtd->oobsize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ rem = do_div(ofs, mtd->writesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ if(ops->datbuf)
|
|
||||||
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
|
|
||||||
+ ops->len, ops->datbuf);
|
|
||||||
+ ofs += mtd->writesize + ops->ooboffs;
|
|
||||||
+ if(ops->oobbuf)
|
|
||||||
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
|
|
||||||
+ ops->ooblen, ops->oobbuf);
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_read_oob: invalid read, start %llx, len %x, "
|
|
||||||
+ "ooblen %x, dev_size %llx, write_size %x\n",
|
|
||||||
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
|
|
||||||
+ struct mtd_oob_ops *ops)
|
|
||||||
+{
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if(ofs + ops->len > mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ if(ops->len && ops->len != mtd->writesize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ if(ops->ooblen + ops->ooboffs > mtd->oobsize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ rem = do_div(ofs, mtd->writesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ if(ops->datbuf)
|
|
||||||
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
|
|
||||||
+ ops->len, ops->datbuf);
|
|
||||||
+ ofs += mtd->writesize + ops->ooboffs;
|
|
||||||
+ if(ops->oobbuf)
|
|
||||||
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
|
|
||||||
+ ops->ooblen, ops->oobbuf);
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_write_oob: invalid write, start %llx, len %x, "
|
|
||||||
+ "ooblen %x, dev_size %llx, write_size %x\n",
|
|
||||||
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
||||||
+ size_t *retlen, u_char *buf)
|
|
||||||
+{
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if(from + len > mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ if(len != mtd->writesize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ rem = do_div(from, mtd->writesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ from *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %llx"
|
|
||||||
+ ", write_size %x\n", from, len, mtd->size, mtd->writesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
||||||
+ size_t *retlen, const u_char *buf)
|
|
||||||
+{
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if(to + len > mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ if(len != mtd->writesize)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ rem = do_div(to, mtd->writesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ to *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %llx"
|
|
||||||
+ ", write_size %x\n", to, len, mtd->size, mtd->writesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
|
||||||
+{
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if(ofs >= mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ rem = do_div(ofs, mtd->erasesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ ofs *= mtd->erasesize / mtd->writesize;
|
|
||||||
+ ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, "
|
|
||||||
+ "write_size %x\n", ofs, mtd->size, mtd->writesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
||||||
+{
|
|
||||||
+ uint32_t rem;
|
|
||||||
+
|
|
||||||
+ if(ofs >= mtd->size)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ rem = do_div(ofs, mtd->erasesize);
|
|
||||||
+ if(rem)
|
|
||||||
+ goto invalid_arg;
|
|
||||||
+ ofs *= mtd->erasesize / mtd->writesize;
|
|
||||||
+ ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+
|
|
||||||
+ if(goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
|
|
||||||
+ return -EIO;
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+invalid_arg:
|
|
||||||
+ printk("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, "
|
|
||||||
+ "write_size %x\n", ofs, mtd->size, mtd->writesize);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)
|
|
||||||
+{
|
|
||||||
+ uint32_t name_len;
|
|
||||||
+ uint32_t result;
|
|
||||||
+ uint32_t flags;
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ unsigned char __iomem *base = nand->base;
|
|
||||||
+ struct mtd_info *mtd = &nand->mtd[id];
|
|
||||||
+ char *name;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&nand->lock, irq_flags);
|
|
||||||
+ writel(id, base + NAND_DEV);
|
|
||||||
+ flags = readl(base + NAND_DEV_FLAGS);
|
|
||||||
+ name_len = readl(base + NAND_DEV_NAME_LEN);
|
|
||||||
+ mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
|
|
||||||
+ mtd->size = readl(base + NAND_DEV_SIZE_LOW);
|
|
||||||
+ mtd->size |= (uint64_t)readl(base + NAND_DEV_SIZE_HIGH) << 32;
|
|
||||||
+ mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
|
|
||||||
+ mtd->oobavail = mtd->oobsize;
|
|
||||||
+ mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
|
|
||||||
+ (mtd->writesize + mtd->oobsize) * mtd->writesize;
|
|
||||||
+ do_div(mtd->size, mtd->writesize + mtd->oobsize);
|
|
||||||
+ mtd->size *= mtd->writesize;
|
|
||||||
+ printk("goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
|
|
||||||
+ id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize);
|
|
||||||
+ spin_unlock_irqrestore(&nand->lock, irq_flags);
|
|
||||||
+
|
|
||||||
+ mtd->priv = nand;
|
|
||||||
+
|
|
||||||
+ mtd->name = name = kmalloc(name_len + 1, GFP_KERNEL);
|
|
||||||
+ if(name == NULL)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, name);
|
|
||||||
+ if(result != name_len) {
|
|
||||||
+ kfree(mtd->name);
|
|
||||||
+ mtd->name = NULL;
|
|
||||||
+ printk("goldfish_nand_init_device failed to get dev name %d != %d\n",
|
|
||||||
+ result, name_len);
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+ ((char *) mtd->name)[name_len] = '\0';
|
|
||||||
+
|
|
||||||
+ /* Setup the MTD structure */
|
|
||||||
+ mtd->type = MTD_NANDFLASH;
|
|
||||||
+ mtd->flags = MTD_CAP_NANDFLASH;
|
|
||||||
+ if(flags & NAND_DEV_FLAG_READ_ONLY)
|
|
||||||
+ mtd->flags &= ~MTD_WRITEABLE;
|
|
||||||
+
|
|
||||||
+ mtd->owner = THIS_MODULE;
|
|
||||||
+ mtd->erase = goldfish_nand_erase;
|
|
||||||
+ mtd->read = goldfish_nand_read;
|
|
||||||
+ mtd->write = goldfish_nand_write;
|
|
||||||
+ mtd->read_oob = goldfish_nand_read_oob;
|
|
||||||
+ mtd->write_oob = goldfish_nand_write_oob;
|
|
||||||
+ mtd->block_isbad = goldfish_nand_block_isbad;
|
|
||||||
+ mtd->block_markbad = goldfish_nand_block_markbad;
|
|
||||||
+
|
|
||||||
+ if (add_mtd_device(mtd)) {
|
|
||||||
+ kfree(mtd->name);
|
|
||||||
+ mtd->name = NULL;
|
|
||||||
+ return -EIO;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ uint32_t num_dev;
|
|
||||||
+ int i;
|
|
||||||
+ int err;
|
|
||||||
+ uint32_t num_dev_working;
|
|
||||||
+ uint32_t version;
|
|
||||||
+ struct resource *r;
|
|
||||||
+ struct goldfish_nand *nand;
|
|
||||||
+ unsigned char __iomem *base;
|
|
||||||
+
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if(r == NULL) {
|
|
||||||
+ err = -ENODEV;
|
|
||||||
+ goto err_no_io_base;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ base = ioremap(r->start, PAGE_SIZE);
|
|
||||||
+ if(base == NULL) {
|
|
||||||
+ err = -ENOMEM;
|
|
||||||
+ goto err_ioremap;
|
|
||||||
+ }
|
|
||||||
+ version = readl(base + NAND_VERSION);
|
|
||||||
+ if(version != NAND_VERSION_CURRENT) {
|
|
||||||
+ printk("goldfish_nand_init: version mismatch, got %d, expected %d\n",
|
|
||||||
+ version, NAND_VERSION_CURRENT);
|
|
||||||
+ err = -ENODEV;
|
|
||||||
+ goto err_no_dev;
|
|
||||||
+ }
|
|
||||||
+ num_dev = readl(base + NAND_NUM_DEV);
|
|
||||||
+ if(num_dev == 0) {
|
|
||||||
+ err = -ENODEV;
|
|
||||||
+ goto err_no_dev;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
|
|
||||||
+ if(nand == NULL) {
|
|
||||||
+ err = -ENOMEM;
|
|
||||||
+ goto err_nand_alloc_failed;
|
|
||||||
+ }
|
|
||||||
+ spin_lock_init(&nand->lock);
|
|
||||||
+ nand->base = base;
|
|
||||||
+ nand->mtd_count = num_dev;
|
|
||||||
+ platform_set_drvdata(pdev, nand);
|
|
||||||
+
|
|
||||||
+ num_dev_working = 0;
|
|
||||||
+ for(i = 0; i < num_dev; i++) {
|
|
||||||
+ err = goldfish_nand_init_device(nand, i);
|
|
||||||
+ if(err == 0)
|
|
||||||
+ num_dev_working++;
|
|
||||||
+ }
|
|
||||||
+ if(num_dev_working == 0) {
|
|
||||||
+ err = -ENODEV;
|
|
||||||
+ goto err_no_working_dev;
|
|
||||||
+ }
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_no_working_dev:
|
|
||||||
+ kfree(nand);
|
|
||||||
+err_nand_alloc_failed:
|
|
||||||
+err_no_dev:
|
|
||||||
+ iounmap(base);
|
|
||||||
+err_ioremap:
|
|
||||||
+err_no_io_base:
|
|
||||||
+ return err;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_nand_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_nand *nand = platform_get_drvdata(pdev);
|
|
||||||
+ int i;
|
|
||||||
+ for(i = 0; i < nand->mtd_count; i++) {
|
|
||||||
+ if(nand->mtd[i].name) {
|
|
||||||
+ del_mtd_device(&nand->mtd[i]);
|
|
||||||
+ kfree(nand->mtd[i].name);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ iounmap(nand->base);
|
|
||||||
+ kfree(nand);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_nand_driver = {
|
|
||||||
+ .probe = goldfish_nand_probe,
|
|
||||||
+ .remove = goldfish_nand_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish_nand"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_nand_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&goldfish_nand_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit goldfish_nand_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&goldfish_nand_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+module_init(goldfish_nand_init);
|
|
||||||
+module_exit(goldfish_nand_exit);
|
|
||||||
+
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/mtd/devices/goldfish_nand_reg.h
|
|
||||||
@@ -0,0 +1,58 @@
|
|
||||||
+/* drivers/mtd/devices/goldfish_nand_reg.h
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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.
|
|
||||||
+**
|
|
||||||
+*/
|
|
||||||
+
|
|
||||||
+#ifndef GOLDFISH_NAND_REG_H
|
|
||||||
+#define GOLDFISH_NAND_REG_H
|
|
||||||
+
|
|
||||||
+enum nand_cmd {
|
|
||||||
+ NAND_CMD_GET_DEV_NAME, // Write device name for NAND_DEV to NAND_DATA (vaddr)
|
|
||||||
+ NAND_CMD_READ,
|
|
||||||
+ NAND_CMD_WRITE,
|
|
||||||
+ NAND_CMD_ERASE,
|
|
||||||
+ NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not
|
|
||||||
+ NAND_CMD_BLOCK_BAD_SET
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+enum nand_dev_flags {
|
|
||||||
+ NAND_DEV_FLAG_READ_ONLY = 0x00000001
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define NAND_VERSION_CURRENT (1)
|
|
||||||
+
|
|
||||||
+enum nand_reg {
|
|
||||||
+ // Global
|
|
||||||
+ NAND_VERSION = 0x000,
|
|
||||||
+ NAND_NUM_DEV = 0x004,
|
|
||||||
+ NAND_DEV = 0x008,
|
|
||||||
+
|
|
||||||
+ // Dev info
|
|
||||||
+ NAND_DEV_FLAGS = 0x010,
|
|
||||||
+ NAND_DEV_NAME_LEN = 0x014,
|
|
||||||
+ NAND_DEV_PAGE_SIZE = 0x018,
|
|
||||||
+ NAND_DEV_EXTRA_SIZE = 0x01c,
|
|
||||||
+ NAND_DEV_ERASE_SIZE = 0x020,
|
|
||||||
+ NAND_DEV_SIZE_LOW = 0x028,
|
|
||||||
+ NAND_DEV_SIZE_HIGH = 0x02c,
|
|
||||||
+
|
|
||||||
+ // Command
|
|
||||||
+ NAND_RESULT = 0x040,
|
|
||||||
+ NAND_COMMAND = 0x044,
|
|
||||||
+ NAND_DATA = 0x048,
|
|
||||||
+ NAND_TRANSFER_SIZE = 0x04c,
|
|
||||||
+ NAND_ADDR_LOW = 0x050,
|
|
||||||
+ NAND_ADDR_HIGH = 0x054,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#endif
|
|
@ -1,298 +0,0 @@
|
|||||||
From 7041346d379e563e9ee66f9dd31277e4fb6b1b1f Mon Sep 17 00:00:00 2001
|
|
||||||
From: Mike Lockwood <lockwood@android.com>
|
|
||||||
Date: Wed, 6 Feb 2008 11:37:10 -0500
|
|
||||||
Subject: [PATCH 126/134] [ARM] goldfish: POWER: New power supply driver for goldfish.
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
---
|
|
||||||
drivers/power/Kconfig | 5 +
|
|
||||||
drivers/power/Makefile | 3 +-
|
|
||||||
drivers/power/goldfish_battery.c | 254 ++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 261 insertions(+), 1 deletions(-)
|
|
||||||
create mode 100644 drivers/power/goldfish_battery.c
|
|
||||||
|
|
||||||
--- a/drivers/power/Kconfig
|
|
||||||
+++ b/drivers/power/Kconfig
|
|
||||||
@@ -88,4 +88,9 @@ config CHARGER_PCF50633
|
|
||||||
help
|
|
||||||
Say Y to include support for NXP PCF50633 Main Battery Charger.
|
|
||||||
|
|
||||||
+config BATTERY_GOLDFISH
|
|
||||||
+ tristate "Goldfish battery driver"
|
|
||||||
+ help
|
|
||||||
+ Say Y to enable support for the battery and AC power in the Goldfish emulator.
|
|
||||||
+
|
|
||||||
endif # POWER_SUPPLY
|
|
||||||
--- a/drivers/power/Makefile
|
|
||||||
+++ b/drivers/power/Makefile
|
|
||||||
@@ -19,10 +19,11 @@ obj-$(CONFIG_APM_POWER) += apm_power.o
|
|
||||||
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
|
||||||
+obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
|
|
||||||
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
|
|
||||||
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
|
|
||||||
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
|
|
||||||
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
|
||||||
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
|
|
||||||
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
|
|
||||||
-obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
|
||||||
\ No newline at end of file
|
|
||||||
+obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/power/goldfish_battery.c
|
|
||||||
@@ -0,0 +1,254 @@
|
|
||||||
+/* drivers/power/goldfish_battery.c
|
|
||||||
+ *
|
|
||||||
+ * Power supply driver for the goldfish emulator
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2008 Google, Inc.
|
|
||||||
+ * Author: Mike Lockwood <lockwood@android.com>
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/err.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/power_supply.h>
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+#include <linux/pci.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct goldfish_battery_data {
|
|
||||||
+ uint32_t reg_base;
|
|
||||||
+ int irq;
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+
|
|
||||||
+ struct power_supply battery;
|
|
||||||
+ struct power_supply ac;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define GOLDFISH_BATTERY_READ(data, addr) (readl(data->reg_base + addr))
|
|
||||||
+#define GOLDFISH_BATTERY_WRITE(data, addr, x) (writel(x, data->reg_base + addr))
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+/* temporary variable used between goldfish_battery_probe() and goldfish_battery_open() */
|
|
||||||
+static struct goldfish_battery_data *battery_data;
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ /* status register */
|
|
||||||
+ BATTERY_INT_STATUS = 0x00,
|
|
||||||
+ /* set this to enable IRQ */
|
|
||||||
+ BATTERY_INT_ENABLE = 0x04,
|
|
||||||
+
|
|
||||||
+ BATTERY_AC_ONLINE = 0x08,
|
|
||||||
+ BATTERY_STATUS = 0x0C,
|
|
||||||
+ BATTERY_HEALTH = 0x10,
|
|
||||||
+ BATTERY_PRESENT = 0x14,
|
|
||||||
+ BATTERY_CAPACITY = 0x18,
|
|
||||||
+
|
|
||||||
+ BATTERY_STATUS_CHANGED = 1U << 0,
|
|
||||||
+ AC_STATUS_CHANGED = 1U << 1,
|
|
||||||
+ BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int goldfish_ac_get_property(struct power_supply *psy,
|
|
||||||
+ enum power_supply_property psp,
|
|
||||||
+ union power_supply_propval *val)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_battery_data *data = container_of(psy,
|
|
||||||
+ struct goldfish_battery_data, ac);
|
|
||||||
+ int ret = 0;
|
|
||||||
+
|
|
||||||
+ switch (psp) {
|
|
||||||
+ case POWER_SUPPLY_PROP_ONLINE:
|
|
||||||
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ ret = -EINVAL;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_battery_get_property(struct power_supply *psy,
|
|
||||||
+ enum power_supply_property psp,
|
|
||||||
+ union power_supply_propval *val)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_battery_data *data = container_of(psy,
|
|
||||||
+ struct goldfish_battery_data, battery);
|
|
||||||
+ int ret = 0;
|
|
||||||
+
|
|
||||||
+ switch (psp) {
|
|
||||||
+ case POWER_SUPPLY_PROP_STATUS:
|
|
||||||
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
|
|
||||||
+ break;
|
|
||||||
+ case POWER_SUPPLY_PROP_HEALTH:
|
|
||||||
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
|
|
||||||
+ break;
|
|
||||||
+ case POWER_SUPPLY_PROP_PRESENT:
|
|
||||||
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
|
|
||||||
+ break;
|
|
||||||
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
|
|
||||||
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
|
||||||
+ break;
|
|
||||||
+ case POWER_SUPPLY_PROP_CAPACITY:
|
|
||||||
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ ret = -EINVAL;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static enum power_supply_property goldfish_battery_props[] = {
|
|
||||||
+ POWER_SUPPLY_PROP_STATUS,
|
|
||||||
+ POWER_SUPPLY_PROP_HEALTH,
|
|
||||||
+ POWER_SUPPLY_PROP_PRESENT,
|
|
||||||
+ POWER_SUPPLY_PROP_TECHNOLOGY,
|
|
||||||
+ POWER_SUPPLY_PROP_CAPACITY,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static enum power_supply_property goldfish_ac_props[] = {
|
|
||||||
+ POWER_SUPPLY_PROP_ONLINE,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ struct goldfish_battery_data *data = dev_id;
|
|
||||||
+ uint32_t status;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&data->lock, irq_flags);
|
|
||||||
+
|
|
||||||
+ /* read status flags, which will clear the interrupt */
|
|
||||||
+ status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
|
|
||||||
+ status &= BATTERY_INT_MASK;
|
|
||||||
+
|
|
||||||
+ if (status & BATTERY_STATUS_CHANGED)
|
|
||||||
+ power_supply_changed(&data->battery);
|
|
||||||
+ if (status & AC_STATUS_CHANGED)
|
|
||||||
+ power_supply_changed(&data->ac);
|
|
||||||
+
|
|
||||||
+ spin_unlock_irqrestore(&data->lock, irq_flags);
|
|
||||||
+ return status ? IRQ_HANDLED : IRQ_NONE;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int goldfish_battery_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct resource *r;
|
|
||||||
+ struct goldfish_battery_data *data;
|
|
||||||
+
|
|
||||||
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
|
|
||||||
+ if (data == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_data_alloc_failed;
|
|
||||||
+ }
|
|
||||||
+ spin_lock_init(&data->lock);
|
|
||||||
+
|
|
||||||
+ data->battery.properties = goldfish_battery_props;
|
|
||||||
+ data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
|
|
||||||
+ data->battery.get_property = goldfish_battery_get_property;
|
|
||||||
+ data->battery.name = "battery";
|
|
||||||
+ data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
|
||||||
+
|
|
||||||
+ data->ac.properties = goldfish_ac_props;
|
|
||||||
+ data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
|
|
||||||
+ data->ac.get_property = goldfish_ac_get_property;
|
|
||||||
+ data->ac.name = "ac";
|
|
||||||
+ data->ac.type = POWER_SUPPLY_TYPE_MAINS;
|
|
||||||
+
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if (r == NULL) {
|
|
||||||
+ printk(KERN_ERR "%s: platform_get_resource failed\n", pdev->name);
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_io_base;
|
|
||||||
+ }
|
|
||||||
+ data->reg_base = IO_ADDRESS(r->start - IO_START);
|
|
||||||
+
|
|
||||||
+ data->irq = platform_get_irq(pdev, 0);
|
|
||||||
+ if (data->irq < 0) {
|
|
||||||
+ printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name);
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_irq;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_request_irq_failed;
|
|
||||||
+
|
|
||||||
+ ret = power_supply_register(&pdev->dev, &data->ac);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_ac_failed;
|
|
||||||
+
|
|
||||||
+ ret = power_supply_register(&pdev->dev, &data->battery);
|
|
||||||
+ if (ret)
|
|
||||||
+ goto err_battery_failed;
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, data);
|
|
||||||
+ battery_data = data;
|
|
||||||
+
|
|
||||||
+ GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_battery_failed:
|
|
||||||
+ power_supply_unregister(&data->ac);
|
|
||||||
+err_ac_failed:
|
|
||||||
+ free_irq(data->irq, data);
|
|
||||||
+err_request_irq_failed:
|
|
||||||
+err_no_irq:
|
|
||||||
+err_no_io_base:
|
|
||||||
+ kfree(data);
|
|
||||||
+err_data_alloc_failed:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_battery_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_battery_data *data = platform_get_drvdata(pdev);
|
|
||||||
+
|
|
||||||
+ power_supply_unregister(&data->battery);
|
|
||||||
+ power_supply_unregister(&data->ac);
|
|
||||||
+
|
|
||||||
+ free_irq(data->irq, data);
|
|
||||||
+ kfree(data);
|
|
||||||
+ battery_data = NULL;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_battery_device = {
|
|
||||||
+ .probe = goldfish_battery_probe,
|
|
||||||
+ .remove = goldfish_battery_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish-battery"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_battery_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&goldfish_battery_device);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit goldfish_battery_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&goldfish_battery_device);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(goldfish_battery_init);
|
|
||||||
+module_exit(goldfish_battery_exit);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
+MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
|
|
@ -1,186 +0,0 @@
|
|||||||
From fce981438497199217ed19f14c4d0f1a1aa309f3 Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
|
|
||||||
Date: Fri, 29 Jun 2007 21:46:31 -0700
|
|
||||||
Subject: [PATCH 127/134] [ARM] goldfish: RTC: Add RTC driver for goldfish.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Gets the current time from the host.
|
|
||||||
Alarms are not supported yet.
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/rtc/Kconfig | 6 ++
|
|
||||||
drivers/rtc/Makefile | 1 +
|
|
||||||
drivers/rtc/rtc-goldfish.c | 138 ++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 145 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/rtc/rtc-goldfish.c
|
|
||||||
|
|
||||||
--- a/drivers/rtc/Kconfig
|
|
||||||
+++ b/drivers/rtc/Kconfig
|
|
||||||
@@ -691,6 +691,12 @@ config RTC_DRV_BFIN
|
|
||||||
This driver can also be built as a module. If so, the module
|
|
||||||
will be called rtc-bfin.
|
|
||||||
|
|
||||||
+config RTC_DRV_GOLDFISH
|
|
||||||
+ tristate "GOLDFISH"
|
|
||||||
+ depends on ARCH_GOLDFISH
|
|
||||||
+ help
|
|
||||||
+ RTC driver for Goldfish Virtual Platform
|
|
||||||
+
|
|
||||||
config RTC_DRV_RS5C313
|
|
||||||
tristate "Ricoh RS5C313"
|
|
||||||
depends on SH_LANDISK
|
|
||||||
--- a/drivers/rtc/Makefile
|
|
||||||
+++ b/drivers/rtc/Makefile
|
|
||||||
@@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds32
|
|
||||||
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
|
|
||||||
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
|
|
||||||
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
|
|
||||||
+obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
|
|
||||||
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
|
|
||||||
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
|
|
||||||
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/rtc/rtc-goldfish.c
|
|
||||||
@@ -0,0 +1,138 @@
|
|
||||||
+/* drivers/rtc/rtc-goldfish.c
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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/interrupt.h>
|
|
||||||
+#include <linux/irq.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/rtc.h>
|
|
||||||
+
|
|
||||||
+#include <mach/timer.h>
|
|
||||||
+#include <mach/hardware.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+
|
|
||||||
+struct goldfish_rtc {
|
|
||||||
+ uint32_t base;
|
|
||||||
+ uint32_t irq;
|
|
||||||
+ struct rtc_device *rtc;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static irqreturn_t
|
|
||||||
+goldfish_rtc_interrupt(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_rtc *qrtc = dev_id;
|
|
||||||
+ unsigned long events = 0;
|
|
||||||
+
|
|
||||||
+ writel(1, qrtc->base + TIMER_CLEAR_INTERRUPT);
|
|
||||||
+ events = RTC_IRQF | RTC_AF;
|
|
||||||
+
|
|
||||||
+ rtc_update_irq(qrtc->rtc, 1, events);
|
|
||||||
+
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|
||||||
+{
|
|
||||||
+ int64_t time;
|
|
||||||
+ struct goldfish_rtc *qrtc = platform_get_drvdata(to_platform_device(dev));
|
|
||||||
+
|
|
||||||
+ time = readl(qrtc->base + TIMER_TIME_LOW);
|
|
||||||
+ time |= (int64_t)readl(qrtc->base + TIMER_TIME_HIGH) << 32;
|
|
||||||
+ do_div(time, NSEC_PER_SEC);
|
|
||||||
+
|
|
||||||
+ rtc_time_to_tm(time, tm);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct rtc_class_ops goldfish_rtc_ops = {
|
|
||||||
+// .ioctl = goldfish_rtc_ioctl,
|
|
||||||
+ .read_time = goldfish_rtc_read_time,
|
|
||||||
+// .set_time = goldfish_rtc_set_time,
|
|
||||||
+// .read_alarm = goldfish_rtc_read_alarm,
|
|
||||||
+// .set_alarm = goldfish_rtc_set_alarm,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int goldfish_rtc_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct resource *r;
|
|
||||||
+ struct goldfish_rtc *qrtc;
|
|
||||||
+
|
|
||||||
+ qrtc = kzalloc(sizeof(*qrtc), GFP_KERNEL);
|
|
||||||
+ if(qrtc == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_qrtc_alloc_failed;
|
|
||||||
+ }
|
|
||||||
+ platform_set_drvdata(pdev, qrtc);
|
|
||||||
+
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if(r == NULL) {
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_io_base;
|
|
||||||
+ }
|
|
||||||
+ qrtc->base = IO_ADDRESS(r->start - IO_START);
|
|
||||||
+ qrtc->irq = platform_get_irq(pdev, 0);
|
|
||||||
+ if(qrtc->irq < 0) {
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_irq;
|
|
||||||
+ }
|
|
||||||
+ qrtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
|
|
||||||
+ &goldfish_rtc_ops, THIS_MODULE);
|
|
||||||
+ if (IS_ERR(qrtc->rtc)) {
|
|
||||||
+ ret = PTR_ERR(qrtc->rtc);
|
|
||||||
+ goto err_rtc_device_register_failed;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = request_irq(qrtc->irq, goldfish_rtc_interrupt, 0, pdev->name, qrtc);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto request_irq;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ free_irq(qrtc->irq, qrtc);
|
|
||||||
+request_irq:
|
|
||||||
+ rtc_device_unregister(qrtc->rtc);
|
|
||||||
+err_rtc_device_register_failed:
|
|
||||||
+err_no_irq:
|
|
||||||
+err_no_io_base:
|
|
||||||
+ kfree(qrtc);
|
|
||||||
+err_qrtc_alloc_failed:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_rtc_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_rtc *qrtc = platform_get_drvdata(pdev);
|
|
||||||
+ free_irq(qrtc->irq, qrtc);
|
|
||||||
+ rtc_device_unregister(qrtc->rtc);
|
|
||||||
+ kfree(qrtc);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_timer = {
|
|
||||||
+ .probe = goldfish_rtc_probe,
|
|
||||||
+ .remove = goldfish_rtc_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish_rtc"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_rtc_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&goldfish_timer);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(goldfish_rtc_init);
|
|
||||||
+
|
|
@ -1,384 +0,0 @@
|
|||||||
From f8101c780fdc050eda9f93c94f8841de9f384b4f Mon Sep 17 00:00:00 2001
|
|
||||||
From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
|
|
||||||
Date: Fri, 29 Jun 2007 22:14:27 -0700
|
|
||||||
Subject: [PATCH 128/134] [ARM] goldfish: fb: Add fb driver for goldfish.
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=utf-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
Supports panning for double buffering.
|
|
||||||
|
|
||||||
Signed-off-by: Mike A. Chan <mikechan@google.com>
|
|
||||||
Signed-off-by: Arve Hjønnevåg <arve@android.com>
|
|
||||||
---
|
|
||||||
drivers/video/Kconfig | 9 ++
|
|
||||||
drivers/video/Makefile | 1 +
|
|
||||||
drivers/video/goldfishfb.c | 334 ++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 344 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/video/goldfishfb.c
|
|
||||||
|
|
||||||
--- a/drivers/video/Kconfig
|
|
||||||
+++ b/drivers/video/Kconfig
|
|
||||||
@@ -2005,6 +2005,15 @@ config FB_XILINX
|
|
||||||
framebuffer. ML300 carries a 640*480 LCD display on the board,
|
|
||||||
ML403 uses a standard DB15 VGA connector.
|
|
||||||
|
|
||||||
+config FB_GOLDFISH
|
|
||||||
+ tristate "Goldfish Framebuffer"
|
|
||||||
+ depends on FB
|
|
||||||
+ select FB_CFB_FILLRECT
|
|
||||||
+ select FB_CFB_COPYAREA
|
|
||||||
+ select FB_CFB_IMAGEBLIT
|
|
||||||
+ ---help---
|
|
||||||
+ Framebuffer driver for Goldfish Virtual Platform
|
|
||||||
+
|
|
||||||
config FB_COBALT
|
|
||||||
tristate "Cobalt server LCD frame buffer support"
|
|
||||||
depends on FB && MIPS_COBALT
|
|
||||||
--- a/drivers/video/Makefile
|
|
||||||
+++ b/drivers/video/Makefile
|
|
||||||
@@ -92,6 +92,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb
|
|
||||||
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
|
|
||||||
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
|
|
||||||
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
|
|
||||||
+obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
|
|
||||||
obj-$(CONFIG_FB_68328) += 68328fb.o
|
|
||||||
obj-$(CONFIG_FB_GBE) += gbefb.o
|
|
||||||
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/video/goldfishfb.c
|
|
||||||
@@ -0,0 +1,334 @@
|
|
||||||
+/* drivers/video/goldfishfb.c
|
|
||||||
+**
|
|
||||||
+** Copyright (C) 2007 Google, Inc.
|
|
||||||
+**
|
|
||||||
+** This software is licensed under the terms of the GNU General Public
|
|
||||||
+** License version 2, as published by the Free Software Foundation, and
|
|
||||||
+** may be copied, distributed, and modified under those terms.
|
|
||||||
+**
|
|
||||||
+** 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/kernel.h>
|
|
||||||
+#include <linux/dma-mapping.h>
|
|
||||||
+#include <linux/errno.h>
|
|
||||||
+#include <linux/string.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+#include <linux/mm.h>
|
|
||||||
+#include <linux/fb.h>
|
|
||||||
+#include <linux/init.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/ioport.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#ifdef CONFIG_ANDROID_POWER
|
|
||||||
+#include <linux/android_power.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+#include <mach/hardware.h>
|
|
||||||
+
|
|
||||||
+enum {
|
|
||||||
+ FB_GET_WIDTH = 0x00,
|
|
||||||
+ FB_GET_HEIGHT = 0x04,
|
|
||||||
+ FB_INT_STATUS = 0x08,
|
|
||||||
+ FB_INT_ENABLE = 0x0c,
|
|
||||||
+ FB_SET_BASE = 0x10,
|
|
||||||
+ FB_SET_ROTATION = 0x14,
|
|
||||||
+ FB_SET_BLANK = 0x18,
|
|
||||||
+ FB_GET_PHYS_WIDTH = 0x1c,
|
|
||||||
+ FB_GET_PHYS_HEIGHT = 0x20,
|
|
||||||
+
|
|
||||||
+ FB_INT_VSYNC = 1U << 0,
|
|
||||||
+ FB_INT_BASE_UPDATE_DONE = 1U << 1
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct goldfish_fb {
|
|
||||||
+ uint32_t reg_base;
|
|
||||||
+ int irq;
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+ wait_queue_head_t wait;
|
|
||||||
+ int base_update_count;
|
|
||||||
+ int rotation;
|
|
||||||
+ struct fb_info fb;
|
|
||||||
+ u32 cmap[16];
|
|
||||||
+#ifdef CONFIG_ANDROID_POWER
|
|
||||||
+ android_early_suspend_t early_suspend;
|
|
||||||
+#endif
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static irqreturn_t
|
|
||||||
+goldfish_fb_interrupt(int irq, void *dev_id)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ struct goldfish_fb *fb = dev_id;
|
|
||||||
+ uint32_t status;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&fb->lock, irq_flags);
|
|
||||||
+ status = readl(fb->reg_base + FB_INT_STATUS);
|
|
||||||
+ if(status & FB_INT_BASE_UPDATE_DONE) {
|
|
||||||
+ fb->base_update_count++;
|
|
||||||
+ wake_up(&fb->wait);
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&fb->lock, irq_flags);
|
|
||||||
+ return status ? IRQ_HANDLED : IRQ_NONE;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
|
|
||||||
+{
|
|
||||||
+ unsigned int mask = (1 << bf->length) - 1;
|
|
||||||
+
|
|
||||||
+ return (val >> (16 - bf->length) & mask) << bf->offset;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int
|
|
||||||
+goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
|
|
||||||
+ unsigned int blue, unsigned int transp, struct fb_info *info)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
|
|
||||||
+
|
|
||||||
+ if (regno < 16) {
|
|
||||||
+ fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
|
|
||||||
+ convert_bitfield(blue, &fb->fb.var.blue) |
|
|
||||||
+ convert_bitfield(green, &fb->fb.var.green) |
|
|
||||||
+ convert_bitfield(red, &fb->fb.var.red);
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+ else {
|
|
||||||
+ return 1;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
|
||||||
+{
|
|
||||||
+ if((var->rotate & 1) != (info->var.rotate & 1)) {
|
|
||||||
+ if((var->xres != info->var.yres) ||
|
|
||||||
+ (var->yres != info->var.xres) ||
|
|
||||||
+ (var->xres_virtual != info->var.yres) ||
|
|
||||||
+ (var->yres_virtual > info->var.xres * 2) ||
|
|
||||||
+ (var->yres_virtual < info->var.xres )) {
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ else {
|
|
||||||
+ if((var->xres != info->var.xres) ||
|
|
||||||
+ (var->yres != info->var.yres) ||
|
|
||||||
+ (var->xres_virtual != info->var.xres) ||
|
|
||||||
+ (var->yres_virtual > info->var.yres * 2) ||
|
|
||||||
+ (var->yres_virtual < info->var.yres )) {
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ if((var->xoffset != info->var.xoffset) ||
|
|
||||||
+ (var->bits_per_pixel != info->var.bits_per_pixel) ||
|
|
||||||
+ (var->grayscale != info->var.grayscale)) {
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_fb_set_par(struct fb_info *info)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
|
|
||||||
+ if(fb->rotation != fb->fb.var.rotate) {
|
|
||||||
+ info->fix.line_length = info->var.xres * 2;
|
|
||||||
+ fb->rotation = fb->fb.var.rotate;
|
|
||||||
+ writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
|
|
||||||
+ }
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ int base_update_count;
|
|
||||||
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&fb->lock, irq_flags);
|
|
||||||
+ base_update_count = fb->base_update_count;
|
|
||||||
+ writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, fb->reg_base + FB_SET_BASE);
|
|
||||||
+ spin_unlock_irqrestore(&fb->lock, irq_flags);
|
|
||||||
+ wait_event_timeout(fb->wait, fb->base_update_count != base_update_count, HZ / 15);
|
|
||||||
+ if(fb->base_update_count == base_update_count)
|
|
||||||
+ printk("goldfish_fb_pan_display: timeout wating for base update\n");
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_ANDROID_POWER
|
|
||||||
+static void goldfish_fb_early_suspend(android_early_suspend_t *h)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend);
|
|
||||||
+ writel(1, fb->reg_base + FB_SET_BLANK);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void goldfish_fb_late_resume(android_early_suspend_t *h)
|
|
||||||
+{
|
|
||||||
+ struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend);
|
|
||||||
+ writel(0, fb->reg_base + FB_SET_BLANK);
|
|
||||||
+}
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+static struct fb_ops goldfish_fb_ops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .fb_check_var = goldfish_fb_check_var,
|
|
||||||
+ .fb_set_par = goldfish_fb_set_par,
|
|
||||||
+ .fb_setcolreg = goldfish_fb_setcolreg,
|
|
||||||
+ .fb_pan_display = goldfish_fb_pan_display,
|
|
||||||
+ .fb_fillrect = cfb_fillrect,
|
|
||||||
+ .fb_copyarea = cfb_copyarea,
|
|
||||||
+ .fb_imageblit = cfb_imageblit,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static int goldfish_fb_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ struct resource *r;
|
|
||||||
+ struct goldfish_fb *fb;
|
|
||||||
+ size_t framesize;
|
|
||||||
+ uint32_t width, height;
|
|
||||||
+ dma_addr_t fbpaddr;
|
|
||||||
+
|
|
||||||
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
|
|
||||||
+ if(fb == NULL) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_fb_alloc_failed;
|
|
||||||
+ }
|
|
||||||
+ spin_lock_init(&fb->lock);
|
|
||||||
+ init_waitqueue_head(&fb->wait);
|
|
||||||
+ platform_set_drvdata(pdev, fb);
|
|
||||||
+
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if(r == NULL) {
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_io_base;
|
|
||||||
+ }
|
|
||||||
+ fb->reg_base = IO_ADDRESS(r->start - IO_START);
|
|
||||||
+
|
|
||||||
+ fb->irq = platform_get_irq(pdev, 0);
|
|
||||||
+ if(fb->irq < 0) {
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto err_no_irq;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ width = readl(fb->reg_base + FB_GET_WIDTH);
|
|
||||||
+ height = readl(fb->reg_base + FB_GET_HEIGHT);
|
|
||||||
+
|
|
||||||
+ fb->fb.fbops = &goldfish_fb_ops;
|
|
||||||
+ fb->fb.flags = FBINFO_FLAG_DEFAULT;
|
|
||||||
+ fb->fb.pseudo_palette = fb->cmap;
|
|
||||||
+ //strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id));
|
|
||||||
+ fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
|
|
||||||
+ fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
|
|
||||||
+ fb->fb.fix.line_length = width * 2;
|
|
||||||
+ fb->fb.fix.accel = FB_ACCEL_NONE;
|
|
||||||
+ fb->fb.fix.ypanstep = 1;
|
|
||||||
+
|
|
||||||
+ fb->fb.var.xres = width;
|
|
||||||
+ fb->fb.var.yres = height;
|
|
||||||
+ fb->fb.var.xres_virtual = width;
|
|
||||||
+ fb->fb.var.yres_virtual = height * 2;
|
|
||||||
+ fb->fb.var.bits_per_pixel = 16;
|
|
||||||
+ fb->fb.var.activate = FB_ACTIVATE_NOW;
|
|
||||||
+ fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
|
|
||||||
+ fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
|
|
||||||
+
|
|
||||||
+ fb->fb.var.red.offset = 11;
|
|
||||||
+ fb->fb.var.red.length = 5;
|
|
||||||
+ fb->fb.var.green.offset = 5;
|
|
||||||
+ fb->fb.var.green.length = 6;
|
|
||||||
+ fb->fb.var.blue.offset = 0;
|
|
||||||
+ fb->fb.var.blue.length = 5;
|
|
||||||
+
|
|
||||||
+ framesize = width * height * 2 * 2;
|
|
||||||
+ fb->fb.screen_base = dma_alloc_writecombine(&pdev->dev, framesize,
|
|
||||||
+ &fbpaddr, GFP_KERNEL);
|
|
||||||
+ printk("allocating frame buffer %d * %d, got %p\n", width, height, fb->fb.screen_base);
|
|
||||||
+ if(fb->fb.screen_base == 0) {
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ goto err_alloc_screen_base_failed;
|
|
||||||
+ }
|
|
||||||
+ fb->fb.fix.smem_start = fbpaddr;
|
|
||||||
+ fb->fb.fix.smem_len = framesize;
|
|
||||||
+
|
|
||||||
+ ret = fb_set_var(&fb->fb, &fb->fb.var);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_fb_set_var_failed;
|
|
||||||
+
|
|
||||||
+ ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_request_irq_failed;
|
|
||||||
+
|
|
||||||
+ writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
|
|
||||||
+ goldfish_fb_pan_display(&fb->fb.var, &fb->fb); // updates base
|
|
||||||
+
|
|
||||||
+ ret = register_framebuffer(&fb->fb);
|
|
||||||
+ if(ret)
|
|
||||||
+ goto err_register_framebuffer_failed;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_ANDROID_POWER
|
|
||||||
+ fb->early_suspend.suspend = goldfish_fb_early_suspend;
|
|
||||||
+ fb->early_suspend.resume = goldfish_fb_late_resume;
|
|
||||||
+ android_register_early_suspend(&fb->early_suspend);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+err_register_framebuffer_failed:
|
|
||||||
+ free_irq(fb->irq, fb);
|
|
||||||
+err_request_irq_failed:
|
|
||||||
+err_fb_set_var_failed:
|
|
||||||
+ dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start);
|
|
||||||
+err_alloc_screen_base_failed:
|
|
||||||
+err_no_irq:
|
|
||||||
+err_no_io_base:
|
|
||||||
+ kfree(fb);
|
|
||||||
+err_fb_alloc_failed:
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int goldfish_fb_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ size_t framesize;
|
|
||||||
+ struct goldfish_fb *fb = platform_get_drvdata(pdev);
|
|
||||||
+
|
|
||||||
+ framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_ANDROID_POWER
|
|
||||||
+ android_unregister_early_suspend(&fb->early_suspend);
|
|
||||||
+#endif
|
|
||||||
+ unregister_framebuffer(&fb->fb);
|
|
||||||
+ free_irq(fb->irq, fb);
|
|
||||||
+ dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start);
|
|
||||||
+ kfree(fb);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static struct platform_driver goldfish_fb_driver = {
|
|
||||||
+ .probe = goldfish_fb_probe,
|
|
||||||
+ .remove = goldfish_fb_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "goldfish_fb"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init goldfish_fb_init(void)
|
|
||||||
+{
|
|
||||||
+ return platform_driver_register(&goldfish_fb_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit goldfish_fb_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&goldfish_fb_driver);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+module_init(goldfish_fb_init);
|
|
||||||
+module_exit(goldfish_fb_exit);
|
|
||||||
+
|
|
@ -1,854 +0,0 @@
|
|||||||
From fcd69dd4537320a25a294c82362cc7abe4fe773c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ye Wen <ywen@google.com>
|
|
||||||
Date: Fri, 14 Jul 2006 14:51:45 +0700
|
|
||||||
Subject: [PATCH 129/134] [ARM] goldfish: qemutrace: Kernel instrumentation for tracing the events.
|
|
||||||
|
|
||||||
Like fork, context switch, execve and exit.
|
|
||||||
This code is to complement Jack's tracing facility.
|
|
||||||
|
|
||||||
To turn tracing on:
|
|
||||||
echo 1 > /sysfs/qemu_trace/state
|
|
||||||
To turn tracing off: echo 0 > /sysfs/qemu_trace/state
|
|
||||||
I also added java methods to Debug.java to turn tracing on and off.
|
|
||||||
The kernel driver also supports adding dynamic symbols to the trace.
|
|
||||||
To add a symbol 'foo' with hex address 'abcd1234' to the trace:
|
|
||||||
echo 'abcd1234 foo' > /sysfs/qemu_trace/symbol
|
|
||||||
|
|
||||||
Signed-off-by: Mike Chan <mike@android.com>
|
|
||||||
|
|
||||||
[ARM] goldfish: qemutrace: Improved support for tracing thread and process names.
|
|
||||||
|
|
||||||
Added a new pseudo file /sys/qemu_trace/process_name to allow user
|
|
||||||
programs to add a trace record for a process name change. Removed
|
|
||||||
the tracing of thread and process names from the exit() system call
|
|
||||||
because that was not sufficiently general. Added tracing of thread
|
|
||||||
names in set_task_comm() and daemonize(). Added tracing of the
|
|
||||||
thread group id to fork() and clone().
|
|
||||||
|
|
||||||
Signed-off-by: Jack Veenstra <veenstra@google.com>
|
|
||||||
Signed-off-by: Mike Chan <mike@android.com>
|
|
||||||
---
|
|
||||||
arch/arm/kernel/entry-armv.S | 5 +
|
|
||||||
drivers/misc/Kconfig | 5 +
|
|
||||||
drivers/misc/Makefile | 1 +
|
|
||||||
drivers/misc/qemutrace/Makefile | 2 +
|
|
||||||
drivers/misc/qemutrace/qemu_trace.c | 386 +++++++++++++++++++++++++++++
|
|
||||||
drivers/misc/qemutrace/qemu_trace.h | 22 ++
|
|
||||||
drivers/misc/qemutrace/qemu_trace_sysfs.c | 182 ++++++++++++++
|
|
||||||
fs/exec.c | 14 +
|
|
||||||
kernel/exit.c | 14 +
|
|
||||||
kernel/fork.c | 8 +
|
|
||||||
kernel/sched.c | 9 +
|
|
||||||
mm/mmap.c | 13 +
|
|
||||||
12 files changed, 661 insertions(+), 0 deletions(-)
|
|
||||||
create mode 100644 drivers/misc/qemutrace/Makefile
|
|
||||||
create mode 100644 drivers/misc/qemutrace/qemu_trace.c
|
|
||||||
create mode 100644 drivers/misc/qemutrace/qemu_trace.h
|
|
||||||
create mode 100644 drivers/misc/qemutrace/qemu_trace_sysfs.c
|
|
||||||
|
|
||||||
--- a/arch/arm/kernel/entry-armv.S
|
|
||||||
+++ b/arch/arm/kernel/entry-armv.S
|
|
||||||
@@ -733,6 +733,11 @@ ENTRY(__switch_to)
|
|
||||||
ldr r0, =thread_notify_head
|
|
||||||
mov r1, #THREAD_NOTIFY_SWITCH
|
|
||||||
bl atomic_notifier_call_chain
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+/*
|
|
||||||
+ mcr p15, 0, r0, c15, c0, 0 @ signal context switch
|
|
||||||
+*/
|
|
||||||
+#endif
|
|
||||||
mov r0, r5
|
|
||||||
ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
|
|
||||||
UNWIND(.fnend )
|
|
||||||
--- a/drivers/misc/Kconfig
|
|
||||||
+++ b/drivers/misc/Kconfig
|
|
||||||
@@ -233,6 +233,11 @@ config ISL29003
|
|
||||||
This driver can also be built as a module. If so, the module
|
|
||||||
will be called isl29003.
|
|
||||||
|
|
||||||
+config QEMU_TRACE
|
|
||||||
+ tristate "Virtual Device for QEMU tracing"
|
|
||||||
+ ---help---
|
|
||||||
+ This is a virtual device for QEMU tracing.
|
|
||||||
+
|
|
||||||
source "drivers/misc/c2port/Kconfig"
|
|
||||||
source "drivers/misc/eeprom/Kconfig"
|
|
||||||
|
|
||||||
--- a/drivers/misc/Makefile
|
|
||||||
+++ b/drivers/misc/Makefile
|
|
||||||
@@ -20,4 +20,5 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/
|
|
||||||
obj-$(CONFIG_HP_ILO) += hpilo.o
|
|
||||||
obj-$(CONFIG_ISL29003) += isl29003.o
|
|
||||||
obj-$(CONFIG_C2PORT) += c2port/
|
|
||||||
+obj-$(CONFIG_QEMU_TRACE) += qemutrace/
|
|
||||||
obj-y += eeprom/
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/misc/qemutrace/Makefile
|
|
||||||
@@ -0,0 +1,2 @@
|
|
||||||
+obj-$(CONFIG_QEMU_TRACE) := qemu_trace.o
|
|
||||||
+obj-$(CONFIG_QEMU_TRACE) += qemu_trace_sysfs.o
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/misc/qemutrace/qemu_trace.c
|
|
||||||
@@ -0,0 +1,386 @@
|
|
||||||
+/* drivers/misc/qemutrace/qemu_trace.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2007-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/kernel.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
+#include <linux/miscdevice.h>
|
|
||||||
+#include <linux/pci.h>
|
|
||||||
+#include <linux/proc_fs.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/mm.h>
|
|
||||||
+#include <linux/sched.h>
|
|
||||||
+#include <asm/uaccess.h>
|
|
||||||
+#include <asm/io.h>
|
|
||||||
+#include <asm/sizes.h>
|
|
||||||
+#include "qemu_trace.h"
|
|
||||||
+
|
|
||||||
+/* trace device registers */
|
|
||||||
+#define TRACE_DEV_REG_SWITCH 0
|
|
||||||
+#define TRACE_DEV_REG_FORK 1
|
|
||||||
+#define TRACE_DEV_REG_EXECVE_PID 2
|
|
||||||
+#define TRACE_DEV_REG_EXECVE_VMSTART 3
|
|
||||||
+#define TRACE_DEV_REG_EXECVE_VMEND 4
|
|
||||||
+#define TRACE_DEV_REG_EXECVE_OFFSET 5
|
|
||||||
+#define TRACE_DEV_REG_EXECVE_EXEPATH 6
|
|
||||||
+#define TRACE_DEV_REG_EXIT 7
|
|
||||||
+#define TRACE_DEV_REG_CMDLINE 8
|
|
||||||
+#define TRACE_DEV_REG_CMDLINE_LEN 9
|
|
||||||
+#define TRACE_DEV_REG_MMAP_EXEPATH 10
|
|
||||||
+#define TRACE_DEV_REG_INIT_PID 11
|
|
||||||
+#define TRACE_DEV_REG_INIT_NAME 12
|
|
||||||
+#define TRACE_DEV_REG_CLONE 13
|
|
||||||
+#define TRACE_DEV_REG_UNMAP_START 14
|
|
||||||
+#define TRACE_DEV_REG_UNMAP_END 15
|
|
||||||
+#define TRACE_DEV_REG_NAME 16
|
|
||||||
+#define TRACE_DEV_REG_TGID 17
|
|
||||||
+#define TRACE_DEV_REG_DYN_SYM 50
|
|
||||||
+#define TRACE_DEV_REG_DYN_SYM_ADDR 51
|
|
||||||
+#define TRACE_DEV_REG_REMOVE_ADDR 52
|
|
||||||
+#define TRACE_DEV_REG_ENABLE 100
|
|
||||||
+
|
|
||||||
+static unsigned char __iomem *qt_base;
|
|
||||||
+static int init_called;
|
|
||||||
+
|
|
||||||
+/* PIDs that start before our device registered */
|
|
||||||
+#define MAX_INIT_PIDS 2048
|
|
||||||
+static int tb_next = 0;
|
|
||||||
+static int init_pids[MAX_INIT_PIDS];
|
|
||||||
+static DEFINE_SPINLOCK(qemu_trace_lock);
|
|
||||||
+
|
|
||||||
+void qemu_trace_start(void)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(1, qt_base + (TRACE_DEV_REG_ENABLE << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void qemu_trace_stop(void)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(0, qt_base + (TRACE_DEV_REG_ENABLE << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+int qemu_trace_get_tracing(void)
|
|
||||||
+{
|
|
||||||
+ int val = 0;
|
|
||||||
+ if (qt_base != NULL)
|
|
||||||
+ val = readl(qt_base + (TRACE_DEV_REG_ENABLE << 2));
|
|
||||||
+ return val;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void qemu_trace_add_mapping(unsigned int addr, const char *symbol)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ /* Write the address first, then the symbol name. */
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(addr, qt_base + (TRACE_DEV_REG_DYN_SYM_ADDR << 2));
|
|
||||||
+ writel(symbol, qt_base + (TRACE_DEV_REG_DYN_SYM << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void qemu_trace_remove_mapping(unsigned int addr)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(addr, qt_base + (TRACE_DEV_REG_REMOVE_ADDR << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* trace the context switch */
|
|
||||||
+void qemu_trace_cs(struct task_struct *next)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(task_pid_nr(next), qt_base);
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_cs);
|
|
||||||
+
|
|
||||||
+/* trace the execve */
|
|
||||||
+void qemu_trace_execve(int argc, char __user * __user *argv)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ char page[PAGE_SIZE];
|
|
||||||
+ char *ptr = page;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ while (argc-- > 0) {
|
|
||||||
+ char __user *str;
|
|
||||||
+ int len;
|
|
||||||
+ if (get_user(str, argv ++))
|
|
||||||
+ return;
|
|
||||||
+ len = strnlen_user(str, PAGE_SIZE);
|
|
||||||
+ if (len == 0)
|
|
||||||
+ return;
|
|
||||||
+ if (copy_from_user(ptr, str, len))
|
|
||||||
+ return;
|
|
||||||
+ ptr += len;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (ptr > page) {
|
|
||||||
+ int len = ptr - page;
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(len, qt_base + (TRACE_DEV_REG_CMDLINE_LEN << 2));
|
|
||||||
+ writel(page, qt_base + (TRACE_DEV_REG_CMDLINE << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_execve);
|
|
||||||
+
|
|
||||||
+/* trace the mmap */
|
|
||||||
+void qemu_trace_mmap(struct vm_area_struct *vma)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ char page[PAGE_SIZE];
|
|
||||||
+ char *p;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (vma->vm_file == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ p = d_path(&vma->vm_file->f_path, page, PAGE_SIZE);
|
|
||||||
+ if (IS_ERR(p))
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(vma->vm_start, qt_base + (TRACE_DEV_REG_EXECVE_VMSTART << 2));
|
|
||||||
+ writel(vma->vm_end, qt_base + (TRACE_DEV_REG_EXECVE_VMEND << 2));
|
|
||||||
+ writel(vma->vm_pgoff * PAGE_SIZE, qt_base + (TRACE_DEV_REG_EXECVE_OFFSET << 2));
|
|
||||||
+ writel(p, qt_base + (TRACE_DEV_REG_MMAP_EXEPATH << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_mmap);
|
|
||||||
+
|
|
||||||
+/* trace the munmap */
|
|
||||||
+void qemu_trace_munmap(unsigned long start, unsigned long end)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(start, qt_base + (TRACE_DEV_REG_UNMAP_START << 2));
|
|
||||||
+ writel(end, qt_base + (TRACE_DEV_REG_UNMAP_END << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_munmap);
|
|
||||||
+
|
|
||||||
+/* trace the fork */
|
|
||||||
+void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ if (qt_base == NULL) {
|
|
||||||
+ if (tb_next >= MAX_INIT_PIDS) {
|
|
||||||
+ if (!init_called)
|
|
||||||
+ printk(KERN_ERR
|
|
||||||
+ "QEMU Trace: too many PIDs before "
|
|
||||||
+ "device registered ignoring %d\n",
|
|
||||||
+ forked->pid);
|
|
||||||
+ } else {
|
|
||||||
+ init_pids[tb_next] = task_pid_nr(forked);
|
|
||||||
+ tb_next++;
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ writel(task_tgid_nr(forked), qt_base + (TRACE_DEV_REG_TGID << 2));
|
|
||||||
+ if (clone_flags & CLONE_VM)
|
|
||||||
+ writel(task_pid_nr(forked), qt_base + (TRACE_DEV_REG_CLONE << 2));
|
|
||||||
+ else
|
|
||||||
+ writel(task_pid_nr(forked), qt_base + (TRACE_DEV_REG_FORK << 2));
|
|
||||||
+ }
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_fork);
|
|
||||||
+
|
|
||||||
+/* trace the exit */
|
|
||||||
+void qemu_trace_exit(int code)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(code, qt_base + (TRACE_DEV_REG_EXIT << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_exit);
|
|
||||||
+
|
|
||||||
+/* trace the thread name */
|
|
||||||
+void qemu_trace_thread_name(const char *name)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(name, qt_base + (TRACE_DEV_REG_NAME << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_thread_name);
|
|
||||||
+
|
|
||||||
+/* trace the process name */
|
|
||||||
+void qemu_trace_process_name(const char *name)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+
|
|
||||||
+ if (qt_base == NULL)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(name, qt_base + (TRACE_DEV_REG_NAME << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+}
|
|
||||||
+EXPORT_SYMBOL(qemu_trace_process_name);
|
|
||||||
+
|
|
||||||
+static void qemu_trace_pid_exec(struct task_struct *tsk)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ char page[PAGE_SIZE];
|
|
||||||
+ struct mm_struct *mm = get_task_mm(tsk);
|
|
||||||
+ if (mm == NULL)
|
|
||||||
+ return;
|
|
||||||
+ down_read(&mm->mmap_sem);
|
|
||||||
+ {
|
|
||||||
+ struct vm_area_struct *vma = mm->mmap;
|
|
||||||
+ while (vma) {
|
|
||||||
+ if ((vma->vm_flags & VM_EXEC) && vma->vm_file) {
|
|
||||||
+ char *p;
|
|
||||||
+ p = d_path(&vma->vm_file->f_path, page, PAGE_SIZE);
|
|
||||||
+ if (!IS_ERR(p)) {
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(vma->vm_start, qt_base + (TRACE_DEV_REG_EXECVE_VMSTART << 2));
|
|
||||||
+ writel(vma->vm_end, qt_base + (TRACE_DEV_REG_EXECVE_VMEND << 2));
|
|
||||||
+ writel(vma->vm_pgoff * PAGE_SIZE, qt_base + (TRACE_DEV_REG_EXECVE_OFFSET << 2));
|
|
||||||
+ writel(p, qt_base + (TRACE_DEV_REG_EXECVE_EXEPATH << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ vma = vma->vm_next;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ up_read(&mm->mmap_sem);
|
|
||||||
+ mmput(mm);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qemu_trace_dump_init_threads(void)
|
|
||||||
+{
|
|
||||||
+ unsigned long irq_flags;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < tb_next; i++) {
|
|
||||||
+ struct task_struct *tsk;
|
|
||||||
+ struct pid *pid = find_get_pid(init_pids[i]);
|
|
||||||
+ if (pid == NULL)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ if ((tsk = get_pid_task(pid, PIDTYPE_PID)) != NULL) {
|
|
||||||
+ /* first give the pid and name */
|
|
||||||
+ task_lock(tsk);
|
|
||||||
+ spin_lock_irqsave(&qemu_trace_lock, irq_flags);
|
|
||||||
+ writel(task_tgid_nr(tsk), qt_base + (TRACE_DEV_REG_TGID << 2));
|
|
||||||
+ writel(task_pid_nr(tsk), qt_base + (TRACE_DEV_REG_INIT_PID << 2));
|
|
||||||
+ writel(tsk->comm, qt_base + (TRACE_DEV_REG_INIT_NAME << 2));
|
|
||||||
+ spin_unlock_irqrestore(&qemu_trace_lock, irq_flags);
|
|
||||||
+ task_unlock(tsk);
|
|
||||||
+ /* check if the task has execs */
|
|
||||||
+ qemu_trace_pid_exec(tsk);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qemu_trace_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct resource *r;
|
|
||||||
+
|
|
||||||
+ /* not thread safe, but this should not happen */
|
|
||||||
+ if (qt_base != NULL) {
|
|
||||||
+ printk(KERN_ERR "QEMU TRACE Device: already mapped at %p\n", qt_base);
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
+ if (r == NULL)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ qt_base = ioremap(r->start, PAGE_SIZE);
|
|
||||||
+ printk(KERN_INFO "QEMU TRACE Device: The mapped IO base is %p\n", qt_base);
|
|
||||||
+
|
|
||||||
+ qemu_trace_dump_init_threads();
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int qemu_trace_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ iounmap(qt_base);
|
|
||||||
+ qt_base = NULL;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static struct platform_driver qemu_trace = {
|
|
||||||
+ .probe = qemu_trace_probe,
|
|
||||||
+ .remove = qemu_trace_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "qemu_trace"
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init qemu_trace_dev_init(void)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+ ret = platform_driver_register(&qemu_trace);
|
|
||||||
+ init_called = 1;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void qemu_trace_dev_exit(void)
|
|
||||||
+{
|
|
||||||
+ platform_driver_unregister(&qemu_trace);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+module_init(qemu_trace_dev_init);
|
|
||||||
+module_exit(qemu_trace_dev_exit);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Ye Wen <ywen@google.com>");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/misc/qemutrace/qemu_trace.h
|
|
||||||
@@ -0,0 +1,22 @@
|
|
||||||
+/* drivers/misc/qemutrace/qemu_trace.h
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2007-2008 Google, Inc.
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ *
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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.
|
|
||||||
+ *
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+void qemu_trace_start(void);
|
|
||||||
+void qemu_trace_stop(void);
|
|
||||||
+int qemu_trace_get_tracing(void);
|
|
||||||
+void qemu_trace_add_mapping(unsigned int addr, const char *symbol);
|
|
||||||
+void qemu_trace_remove_mapping(unsigned int addr);
|
|
||||||
+void qemu_trace_process_name(const char *name);
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/misc/qemutrace/qemu_trace_sysfs.c
|
|
||||||
@@ -0,0 +1,182 @@
|
|
||||||
+/* drivers/misc/qemu_sysfs.c
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2007-2008 Google, Inc.
|
|
||||||
+ * Author: Jack Veenstra
|
|
||||||
+ *
|
|
||||||
+ * This software is licensed under the terms of the GNU General Public
|
|
||||||
+ * License version 2, as published by the Free Software Foundation, and
|
|
||||||
+ * may be copied, distributed, and modified under those terms.
|
|
||||||
+ *
|
|
||||||
+ * 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/list.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/miscdevice.h>
|
|
||||||
+#include <linux/sysdev.h>
|
|
||||||
+#include <linux/fs.h>
|
|
||||||
+#include <linux/poll.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/delay.h>
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/wait.h>
|
|
||||||
+#include "qemu_trace.h"
|
|
||||||
+
|
|
||||||
+MODULE_DESCRIPTION("Qemu Trace Driver");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
+MODULE_VERSION("1.0");
|
|
||||||
+
|
|
||||||
+static struct kobject *qemu_trace_kobj;
|
|
||||||
+
|
|
||||||
+static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
|
|
||||||
+{
|
|
||||||
+ int val = qemu_trace_get_tracing();
|
|
||||||
+ buf[0] = '0' + val;
|
|
||||||
+ buf[1] = '\n';
|
|
||||||
+ return 2;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
|
|
||||||
+{
|
|
||||||
+ if (n <= 0)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ if (buf[0] == '0')
|
|
||||||
+ qemu_trace_stop();
|
|
||||||
+ else if (buf[0] == '1')
|
|
||||||
+ qemu_trace_start();
|
|
||||||
+ else
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ return n;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t symbol_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+// We are expecting a string of the form "addr symbol" where 'addr' is a hex address
|
|
||||||
+// (without the leading '0x') and symbol is a newline-terminated string. This symbol
|
|
||||||
+// with its corresponding address will be added to the trace file.
|
|
||||||
+//
|
|
||||||
+// To remove the mapping for (addr, symbol) in the trace file, write just the
|
|
||||||
+// address. As before, the address is in hex without the leading '0x'. It can
|
|
||||||
+// be newline-terminated or zero-terminated.
|
|
||||||
+static ssize_t symbol_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
|
|
||||||
+{
|
|
||||||
+ const char *cp;
|
|
||||||
+ unsigned int addr = 0;
|
|
||||||
+ int len;
|
|
||||||
+ char *sym;
|
|
||||||
+
|
|
||||||
+ if (n <= 0 || buf == NULL)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ for (cp = buf; *cp != ' '; ++cp) {
|
|
||||||
+ unsigned int digit;
|
|
||||||
+
|
|
||||||
+ if (*cp >= '0' && *cp <= '9')
|
|
||||||
+ digit = *cp - '0';
|
|
||||||
+ else if (*cp >= 'a' && *cp <= 'f')
|
|
||||||
+ digit = *cp - 'a' + 10;
|
|
||||||
+ else if (*cp == 0 || *cp == '\n') {
|
|
||||||
+ qemu_trace_remove_mapping(addr);
|
|
||||||
+ return n;
|
|
||||||
+ } else
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ addr = (addr << 4) + digit;
|
|
||||||
+ }
|
|
||||||
+ // Move past the space
|
|
||||||
+ cp += 1;
|
|
||||||
+
|
|
||||||
+ // Copy the string to a new buffer so that we can replace the newline
|
|
||||||
+ // with '\0'.
|
|
||||||
+ len = strlen(cp);
|
|
||||||
+ sym = kzalloc(len + 1, GFP_KERNEL);
|
|
||||||
+ strcpy(sym, cp);
|
|
||||||
+ if (sym[len - 1] == '\n')
|
|
||||||
+ sym[len - 1] = 0;
|
|
||||||
+
|
|
||||||
+ qemu_trace_add_mapping(addr, sym);
|
|
||||||
+ kfree(sym);
|
|
||||||
+ return n;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t process_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* This expects a string that is the process name. If the string contains
|
|
||||||
+ * a trailing newline, that is removed in the emulator tracing code because
|
|
||||||
+ * it is simpler to do it there.
|
|
||||||
+ */
|
|
||||||
+static ssize_t process_name_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
|
|
||||||
+{
|
|
||||||
+ if (n <= 0 || buf == NULL)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ qemu_trace_process_name(buf);
|
|
||||||
+ return n;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+#define qemu_trace_attr(_name) \
|
|
||||||
+static struct kobj_attribute _name##_attr = { \
|
|
||||||
+ .attr = { \
|
|
||||||
+ .name = __stringify(_name), \
|
|
||||||
+ .mode = 0666, \
|
|
||||||
+ }, \
|
|
||||||
+ .show = _name##_show, \
|
|
||||||
+ .store = _name##_store, \
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+qemu_trace_attr(state);
|
|
||||||
+qemu_trace_attr(symbol);
|
|
||||||
+qemu_trace_attr(process_name);
|
|
||||||
+
|
|
||||||
+static struct attribute * qemu_trace_attrs[] = {
|
|
||||||
+ &state_attr.attr,
|
|
||||||
+ &symbol_attr.attr,
|
|
||||||
+ &process_name_attr.attr,
|
|
||||||
+ NULL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct attribute_group qemu_trace_attr_group = {
|
|
||||||
+ .attrs = qemu_trace_attrs,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int __init qemu_trace_init(void)
|
|
||||||
+{
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ qemu_trace_kobj = kobject_create_and_add("qemu_trace", NULL);
|
|
||||||
+ if (qemu_trace_kobj == NULL) {
|
|
||||||
+ printk("qemu_trace_init: kobject_create_and_add failed\n");
|
|
||||||
+ ret = -ENOMEM;
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+ ret = sysfs_create_group(qemu_trace_kobj, &qemu_trace_attr_group);
|
|
||||||
+ if (ret) {
|
|
||||||
+ printk("qemu_trace_init: sysfs_create_group failed\n");
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err:
|
|
||||||
+ kobject_del(qemu_trace_kobj);
|
|
||||||
+ qemu_trace_kobj = NULL;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void __exit qemu_trace_exit(void)
|
|
||||||
+{
|
|
||||||
+ sysfs_remove_group(qemu_trace_kobj, &qemu_trace_attr_group);
|
|
||||||
+ kobject_del(qemu_trace_kobj);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+core_initcall(qemu_trace_init);
|
|
||||||
+module_exit(qemu_trace_exit);
|
|
||||||
--- a/fs/exec.c
|
|
||||||
+++ b/fs/exec.c
|
|
||||||
@@ -59,6 +59,9 @@
|
|
||||||
#include <asm/mmu_context.h>
|
|
||||||
#include <asm/tlb.h>
|
|
||||||
#include "internal.h"
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ void qemu_trace_thread_name(char *name);
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
int core_uses_pid;
|
|
||||||
char core_pattern[CORENAME_MAX_SIZE] = "core";
|
|
||||||
@@ -922,6 +925,9 @@ void set_task_comm(struct task_struct *t
|
|
||||||
task_lock(tsk);
|
|
||||||
strlcpy(tsk->comm, buf, sizeof(tsk->comm));
|
|
||||||
task_unlock(tsk);
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ qemu_trace_thread_name(buf);
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int flush_old_exec(struct linux_binprm * bprm)
|
|
||||||
@@ -1245,6 +1251,10 @@ void free_bprm(struct linux_binprm *bprm
|
|
||||||
kfree(bprm);
|
|
||||||
}
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+extern void qemu_trace_execve(int argc, char __user * __user * argv);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* sys_execve() executes a new program.
|
|
||||||
*/
|
|
||||||
@@ -1324,6 +1334,10 @@ int do_execve(char * filename,
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
current->flags &= ~PF_KTHREAD;
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ qemu_trace_execve(bprm->argc, argv);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
retval = search_binary_handler(bprm,regs);
|
|
||||||
if (retval < 0)
|
|
||||||
goto out;
|
|
||||||
--- a/kernel/exit.c
|
|
||||||
+++ b/kernel/exit.c
|
|
||||||
@@ -60,6 +60,11 @@ DEFINE_TRACE(sched_process_free);
|
|
||||||
DEFINE_TRACE(sched_process_exit);
|
|
||||||
DEFINE_TRACE(sched_process_wait);
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+void qemu_trace_thread_name(char *name);
|
|
||||||
+void qemu_trace_exit(int code);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
static void exit_mm(struct task_struct * tsk);
|
|
||||||
|
|
||||||
static void __unhash_process(struct task_struct *p)
|
|
||||||
@@ -426,6 +431,9 @@ void daemonize(const char *name, ...)
|
|
||||||
va_start(args, name);
|
|
||||||
vsnprintf(current->comm, sizeof(current->comm), name, args);
|
|
||||||
va_end(args);
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ qemu_trace_thread_name(current->comm);
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we were started as result of loading a module, close all of the
|
|
||||||
@@ -1012,6 +1020,12 @@ NORET_TYPE void do_exit(long code)
|
|
||||||
preempt_disable();
|
|
||||||
/* causes final put_task_struct in finish_task_switch(). */
|
|
||||||
tsk->state = TASK_DEAD;
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ /* Emit a trace record for the exit() call. */
|
|
||||||
+ qemu_trace_exit(code);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
schedule();
|
|
||||||
BUG();
|
|
||||||
/* Avoid "noreturn function does return". */
|
|
||||||
--- a/kernel/fork.c
|
|
||||||
+++ b/kernel/fork.c
|
|
||||||
@@ -1323,6 +1323,10 @@ struct task_struct * __cpuinit fork_idle
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+extern void qemu_trace_fork(struct task_struct *forked, unsigned long clone_flags);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Ok, this is the main fork-routine.
|
|
||||||
*
|
|
||||||
@@ -1424,6 +1428,10 @@ long do_fork(unsigned long clone_flags,
|
|
||||||
tracehook_report_clone_complete(trace, regs,
|
|
||||||
clone_flags, nr, p);
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ qemu_trace_fork(p, clone_flags);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
if (clone_flags & CLONE_VFORK) {
|
|
||||||
freezer_do_not_count();
|
|
||||||
wait_for_completion(&vfork);
|
|
||||||
--- a/kernel/sched.c
|
|
||||||
+++ b/kernel/sched.c
|
|
||||||
@@ -2748,6 +2748,10 @@ asmlinkage void schedule_tail(struct tas
|
|
||||||
put_user(task_pid_vnr(current), current->set_child_tid);
|
|
||||||
}
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+void qemu_trace_cs(struct task_struct *next);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* context_switch - switch to the new MM and the new
|
|
||||||
* thread's register state.
|
|
||||||
@@ -2790,6 +2794,11 @@ context_switch(struct rq *rq, struct tas
|
|
||||||
spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ /* Emit a trace record for the context switch. */
|
|
||||||
+ qemu_trace_cs(next);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/* Here we just switch the register state and the stack. */
|
|
||||||
switch_to(prev, next, prev);
|
|
||||||
|
|
||||||
--- a/mm/mmap.c
|
|
||||||
+++ b/mm/mmap.c
|
|
||||||
@@ -906,6 +906,11 @@ void vm_stat_account(struct mm_struct *m
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_PROC_FS */
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+extern void qemu_trace_mmap(struct vm_area_struct * vma);
|
|
||||||
+extern void qemu_trace_munmap(unsigned long start, unsigned long end);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* The caller must hold down_write(current->mm->mmap_sem).
|
|
||||||
*/
|
|
||||||
@@ -1212,6 +1217,10 @@ munmap_back:
|
|
||||||
pgoff = vma->vm_pgoff;
|
|
||||||
vm_flags = vma->vm_flags;
|
|
||||||
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ qemu_trace_mmap(vma);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
if (vma_wants_writenotify(vma))
|
|
||||||
vma->vm_page_prot = vm_get_page_prot(vm_flags & ~VM_SHARED);
|
|
||||||
|
|
||||||
@@ -1938,6 +1947,10 @@ int do_munmap(struct mm_struct *mm, unsi
|
|
||||||
* Remove the vma's, and unmap the actual pages
|
|
||||||
*/
|
|
||||||
detach_vmas_to_be_unmapped(mm, vma, prev, end);
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_QEMU_TRACE
|
|
||||||
+ qemu_trace_munmap(start, end);
|
|
||||||
+#endif
|
|
||||||
unmap_region(mm, vma, prev, start, end);
|
|
||||||
|
|
||||||
/* Fix up all other VM information */
|
|
@ -1,84 +0,0 @@
|
|||||||
From 2309613958ee518f94f1a7ba900e08e604e06048 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Jack Veenstra <veenstra@android.com>
|
|
||||||
Date: Fri, 1 May 2009 18:50:10 -0700
|
|
||||||
Subject: [PATCH 133/134] [ARM] goldfish: qemutrace: Add mmap support.
|
|
||||||
|
|
||||||
This makes a page of data available for writing from user-space to allow
|
|
||||||
the Dalvik interpreter to send method trace information to the emulator.
|
|
||||||
|
|
||||||
Signed-off-by: Jack Veenstra <veenstra@android.com>
|
|
||||||
---
|
|
||||||
drivers/misc/qemutrace/qemu_trace.c | 35 ++++++++++++++++++++++++++++++++++-
|
|
||||||
1 files changed, 34 insertions(+), 1 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/misc/qemutrace/qemu_trace.c
|
|
||||||
+++ b/drivers/misc/qemutrace/qemu_trace.c
|
|
||||||
@@ -53,6 +53,7 @@
|
|
||||||
|
|
||||||
static unsigned char __iomem *qt_base;
|
|
||||||
static int init_called;
|
|
||||||
+static uint32_t qemu_trace_paddr;
|
|
||||||
|
|
||||||
/* PIDs that start before our device registered */
|
|
||||||
#define MAX_INIT_PIDS 2048
|
|
||||||
@@ -330,8 +331,30 @@ static void qemu_trace_dump_init_threads
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int qemu_trace_mmap_fop(struct file *file, struct vm_area_struct *vma)
|
|
||||||
+{
|
|
||||||
+ int ret = io_remap_pfn_range(vma, vma->vm_start,
|
|
||||||
+ (qemu_trace_paddr >> PAGE_SHIFT) + 1,
|
|
||||||
+ PAGE_SIZE, vma->vm_page_prot);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct file_operations qemu_trace_fops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .mmap = qemu_trace_mmap_fop,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct miscdevice qemu_trace_device = {
|
|
||||||
+ .minor = MISC_DYNAMIC_MINOR,
|
|
||||||
+ .name = "qemu_trace",
|
|
||||||
+ .fops = &qemu_trace_fops,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
static int qemu_trace_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
+ int err;
|
|
||||||
struct resource *r;
|
|
||||||
|
|
||||||
/* not thread safe, but this should not happen */
|
|
||||||
@@ -340,18 +363,28 @@ static int qemu_trace_probe(struct platf
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
- if (r == NULL)
|
|
||||||
+ if (r == NULL || r->end - r->start < 2 * PAGE_SIZE - 1)
|
|
||||||
return -EINVAL;
|
|
||||||
+ qemu_trace_paddr = r->start;
|
|
||||||
qt_base = ioremap(r->start, PAGE_SIZE);
|
|
||||||
printk(KERN_INFO "QEMU TRACE Device: The mapped IO base is %p\n", qt_base);
|
|
||||||
|
|
||||||
qemu_trace_dump_init_threads();
|
|
||||||
+ err = misc_register(&qemu_trace_device);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err_misc_register;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
+
|
|
||||||
+err_misc_register:
|
|
||||||
+ iounmap(qt_base);
|
|
||||||
+ qt_base = NULL;
|
|
||||||
+ return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qemu_trace_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
+ misc_deregister(&qemu_trace_device);
|
|
||||||
iounmap(qt_base);
|
|
||||||
qt_base = NULL;
|
|
||||||
return 0;
|
|
@ -1,94 +0,0 @@
|
|||||||
--- a/drivers/mtd/devices/goldfish_nand.c
|
|
||||||
+++ b/drivers/mtd/devices/goldfish_nand.c
|
|
||||||
@@ -65,7 +65,7 @@ static int goldfish_nand_erase(struct mt
|
|
||||||
if(rem)
|
|
||||||
goto invalid_arg;
|
|
||||||
ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
-
|
|
||||||
+
|
|
||||||
if(len % mtd->writesize)
|
|
||||||
goto invalid_arg;
|
|
||||||
len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
|
|
||||||
@@ -94,15 +94,12 @@ static int goldfish_nand_read_oob(struct
|
|
||||||
|
|
||||||
if(ofs + ops->len > mtd->size)
|
|
||||||
goto invalid_arg;
|
|
||||||
- if(ops->datbuf && ops->len && ops->len != mtd->writesize)
|
|
||||||
- goto invalid_arg;
|
|
||||||
if(ops->ooblen + ops->ooboffs > mtd->oobsize)
|
|
||||||
goto invalid_arg;
|
|
||||||
|
|
||||||
rem = do_div(ofs, mtd->writesize);
|
|
||||||
- if(rem)
|
|
||||||
- goto invalid_arg;
|
|
||||||
ofs *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+ ofs += rem;
|
|
||||||
|
|
||||||
if(ops->datbuf)
|
|
||||||
ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
|
|
||||||
@@ -131,7 +128,7 @@ static int goldfish_nand_write_oob(struc
|
|
||||||
goto invalid_arg;
|
|
||||||
if(ops->ooblen + ops->ooboffs > mtd->oobsize)
|
|
||||||
goto invalid_arg;
|
|
||||||
-
|
|
||||||
+
|
|
||||||
rem = do_div(ofs, mtd->writesize);
|
|
||||||
if(rem)
|
|
||||||
goto invalid_arg;
|
|
||||||
@@ -160,15 +157,24 @@ static int goldfish_nand_read(struct mtd
|
|
||||||
|
|
||||||
if(from + len > mtd->size)
|
|
||||||
goto invalid_arg;
|
|
||||||
- if(len != mtd->writesize)
|
|
||||||
- goto invalid_arg;
|
|
||||||
+
|
|
||||||
+ *retlen = 0;
|
|
||||||
|
|
||||||
rem = do_div(from, mtd->writesize);
|
|
||||||
- if(rem)
|
|
||||||
- goto invalid_arg;
|
|
||||||
from *= (mtd->writesize + mtd->oobsize);
|
|
||||||
+ from += rem;
|
|
||||||
|
|
||||||
- *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
|
|
||||||
+ do {
|
|
||||||
+ *retlen += goldfish_nand_cmd(mtd, NAND_CMD_READ, from, min(len, mtd->writesize - rem), buf);
|
|
||||||
+ if (len > mtd->writesize - rem) {
|
|
||||||
+ len -= mtd->writesize - rem;
|
|
||||||
+ buf += mtd->writesize - rem;
|
|
||||||
+ from += mtd->writesize + mtd->oobsize - rem;
|
|
||||||
+ rem = 0;
|
|
||||||
+ } else {
|
|
||||||
+ len = 0;
|
|
||||||
+ }
|
|
||||||
+ } while (len);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
invalid_arg:
|
|
||||||
@@ -184,15 +190,23 @@ static int goldfish_nand_write(struct mt
|
|
||||||
|
|
||||||
if(to + len > mtd->size)
|
|
||||||
goto invalid_arg;
|
|
||||||
- if(len != mtd->writesize)
|
|
||||||
- goto invalid_arg;
|
|
||||||
|
|
||||||
rem = do_div(to, mtd->writesize);
|
|
||||||
if(rem)
|
|
||||||
goto invalid_arg;
|
|
||||||
to *= (mtd->writesize + mtd->oobsize);
|
|
||||||
|
|
||||||
- *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
|
|
||||||
+ *retlen = 0;
|
|
||||||
+ do {
|
|
||||||
+ *retlen += goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, min(len, mtd->writesize), (void *)buf);
|
|
||||||
+ if (len > mtd->writesize) {
|
|
||||||
+ len -= mtd->writesize;
|
|
||||||
+ buf += mtd->writesize;
|
|
||||||
+ to += mtd->writesize + mtd->oobsize;
|
|
||||||
+ } else {
|
|
||||||
+ len = 0;
|
|
||||||
+ }
|
|
||||||
+ } while (len);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
invalid_arg:
|
|
Loading…
Reference in New Issue
Block a user