Merge branch 'experimental/android-3.10' of https://android.googlesource.com/kernel/common into linux-linaro-lsk-android

Conflicts:
	arch/arm/include/asm/smp.h
	arch/arm/kernel/smp.c
	kernel/futex.c
This commit is contained in:
Mark Brown 2013-07-19 10:42:11 +01:00
commit 0b37fbf750
286 changed files with 27136 additions and 607 deletions

121
Documentation/android.txt Normal file
View File

@ -0,0 +1,121 @@
=============
A N D R O I D
=============
Copyright (C) 2009 Google, Inc.
Written by Mike Chan <mike@android.com>
CONTENTS:
---------
1. Android
1.1 Required enabled config options
1.2 Required disabled config options
1.3 Recommended enabled config options
2. Contact
1. Android
==========
Android (www.android.com) is an open source operating system for mobile devices.
This document describes configurations needed to run the Android framework on
top of the Linux kernel.
To see a working defconfig look at msm_defconfig or goldfish_defconfig
which can be found at http://android.git.kernel.org in kernel/common.git
and kernel/msm.git
1.1 Required enabled config options
-----------------------------------
After building a standard defconfig, ensure that these options are enabled in
your .config or defconfig if they are not already. Based off the msm_defconfig.
You should keep the rest of the default options enabled in the defconfig
unless you know what you are doing.
ANDROID_PARANOID_NETWORK
ASHMEM
CONFIG_FB_MODE_HELPERS
CONFIG_FONT_8x16
CONFIG_FONT_8x8
CONFIG_YAFFS_SHORT_NAMES_IN_RAM
DAB
EARLYSUSPEND
FB
FB_CFB_COPYAREA
FB_CFB_FILLRECT
FB_CFB_IMAGEBLIT
FB_DEFERRED_IO
FB_TILEBLITTING
HIGH_RES_TIMERS
INOTIFY
INOTIFY_USER
INPUT_EVDEV
INPUT_GPIO
INPUT_MISC
LEDS_CLASS
LEDS_GPIO
LOCK_KERNEL
LkOGGER
LOW_MEMORY_KILLER
MISC_DEVICES
NEW_LEDS
NO_HZ
POWER_SUPPLY
PREEMPT
RAMFS
RTC_CLASS
RTC_LIB
SWITCH
SWITCH_GPIO
TMPFS
UID_STAT
UID16
USB_FUNCTION
USB_FUNCTION_ADB
USER_WAKELOCK
VIDEO_OUTPUT_CONTROL
WAKELOCK
YAFFS_AUTO_YAFFS2
YAFFS_FS
YAFFS_YAFFS1
YAFFS_YAFFS2
1.2 Required disabled config options
------------------------------------
CONFIG_YAFFS_DISABLE_LAZY_LOAD
DNOTIFY
1.3 Recommended enabled config options
------------------------------
ANDROID_PMEM
PSTORE_CONSOLE
PSTORE_RAM
SCHEDSTATS
DEBUG_PREEMPT
DEBUG_MUTEXES
DEBUG_SPINLOCK_SLEEP
DEBUG_INFO
FRAME_POINTER
CPU_FREQ
CPU_FREQ_TABLE
CPU_FREQ_DEFAULT_GOV_ONDEMAND
CPU_FREQ_GOV_ONDEMAND
CRC_CCITT
EMBEDDED
INPUT_TOUCHSCREEN
I2C
I2C_BOARDINFO
LOG_BUF_SHIFT=17
SERIAL_CORE
SERIAL_CORE_CONSOLE
2. Contact
==========
website: http://android.git.kernel.org
mailing-lists: android-kernel@googlegroups.com

View File

@ -598,6 +598,15 @@ is completely unused; @cgrp->parent is still valid. (Note - can also
be called for a newly-created cgroup if an error occurs after this
subsystem's create() method has been called for the new cgroup).
int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
(cgroup_mutex held by caller)
Called prior to moving a task into a cgroup; if the subsystem
returns an error, this will abort the attach operation. Used
to extend the permission checks - if all subsystems in a cgroup
return 0, the attach will be allowed to proceed, even if the
default permission check (root or same user) fails.
int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
(cgroup_mutex held by caller)

View File

@ -28,6 +28,7 @@ Contents:
2.3 Userspace
2.4 Ondemand
2.5 Conservative
2.6 Interactive
3. The Governor Interface in the CPUfreq Core
@ -218,6 +219,90 @@ a decision on when to decrease the frequency while running in any
speed. Load for frequency increase is still evaluated every
sampling rate.
2.6 Interactive
---------------
The CPUfreq governor "interactive" is designed for latency-sensitive,
interactive workloads. This governor sets the CPU speed depending on
usage, similar to "ondemand" and "conservative" governors, but with a
different set of configurable behaviors.
The tuneable values for this governor are:
target_loads: CPU load values used to adjust speed to influence the
current CPU load toward that value. In general, the lower the target
load, the more often the governor will raise CPU speeds to bring load
below the target. The format is a single target load, optionally
followed by pairs of CPU speeds and CPU loads to target at or above
those speeds. Colons can be used between the speeds and associated
target loads for readability. For example:
85 1000000:90 1700000:99
targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until
1.7GHz and above, at which load 99% is targeted. If speeds are
specified these must appear in ascending order. Higher target load
values are typically specified for higher speeds, that is, target load
values also usually appear in an ascending order. The default is
target load 90% for all speeds.
min_sample_time: The minimum amount of time to spend at the current
frequency before ramping down. Default is 80000 uS.
hispeed_freq: An intermediate "hi speed" at which to initially ramp
when CPU load hits the value specified in go_hispeed_load. If load
stays high for the amount of time specified in above_hispeed_delay,
then speed may be bumped higher. Default is the maximum speed
allowed by the policy at governor initialization time.
go_hispeed_load: The CPU load at which to ramp to hispeed_freq.
Default is 99%.
above_hispeed_delay: When speed is at or above hispeed_freq, wait for
this long before raising speed in response to continued high load.
The format is a single delay value, optionally followed by pairs of
CPU speeds and the delay to use at or above those speeds. Colons can
be used between the speeds and associated delays for readability. For
example:
80000 1300000:200000 1500000:40000
uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay
200000 uS is used until speed 1.5 GHz, at which speed (and above)
delay 40000 uS is used. If speeds are specified these must appear in
ascending order. Default is 20000 uS.
timer_rate: Sample rate for reevaluating CPU load when the CPU is not
idle. A deferrable timer is used, such that the CPU will not be woken
from idle to service this timer until something else needs to run.
(The maximum time to allow deferring this timer when not running at
minimum speed is configurable via timer_slack.) Default is 20000 uS.
timer_slack: Maximum additional time to defer handling the governor
sampling timer beyond timer_rate when running at speeds above the
minimum. For platforms that consume additional power at idle when
CPUs are running at speeds greater than minimum, this places an upper
bound on how long the timer will be deferred prior to re-evaluating
load and dropping speed. For example, if timer_rate is 20000uS and
timer_slack is 10000uS then timers will be deferred for up to 30msec
when not at lowest speed. A value of -1 means defer timers
indefinitely at all speeds. Default is 80000 uS.
boost: If non-zero, immediately boost speed of all CPUs to at least
hispeed_freq until zero is written to this attribute. If zero, allow
CPU speeds to drop below hispeed_freq according to load as usual.
Default is zero.
boostpulse: On each write, immediately boost speed of all CPUs to
hispeed_freq for at least the period of time specified by
boostpulse_duration, after which speeds are allowed to drop below
hispeed_freq according to load as usual.
boostpulse_duration: Length of time to hold CPU speed at hispeed_freq
on a write to boostpulse, before allowing speed to drop according to
load as usual. Default is 80000 uS.
3. The Governor Interface in the CPUfreq Core
=============================================

75
Documentation/sync.txt Normal file
View File

@ -0,0 +1,75 @@
Motivation:
In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display)
a consumer of a buffer needs to know when the producer has finished producing
it. Likewise the producer needs to know when the consumer is finished with the
buffer so it can reuse it. A particular buffer may be consumed by multiple
consumers which will retain the buffer for different amounts of time. In
addition, a consumer may consume multiple buffers atomically.
The sync framework adds an API which allows synchronization between the
producers and consumers in a generic way while also allowing platforms which
have shared hardware synchronization primitives to exploit them.
Goals:
* provide a generic API for expressing synchronization dependencies
* allow drivers to exploit hardware synchronization between hardware
blocks
* provide a userspace API that allows a compositor to manage
dependencies.
* provide rich telemetry data to allow debugging slowdowns and stalls of
the graphics pipeline.
Objects:
* sync_timeline
* sync_pt
* sync_fence
sync_timeline:
A sync_timeline is an abstract monotonically increasing counter. In general,
each driver/hardware block context will have one of these. They can be backed
by the appropriate hardware or rely on the generic sw_sync implementation.
Timelines are only ever created through their specific implementations
(i.e. sw_sync.)
sync_pt:
A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts
have a single timeline parent. They have 3 states: active, signaled, and error.
They start in active state and transition, once, to either signaled (when the
timeline counter advances beyond the sync_pts value) or error state.
sync_fence:
Sync_fences are the primary primitives used by drivers to coordinate
synchronization of their buffers. They are a collection of sync_pts which may
or may not have the same timeline parent. A sync_pt can only exist in one fence
and the fence's list of sync_pts is immutable once created. Fences can be
waited on synchronously or asynchronously. Two fences can also be merged to
create a third fence containing a copy of the two fences sync_pts. Fences are
backed by file descriptors to allow userspace to coordinate the display pipeline
dependencies.
Use:
A driver implementing sync support should have a work submission function which:
* takes a fence argument specifying when to begin work
* asynchronously queues that work to kick off when the fence is signaled
* returns a fence to indicate when its work will be done.
* signals the returned fence once the work is completed.
Consider an imaginary display driver that has the following API:
/*
* assumes buf is ready to be displayed.
* blocks until the buffer is on screen.
*/
void display_buffer(struct dma_buf *buf);
The new API will become:
/*
* will display buf when fence is signaled.
* returns immediately with a fence that will signal when buf
* is no longer displayed.
*/
struct sync_fence* display_buffer(struct dma_buf *buf,
struct sync_fence *fence);

View File

@ -2013,6 +2013,35 @@ will produce:
1) 1.449 us | }
You can disable the hierarchical function call formatting and instead print a
flat list of function entry and return events. This uses the format described
in the Output Formatting section and respects all the trace options that
control that formatting. Hierarchical formatting is the default.
hierachical: echo nofuncgraph-flat > trace_options
flat: echo funcgraph-flat > trace_options
ie:
# tracer: function_graph
#
# entries-in-buffer/entries-written: 68355/68355 #P:2
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
sh-1806 [001] d... 198.843443: graph_ent: func=_raw_spin_lock
sh-1806 [001] d... 198.843445: graph_ent: func=__raw_spin_lock
sh-1806 [001] d..1 198.843447: graph_ret: func=__raw_spin_lock
sh-1806 [001] d..1 198.843449: graph_ret: func=_raw_spin_lock
sh-1806 [001] d..1 198.843451: graph_ent: func=_raw_spin_unlock_irqrestore
sh-1806 [001] d... 198.843453: graph_ret: func=_raw_spin_unlock_irqrestore
You might find other useful features for this tracer in the
following "dynamic ftrace" section such as tracing only specific
functions or tasks.

13
android/configs/README Normal file
View File

@ -0,0 +1,13 @@
The files in this directory are meant to be used as a base for an Android
kernel config. All devices should have the options in android-base.cfg enabled.
While not mandatory, the options in android-recommended.cfg enable advanced
Android features.
Assuming you already have a minimalist defconfig for your device, a possible
way to enable these options would be:
ARCH=<arch> scripts/kconfig/merge_config.sh <path_to>/<device>_defconfig android/configs/android-base.cfg android/configs/android-recommended.cfg
This will generate a .config that can then be used to save a new defconfig or
compile a new kernel with Android features enabled.

View File

@ -0,0 +1,136 @@
# CONFIG_INET_LRO is not set
# CONFIG_MODULES is not set
# CONFIG_OABI_COMPAT is not set
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_INTF_ALARM_DEV=y
CONFIG_ANDROID_LOGGER=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ASHMEM=y
CONFIG_BLK_DEV_DM=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_SCHED=y
CONFIG_DM_CRYPT=y
CONFIG_EMBEDDED=y
CONFIG_EXPERIMENTAL=y
CONFIG_FB=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_IPCOMP=y
CONFIG_INET=y
CONFIG_INET_ESP=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_MANGLE=y
CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
CONFIG_IPV6_PRIVACY=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARP_MANGLE=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_MATCH_AH=y
CONFIG_IP_NF_MATCH_ECN=y
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_TARGET_REJECT_SKERR=y
CONFIG_NET=y
CONFIG_NETDEVICES=y
CONFIG_NETFILTER=y
CONFIG_NETFILTER_TPROXY=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
CONFIG_NETFILTER_XT_MATCH_HELPER=y
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
CONFIG_NETFILTER_XT_MATCH_LENGTH=y
CONFIG_NETFILTER_XT_MATCH_LIMIT=y
CONFIG_NETFILTER_XT_MATCH_MAC=y
CONFIG_NETFILTER_XT_MATCH_MARK=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
CONFIG_NETFILTER_XT_MATCH_STRING=y
CONFIG_NETFILTER_XT_MATCH_TIME=y
CONFIG_NETFILTER_XT_MATCH_U32=y
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NET_CLS_ACT=y
CONFIG_NET_CLS_U32=y
CONFIG_NET_EMATCH=y
CONFIG_NET_EMATCH_U32=y
CONFIG_NET_KEY=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_HTB=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_AMANDA=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_FTP=y
CONFIG_NF_CONNTRACK_H323=y
CONFIG_NF_CONNTRACK_IPV4=y
CONFIG_NF_CONNTRACK_IPV6=y
CONFIG_NF_CONNTRACK_IRC=y
CONFIG_NF_CONNTRACK_NETBIOS_NS=y
CONFIG_NF_CONNTRACK_PPTP=y
CONFIG_NF_CONNTRACK_SANE=y
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_NF_CT_NETLINK=y
CONFIG_NF_CT_PROTO_DCCP=y
CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_NAT=y
CONFIG_NO_HZ=y
CONFIG_PACKET=y
CONFIG_PM_AUTOSLEEP=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PPP=y
CONFIG_PPPOLAC=y
CONFIG_PPPOPNS=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
CONFIG_PPP_MPPE=y
CONFIG_PREEMPT=y
CONFIG_RESOURCE_COUNTERS=y
CONFIG_RTC_CLASS=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_STAGING=y
CONFIG_SWITCH=y
CONFIG_SYNC=y
CONFIG_SYSVIPC=y
CONFIG_TUN=y
CONFIG_UNIX=y
CONFIG_USB_GADGET=y
CONFIG_USB_G_ANDROID=y
CONFIG_USB_OTG_WAKELOCK=y
CONFIG_XFRM_USER=y

View File

@ -0,0 +1,118 @@
CONFIG_PANIC_TIMEOUT=5
CONFIG_KALLSYMS_ALL=y
CONFIG_PERF_EVENTS=y
CONFIG_COMPACTION=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_NF_CONNTRACK_SIP is not set
# CONFIG_PM_WAKELOCKS_GC is not set
# CONFIG_VT is not set
CONFIG_ANDROID_RAM_CONSOLE=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BATTERY_ANDROID=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_DM_UEVENT=y
CONFIG_DRAGONRISE_FF=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_FUSE_FS=y
CONFIG_GREENASIA_FF=y
CONFIG_HID_A4TECH=y
CONFIG_HID_ACRUX=y
CONFIG_HID_ACRUX_FF=y
CONFIG_HID_APPLE=y
CONFIG_HID_BELKIN=y
CONFIG_HID_CHERRY=y
CONFIG_HID_CHICONY=y
CONFIG_HID_CYPRESS=y
CONFIG_HID_DRAGONRISE=y
CONFIG_HID_ELECOM=y
CONFIG_HID_EMS_FF=y
CONFIG_HID_EZKEY=y
CONFIG_HID_GREENASIA=y
CONFIG_HID_GYRATION=y
CONFIG_HID_HOLTEK=y
CONFIG_HID_KENSINGTON=y
CONFIG_HID_KEYTOUCH=y
CONFIG_HID_KYE=y
CONFIG_HID_LCPOWER=y
CONFIG_HID_LOGITECH=y
CONFIG_HID_LOGITECH_DJ=y
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MONTEREY=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_NTRIG=y
CONFIG_HID_ORTEK=y
CONFIG_HID_PANTHERLORD=y
CONFIG_HID_PETALYNX=y
CONFIG_HID_PICOLCD=y
CONFIG_HID_PRIMAX=y
CONFIG_HID_PRODIKEYS=y
CONFIG_HID_ROCCAT=y
CONFIG_HID_SAITEK=y
CONFIG_HID_SAMSUNG=y
CONFIG_HID_SMARTJOYPLUS=y
CONFIG_HID_SONY=y
CONFIG_HID_SPEEDLINK=y
CONFIG_HID_SUNPLUS=y
CONFIG_HID_THRUSTMASTER=y
CONFIG_HID_TIVO=y
CONFIG_HID_TOPSEED=y
CONFIG_HID_TWINHAN=y
CONFIG_HID_UCLOGIC=y
CONFIG_HID_WACOM=y
CONFIG_HID_WALTOP=y
CONFIG_HID_WIIMOTE=y
CONFIG_HID_ZEROPLUS=y
CONFIG_HID_ZYDACRON=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_GPIO=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_INPUT_KEYCHORD=y
CONFIG_INPUT_KEYRESET=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_TABLET=y
CONFIG_INPUT_UINPUT=y
CONFIG_ION=y
CONFIG_JOYSTICK_XPAD=y
CONFIG_JOYSTICK_XPAD_FF=y
CONFIG_JOYSTICK_XPAD_LEDS=y
CONFIG_KSM=y
CONFIG_LOGIG940_FF=y
CONFIG_LOGIRUMBLEPAD2_FF=y
CONFIG_LOGITECH_FF=y
CONFIG_MD=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MSDOS_FS=y
CONFIG_PANTHERLORD_FF=y
CONFIG_PM_DEBUG=y
CONFIG_PM_RUNTIME=y
CONFIG_PM_WAKELOCKS_LIMIT=0
CONFIG_POWER_SUPPLY=y
CONFIG_SCHEDSTATS=y
CONFIG_SCHED_TRACER=y
CONFIG_SMARTJOYPLUS_FF=y
CONFIG_SND=y
CONFIG_SOUND=y
CONFIG_SUSPEND_TIME=y
CONFIG_TABLET_USB_ACECAD=y
CONFIG_TABLET_USB_AIPTEK=y
CONFIG_TABLET_USB_GTCO=y
CONFIG_TABLET_USB_HANWANG=y
CONFIG_TABLET_USB_KBTAB=y
CONFIG_TABLET_USB_WACOM=y
CONFIG_TIMER_STATS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_UHID=y
CONFIG_UID_STAT=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_HIDDEV=y
CONFIG_USB_USBNET=y
CONFIG_VFAT_FS=y

View File

@ -1928,6 +1928,15 @@ config XEN
help
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
config ARM_FLUSH_CONSOLE_ON_RESTART
bool "Force flush the console on restart"
help
If the console is locked while the system is rebooted, the messages
in the temporary logbuffer would not have propogated to all the
console drivers. This option forces the console lock to be
released if it failed to be acquired, which will cause all the
pending messages to be flushed.
endmenu
menu "Boot options"
@ -1957,6 +1966,21 @@ config DEPRECATED_PARAM_STRUCT
This was deprecated in 2001 and announced to live on for 5 years.
Some old boot loaders still use this way.
config BUILD_ARM_APPENDED_DTB_IMAGE
bool "Build a concatenated zImage/dtb by default"
depends on OF
help
Enabling this option will cause a concatenated zImage and list of
DTBs to be built by default (instead of a standalone zImage.)
The image will built in arch/arm/boot/zImage-dtb
config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES
string "Default dtb names"
depends on BUILD_ARM_APPENDED_DTB_IMAGE
help
Space separated list of names of dtbs to append when
building a concatenated zImage-dtb.
# Compressed boot loader in ROM. Yes, we really want to ask about
# TEXT and BSS so we preserve their values in the config files.
config ZBOOT_ROM_TEXT

View File

@ -63,6 +63,27 @@ config DEBUG_USER
8 - SIGSEGV faults
16 - SIGBUS faults
config DEBUG_RODATA
bool "Write protect kernel text section"
default n
depends on DEBUG_KERNEL && MMU
---help---
Mark the kernel text section as write-protected in the pagetables,
in order to catch accidental (and incorrect) writes to such const
data. This will cause the size of the kernel, plus up to 4MB, to
be mapped as pages instead of sections, which will increase TLB
pressure.
If in doubt, say "N".
config DEBUG_RODATA_TEST
bool "Testcase for the DEBUG_RODATA feature"
depends on DEBUG_RODATA
default n
---help---
This option enables a testcase for the DEBUG_RODATA
feature.
If in doubt, say "N"
# These options are only for real kernel hackers who want to get their hands dirty.
config DEBUG_LL
bool "Kernel low-level debugging functions (read help!)"

View File

@ -264,6 +264,8 @@ libs-y := arch/arm/lib/ $(libs-y)
# Default target when executing plain make
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y)
KBUILD_IMAGE := zImage-dtb
else
KBUILD_IMAGE := zImage
endif
@ -295,6 +297,9 @@ zinstall uinstall install: vmlinux
dtbs: scripts
$(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs
zImage-dtb: vmlinux scripts dtbs
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
# We use MRPROPER_FILES and CLEAN_FILES now
archclean:
$(Q)$(MAKE) $(clean)=$(boot)

View File

@ -4,3 +4,4 @@ xipImage
bootpImage
uImage
*.dtb
zImage-dtb

View File

@ -27,6 +27,14 @@ export ZRELADDR INITRD_PHYS PARAMS_PHYS
targets := Image zImage xipImage bootpImage uImage
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
ifneq ($(DTB_NAMES),)
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
else
DTB_LIST := $(dtb-y)
endif
DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST))
ifeq ($(CONFIG_XIP_KERNEL),y)
$(obj)/xipImage: vmlinux FORCE
@ -55,6 +63,10 @@ $(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@$(kecho) ' Kernel: $@ is ready'
$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE
$(call if_changed,cat)
@echo ' Kernel: $@ is ready'
endif
ifneq ($(LOADADDR),)

View File

@ -717,6 +717,8 @@ __armv7_mmu_cache_on:
bic r6, r6, #1 << 31 @ 32-bit translation system
bic r6, r6, #3 << 0 @ use only ttbr0
mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer
mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
mcr p15, 0, r0, c7, c5, 4 @ ISB
mcrne p15, 0, r1, c3, c0, 0 @ load domain access control
mcrne p15, 0, r6, c2, c0, 2 @ load ttb control
#endif

View File

@ -217,13 +217,20 @@ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
wm8850-w70v2.dtb
dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
ifneq ($(DTB_NAMES),)
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
else
DTB_LIST := $(dtb-y)
endif
targets += dtbs
targets += $(dtb-y)
targets += $(DTB_LIST)
endif
# *.dtb used to be generated in the directory above. Clean out the
# old build results so people don't accidentally use them.
dtbs: $(addprefix $(obj)/, $(dtb-y))
dtbs: $(addprefix $(obj)/, $(DTB_LIST))
$(Q)rm -f $(obj)/../*.dtb
clean-files := *.dtb

View File

@ -17,3 +17,53 @@ config SHARP_PARAM
config SHARP_SCOOP
bool
config FIQ_GLUE
bool
select FIQ
config FIQ_DEBUGGER
bool "FIQ Mode Serial Debugger"
select FIQ
select FIQ_GLUE
default n
help
The FIQ serial debugger can accept commands even when the
kernel is unresponsive due to being stuck with interrupts
disabled.
config FIQ_DEBUGGER_NO_SLEEP
bool "Keep serial debugger active"
depends on FIQ_DEBUGGER
default n
help
Enables the serial debugger at boot. Passing
fiq_debugger.no_sleep on the kernel commandline will
override this config option.
config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
bool "Don't disable wakeup IRQ when debugger is active"
depends on FIQ_DEBUGGER
default n
help
Don't disable the wakeup irq when enabling the uart clock. This will
cause extra interrupts, but it makes the serial debugger usable with
on some MSM radio builds that ignore the uart clock request in power
collapse.
config FIQ_DEBUGGER_CONSOLE
bool "Console on FIQ Serial Debugger port"
depends on FIQ_DEBUGGER
default n
help
Enables a console so that printk messages are displayed on
the debugger serial port as the occur.
config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
bool "Put the FIQ debugger into console mode by default"
depends on FIQ_DEBUGGER_CONSOLE
default n
help
If enabled, this puts the fiq debugger into console mode by default.
Otherwise, the fiq debugger will start out in debug mode.

View File

@ -4,6 +4,8 @@
obj-y += firmware.o
obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o
obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o
obj-$(CONFIG_ICST) += icst.o
obj-$(CONFIG_SA1111) += sa1111.o
obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
/*
* arch/arm/common/fiq_debugger_ringbuf.c
*
* simple lockless ringbuffer
*
* Copyright (C) 2010 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/kernel.h>
#include <linux/slab.h>
struct fiq_debugger_ringbuf {
int len;
int head;
int tail;
u8 buf[];
};
static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len)
{
struct fiq_debugger_ringbuf *rbuf;
rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL);
if (rbuf == NULL)
return NULL;
rbuf->len = len;
rbuf->head = 0;
rbuf->tail = 0;
smp_mb();
return rbuf;
}
static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf)
{
kfree(rbuf);
}
static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf)
{
int level = rbuf->head - rbuf->tail;
if (level < 0)
level = rbuf->len + level;
return level;
}
static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf)
{
return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1;
}
static inline u8
fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i)
{
return rbuf->buf[(rbuf->tail + i) % rbuf->len];
}
static inline int
fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count)
{
count = min(count, fiq_debugger_ringbuf_level(rbuf));
rbuf->tail = (rbuf->tail + count) % rbuf->len;
smp_mb();
return count;
}
static inline int
fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum)
{
if (fiq_debugger_ringbuf_room(rbuf) == 0)
return 0;
rbuf->buf[rbuf->head] = datum;
smp_mb();
rbuf->head = (rbuf->head + 1) % rbuf->len;
smp_mb();
return 1;
}

111
arch/arm/common/fiq_glue.S Normal file
View File

@ -0,0 +1,111 @@
/*
* 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/linkage.h>
#include <asm/assembler.h>
.text
.global fiq_glue_end
/* fiq stack: r0-r15,cpsr,spsr of interrupted mode */
ENTRY(fiq_glue)
/* store pc, cpsr from previous mode */
mrs r12, spsr
sub r11, lr, #4
subs r10, #1
bne nested_fiq
stmfd sp!, {r11-r12, lr}
/* store r8-r14 from previous mode */
sub sp, sp, #(7 * 4)
stmia sp, {r8-r14}^
nop
/* store r0-r7 from previous mode */
stmfd sp!, {r0-r7}
/* setup func(data,regs) arguments */
mov r0, r9
mov r1, sp
mov r3, r8
mov r7, sp
/* Get sp and lr from non-user modes */
and r4, r12, #MODE_MASK
cmp r4, #USR_MODE
beq fiq_from_usr_mode
mov r7, sp
orr r4, r4, #(PSR_I_BIT | PSR_F_BIT)
msr cpsr_c, r4
str sp, [r7, #(4 * 13)]
str lr, [r7, #(4 * 14)]
mrs r5, spsr
str r5, [r7, #(4 * 17)]
cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
/* use fiq stack if we reenter this mode */
subne sp, r7, #(4 * 3)
fiq_from_usr_mode:
msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT)
mov r2, sp
sub sp, r7, #12
stmfd sp!, {r2, ip, lr}
/* call func(data,regs) */
blx r3
ldmfd sp, {r2, ip, lr}
mov sp, r2
/* restore/discard saved state */
cmp r4, #USR_MODE
beq fiq_from_usr_mode_exit
msr cpsr_c, r4
ldr sp, [r7, #(4 * 13)]
ldr lr, [r7, #(4 * 14)]
msr spsr_cxsf, r5
fiq_from_usr_mode_exit:
msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
ldmfd sp!, {r0-r7}
add sp, sp, #(7 * 4)
ldmfd sp!, {r11-r12, lr}
exit_fiq:
msr spsr_cxsf, r12
add r10, #1
movs pc, r11
nested_fiq:
orr r12, r12, #(PSR_F_BIT)
b exit_fiq
fiq_glue_end:
ENTRY(fiq_glue_setup) /* func, data, sp */
mrs r3, cpsr
msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
movs r8, r0
mov r9, r1
mov sp, r2
moveq r10, #0
movne r10, #1
msr cpsr_c, r3
bx lr

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2010 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/kernel.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <asm/fiq.h>
#include <asm/fiq_glue.h>
extern unsigned char fiq_glue, fiq_glue_end;
extern void fiq_glue_setup(void *func, void *data, void *sp);
static struct fiq_handler fiq_debbuger_fiq_handler = {
.name = "fiq_glue",
};
DEFINE_PER_CPU(void *, fiq_stack);
static struct fiq_glue_handler *current_handler;
static DEFINE_MUTEX(fiq_glue_lock);
static void fiq_glue_setup_helper(void *info)
{
struct fiq_glue_handler *handler = info;
fiq_glue_setup(handler->fiq, handler,
__get_cpu_var(fiq_stack) + THREAD_START_SP);
}
int fiq_glue_register_handler(struct fiq_glue_handler *handler)
{
int ret;
int cpu;
if (!handler || !handler->fiq)
return -EINVAL;
mutex_lock(&fiq_glue_lock);
if (fiq_stack) {
ret = -EBUSY;
goto err_busy;
}
for_each_possible_cpu(cpu) {
void *stack;
stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
if (WARN_ON(!stack)) {
ret = -ENOMEM;
goto err_alloc_fiq_stack;
}
per_cpu(fiq_stack, cpu) = stack;
}
ret = claim_fiq(&fiq_debbuger_fiq_handler);
if (WARN_ON(ret))
goto err_claim_fiq;
current_handler = handler;
on_each_cpu(fiq_glue_setup_helper, handler, true);
set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue);
mutex_unlock(&fiq_glue_lock);
return 0;
err_claim_fiq:
err_alloc_fiq_stack:
for_each_possible_cpu(cpu) {
__free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER);
per_cpu(fiq_stack, cpu) = NULL;
}
err_busy:
mutex_unlock(&fiq_glue_lock);
return ret;
}
/**
* fiq_glue_resume - Restore fiqs after suspend or low power idle states
*
* This must be called before calling local_fiq_enable after returning from a
* power state where the fiq mode registers were lost. If a driver provided
* a resume hook when it registered the handler it will be called.
*/
void fiq_glue_resume(void)
{
if (!current_handler)
return;
fiq_glue_setup(current_handler->fiq, current_handler,
__get_cpu_var(fiq_stack) + THREAD_START_SP);
if (current_handler->resume)
current_handler->resume(current_handler);
}

View File

@ -16,6 +16,7 @@
#include <asm/shmparam.h>
#include <asm/cachetype.h>
#include <asm/outercache.h>
#include <asm/rodata.h>
#define CACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT)

View File

@ -0,0 +1,64 @@
/*
* arch/arm/include/asm/fiq_debugger.h
*
* Copyright (C) 2010 Google, Inc.
* Author: Colin Cross <ccross@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.
*
*/
#ifndef _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_
#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_
#include <linux/serial_core.h>
#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR
#define FIQ_DEBUGGER_BREAK 0x00ff0100
#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq"
#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal"
#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup"
/**
* struct fiq_debugger_pdata - fiq debugger platform data
* @uart_resume: used to restore uart state right before enabling
* the fiq.
* @uart_enable: Do the work necessary to communicate with the uart
* hw (enable clocks, etc.). This must be ref-counted.
* @uart_disable: Do the work necessary to disable the uart hw
* (disable clocks, etc.). This must be ref-counted.
* @uart_dev_suspend: called during PM suspend, generally not needed
* for real fiq mode debugger.
* @uart_dev_resume: called during PM resume, generally not needed
* for real fiq mode debugger.
*/
struct fiq_debugger_pdata {
int (*uart_init)(struct platform_device *pdev);
void (*uart_free)(struct platform_device *pdev);
int (*uart_resume)(struct platform_device *pdev);
int (*uart_getc)(struct platform_device *pdev);
void (*uart_putc)(struct platform_device *pdev, unsigned int c);
void (*uart_flush)(struct platform_device *pdev);
void (*uart_enable)(struct platform_device *pdev);
void (*uart_disable)(struct platform_device *pdev);
int (*uart_dev_suspend)(struct platform_device *pdev);
int (*uart_dev_resume)(struct platform_device *pdev);
void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq,
bool enable);
void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq);
void (*force_irq)(struct platform_device *pdev, unsigned int irq);
void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq);
};
#endif

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2010 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 __ASM_FIQ_GLUE_H
#define __ASM_FIQ_GLUE_H
struct fiq_glue_handler {
void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp);
void (*resume)(struct fiq_glue_handler *h);
};
int fiq_glue_register_handler(struct fiq_glue_handler *handler);
#ifdef CONFIG_FIQ_GLUE
void fiq_glue_resume(void);
#else
static inline void fiq_glue_resume(void) {}
#endif
#endif

View File

@ -5,7 +5,7 @@
#include <linux/threads.h>
#include <asm/irq.h>
#define NR_IPI 7
#define NR_IPI 8
typedef struct {
unsigned int __softirq_pending;

View File

@ -66,6 +66,7 @@
#define L2X0_STNDBY_MODE_EN (1 << 0)
/* Registers shifts and masks */
#define L2X0_CACHE_ID_REV_MASK (0x3f)
#define L2X0_CACHE_ID_PART_MASK (0xf << 6)
#define L2X0_CACHE_ID_PART_L210 (1 << 6)
#define L2X0_CACHE_ID_PART_L310 (3 << 6)
@ -106,6 +107,8 @@
#define L2X0_WAY_SIZE_SHIFT 3
#define REV_PL310_R2P0 4
#ifndef __ASSEMBLY__
extern void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask);
#if defined(CONFIG_CACHE_L2X0) && defined(CONFIG_OF)

View File

@ -17,15 +17,23 @@
#define TRACER_ACCESSED_BIT 0
#define TRACER_RUNNING_BIT 1
#define TRACER_CYCLE_ACC_BIT 2
#define TRACER_TRACE_DATA_BIT 3
#define TRACER_TIMESTAMP_BIT 4
#define TRACER_BRANCHOUTPUT_BIT 5
#define TRACER_RETURN_STACK_BIT 6
#define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT)
#define TRACER_RUNNING BIT(TRACER_RUNNING_BIT)
#define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT)
#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT)
#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT)
#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT)
#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT)
#define TRACER_TIMEOUT 10000
#define etm_writel(t, v, x) \
(__raw_writel((v), (t)->etm_regs + (x)))
#define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x)))
#define etm_writel(t, id, v, x) \
(__raw_writel((v), (t)->etm_regs[(id)] + (x)))
#define etm_readl(t, id, x) (__raw_readl((t)->etm_regs[(id)] + (x)))
/* CoreSight Management Registers */
#define CSMR_LOCKACCESS 0xfb0
@ -43,7 +51,7 @@
#define ETMCTRL_POWERDOWN 1
#define ETMCTRL_PROGRAM (1 << 10)
#define ETMCTRL_PORTSEL (1 << 11)
#define ETMCTRL_DO_CONTEXTID (3 << 14)
#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14)
#define ETMCTRL_PORTMASK1 (7 << 4)
#define ETMCTRL_PORTMASK2 (1 << 21)
#define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2)
@ -55,9 +63,12 @@
#define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR)
#define ETMCTRL_BRANCH_OUTPUT (1 << 8)
#define ETMCTRL_CYCLEACCURATE (1 << 12)
#define ETMCTRL_TIMESTAMP_EN (1 << 28)
#define ETMCTRL_RETURN_STACK_EN (1 << 29)
/* ETM configuration code register */
#define ETMR_CONFCODE (0x04)
#define ETMCCR_ETMIDR_PRESENT BIT(31)
/* ETM trace start/stop resource control register */
#define ETMR_TRACESSCTRL (0x18)
@ -113,10 +124,25 @@
#define ETMR_TRACEENCTRL 0x24
#define ETMTE_INCLEXCL BIT(24)
#define ETMR_TRACEENEVT 0x20
#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \
ETMCTRL_DATA_DO_ADDR | \
ETMCTRL_BRANCH_OUTPUT | \
ETMCTRL_DO_CONTEXTID)
#define ETMR_VIEWDATAEVT 0x30
#define ETMR_VIEWDATACTRL1 0x34
#define ETMR_VIEWDATACTRL2 0x38
#define ETMR_VIEWDATACTRL3 0x3c
#define ETMVDC3_EXCLONLY BIT(16)
#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT)
#define ETMR_ID 0x1e4
#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff)
#define ETMIDR_VERSION_3_1 0x21
#define ETMIDR_VERSION_PFT_1_0 0x30
#define ETMR_CCE 0x1e8
#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23)
#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22)
#define ETMR_TRACEIDR 0x200
/* ETM management registers, "ETM Architecture", 3.5.24 */
#define ETMMR_OSLAR 0x300
@ -140,14 +166,16 @@
#define ETBFF_TRIGIN BIT(8)
#define ETBFF_TRIGEVT BIT(9)
#define ETBFF_TRIGFL BIT(10)
#define ETBFF_STOPFL BIT(12)
#define etb_writel(t, v, x) \
(__raw_writel((v), (t)->etb_regs + (x)))
#define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x)))
#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0)
#define etm_unlock(t) \
do { etm_writel((t), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)
#define etm_lock(t, id) \
do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0)
#define etm_unlock(t, id) \
do { etm_writel((t), (id), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0)
#define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0)
#define etb_unlock(t) \

View File

@ -35,6 +35,9 @@ extern void (*handle_arch_irq)(struct pt_regs *);
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
#endif
void arch_trigger_all_cpu_backtrace(void);
#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
#endif
#endif

View File

@ -0,0 +1,28 @@
/*
* arch/arm/include/asm/mach/mmc.h
*/
#ifndef ASMARM_MACH_MMC_H
#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 */
int built_in; /* built-in device flag */
int card_present; /* card detect state */
u32 (*translate_vdd)(struct device *, unsigned int);
unsigned int (*status)(struct device *);
struct embedded_sdio_data *embedded_sdio;
int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id);
};
#endif

View File

@ -0,0 +1,32 @@
/*
* arch/arm/include/asm/rodata.h
*
* Copyright (C) 2011 Google, Inc.
*
* Author: Colin Cross <ccross@android.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ASMARM_RODATA_H
#define _ASMARM_RODATA_H
#ifndef __ASSEMBLY__
#ifdef CONFIG_DEBUG_RODATA
int set_memory_rw(unsigned long virt, int numpages);
int set_memory_ro(unsigned long virt, int numpages);
void mark_rodata_ro(void);
void set_kernel_text_rw(void);
void set_kernel_text_ro(void);
#else
static inline void set_kernel_text_rw(void) { }
static inline void set_kernel_text_ro(void) { }
#endif
#endif
#endif

View File

@ -82,6 +82,7 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask);
extern int register_ipi_completion(struct completion *completion, int cpu);
extern void smp_send_all_cpu_backtrace(void);
struct smp_operations {
#ifdef CONFIG_SMP

View File

@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/clk.h>
@ -37,26 +38,37 @@ MODULE_AUTHOR("Alexander Shishkin");
struct tracectx {
unsigned int etb_bufsz;
void __iomem *etb_regs;
void __iomem *etm_regs;
void __iomem **etm_regs;
int etm_regs_count;
unsigned long flags;
int ncmppairs;
int etm_portsz;
int etm_contextid_size;
u32 etb_fc;
unsigned long range_start;
unsigned long range_end;
unsigned long data_range_start;
unsigned long data_range_end;
bool dump_initial_etb;
struct device *dev;
struct clk *emu_clk;
struct mutex mutex;
};
static struct tracectx tracer;
static struct tracectx tracer = {
.range_start = (unsigned long)_stext,
.range_end = (unsigned long)_etext,
};
static inline bool trace_isrunning(struct tracectx *t)
{
return !!(t->flags & TRACER_RUNNING);
}
static int etm_setup_address_range(struct tracectx *t, int n,
static int etm_setup_address_range(struct tracectx *t, int id, int n,
unsigned long start, unsigned long end, int exclude, int data)
{
u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \
u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY |
ETMAAT_NOVALCMP;
if (n < 1 || n > t->ncmppairs)
@ -72,95 +84,185 @@ static int etm_setup_address_range(struct tracectx *t, int n,
flags |= ETMAAT_IEXEC;
/* first comparator for the range */
etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2));
etm_writel(t, start, ETMR_COMP_VAL(n * 2));
etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2));
etm_writel(t, id, start, ETMR_COMP_VAL(n * 2));
/* second comparator is right next to it */
etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1));
etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1));
etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1));
flags = exclude ? ETMTE_INCLEXCL : 0;
etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL);
if (data) {
flags = exclude ? ETMVDC3_EXCLONLY : 0;
if (exclude)
n += 8;
etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3);
} else {
flags = exclude ? ETMTE_INCLEXCL : 0;
etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL);
}
return 0;
}
static int trace_start_etm(struct tracectx *t, int id)
{
u32 v;
unsigned long timeout = TRACER_TIMEOUT;
v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz);
v |= ETMCTRL_CONTEXTIDSIZE(t->etm_contextid_size);
if (t->flags & TRACER_CYCLE_ACC)
v |= ETMCTRL_CYCLEACCURATE;
if (t->flags & TRACER_BRANCHOUTPUT)
v |= ETMCTRL_BRANCH_OUTPUT;
if (t->flags & TRACER_TRACE_DATA)
v |= ETMCTRL_DATA_DO_ADDR;
if (t->flags & TRACER_TIMESTAMP)
v |= ETMCTRL_TIMESTAMP_EN;
if (t->flags & TRACER_RETURN_STACK)
v |= ETMCTRL_RETURN_STACK_EN;
etm_unlock(t, id);
etm_writel(t, id, v, ETMR_CTRL);
while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
etm_lock(t, id);
return -EFAULT;
}
if (t->range_start || t->range_end)
etm_setup_address_range(t, id, 1,
t->range_start, t->range_end, 0, 0);
else
etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL);
etm_writel(t, id, 0, ETMR_TRACEENCTRL2);
etm_writel(t, id, 0, ETMR_TRACESSCTRL);
etm_writel(t, id, 0x6f, ETMR_TRACEENEVT);
etm_writel(t, id, 0, ETMR_VIEWDATACTRL1);
etm_writel(t, id, 0, ETMR_VIEWDATACTRL2);
if (t->data_range_start || t->data_range_end)
etm_setup_address_range(t, id, 2, t->data_range_start,
t->data_range_end, 0, 1);
else
etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3);
etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT);
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
etm_writel(t, id, v, ETMR_CTRL);
timeout = TRACER_TIMEOUT;
while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n");
etm_lock(t, id);
return -EFAULT;
}
etm_lock(t, id);
return 0;
}
static int trace_start(struct tracectx *t)
{
u32 v;
unsigned long timeout = TRACER_TIMEOUT;
int ret;
int id;
u32 etb_fc = t->etb_fc;
etb_unlock(t);
etb_writel(t, 0, ETBR_FORMATTERCTRL);
t->dump_initial_etb = false;
etb_writel(t, 0, ETBR_WRITEADDR);
etb_writel(t, etb_fc, ETBR_FORMATTERCTRL);
etb_writel(t, 1, ETBR_CTRL);
etb_lock(t);
/* configure etm */
v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz);
if (t->flags & TRACER_CYCLE_ACC)
v |= ETMCTRL_CYCLEACCURATE;
etm_unlock(t);
etm_writel(t, v, ETMR_CTRL);
while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
etm_lock(t);
return -EFAULT;
/* configure etm(s) */
for (id = 0; id < t->etm_regs_count; id++) {
ret = trace_start_etm(t, id);
if (ret)
return ret;
}
etm_setup_address_range(t, 1, (unsigned long)_stext,
(unsigned long)_etext, 0, 0);
etm_writel(t, 0, ETMR_TRACEENCTRL2);
etm_writel(t, 0, ETMR_TRACESSCTRL);
etm_writel(t, 0x6f, ETMR_TRACEENEVT);
v &= ~ETMCTRL_PROGRAM;
v |= ETMCTRL_PORTSEL;
etm_writel(t, v, ETMR_CTRL);
timeout = TRACER_TIMEOUT;
while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n");
etm_lock(t);
return -EFAULT;
}
etm_lock(t);
t->flags |= TRACER_RUNNING;
return 0;
}
static int trace_stop(struct tracectx *t)
static int trace_stop_etm(struct tracectx *t, int id)
{
unsigned long timeout = TRACER_TIMEOUT;
etm_unlock(t);
etm_unlock(t, id);
etm_writel(t, 0x440, ETMR_CTRL);
while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
etm_writel(t, id, 0x440, ETMR_CTRL);
while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout)
;
if (!timeout) {
dev_dbg(t->dev, "Waiting for progbit to assert timed out\n");
etm_lock(t);
dev_err(t->dev,
"etm%d: Waiting for progbit to assert timed out\n",
id);
etm_lock(t, id);
return -EFAULT;
}
etm_lock(t);
etm_lock(t, id);
return 0;
}
static int trace_power_down_etm(struct tracectx *t, int id)
{
unsigned long timeout = TRACER_TIMEOUT;
etm_unlock(t, id);
while (!(etm_readl(t, id, ETMR_STATUS) & ETMST_PROGBIT) && --timeout)
;
if (!timeout) {
dev_err(t->dev, "etm%d: Waiting for status progbit to assert timed out\n",
id);
etm_lock(t, id);
return -EFAULT;
}
etm_writel(t, id, 0x441, ETMR_CTRL);
etm_lock(t, id);
return 0;
}
static int trace_stop(struct tracectx *t)
{
int id;
unsigned long timeout = TRACER_TIMEOUT;
u32 etb_fc = t->etb_fc;
for (id = 0; id < t->etm_regs_count; id++)
trace_stop_etm(t, id);
for (id = 0; id < t->etm_regs_count; id++)
trace_power_down_etm(t, id);
etb_unlock(t);
etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
if (etb_fc) {
etb_fc |= ETBFF_STOPFL;
etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL);
}
etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL);
timeout = TRACER_TIMEOUT;
while (etb_readl(t, ETBR_FORMATTERCTRL) &
@ -185,24 +287,15 @@ static int trace_stop(struct tracectx *t)
static int etb_getdatalen(struct tracectx *t)
{
u32 v;
int rp, wp;
int wp;
v = etb_readl(t, ETBR_STATUS);
if (v & 1)
return t->etb_bufsz;
rp = etb_readl(t, ETBR_READADDR);
wp = etb_readl(t, ETBR_WRITEADDR);
if (rp > wp) {
etb_writel(t, 0, ETBR_READADDR);
etb_writel(t, 0, ETBR_WRITEADDR);
return 0;
}
return wp - rp;
return wp;
}
/* sysrq+v will always stop the running trace and leave it at that */
@ -235,21 +328,18 @@ static void etm_dump(void)
printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM)));
printk(KERN_INFO "\n--- ETB buffer end ---\n");
/* deassert the overflow bit */
etb_writel(t, 1, ETBR_CTRL);
etb_writel(t, 0, ETBR_CTRL);
etb_writel(t, 0, ETBR_TRIGGERCOUNT);
etb_writel(t, 0, ETBR_READADDR);
etb_writel(t, 0, ETBR_WRITEADDR);
etb_lock(t);
}
static void sysrq_etm_dump(int key)
{
if (!mutex_trylock(&tracer.mutex)) {
printk(KERN_INFO "Tracing hardware busy\n");
return;
}
dev_dbg(tracer.dev, "Dumping ETB buffer\n");
etm_dump();
mutex_unlock(&tracer.mutex);
}
static struct sysrq_key_op sysrq_etm_op = {
@ -276,6 +366,10 @@ static ssize_t etb_read(struct file *file, char __user *data,
struct tracectx *t = file->private_data;
u32 first = 0;
u32 *buf;
int wpos;
int skip;
long wlength;
loff_t pos = *ppos;
mutex_lock(&t->mutex);
@ -287,31 +381,39 @@ static ssize_t etb_read(struct file *file, char __user *data,
etb_unlock(t);
total = etb_getdatalen(t);
if (total == 0 && t->dump_initial_etb)
total = t->etb_bufsz;
if (total == t->etb_bufsz)
first = etb_readl(t, ETBR_WRITEADDR);
if (pos > total * 4) {
skip = 0;
wpos = total;
} else {
skip = (int)pos % 4;
wpos = (int)pos / 4;
}
total -= wpos;
first = (first + wpos) % t->etb_bufsz;
etb_writel(t, first, ETBR_READADDR);
length = min(total * 4, (int)len);
buf = vmalloc(length);
wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4));
length = min(total * 4 - skip, (int)len);
buf = vmalloc(wlength * 4);
dev_dbg(t->dev, "ETB buffer length: %d\n", total);
dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n",
length, pos, wlength, first);
dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos);
dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS));
for (i = 0; i < length / 4; i++)
for (i = 0; i < wlength; i++)
buf[i] = etb_readl(t, ETBR_READMEM);
/* the only way to deassert overflow bit in ETB status is this */
etb_writel(t, 1, ETBR_CTRL);
etb_writel(t, 0, ETBR_CTRL);
etb_writel(t, 0, ETBR_WRITEADDR);
etb_writel(t, 0, ETBR_READADDR);
etb_writel(t, 0, ETBR_TRIGGERCOUNT);
etb_lock(t);
length -= copy_to_user(data, buf, length);
length -= copy_to_user(data, (u8 *)buf + skip, length);
vfree(buf);
*ppos = pos + length;
out:
mutex_unlock(&t->mutex);
@ -348,28 +450,17 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id)
if (ret)
goto out;
mutex_lock(&t->mutex);
t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
if (!t->etb_regs) {
ret = -ENOMEM;
goto out_release;
}
t->dev = &dev->dev;
t->dump_initial_etb = true;
amba_set_drvdata(dev, t);
etb_miscdev.parent = &dev->dev;
ret = misc_register(&etb_miscdev);
if (ret)
goto out_unmap;
t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
if (IS_ERR(t->emu_clk)) {
dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
return -EFAULT;
}
clk_enable(t->emu_clk);
etb_unlock(t);
t->etb_bufsz = etb_readl(t, ETBR_DEPTH);
dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz);
@ -378,6 +469,20 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id)
etb_writel(t, 0, ETBR_CTRL);
etb_writel(t, 0x1000, ETBR_FORMATTERCTRL);
etb_lock(t);
mutex_unlock(&t->mutex);
etb_miscdev.parent = &dev->dev;
ret = misc_register(&etb_miscdev);
if (ret)
goto out_unmap;
/* Get optional clock. Currently used to select clock source on omap3 */
t->emu_clk = clk_get(&dev->dev, "emu_src_ck");
if (IS_ERR(t->emu_clk))
dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n");
else
clk_enable(t->emu_clk);
dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n");
@ -385,10 +490,13 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id)
return ret;
out_unmap:
mutex_lock(&t->mutex);
amba_set_drvdata(dev, NULL);
iounmap(t->etb_regs);
t->etb_regs = NULL;
out_release:
mutex_unlock(&t->mutex);
amba_release_regions(dev);
return ret;
@ -403,8 +511,10 @@ static int etb_remove(struct amba_device *dev)
iounmap(t->etb_regs);
t->etb_regs = NULL;
clk_disable(t->emu_clk);
clk_put(t->emu_clk);
if (!IS_ERR(t->emu_clk)) {
clk_disable(t->emu_clk);
clk_put(t->emu_clk);
}
amba_release_regions(dev);
@ -448,7 +558,10 @@ static ssize_t trace_running_store(struct kobject *kobj,
return -EINVAL;
mutex_lock(&tracer.mutex);
ret = value ? trace_start(&tracer) : trace_stop(&tracer);
if (!tracer.etb_regs)
ret = -ENODEV;
else
ret = value ? trace_start(&tracer) : trace_stop(&tracer);
mutex_unlock(&tracer.mutex);
return ret ? : n;
@ -463,36 +576,50 @@ static ssize_t trace_info_show(struct kobject *kobj,
{
u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st;
int datalen;
int id;
int ret;
etb_unlock(&tracer);
datalen = etb_getdatalen(&tracer);
etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
etb_ra = etb_readl(&tracer, ETBR_READADDR);
etb_st = etb_readl(&tracer, ETBR_STATUS);
etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
etb_lock(&tracer);
mutex_lock(&tracer.mutex);
if (tracer.etb_regs) {
etb_unlock(&tracer);
datalen = etb_getdatalen(&tracer);
etb_wa = etb_readl(&tracer, ETBR_WRITEADDR);
etb_ra = etb_readl(&tracer, ETBR_READADDR);
etb_st = etb_readl(&tracer, ETBR_STATUS);
etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL);
etb_lock(&tracer);
} else {
etb_wa = etb_ra = etb_st = etb_fc = ~0;
datalen = -1;
}
etm_unlock(&tracer);
etm_ctrl = etm_readl(&tracer, ETMR_CTRL);
etm_st = etm_readl(&tracer, ETMR_STATUS);
etm_lock(&tracer);
return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n"
"ETBR_WRITEADDR:\t%08x\n"
"ETBR_READADDR:\t%08x\n"
"ETBR_STATUS:\t%08x\n"
"ETBR_FORMATTERCTRL:\t%08x\n"
"ETMR_CTRL:\t%08x\n"
"ETMR_STATUS:\t%08x\n",
"ETBR_FORMATTERCTRL:\t%08x\n",
datalen,
tracer.ncmppairs,
etb_wa,
etb_ra,
etb_st,
etb_fc,
etb_fc
);
for (id = 0; id < tracer.etm_regs_count; id++) {
etm_unlock(&tracer, id);
etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL);
etm_st = etm_readl(&tracer, id, ETMR_STATUS);
etm_lock(&tracer, id);
ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n"
"ETMR_STATUS:\t%08x\n",
etm_ctrl,
etm_st
);
}
mutex_unlock(&tracer.mutex);
return ret;
}
static struct kobj_attribute trace_info_attr =
@ -531,42 +658,260 @@ static ssize_t trace_mode_store(struct kobject *kobj,
static struct kobj_attribute trace_mode_attr =
__ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store);
static ssize_t trace_contextid_size_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
/* 0: No context id tracing, 1: One byte, 2: Two bytes, 3: Four bytes */
return sprintf(buf, "%d\n", (1 << tracer.etm_contextid_size) >> 1);
}
static ssize_t trace_contextid_size_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned int contextid_size;
if (sscanf(buf, "%u", &contextid_size) != 1)
return -EINVAL;
if (contextid_size == 3 || contextid_size > 4)
return -EINVAL;
mutex_lock(&tracer.mutex);
tracer.etm_contextid_size = fls(contextid_size);
mutex_unlock(&tracer.mutex);
return n;
}
static struct kobj_attribute trace_contextid_size_attr =
__ATTR(trace_contextid_size, 0644,
trace_contextid_size_show, trace_contextid_size_store);
static ssize_t trace_branch_output_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_BRANCHOUTPUT));
}
static ssize_t trace_branch_output_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned int branch_output;
if (sscanf(buf, "%u", &branch_output) != 1)
return -EINVAL;
mutex_lock(&tracer.mutex);
if (branch_output) {
tracer.flags |= TRACER_BRANCHOUTPUT;
/* Branch broadcasting is incompatible with the return stack */
tracer.flags &= ~TRACER_RETURN_STACK;
} else {
tracer.flags &= ~TRACER_BRANCHOUTPUT;
}
mutex_unlock(&tracer.mutex);
return n;
}
static struct kobj_attribute trace_branch_output_attr =
__ATTR(trace_branch_output, 0644,
trace_branch_output_show, trace_branch_output_store);
static ssize_t trace_return_stack_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_RETURN_STACK));
}
static ssize_t trace_return_stack_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned int return_stack;
if (sscanf(buf, "%u", &return_stack) != 1)
return -EINVAL;
mutex_lock(&tracer.mutex);
if (return_stack) {
tracer.flags |= TRACER_RETURN_STACK;
/* Return stack is incompatible with branch broadcasting */
tracer.flags &= ~TRACER_BRANCHOUTPUT;
} else {
tracer.flags &= ~TRACER_RETURN_STACK;
}
mutex_unlock(&tracer.mutex);
return n;
}
static struct kobj_attribute trace_return_stack_attr =
__ATTR(trace_return_stack, 0644,
trace_return_stack_show, trace_return_stack_store);
static ssize_t trace_timestamp_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_TIMESTAMP));
}
static ssize_t trace_timestamp_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned int timestamp;
if (sscanf(buf, "%u", &timestamp) != 1)
return -EINVAL;
mutex_lock(&tracer.mutex);
if (timestamp)
tracer.flags |= TRACER_TIMESTAMP;
else
tracer.flags &= ~TRACER_TIMESTAMP;
mutex_unlock(&tracer.mutex);
return n;
}
static struct kobj_attribute trace_timestamp_attr =
__ATTR(trace_timestamp, 0644,
trace_timestamp_show, trace_timestamp_store);
static ssize_t trace_range_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%08lx %08lx\n",
tracer.range_start, tracer.range_end);
}
static ssize_t trace_range_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned long range_start, range_end;
if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2)
return -EINVAL;
mutex_lock(&tracer.mutex);
tracer.range_start = range_start;
tracer.range_end = range_end;
mutex_unlock(&tracer.mutex);
return n;
}
static struct kobj_attribute trace_range_attr =
__ATTR(trace_range, 0644, trace_range_show, trace_range_store);
static ssize_t trace_data_range_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
unsigned long range_start;
u64 range_end;
mutex_lock(&tracer.mutex);
range_start = tracer.data_range_start;
range_end = tracer.data_range_end;
if (!range_end && (tracer.flags & TRACER_TRACE_DATA))
range_end = 0x100000000ULL;
mutex_unlock(&tracer.mutex);
return sprintf(buf, "%08lx %08llx\n", range_start, range_end);
}
static ssize_t trace_data_range_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
unsigned long range_start;
u64 range_end;
if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2)
return -EINVAL;
mutex_lock(&tracer.mutex);
tracer.data_range_start = range_start;
tracer.data_range_end = (unsigned long)range_end;
if (range_end)
tracer.flags |= TRACER_TRACE_DATA;
else
tracer.flags &= ~TRACER_TRACE_DATA;
mutex_unlock(&tracer.mutex);
return n;
}
static struct kobj_attribute trace_data_range_attr =
__ATTR(trace_data_range, 0644,
trace_data_range_show, trace_data_range_store);
static int etm_probe(struct amba_device *dev, const struct amba_id *id)
{
struct tracectx *t = &tracer;
int ret = 0;
void __iomem **new_regs;
int new_count;
u32 etmccr;
u32 etmidr;
u32 etmccer = 0;
u8 etm_version = 0;
if (t->etm_regs) {
dev_dbg(&dev->dev, "ETM already initialized\n");
ret = -EBUSY;
mutex_lock(&t->mutex);
new_count = t->etm_regs_count + 1;
new_regs = krealloc(t->etm_regs,
sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL);
if (!new_regs) {
dev_dbg(&dev->dev, "Failed to allocate ETM register array\n");
ret = -ENOMEM;
goto out;
}
t->etm_regs = new_regs;
ret = amba_request_regions(dev, NULL);
if (ret)
goto out;
t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res));
if (!t->etm_regs) {
t->etm_regs[t->etm_regs_count] =
ioremap_nocache(dev->res.start, resource_size(&dev->res));
if (!t->etm_regs[t->etm_regs_count]) {
ret = -ENOMEM;
goto out_release;
}
amba_set_drvdata(dev, t);
amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]);
mutex_init(&t->mutex);
t->dev = &dev->dev;
t->flags = TRACER_CYCLE_ACC;
t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA | TRACER_BRANCHOUTPUT;
t->etm_portsz = 1;
t->etm_contextid_size = 3;
etm_unlock(t);
(void)etm_readl(t, ETMMR_PDSR);
etm_unlock(t, t->etm_regs_count);
(void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR);
/* dummy first read */
(void)etm_readl(&tracer, ETMMR_OSSRR);
(void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR);
t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf;
etm_writel(t, 0x440, ETMR_CTRL);
etm_lock(t);
etmccr = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE);
t->ncmppairs = etmccr & 0xf;
if (etmccr & ETMCCR_ETMIDR_PRESENT) {
etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID);
etm_version = ETMIDR_VERSION(etmidr);
if (etm_version >= ETMIDR_VERSION_3_1)
etmccer = etm_readl(t, t->etm_regs_count, ETMR_CCE);
}
etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL);
etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR);
etm_lock(t, t->etm_regs_count);
ret = sysfs_create_file(&dev->dev.kobj,
&trace_running_attr.attr);
@ -582,35 +927,100 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id)
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n");
dev_dbg(t->dev, "ETM AMBA driver initialized.\n");
ret = sysfs_create_file(&dev->dev.kobj,
&trace_contextid_size_attr.attr);
if (ret)
dev_dbg(&dev->dev,
"Failed to create trace_contextid_size in sysfs\n");
ret = sysfs_create_file(&dev->dev.kobj,
&trace_branch_output_attr.attr);
if (ret)
dev_dbg(&dev->dev,
"Failed to create trace_branch_output in sysfs\n");
if (etmccer & ETMCCER_RETURN_STACK_IMPLEMENTED) {
ret = sysfs_create_file(&dev->dev.kobj,
&trace_return_stack_attr.attr);
if (ret)
dev_dbg(&dev->dev,
"Failed to create trace_return_stack in sysfs\n");
}
if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) {
ret = sysfs_create_file(&dev->dev.kobj,
&trace_timestamp_attr.attr);
if (ret)
dev_dbg(&dev->dev,
"Failed to create trace_timestamp in sysfs\n");
}
ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr);
if (ret)
dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n");
if (etm_version < ETMIDR_VERSION_PFT_1_0) {
ret = sysfs_create_file(&dev->dev.kobj,
&trace_data_range_attr.attr);
if (ret)
dev_dbg(&dev->dev,
"Failed to create trace_data_range in sysfs\n");
} else {
tracer.flags &= ~TRACER_TRACE_DATA;
}
dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n");
/* Enable formatter if there are multiple trace sources */
if (new_count > 1)
t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC;
t->etm_regs_count = new_count;
out:
mutex_unlock(&t->mutex);
return ret;
out_unmap:
amba_set_drvdata(dev, NULL);
iounmap(t->etm_regs);
iounmap(t->etm_regs[t->etm_regs_count]);
out_release:
amba_release_regions(dev);
mutex_unlock(&t->mutex);
return ret;
}
static int etm_remove(struct amba_device *dev)
{
struct tracectx *t = amba_get_drvdata(dev);
amba_set_drvdata(dev, NULL);
iounmap(t->etm_regs);
t->etm_regs = NULL;
amba_release_regions(dev);
int i;
struct tracectx *t = &tracer;
void __iomem *etm_regs = amba_get_drvdata(dev);
sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr);
sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr);
amba_set_drvdata(dev, NULL);
mutex_lock(&t->mutex);
for (i = 0; i < t->etm_regs_count; i++)
if (t->etm_regs[i] == etm_regs)
break;
for (; i < t->etm_regs_count - 1; i++)
t->etm_regs[i] = t->etm_regs[i + 1];
t->etm_regs_count--;
if (!t->etm_regs_count) {
kfree(t->etm_regs);
t->etm_regs = NULL;
}
mutex_unlock(&t->mutex);
iounmap(etm_regs);
amba_release_regions(dev);
return 0;
}
@ -620,6 +1030,10 @@ static struct amba_id etm_ids[] = {
.id = 0x0003b921,
.mask = 0x0007ffff,
},
{
.id = 0x0003b950,
.mask = 0x0007ffff,
},
{ 0, 0 },
};
@ -637,6 +1051,8 @@ static int __init etm_init(void)
{
int retval;
mutex_init(&tracer.mutex);
retval = amba_driver_register(&etb_driver);
if (retval) {
printk(KERN_ERR "Failed to register etb\n");

View File

@ -13,6 +13,7 @@
*/
#include <linux/ftrace.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
@ -63,6 +64,20 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
}
#endif
int ftrace_arch_code_modify_prepare(void)
{
set_kernel_text_rw();
set_all_modules_text_rw();
return 0;
}
int ftrace_arch_code_modify_post_process(void)
{
set_all_modules_text_ro();
set_kernel_text_ro();
return 0;
}
static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
{
return arm_gen_branch_link(pc, addr);

View File

@ -32,6 +32,7 @@
#include <linux/hw_breakpoint.h>
#include <linux/cpuidle.h>
#include <linux/leds.h>
#include <linux/console.h>
#include <asm/cacheflush.h>
#include <asm/idmap.h>
@ -57,9 +58,46 @@ static const char *isa_modes[] = {
"ARM" , "Thumb" , "Jazelle", "ThumbEE"
};
#ifdef CONFIG_SMP
void arch_trigger_all_cpu_backtrace(void)
{
smp_send_all_cpu_backtrace();
}
#else
void arch_trigger_all_cpu_backtrace(void)
{
dump_stack();
}
#endif
extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
typedef void (*phys_reset_t)(unsigned long);
#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART
void arm_machine_flush_console(void)
{
printk("\n");
pr_emerg("Restarting %s\n", linux_banner);
if (console_trylock()) {
console_unlock();
return;
}
mdelay(50);
local_irq_disable();
if (!console_trylock())
pr_emerg("arm_restart: Console was locked! Busting\n");
else
pr_emerg("arm_restart: Console was locked!\n");
console_unlock();
}
#else
void arm_machine_flush_console(void)
{
}
#endif
/*
* A temporary stack to use for CPU reset. This is static so that we
* don't clobber it with the identity mapping. When running with this
@ -147,6 +185,7 @@ void arch_cpu_idle_prepare(void)
void arch_cpu_idle_enter(void)
{
idle_notifier_call_chain(IDLE_START);
ledtrig_cpu(CPU_LED_IDLE_START);
#ifdef CONFIG_PL310_ERRATA_769419
wmb();
@ -156,6 +195,7 @@ void arch_cpu_idle_enter(void)
void arch_cpu_idle_exit(void)
{
ledtrig_cpu(CPU_LED_IDLE_END);
idle_notifier_call_chain(IDLE_END);
}
#ifdef CONFIG_HOTPLUG_CPU
@ -195,6 +235,16 @@ __setup("reboot=", reboot_setup);
*/
void machine_shutdown(void)
{
#ifdef CONFIG_SMP
/*
* Disable preemption so we're guaranteed to
* run to power off or reboot and prevent
* the possibility of switching to another
* thread that might wind up blocking on
* one of the stopped CPUs.
*/
preempt_disable();
#endif
disable_nonboot_cpus();
}
@ -240,6 +290,10 @@ void machine_restart(char *cmd)
{
smp_send_stop();
/* Flush the console to make sure all the relevant messages make it
* out to the console drivers */
arm_machine_flush_console();
arm_pm_restart(reboot_mode, cmd);
/* Give a grace period for failure to restart of 1s */
@ -251,6 +305,77 @@ void machine_restart(char *cmd)
while (1);
}
/*
* dump a block of kernel memory from around the given address
*/
static void show_data(unsigned long addr, int nbytes, const char *name)
{
int i, j;
int nlines;
u32 *p;
/*
* don't attempt to dump non-kernel addresses or
* values that are probably just small negative numbers
*/
if (addr < PAGE_OFFSET || addr > -256UL)
return;
printk("\n%s: %#lx:\n", name, addr);
/*
* round address down to a 32 bit boundary
* and always dump a multiple of 32 bytes
*/
p = (u32 *)(addr & ~(sizeof(u32) - 1));
nbytes += (addr & (sizeof(u32) - 1));
nlines = (nbytes + 31) / 32;
for (i = 0; i < nlines; i++) {
/*
* just display low 16 bits of address to keep
* each line of the dump < 80 characters
*/
printk("%04lx ", (unsigned long)p & 0xffff);
for (j = 0; j < 8; j++) {
u32 data;
if (probe_kernel_address(p, data)) {
printk(" ********");
} else {
printk(" %08x", data);
}
++p;
}
printk("\n");
}
}
static void show_extra_register_data(struct pt_regs *regs, int nbytes)
{
mm_segment_t fs;
fs = get_fs();
set_fs(KERNEL_DS);
show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC");
show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR");
show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP");
show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP");
show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP");
show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0");
show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1");
show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2");
show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3");
show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4");
show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5");
show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6");
show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7");
show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8");
show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9");
show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10");
set_fs(fs);
}
void __show_regs(struct pt_regs *regs)
{
unsigned long flags;
@ -307,6 +432,8 @@ void __show_regs(struct pt_regs *regs)
printk("Control: %08x%s\n", ctrl, buf);
}
#endif
show_extra_register_data(regs, 128);
}
void show_regs(struct pt_regs * regs)

View File

@ -67,6 +67,7 @@ enum ipi_msg_type {
IPI_CALL_FUNC_SINGLE,
IPI_CPU_STOP,
IPI_COMPLETION,
IPI_CPU_BACKTRACE,
};
static DECLARE_COMPLETION(cpu_running);
@ -465,6 +466,7 @@ static const char *ipi_types[NR_IPI] = {
S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),
S(IPI_CPU_STOP, "CPU stop interrupts"),
S(IPI_COMPLETION, "completion interrupts"),
S(IPI_CPU_BACKTRACE, "CPU backtrace"),
};
void show_ipi_list(struct seq_file *p, int prec)
@ -603,6 +605,58 @@ static void ipi_complete(unsigned int cpu)
complete(per_cpu(cpu_completion, cpu));
}
static cpumask_t backtrace_mask;
static DEFINE_RAW_SPINLOCK(backtrace_lock);
/* "in progress" flag of arch_trigger_all_cpu_backtrace */
static unsigned long backtrace_flag;
void smp_send_all_cpu_backtrace(void)
{
unsigned int this_cpu = smp_processor_id();
int i;
if (test_and_set_bit(0, &backtrace_flag))
/*
* If there is already a trigger_all_cpu_backtrace() in progress
* (backtrace_flag == 1), don't output double cpu dump infos.
*/
return;
cpumask_copy(&backtrace_mask, cpu_online_mask);
cpu_clear(this_cpu, backtrace_mask);
pr_info("Backtrace for cpu %d (current):\n", this_cpu);
dump_stack();
pr_info("\nsending IPI to all other CPUs:\n");
smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE);
/* Wait for up to 10 seconds for all other CPUs to do the backtrace */
for (i = 0; i < 10 * 1000; i++) {
if (cpumask_empty(&backtrace_mask))
break;
mdelay(1);
}
clear_bit(0, &backtrace_flag);
smp_mb__after_clear_bit();
}
/*
* ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace()
*/
static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs)
{
if (cpu_isset(cpu, backtrace_mask)) {
raw_spin_lock(&backtrace_lock);
pr_warning("IPI backtrace for cpu %d\n", cpu);
show_regs(regs);
raw_spin_unlock(&backtrace_lock);
cpu_clear(cpu, backtrace_mask);
}
}
/*
* Main handler for inter-processor interrupts
*/
@ -659,6 +713,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
irq_exit();
break;
case IPI_CPU_BACKTRACE:
ipi_cpu_backtrace(cpu, regs);
break;
default:
printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
cpu, ipinr);

View File

@ -7,6 +7,7 @@ obj-y := dma-mapping.o extable.o fault.o init.o \
obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
mmap.o pgd.o mmu.o
obj-$(CONFIG_DEBUG_RODATA) += rodata.o
ifneq ($(CONFIG_MMU),y)
obj-y += nommu.o

View File

@ -33,6 +33,9 @@ static void __iomem *l2x0_base;
static DEFINE_RAW_SPINLOCK(l2x0_lock);
static u32 l2x0_way_mask; /* Bitmask of active ways */
static u32 l2x0_size;
static u32 l2x0_cache_id;
static unsigned int l2x0_sets;
static unsigned int l2x0_ways;
static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
/* Aurora don't have the cache ID register available, so we have to
@ -49,6 +52,13 @@ struct l2x0_of_data {
static bool of_init = false;
static inline bool is_pl310_rev(int rev)
{
return (l2x0_cache_id &
(L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) ==
(L2X0_CACHE_ID_PART_L310 | rev);
}
static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
{
/* wait for cache operation by line or way to complete */
@ -137,6 +147,23 @@ static void l2x0_cache_sync(void)
raw_spin_unlock_irqrestore(&l2x0_lock, flags);
}
#ifdef CONFIG_PL310_ERRATA_727915
static void l2x0_for_each_set_way(void __iomem *reg)
{
int set;
int way;
unsigned long flags;
for (way = 0; way < l2x0_ways; way++) {
raw_spin_lock_irqsave(&l2x0_lock, flags);
for (set = 0; set < l2x0_sets; set++)
writel_relaxed((way << 28) | (set << 5), reg);
cache_sync();
raw_spin_unlock_irqrestore(&l2x0_lock, flags);
}
}
#endif
static void __l2x0_flush_all(void)
{
debug_writel(0x03);
@ -150,6 +177,13 @@ static void l2x0_flush_all(void)
{
unsigned long flags;
#ifdef CONFIG_PL310_ERRATA_727915
if (is_pl310_rev(REV_PL310_R2P0)) {
l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX);
return;
}
#endif
/* clean all ways */
raw_spin_lock_irqsave(&l2x0_lock, flags);
__l2x0_flush_all();
@ -160,11 +194,20 @@ static void l2x0_clean_all(void)
{
unsigned long flags;
#ifdef CONFIG_PL310_ERRATA_727915
if (is_pl310_rev(REV_PL310_R2P0)) {
l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX);
return;
}
#endif
/* clean all ways */
raw_spin_lock_irqsave(&l2x0_lock, flags);
debug_writel(0x03);
writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
cache_sync();
debug_writel(0x00);
raw_spin_unlock_irqrestore(&l2x0_lock, flags);
}
@ -323,65 +366,64 @@ static void l2x0_unlock(u32 cache_id)
void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
{
u32 aux;
u32 cache_id;
u32 way_size = 0;
int ways;
int way_size_shift = L2X0_WAY_SIZE_SHIFT;
const char *type;
l2x0_base = base;
if (cache_id_part_number_from_dt)
cache_id = cache_id_part_number_from_dt;
l2x0_cache_id = cache_id_part_number_from_dt;
else
cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
aux &= aux_mask;
aux |= aux_val;
/* Determine the number of ways */
switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) {
case L2X0_CACHE_ID_PART_L310:
if (aux & (1 << 16))
ways = 16;
l2x0_ways = 16;
else
ways = 8;
l2x0_ways = 8;
type = "L310";
#ifdef CONFIG_PL310_ERRATA_753970
/* Unmapped register. */
sync_reg_offset = L2X0_DUMMY_REG;
#endif
if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0)
if ((l2x0_cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0)
outer_cache.set_debug = pl310_set_debug;
break;
case L2X0_CACHE_ID_PART_L210:
ways = (aux >> 13) & 0xf;
l2x0_ways = (aux >> 13) & 0xf;
type = "L210";
break;
case AURORA_CACHE_ID:
sync_reg_offset = AURORA_SYNC_REG;
ways = (aux >> 13) & 0xf;
ways = 2 << ((ways + 1) >> 2);
l2x0_ways = (aux >> 13) & 0xf;
l2x0_ways = 2 << ((l2x0_ways + 1) >> 2);
way_size_shift = AURORA_WAY_SIZE_SHIFT;
type = "Aurora";
break;
default:
/* Assume unknown chips have 8 ways */
ways = 8;
l2x0_ways = 8;
type = "L2x0 series";
break;
}
l2x0_way_mask = (1 << ways) - 1;
l2x0_way_mask = (1 << l2x0_ways) - 1;
/*
* L2 cache Size = Way size * Number of ways
*/
way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
way_size = 1 << (way_size + way_size_shift);
way_size = SZ_1K << (way_size + way_size_shift);
l2x0_size = ways * way_size * SZ_1K;
l2x0_size = l2x0_ways * way_size;
l2x0_sets = way_size / CACHE_LINE_SIZE;
/*
* Check if l2x0 controller is already enabled.
@ -390,7 +432,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
*/
if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
/* Make sure that I&D is not locked down when starting */
l2x0_unlock(cache_id);
l2x0_unlock(l2x0_cache_id);
/* l2x0 controller is disabled */
writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL);
@ -419,7 +461,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
printk(KERN_INFO "%s cache controller enabled\n", type);
printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
ways, cache_id, aux, l2x0_size);
l2x0_ways, l2x0_cache_id, aux, l2x0_size);
}
#ifdef CONFIG_OF

View File

@ -270,6 +270,11 @@ v6_dma_clean_range:
* - end - virtual end address of region
*/
ENTRY(v6_dma_flush_range)
#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT
sub r2, r1, r0
cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT
bhi v6_dma_flush_dcache_all
#endif
#ifdef CONFIG_DMA_CACHE_RWFO
ldrb r2, [r0] @ read for ownership
strb r2, [r0] @ write for ownership
@ -292,6 +297,18 @@ ENTRY(v6_dma_flush_range)
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
mov pc, lr
#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT
v6_dma_flush_dcache_all:
mov r0, #0
#ifdef HARVARD_CACHE
mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate
#else
mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate
#endif
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
mov pc, lr
#endif
/*
* dma_map_area(start, size, dir)
* - start - kernel virtual start address

View File

@ -276,10 +276,10 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
local_irq_enable();
/*
* If we're in an interrupt or have no user
* If we're in an interrupt, or have no irqs, or have no user
* context, we must not take the fault..
*/
if (in_atomic() || !mm)
if (in_atomic() || irqs_disabled() || !mm)
goto no_context;
/*

View File

@ -595,11 +595,25 @@ static void __init *early_alloc(unsigned long sz)
return early_alloc_aligned(sz, sz);
}
static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
static pte_t * __init early_pte_alloc(pmd_t *pmd)
{
if (pmd_none(*pmd) || pmd_bad(*pmd))
return early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
return pmd_page_vaddr(*pmd);
}
static void __init early_pte_install(pmd_t *pmd, pte_t *pte, unsigned long prot)
{
__pmd_populate(pmd, __pa(pte), prot);
BUG_ON(pmd_bad(*pmd));
}
static pte_t * __init early_pte_alloc_and_install(pmd_t *pmd,
unsigned long addr, unsigned long prot)
{
if (pmd_none(*pmd)) {
pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
__pmd_populate(pmd, __pa(pte), prot);
pte_t *pte = early_pte_alloc(pmd);
early_pte_install(pmd, pte, prot);
}
BUG_ON(pmd_bad(*pmd));
return pte_offset_kernel(pmd, addr);
@ -609,11 +623,17 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
unsigned long end, unsigned long pfn,
const struct mem_type *type)
{
pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);
pte_t *start_pte = early_pte_alloc(pmd);
pte_t *pte = start_pte + pte_index(addr);
/* If replacing a section mapping, the whole section must be replaced */
BUG_ON(!pmd_none(*pmd) && pmd_bad(*pmd) && ((addr | end) & ~PMD_MASK));
do {
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
early_pte_install(pmd, start_pte, type->prot_l1);
}
static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
@ -645,7 +665,8 @@ static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
unsigned long end, phys_addr_t phys,
const struct mem_type *type)
const struct mem_type *type,
bool force_pages)
{
pmd_t *pmd = pmd_offset(pud, addr);
unsigned long next;
@ -662,7 +683,8 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
* aligned to a section boundary.
*/
if (type->prot_sect &&
((addr | next | phys) & ~SECTION_MASK) == 0) {
((addr | next | phys) & ~SECTION_MASK) == 0 &&
!force_pages) {
__map_init_section(pmd, addr, next, phys, type);
} else {
alloc_init_pte(pmd, addr, next,
@ -675,14 +697,15 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
}
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
unsigned long end, unsigned long phys, const struct mem_type *type)
unsigned long end, unsigned long phys, const struct mem_type *type,
bool force_pages)
{
pud_t *pud = pud_offset(pgd, addr);
unsigned long next;
do {
next = pud_addr_end(addr, end);
alloc_init_pmd(pud, addr, next, phys, type);
alloc_init_pmd(pud, addr, next, phys, type, force_pages);
phys += next - addr;
} while (pud++, addr = next, addr != end);
}
@ -756,7 +779,7 @@ static void __init create_36bit_mapping(struct map_desc *md,
* offsets, and we take full advantage of sections and
* supersections.
*/
static void __init create_mapping(struct map_desc *md)
static void __init create_mapping(struct map_desc *md, bool force_pages)
{
unsigned long addr, length, end;
phys_addr_t phys;
@ -806,7 +829,7 @@ static void __init create_mapping(struct map_desc *md)
do {
unsigned long next = pgd_addr_end(addr, end);
alloc_init_pud(pgd, addr, next, phys, type);
alloc_init_pud(pgd, addr, next, phys, type, force_pages);
phys += next - addr;
addr = next;
@ -828,7 +851,7 @@ void __init iotable_init(struct map_desc *io_desc, int nr)
svm = early_alloc_aligned(sizeof(*svm) * nr, __alignof__(*svm));
for (md = io_desc; nr; md++, nr--) {
create_mapping(md);
create_mapping(md, false);
vm = &svm->vm;
vm->addr = (void *)(md->virtual & PAGE_MASK);
@ -949,7 +972,7 @@ void __init debug_ll_io_init(void)
map.virtual &= PAGE_MASK;
map.length = PAGE_SIZE;
map.type = MT_DEVICE;
create_mapping(&map);
create_mapping(&map, false);
}
#endif
@ -994,6 +1017,28 @@ void __init sanity_check_meminfo(void)
struct membank *bank = &meminfo.bank[j];
*bank = meminfo.bank[i];
#ifdef CONFIG_SPARSEMEM
if (pfn_to_section_nr(bank_pfn_start(bank)) !=
pfn_to_section_nr(bank_pfn_end(bank) - 1)) {
phys_addr_t sz;
unsigned long start_pfn = bank_pfn_start(bank);
unsigned long end_pfn = SECTION_ALIGN_UP(start_pfn + 1);
sz = ((phys_addr_t)(end_pfn - start_pfn) << PAGE_SHIFT);
if (meminfo.nr_banks >= NR_BANKS) {
pr_crit("NR_BANKS too low, ignoring %lld bytes of memory\n",
(unsigned long long)(bank->size - sz));
} else {
memmove(bank + 1, bank,
(meminfo.nr_banks - i) * sizeof(*bank));
meminfo.nr_banks++;
bank[1].size -= sz;
bank[1].start = __pfn_to_phys(end_pfn);
}
bank->size = sz;
}
#endif
if (bank->start > ULONG_MAX)
highmem = 1;
@ -1191,7 +1236,7 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
map.virtual = MODULES_VADDR;
map.length = ((unsigned long)_etext - map.virtual + ~SECTION_MASK) & SECTION_MASK;
map.type = MT_ROM;
create_mapping(&map);
create_mapping(&map, false);
#endif
/*
@ -1202,14 +1247,14 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
map.virtual = FLUSH_BASE;
map.length = SZ_1M;
map.type = MT_CACHECLEAN;
create_mapping(&map);
create_mapping(&map, false);
#endif
#ifdef FLUSH_BASE_MINICACHE
map.pfn = __phys_to_pfn(FLUSH_BASE_PHYS + SZ_1M);
map.virtual = FLUSH_BASE_MINICACHE;
map.length = SZ_1M;
map.type = MT_MINICLEAN;
create_mapping(&map);
create_mapping(&map, false);
#endif
/*
@ -1221,12 +1266,12 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
map.virtual = 0xffff0000;
map.length = PAGE_SIZE;
map.type = MT_HIGH_VECTORS;
create_mapping(&map);
create_mapping(&map, false);
if (!vectors_high()) {
map.virtual = 0;
map.type = MT_LOW_VECTORS;
create_mapping(&map);
create_mapping(&map, false);
}
/*
@ -1252,20 +1297,23 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
static void __init kmap_init(void)
{
#ifdef CONFIG_HIGHMEM
pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE),
pkmap_page_table = early_pte_alloc_and_install(pmd_off_k(PKMAP_BASE),
PKMAP_BASE, _PAGE_KERNEL_TABLE);
#endif
}
static void __init map_lowmem(void)
{
struct memblock_region *reg;
phys_addr_t start;
phys_addr_t end;
struct map_desc map;
/* Map all the lowmem memory banks. */
for_each_memblock(memory, reg) {
phys_addr_t start = reg->base;
phys_addr_t end = start + reg->size;
struct map_desc map;
start = reg->base;
end = start + reg->size;
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
@ -1277,8 +1325,20 @@ static void __init map_lowmem(void)
map.length = end - start;
map.type = MT_MEMORY;
create_mapping(&map);
create_mapping(&map, false);
}
#ifdef CONFIG_DEBUG_RODATA
start = __pa(_stext) & PMD_MASK;
end = ALIGN(__pa(__end_rodata), PMD_SIZE);
map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
map.length = end - start;
map.type = MT_MEMORY;
create_mapping(&map, true);
#endif
}
/*

159
arch/arm/mm/rodata.c Normal file
View File

@ -0,0 +1,159 @@
/*
* linux/arch/arm/mm/rodata.c
*
* Copyright (C) 2011 Google, Inc.
*
* Author: Colin Cross <ccross@android.com>
*
* Based on x86 implementation in arch/x86/mm/init_32.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <asm/cache.h>
#include <asm/pgtable.h>
#include <asm/rodata.h>
#include <asm/sections.h>
#include <asm/tlbflush.h>
#include "mm.h"
static int kernel_set_to_readonly __read_mostly;
#ifdef CONFIG_DEBUG_RODATA_TEST
static const int rodata_test_data = 0xC3;
static noinline void rodata_test(void)
{
int result;
pr_info("%s: attempting to write to read-only section:\n", __func__);
if (*(volatile int *)&rodata_test_data != 0xC3) {
pr_err("read only data changed before test\n");
return;
}
/*
* Attempt to to write to rodata_test_data, trapping the expected
* data abort. If the trap executed, result will be 1. If it didn't,
* result will be 0xFF.
*/
asm volatile(
"0: str %[zero], [%[rodata_test_data]]\n"
" mov %[result], #0xFF\n"
" b 2f\n"
"1: mov %[result], #1\n"
"2:\n"
/* Exception fixup - if store at label 0 faults, jumps to 1 */
".pushsection __ex_table, \"a\"\n"
" .long 0b, 1b\n"
".popsection\n"
: [result] "=r" (result)
: [rodata_test_data] "r" (&rodata_test_data), [zero] "r" (0)
: "memory"
);
if (result == 1)
pr_info("write to read-only section trapped, success\n");
else
pr_err("write to read-only section NOT trapped, test failed\n");
if (*(volatile int *)&rodata_test_data != 0xC3)
pr_err("read only data changed during write\n");
}
#else
static inline void rodata_test(void) { }
#endif
static int set_page_attributes(unsigned long virt, int numpages,
pte_t (*f)(pte_t))
{
pmd_t *pmd;
pte_t *pte;
unsigned long start = virt;
unsigned long end = virt + (numpages << PAGE_SHIFT);
unsigned long pmd_end;
while (virt < end) {
pmd = pmd_off_k(virt);
pmd_end = min(ALIGN(virt + 1, PMD_SIZE), end);
if ((pmd_val(*pmd) & PMD_TYPE_MASK) != PMD_TYPE_TABLE) {
pr_err("%s: pmd %p=%08lx for %08lx not page table\n",
__func__, pmd, pmd_val(*pmd), virt);
virt = pmd_end;
continue;
}
while (virt < pmd_end) {
pte = pte_offset_kernel(pmd, virt);
set_pte_ext(pte, f(*pte), 0);
virt += PAGE_SIZE;
}
}
flush_tlb_kernel_range(start, end);
return 0;
}
int set_memory_ro(unsigned long virt, int numpages)
{
return set_page_attributes(virt, numpages, pte_wrprotect);
}
EXPORT_SYMBOL(set_memory_ro);
int set_memory_rw(unsigned long virt, int numpages)
{
return set_page_attributes(virt, numpages, pte_mkwrite);
}
EXPORT_SYMBOL(set_memory_rw);
void set_kernel_text_rw(void)
{
unsigned long start = PAGE_ALIGN((unsigned long)_text);
unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start;
if (!kernel_set_to_readonly)
return;
pr_debug("Set kernel text: %lx - %lx to read-write\n",
start, start + size);
set_memory_rw(start, size >> PAGE_SHIFT);
}
void set_kernel_text_ro(void)
{
unsigned long start = PAGE_ALIGN((unsigned long)_text);
unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start;
if (!kernel_set_to_readonly)
return;
pr_info_once("Write protecting the kernel text section %lx - %lx\n",
start, start + size);
pr_debug("Set kernel text: %lx - %lx to read only\n",
start, start + size);
set_memory_ro(start, size >> PAGE_SHIFT);
}
void mark_rodata_ro(void)
{
kernel_set_to_readonly = 1;
set_kernel_text_ro();
rodata_test();
}

View File

@ -1,13 +1,6 @@
#ifndef _ASM_X86_IDLE_H
#define _ASM_X86_IDLE_H
#define IDLE_START 1
#define IDLE_END 2
struct notifier_block;
void idle_notifier_register(struct notifier_block *n);
void idle_notifier_unregister(struct notifier_block *n);
#ifdef CONFIG_X86_64
void enter_idle(void);
void exit_idle(void);

View File

@ -40,19 +40,6 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS;
#ifdef CONFIG_X86_64
static DEFINE_PER_CPU(unsigned char, is_idle);
static ATOMIC_NOTIFIER_HEAD(idle_notifier);
void idle_notifier_register(struct notifier_block *n)
{
atomic_notifier_chain_register(&idle_notifier, n);
}
EXPORT_SYMBOL_GPL(idle_notifier_register);
void idle_notifier_unregister(struct notifier_block *n)
{
atomic_notifier_chain_unregister(&idle_notifier, n);
}
EXPORT_SYMBOL_GPL(idle_notifier_unregister);
#endif
struct kmem_cache *task_xstate_cachep;
@ -257,14 +244,14 @@ static inline void play_dead(void)
void enter_idle(void)
{
this_cpu_write(is_idle, 1);
atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL);
idle_notifier_call_chain(IDLE_START);
}
static void __exit_idle(void)
{
if (x86_test_and_clear_bit_percpu(0, is_idle) == 0)
return;
atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL);
idle_notifier_call_chain(IDLE_END);
}
/* Called from interrupts to signify idle end */

View File

@ -1107,6 +1107,22 @@ static void disk_release(struct device *dev)
blk_put_queue(disk->queue);
kfree(disk);
}
static int disk_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct gendisk *disk = dev_to_disk(dev);
struct disk_part_iter piter;
struct hd_struct *part;
int cnt = 0;
disk_part_iter_init(&piter, disk, 0);
while((part = disk_part_iter_next(&piter)))
cnt++;
disk_part_iter_exit(&piter);
add_uevent_var(env, "NPARTS=%u", cnt);
return 0;
}
struct class block_class = {
.name = "block",
};
@ -1126,6 +1142,7 @@ static struct device_type disk_type = {
.groups = disk_attr_groups,
.release = disk_release,
.devnode = block_devnode,
.uevent = disk_uevent,
};
#ifdef CONFIG_PROC_FS

View File

@ -216,10 +216,21 @@ static void part_release(struct device *dev)
kfree(p);
}
static int part_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct hd_struct *part = dev_to_part(dev);
add_uevent_var(env, "PARTN=%u", part->partno);
if (part->info && part->info->volname[0])
add_uevent_var(env, "PARTNAME=%s", part->info->volname);
return 0;
}
struct device_type part_type = {
.name = "partition",
.groups = part_attr_groups,
.release = part_release,
.uevent = part_uevent,
};
static void delete_partition_rcu_cb(struct rcu_head *head)

View File

@ -100,6 +100,8 @@ source "drivers/memstick/Kconfig"
source "drivers/leds/Kconfig"
source "drivers/switch/Kconfig"
source "drivers/accessibility/Kconfig"
source "drivers/infiniband/Kconfig"

View File

@ -111,6 +111,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-y += mmc/
obj-$(CONFIG_MEMSTICK) += memstick/
obj-y += leds/
obj-$(CONFIG_SWITCH) += switch/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/

View File

@ -29,6 +29,8 @@
#include <linux/async.h>
#include <linux/suspend.h>
#include <linux/cpuidle.h>
#include <linux/timer.h>
#include "../base.h"
#include "power.h"
@ -54,6 +56,12 @@ struct suspend_stats suspend_stats;
static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;
struct dpm_watchdog {
struct device *dev;
struct task_struct *tsk;
struct timer_list timer;
};
static int async_error;
/**
@ -384,6 +392,56 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
return error;
}
/**
* dpm_wd_handler - Driver suspend / resume watchdog handler.
*
* Called when a driver has timed out suspending or resuming.
* There's not much we can do here to recover so BUG() out for
* a crash-dump
*/
static void dpm_wd_handler(unsigned long data)
{
struct dpm_watchdog *wd = (void *)data;
struct device *dev = wd->dev;
struct task_struct *tsk = wd->tsk;
dev_emerg(dev, "**** DPM device timeout ****\n");
show_stack(tsk, NULL);
BUG();
}
/**
* dpm_wd_set - Enable pm watchdog for given device.
* @wd: Watchdog. Must be allocated on the stack.
* @dev: Device to handle.
*/
static void dpm_wd_set(struct dpm_watchdog *wd, struct device *dev)
{
struct timer_list *timer = &wd->timer;
wd->dev = dev;
wd->tsk = get_current();
init_timer_on_stack(timer);
timer->expires = jiffies + HZ * 12;
timer->function = dpm_wd_handler;
timer->data = (unsigned long)wd;
add_timer(timer);
}
/**
* dpm_wd_clear - Disable pm watchdog.
* @wd: Watchdog to disable.
*/
static void dpm_wd_clear(struct dpm_watchdog *wd)
{
struct timer_list *timer = &wd->timer;
del_timer_sync(timer);
destroy_timer_on_stack(timer);
}
/*------------------------- Resume routines -------------------------*/
/**
@ -570,6 +628,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
pm_callback_t callback = NULL;
char *info = NULL;
int error = 0;
struct dpm_watchdog wd;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
@ -585,6 +644,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
* a resumed device, even if the device hasn't been completed yet.
*/
dev->power.is_prepared = false;
dpm_wd_set(&wd, dev);
if (!dev->power.is_suspended)
goto Unlock;
@ -636,6 +696,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
Unlock:
device_unlock(dev);
dpm_wd_clear(&wd);
Complete:
complete_all(&dev->power.completion);
@ -1053,6 +1114,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
pm_callback_t callback = NULL;
char *info = NULL;
int error = 0;
struct dpm_watchdog wd;
dpm_wait_for_children(dev, async);
@ -1075,6 +1137,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (dev->power.syscore)
goto Complete;
dpm_wd_set(&wd, dev);
device_lock(dev);
@ -1131,6 +1195,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
device_unlock(dev);
dpm_wd_clear(&wd);
Complete:
complete_all(&dev->power.completion);
if (error)

View File

@ -6,6 +6,19 @@ menu "Character devices"
source "drivers/tty/Kconfig"
config DEVMEM
bool "Memory device driver"
default y
help
The memory driver provides two character devices, mem and kmem, which
provide access to the system's memory. The mem device is a view of
physical memory, and each byte in the device corresponds to the
matching physical address. The kmem device is the same as mem, but
the addresses correspond to the kernel's virtual address space rather
than physical memory. These devices are standard parts of a Linux
system and most users should say Y here. You might say N if very
security conscience or memory is tight.
config DEVKMEM
bool "/dev/kmem virtual device support"
default y
@ -584,6 +597,10 @@ config DEVPORT
depends on ISA || PCI
default y
config DCC_TTY
tristate "DCC tty driver"
depends on ARM
source "drivers/s390/char/Kconfig"
config MSM_SMD_PKT

View File

@ -56,6 +56,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/
obj-$(CONFIG_DCC_TTY) += dcc_tty.o
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o

326
drivers/char/dcc_tty.c Normal file
View File

@ -0,0 +1,326 @@
/* drivers/char/dcc_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/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/console.h>
#include <linux/hrtimer.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
MODULE_DESCRIPTION("DCC TTY Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED;
static struct hrtimer g_dcc_timer;
static char g_dcc_buffer[16];
static int g_dcc_buffer_head;
static int g_dcc_buffer_count;
static unsigned g_dcc_write_delay_usecs = 1;
static struct tty_driver *g_dcc_tty_driver;
static struct tty_struct *g_dcc_tty;
static int g_dcc_tty_open_count;
static void dcc_poll_locked(void)
{
char ch;
int rch;
int written;
while (g_dcc_buffer_count) {
ch = g_dcc_buffer[g_dcc_buffer_head];
asm(
"mrc 14, 0, r15, c0, c1, 0\n"
"mcrcc 14, 0, %1, c0, c5, 0\n"
"movcc %0, #1\n"
"movcs %0, #0\n"
: "=r" (written)
: "r" (ch)
);
if (written) {
if (ch == '\n')
g_dcc_buffer[g_dcc_buffer_head] = '\r';
else {
g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer);
g_dcc_buffer_count--;
if (g_dcc_tty)
tty_wakeup(g_dcc_tty);
}
g_dcc_write_delay_usecs = 1;
} else {
if (g_dcc_write_delay_usecs > 0x100)
break;
g_dcc_write_delay_usecs <<= 1;
udelay(g_dcc_write_delay_usecs);
}
}
if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) {
asm(
"mrc 14, 0, %0, c0, c1, 0\n"
"tst %0, #(1 << 30)\n"
"moveq %0, #-1\n"
"mrcne 14, 0, %0, c0, c5, 0\n"
: "=r" (rch)
);
if (rch >= 0) {
ch = rch;
tty_insert_flip_string(g_dcc_tty, &ch, 1);
tty_flip_buffer_push(g_dcc_tty);
}
}
if (g_dcc_buffer_count)
hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL);
else
hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL);
}
static int dcc_tty_open(struct tty_struct * tty, struct file * filp)
{
int ret;
unsigned long irq_flags;
spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
if (g_dcc_tty == NULL || g_dcc_tty == tty) {
g_dcc_tty = tty;
g_dcc_tty_open_count++;
ret = 0;
} else
ret = -EBUSY;
spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret);
return ret;
}
static void dcc_tty_close(struct tty_struct * tty, struct file * filp)
{
printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags);
if (g_dcc_tty == tty) {
if (--g_dcc_tty_open_count == 0)
g_dcc_tty = NULL;
}
}
static int dcc_write(const unsigned char *buf_start, int count)
{
const unsigned char *buf = buf_start;
unsigned long irq_flags;
int copy_len;
int space_left;
int tail;
if (count < 1)
return 0;
spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
do {
tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer);
copy_len = ARRAY_SIZE(g_dcc_buffer) - tail;
space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
if (copy_len > space_left)
copy_len = space_left;
if (copy_len > count)
copy_len = count;
memcpy(&g_dcc_buffer[tail], buf, copy_len);
g_dcc_buffer_count += copy_len;
buf += copy_len;
count -= copy_len;
if (copy_len < count && copy_len < space_left) {
space_left -= copy_len;
copy_len = count;
if (copy_len > space_left) {
copy_len = space_left;
}
memcpy(g_dcc_buffer, buf, copy_len);
buf += copy_len;
count -= copy_len;
g_dcc_buffer_count += copy_len;
}
dcc_poll_locked();
space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
} while(count && space_left);
spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
return buf - buf_start;
}
static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
{
int ret;
/* printk("dcc_tty_write %p, %d\n", buf, count); */
ret = dcc_write(buf, count);
if (ret != count)
printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret);
return ret;
}
static int dcc_tty_write_room(struct tty_struct *tty)
{
int space_left;
unsigned long irq_flags;
spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count;
spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
return space_left;
}
static int dcc_tty_chars_in_buffer(struct tty_struct *tty)
{
int ret;
asm(
"mrc 14, 0, %0, c0, c1, 0\n"
"mov %0, %0, LSR #30\n"
"and %0, %0, #1\n"
: "=r" (ret)
);
return ret;
}
static void dcc_tty_unthrottle(struct tty_struct * tty)
{
unsigned long irq_flags;
spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
dcc_poll_locked();
spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
}
static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer)
{
unsigned long irq_flags;
spin_lock_irqsave(&g_dcc_tty_lock, irq_flags);
dcc_poll_locked();
spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags);
return HRTIMER_NORESTART;
}
void dcc_console_write(struct console *co, const char *b, unsigned count)
{
#if 1
dcc_write(b, count);
#else
/* blocking printk */
while (count > 0) {
int written;
written = dcc_write(b, count);
if (written) {
b += written;
count -= written;
}
}
#endif
}
static struct tty_driver *dcc_console_device(struct console *c, int *index)
{
*index = 0;
return g_dcc_tty_driver;
}
static int __init dcc_console_setup(struct console *co, char *options)
{
if (co->index != 0)
return -ENODEV;
return 0;
}
static struct console dcc_console =
{
.name = "ttyDCC",
.write = dcc_console_write,
.device = dcc_console_device,
.setup = dcc_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
static struct tty_operations dcc_tty_ops = {
.open = dcc_tty_open,
.close = dcc_tty_close,
.write = dcc_tty_write,
.write_room = dcc_tty_write_room,
.chars_in_buffer = dcc_tty_chars_in_buffer,
.unthrottle = dcc_tty_unthrottle,
};
static int __init dcc_tty_init(void)
{
int ret;
hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
g_dcc_timer.function = dcc_tty_timer_func;
g_dcc_tty_driver = alloc_tty_driver(1);
if (!g_dcc_tty_driver) {
printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n");
ret = -ENOMEM;
goto err_alloc_tty_driver_failed;
}
g_dcc_tty_driver->owner = THIS_MODULE;
g_dcc_tty_driver->driver_name = "dcc";
g_dcc_tty_driver->name = "ttyDCC";
g_dcc_tty_driver->major = 0; // auto assign
g_dcc_tty_driver->minor_start = 0;
g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
g_dcc_tty_driver->init_termios = tty_std_termios;
g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops);
ret = tty_register_driver(g_dcc_tty_driver);
if (ret) {
printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret);
goto err_tty_register_driver_failed;
}
tty_register_device(g_dcc_tty_driver, 0, NULL);
register_console(&dcc_console);
hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL);
return 0;
err_tty_register_driver_failed:
put_tty_driver(g_dcc_tty_driver);
g_dcc_tty_driver = NULL;
err_alloc_tty_driver_failed:
return ret;
}
static void __exit dcc_tty_exit(void)
{
int ret;
tty_unregister_device(g_dcc_tty_driver, 0);
ret = tty_unregister_driver(g_dcc_tty_driver);
if (ret < 0) {
printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret);
} else {
put_tty_driver(g_dcc_tty_driver);
}
g_dcc_tty_driver = NULL;
}
module_init(dcc_tty_init);
module_exit(dcc_tty_exit);

View File

@ -60,6 +60,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
}
#endif
#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
#ifdef CONFIG_STRICT_DEVMEM
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
@ -85,7 +86,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
return 1;
}
#endif
#endif
#ifdef CONFIG_DEVMEM
void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr)
{
}
@ -212,6 +215,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
*ppos += written;
return written;
}
#endif /* CONFIG_DEVMEM */
#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
int __weak phys_mem_access_prot_allowed(struct file *file,
unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
@ -333,6 +339,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
}
return 0;
}
#endif /* CONFIG_DEVMEM */
#ifdef CONFIG_DEVKMEM
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
@ -727,6 +734,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig)
return file->f_pos = 0;
}
#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@ -760,10 +769,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
return ret;
}
#endif
#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
static int open_port(struct inode *inode, struct file *filp)
{
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}
#endif
#define zero_lseek null_lseek
#define full_lseek null_lseek
@ -774,6 +787,7 @@ static int open_port(struct inode *inode, struct file *filp)
#define open_kmem open_mem
#define open_oldmem open_mem
#ifdef CONFIG_DEVMEM
static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
@ -782,6 +796,7 @@ static const struct file_operations mem_fops = {
.open = open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};
#endif
#ifdef CONFIG_DEVKMEM
static const struct file_operations kmem_fops = {
@ -851,7 +866,9 @@ static const struct memdev {
const struct file_operations *fops;
struct backing_dev_info *dev_info;
} devlist[] = {
#ifdef CONFIG_DEVMEM
[1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
#endif
#ifdef CONFIG_DEVKMEM
[2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif

View File

@ -102,6 +102,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
Be aware that not all cpufreq drivers support the conservative
governor. If unsure have a look at the help section of the
driver. Fallback governor will be the performance governor.
config CPU_FREQ_DEFAULT_GOV_INTERACTIVE
bool "interactive"
select CPU_FREQ_GOV_INTERACTIVE
help
Use the CPUFreq governor 'interactive' as default. This allows
you to get a full dynamic cpu frequency capable system by simply
loading your cpufreq low-level hardware driver, using the
'interactive' governor for latency-sensitive workloads.
endchoice
config CPU_FREQ_GOV_PERFORMANCE
@ -160,6 +170,23 @@ config CPU_FREQ_GOV_ONDEMAND
If in doubt, say N.
config CPU_FREQ_GOV_INTERACTIVE
tristate "'interactive' cpufreq policy governor"
help
'interactive' - This driver adds a dynamic cpufreq policy governor
designed for latency-sensitive workloads.
This governor attempts to reduce the latency of clock
increases so that the system is more responsive to
interactive workloads.
To compile this driver as a module, choose M here: the
module will be called cpufreq_interactive.
For details, take a look at linux/Documentation/cpu-freq.
If in doubt, say N.
config CPU_FREQ_GOV_CONSERVATIVE
tristate "'conservative' cpufreq governor"
depends on CPU_FREQ

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o
obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o
obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o
obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o
obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
# CPUfreq cross-arch helpers

File diff suppressed because it is too large Load Diff

View File

@ -341,6 +341,27 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
return 0;
}
static int cpufreq_stats_create_table_cpu(unsigned int cpu)
{
struct cpufreq_policy *policy;
struct cpufreq_frequency_table *table;
int ret = -ENODEV;
policy = cpufreq_cpu_get(cpu);
if (!policy)
return -ENODEV;
table = cpufreq_frequency_get_table(cpu);
if (!table)
goto out;
ret = cpufreq_stats_create_table(policy, table);
out:
cpufreq_cpu_put(policy);
return ret;
}
static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
@ -362,6 +383,10 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
cpufreq_stats_free_sysfs(cpu);
cpufreq_stats_free_table(cpu);
break;
case CPU_DOWN_FAILED:
case CPU_DOWN_FAILED_FROZEN:
cpufreq_stats_create_table_cpu(cpu);
break;
}
return NOTIFY_OK;
}

View File

@ -187,7 +187,12 @@ static inline int performance_multiplier(void)
/* for higher loadavg, we are more reluctant */
mult += 2 * get_loadavg();
/*
* this doesn't work as intended - it is almost always 0, but can
* sometimes, depending on workload, spike very high into the hundreds
* even when the average cpu load is under 10%.
*/
/* mult += 2 * get_loadavg(); */
/* for IO wait tasks (per cpu!) we add 5x each */
mult += 10 * nr_iowait_cpu(smp_processor_id());

View File

@ -1,2 +1,2 @@
obj-y += drm/ vga/
obj-y += drm/ vga/ ion/
obj-$(CONFIG_TEGRA_HOST1X) += host1x/

14
drivers/gpu/ion/Kconfig Normal file
View File

@ -0,0 +1,14 @@
menuconfig ION
tristate "Ion Memory Manager"
depends on ARM
select GENERIC_ALLOCATOR
select DMA_SHARED_BUFFER
help
Chose this option to enable the ION Memory Manager.
config ION_TEGRA
tristate "Ion for Tegra"
depends on ARCH_TEGRA && ION
help
Choose this option if you wish to use ion on an nVidia Tegra.

3
drivers/gpu/ion/Makefile Normal file
View File

@ -0,0 +1,3 @@
obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
ion_carveout_heap.o ion_chunk_heap.o
obj-$(CONFIG_ION_TEGRA) += tegra/

1437
drivers/gpu/ion/ion.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
/*
* drivers/gpu/ion/ion_carveout_heap.c
*
* Copyright (C) 2011 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/spinlock.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/ion.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion_priv.h"
#include <asm/mach/map.h>
struct ion_carveout_heap {
struct ion_heap heap;
struct gen_pool *pool;
ion_phys_addr_t base;
};
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
unsigned long size,
unsigned long align)
{
struct ion_carveout_heap *carveout_heap =
container_of(heap, struct ion_carveout_heap, heap);
unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);
if (!offset)
return ION_CARVEOUT_ALLOCATE_FAIL;
return offset;
}
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
unsigned long size)
{
struct ion_carveout_heap *carveout_heap =
container_of(heap, struct ion_carveout_heap, heap);
if (addr == ION_CARVEOUT_ALLOCATE_FAIL)
return;
gen_pool_free(carveout_heap->pool, addr, size);
}
static int ion_carveout_heap_phys(struct ion_heap *heap,
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
*addr = buffer->priv_phys;
*len = buffer->size;
return 0;
}
static int ion_carveout_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size, unsigned long align,
unsigned long flags)
{
buffer->priv_phys = ion_carveout_allocate(heap, size, align);
return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0;
}
static void ion_carveout_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
ion_carveout_free(heap, buffer->priv_phys, buffer->size);
buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL;
}
struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret) {
kfree(table);
return ERR_PTR(ret);
}
sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size,
0);
return table;
}
void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
sg_free_table(buffer->sg_table);
}
void *ion_carveout_heap_map_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
int mtype = MT_MEMORY_NONCACHED;
if (buffer->flags & ION_FLAG_CACHED)
mtype = MT_MEMORY;
return __arm_ioremap(buffer->priv_phys, buffer->size,
mtype);
}
void ion_carveout_heap_unmap_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
__arm_iounmap(buffer->vaddr);
buffer->vaddr = NULL;
return;
}
int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma)
{
return remap_pfn_range(vma, vma->vm_start,
__phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
pgprot_noncached(vma->vm_page_prot));
}
static struct ion_heap_ops carveout_heap_ops = {
.allocate = ion_carveout_heap_allocate,
.free = ion_carveout_heap_free,
.phys = ion_carveout_heap_phys,
.map_dma = ion_carveout_heap_map_dma,
.unmap_dma = ion_carveout_heap_unmap_dma,
.map_user = ion_carveout_heap_map_user,
.map_kernel = ion_carveout_heap_map_kernel,
.unmap_kernel = ion_carveout_heap_unmap_kernel,
};
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_carveout_heap *carveout_heap;
carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
if (!carveout_heap)
return ERR_PTR(-ENOMEM);
carveout_heap->pool = gen_pool_create(12, -1);
if (!carveout_heap->pool) {
kfree(carveout_heap);
return ERR_PTR(-ENOMEM);
}
carveout_heap->base = heap_data->base;
gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,
-1);
carveout_heap->heap.ops = &carveout_heap_ops;
carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
return &carveout_heap->heap;
}
void ion_carveout_heap_destroy(struct ion_heap *heap)
{
struct ion_carveout_heap *carveout_heap =
container_of(heap, struct ion_carveout_heap, heap);
gen_pool_destroy(carveout_heap->pool);
kfree(carveout_heap);
carveout_heap = NULL;
}

View File

@ -0,0 +1,210 @@
/*
* drivers/gpu/ion/ion_chunk_heap.c
*
* Copyright (C) 2012 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/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/ion.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion_priv.h"
#include <asm/mach/map.h>
struct ion_chunk_heap {
struct ion_heap heap;
struct gen_pool *pool;
ion_phys_addr_t base;
unsigned long chunk_size;
unsigned long size;
unsigned long allocated;
};
static int ion_chunk_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size, unsigned long align,
unsigned long flags)
{
struct ion_chunk_heap *chunk_heap =
container_of(heap, struct ion_chunk_heap, heap);
struct sg_table *table;
struct scatterlist *sg;
int ret, i;
unsigned long num_chunks;
if (ion_buffer_fault_user_mappings(buffer))
return -ENOMEM;
num_chunks = ALIGN(size, chunk_heap->chunk_size) /
chunk_heap->chunk_size;
buffer->size = num_chunks * chunk_heap->chunk_size;
if (buffer->size > chunk_heap->size - chunk_heap->allocated)
return -ENOMEM;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return -ENOMEM;
ret = sg_alloc_table(table, num_chunks, GFP_KERNEL);
if (ret) {
kfree(table);
return ret;
}
sg = table->sgl;
for (i = 0; i < num_chunks; i++) {
unsigned long paddr = gen_pool_alloc(chunk_heap->pool,
chunk_heap->chunk_size);
if (!paddr)
goto err;
sg_set_page(sg, phys_to_page(paddr), chunk_heap->chunk_size, 0);
sg = sg_next(sg);
}
buffer->priv_virt = table;
chunk_heap->allocated += buffer->size;
return 0;
err:
sg = table->sgl;
for (i -= 1; i >= 0; i--) {
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
sg_dma_len(sg));
sg = sg_next(sg);
}
sg_free_table(table);
kfree(table);
return -ENOMEM;
}
static void ion_chunk_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
struct ion_chunk_heap *chunk_heap =
container_of(heap, struct ion_chunk_heap, heap);
struct sg_table *table = buffer->priv_virt;
struct scatterlist *sg;
int i;
ion_heap_buffer_zero(buffer);
for_each_sg(table->sgl, sg, table->nents, i) {
if (ion_buffer_cached(buffer))
arm_dma_ops.sync_single_for_device(NULL,
pfn_to_dma(NULL, page_to_pfn(sg_page(sg))),
sg_dma_len(sg), DMA_BIDIRECTIONAL);
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
sg_dma_len(sg));
}
chunk_heap->allocated -= buffer->size;
sg_free_table(table);
kfree(table);
}
struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return buffer->priv_virt;
}
void ion_chunk_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return;
}
static struct ion_heap_ops chunk_heap_ops = {
.allocate = ion_chunk_heap_allocate,
.free = ion_chunk_heap_free,
.map_dma = ion_chunk_heap_map_dma,
.unmap_dma = ion_chunk_heap_unmap_dma,
.map_user = ion_heap_map_user,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
};
struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_chunk_heap *chunk_heap;
struct vm_struct *vm_struct;
pgprot_t pgprot = pgprot_writecombine(PAGE_KERNEL);
int i, ret;
chunk_heap = kzalloc(sizeof(struct ion_chunk_heap), GFP_KERNEL);
if (!chunk_heap)
return ERR_PTR(-ENOMEM);
chunk_heap->chunk_size = (unsigned long)heap_data->priv;
chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) +
PAGE_SHIFT, -1);
if (!chunk_heap->pool) {
ret = -ENOMEM;
goto error_gen_pool_create;
}
chunk_heap->base = heap_data->base;
chunk_heap->size = heap_data->size;
chunk_heap->allocated = 0;
vm_struct = get_vm_area(PAGE_SIZE, VM_ALLOC);
if (!vm_struct) {
ret = -ENOMEM;
goto error;
}
for (i = 0; i < chunk_heap->size; i += PAGE_SIZE) {
struct page *page = phys_to_page(chunk_heap->base + i);
struct page **pages = &page;
ret = map_vm_area(vm_struct, pgprot, &pages);
if (ret)
goto error_map_vm_area;
memset(vm_struct->addr, 0, PAGE_SIZE);
unmap_kernel_range((unsigned long)vm_struct->addr, PAGE_SIZE);
}
free_vm_area(vm_struct);
arm_dma_ops.sync_single_for_device(NULL,
pfn_to_dma(NULL, page_to_pfn(phys_to_page(heap_data->base))),
heap_data->size, DMA_BIDIRECTIONAL);
gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1);
chunk_heap->heap.ops = &chunk_heap_ops;
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
pr_info("%s: base %lu size %u align %ld\n", __func__, chunk_heap->base,
heap_data->size, heap_data->align);
return &chunk_heap->heap;
error_map_vm_area:
free_vm_area(vm_struct);
error:
gen_pool_destroy(chunk_heap->pool);
error_gen_pool_create:
kfree(chunk_heap);
return ERR_PTR(ret);
}
void ion_chunk_heap_destroy(struct ion_heap *heap)
{
struct ion_chunk_heap *chunk_heap =
container_of(heap, struct ion_chunk_heap, heap);
gen_pool_destroy(chunk_heap->pool);
kfree(chunk_heap);
chunk_heap = NULL;
}

297
drivers/gpu/ion/ion_heap.c Normal file
View File

@ -0,0 +1,297 @@
/*
* drivers/gpu/ion/ion_heap.c
*
* Copyright (C) 2011 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/err.h>
#include <linux/freezer.h>
#include <linux/ion.h>
#include <linux/kthread.h>
#include <linux/mm.h>
#include <linux/rtmutex.h>
#include <linux/sched.h>
#include <linux/scatterlist.h>
#include <linux/vmalloc.h>
#include "ion_priv.h"
void *ion_heap_map_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct scatterlist *sg;
int i, j;
void *vaddr;
pgprot_t pgprot;
struct sg_table *table = buffer->sg_table;
int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
struct page **pages = vmalloc(sizeof(struct page *) * npages);
struct page **tmp = pages;
if (!pages)
return 0;
if (buffer->flags & ION_FLAG_CACHED)
pgprot = PAGE_KERNEL;
else
pgprot = pgprot_writecombine(PAGE_KERNEL);
for_each_sg(table->sgl, sg, table->nents, i) {
int npages_this_entry = PAGE_ALIGN(sg_dma_len(sg)) / PAGE_SIZE;
struct page *page = sg_page(sg);
BUG_ON(i >= npages);
for (j = 0; j < npages_this_entry; j++) {
*(tmp++) = page++;
}
}
vaddr = vmap(pages, npages, VM_MAP, pgprot);
vfree(pages);
return vaddr;
}
void ion_heap_unmap_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
vunmap(buffer->vaddr);
}
int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma)
{
struct sg_table *table = buffer->sg_table;
unsigned long addr = vma->vm_start;
unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
struct scatterlist *sg;
int i;
for_each_sg(table->sgl, sg, table->nents, i) {
struct page *page = sg_page(sg);
unsigned long remainder = vma->vm_end - addr;
unsigned long len = sg_dma_len(sg);
if (offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
continue;
} else if (offset) {
page += offset / PAGE_SIZE;
len = sg_dma_len(sg) - offset;
offset = 0;
}
len = min(len, remainder);
remap_pfn_range(vma, addr, page_to_pfn(page), len,
vma->vm_page_prot);
addr += len;
if (addr >= vma->vm_end)
return 0;
}
return 0;
}
int ion_heap_buffer_zero(struct ion_buffer *buffer)
{
struct sg_table *table = buffer->sg_table;
pgprot_t pgprot;
struct scatterlist *sg;
struct vm_struct *vm_struct;
int i, j, ret = 0;
if (buffer->flags & ION_FLAG_CACHED)
pgprot = PAGE_KERNEL;
else
pgprot = pgprot_writecombine(PAGE_KERNEL);
vm_struct = get_vm_area(PAGE_SIZE, VM_ALLOC);
if (!vm_struct)
return -ENOMEM;
for_each_sg(table->sgl, sg, table->nents, i) {
struct page *page = sg_page(sg);
unsigned long len = sg_dma_len(sg);
for (j = 0; j < len / PAGE_SIZE; j++) {
struct page *sub_page = page + j;
struct page **pages = &sub_page;
ret = map_vm_area(vm_struct, pgprot, &pages);
if (ret)
goto end;
memset(vm_struct->addr, 0, PAGE_SIZE);
unmap_kernel_range((unsigned long)vm_struct->addr,
PAGE_SIZE);
}
}
end:
free_vm_area(vm_struct);
return ret;
}
void ion_heap_free_page(struct ion_buffer *buffer, struct page *page,
unsigned int order)
{
int i;
if (!ion_buffer_fault_user_mappings(buffer)) {
__free_pages(page, order);
return;
}
for (i = 0; i < (1 << order); i++)
__free_page(page + i);
}
void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer * buffer)
{
rt_mutex_lock(&heap->lock);
list_add(&buffer->list, &heap->free_list);
heap->free_list_size += buffer->size;
rt_mutex_unlock(&heap->lock);
wake_up(&heap->waitqueue);
}
size_t ion_heap_freelist_size(struct ion_heap *heap)
{
size_t size;
rt_mutex_lock(&heap->lock);
size = heap->free_list_size;
rt_mutex_unlock(&heap->lock);
return size;
}
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
{
struct ion_buffer *buffer, *tmp;
size_t total_drained = 0;
if (ion_heap_freelist_size(heap) == 0)
return 0;
rt_mutex_lock(&heap->lock);
if (size == 0)
size = heap->free_list_size;
list_for_each_entry_safe(buffer, tmp, &heap->free_list, list) {
if (total_drained >= size)
break;
list_del(&buffer->list);
ion_buffer_destroy(buffer);
heap->free_list_size -= buffer->size;
total_drained += buffer->size;
}
rt_mutex_unlock(&heap->lock);
return total_drained;
}
int ion_heap_deferred_free(void *data)
{
struct ion_heap *heap = data;
while (true) {
struct ion_buffer *buffer;
wait_event_freezable(heap->waitqueue,
ion_heap_freelist_size(heap) > 0);
rt_mutex_lock(&heap->lock);
if (list_empty(&heap->free_list)) {
rt_mutex_unlock(&heap->lock);
continue;
}
buffer = list_first_entry(&heap->free_list, struct ion_buffer,
list);
list_del(&buffer->list);
heap->free_list_size -= buffer->size;
rt_mutex_unlock(&heap->lock);
ion_buffer_destroy(buffer);
}
return 0;
}
int ion_heap_init_deferred_free(struct ion_heap *heap)
{
struct sched_param param = { .sched_priority = 0 };
INIT_LIST_HEAD(&heap->free_list);
heap->free_list_size = 0;
rt_mutex_init(&heap->lock);
init_waitqueue_head(&heap->waitqueue);
heap->task = kthread_run(ion_heap_deferred_free, heap,
"%s", heap->name);
sched_setscheduler(heap->task, SCHED_IDLE, &param);
if (IS_ERR(heap->task)) {
pr_err("%s: creating thread for deferred free failed\n",
__func__);
return PTR_RET(heap->task);
}
return 0;
}
struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_heap *heap = NULL;
switch (heap_data->type) {
case ION_HEAP_TYPE_SYSTEM_CONTIG:
heap = ion_system_contig_heap_create(heap_data);
break;
case ION_HEAP_TYPE_SYSTEM:
heap = ion_system_heap_create(heap_data);
break;
case ION_HEAP_TYPE_CARVEOUT:
heap = ion_carveout_heap_create(heap_data);
break;
case ION_HEAP_TYPE_CHUNK:
heap = ion_chunk_heap_create(heap_data);
break;
default:
pr_err("%s: Invalid heap type %d\n", __func__,
heap_data->type);
return ERR_PTR(-EINVAL);
}
if (IS_ERR_OR_NULL(heap)) {
pr_err("%s: error creating heap %s type %d base %lu size %u\n",
__func__, heap_data->name, heap_data->type,
heap_data->base, heap_data->size);
return ERR_PTR(-EINVAL);
}
heap->name = heap_data->name;
heap->id = heap_data->id;
return heap;
}
void ion_heap_destroy(struct ion_heap *heap)
{
if (!heap)
return;
switch (heap->type) {
case ION_HEAP_TYPE_SYSTEM_CONTIG:
ion_system_contig_heap_destroy(heap);
break;
case ION_HEAP_TYPE_SYSTEM:
ion_system_heap_destroy(heap);
break;
case ION_HEAP_TYPE_CARVEOUT:
ion_carveout_heap_destroy(heap);
break;
case ION_HEAP_TYPE_CHUNK:
ion_chunk_heap_destroy(heap);
break;
default:
pr_err("%s: Invalid heap type %d\n", __func__,
heap->type);
}
}

View File

@ -0,0 +1,200 @@
/*
* drivers/gpu/ion/ion_mem_pool.c
*
* Copyright (C) 2011 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/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "ion_priv.h"
struct ion_page_pool_item {
struct page *page;
struct list_head list;
};
static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
{
struct page *page = alloc_pages(pool->gfp_mask, pool->order);
if (!page)
return NULL;
/* this is only being used to flush the page for dma,
this api is not really suitable for calling from a driver
but no better way to flush a page for dma exist at this time */
arm_dma_ops.sync_single_for_device(NULL,
pfn_to_dma(NULL, page_to_pfn(page)),
PAGE_SIZE << pool->order,
DMA_BIDIRECTIONAL);
return page;
}
static void ion_page_pool_free_pages(struct ion_page_pool *pool,
struct page *page)
{
__free_pages(page, pool->order);
}
static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
{
struct ion_page_pool_item *item;
item = kmalloc(sizeof(struct ion_page_pool_item), GFP_KERNEL);
if (!item)
return -ENOMEM;
mutex_lock(&pool->mutex);
item->page = page;
if (PageHighMem(page)) {
list_add_tail(&item->list, &pool->high_items);
pool->high_count++;
} else {
list_add_tail(&item->list, &pool->low_items);
pool->low_count++;
}
mutex_unlock(&pool->mutex);
return 0;
}
static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
{
struct ion_page_pool_item *item;
struct page *page;
if (high) {
BUG_ON(!pool->high_count);
item = list_first_entry(&pool->high_items,
struct ion_page_pool_item, list);
pool->high_count--;
} else {
BUG_ON(!pool->low_count);
item = list_first_entry(&pool->low_items,
struct ion_page_pool_item, list);
pool->low_count--;
}
list_del(&item->list);
page = item->page;
kfree(item);
return page;
}
void *ion_page_pool_alloc(struct ion_page_pool *pool)
{
struct page *page = NULL;
BUG_ON(!pool);
mutex_lock(&pool->mutex);
if (pool->high_count)
page = ion_page_pool_remove(pool, true);
else if (pool->low_count)
page = ion_page_pool_remove(pool, false);
mutex_unlock(&pool->mutex);
if (!page)
page = ion_page_pool_alloc_pages(pool);
return page;
}
void ion_page_pool_free(struct ion_page_pool *pool, struct page* page)
{
int ret;
ret = ion_page_pool_add(pool, page);
if (ret)
ion_page_pool_free_pages(pool, page);
}
static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
{
int total = 0;
total += high ? (pool->high_count + pool->low_count) *
(1 << pool->order) :
pool->low_count * (1 << pool->order);
return total;
}
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
int nr_to_scan)
{
int nr_freed = 0;
int i;
bool high;
high = gfp_mask & __GFP_HIGHMEM;
if (nr_to_scan == 0)
return ion_page_pool_total(pool, high);
for (i = 0; i < nr_to_scan; i++) {
struct page *page;
mutex_lock(&pool->mutex);
if (high && pool->high_count) {
page = ion_page_pool_remove(pool, true);
} else if (pool->low_count) {
page = ion_page_pool_remove(pool, false);
} else {
mutex_unlock(&pool->mutex);
break;
}
mutex_unlock(&pool->mutex);
ion_page_pool_free_pages(pool, page);
nr_freed += (1 << pool->order);
}
return nr_freed;
}
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
{
struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
GFP_KERNEL);
if (!pool)
return NULL;
pool->high_count = 0;
pool->low_count = 0;
INIT_LIST_HEAD(&pool->low_items);
INIT_LIST_HEAD(&pool->high_items);
pool->gfp_mask = gfp_mask;
pool->order = order;
mutex_init(&pool->mutex);
plist_node_init(&pool->list, order);
return pool;
}
void ion_page_pool_destroy(struct ion_page_pool *pool)
{
kfree(pool);
}
static int __init ion_page_pool_init(void)
{
return 0;
}
static void __exit ion_page_pool_exit(void)
{
}
module_init(ion_page_pool_init);
module_exit(ion_page_pool_exit);

341
drivers/gpu/ion/ion_priv.h Normal file
View File

@ -0,0 +1,341 @@
/*
* drivers/gpu/ion/ion_priv.h
*
* Copyright (C) 2011 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 _ION_PRIV_H
#define _ION_PRIV_H
#include <linux/ion.h>
#include <linux/kref.h>
#include <linux/mm_types.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/shrinker.h>
#include <linux/types.h>
struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
/**
* struct ion_buffer - metadata for a particular buffer
* @ref: refernce count
* @node: node in the ion_device buffers tree
* @dev: back pointer to the ion_device
* @heap: back pointer to the heap the buffer came from
* @flags: buffer specific flags
* @size: size of the buffer
* @priv_virt: private data to the buffer representable as
* a void *
* @priv_phys: private data to the buffer representable as
* an ion_phys_addr_t (and someday a phys_addr_t)
* @lock: protects the buffers cnt fields
* @kmap_cnt: number of times the buffer is mapped to the kernel
* @vaddr: the kenrel mapping if kmap_cnt is not zero
* @dmap_cnt: number of times the buffer is mapped for dma
* @sg_table: the sg table for the buffer if dmap_cnt is not zero
* @dirty: bitmask representing which pages of this buffer have
* been dirtied by the cpu and need cache maintenance
* before dma
* @vmas: list of vma's mapping this buffer
* @handle_count: count of handles referencing this buffer
* @task_comm: taskcomm of last client to reference this buffer in a
* handle, used for debugging
* @pid: pid of last client to reference this buffer in a
* handle, used for debugging
*/
struct ion_buffer {
struct kref ref;
union {
struct rb_node node;
struct list_head list;
};
struct ion_device *dev;
struct ion_heap *heap;
unsigned long flags;
size_t size;
union {
void *priv_virt;
ion_phys_addr_t priv_phys;
};
struct mutex lock;
int kmap_cnt;
void *vaddr;
int dmap_cnt;
struct sg_table *sg_table;
unsigned long *dirty;
struct list_head vmas;
/* used to track orphaned buffers */
int handle_count;
char task_comm[TASK_COMM_LEN];
pid_t pid;
};
void ion_buffer_destroy(struct ion_buffer *buffer);
/**
* struct ion_heap_ops - ops to operate on a given heap
* @allocate: allocate memory
* @free: free memory
* @phys get physical address of a buffer (only define on
* physically contiguous heaps)
* @map_dma map the memory for dma to a scatterlist
* @unmap_dma unmap the memory for dma
* @map_kernel map memory to the kernel
* @unmap_kernel unmap memory to the kernel
* @map_user map memory to userspace
*/
struct ion_heap_ops {
int (*allocate) (struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags);
void (*free) (struct ion_buffer *buffer);
int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len);
struct sg_table *(*map_dma) (struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_dma) (struct ion_heap *heap, struct ion_buffer *buffer);
void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer);
void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer);
int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer,
struct vm_area_struct *vma);
};
/**
* heap flags - flags between the heaps and core ion code
*/
#define ION_HEAP_FLAG_DEFER_FREE (1 << 0)
/**
* struct ion_heap - represents a heap in the system
* @node: rb node to put the heap on the device's tree of heaps
* @dev: back pointer to the ion_device
* @type: type of heap
* @ops: ops struct as above
* @flags: flags
* @id: id of heap, also indicates priority of this heap when
* allocating. These are specified by platform data and
* MUST be unique
* @name: used for debugging
* @shrinker: a shrinker for the heap, if the heap caches system
* memory, it must define a shrinker to return it on low
* memory conditions, this includes system memory cached
* in the deferred free lists for heaps that support it
* @free_list: free list head if deferred free is used
* @free_list_size size of the deferred free list in bytes
* @lock: protects the free list
* @waitqueue: queue to wait on from deferred free thread
* @task: task struct of deferred free thread
* @debug_show: called when heap debug file is read to add any
* heap specific debug info to output
*
* Represents a pool of memory from which buffers can be made. In some
* systems the only heap is regular system memory allocated via vmalloc.
* On others, some blocks might require large physically contiguous buffers
* that are allocated from a specially reserved heap.
*/
struct ion_heap {
struct plist_node node;
struct ion_device *dev;
enum ion_heap_type type;
struct ion_heap_ops *ops;
unsigned long flags;
unsigned int id;
const char *name;
struct shrinker shrinker;
struct list_head free_list;
size_t free_list_size;
struct rt_mutex lock;
wait_queue_head_t waitqueue;
struct task_struct *task;
int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
};
/**
* ion_buffer_cached - this ion buffer is cached
* @buffer: buffer
*
* indicates whether this ion buffer is cached
*/
bool ion_buffer_cached(struct ion_buffer *buffer);
/**
* ion_buffer_fault_user_mappings - fault in user mappings of this buffer
* @buffer: buffer
*
* indicates whether userspace mappings of this buffer will be faulted
* in, this can affect how buffers are allocated from the heap.
*/
bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer);
/**
* ion_device_create - allocates and returns an ion device
* @custom_ioctl: arch specific ioctl function if applicable
*
* returns a valid device or -PTR_ERR
*/
struct ion_device *ion_device_create(long (*custom_ioctl)
(struct ion_client *client,
unsigned int cmd,
unsigned long arg));
/**
* ion_device_destroy - free and device and it's resource
* @dev: the device
*/
void ion_device_destroy(struct ion_device *dev);
/**
* ion_device_add_heap - adds a heap to the ion device
* @dev: the device
* @heap: the heap to add
*/
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap);
/**
* some helpers for common operations on buffers using the sg_table
* and vaddr fields
*/
void *ion_heap_map_kernel(struct ion_heap *, struct ion_buffer *);
void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *);
int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
struct vm_area_struct *);
int ion_heap_buffer_zero(struct ion_buffer *buffer);
/**
* ion_heap_init_deferred_free -- initialize deferred free functionality
* @heap: the heap
*
* If a heap sets the ION_HEAP_FLAG_DEFER_FREE flag this function will
* be called to setup deferred frees. Calls to free the buffer will
* return immediately and the actual free will occur some time later
*/
int ion_heap_init_deferred_free(struct ion_heap *heap);
/**
* ion_heap_freelist_add - add a buffer to the deferred free list
* @heap: the heap
* @buffer: the buffer
*
* Adds an item to the deferred freelist.
*/
void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer);
/**
* ion_heap_freelist_drain - drain the deferred free list
* @heap: the heap
* @size: ammount of memory to drain in bytes
*
* Drains the indicated amount of memory from the deferred freelist immediately.
* Returns the total amount freed. The total freed may be higher depending
* on the size of the items in the list, or lower if there is insufficient
* total memory on the freelist.
*/
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size);
/**
* ion_heap_freelist_size - returns the size of the freelist in bytes
* @heap: the heap
*/
size_t ion_heap_freelist_size(struct ion_heap *heap);
/**
* functions for creating and destroying the built in ion heaps.
* architectures can add their own custom architecture specific
* heaps as appropriate.
*/
struct ion_heap *ion_heap_create(struct ion_platform_heap *);
void ion_heap_destroy(struct ion_heap *);
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *);
void ion_system_heap_destroy(struct ion_heap *);
struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *);
void ion_system_contig_heap_destroy(struct ion_heap *);
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *);
void ion_carveout_heap_destroy(struct ion_heap *);
struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *);
void ion_chunk_heap_destroy(struct ion_heap *);
/**
* kernel api to allocate/free from carveout -- used when carveout is
* used to back an architecture specific custom heap
*/
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size,
unsigned long align);
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
unsigned long size);
/**
* The carveout heap returns physical addresses, since 0 may be a valid
* physical address, this is used to indicate allocation failed
*/
#define ION_CARVEOUT_ALLOCATE_FAIL -1
/**
* functions for creating and destroying a heap pool -- allows you
* to keep a pool of pre allocated memory to use from your heap. Keeping
* a pool of memory that is ready for dma, ie any cached mapping have been
* invalidated from the cache, provides a significant peformance benefit on
* many systems */
/**
* struct ion_page_pool - pagepool struct
* @high_count: number of highmem items in the pool
* @low_count: number of lowmem items in the pool
* @high_items: list of highmem items
* @low_items: list of lowmem items
* @shrinker: a shrinker for the items
* @mutex: lock protecting this struct and especially the count
* item list
* @alloc: function to be used to allocate pageory when the pool
* is empty
* @free: function to be used to free pageory back to the system
* when the shrinker fires
* @gfp_mask: gfp_mask to use from alloc
* @order: order of pages in the pool
* @list: plist node for list of pools
*
* Allows you to keep a pool of pre allocated pages to use from your heap.
* Keeping a pool of pages that is ready for dma, ie any cached mapping have
* been invalidated from the cache, provides a significant peformance benefit
* on many systems
*/
struct ion_page_pool {
int high_count;
int low_count;
struct list_head high_items;
struct list_head low_items;
struct mutex mutex;
gfp_t gfp_mask;
unsigned int order;
struct plist_node list;
};
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
void ion_page_pool_destroy(struct ion_page_pool *);
void *ion_page_pool_alloc(struct ion_page_pool *);
void ion_page_pool_free(struct ion_page_pool *, struct page *);
/** ion_page_pool_shrink - shrinks the size of the memory cached in the pool
* @pool: the pool
* @gfp_mask: the memory type to reclaim
* @nr_to_scan: number of items to shrink in pages
*
* returns the number of items freed in pages
*/
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
int nr_to_scan);
#endif /* _ION_PRIV_H */

View File

@ -0,0 +1,467 @@
/*
* drivers/gpu/ion/ion_system_heap.c
*
* Copyright (C) 2011 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/page.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/ion.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion_priv.h"
static unsigned int high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
__GFP_NOWARN | __GFP_NORETRY) &
~__GFP_WAIT;
static unsigned int low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
__GFP_NOWARN);
static const unsigned int orders[] = {8, 4, 0};
static const int num_orders = ARRAY_SIZE(orders);
static int order_to_index(unsigned int order)
{
int i;
for (i = 0; i < num_orders; i++)
if (order == orders[i])
return i;
BUG();
return -1;
}
static unsigned int order_to_size(int order)
{
return PAGE_SIZE << order;
}
struct ion_system_heap {
struct ion_heap heap;
struct ion_page_pool **pools;
};
struct page_info {
struct page *page;
unsigned int order;
struct list_head list;
};
static struct page *alloc_buffer_page(struct ion_system_heap *heap,
struct ion_buffer *buffer,
unsigned long order)
{
bool cached = ion_buffer_cached(buffer);
bool split_pages = ion_buffer_fault_user_mappings(buffer);
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
struct page *page;
if (!cached) {
page = ion_page_pool_alloc(pool);
} else {
gfp_t gfp_flags = low_order_gfp_flags;
if (order > 4)
gfp_flags = high_order_gfp_flags;
page = alloc_pages(gfp_flags, order);
if (!page)
return 0;
arm_dma_ops.sync_single_for_device(NULL,
pfn_to_dma(NULL, page_to_pfn(page)),
PAGE_SIZE << order, DMA_BIDIRECTIONAL);
}
if (!page)
return 0;
if (split_pages)
split_page(page, order);
return page;
}
static void free_buffer_page(struct ion_system_heap *heap,
struct ion_buffer *buffer, struct page *page,
unsigned int order)
{
bool cached = ion_buffer_cached(buffer);
bool split_pages = ion_buffer_fault_user_mappings(buffer);
int i;
if (!cached) {
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
ion_page_pool_free(pool, page);
} else if (split_pages) {
for (i = 0; i < (1 << order); i++)
__free_page(page + i);
} else {
__free_pages(page, order);
}
}
static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
struct ion_buffer *buffer,
unsigned long size,
unsigned int max_order)
{
struct page *page;
struct page_info *info;
int i;
for (i = 0; i < num_orders; i++) {
if (size < order_to_size(orders[i]))
continue;
if (max_order < orders[i])
continue;
page = alloc_buffer_page(heap, buffer, orders[i]);
if (!page)
continue;
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
info->page = page;
info->order = orders[i];
return info;
}
return NULL;
}
static int ion_system_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size, unsigned long align,
unsigned long flags)
{
struct ion_system_heap *sys_heap = container_of(heap,
struct ion_system_heap,
heap);
struct sg_table *table;
struct scatterlist *sg;
int ret;
struct list_head pages;
struct page_info *info, *tmp_info;
int i = 0;
long size_remaining = PAGE_ALIGN(size);
unsigned int max_order = orders[0];
bool split_pages = ion_buffer_fault_user_mappings(buffer);
INIT_LIST_HEAD(&pages);
while (size_remaining > 0) {
info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order);
if (!info)
goto err;
list_add_tail(&info->list, &pages);
size_remaining -= (1 << info->order) * PAGE_SIZE;
max_order = info->order;
i++;
}
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
goto err;
if (split_pages)
ret = sg_alloc_table(table, PAGE_ALIGN(size) / PAGE_SIZE,
GFP_KERNEL);
else
ret = sg_alloc_table(table, i, GFP_KERNEL);
if (ret)
goto err1;
sg = table->sgl;
list_for_each_entry_safe(info, tmp_info, &pages, list) {
struct page *page = info->page;
if (split_pages) {
for (i = 0; i < (1 << info->order); i++) {
sg_set_page(sg, page + i, PAGE_SIZE, 0);
sg = sg_next(sg);
}
} else {
sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE,
0);
sg = sg_next(sg);
}
list_del(&info->list);
kfree(info);
}
buffer->priv_virt = table;
return 0;
err1:
kfree(table);
err:
list_for_each_entry(info, &pages, list) {
free_buffer_page(sys_heap, buffer, info->page, info->order);
kfree(info);
}
return -ENOMEM;
}
void ion_system_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
struct ion_system_heap *sys_heap = container_of(heap,
struct ion_system_heap,
heap);
struct sg_table *table = buffer->sg_table;
bool cached = ion_buffer_cached(buffer);
struct scatterlist *sg;
LIST_HEAD(pages);
int i;
/* uncached pages come from the page pools, zero them before returning
for security purposes (other allocations are zerod at alloc time */
if (!cached)
ion_heap_buffer_zero(buffer);
for_each_sg(table->sgl, sg, table->nents, i)
free_buffer_page(sys_heap, buffer, sg_page(sg),
get_order(sg_dma_len(sg)));
sg_free_table(table);
kfree(table);
}
struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return buffer->priv_virt;
}
void ion_system_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return;
}
static struct ion_heap_ops system_heap_ops = {
.allocate = ion_system_heap_allocate,
.free = ion_system_heap_free,
.map_dma = ion_system_heap_map_dma,
.unmap_dma = ion_system_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_heap_map_user,
};
static int ion_system_heap_shrink(struct shrinker *shrinker,
struct shrink_control *sc) {
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
shrinker);
struct ion_system_heap *sys_heap = container_of(heap,
struct ion_system_heap,
heap);
int nr_total = 0;
int nr_freed = 0;
int i;
if (sc->nr_to_scan == 0)
goto end;
/* shrink the free list first, no point in zeroing the memory if
we're just going to reclaim it */
nr_freed += ion_heap_freelist_drain(heap, sc->nr_to_scan * PAGE_SIZE) /
PAGE_SIZE;
if (nr_freed >= sc->nr_to_scan)
goto end;
for (i = 0; i < num_orders; i++) {
struct ion_page_pool *pool = sys_heap->pools[i];
nr_freed += ion_page_pool_shrink(pool, sc->gfp_mask,
sc->nr_to_scan);
if (nr_freed >= sc->nr_to_scan)
break;
}
end:
/* total number of items is whatever the page pools are holding
plus whatever's in the freelist */
for (i = 0; i < num_orders; i++) {
struct ion_page_pool *pool = sys_heap->pools[i];
nr_total += ion_page_pool_shrink(pool, sc->gfp_mask, 0);
}
nr_total += ion_heap_freelist_size(heap) / PAGE_SIZE;
return nr_total;
}
static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
void *unused)
{
struct ion_system_heap *sys_heap = container_of(heap,
struct ion_system_heap,
heap);
int i;
for (i = 0; i < num_orders; i++) {
struct ion_page_pool *pool = sys_heap->pools[i];
seq_printf(s, "%d order %u highmem pages in pool = %lu total\n",
pool->high_count, pool->order,
(1 << pool->order) * PAGE_SIZE * pool->high_count);
seq_printf(s, "%d order %u lowmem pages in pool = %lu total\n",
pool->low_count, pool->order,
(1 << pool->order) * PAGE_SIZE * pool->low_count);
}
return 0;
}
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
{
struct ion_system_heap *heap;
int i;
heap = kzalloc(sizeof(struct ion_system_heap), GFP_KERNEL);
if (!heap)
return ERR_PTR(-ENOMEM);
heap->heap.ops = &system_heap_ops;
heap->heap.type = ION_HEAP_TYPE_SYSTEM;
heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
heap->pools = kzalloc(sizeof(struct ion_page_pool *) * num_orders,
GFP_KERNEL);
if (!heap->pools)
goto err_alloc_pools;
for (i = 0; i < num_orders; i++) {
struct ion_page_pool *pool;
gfp_t gfp_flags = low_order_gfp_flags;
if (orders[i] > 4)
gfp_flags = high_order_gfp_flags;
pool = ion_page_pool_create(gfp_flags, orders[i]);
if (!pool)
goto err_create_pool;
heap->pools[i] = pool;
}
heap->heap.shrinker.shrink = ion_system_heap_shrink;
heap->heap.shrinker.seeks = DEFAULT_SEEKS;
heap->heap.shrinker.batch = 0;
register_shrinker(&heap->heap.shrinker);
heap->heap.debug_show = ion_system_heap_debug_show;
return &heap->heap;
err_create_pool:
for (i = 0; i < num_orders; i++)
if (heap->pools[i])
ion_page_pool_destroy(heap->pools[i]);
kfree(heap->pools);
err_alloc_pools:
kfree(heap);
return ERR_PTR(-ENOMEM);
}
void ion_system_heap_destroy(struct ion_heap *heap)
{
struct ion_system_heap *sys_heap = container_of(heap,
struct ion_system_heap,
heap);
int i;
for (i = 0; i < num_orders; i++)
ion_page_pool_destroy(sys_heap->pools[i]);
kfree(sys_heap->pools);
kfree(sys_heap);
}
static int ion_system_contig_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long len,
unsigned long align,
unsigned long flags)
{
buffer->priv_virt = kzalloc(len, GFP_KERNEL);
if (!buffer->priv_virt)
return -ENOMEM;
return 0;
}
void ion_system_contig_heap_free(struct ion_buffer *buffer)
{
kfree(buffer->priv_virt);
}
static int ion_system_contig_heap_phys(struct ion_heap *heap,
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
*addr = virt_to_phys(buffer->priv_virt);
*len = buffer->size;
return 0;
}
struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret) {
kfree(table);
return ERR_PTR(ret);
}
sg_set_page(table->sgl, virt_to_page(buffer->priv_virt), buffer->size,
0);
return table;
}
void ion_system_contig_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
sg_free_table(buffer->sg_table);
kfree(buffer->sg_table);
}
int ion_system_contig_heap_map_user(struct ion_heap *heap,
struct ion_buffer *buffer,
struct vm_area_struct *vma)
{
unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt));
return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
static struct ion_heap_ops kmalloc_ops = {
.allocate = ion_system_contig_heap_allocate,
.free = ion_system_contig_heap_free,
.phys = ion_system_contig_heap_phys,
.map_dma = ion_system_contig_heap_map_dma,
.unmap_dma = ion_system_contig_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_system_contig_heap_map_user,
};
struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused)
{
struct ion_heap *heap;
heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
if (!heap)
return ERR_PTR(-ENOMEM);
heap->ops = &kmalloc_ops;
heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
return heap;
}
void ion_system_contig_heap_destroy(struct ion_heap *heap)
{
kfree(heap);
}

View File

@ -0,0 +1,114 @@
/*
* drivers/gpu/ion/ion_system_mapper.c
*
* Copyright (C) 2011 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/err.h>
#include <linux/ion.h>
#include <linux/memory.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion_priv.h"
/*
* This mapper is valid for any heap that allocates memory that already has
* a kernel mapping, this includes vmalloc'd memory, kmalloc'd memory,
* pages obtained via io_remap, etc.
*/
static void *ion_kernel_mapper_map(struct ion_mapper *mapper,
struct ion_buffer *buffer,
struct ion_mapping **mapping)
{
if (!((1 << buffer->heap->type) & mapper->heap_mask)) {
pr_err("%s: attempting to map an unsupported heap\n", __func__);
return ERR_PTR(-EINVAL);
}
/* XXX REVISIT ME!!! */
*((unsigned long *)mapping) = (unsigned long)buffer->priv;
return buffer->priv;
}
static void ion_kernel_mapper_unmap(struct ion_mapper *mapper,
struct ion_buffer *buffer,
struct ion_mapping *mapping)
{
if (!((1 << buffer->heap->type) & mapper->heap_mask))
pr_err("%s: attempting to unmap an unsupported heap\n",
__func__);
}
static void *ion_kernel_mapper_map_kernel(struct ion_mapper *mapper,
struct ion_buffer *buffer,
struct ion_mapping *mapping)
{
if (!((1 << buffer->heap->type) & mapper->heap_mask)) {
pr_err("%s: attempting to unmap an unsupported heap\n",
__func__);
return ERR_PTR(-EINVAL);
}
return buffer->priv;
}
static int ion_kernel_mapper_map_user(struct ion_mapper *mapper,
struct ion_buffer *buffer,
struct vm_area_struct *vma,
struct ion_mapping *mapping)
{
int ret;
switch (buffer->heap->type) {
case ION_HEAP_KMALLOC:
{
unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv));
ret = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
break;
}
case ION_HEAP_VMALLOC:
ret = remap_vmalloc_range(vma, buffer->priv, vma->vm_pgoff);
break;
default:
pr_err("%s: attempting to map unsupported heap to userspace\n",
__func__);
return -EINVAL;
}
return ret;
}
static struct ion_mapper_ops ops = {
.map = ion_kernel_mapper_map,
.map_kernel = ion_kernel_mapper_map_kernel,
.map_user = ion_kernel_mapper_map_user,
.unmap = ion_kernel_mapper_unmap,
};
struct ion_mapper *ion_system_mapper_create(void)
{
struct ion_mapper *mapper;
mapper = kzalloc(sizeof(struct ion_mapper), GFP_KERNEL);
if (!mapper)
return ERR_PTR(-ENOMEM);
mapper->type = ION_SYSTEM_MAPPER;
mapper->ops = &ops;
mapper->heap_mask = (1 << ION_HEAP_VMALLOC) | (1 << ION_HEAP_KMALLOC);
return mapper;
}
void ion_system_mapper_destroy(struct ion_mapper *mapper)
{
kfree(mapper);
}

View File

@ -0,0 +1 @@
obj-y += tegra_ion.o

View File

@ -0,0 +1,96 @@
/*
* drivers/gpu/tegra/tegra_ion.c
*
* Copyright (C) 2011 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/err.h>
#include <linux/ion.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../ion_priv.h"
struct ion_device *idev;
struct ion_mapper *tegra_user_mapper;
int num_heaps;
struct ion_heap **heaps;
int tegra_ion_probe(struct platform_device *pdev)
{
struct ion_platform_data *pdata = pdev->dev.platform_data;
int err;
int i;
num_heaps = pdata->nr;
heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL);
idev = ion_device_create(NULL);
if (IS_ERR_OR_NULL(idev)) {
kfree(heaps);
return PTR_ERR(idev);
}
/* create the heaps as specified in the board file */
for (i = 0; i < num_heaps; i++) {
struct ion_platform_heap *heap_data = &pdata->heaps[i];
heaps[i] = ion_heap_create(heap_data);
if (IS_ERR_OR_NULL(heaps[i])) {
err = PTR_ERR(heaps[i]);
goto err;
}
ion_device_add_heap(idev, heaps[i]);
}
platform_set_drvdata(pdev, idev);
return 0;
err:
for (i = 0; i < num_heaps; i++) {
if (heaps[i])
ion_heap_destroy(heaps[i]);
}
kfree(heaps);
return err;
}
int tegra_ion_remove(struct platform_device *pdev)
{
struct ion_device *idev = platform_get_drvdata(pdev);
int i;
ion_device_destroy(idev);
for (i = 0; i < num_heaps; i++)
ion_heap_destroy(heaps[i]);
kfree(heaps);
return 0;
}
static struct platform_driver ion_driver = {
.probe = tegra_ion_probe,
.remove = tegra_ion_remove,
.driver = { .name = "ion-tegra" }
};
static int __init ion_init(void)
{
return platform_driver_register(&ion_driver);
}
static void __exit ion_exit(void)
{
platform_driver_unregister(&ion_driver);
}
module_init(ion_init);
module_exit(ion_exit);

View File

@ -1321,8 +1321,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
if (drv->input_configured)
drv->input_configured(hid, hidinput);
if (drv->input_configured &&
drv->input_configured(hid, hidinput))
goto out_cleanup;
if (input_register_device(hidinput->input))
goto out_cleanup;
hidinput = NULL;
@ -1343,8 +1344,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
if (hidinput) {
if (drv->input_configured)
drv->input_configured(hid, hidinput);
if (drv->input_configured &&
drv->input_configured(hid, hidinput))
goto out_cleanup;
if (input_register_device(hidinput->input))
goto out_cleanup;
}

View File

@ -445,6 +445,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
(usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
td->mt_flags |= INPUT_MT_POINTER;
/* Only map fields from TouchScreen or TouchPad collections.
* We need to ignore fields that belong to other collections
* such as Mouse that might have the same GenericDesktop usages. */
if (field->application == HID_DG_TOUCHSCREEN)
set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
else if (field->application == HID_DG_TOUCHPAD)
set_bit(INPUT_PROP_POINTER, hi->input->propbit);
else
return 0;
if (usage->usage_index)
prev_usage = &field->usage[usage->usage_index - 1];
@ -770,12 +780,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
mt_sync_frame(td, report->field[0]->hidinput->input);
}
static void mt_touch_input_configured(struct hid_device *hdev,
static int mt_touch_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
struct input_dev *input = hi->input;
int ret;
if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
@ -790,9 +801,12 @@ static void mt_touch_input_configured(struct hid_device *hdev,
if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
td->mt_flags |= INPUT_MT_DROP_UNUSED;
input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
if (ret)
return ret;
td->mt_flags = 0;
return 0;
}
static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@ -925,19 +939,21 @@ static void mt_post_parse(struct mt_device *td)
cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
}
static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
char *name = kstrdup(hdev->name, GFP_KERNEL);
int ret = 0;
if (name)
hi->input->name = name;
if (hi->report->id == td->mt_report_id)
mt_touch_input_configured(hdev, hi);
ret = mt_touch_input_configured(hdev, hi);
if (hi->report->id == td->pen_report_id)
mt_pen_input_configured(hdev, hi);
return ret;
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)

View File

@ -35,6 +35,7 @@
*/
struct iio_event_interface {
wait_queue_head_t wait;
struct mutex read_lock;
DECLARE_KFIFO(det_events, struct iio_event_data, 16);
struct list_head dev_attr_list;
@ -97,14 +98,16 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
if (count < sizeof(struct iio_event_data))
return -EINVAL;
spin_lock_irq(&ev_int->wait.lock);
if (mutex_lock_interruptible(&ev_int->read_lock))
return -ERESTARTSYS;
if (kfifo_is_empty(&ev_int->det_events)) {
if (filep->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto error_unlock;
}
/* Blocking on device; waiting for something to be there */
ret = wait_event_interruptible_locked_irq(ev_int->wait,
ret = wait_event_interruptible(ev_int->wait,
!kfifo_is_empty(&ev_int->det_events));
if (ret)
goto error_unlock;
@ -114,7 +117,7 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
error_unlock:
spin_unlock_irq(&ev_int->wait.lock);
mutex_unlock(&ev_int->read_lock);
return ret ? ret : copied;
}
@ -371,6 +374,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int)
{
INIT_KFIFO(ev_int->det_events);
init_waitqueue_head(&ev_int->wait);
mutex_init(&ev_int->read_lock);
}
static const char *iio_event_group_name = "events";
@ -434,6 +438,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
error_free_setup_event_lines:
__iio_remove_event_config_attrs(indio_dev);
mutex_destroy(&indio_dev->event_interface->read_lock);
kfree(indio_dev->event_interface);
error_ret:
@ -446,5 +451,6 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev)
return;
__iio_remove_event_config_attrs(indio_dev);
kfree(indio_dev->event_interface->group.attrs);
mutex_destroy(&indio_dev->event_interface->read_lock);
kfree(indio_dev->event_interface);
}

View File

@ -174,6 +174,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.
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"

View File

@ -25,3 +25,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o

View File

@ -24,6 +24,7 @@
#include <linux/major.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/wakelock.h>
#include "input-compat.h"
struct evdev {
@ -44,6 +45,9 @@ struct evdev_client {
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct wake_lock wake_lock;
bool use_wake_lock;
char name[28];
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
@ -71,10 +75,14 @@ static void __pass_event(struct evdev_client *client,
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
if (client->use_wake_lock)
wake_unlock(&client->wake_lock);
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
if (client->use_wake_lock)
wake_lock(&client->wake_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
}
@ -289,6 +297,8 @@ static int evdev_release(struct inode *inode, struct file *file)
mutex_unlock(&evdev->mutex);
evdev_detach_client(evdev, client);
if (client->use_wake_lock)
wake_lock_destroy(&client->wake_lock);
kfree(client);
evdev_close_device(evdev);
@ -320,6 +330,8 @@ static int evdev_open(struct inode *inode, struct file *file)
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
snprintf(client->name, sizeof(client->name), "%s-%d",
dev_name(&evdev->dev), task_tgid_vnr(current));
client->evdev = evdev;
evdev_attach_client(evdev, client);
@ -386,6 +398,9 @@ static int evdev_fetch_next_event(struct evdev_client *client,
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
if (client->use_wake_lock &&
client->packet_head == client->tail)
wake_unlock(&client->wake_lock);
}
spin_unlock_irq(&client->buffer_lock);
@ -674,6 +689,35 @@ static int evdev_handle_mt_request(struct input_dev *dev,
return 0;
}
static int evdev_enable_suspend_block(struct evdev *evdev,
struct evdev_client *client)
{
if (client->use_wake_lock)
return 0;
spin_lock_irq(&client->buffer_lock);
wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
client->use_wake_lock = true;
if (client->packet_head != client->tail)
wake_lock(&client->wake_lock);
spin_unlock_irq(&client->buffer_lock);
return 0;
}
static int evdev_disable_suspend_block(struct evdev *evdev,
struct evdev_client *client)
{
if (!client->use_wake_lock)
return 0;
spin_lock_irq(&client->buffer_lock);
client->use_wake_lock = false;
wake_lock_destroy(&client->wake_lock);
spin_unlock_irq(&client->buffer_lock);
return 0;
}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
@ -755,6 +799,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
case EVIOCSKEYCODE_V2:
return evdev_handle_set_keycode_v2(dev, p);
case EVIOCGSUSPENDBLOCK:
return put_user(client->use_wake_lock, ip);
case EVIOCSSUSPENDBLOCK:
if (p)
return evdev_enable_suspend_block(evdev, client);
else
return evdev_disable_suspend_block(evdev, client);
}
size = _IOC_SIZE(cmd);

239
drivers/input/keyreset.c Normal file
View File

@ -0,0 +1,239 @@
/* 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/slab.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 (*reset_fn)(void);
};
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);
if (state->reset_fn) {
restart_requested = state->reset_fn();
} else {
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);
}
}
if (pdata->reset_fn)
state->reset_fn = pdata->reset_fn;
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);

View File

@ -299,6 +299,17 @@ config INPUT_ATI_REMOTE2
To compile this driver as a module, choose M here: the module will be
called ati_remote2.
config INPUT_KEYCHORD
tristate "Key chord input driver support"
help
Say Y here if you want to enable the key chord driver
accessible at /dev/keychord. This driver can be used
for receiving notifications when client specified key
combinations are pressed.
To compile this driver as a module, choose M here: the
module will be called keychord.
config INPUT_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control"
depends on USB_ARCH_HAS_HCD
@ -434,6 +445,11 @@ config INPUT_SGI_BTNS
To compile this driver as a module, choose M here: the
module will be called sgi_btns.
config INPUT_GPIO
tristate "GPIO driver support"
help
Say Y here if you want to support gpio based keys, wheels etc...
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO

View File

@ -28,9 +28,11 @@ obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o

View File

@ -0,0 +1,192 @@
/* drivers/input/misc/gpio_axis.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/kernel.h>
#include <linux/gpio.h>
#include <linux/gpio_event.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
struct gpio_axis_state {
struct gpio_event_input_devs *input_devs;
struct gpio_event_axis_info *info;
uint32_t pos;
};
uint16_t gpio_axis_4bit_gray_map_table[] = {
[0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */
[0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */
[0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */
[0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */
[0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */
[0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */
[0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */
[0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */
};
uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in)
{
return gpio_axis_4bit_gray_map_table[in];
}
uint16_t gpio_axis_5bit_singletrack_map_table[] = {
[0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */
[0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */
[0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */
[0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */
[0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */
[0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */
[0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */
[0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */
[0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */
[0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */
};
uint16_t gpio_axis_5bit_singletrack_map(
struct gpio_event_axis_info *info, uint16_t in)
{
return gpio_axis_5bit_singletrack_map_table[in];
}
static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
{
struct gpio_event_axis_info *ai = as->info;
int i;
int change;
uint16_t state = 0;
uint16_t pos;
uint16_t old_pos = as->pos;
for (i = ai->count - 1; i >= 0; i--)
state = (state << 1) | gpio_get_value(ai->gpio[i]);
pos = ai->map(ai, state);
if (ai->flags & GPIOEAF_PRINT_RAW)
pr_info("axis %d-%d raw %x, pos %d -> %d\n",
ai->type, ai->code, state, old_pos, pos);
if (report && pos != old_pos) {
if (ai->type == EV_REL) {
change = (ai->decoded_size + pos - old_pos) %
ai->decoded_size;
if (change > ai->decoded_size / 2)
change -= ai->decoded_size;
if (change == ai->decoded_size / 2) {
if (ai->flags & GPIOEAF_PRINT_EVENT)
pr_info("axis %d-%d unknown direction, "
"pos %d -> %d\n", ai->type,
ai->code, old_pos, pos);
change = 0; /* no closest direction */
}
if (ai->flags & GPIOEAF_PRINT_EVENT)
pr_info("axis %d-%d change %d\n",
ai->type, ai->code, change);
input_report_rel(as->input_devs->dev[ai->dev],
ai->code, change);
} else {
if (ai->flags & GPIOEAF_PRINT_EVENT)
pr_info("axis %d-%d now %d\n",
ai->type, ai->code, pos);
input_event(as->input_devs->dev[ai->dev],
ai->type, ai->code, pos);
}
input_sync(as->input_devs->dev[ai->dev]);
}
as->pos = pos;
}
static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
{
struct gpio_axis_state *as = dev_id;
gpio_event_update_axis(as, 1);
return IRQ_HANDLED;
}
int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
struct gpio_event_info *info, void **data, int func)
{
int ret;
int i;
int irq;
struct gpio_event_axis_info *ai;
struct gpio_axis_state *as;
ai = container_of(info, struct gpio_event_axis_info, info);
if (func == GPIO_EVENT_FUNC_SUSPEND) {
for (i = 0; i < ai->count; i++)
disable_irq(gpio_to_irq(ai->gpio[i]));
return 0;
}
if (func == GPIO_EVENT_FUNC_RESUME) {
for (i = 0; i < ai->count; i++)
enable_irq(gpio_to_irq(ai->gpio[i]));
return 0;
}
if (func == GPIO_EVENT_FUNC_INIT) {
*data = as = kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
ret = -ENOMEM;
goto err_alloc_axis_state_failed;
}
as->input_devs = input_devs;
as->info = ai;
if (ai->dev >= input_devs->count) {
pr_err("gpio_event_axis: bad device index %d >= %d "
"for %d:%d\n", ai->dev, input_devs->count,
ai->type, ai->code);
ret = -EINVAL;
goto err_bad_device_index;
}
input_set_capability(input_devs->dev[ai->dev],
ai->type, ai->code);
if (ai->type == EV_ABS) {
input_set_abs_params(input_devs->dev[ai->dev], ai->code,
0, ai->decoded_size - 1, 0, 0);
}
for (i = 0; i < ai->count; i++) {
ret = gpio_request(ai->gpio[i], "gpio_event_axis");
if (ret < 0)
goto err_request_gpio_failed;
ret = gpio_direction_input(ai->gpio[i]);
if (ret < 0)
goto err_gpio_direction_input_failed;
ret = irq = gpio_to_irq(ai->gpio[i]);
if (ret < 0)
goto err_get_irq_num_failed;
ret = request_irq(irq, gpio_axis_irq_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"gpio_event_axis", as);
if (ret < 0)
goto err_request_irq_failed;
}
gpio_event_update_axis(as, 0);
return 0;
}
ret = 0;
as = *data;
for (i = ai->count - 1; i >= 0; i--) {
free_irq(gpio_to_irq(ai->gpio[i]), as);
err_request_irq_failed:
err_get_irq_num_failed:
err_gpio_direction_input_failed:
gpio_free(ai->gpio[i]);
err_request_gpio_failed:
;
}
err_bad_device_index:
kfree(as);
*data = NULL;
err_alloc_axis_state_failed:
return ret;
}

View File

@ -0,0 +1,228 @@
/* drivers/input/misc/gpio_event.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/input.h>
#include <linux/gpio_event.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct gpio_event {
struct gpio_event_input_devs *input_devs;
const struct gpio_event_platform_data *info;
void *state[0];
};
static int gpio_input_event(
struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
int i;
int devnr;
int ret = 0;
int tmp_ret;
struct gpio_event_info **ii;
struct gpio_event *ip = input_get_drvdata(dev);
for (devnr = 0; devnr < ip->input_devs->count; devnr++)
if (ip->input_devs->dev[devnr] == dev)
break;
if (devnr == ip->input_devs->count) {
pr_err("gpio_input_event: unknown device %p\n", dev);
return -EIO;
}
for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
if ((*ii)->event) {
tmp_ret = (*ii)->event(ip->input_devs, *ii,
&ip->state[i],
devnr, type, code, value);
if (tmp_ret)
ret = tmp_ret;
}
}
return ret;
}
static int gpio_event_call_all_func(struct gpio_event *ip, int func)
{
int i;
int ret;
struct gpio_event_info **ii;
if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
ii = ip->info->info;
for (i = 0; i < ip->info->info_count; i++, ii++) {
if ((*ii)->func == NULL) {
ret = -ENODEV;
pr_err("gpio_event_probe: Incomplete pdata, "
"no function\n");
goto err_no_func;
}
if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend)
continue;
ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i],
func);
if (ret) {
pr_err("gpio_event_probe: function failed\n");
goto err_func_failed;
}
}
return 0;
}
ret = 0;
i = ip->info->info_count;
ii = ip->info->info + i;
while (i > 0) {
i--;
ii--;
if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend)
continue;
(*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1);
err_func_failed:
err_no_func:
;
}
return ret;
}
static void __maybe_unused gpio_event_suspend(struct gpio_event *ip)
{
gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
if (ip->info->power)
ip->info->power(ip->info, 0);
}
static void __maybe_unused gpio_event_resume(struct gpio_event *ip)
{
if (ip->info->power)
ip->info->power(ip->info, 1);
gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
}
static int gpio_event_probe(struct platform_device *pdev)
{
int err;
struct gpio_event *ip;
struct gpio_event_platform_data *event_info;
int dev_count = 1;
int i;
int registered = 0;
event_info = pdev->dev.platform_data;
if (event_info == NULL) {
pr_err("gpio_event_probe: No pdata\n");
return -ENODEV;
}
if ((!event_info->name && !event_info->names[0]) ||
!event_info->info || !event_info->info_count) {
pr_err("gpio_event_probe: Incomplete pdata\n");
return -ENODEV;
}
if (!event_info->name)
while (event_info->names[dev_count])
dev_count++;
ip = kzalloc(sizeof(*ip) +
sizeof(ip->state[0]) * event_info->info_count +
sizeof(*ip->input_devs) +
sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL);
if (ip == NULL) {
err = -ENOMEM;
pr_err("gpio_event_probe: Failed to allocate private data\n");
goto err_kp_alloc_failed;
}
ip->input_devs = (void*)&ip->state[event_info->info_count];
platform_set_drvdata(pdev, ip);
for (i = 0; i < dev_count; i++) {
struct input_dev *input_dev = input_allocate_device();
if (input_dev == NULL) {
err = -ENOMEM;
pr_err("gpio_event_probe: "
"Failed to allocate input device\n");
goto err_input_dev_alloc_failed;
}
input_set_drvdata(input_dev, ip);
input_dev->name = event_info->name ?
event_info->name : event_info->names[i];
input_dev->event = gpio_input_event;
ip->input_devs->dev[i] = input_dev;
}
ip->input_devs->count = dev_count;
ip->info = event_info;
if (event_info->power)
ip->info->power(ip->info, 1);
err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
if (err)
goto err_call_all_func_failed;
for (i = 0; i < dev_count; i++) {
err = input_register_device(ip->input_devs->dev[i]);
if (err) {
pr_err("gpio_event_probe: Unable to register %s "
"input device\n", ip->input_devs->dev[i]->name);
goto err_input_register_device_failed;
}
registered++;
}
return 0;
err_input_register_device_failed:
gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
err_call_all_func_failed:
if (event_info->power)
ip->info->power(ip->info, 0);
for (i = 0; i < registered; i++)
input_unregister_device(ip->input_devs->dev[i]);
for (i = dev_count - 1; i >= registered; i--) {
input_free_device(ip->input_devs->dev[i]);
err_input_dev_alloc_failed:
;
}
kfree(ip);
err_kp_alloc_failed:
return err;
}
static int gpio_event_remove(struct platform_device *pdev)
{
struct gpio_event *ip = platform_get_drvdata(pdev);
int i;
gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
if (ip->info->power)
ip->info->power(ip->info, 0);
for (i = 0; i < ip->input_devs->count; i++)
input_unregister_device(ip->input_devs->dev[i]);
kfree(ip);
return 0;
}
static struct platform_driver gpio_event_driver = {
.probe = gpio_event_probe,
.remove = gpio_event_remove,
.driver = {
.name = GPIO_EVENT_DEV_NAME,
},
};
module_platform_driver(gpio_event_driver);
MODULE_DESCRIPTION("GPIO Event Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,390 @@
/* drivers/input/misc/gpio_input.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/kernel.h>
#include <linux/gpio.h>
#include <linux/gpio_event.h>
#include <linux/hrtimer.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/pm_wakeup.h>
enum {
DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */
DEBOUNCE_PRESSED = BIT(1),
DEBOUNCE_NOTPRESSED = BIT(2),
DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */
DEBOUNCE_POLL = BIT(4), /* Stable polling state */
DEBOUNCE_UNKNOWN =
DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED,
};
struct gpio_key_state {
struct gpio_input_state *ds;
uint8_t debounce;
};
struct gpio_input_state {
struct gpio_event_input_devs *input_devs;
const struct gpio_event_input_info *info;
struct hrtimer timer;
int use_irq;
int debounce_count;
spinlock_t irq_lock;
struct wakeup_source *ws;
struct gpio_key_state key_state[0];
};
static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
{
int i;
int pressed;
struct gpio_input_state *ds =
container_of(timer, struct gpio_input_state, timer);
unsigned gpio_flags = ds->info->flags;
unsigned npolarity;
int nkeys = ds->info->keymap_size;
const struct gpio_event_direct_entry *key_entry;
struct gpio_key_state *key_state;
unsigned long irqflags;
uint8_t debounce;
bool sync_needed;
#if 0
key_entry = kp->keys_info->keymap;
key_state = kp->key_state;
for (i = 0; i < nkeys; i++, key_entry++, key_state++)
pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
gpio_read_detect_status(key_entry->gpio));
#endif
key_entry = ds->info->keymap;
key_state = ds->key_state;
sync_needed = false;
spin_lock_irqsave(&ds->irq_lock, irqflags);
for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
debounce = key_state->debounce;
if (debounce & DEBOUNCE_WAIT_IRQ)
continue;
if (key_state->debounce & DEBOUNCE_UNSTABLE) {
debounce = key_state->debounce = DEBOUNCE_UNKNOWN;
enable_irq(gpio_to_irq(key_entry->gpio));
if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE)
pr_info("gpio_keys_scan_keys: key %x-%x, %d "
"(%d) continue debounce\n",
ds->info->type, key_entry->code,
i, key_entry->gpio);
}
npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH);
pressed = gpio_get_value(key_entry->gpio) ^ npolarity;
if (debounce & DEBOUNCE_POLL) {
if (pressed == !(debounce & DEBOUNCE_PRESSED)) {
ds->debounce_count++;
key_state->debounce = DEBOUNCE_UNKNOWN;
if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
pr_info("gpio_keys_scan_keys: key %x-"
"%x, %d (%d) start debounce\n",
ds->info->type, key_entry->code,
i, key_entry->gpio);
}
continue;
}
if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) {
if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
pr_info("gpio_keys_scan_keys: key %x-%x, %d "
"(%d) debounce pressed 1\n",
ds->info->type, key_entry->code,
i, key_entry->gpio);
key_state->debounce = DEBOUNCE_PRESSED;
continue;
}
if (!pressed && (debounce & DEBOUNCE_PRESSED)) {
if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
pr_info("gpio_keys_scan_keys: key %x-%x, %d "
"(%d) debounce pressed 0\n",
ds->info->type, key_entry->code,
i, key_entry->gpio);
key_state->debounce = DEBOUNCE_NOTPRESSED;
continue;
}
/* key is stable */
ds->debounce_count--;
if (ds->use_irq)
key_state->debounce |= DEBOUNCE_WAIT_IRQ;
else
key_state->debounce |= DEBOUNCE_POLL;
if (gpio_flags & GPIOEDF_PRINT_KEYS)
pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
"changed to %d\n", ds->info->type,
key_entry->code, i, key_entry->gpio, pressed);
input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
key_entry->code, pressed);
sync_needed = true;
}
if (sync_needed) {
for (i = 0; i < ds->input_devs->count; i++)
input_sync(ds->input_devs->dev[i]);
}
#if 0
key_entry = kp->keys_info->keymap;
key_state = kp->key_state;
for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
gpio_read_detect_status(key_entry->gpio));
}
#endif
if (ds->debounce_count)
hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL);
else if (!ds->use_irq)
hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL);
else
__pm_relax(ds->ws);
spin_unlock_irqrestore(&ds->irq_lock, irqflags);
return HRTIMER_NORESTART;
}
static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
{
struct gpio_key_state *ks = dev_id;
struct gpio_input_state *ds = ks->ds;
int keymap_index = ks - ds->key_state;
const struct gpio_event_direct_entry *key_entry;
unsigned long irqflags;
int pressed;
if (!ds->use_irq)
return IRQ_HANDLED;
key_entry = &ds->info->keymap[keymap_index];
if (ds->info->debounce_time.tv64) {
spin_lock_irqsave(&ds->irq_lock, irqflags);
if (ks->debounce & DEBOUNCE_WAIT_IRQ) {
ks->debounce = DEBOUNCE_UNKNOWN;
if (ds->debounce_count++ == 0) {
__pm_stay_awake(ds->ws);
hrtimer_start(
&ds->timer, ds->info->debounce_time,
HRTIMER_MODE_REL);
}
if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
pr_info("gpio_event_input_irq_handler: "
"key %x-%x, %d (%d) start debounce\n",
ds->info->type, key_entry->code,
keymap_index, key_entry->gpio);
} else {
disable_irq_nosync(irq);
ks->debounce = DEBOUNCE_UNSTABLE;
}
spin_unlock_irqrestore(&ds->irq_lock, irqflags);
} else {
pressed = gpio_get_value(key_entry->gpio) ^
!(ds->info->flags & GPIOEDF_ACTIVE_HIGH);
if (ds->info->flags & GPIOEDF_PRINT_KEYS)
pr_info("gpio_event_input_irq_handler: key %x-%x, %d "
"(%d) changed to %d\n",
ds->info->type, key_entry->code, keymap_index,
key_entry->gpio, pressed);
input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
key_entry->code, pressed);
input_sync(ds->input_devs->dev[key_entry->dev]);
}
return IRQ_HANDLED;
}
static int gpio_event_input_request_irqs(struct gpio_input_state *ds)
{
int i;
int err;
unsigned int irq;
unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
for (i = 0; i < ds->info->keymap_size; i++) {
err = irq = gpio_to_irq(ds->info->keymap[i].gpio);
if (err < 0)
goto err_gpio_get_irq_num_failed;
err = request_irq(irq, gpio_event_input_irq_handler,
req_flags, "gpio_keys", &ds->key_state[i]);
if (err) {
pr_err("gpio_event_input_request_irqs: request_irq "
"failed for input %d, irq %d\n",
ds->info->keymap[i].gpio, irq);
goto err_request_irq_failed;
}
if (ds->info->info.no_suspend) {
err = enable_irq_wake(irq);
if (err) {
pr_err("gpio_event_input_request_irqs: "
"enable_irq_wake failed for input %d, "
"irq %d\n",
ds->info->keymap[i].gpio, irq);
goto err_enable_irq_wake_failed;
}
}
}
return 0;
for (i = ds->info->keymap_size - 1; i >= 0; i--) {
irq = gpio_to_irq(ds->info->keymap[i].gpio);
if (ds->info->info.no_suspend)
disable_irq_wake(irq);
err_enable_irq_wake_failed:
free_irq(irq, &ds->key_state[i]);
err_request_irq_failed:
err_gpio_get_irq_num_failed:
;
}
return err;
}
int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
struct gpio_event_info *info, void **data, int func)
{
int ret;
int i;
unsigned long irqflags;
struct gpio_event_input_info *di;
struct gpio_input_state *ds = *data;
char *wlname;
di = container_of(info, struct gpio_event_input_info, info);
if (func == GPIO_EVENT_FUNC_SUSPEND) {
if (ds->use_irq)
for (i = 0; i < di->keymap_size; i++)
disable_irq(gpio_to_irq(di->keymap[i].gpio));
hrtimer_cancel(&ds->timer);
return 0;
}
if (func == GPIO_EVENT_FUNC_RESUME) {
spin_lock_irqsave(&ds->irq_lock, irqflags);
if (ds->use_irq)
for (i = 0; i < di->keymap_size; i++)
enable_irq(gpio_to_irq(di->keymap[i].gpio));
hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
spin_unlock_irqrestore(&ds->irq_lock, irqflags);
return 0;
}
if (func == GPIO_EVENT_FUNC_INIT) {
if (ktime_to_ns(di->poll_time) <= 0)
di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC);
*data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) *
di->keymap_size, GFP_KERNEL);
if (ds == NULL) {
ret = -ENOMEM;
pr_err("gpio_event_input_func: "
"Failed to allocate private data\n");
goto err_ds_alloc_failed;
}
ds->debounce_count = di->keymap_size;
ds->input_devs = input_devs;
ds->info = di;
wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s",
input_devs->dev[0]->name,
(input_devs->count > 1) ? "..." : "");
ds->ws = wakeup_source_register(wlname);
kfree(wlname);
if (!ds->ws) {
ret = -ENOMEM;
pr_err("gpio_event_input_func: "
"Failed to allocate wakeup source\n");
goto err_ws_failed;
}
spin_lock_init(&ds->irq_lock);
for (i = 0; i < di->keymap_size; i++) {
int dev = di->keymap[i].dev;
if (dev >= input_devs->count) {
pr_err("gpio_event_input_func: bad device "
"index %d >= %d for key code %d\n",
dev, input_devs->count,
di->keymap[i].code);
ret = -EINVAL;
goto err_bad_keymap;
}
input_set_capability(input_devs->dev[dev], di->type,
di->keymap[i].code);
ds->key_state[i].ds = ds;
ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
}
for (i = 0; i < di->keymap_size; i++) {
ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in");
if (ret) {
pr_err("gpio_event_input_func: gpio_request "
"failed for %d\n", di->keymap[i].gpio);
goto err_gpio_request_failed;
}
ret = gpio_direction_input(di->keymap[i].gpio);
if (ret) {
pr_err("gpio_event_input_func: "
"gpio_direction_input failed for %d\n",
di->keymap[i].gpio);
goto err_gpio_configure_failed;
}
}
ret = gpio_event_input_request_irqs(ds);
spin_lock_irqsave(&ds->irq_lock, irqflags);
ds->use_irq = ret == 0;
pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s "
"mode\n", input_devs->dev[0]->name,
(input_devs->count > 1) ? "..." : "",
ret == 0 ? "interrupt" : "polling");
hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ds->timer.function = gpio_event_input_timer_func;
hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
spin_unlock_irqrestore(&ds->irq_lock, irqflags);
return 0;
}
ret = 0;
spin_lock_irqsave(&ds->irq_lock, irqflags);
hrtimer_cancel(&ds->timer);
if (ds->use_irq) {
for (i = di->keymap_size - 1; i >= 0; i--) {
int irq = gpio_to_irq(di->keymap[i].gpio);
if (ds->info->info.no_suspend)
disable_irq_wake(irq);
free_irq(irq, &ds->key_state[i]);
}
}
spin_unlock_irqrestore(&ds->irq_lock, irqflags);
for (i = di->keymap_size - 1; i >= 0; i--) {
err_gpio_configure_failed:
gpio_free(di->keymap[i].gpio);
err_gpio_request_failed:
;
}
err_bad_keymap:
wakeup_source_unregister(ds->ws);
err_ws_failed:
kfree(ds);
err_ds_alloc_failed:
return ret;
}

View File

@ -0,0 +1,441 @@
/* drivers/input/misc/gpio_matrix.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/kernel.h>
#include <linux/gpio.h>
#include <linux/gpio_event.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/wakelock.h>
struct gpio_kp {
struct gpio_event_input_devs *input_devs;
struct gpio_event_matrix_info *keypad_info;
struct hrtimer timer;
struct wake_lock wake_lock;
int current_output;
unsigned int use_irq:1;
unsigned int key_state_changed:1;
unsigned int last_key_state_changed:1;
unsigned int some_keys_pressed:2;
unsigned int disabled_irq:1;
unsigned long keys_pressed[0];
};
static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
{
struct gpio_event_matrix_info *mi = kp->keypad_info;
int key_index = out * mi->ninputs + in;
unsigned short keyentry = mi->keymap[key_index];
unsigned short keycode = keyentry & MATRIX_KEY_MASK;
unsigned short dev = keyentry >> MATRIX_CODE_BITS;
if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
"cleared\n", keycode, out, in,
mi->output_gpios[out], mi->input_gpios[in]);
__clear_bit(key_index, kp->keys_pressed);
} else {
if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
"not cleared\n", keycode, out, in,
mi->output_gpios[out], mi->input_gpios[in]);
}
}
static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
{
int rv = 0;
int key_index;
key_index = out * kp->keypad_info->ninputs + in;
while (out < kp->keypad_info->noutputs) {
if (test_bit(key_index, kp->keys_pressed)) {
rv = 1;
clear_phantom_key(kp, out, in);
}
key_index += kp->keypad_info->ninputs;
out++;
}
return rv;
}
static void remove_phantom_keys(struct gpio_kp *kp)
{
int out, in, inp;
int key_index;
if (kp->some_keys_pressed < 3)
return;
for (out = 0; out < kp->keypad_info->noutputs; out++) {
inp = -1;
key_index = out * kp->keypad_info->ninputs;
for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
if (test_bit(key_index, kp->keys_pressed)) {
if (inp == -1) {
inp = in;
continue;
}
if (inp >= 0) {
if (!restore_keys_for_input(kp, out + 1,
inp))
break;
clear_phantom_key(kp, out, inp);
inp = -2;
}
restore_keys_for_input(kp, out, in);
}
}
}
}
static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
{
struct gpio_event_matrix_info *mi = kp->keypad_info;
int pressed = test_bit(key_index, kp->keys_pressed);
unsigned short keyentry = mi->keymap[key_index];
unsigned short keycode = keyentry & MATRIX_KEY_MASK;
unsigned short dev = keyentry >> MATRIX_CODE_BITS;
if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
if (keycode == KEY_RESERVED) {
if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
pr_info("gpiomatrix: unmapped key, %d-%d "
"(%d-%d) changed to %d\n",
out, in, mi->output_gpios[out],
mi->input_gpios[in], pressed);
} else {
if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
"changed to %d\n", keycode,
out, in, mi->output_gpios[out],
mi->input_gpios[in], pressed);
input_report_key(kp->input_devs->dev[dev], keycode, pressed);
}
}
}
static void report_sync(struct gpio_kp *kp)
{
int i;
for (i = 0; i < kp->input_devs->count; i++)
input_sync(kp->input_devs->dev[i]);
}
static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
{
int out, in;
int key_index;
int gpio;
struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
struct gpio_event_matrix_info *mi = kp->keypad_info;
unsigned gpio_keypad_flags = mi->flags;
unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
out = kp->current_output;
if (out == mi->noutputs) {
out = 0;
kp->last_key_state_changed = kp->key_state_changed;
kp->key_state_changed = 0;
kp->some_keys_pressed = 0;
} else {
key_index = out * mi->ninputs;
for (in = 0; in < mi->ninputs; in++, key_index++) {
gpio = mi->input_gpios[in];
if (gpio_get_value(gpio) ^ !polarity) {
if (kp->some_keys_pressed < 3)
kp->some_keys_pressed++;
kp->key_state_changed |= !__test_and_set_bit(
key_index, kp->keys_pressed);
} else
kp->key_state_changed |= __test_and_clear_bit(
key_index, kp->keys_pressed);
}
gpio = mi->output_gpios[out];
if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
gpio_set_value(gpio, !polarity);
else
gpio_direction_input(gpio);
out++;
}
kp->current_output = out;
if (out < mi->noutputs) {
gpio = mi->output_gpios[out];
if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
gpio_set_value(gpio, polarity);
else
gpio_direction_output(gpio, polarity);
hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
if (kp->key_state_changed) {
hrtimer_start(&kp->timer, mi->debounce_delay,
HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
kp->key_state_changed = kp->last_key_state_changed;
}
if (kp->key_state_changed) {
if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
remove_phantom_keys(kp);
key_index = 0;
for (out = 0; out < mi->noutputs; out++)
for (in = 0; in < mi->ninputs; in++, key_index++)
report_key(kp, key_index, out, in);
report_sync(kp);
}
if (!kp->use_irq || kp->some_keys_pressed) {
hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
/* No keys are pressed, reenable interrupt */
for (out = 0; out < mi->noutputs; out++) {
if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
gpio_set_value(mi->output_gpios[out], polarity);
else
gpio_direction_output(mi->output_gpios[out], polarity);
}
for (in = 0; in < mi->ninputs; in++)
enable_irq(gpio_to_irq(mi->input_gpios[in]));
wake_unlock(&kp->wake_lock);
return HRTIMER_NORESTART;
}
static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id)
{
int i;
struct gpio_kp *kp = dev_id;
struct gpio_event_matrix_info *mi = kp->keypad_info;
unsigned gpio_keypad_flags = mi->flags;
if (!kp->use_irq) {
/* ignore interrupt while registering the handler */
kp->disabled_irq = 1;
disable_irq_nosync(irq_in);
return IRQ_HANDLED;
}
for (i = 0; i < mi->ninputs; i++)
disable_irq_nosync(gpio_to_irq(mi->input_gpios[i]));
for (i = 0; i < mi->noutputs; i++) {
if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
gpio_set_value(mi->output_gpios[i],
!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH));
else
gpio_direction_input(mi->output_gpios[i]);
}
wake_lock(&kp->wake_lock);
hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
return IRQ_HANDLED;
}
static int gpio_keypad_request_irqs(struct gpio_kp *kp)
{
int i;
int err;
unsigned int irq;
unsigned long request_flags;
struct gpio_event_matrix_info *mi = kp->keypad_info;
switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) {
default:
request_flags = IRQF_TRIGGER_FALLING;
break;
case GPIOKPF_ACTIVE_HIGH:
request_flags = IRQF_TRIGGER_RISING;
break;
case GPIOKPF_LEVEL_TRIGGERED_IRQ:
request_flags = IRQF_TRIGGER_LOW;
break;
case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH:
request_flags = IRQF_TRIGGER_HIGH;
break;
}
for (i = 0; i < mi->ninputs; i++) {
err = irq = gpio_to_irq(mi->input_gpios[i]);
if (err < 0)
goto err_gpio_get_irq_num_failed;
err = request_irq(irq, gpio_keypad_irq_handler, request_flags,
"gpio_kp", kp);
if (err) {
pr_err("gpiomatrix: request_irq failed for input %d, "
"irq %d\n", mi->input_gpios[i], irq);
goto err_request_irq_failed;
}
err = enable_irq_wake(irq);
if (err) {
pr_err("gpiomatrix: set_irq_wake failed for input %d, "
"irq %d\n", mi->input_gpios[i], irq);
}
disable_irq(irq);
if (kp->disabled_irq) {
kp->disabled_irq = 0;
enable_irq(irq);
}
}
return 0;
for (i = mi->noutputs - 1; i >= 0; i--) {
free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
err_request_irq_failed:
err_gpio_get_irq_num_failed:
;
}
return err;
}
int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
struct gpio_event_info *info, void **data, int func)
{
int i;
int err;
int key_count;
struct gpio_kp *kp;
struct gpio_event_matrix_info *mi;
mi = container_of(info, struct gpio_event_matrix_info, info);
if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) {
/* TODO: disable scanning */
return 0;
}
if (func == GPIO_EVENT_FUNC_INIT) {
if (mi->keymap == NULL ||
mi->input_gpios == NULL ||
mi->output_gpios == NULL) {
err = -ENODEV;
pr_err("gpiomatrix: Incomplete pdata\n");
goto err_invalid_platform_data;
}
key_count = mi->ninputs * mi->noutputs;
*data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) *
BITS_TO_LONGS(key_count), GFP_KERNEL);
if (kp == NULL) {
err = -ENOMEM;
pr_err("gpiomatrix: Failed to allocate private data\n");
goto err_kp_alloc_failed;
}
kp->input_devs = input_devs;
kp->keypad_info = mi;
for (i = 0; i < key_count; i++) {
unsigned short keyentry = mi->keymap[i];
unsigned short keycode = keyentry & MATRIX_KEY_MASK;
unsigned short dev = keyentry >> MATRIX_CODE_BITS;
if (dev >= input_devs->count) {
pr_err("gpiomatrix: bad device index %d >= "
"%d for key code %d\n",
dev, input_devs->count, keycode);
err = -EINVAL;
goto err_bad_keymap;
}
if (keycode && keycode <= KEY_MAX)
input_set_capability(input_devs->dev[dev],
EV_KEY, keycode);
}
for (i = 0; i < mi->noutputs; i++) {
err = gpio_request(mi->output_gpios[i], "gpio_kp_out");
if (err) {
pr_err("gpiomatrix: gpio_request failed for "
"output %d\n", mi->output_gpios[i]);
goto err_request_output_gpio_failed;
}
if (gpio_cansleep(mi->output_gpios[i])) {
pr_err("gpiomatrix: unsupported output gpio %d,"
" can sleep\n", mi->output_gpios[i]);
err = -EINVAL;
goto err_output_gpio_configure_failed;
}
if (mi->flags & GPIOKPF_DRIVE_INACTIVE)
err = gpio_direction_output(mi->output_gpios[i],
!(mi->flags & GPIOKPF_ACTIVE_HIGH));
else
err = gpio_direction_input(mi->output_gpios[i]);
if (err) {
pr_err("gpiomatrix: gpio_configure failed for "
"output %d\n", mi->output_gpios[i]);
goto err_output_gpio_configure_failed;
}
}
for (i = 0; i < mi->ninputs; i++) {
err = gpio_request(mi->input_gpios[i], "gpio_kp_in");
if (err) {
pr_err("gpiomatrix: gpio_request failed for "
"input %d\n", mi->input_gpios[i]);
goto err_request_input_gpio_failed;
}
err = gpio_direction_input(mi->input_gpios[i]);
if (err) {
pr_err("gpiomatrix: gpio_direction_input failed"
" for input %d\n", mi->input_gpios[i]);
goto err_gpio_direction_input_failed;
}
}
kp->current_output = mi->noutputs;
kp->key_state_changed = 1;
hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
kp->timer.function = gpio_keypad_timer_func;
wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
err = gpio_keypad_request_irqs(kp);
kp->use_irq = err == 0;
pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for "
"%s%s in %s mode\n", input_devs->dev[0]->name,
(input_devs->count > 1) ? "..." : "",
kp->use_irq ? "interrupt" : "polling");
if (kp->use_irq)
wake_lock(&kp->wake_lock);
hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
return 0;
}
err = 0;
kp = *data;
if (kp->use_irq)
for (i = mi->noutputs - 1; i >= 0; i--)
free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
hrtimer_cancel(&kp->timer);
wake_lock_destroy(&kp->wake_lock);
for (i = mi->noutputs - 1; i >= 0; i--) {
err_gpio_direction_input_failed:
gpio_free(mi->input_gpios[i]);
err_request_input_gpio_failed:
;
}
for (i = mi->noutputs - 1; i >= 0; i--) {
err_output_gpio_configure_failed:
gpio_free(mi->output_gpios[i]);
err_request_output_gpio_failed:
;
}
err_bad_keymap:
kfree(kp);
err_kp_alloc_failed:
err_invalid_platform_data:
return err;
}

View File

@ -0,0 +1,97 @@
/* drivers/input/misc/gpio_output.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/kernel.h>
#include <linux/gpio.h>
#include <linux/gpio_event.h>
int gpio_event_output_event(
struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
void **data, unsigned int dev, unsigned int type,
unsigned int code, int value)
{
int i;
struct gpio_event_output_info *oi;
oi = container_of(info, struct gpio_event_output_info, info);
if (type != oi->type)
return 0;
if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
value = !value;
for (i = 0; i < oi->keymap_size; i++)
if (dev == oi->keymap[i].dev && code == oi->keymap[i].code)
gpio_set_value(oi->keymap[i].gpio, value);
return 0;
}
int gpio_event_output_func(
struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
void **data, int func)
{
int ret;
int i;
struct gpio_event_output_info *oi;
oi = container_of(info, struct gpio_event_output_info, info);
if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME)
return 0;
if (func == GPIO_EVENT_FUNC_INIT) {
int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
for (i = 0; i < oi->keymap_size; i++) {
int dev = oi->keymap[i].dev;
if (dev >= input_devs->count) {
pr_err("gpio_event_output_func: bad device "
"index %d >= %d for key code %d\n",
dev, input_devs->count,
oi->keymap[i].code);
ret = -EINVAL;
goto err_bad_keymap;
}
input_set_capability(input_devs->dev[dev], oi->type,
oi->keymap[i].code);
}
for (i = 0; i < oi->keymap_size; i++) {
ret = gpio_request(oi->keymap[i].gpio,
"gpio_event_output");
if (ret) {
pr_err("gpio_event_output_func: gpio_request "
"failed for %d\n", oi->keymap[i].gpio);
goto err_gpio_request_failed;
}
ret = gpio_direction_output(oi->keymap[i].gpio,
output_level);
if (ret) {
pr_err("gpio_event_output_func: "
"gpio_direction_output failed for %d\n",
oi->keymap[i].gpio);
goto err_gpio_direction_output_failed;
}
}
return 0;
}
ret = 0;
for (i = oi->keymap_size - 1; i >= 0; i--) {
err_gpio_direction_output_failed:
gpio_free(oi->keymap[i].gpio);
err_gpio_request_failed:
;
}
err_bad_keymap:
return ret;
}

View File

@ -0,0 +1,391 @@
/*
* drivers/input/misc/keychord.c
*
* 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/poll.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/keychord.h>
#include <linux/sched.h>
#define KEYCHORD_NAME "keychord"
#define BUFFER_SIZE 16
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
MODULE_DESCRIPTION("Key chord input driver");
MODULE_SUPPORTED_DEVICE("keychord");
MODULE_LICENSE("GPL");
#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \
((char *)kc + sizeof(struct input_keychord) + \
kc->count * sizeof(kc->keycodes[0])))
struct keychord_device {
struct input_handler input_handler;
int registered;
/* list of keychords to monitor */
struct input_keychord *keychords;
int keychord_count;
/* bitmask of keys contained in our keychords */
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
/* current state of the keys */
unsigned long keystate[BITS_TO_LONGS(KEY_CNT)];
/* number of keys that are currently pressed */
int key_down;
/* second input_device_id is needed for null termination */
struct input_device_id device_ids[2];
spinlock_t lock;
wait_queue_head_t waitq;
unsigned char head;
unsigned char tail;
__u16 buff[BUFFER_SIZE];
};
static int check_keychord(struct keychord_device *kdev,
struct input_keychord *keychord)
{
int i;
if (keychord->count != kdev->key_down)
return 0;
for (i = 0; i < keychord->count; i++) {
if (!test_bit(keychord->keycodes[i], kdev->keystate))
return 0;
}
/* we have a match */
return 1;
}
static void keychord_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
struct keychord_device *kdev = handle->private;
struct input_keychord *keychord;
unsigned long flags;
int i, got_chord = 0;
if (type != EV_KEY || code >= KEY_MAX)
return;
spin_lock_irqsave(&kdev->lock, flags);
/* do nothing if key state did not change */
if (!test_bit(code, kdev->keystate) == !value)
goto done;
__change_bit(code, kdev->keystate);
if (value)
kdev->key_down++;
else
kdev->key_down--;
/* don't notify on key up */
if (!value)
goto done;
/* ignore this event if it is not one of the keys we are monitoring */
if (!test_bit(code, kdev->keybit))
goto done;
keychord = kdev->keychords;
if (!keychord)
goto done;
/* check to see if the keyboard state matches any keychords */
for (i = 0; i < kdev->keychord_count; i++) {
if (check_keychord(kdev, keychord)) {
kdev->buff[kdev->head] = keychord->id;
kdev->head = (kdev->head + 1) % BUFFER_SIZE;
got_chord = 1;
break;
}
/* skip to next keychord */
keychord = NEXT_KEYCHORD(keychord);
}
done:
spin_unlock_irqrestore(&kdev->lock, flags);
if (got_chord) {
pr_info("keychord: got keychord id %d. Any tasks: %d\n",
keychord->id,
!list_empty_careful(&kdev->waitq.task_list));
wake_up_interruptible(&kdev->waitq);
}
}
static int keychord_connect(struct input_handler *handler,
struct input_dev *dev,
const struct input_device_id *id)
{
int i, ret;
struct input_handle *handle;
struct keychord_device *kdev =
container_of(handler, struct keychord_device, input_handler);
/*
* ignore this input device if it does not contain any keycodes
* that we are monitoring
*/
for (i = 0; i < KEY_MAX; i++) {
if (test_bit(i, kdev->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 = KEYCHORD_NAME;
handle->private = kdev;
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("keychord: using input dev %s for fevent\n", dev->name);
return 0;
err_input_open_device:
input_unregister_handle(handle);
err_input_register_handle:
kfree(handle);
return ret;
}
static void keychord_disconnect(struct input_handle *handle)
{
input_close_device(handle);
input_unregister_handle(handle);
kfree(handle);
}
/*
* keychord_read is used to read keychord events from the driver
*/
static ssize_t keychord_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct keychord_device *kdev = file->private_data;
__u16 id;
int retval;
unsigned long flags;
if (count < sizeof(id))
return -EINVAL;
count = sizeof(id);
if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(kdev->waitq,
kdev->head != kdev->tail);
if (retval)
return retval;
spin_lock_irqsave(&kdev->lock, flags);
/* pop a keychord ID off the queue */
id = kdev->buff[kdev->tail];
kdev->tail = (kdev->tail + 1) % BUFFER_SIZE;
spin_unlock_irqrestore(&kdev->lock, flags);
if (copy_to_user(buffer, &id, count))
return -EFAULT;
return count;
}
/*
* keychord_write is used to configure the driver
*/
static ssize_t keychord_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct keychord_device *kdev = file->private_data;
struct input_keychord *keychords = 0;
struct input_keychord *keychord, *next, *end;
int ret, i, key;
unsigned long flags;
if (count < sizeof(struct input_keychord))
return -EINVAL;
keychords = kzalloc(count, GFP_KERNEL);
if (!keychords)
return -ENOMEM;
/* read list of keychords from userspace */
if (copy_from_user(keychords, buffer, count)) {
kfree(keychords);
return -EFAULT;
}
/* unregister handler before changing configuration */
if (kdev->registered) {
input_unregister_handler(&kdev->input_handler);
kdev->registered = 0;
}
spin_lock_irqsave(&kdev->lock, flags);
/* clear any existing configuration */
kfree(kdev->keychords);
kdev->keychords = 0;
kdev->keychord_count = 0;
kdev->key_down = 0;
memset(kdev->keybit, 0, sizeof(kdev->keybit));
memset(kdev->keystate, 0, sizeof(kdev->keystate));
kdev->head = kdev->tail = 0;
keychord = keychords;
end = (struct input_keychord *)((char *)keychord + count);
while (keychord < end) {
next = NEXT_KEYCHORD(keychord);
if (keychord->count <= 0 || next > end) {
pr_err("keychord: invalid keycode count %d\n",
keychord->count);
goto err_unlock_return;
}
if (keychord->version != KEYCHORD_VERSION) {
pr_err("keychord: unsupported version %d\n",
keychord->version);
goto err_unlock_return;
}
/* keep track of the keys we are monitoring in keybit */
for (i = 0; i < keychord->count; i++) {
key = keychord->keycodes[i];
if (key < 0 || key >= KEY_CNT) {
pr_err("keychord: keycode %d out of range\n",
key);
goto err_unlock_return;
}
__set_bit(key, kdev->keybit);
}
kdev->keychord_count++;
keychord = next;
}
kdev->keychords = keychords;
spin_unlock_irqrestore(&kdev->lock, flags);
ret = input_register_handler(&kdev->input_handler);
if (ret) {
kfree(keychords);
kdev->keychords = 0;
return ret;
}
kdev->registered = 1;
return count;
err_unlock_return:
spin_unlock_irqrestore(&kdev->lock, flags);
kfree(keychords);
return -EINVAL;
}
static unsigned int keychord_poll(struct file *file, poll_table *wait)
{
struct keychord_device *kdev = file->private_data;
poll_wait(file, &kdev->waitq, wait);
if (kdev->head != kdev->tail)
return POLLIN | POLLRDNORM;
return 0;
}
static int keychord_open(struct inode *inode, struct file *file)
{
struct keychord_device *kdev;
kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL);
if (!kdev)
return -ENOMEM;
spin_lock_init(&kdev->lock);
init_waitqueue_head(&kdev->waitq);
kdev->input_handler.event = keychord_event;
kdev->input_handler.connect = keychord_connect;
kdev->input_handler.disconnect = keychord_disconnect;
kdev->input_handler.name = KEYCHORD_NAME;
kdev->input_handler.id_table = kdev->device_ids;
kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT;
__set_bit(EV_KEY, kdev->device_ids[0].evbit);
file->private_data = kdev;
return 0;
}
static int keychord_release(struct inode *inode, struct file *file)
{
struct keychord_device *kdev = file->private_data;
if (kdev->registered)
input_unregister_handler(&kdev->input_handler);
kfree(kdev);
return 0;
}
static const struct file_operations keychord_fops = {
.owner = THIS_MODULE,
.open = keychord_open,
.release = keychord_release,
.read = keychord_read,
.write = keychord_write,
.poll = keychord_poll,
};
static struct miscdevice keychord_misc = {
.fops = &keychord_fops,
.name = KEYCHORD_NAME,
.minor = MISC_DYNAMIC_MINOR,
};
static int __init keychord_init(void)
{
return misc_register(&keychord_misc);
}
static void __exit keychord_exit(void)
{
misc_deregister(&keychord_misc);
}
module_init(keychord_init);
module_exit(keychord_exit);

View File

@ -424,6 +424,10 @@ config TI_DAC7512
This driver can also be built as a module. If so, the module
will be called ti_dac7512.
config UID_STAT
bool "UID based statistics tracking exported to /proc/uid_stat"
default n
config VMWARE_BALLOON
tristate "VMware Balloon Driver"
depends on X86 && HYPERVISOR_GUEST

View File

@ -36,6 +36,7 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o
obj-$(CONFIG_UID_STAT) += uid_stat.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_HMC6352) += hmc6352.o
obj-y += eeprom/

152
drivers/misc/uid_stat.c Normal file
View File

@ -0,0 +1,152 @@
/* drivers/misc/uid_stat.c
*
* Copyright (C) 2008 - 2009 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/atomic.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
#include <linux/uid_stat.h>
#include <net/activity_stats.h>
static DEFINE_SPINLOCK(uid_lock);
static LIST_HEAD(uid_list);
static struct proc_dir_entry *parent;
struct uid_stat {
struct list_head link;
uid_t uid;
atomic_t tcp_rcv;
atomic_t tcp_snd;
};
static struct uid_stat *find_uid_stat(uid_t uid) {
struct uid_stat *entry;
list_for_each_entry(entry, &uid_list, link) {
if (entry->uid == uid) {
return entry;
}
}
return NULL;
}
static int uid_stat_atomic_int_show(struct seq_file *m, void *v)
{
unsigned int bytes;
atomic_t *counter = m->private;
bytes = (unsigned int) (atomic_read(counter) + INT_MIN);
return seq_printf(m, "%u\n", bytes);
}
static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file)
{
return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode));
}
static const struct file_operations uid_stat_read_atomic_int_fops = {
.open = uid_stat_read_atomic_int_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/* Create a new entry for tracking the specified uid. */
static struct uid_stat *create_stat(uid_t uid) {
struct uid_stat *new_uid;
/* Create the uid stat struct and append it to the list. */
new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC);
if (!new_uid)
return NULL;
new_uid->uid = uid;
/* Counters start at INT_MIN, so we can track 4GB of network traffic. */
atomic_set(&new_uid->tcp_rcv, INT_MIN);
atomic_set(&new_uid->tcp_snd, INT_MIN);
list_add_tail(&new_uid->link, &uid_list);
return new_uid;
}
static void create_stat_proc(struct uid_stat *new_uid)
{
char uid_s[32];
struct proc_dir_entry *entry;
sprintf(uid_s, "%d", new_uid->uid);
entry = proc_mkdir(uid_s, parent);
/* Keep reference to uid_stat so we know what uid to read stats from. */
proc_create_data("tcp_snd", S_IRUGO, entry,
&uid_stat_read_atomic_int_fops, &new_uid->tcp_snd);
proc_create_data("tcp_rcv", S_IRUGO, entry,
&uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv);
}
static struct uid_stat *find_or_create_uid_stat(uid_t uid)
{
struct uid_stat *entry;
unsigned long flags;
spin_lock_irqsave(&uid_lock, flags);
entry = find_uid_stat(uid);
if (entry) {
spin_unlock_irqrestore(&uid_lock, flags);
return entry;
}
entry = create_stat(uid);
spin_unlock_irqrestore(&uid_lock, flags);
if (entry)
create_stat_proc(entry);
return entry;
}
int uid_stat_tcp_snd(uid_t uid, int size) {
struct uid_stat *entry;
activity_stats_update();
entry = find_or_create_uid_stat(uid);
if (!entry)
return -1;
atomic_add(size, &entry->tcp_snd);
return 0;
}
int uid_stat_tcp_rcv(uid_t uid, int size) {
struct uid_stat *entry;
activity_stats_update();
entry = find_or_create_uid_stat(uid);
if (!entry)
return -1;
atomic_add(size, &entry->tcp_rcv);
return 0;
}
static int __init uid_stat_init(void)
{
parent = proc_mkdir("uid_stat", NULL);
if (!parent) {
pr_err("uid_stat: failed to create proc entry\n");
return -1;
}
return 0;
}
__initcall(uid_stat_init);

View File

@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE
If unsure, say Y here.
config MMC_BLOCK_DEFERRED_RESUME
bool "Deferr MMC layer resume until I/O is requested"
depends on MMC_BLOCK
default n
help
Say Y here to enable deferred MMC resume until I/O
is requested. This will reduce overall resume latency and
save power when theres an SD card inserted but not being used.
config SDIO_UART
tristate "SDIO UART/GPS class support"
depends on TTY

View File

@ -35,6 +35,9 @@
#include <linux/capability.h>
#include <linux/compat.h>
#define CREATE_TRACE_POINTS
#include <trace/events/mmc.h>
#include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@ -163,11 +166,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
static inline int mmc_get_devidx(struct gendisk *disk)
{
int devmaj = MAJOR(disk_devt(disk));
int devidx = MINOR(disk_devt(disk)) / perdev_minors;
if (!devmaj)
devidx = disk->first_minor / perdev_minors;
int devidx = disk->first_minor / perdev_minors;
return devidx;
}
@ -728,18 +727,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
req->rq_disk->disk_name, "timed out", name, status);
/* If the status cmd initially failed, retry the r/w cmd */
if (!status_valid)
if (!status_valid) {
pr_err("%s: status not valid, retrying timeout\n", req->rq_disk->disk_name);
return ERR_RETRY;
}
/*
* If it was a r/w cmd crc error, or illegal command
* (eg, issued in wrong state) then retry - we should
* have corrected the state problem above.
*/
if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND))
if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) {
pr_err("%s: command error, retrying timeout\n", req->rq_disk->disk_name);
return ERR_RETRY;
}
/* Otherwise abort the command */
pr_err("%s: not retrying timeout\n", req->rq_disk->disk_name);
return ERR_ABORT;
default:
@ -1002,9 +1005,12 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
goto out;
}
if (mmc_can_sanitize(card))
if (mmc_can_sanitize(card)) {
trace_mmc_blk_erase_start(EXT_CSD_SANITIZE_START, 0, 0);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_SANITIZE_START, 1, 0);
trace_mmc_blk_erase_end(EXT_CSD_SANITIZE_START, 0, 0);
}
out_retry:
if (err && !mmc_blk_reset(md, card->host, type))
goto retry;
@ -1893,6 +1899,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_host *host = card->host;
unsigned long flags;
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
if (mmc_bus_needs_resume(card->host))
mmc_resume_bus(card->host);
#endif
if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */
mmc_claim_host(card->host);
@ -2015,6 +2026,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = parent;
set_disk_ro(md->disk, md->read_only || default_ro);
md->disk->flags = GENHD_FL_EXT_DEVT;
if (area_type & MMC_BLK_DATA_AREA_RPMB)
md->disk->flags |= GENHD_FL_NO_PART_SCAN;
@ -2329,6 +2341,9 @@ static int mmc_blk_probe(struct mmc_card *card)
mmc_set_drvdata(card, md);
mmc_fixup_device(card, blk_fixups);
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
mmc_set_bus_resume_policy(card->host, 1);
#endif
if (mmc_add_disk(md))
goto out;
@ -2354,6 +2369,9 @@ static void mmc_blk_remove(struct mmc_card *card)
mmc_release_host(card->host);
mmc_blk_remove_req(md);
mmc_set_drvdata(card, NULL);
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
mmc_set_bus_resume_policy(card->host, 0);
#endif
}
#ifdef CONFIG_PM

View File

@ -26,3 +26,18 @@ config MMC_CLKGATE
support handling this in order for it to be of any use.
If unsure, say N.
config MMC_EMBEDDED_SDIO
boolean "MMC embedded SDIO device support (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)"
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.

View File

@ -27,6 +27,9 @@
#include <linux/fault-inject.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/wakelock.h>
#include <trace/events/mmc.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@ -172,6 +175,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data);
}
if (mrq->stop) {
@ -536,8 +540,12 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
mmc_start_bkops(host->card, true);
}
if (!err && areq)
if (!err && areq) {
trace_mmc_blk_rw_start(areq->mrq->cmd->opcode,
areq->mrq->cmd->arg,
areq->mrq->data);
start_err = __mmc_start_data_req(host, areq->mrq);
}
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
@ -1591,6 +1599,36 @@ static inline void mmc_bus_put(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
}
int mmc_resume_bus(struct mmc_host *host)
{
unsigned long flags;
if (!mmc_bus_needs_resume(host))
return -EINVAL;
printk("%s: Starting deferred resume\n", mmc_hostname(host));
spin_lock_irqsave(&host->lock, flags);
host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
host->rescan_disable = 0;
spin_unlock_irqrestore(&host->lock, flags);
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
mmc_power_up(host);
BUG_ON(!host->bus_ops->resume);
host->bus_ops->resume(host);
}
if (host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host);
mmc_bus_put(host);
printk("%s: Deferred resume completed\n", mmc_hostname(host));
return 0;
}
EXPORT_SYMBOL(mmc_resume_bus);
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
@ -1656,6 +1694,8 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
spin_unlock_irqrestore(&host->lock, flags);
#endif
host->detect_change = 1;
wake_lock(&host->detect_wake_lock);
mmc_schedule_delayed_work(&host->detect, delay);
}
@ -1815,8 +1855,13 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
struct mmc_command cmd = {0};
unsigned int qty = 0;
unsigned long timeout;
unsigned int fr, nr;
int err;
fr = from;
nr = to - from + 1;
trace_mmc_blk_erase_start(arg, fr, nr);
/*
* qty is used to calculate the erase timeout which depends on how many
* erase groups (or allocation units in SD terminology) are affected.
@ -1920,6 +1965,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
out:
trace_mmc_blk_erase_end(arg, fr, nr);
return err;
}
@ -2351,6 +2398,7 @@ void mmc_rescan(struct work_struct *work)
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
int i;
bool extend_wakelock = false;
if (host->rescan_disable)
return;
@ -2372,6 +2420,12 @@ void mmc_rescan(struct work_struct *work)
host->detect_change = 0;
/* If the card was removed the bus will be marked
* as dead - extend the wakelock so userspace
* can respond */
if (host->bus_dead)
extend_wakelock = 1;
/*
* Let mmc_bus_put() free the bus/bus_ops if we've found that
* the card is no longer present.
@ -2400,16 +2454,24 @@ void mmc_rescan(struct work_struct *work)
mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
extend_wakelock = true;
break;
}
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
if (extend_wakelock)
wake_lock_timeout(&host->detect_wake_lock, HZ / 2);
else
wake_unlock(&host->detect_wake_lock);
if (host->caps & MMC_CAP_NEEDS_POLL) {
wake_lock(&host->detect_wake_lock);
mmc_schedule_delayed_work(&host->detect, HZ);
}
}
void mmc_start_host(struct mmc_host *host)
@ -2433,7 +2495,8 @@ void mmc_stop_host(struct mmc_host *host)
#endif
host->rescan_disable = 1;
cancel_delayed_work_sync(&host->detect);
if (cancel_delayed_work_sync(&host->detect))
wake_unlock(&host->detect_wake_lock);
mmc_flush_scheduled_work();
/* clear pm flags now and let card drivers set them as needed */
@ -2628,7 +2691,11 @@ int mmc_suspend_host(struct mmc_host *host)
{
int err = 0;
cancel_delayed_work(&host->detect);
if (mmc_bus_needs_resume(host))
return 0;
if (cancel_delayed_work(&host->detect))
wake_unlock(&host->detect_wake_lock);
mmc_flush_scheduled_work();
mmc_bus_get(host);
@ -2679,6 +2746,12 @@ int mmc_resume_host(struct mmc_host *host)
int err = 0;
mmc_bus_get(host);
if (mmc_bus_manual_resume(host)) {
host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
mmc_bus_put(host);
return 0;
}
if (host->bus_ops && !host->bus_dead) {
if (!mmc_card_keep_power(host)) {
mmc_power_up(host);
@ -2739,9 +2812,14 @@ int mmc_pm_notify(struct notifier_block *notify_block,
}
spin_lock_irqsave(&host->lock, flags);
if (mmc_bus_needs_resume(host)) {
spin_unlock_irqrestore(&host->lock, flags);
break;
}
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
if (cancel_delayed_work_sync(&host->detect))
wake_unlock(&host->detect_wake_lock);
if (!host->bus_ops || host->bus_ops->suspend)
break;
@ -2762,6 +2840,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
case PM_POST_RESTORE:
spin_lock_irqsave(&host->lock, flags);
if (mmc_bus_manual_resume(host)) {
spin_unlock_irqrestore(&host->lock, flags);
break;
}
host->rescan_disable = 0;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
@ -2789,6 +2871,22 @@ void mmc_init_context_info(struct mmc_host *host)
init_waitqueue_head(&host->context_info.wait);
}
#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;

View File

@ -459,6 +459,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND,
kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)));
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
@ -511,7 +513,8 @@ int mmc_add_host(struct mmc_host *host)
mmc_host_clk_sysfs_init(host);
mmc_start_host(host);
register_pm_notifier(&host->pm_notify);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
register_pm_notifier(&host->pm_notify);
return 0;
}
@ -528,7 +531,9 @@ EXPORT_SYMBOL(mmc_add_host);
*/
void mmc_remove_host(struct mmc_host *host)
{
unregister_pm_notifier(&host->pm_notify);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
unregister_pm_notifier(&host->pm_notify);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
@ -555,6 +560,7 @@ void mmc_free_host(struct mmc_host *host)
spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock);
wake_lock_destroy(&host->detect_wake_lock);
put_device(&host->class_dev);
}

View File

@ -805,6 +805,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit)
{
int err;
#ifdef CONFIG_MMC_PARANOID_SD_INIT
int retries;
#endif
if (!reinit) {
/*
@ -831,7 +834,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *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)
return err;
}
@ -1032,7 +1054,10 @@ static int mmc_sd_alive(struct mmc_host *host)
*/
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);
@ -1042,7 +1067,23 @@ static void mmc_sd_detect(struct mmc_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);
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_detect_card_removed(host);
#endif
mmc_release_host(host);
@ -1084,12 +1125,31 @@ static int mmc_sd_suspend(struct mmc_host *host)
static int 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);
return err;
@ -1143,6 +1203,9 @@ int mmc_attach_sd(struct mmc_host *host)
{
int err;
u32 ocr;
#ifdef CONFIG_MMC_PARANOID_SD_INIT
int retries;
#endif
BUG_ON(!host);
WARN_ON(!host->claimed);
@ -1198,9 +1261,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);
err = mmc_add_card(host->card);

View File

@ -10,6 +10,7 @@
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
@ -28,6 +29,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;
@ -728,19 +733,35 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto finish;
}
/*
* Read the common registers.
*/
err = sdio_read_cccr(card, ocr);
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
/*
* Read the common registers.
*/
err = sdio_read_cccr(card, ocr);
if (err)
goto remove;
#ifdef CONFIG_MMC_EMBEDDED_SDIO
}
#endif
/*
* 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.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
if (oldcard) {
int same = (card->cis.vendor == oldcard->cis.vendor &&
@ -1147,14 +1168,36 @@ int mmc_attach_sdio(struct mmc_host *host)
funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0;
#ifdef CONFIG_MMC_EMBEDDED_SDIO
if (host->embedded_sdio_data.funcs)
card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs;
#endif
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
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
/*
* Enable Runtime PM for this func (if supported)
*/
@ -1202,3 +1245,39 @@ int mmc_attach_sdio(struct mmc_host *host)
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_claim_host(host);
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_sdio_init_card(host, host->ocr, card, 0);
if (err)
goto err;
mmc_release_host(host);
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);

View File

@ -25,6 +25,10 @@
#include "sdio_cis.h"
#include "sdio_bus.h"
#ifdef CONFIG_MMC_EMBEDDED_SDIO
#include <linux/mmc/host.h>
#endif
/* show configuration fields */
#define sdio_config_attr(field, format_string) \
static ssize_t \
@ -270,7 +274,14 @@ static void sdio_release_func(struct device *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);
kfree(func->info);

33
drivers/mmc/core/sdio_io.c Normal file → Executable file
View File

@ -383,6 +383,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
}
EXPORT_SYMBOL_GPL(sdio_readb);
/**
* sdio_readb_ext - read a single byte from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
* @in: value to add to argument
*
* Reads a single byte from the address space of a given SDIO
* function. If there is a problem reading the address, 0xff
* is returned and @err_ret will contain the error code.
*/
unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr,
int *err_ret, unsigned in)
{
int ret;
unsigned char val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_readb_ext);
/**
* sdio_writeb - write a single byte to a SDIO function
* @func: SDIO function to access

View File

@ -1,3 +1,10 @@
config MTD_NAND_IDS
tristate "Include chip ids for known NAND devices."
depends on MTD
help
Useful for NAND drivers that do not use the NAND subsystem but
still like to take advantage of the known chip information.
config MTD_NAND_ECC
tristate
@ -133,9 +140,6 @@ config BCH_CONST_T
default 8 if MTD_NAND_OMAP_BCH8
endif
config MTD_NAND_IDS
tristate
config MTD_NAND_RICOH
tristate "Ricoh xD card reader"
default n

View File

@ -149,6 +149,23 @@ config PPPOL2TP
tunnels. L2TP is replacing PPTP for VPN uses.
if TTY
config PPPOLAC
tristate "PPP on L2TP Access Concentrator"
depends on PPP && INET
help
L2TP (RFC 2661) is a tunneling protocol widely used in virtual private
networks. This driver handles L2TP data packets between a UDP socket
and a PPP channel, but only permits one session per socket. Thus it is
fairly simple and suited for clients.
config PPPOPNS
tristate "PPP on PPTP Network Server"
depends on PPP && INET
help
PPTP (RFC 2637) is a tunneling protocol widely used in virtual private
networks. This driver handles PPTP data packets between a RAW socket
and a PPP channel. It is fairly simple and easy to use.
config PPP_ASYNC
tristate "PPP support for async serial ports"
depends on PPP

View File

@ -11,3 +11,5 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o
obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
obj-$(CONFIG_PPPOL2TP) += pppox.o
obj-$(CONFIG_PPTP) += pppox.o pptp.o
obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o
obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o

Some files were not shown because too many files have changed in this diff Show More