mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
Driver core pull for 3.5-rc1
Here's the driver core, and other driver subsystems, pull request for the 3.5-rc1 merge window. Outside of a few minor driver core changes, we ended up with the following different subsystem and core changes as well, due to interdependancies on the driver core: - hyperv driver updates - drivers/memory being created and some drivers moved into it - extcon driver subsystem created out of the old Android staging switch driver code - dynamic debug updates - printk rework, and /dev/kmsg changes All of this has been tested in the linux-next releases for a few weeks with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAk+7q28ACgkQMUfUDdst+ykXmwCfcPASzC+/bDkuqdWsqzxlWZ7+ VOQAnAriySv397St36J6Hz5bMQZwB1Yq =SQc+ -----END PGP SIGNATURE----- Merge tag 'driver-core-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core updates from Greg Kroah-Hartman: "Here's the driver core, and other driver subsystems, pull request for the 3.5-rc1 merge window. Outside of a few minor driver core changes, we ended up with the following different subsystem and core changes as well, due to interdependancies on the driver core: - hyperv driver updates - drivers/memory being created and some drivers moved into it - extcon driver subsystem created out of the old Android staging switch driver code - dynamic debug updates - printk rework, and /dev/kmsg changes All of this has been tested in the linux-next releases for a few weeks with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" Fix up conflicts in drivers/extcon/extcon-max8997.c where git noticed that a patch to the deleted drivers/misc/max8997-muic.c driver needs to be applied to this one. * tag 'driver-core-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (90 commits) uio_pdrv_genirq: get irq through platform resource if not set otherwise memory: tegra{20,30}-mc: Remove empty *_remove() printk() - isolate KERN_CONT users from ordinary complete lines sysfs: get rid of some lockdep false positives Drivers: hv: util: Properly handle version negotiations. Drivers: hv: Get rid of an unnecessary check in vmbus_prep_negotiate_resp() memory: tegra{20,30}-mc: Use dev_err_ratelimited() driver core: Add dev_*_ratelimited() family Driver Core: don't oops with unregistered driver in driver_find_device() printk() - restore prefix/timestamp printing for multi-newline strings printk: add stub for prepend_timestamp() ARM: tegra30: Make MC optional in Kconfig ARM: tegra20: Make MC optional in Kconfig ARM: tegra30: MC: Remove unnecessary BUG*() ARM: tegra20: MC: Remove unnecessary BUG*() printk: correctly align __log_buf ARM: tegra30: Add Tegra Memory Controller(MC) driver ARM: tegra20: Add Tegra Memory Controller(MC) driver printk() - restore timestamp printing at console output printk() - do not merge continuation lines of different threads ...
This commit is contained in:
commit
5d4e2d08e7
90
Documentation/ABI/testing/dev-kmsg
Normal file
90
Documentation/ABI/testing/dev-kmsg
Normal file
@ -0,0 +1,90 @@
|
||||
What: /dev/kmsg
|
||||
Date: Mai 2012
|
||||
KernelVersion: 3.5
|
||||
Contact: Kay Sievers <kay@vrfy.org>
|
||||
Description: The /dev/kmsg character device node provides userspace access
|
||||
to the kernel's printk buffer.
|
||||
|
||||
Injecting messages:
|
||||
Every write() to the opened device node places a log entry in
|
||||
the kernel's printk buffer.
|
||||
|
||||
The logged line can be prefixed with a <N> syslog prefix, which
|
||||
carries the syslog priority and facility. The single decimal
|
||||
prefix number is composed of the 3 lowest bits being the syslog
|
||||
priority and the higher bits the syslog facility number.
|
||||
|
||||
If no prefix is given, the priority number is the default kernel
|
||||
log priority and the facility number is set to LOG_USER (1). It
|
||||
is not possible to inject messages from userspace with the
|
||||
facility number LOG_KERN (0), to make sure that the origin of
|
||||
the messages can always be reliably determined.
|
||||
|
||||
Accessing the buffer:
|
||||
Every read() from the opened device node receives one record
|
||||
of the kernel's printk buffer.
|
||||
|
||||
The first read() directly following an open() always returns
|
||||
first message in the buffer; there is no kernel-internal
|
||||
persistent state; many readers can concurrently open the device
|
||||
and read from it, without affecting other readers.
|
||||
|
||||
Every read() will receive the next available record. If no more
|
||||
records are available read() will block, or if O_NONBLOCK is
|
||||
used -EAGAIN returned.
|
||||
|
||||
Messages in the record ring buffer get overwritten as whole,
|
||||
there are never partial messages received by read().
|
||||
|
||||
In case messages get overwritten in the circular buffer while
|
||||
the device is kept open, the next read() will return -EPIPE,
|
||||
and the seek position be updated to the next available record.
|
||||
Subsequent reads() will return available records again.
|
||||
|
||||
Unlike the classic syslog() interface, the 64 bit record
|
||||
sequence numbers allow to calculate the amount of lost
|
||||
messages, in case the buffer gets overwritten. And they allow
|
||||
to reconnect to the buffer and reconstruct the read position
|
||||
if needed, without limiting the interface to a single reader.
|
||||
|
||||
The device supports seek with the following parameters:
|
||||
SEEK_SET, 0
|
||||
seek to the first entry in the buffer
|
||||
SEEK_END, 0
|
||||
seek after the last entry in the buffer
|
||||
SEEK_DATA, 0
|
||||
seek after the last record available at the time
|
||||
the last SYSLOG_ACTION_CLEAR was issued.
|
||||
|
||||
The output format consists of a prefix carrying the syslog
|
||||
prefix including priority and facility, the 64 bit message
|
||||
sequence number and the monotonic timestamp in microseconds.
|
||||
The values are separated by a ','. Future extensions might
|
||||
add more comma separated values before the terminating ';'.
|
||||
Unknown values should be gracefully ignored.
|
||||
|
||||
The human readable text string starts directly after the ';'
|
||||
and is terminated by a '\n'. Untrusted values derived from
|
||||
hardware or other facilities are printed, therefore
|
||||
all non-printable characters in the log message are escaped
|
||||
by "\x00" C-style hex encoding.
|
||||
|
||||
A line starting with ' ', is a continuation line, adding
|
||||
key/value pairs to the log message, which provide the machine
|
||||
readable context of the message, for reliable processing in
|
||||
userspace.
|
||||
|
||||
Example:
|
||||
7,160,424069;pci_root PNP0A03:00: host bridge window [io 0x0000-0x0cf7] (ignored)
|
||||
SUBSYSTEM=acpi
|
||||
DEVICE=+acpi:PNP0A03:00
|
||||
6,339,5140900;NET: Registered protocol family 10
|
||||
30,340,5690716;udevd[80]: starting version 181
|
||||
|
||||
The DEVICE= key uniquely identifies devices the following way:
|
||||
b12:8 - block dev_t
|
||||
c127:3 - char dev_t
|
||||
n8 - netdev ifindex
|
||||
+sound:card0 - subsystem:devname
|
||||
|
||||
Users: dmesg(1), userspace kernel log consumers
|
97
Documentation/ABI/testing/sysfs-class-extcon
Normal file
97
Documentation/ABI/testing/sysfs-class-extcon
Normal file
@ -0,0 +1,97 @@
|
||||
What: /sys/class/extcon/.../
|
||||
Date: February 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
Provide a place in sysfs for the extcon objects.
|
||||
This allows accessing extcon specific variables.
|
||||
The name of extcon object denoted as ... is the name given
|
||||
with extcon_dev_register.
|
||||
|
||||
One extcon device denotes a single external connector
|
||||
port. An external connector may have multiple cables
|
||||
attached simultaneously. Many of docks, cradles, and
|
||||
accessory cables have such capability. For example,
|
||||
the 30-pin port of Nuri board (/arch/arm/mach-exynos)
|
||||
may have both HDMI and Charger attached, or analog audio,
|
||||
video, and USB cables attached simulteneously.
|
||||
|
||||
If there are cables mutually exclusive with each other,
|
||||
such binary relations may be expressed with extcon_dev's
|
||||
mutually_exclusive array.
|
||||
|
||||
What: /sys/class/extcon/.../name
|
||||
Date: February 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/extcon/.../name shows the name of the extcon
|
||||
object. If the extcon object has an optional callback
|
||||
"show_name" defined, the callback will provide the name with
|
||||
this sysfs node.
|
||||
|
||||
What: /sys/class/extcon/.../state
|
||||
Date: February 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/extcon/.../state shows and stores the cable
|
||||
attach/detach information of the corresponding extcon object.
|
||||
If the extcon object has an optional callback "show_state"
|
||||
defined, the showing function is overriden with the optional
|
||||
callback.
|
||||
|
||||
If the default callback for showing function is used, the
|
||||
format is like this:
|
||||
# cat state
|
||||
USB_OTG=1
|
||||
HDMI=0
|
||||
TA=1
|
||||
EAR_JACK=0
|
||||
#
|
||||
In this example, the extcon device have USB_OTG and TA
|
||||
cables attached and HDMI and EAR_JACK cables detached.
|
||||
|
||||
In order to update the state of an extcon device, enter a hex
|
||||
state number starting with 0x.
|
||||
echo 0xHEX > state
|
||||
|
||||
This updates the whole state of the extcon dev.
|
||||
Inputs of all the methods are required to meet the
|
||||
mutually_exclusive contidions if they exist.
|
||||
|
||||
It is recommended to use this "global" state interface if
|
||||
you need to enter the value atomically. The later state
|
||||
interface associated with each cable cannot update
|
||||
multiple cable states of an extcon device simultaneously.
|
||||
|
||||
What: /sys/class/extcon/.../cable.x/name
|
||||
Date: February 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/extcon/.../cable.x/name shows the name of cable
|
||||
"x" (integer between 0 and 31) of an extcon device.
|
||||
|
||||
What: /sys/class/extcon/.../cable.x/state
|
||||
Date: February 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/extcon/.../cable.x/name shows and stores the
|
||||
state of cable "x" (integer between 0 and 31) of an extcon
|
||||
device. The state value is either 0 (detached) or 1
|
||||
(attached).
|
||||
|
||||
What: /sys/class/extcon/.../mutually_exclusive/...
|
||||
Date: December 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
Shows the relations of mutually exclusiveness. For example,
|
||||
if the mutually_exclusive array of extcon_dev is
|
||||
{0x3, 0x5, 0xC, 0x0}, the, the output is:
|
||||
# ls mutually_exclusive/
|
||||
0x3
|
||||
0x5
|
||||
0xc
|
||||
#
|
||||
|
||||
Note that mutually_exclusive is a sub-directory of the extcon
|
||||
device and the file names under the mutually_exclusive
|
||||
directory show the mutually-exclusive sets, not the contents
|
||||
of the files.
|
@ -218,16 +218,16 @@ The development process
|
||||
Linux kernel development process currently consists of a few different
|
||||
main kernel "branches" and lots of different subsystem-specific kernel
|
||||
branches. These different branches are:
|
||||
- main 2.6.x kernel tree
|
||||
- 2.6.x.y -stable kernel tree
|
||||
- 2.6.x -git kernel patches
|
||||
- main 3.x kernel tree
|
||||
- 3.x.y -stable kernel tree
|
||||
- 3.x -git kernel patches
|
||||
- subsystem specific kernel trees and patches
|
||||
- the 2.6.x -next kernel tree for integration tests
|
||||
- the 3.x -next kernel tree for integration tests
|
||||
|
||||
2.6.x kernel tree
|
||||
3.x kernel tree
|
||||
-----------------
|
||||
2.6.x kernels are maintained by Linus Torvalds, and can be found on
|
||||
kernel.org in the pub/linux/kernel/v2.6/ directory. Its development
|
||||
3.x kernels are maintained by Linus Torvalds, and can be found on
|
||||
kernel.org in the pub/linux/kernel/v3.x/ directory. Its development
|
||||
process is as follows:
|
||||
- As soon as a new kernel is released a two weeks window is open,
|
||||
during this period of time maintainers can submit big diffs to
|
||||
@ -262,20 +262,20 @@ mailing list about kernel releases:
|
||||
released according to perceived bug status, not according to a
|
||||
preconceived timeline."
|
||||
|
||||
2.6.x.y -stable kernel tree
|
||||
3.x.y -stable kernel tree
|
||||
---------------------------
|
||||
Kernels with 4-part versions are -stable kernels. They contain
|
||||
Kernels with 3-part versions are -stable kernels. They contain
|
||||
relatively small and critical fixes for security problems or significant
|
||||
regressions discovered in a given 2.6.x kernel.
|
||||
regressions discovered in a given 3.x kernel.
|
||||
|
||||
This is the recommended branch for users who want the most recent stable
|
||||
kernel and are not interested in helping test development/experimental
|
||||
versions.
|
||||
|
||||
If no 2.6.x.y kernel is available, then the highest numbered 2.6.x
|
||||
If no 3.x.y kernel is available, then the highest numbered 3.x
|
||||
kernel is the current stable kernel.
|
||||
|
||||
2.6.x.y are maintained by the "stable" team <stable@vger.kernel.org>, and
|
||||
3.x.y are maintained by the "stable" team <stable@vger.kernel.org>, and
|
||||
are released as needs dictate. The normal release period is approximately
|
||||
two weeks, but it can be longer if there are no pressing problems. A
|
||||
security-related problem, instead, can cause a release to happen almost
|
||||
@ -285,7 +285,7 @@ The file Documentation/stable_kernel_rules.txt in the kernel tree
|
||||
documents what kinds of changes are acceptable for the -stable tree, and
|
||||
how the release process works.
|
||||
|
||||
2.6.x -git patches
|
||||
3.x -git patches
|
||||
------------------
|
||||
These are daily snapshots of Linus' kernel tree which are managed in a
|
||||
git repository (hence the name.) These patches are usually released
|
||||
@ -317,13 +317,13 @@ revisions to it, and maintainers can mark patches as under review,
|
||||
accepted, or rejected. Most of these patchwork sites are listed at
|
||||
http://patchwork.kernel.org/.
|
||||
|
||||
2.6.x -next kernel tree for integration tests
|
||||
3.x -next kernel tree for integration tests
|
||||
---------------------------------------------
|
||||
Before updates from subsystem trees are merged into the mainline 2.6.x
|
||||
Before updates from subsystem trees are merged into the mainline 3.x
|
||||
tree, they need to be integration-tested. For this purpose, a special
|
||||
testing repository exists into which virtually all subsystem trees are
|
||||
pulled on an almost daily basis:
|
||||
http://git.kernel.org/?p=linux/kernel/git/sfr/linux-next.git
|
||||
http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git
|
||||
http://linux.f-seidel.de/linux-next/pmwiki/
|
||||
|
||||
This way, the -next kernel gives a summary outlook onto what will be
|
||||
|
@ -98,7 +98,8 @@ Your cooperation is appreciated.
|
||||
8 = /dev/random Nondeterministic random number gen.
|
||||
9 = /dev/urandom Faster, less secure random number gen.
|
||||
10 = /dev/aio Asynchronous I/O notification interface
|
||||
11 = /dev/kmsg Writes to this come out as printk's
|
||||
11 = /dev/kmsg Writes to this come out as printk's, reads
|
||||
export the buffered printk records.
|
||||
12 = /dev/oldmem Used by crashdump kernels to access
|
||||
the memory of the kernel that crashed.
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
NVIDIA Tegra20 MC(Memory Controller)
|
||||
|
||||
Required properties:
|
||||
- compatible : "nvidia,tegra20-mc"
|
||||
- reg : Should contain 2 register ranges(address and length); see the
|
||||
example below. Note that the MC registers are interleaved with the
|
||||
GART registers, and hence must be represented as multiple ranges.
|
||||
- interrupts : Should contain MC General interrupt.
|
||||
|
||||
Example:
|
||||
mc {
|
||||
compatible = "nvidia,tegra20-mc";
|
||||
reg = <0x7000f000 0x024
|
||||
0x7000f03c 0x3c4>;
|
||||
interrupts = <0 77 0x04>;
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
NVIDIA Tegra30 MC(Memory Controller)
|
||||
|
||||
Required properties:
|
||||
- compatible : "nvidia,tegra30-mc"
|
||||
- reg : Should contain 4 register ranges(address and length); see the
|
||||
example below. Note that the MC registers are interleaved with the
|
||||
SMMU registers, and hence must be represented as multiple ranges.
|
||||
- interrupts : Should contain MC General interrupt.
|
||||
|
||||
Example:
|
||||
mc {
|
||||
compatible = "nvidia,tegra30-mc";
|
||||
reg = <0x7000f000 0x010
|
||||
0x7000f03c 0x1b4
|
||||
0x7000f200 0x028
|
||||
0x7000f284 0x17c>;
|
||||
interrupts = <0 77 0x04>;
|
||||
};
|
@ -2,17 +2,17 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
This document describes how to use the dynamic debug (ddebug) feature.
|
||||
This document describes how to use the dynamic debug (dyndbg) feature.
|
||||
|
||||
Dynamic debug is designed to allow you to dynamically enable/disable kernel
|
||||
code to obtain additional kernel information. Currently, if
|
||||
CONFIG_DYNAMIC_DEBUG is set, then all pr_debug()/dev_dbg() calls can be
|
||||
dynamically enabled per-callsite.
|
||||
Dynamic debug is designed to allow you to dynamically enable/disable
|
||||
kernel code to obtain additional kernel information. Currently, if
|
||||
CONFIG_DYNAMIC_DEBUG is set, then all pr_debug()/dev_dbg() calls can
|
||||
be dynamically enabled per-callsite.
|
||||
|
||||
Dynamic debug has even more useful features:
|
||||
|
||||
* Simple query language allows turning on and off debugging statements by
|
||||
matching any combination of 0 or 1 of:
|
||||
* Simple query language allows turning on and off debugging
|
||||
statements by matching any combination of 0 or 1 of:
|
||||
|
||||
- source filename
|
||||
- function name
|
||||
@ -20,17 +20,19 @@ Dynamic debug has even more useful features:
|
||||
- module name
|
||||
- format string
|
||||
|
||||
* Provides a debugfs control file: <debugfs>/dynamic_debug/control which can be
|
||||
read to display the complete list of known debug statements, to help guide you
|
||||
* Provides a debugfs control file: <debugfs>/dynamic_debug/control
|
||||
which can be read to display the complete list of known debug
|
||||
statements, to help guide you
|
||||
|
||||
Controlling dynamic debug Behaviour
|
||||
===================================
|
||||
|
||||
The behaviour of pr_debug()/dev_dbg()s are controlled via writing to a
|
||||
control file in the 'debugfs' filesystem. Thus, you must first mount the debugfs
|
||||
filesystem, in order to make use of this feature. Subsequently, we refer to the
|
||||
control file as: <debugfs>/dynamic_debug/control. For example, if you want to
|
||||
enable printing from source file 'svcsock.c', line 1603 you simply do:
|
||||
control file in the 'debugfs' filesystem. Thus, you must first mount
|
||||
the debugfs filesystem, in order to make use of this feature.
|
||||
Subsequently, we refer to the control file as:
|
||||
<debugfs>/dynamic_debug/control. For example, if you want to enable
|
||||
printing from source file 'svcsock.c', line 1603 you simply do:
|
||||
|
||||
nullarbor:~ # echo 'file svcsock.c line 1603 +p' >
|
||||
<debugfs>/dynamic_debug/control
|
||||
@ -44,15 +46,15 @@ nullarbor:~ # echo 'file svcsock.c wtf 1 +p' >
|
||||
Viewing Dynamic Debug Behaviour
|
||||
===========================
|
||||
|
||||
You can view the currently configured behaviour of all the debug statements
|
||||
via:
|
||||
You can view the currently configured behaviour of all the debug
|
||||
statements via:
|
||||
|
||||
nullarbor:~ # cat <debugfs>/dynamic_debug/control
|
||||
# filename:lineno [module]function flags format
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup - "SVCRDMA Module Removed, deregister RPC RDMA transport\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init - "\011max_inline : %d\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init - "\011sq_depth : %d\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init - "\011max_requests : %d\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup =_ "SVCRDMA Module Removed, deregister RPC RDMA transport\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init =_ "\011max_inline : %d\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init =_ "\011sq_depth : %d\012"
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init =_ "\011max_requests : %d\012"
|
||||
...
|
||||
|
||||
|
||||
@ -65,12 +67,12 @@ nullarbor:~ # grep -i rdma <debugfs>/dynamic_debug/control | wc -l
|
||||
nullarbor:~ # grep -i tcp <debugfs>/dynamic_debug/control | wc -l
|
||||
42
|
||||
|
||||
Note in particular that the third column shows the enabled behaviour
|
||||
flags for each debug statement callsite (see below for definitions of the
|
||||
flags). The default value, no extra behaviour enabled, is "-". So
|
||||
you can view all the debug statement callsites with any non-default flags:
|
||||
The third column shows the currently enabled flags for each debug
|
||||
statement callsite (see below for definitions of the flags). The
|
||||
default value, with no flags enabled, is "=_". So you can view all
|
||||
the debug statement callsites with any non-default flags:
|
||||
|
||||
nullarbor:~ # awk '$3 != "-"' <debugfs>/dynamic_debug/control
|
||||
nullarbor:~ # awk '$3 != "=_"' <debugfs>/dynamic_debug/control
|
||||
# filename:lineno [module]function flags format
|
||||
/usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process: st_sendto returned %d\012"
|
||||
|
||||
@ -103,15 +105,14 @@ specifications, followed by a flags change specification.
|
||||
|
||||
command ::= match-spec* flags-spec
|
||||
|
||||
The match-spec's are used to choose a subset of the known dprintk()
|
||||
The match-spec's are used to choose a subset of the known pr_debug()
|
||||
callsites to which to apply the flags-spec. Think of them as a query
|
||||
with implicit ANDs between each pair. Note that an empty list of
|
||||
match-specs is possible, but is not very useful because it will not
|
||||
match any debug statement callsites.
|
||||
match-specs will select all debug statement callsites.
|
||||
|
||||
A match specification comprises a keyword, which controls the attribute
|
||||
of the callsite to be compared, and a value to compare against. Possible
|
||||
keywords are:
|
||||
A match specification comprises a keyword, which controls the
|
||||
attribute of the callsite to be compared, and a value to compare
|
||||
against. Possible keywords are:
|
||||
|
||||
match-spec ::= 'func' string |
|
||||
'file' string |
|
||||
@ -164,15 +165,15 @@ format
|
||||
characters (") or single quote characters (').
|
||||
Examples:
|
||||
|
||||
format svcrdma: // many of the NFS/RDMA server dprintks
|
||||
format readahead // some dprintks in the readahead cache
|
||||
format svcrdma: // many of the NFS/RDMA server pr_debugs
|
||||
format readahead // some pr_debugs in the readahead cache
|
||||
format nfsd:\040SETATTR // one way to match a format with whitespace
|
||||
format "nfsd: SETATTR" // a neater way to match a format with whitespace
|
||||
format 'nfsd: SETATTR' // yet another way to match a format with whitespace
|
||||
|
||||
line
|
||||
The given line number or range of line numbers is compared
|
||||
against the line number of each dprintk() callsite. A single
|
||||
against the line number of each pr_debug() callsite. A single
|
||||
line number matches the callsite line number exactly. A
|
||||
range of line numbers matches any callsite between the first
|
||||
and last line number inclusive. An empty first number means
|
||||
@ -188,51 +189,93 @@ The flags specification comprises a change operation followed
|
||||
by one or more flag characters. The change operation is one
|
||||
of the characters:
|
||||
|
||||
-
|
||||
remove the given flags
|
||||
|
||||
+
|
||||
add the given flags
|
||||
|
||||
=
|
||||
set the flags to the given flags
|
||||
- remove the given flags
|
||||
+ add the given flags
|
||||
= set the flags to the given flags
|
||||
|
||||
The flags are:
|
||||
|
||||
f
|
||||
Include the function name in the printed message
|
||||
l
|
||||
Include line number in the printed message
|
||||
m
|
||||
Include module name in the printed message
|
||||
p
|
||||
Causes a printk() message to be emitted to dmesg
|
||||
t
|
||||
Include thread ID in messages not generated from interrupt context
|
||||
p enables the pr_debug() callsite.
|
||||
f Include the function name in the printed message
|
||||
l Include line number in the printed message
|
||||
m Include module name in the printed message
|
||||
t Include thread ID in messages not generated from interrupt context
|
||||
_ No flags are set. (Or'd with others on input)
|
||||
|
||||
Note the regexp ^[-+=][flmpt]+$ matches a flags specification.
|
||||
Note also that there is no convenient syntax to remove all
|
||||
the flags at once, you need to use "-flmpt".
|
||||
For display, the flags are preceded by '='
|
||||
(mnemonic: what the flags are currently equal to).
|
||||
|
||||
Note the regexp ^[-+=][flmpt_]+$ matches a flags specification.
|
||||
To clear all flags at once, use "=_" or "-flmpt".
|
||||
|
||||
|
||||
Debug messages during boot process
|
||||
Debug messages during Boot Process
|
||||
==================================
|
||||
|
||||
To be able to activate debug messages during the boot process,
|
||||
even before userspace and debugfs exists, use the boot parameter:
|
||||
ddebug_query="QUERY"
|
||||
To activate debug messages for core code and built-in modules during
|
||||
the boot process, even before userspace and debugfs exists, use
|
||||
dyndbg="QUERY", module.dyndbg="QUERY", or ddebug_query="QUERY"
|
||||
(ddebug_query is obsoleted by dyndbg, and deprecated). QUERY follows
|
||||
the syntax described above, but must not exceed 1023 characters. Your
|
||||
bootloader may impose lower limits.
|
||||
|
||||
These dyndbg params are processed just after the ddebug tables are
|
||||
processed, as part of the arch_initcall. Thus you can enable debug
|
||||
messages in all code run after this arch_initcall via this boot
|
||||
parameter.
|
||||
|
||||
QUERY follows the syntax described above, but must not exceed 1023
|
||||
characters. The enablement of debug messages is done as an arch_initcall.
|
||||
Thus you can enable debug messages in all code processed after this
|
||||
arch_initcall via this boot parameter.
|
||||
On an x86 system for example ACPI enablement is a subsys_initcall and
|
||||
ddebug_query="file ec.c +p"
|
||||
dyndbg="file ec.c +p"
|
||||
will show early Embedded Controller transactions during ACPI setup if
|
||||
your machine (typically a laptop) has an Embedded Controller.
|
||||
PCI (or other devices) initialization also is a hot candidate for using
|
||||
this boot parameter for debugging purposes.
|
||||
|
||||
If foo module is not built-in, foo.dyndbg will still be processed at
|
||||
boot time, without effect, but will be reprocessed when module is
|
||||
loaded later. dyndbg_query= and bare dyndbg= are only processed at
|
||||
boot.
|
||||
|
||||
|
||||
Debug Messages at Module Initialization Time
|
||||
============================================
|
||||
|
||||
When "modprobe foo" is called, modprobe scans /proc/cmdline for
|
||||
foo.params, strips "foo.", and passes them to the kernel along with
|
||||
params given in modprobe args or /etc/modprob.d/*.conf files,
|
||||
in the following order:
|
||||
|
||||
1. # parameters given via /etc/modprobe.d/*.conf
|
||||
options foo dyndbg=+pt
|
||||
options foo dyndbg # defaults to +p
|
||||
|
||||
2. # foo.dyndbg as given in boot args, "foo." is stripped and passed
|
||||
foo.dyndbg=" func bar +p; func buz +mp"
|
||||
|
||||
3. # args to modprobe
|
||||
modprobe foo dyndbg==pmf # override previous settings
|
||||
|
||||
These dyndbg queries are applied in order, with last having final say.
|
||||
This allows boot args to override or modify those from /etc/modprobe.d
|
||||
(sensible, since 1 is system wide, 2 is kernel or boot specific), and
|
||||
modprobe args to override both.
|
||||
|
||||
In the foo.dyndbg="QUERY" form, the query must exclude "module foo".
|
||||
"foo" is extracted from the param-name, and applied to each query in
|
||||
"QUERY", and only 1 match-spec of each type is allowed.
|
||||
|
||||
The dyndbg option is a "fake" module parameter, which means:
|
||||
|
||||
- modules do not need to define it explicitly
|
||||
- every module gets it tacitly, whether they use pr_debug or not
|
||||
- it doesnt appear in /sys/module/$module/parameters/
|
||||
To see it, grep the control file, or inspect /proc/cmdline.
|
||||
|
||||
For CONFIG_DYNAMIC_DEBUG kernels, any settings given at boot-time (or
|
||||
enabled by -DDEBUG flag during compilation) can be disabled later via
|
||||
the sysfs interface if the debug messages are no longer needed:
|
||||
|
||||
echo "module module_name -p" > <debugfs>/dynamic_debug/control
|
||||
|
||||
Examples
|
||||
========
|
||||
@ -260,3 +303,18 @@ nullarbor:~ # echo -n 'func svc_process -p' >
|
||||
// enable messages for NFS calls READ, READLINK, READDIR and READDIR+.
|
||||
nullarbor:~ # echo -n 'format "nfsd: READ" +p' >
|
||||
<debugfs>/dynamic_debug/control
|
||||
|
||||
// enable all messages
|
||||
nullarbor:~ # echo -n '+p' > <debugfs>/dynamic_debug/control
|
||||
|
||||
// add module, function to all enabled messages
|
||||
nullarbor:~ # echo -n '+mf' > <debugfs>/dynamic_debug/control
|
||||
|
||||
// boot-args example, with newlines and comments for readability
|
||||
Kernel command line: ...
|
||||
// see whats going on in dyndbg=value processing
|
||||
dynamic_debug.verbose=1
|
||||
// enable pr_debugs in 2 builtins, #cmt is stripped
|
||||
dyndbg="module params +p #cmt ; module sys +p"
|
||||
// enable pr_debugs in 2 functions in a module loaded later
|
||||
pc87360.dyndbg="func pc87360_init_device +p; func pc87360_find +p"
|
||||
|
124
Documentation/extcon/porting-android-switch-class
Normal file
124
Documentation/extcon/porting-android-switch-class
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
Staging/Android Switch Class Porting Guide
|
||||
(linux/drivers/staging/android/switch)
|
||||
(c) Copyright 2012 Samsung Electronics
|
||||
|
||||
AUTHORS
|
||||
MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
|
||||
/*****************************************************************
|
||||
* CHAPTER 1. *
|
||||
* PORTING SWITCH CLASS DEVICE DRIVERS *
|
||||
*****************************************************************/
|
||||
|
||||
****** STEP 1. Basic Functionality
|
||||
No extcon extended feature, but switch features only.
|
||||
|
||||
- struct switch_dev (fed to switch_dev_register/unregister)
|
||||
@name: no change
|
||||
@dev: no change
|
||||
@index: drop (not used in switch device driver side anyway)
|
||||
@state: no change
|
||||
If you have used @state with magic numbers, keep it
|
||||
at this step.
|
||||
@print_name: no change but type change (switch_dev->extcon_dev)
|
||||
@print_state: no change but type change (switch_dev->extcon_dev)
|
||||
|
||||
- switch_dev_register(sdev, dev)
|
||||
=> extcon_dev_register(edev, dev)
|
||||
: no change but type change (sdev->edev)
|
||||
- switch_dev_unregister(sdev)
|
||||
=> extcon_dev_unregister(edev)
|
||||
: no change but type change (sdev->edev)
|
||||
- switch_get_state(sdev)
|
||||
=> extcon_get_state(edev)
|
||||
: no change but type change (sdev->edev) and (return: int->u32)
|
||||
- switch_set_state(sdev, state)
|
||||
=> extcon_set_state(edev, state)
|
||||
: no change but type change (sdev->edev) and (state: int->u32)
|
||||
|
||||
With this changes, the ex-switch extcon class device works as it once
|
||||
worked as switch class device. However, it will now have additional
|
||||
interfaces (both ABI and in-kernel API) and different ABI locations.
|
||||
However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH,
|
||||
/sys/class/switch/* will be symbolically linked to /sys/class/extcon/
|
||||
so that they are still compatible with legacy userspace processes.
|
||||
|
||||
****** STEP 2. Multistate (no more magic numbers in state value)
|
||||
Extcon's extended features for switch device drivers with
|
||||
complex features usually required magic numbers in state
|
||||
value of switch_dev. With extcon, such magic numbers that
|
||||
support multiple cables (
|
||||
|
||||
1. Define cable names at edev->supported_cable.
|
||||
2. (Recommended) remove print_state callback.
|
||||
3. Use extcon_get_cable_state_(edev, index) or
|
||||
extcon_get_cable_state(edev, cable_name) instead of
|
||||
extcon_get_state(edev) if you intend to get a state of a specific
|
||||
cable. Same for set_state. This way, you can remove the usage of
|
||||
magic numbers in state value.
|
||||
4. Use extcon_update_state() if you are updating specific bits of
|
||||
the state value.
|
||||
|
||||
Example: a switch device driver w/ magic numbers for two cables.
|
||||
"0x00": no cables connected.
|
||||
"0x01": cable 1 connected
|
||||
"0x02": cable 2 connected
|
||||
"0x03": cable 1 and 2 connected
|
||||
1. edev->supported_cable = {"1", "2", NULL};
|
||||
2. edev->print_state = NULL;
|
||||
3. extcon_get_cable_state_(edev, 0) shows cable 1's state.
|
||||
extcon_get_cable_state(edev, "1") shows cable 1's state.
|
||||
extcon_set_cable_state_(edev, 1) sets cable 2's state.
|
||||
extcon_set_cable_state(edev, "2") sets cable 2's state
|
||||
4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0.
|
||||
|
||||
****** STEP 3. Notify other device drivers
|
||||
|
||||
You can notify others of the cable attach/detach events with
|
||||
notifier chains.
|
||||
|
||||
At the side of other device drivers (the extcon device itself
|
||||
does not need to get notified of its own events), there are two
|
||||
methods to register notifier_block for cable events:
|
||||
(a) for a specific cable or (b) for every cable.
|
||||
|
||||
(a) extcon_register_interest(obj, extcon_name, cable_name, nb)
|
||||
Example: want to get news of "MAX8997_MUIC"'s "USB" cable
|
||||
|
||||
obj = kzalloc(sizeof(struct extcon_specific_cable_nb),
|
||||
GFP_KERNEL);
|
||||
nb->notifier_call = the_callback_to_handle_usb;
|
||||
|
||||
extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb);
|
||||
|
||||
(b) extcon_register_notifier(edev, nb)
|
||||
Call nb for any changes in edev.
|
||||
|
||||
Please note that in order to properly behave with method (a),
|
||||
the extcon device driver should support multistate feature (STEP 2).
|
||||
|
||||
****** STEP 4. Inter-cable relation (mutually exclusive)
|
||||
|
||||
You can provide inter-cable mutually exclusiveness information
|
||||
for an extcon device. When cables A and B are declared to be mutually
|
||||
exclusive, the two cables cannot be in ATTACHED state simulteneously.
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* CHAPTER 2. *
|
||||
* PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT *
|
||||
*****************************************************************/
|
||||
|
||||
****** ABI Location
|
||||
|
||||
If "CONFIG_ANDROID" is enabled and "CONFIG_ANDROID_SWITCH" is
|
||||
disabled, /sys/class/switch/* are created as symbolic links to
|
||||
/sys/class/extcon/*. Because CONFIG_ANDROID_SWITCH creates
|
||||
/sys/class/switch directory, we disable symboling linking if
|
||||
CONFIG_ANDROID_SWITCH is enabled.
|
||||
|
||||
The two files of switch class, name and state, are provided with
|
||||
extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is
|
||||
not enabled or print_state callback is supplied, the output of
|
||||
state ABI is same with switch class.
|
@ -2,7 +2,14 @@ The following is a list of files and features that are going to be
|
||||
removed in the kernel source tree. Every entry should contain what
|
||||
exactly is going away, why it is happening, and who is going to be doing
|
||||
the work. When the feature is removed from the kernel, it should also
|
||||
be removed from this file.
|
||||
be removed from this file. The suggested deprecation period is 3 releases.
|
||||
|
||||
---------------------------
|
||||
|
||||
What: ddebug_query="query" boot cmdline param
|
||||
When: v3.8
|
||||
Why: obsoleted by dyndbg="query" and module.dyndbg="query"
|
||||
Who: Jim Cromie <jim.cromie@gmail.com>, Jason Baron <jbaron@redhat.com>
|
||||
|
||||
---------------------------
|
||||
|
||||
|
@ -611,7 +611,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
|
||||
ddebug_query= [KNL,DYNAMIC_DEBUG] Enable debug messages at early boot
|
||||
time. See Documentation/dynamic-debug-howto.txt for
|
||||
details.
|
||||
details. Deprecated, see dyndbg.
|
||||
|
||||
debug [KNL] Enable kernel debugging (events log level).
|
||||
|
||||
@ -731,6 +731,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
|
||||
dscc4.setup= [NET]
|
||||
|
||||
dyndbg[="val"] [KNL,DYNAMIC_DEBUG]
|
||||
module.dyndbg[="val"]
|
||||
Enable debug messages at boot time. See
|
||||
Documentation/dynamic-debug-howto.txt for details.
|
||||
|
||||
earlycon= [KNL] Output early console device and options.
|
||||
uart[8250],io,<addr>[,options]
|
||||
uart[8250],mmio,<addr>[,options]
|
||||
|
57
Documentation/memory-devices/ti-emif.txt
Normal file
57
Documentation/memory-devices/ti-emif.txt
Normal file
@ -0,0 +1,57 @@
|
||||
TI EMIF SDRAM Controller Driver:
|
||||
|
||||
Author
|
||||
========
|
||||
Aneesh V <aneesh@ti.com>
|
||||
|
||||
Location
|
||||
============
|
||||
driver/memory/emif.c
|
||||
|
||||
Supported SoCs:
|
||||
===================
|
||||
TI OMAP44xx
|
||||
TI OMAP54xx
|
||||
|
||||
Menuconfig option:
|
||||
==========================
|
||||
Device Drivers
|
||||
Memory devices
|
||||
Texas Instruments EMIF driver
|
||||
|
||||
Description
|
||||
===========
|
||||
This driver is for the EMIF module available in Texas Instruments
|
||||
SoCs. EMIF is an SDRAM controller that, based on its revision,
|
||||
supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
|
||||
This driver takes care of only LPDDR2 memories presently. The
|
||||
functions of the driver includes re-configuring AC timing
|
||||
parameters and other settings during frequency, voltage and
|
||||
temperature changes
|
||||
|
||||
Platform Data (see include/linux/platform_data/emif_plat.h):
|
||||
=====================================================================
|
||||
DDR device details and other board dependent and SoC dependent
|
||||
information can be passed through platform data (struct emif_platform_data)
|
||||
- DDR device details: 'struct ddr_device_info'
|
||||
- Device AC timings: 'struct lpddr2_timings' and 'struct lpddr2_min_tck'
|
||||
- Custom configurations: customizable policy options through
|
||||
'struct emif_custom_configs'
|
||||
- IP revision
|
||||
- PHY type
|
||||
|
||||
Interface to the external world:
|
||||
================================
|
||||
EMIF driver registers notifiers for voltage and frequency changes
|
||||
affecting EMIF and takes appropriate actions when these are invoked.
|
||||
- freq_pre_notify_handling()
|
||||
- freq_post_notify_handling()
|
||||
- volt_notify_handling()
|
||||
|
||||
Debugfs
|
||||
========
|
||||
The driver creates two debugfs entries per device.
|
||||
- regcache_dump : dump of register values calculated and saved for all
|
||||
frequencies used so far.
|
||||
- mr4 : last polled value of MR4 register in the LPDDR2 device. MR4
|
||||
indicates the current temperature level of the device.
|
@ -160,7 +160,7 @@ QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry drivers/scsi/arm/queue.c
|
||||
HTB_CMAGIC 0xFEFAFEF1 htb_class net/sched/sch_htb.c
|
||||
NMI_MAGIC 0x48414d4d455201 nmi_s arch/mips/include/asm/sn/nmi.h
|
||||
|
||||
请注意,在声音记忆管理中仍然有每一些被定义的驱动魔术值。查看include/sound/sndmagic.h来获取他们完整的列表信息。很多OSS声音驱动拥有自己从声卡PCI ID构建的魔术值-他们也没有被列在这里。
|
||||
请注意,在声音记忆管理中仍然有一些特殊的为每个驱动定义的魔术值。查看include/sound/sndmagic.h来获取他们完整的列表信息。很多OSS声音驱动拥有自己从声卡PCI ID构建的魔术值-他们也没有被列在这里。
|
||||
|
||||
IrDA子系统也使用了大量的自己的魔术值,查看include/net/irda/irda.h来获取他们完整的信息。
|
||||
|
||||
|
@ -271,7 +271,8 @@ int alloc_bootmem_huge_page(struct hstate *hstate)
|
||||
|
||||
unsigned long gpage_npages[MMU_PAGE_COUNT];
|
||||
|
||||
static int __init do_gpage_early_setup(char *param, char *val)
|
||||
static int __init do_gpage_early_setup(char *param, char *val,
|
||||
const char *unused)
|
||||
{
|
||||
static phys_addr_t size;
|
||||
unsigned long npages;
|
||||
|
@ -140,4 +140,8 @@ source "drivers/virt/Kconfig"
|
||||
|
||||
source "drivers/devfreq/Kconfig"
|
||||
|
||||
source "drivers/extcon/Kconfig"
|
||||
|
||||
source "drivers/memory/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -134,3 +134,5 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/
|
||||
obj-$(CONFIG_HYPERV) += hv/
|
||||
|
||||
obj-$(CONFIG_PM_DEVFREQ) += devfreq/
|
||||
obj-$(CONFIG_EXTCON) += extcon/
|
||||
obj-$(CONFIG_MEMORY) += memory/
|
||||
|
@ -720,21 +720,21 @@ static int acpi_pci_link_add(struct acpi_device *device)
|
||||
acpi_device_bid(device));
|
||||
for (i = 0; i < link->irq.possible_count; i++) {
|
||||
if (link->irq.active == link->irq.possible[i]) {
|
||||
printk(" *%d", link->irq.possible[i]);
|
||||
printk(KERN_CONT " *%d", link->irq.possible[i]);
|
||||
found = 1;
|
||||
} else
|
||||
printk(" %d", link->irq.possible[i]);
|
||||
printk(KERN_CONT " %d", link->irq.possible[i]);
|
||||
}
|
||||
|
||||
printk(")");
|
||||
printk(KERN_CONT ")");
|
||||
|
||||
if (!found)
|
||||
printk(" *%d", link->irq.active);
|
||||
printk(KERN_CONT " *%d", link->irq.active);
|
||||
|
||||
if (!link->device->status.enabled)
|
||||
printk(", disabled.");
|
||||
printk(KERN_CONT ", disabled.");
|
||||
|
||||
printk("\n");
|
||||
printk(KERN_CONT "\n");
|
||||
|
||||
list_add_tail(&link->list, &acpi_link_list);
|
||||
|
||||
|
@ -887,7 +887,7 @@ int __init acpi_sleep_init(void)
|
||||
status = acpi_get_sleep_type_data(i, &type_a, &type_b);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
sleep_states[i] = 1;
|
||||
printk(" S%d", i);
|
||||
printk(KERN_CONT " S%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,7 +901,7 @@ int __init acpi_sleep_init(void)
|
||||
hibernation_set_ops(old_suspend_ordering ?
|
||||
&acpi_hibernation_ops_old : &acpi_hibernation_ops);
|
||||
sleep_states[ACPI_STATE_S4] = 1;
|
||||
printk(" S4");
|
||||
printk(KERN_CONT " S4");
|
||||
if (!nosigcheck) {
|
||||
acpi_get_table(ACPI_SIG_FACS, 1,
|
||||
(struct acpi_table_header **)&facs);
|
||||
@ -914,11 +914,11 @@ int __init acpi_sleep_init(void)
|
||||
status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
sleep_states[ACPI_STATE_S5] = 1;
|
||||
printk(" S5");
|
||||
printk(KERN_CONT " S5");
|
||||
pm_power_off_prepare = acpi_power_off_prepare;
|
||||
pm_power_off = acpi_power_off;
|
||||
}
|
||||
printk(")\n");
|
||||
printk(KERN_CONT ")\n");
|
||||
/*
|
||||
* Register the tts_notifier to reboot notifier list so that the _TTS
|
||||
* object can also be evaluated when the system enters S5.
|
||||
|
@ -21,8 +21,7 @@
|
||||
#include "power/power.h"
|
||||
|
||||
/* /sys/devices/system */
|
||||
/* FIXME: make static after drivers/base/sys.c is deleted */
|
||||
struct kset *system_kset;
|
||||
static struct kset *system_kset;
|
||||
|
||||
#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
@ -65,7 +66,7 @@ static inline int device_is_not_partition(struct device *dev)
|
||||
* @dev: struct device to get the name of
|
||||
*
|
||||
* Will return the device's driver's name if it is bound to a device. If
|
||||
* the device is not bound to a device, it will return the name of the bus
|
||||
* the device is not bound to a driver, it will return the name of the bus
|
||||
* it is attached to. If it is not attached to a bus either, an empty
|
||||
* string will be returned.
|
||||
*/
|
||||
@ -878,8 +879,8 @@ EXPORT_SYMBOL_GPL(dev_set_name);
|
||||
* to NULL prevents an entry from being created. class->dev_kobj must
|
||||
* be set (or cleared) before any devices are registered to the class
|
||||
* otherwise device_create_sys_dev_entry() and
|
||||
* device_remove_sys_dev_entry() will disagree about the the presence
|
||||
* of the link.
|
||||
* device_remove_sys_dev_entry() will disagree about the presence of
|
||||
* the link.
|
||||
*/
|
||||
static struct kobject *device_to_dev_kobj(struct device *dev)
|
||||
{
|
||||
@ -1843,15 +1844,60 @@ void device_shutdown(void)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
|
||||
int __dev_printk(const char *level, const struct device *dev,
|
||||
struct va_format *vaf)
|
||||
{
|
||||
char dict[128];
|
||||
size_t dictlen = 0;
|
||||
const char *subsys;
|
||||
|
||||
if (!dev)
|
||||
return printk("%s(NULL device *): %pV", level, vaf);
|
||||
|
||||
return printk("%s%s %s: %pV",
|
||||
level, dev_driver_string(dev), dev_name(dev), vaf);
|
||||
if (dev->class)
|
||||
subsys = dev->class->name;
|
||||
else if (dev->bus)
|
||||
subsys = dev->bus->name;
|
||||
else
|
||||
goto skip;
|
||||
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"SUBSYSTEM=%s", subsys);
|
||||
|
||||
/*
|
||||
* Add device identifier DEVICE=:
|
||||
* b12:8 block dev_t
|
||||
* c127:3 char dev_t
|
||||
* n8 netdev ifindex
|
||||
* +sound:card0 subsystem:devname
|
||||
*/
|
||||
if (MAJOR(dev->devt)) {
|
||||
char c;
|
||||
|
||||
if (strcmp(subsys, "block") == 0)
|
||||
c = 'b';
|
||||
else
|
||||
c = 'c';
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=%c%u:%u",
|
||||
c, MAJOR(dev->devt), MINOR(dev->devt));
|
||||
} else if (strcmp(subsys, "net") == 0) {
|
||||
struct net_device *net = to_net_dev(dev);
|
||||
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=n%u", net->ifindex);
|
||||
} else {
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=+%s:%s", subsys, dev_name(dev));
|
||||
}
|
||||
skip:
|
||||
return printk_emit(0, level[1] - '0',
|
||||
dictlen ? dict : NULL, dictlen,
|
||||
"%s %s: %pV",
|
||||
dev_driver_string(dev), dev_name(dev), vaf);
|
||||
}
|
||||
EXPORT_SYMBOL(__dev_printk);
|
||||
|
||||
|
@ -309,6 +309,10 @@ EXPORT_SYMBOL_GPL(devres_remove);
|
||||
* which @match returns 1. If @match is NULL, it's considered to
|
||||
* match all. If found, the resource is removed atomically and freed.
|
||||
*
|
||||
* Note that the release function for the resource will not be called,
|
||||
* only the devres-allocated data will be freed. The caller becomes
|
||||
* responsible for freeing any other data.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 if devres is found and freed, -ENOENT if not found.
|
||||
*/
|
||||
@ -326,6 +330,37 @@ int devres_destroy(struct device *dev, dr_release_t release,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devres_destroy);
|
||||
|
||||
|
||||
/**
|
||||
* devres_release - Find a device resource and destroy it, calling release
|
||||
* @dev: Device to find resource from
|
||||
* @release: Look for resources associated with this release function
|
||||
* @match: Match function (optional)
|
||||
* @match_data: Data for the match function
|
||||
*
|
||||
* Find the latest devres of @dev associated with @release and for
|
||||
* which @match returns 1. If @match is NULL, it's considered to
|
||||
* match all. If found, the resource is removed atomically, the
|
||||
* release function called and the resource freed.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 if devres is found and freed, -ENOENT if not found.
|
||||
*/
|
||||
int devres_release(struct device *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
void *res;
|
||||
|
||||
res = devres_remove(dev, release, match, match_data);
|
||||
if (unlikely(!res))
|
||||
return -ENOENT;
|
||||
|
||||
(*release)(dev, res);
|
||||
devres_free(res);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devres_release);
|
||||
|
||||
static int remove_nodes(struct device *dev,
|
||||
struct list_head *first, struct list_head *end,
|
||||
struct list_head *todo)
|
||||
|
@ -7,9 +7,9 @@
|
||||
* devtmpfs, a tmpfs-based filesystem is created. Every driver-core
|
||||
* device which requests a device node, will add a node in this
|
||||
* filesystem.
|
||||
* By default, all devices are named after the the name of the
|
||||
* device, owned by root and have a default mode of 0600. Subsystems
|
||||
* can overwrite the default setting if needed.
|
||||
* By default, all devices are named after the name of the device,
|
||||
* owned by root and have a default mode of 0600. Subsystems can
|
||||
* overwrite the default setting if needed.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -293,7 +293,7 @@ EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
|
||||
* cpu in the kernel context. Calls begin_cpu_access to allow exporter-specific
|
||||
* preparations. Coherency is only guaranteed in the specified range for the
|
||||
* specified access direction.
|
||||
* @dma_buf: [in] buffer to prepare cpu access for.
|
||||
* @dmabuf: [in] buffer to prepare cpu access for.
|
||||
* @start: [in] start of range for cpu access.
|
||||
* @len: [in] length of range for cpu access.
|
||||
* @direction: [in] length of range for cpu access.
|
||||
@ -320,7 +320,7 @@ EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
|
||||
* cpu in the kernel context. Calls end_cpu_access to allow exporter-specific
|
||||
* actions. Coherency is only guaranteed in the specified range for the
|
||||
* specified access direction.
|
||||
* @dma_buf: [in] buffer to complete cpu access for.
|
||||
* @dmabuf: [in] buffer to complete cpu access for.
|
||||
* @start: [in] start of range for cpu access.
|
||||
* @len: [in] length of range for cpu access.
|
||||
* @direction: [in] length of range for cpu access.
|
||||
@ -340,7 +340,7 @@ EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
|
||||
/**
|
||||
* dma_buf_kmap_atomic - Map a page of the buffer object into kernel address
|
||||
* space. The same restrictions as for kmap_atomic and friends apply.
|
||||
* @dma_buf: [in] buffer to map page from.
|
||||
* @dmabuf: [in] buffer to map page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to map.
|
||||
*
|
||||
* This call must always succeed, any necessary preparations that might fail
|
||||
@ -356,7 +356,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kmap_atomic);
|
||||
|
||||
/**
|
||||
* dma_buf_kunmap_atomic - Unmap a page obtained by dma_buf_kmap_atomic.
|
||||
* @dma_buf: [in] buffer to unmap page from.
|
||||
* @dmabuf: [in] buffer to unmap page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to unmap.
|
||||
* @vaddr: [in] kernel space pointer obtained from dma_buf_kmap_atomic.
|
||||
*
|
||||
@ -375,7 +375,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kunmap_atomic);
|
||||
/**
|
||||
* dma_buf_kmap - Map a page of the buffer object into kernel address space. The
|
||||
* same restrictions as for kmap and friends apply.
|
||||
* @dma_buf: [in] buffer to map page from.
|
||||
* @dmabuf: [in] buffer to map page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to map.
|
||||
*
|
||||
* This call must always succeed, any necessary preparations that might fail
|
||||
@ -391,7 +391,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kmap);
|
||||
|
||||
/**
|
||||
* dma_buf_kunmap - Unmap a page obtained by dma_buf_kmap.
|
||||
* @dma_buf: [in] buffer to unmap page from.
|
||||
* @dmabuf: [in] buffer to unmap page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to unmap.
|
||||
* @vaddr: [in] kernel space pointer obtained from dma_buf_kmap.
|
||||
*
|
||||
|
@ -80,7 +80,7 @@ struct device *driver_find_device(struct device_driver *drv,
|
||||
struct klist_iter i;
|
||||
struct device *dev;
|
||||
|
||||
if (!drv)
|
||||
if (!drv || !drv->p)
|
||||
return NULL;
|
||||
|
||||
klist_iter_init_node(&drv->p->klist_devices, &i,
|
||||
|
@ -807,44 +807,6 @@ static const struct file_operations oldmem_fops = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
|
||||
unsigned long count, loff_t pos)
|
||||
{
|
||||
char *line, *p;
|
||||
int i;
|
||||
ssize_t ret = -EFAULT;
|
||||
size_t len = iov_length(iv, count);
|
||||
|
||||
line = kmalloc(len + 1, GFP_KERNEL);
|
||||
if (line == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* copy all vectors into a single string, to ensure we do
|
||||
* not interleave our log line with other printk calls
|
||||
*/
|
||||
p = line;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (copy_from_user(p, iv[i].iov_base, iv[i].iov_len))
|
||||
goto out;
|
||||
p += iv[i].iov_len;
|
||||
}
|
||||
p[0] = '\0';
|
||||
|
||||
ret = printk("%s", line);
|
||||
/* printk can add a prefix */
|
||||
if (ret > len)
|
||||
ret = len;
|
||||
out:
|
||||
kfree(line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations kmsg_fops = {
|
||||
.aio_write = kmsg_writev,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static const struct memdev {
|
||||
const char *name;
|
||||
umode_t mode;
|
||||
@ -863,7 +825,9 @@ static const struct memdev {
|
||||
[7] = { "full", 0666, &full_fops, NULL },
|
||||
[8] = { "random", 0666, &random_fops, NULL },
|
||||
[9] = { "urandom", 0666, &urandom_fops, NULL },
|
||||
[11] = { "kmsg", 0, &kmsg_fops, NULL },
|
||||
#ifdef CONFIG_PRINTK
|
||||
[11] = { "kmsg", 0644, &kmsg_fops, NULL },
|
||||
#endif
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
[12] = { "oldmem", 0, &oldmem_fops, NULL },
|
||||
#endif
|
||||
|
32
drivers/extcon/Kconfig
Normal file
32
drivers/extcon/Kconfig
Normal file
@ -0,0 +1,32 @@
|
||||
menuconfig EXTCON
|
||||
tristate "External Connector Class (extcon) support"
|
||||
help
|
||||
Say Y here to enable external connector class (extcon) support.
|
||||
This allows monitoring external connectors by userspace
|
||||
via sysfs and uevent and supports external connectors with
|
||||
multiple states; i.e., an extcon that may have multiple
|
||||
cables attached. For example, an external connector of a device
|
||||
may be used to connect an HDMI cable and a AC adaptor, and to
|
||||
host USB ports. Many of 30-pin connectors including PDMI are
|
||||
also good examples.
|
||||
|
||||
if EXTCON
|
||||
|
||||
comment "Extcon Device Drivers"
|
||||
|
||||
config EXTCON_GPIO
|
||||
tristate "GPIO extcon support"
|
||||
depends on GENERIC_GPIO
|
||||
help
|
||||
Say Y here to enable GPIO based extcon support. Note that GPIO
|
||||
extcon supports single state per extcon instance.
|
||||
|
||||
config EXTCON_MAX8997
|
||||
tristate "MAX8997 EXTCON Support"
|
||||
depends on MFD_MAX8997
|
||||
help
|
||||
If you say yes here you get support for the MUIC device of
|
||||
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
|
||||
detector and switch.
|
||||
|
||||
endif # MULTISTATE_SWITCH
|
7
drivers/extcon/Makefile
Normal file
7
drivers/extcon/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for external connector class (extcon) devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EXTCON) += extcon_class.o
|
||||
obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o
|
||||
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* max8997-muic.c - MAX8997 muic driver for the Maxim 8997
|
||||
* extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electrnoics
|
||||
* Copyright (C) 2012 Samsung Electrnoics
|
||||
* Donggeun Kim <dg77.kim@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -13,11 +13,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
@ -30,6 +25,9 @@
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mfd/max8997.h>
|
||||
#include <linux/mfd/max8997-private.h>
|
||||
#include <linux/extcon.h>
|
||||
|
||||
#define DEV_NAME "max8997-muic"
|
||||
|
||||
/* MAX8997-MUIC STATUS1 register */
|
||||
#define STATUS1_ADC_SHIFT 0
|
||||
@ -95,7 +93,6 @@ static struct max8997_muic_irq muic_irqs[] = {
|
||||
|
||||
struct max8997_muic_info {
|
||||
struct device *dev;
|
||||
struct max8997_dev *iodev;
|
||||
struct i2c_client *muic;
|
||||
struct max8997_muic_platform_data *muic_pdata;
|
||||
|
||||
@ -106,12 +103,28 @@ struct max8997_muic_info {
|
||||
int pre_adc;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
struct extcon_dev *edev;
|
||||
};
|
||||
|
||||
const char *max8997_extcon_cable[] = {
|
||||
[0] = "USB",
|
||||
[1] = "USB-Host",
|
||||
[2] = "TA",
|
||||
[3] = "Fast-charger",
|
||||
[4] = "Slow-charger",
|
||||
[5] = "Charge-downstream",
|
||||
[6] = "MHL",
|
||||
[7] = "Dock-desk",
|
||||
[7] = "Dock-card",
|
||||
[8] = "JIG",
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int max8997_muic_handle_usb(struct max8997_muic_info *info,
|
||||
enum max8997_muic_usb_type usb_type, bool attached)
|
||||
{
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
int ret = 0;
|
||||
|
||||
if (usb_type == MAX8997_USB_HOST) {
|
||||
@ -125,25 +138,25 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info,
|
||||
}
|
||||
}
|
||||
|
||||
if (mdata->usb_callback)
|
||||
mdata->usb_callback(usb_type, attached);
|
||||
switch (usb_type) {
|
||||
case MAX8997_USB_HOST:
|
||||
extcon_set_cable_state(info->edev, "USB-Host", attached);
|
||||
break;
|
||||
case MAX8997_USB_DEVICE:
|
||||
extcon_set_cable_state(info->edev, "USB", attached);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void max8997_muic_handle_mhl(struct max8997_muic_info *info,
|
||||
bool attached)
|
||||
{
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
|
||||
if (mdata->mhl_callback)
|
||||
mdata->mhl_callback(attached);
|
||||
}
|
||||
|
||||
static int max8997_muic_handle_dock(struct max8997_muic_info *info,
|
||||
int adc, bool attached)
|
||||
{
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
int ret = 0;
|
||||
|
||||
/* switch to AUDIO */
|
||||
@ -157,14 +170,13 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info,
|
||||
|
||||
switch (adc) {
|
||||
case MAX8997_ADC_DESKDOCK:
|
||||
if (mdata->deskdock_callback)
|
||||
mdata->deskdock_callback(attached);
|
||||
extcon_set_cable_state(info->edev, "Dock-desk", attached);
|
||||
break;
|
||||
case MAX8997_ADC_CARDOCK:
|
||||
if (mdata->cardock_callback)
|
||||
mdata->cardock_callback(attached);
|
||||
extcon_set_cable_state(info->edev, "Dock-card", attached);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
@ -174,7 +186,6 @@ out:
|
||||
static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
|
||||
bool attached)
|
||||
{
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
int ret = 0;
|
||||
|
||||
/* switch to UART */
|
||||
@ -186,8 +197,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mdata->uart_callback)
|
||||
mdata->uart_callback(attached);
|
||||
extcon_set_cable_state(info->edev, "JIG", attached);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@ -201,7 +211,7 @@ static int max8997_muic_handle_adc_detach(struct max8997_muic_info *info)
|
||||
ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, false);
|
||||
break;
|
||||
case MAX8997_ADC_MHL:
|
||||
max8997_muic_handle_mhl(info, false);
|
||||
extcon_set_cable_state(info->edev, "MHL", false);
|
||||
break;
|
||||
case MAX8997_ADC_JIG_USB_1:
|
||||
case MAX8997_ADC_JIG_USB_2:
|
||||
@ -230,7 +240,7 @@ static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc)
|
||||
ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, true);
|
||||
break;
|
||||
case MAX8997_ADC_MHL:
|
||||
max8997_muic_handle_mhl(info, true);
|
||||
extcon_set_cable_state(info->edev, "MHL", true);
|
||||
break;
|
||||
case MAX8997_ADC_JIG_USB_1:
|
||||
case MAX8997_ADC_JIG_USB_2:
|
||||
@ -247,10 +257,40 @@ static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc)
|
||||
ret = max8997_muic_handle_adc_detach(info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->pre_adc = adc;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max8997_muic_handle_charger_type_detach(
|
||||
struct max8997_muic_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (info->pre_charger_type) {
|
||||
case MAX8997_CHARGER_TYPE_USB:
|
||||
extcon_set_cable_state(info->edev, "USB", false);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
extcon_set_cable_state(info->edev, "Charge-downstream", false);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
|
||||
extcon_set_cable_state(info->edev, "TA", false);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_500MA:
|
||||
extcon_set_cable_state(info->edev, "Slow-charger", false);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_1A:
|
||||
extcon_set_cable_state(info->edev, "Fast-charger", false);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -258,7 +298,6 @@ static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc)
|
||||
static int max8997_muic_handle_charger_type(struct max8997_muic_info *info,
|
||||
enum max8997_muic_charger_type charger_type)
|
||||
{
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
u8 adc;
|
||||
int ret;
|
||||
|
||||
@ -270,30 +309,29 @@ static int max8997_muic_handle_charger_type(struct max8997_muic_info *info,
|
||||
|
||||
switch (charger_type) {
|
||||
case MAX8997_CHARGER_TYPE_NONE:
|
||||
if (mdata->charger_callback)
|
||||
mdata->charger_callback(false, charger_type);
|
||||
if (info->pre_charger_type == MAX8997_CHARGER_TYPE_USB) {
|
||||
max8997_muic_handle_usb(info,
|
||||
MAX8997_USB_DEVICE, false);
|
||||
}
|
||||
ret = max8997_muic_handle_charger_type_detach(info);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_USB:
|
||||
if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) {
|
||||
max8997_muic_handle_usb(info,
|
||||
MAX8997_USB_DEVICE, true);
|
||||
}
|
||||
if (mdata->charger_callback)
|
||||
mdata->charger_callback(true, charger_type);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
|
||||
extcon_set_cable_state(info->edev, "Charge-downstream", true);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
|
||||
extcon_set_cable_state(info->edev, "TA", true);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_500MA:
|
||||
extcon_set_cable_state(info->edev, "Slow-charger", true);
|
||||
break;
|
||||
case MAX8997_CHARGER_TYPE_1A:
|
||||
if (mdata->charger_callback)
|
||||
mdata->charger_callback(true, charger_type);
|
||||
extcon_set_cable_state(info->edev, "Fast-charger", true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->pre_charger_type = charger_type;
|
||||
@ -305,18 +343,17 @@ static void max8997_muic_irq_work(struct work_struct *work)
|
||||
{
|
||||
struct max8997_muic_info *info = container_of(work,
|
||||
struct max8997_muic_info, irq_work);
|
||||
struct max8997_platform_data *pdata =
|
||||
dev_get_platdata(info->iodev->dev);
|
||||
u8 status[3];
|
||||
struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
|
||||
u8 status[2];
|
||||
u8 adc, chg_type;
|
||||
|
||||
int irq_type = info->irq - pdata->irq_base;
|
||||
int irq_type = info->irq - max8997->irq_base;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
|
||||
3, status);
|
||||
2, status);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to read muic register\n");
|
||||
mutex_unlock(&info->mutex);
|
||||
@ -340,8 +377,8 @@ static void max8997_muic_irq_work(struct work_struct *work)
|
||||
max8997_muic_handle_charger_type(info, chg_type);
|
||||
break;
|
||||
default:
|
||||
dev_info(info->dev, "misc interrupt: %s occurred\n",
|
||||
muic_irqs[irq_type].name);
|
||||
dev_info(info->dev, "misc interrupt: irq %d occurred\n",
|
||||
irq_type);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -387,21 +424,10 @@ static void max8997_muic_detect_dev(struct max8997_muic_info *info)
|
||||
max8997_muic_handle_charger_type(info, chg_type);
|
||||
}
|
||||
|
||||
static void max8997_initialize_device(struct max8997_muic_info *info)
|
||||
{
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mdata->num_init_data; i++) {
|
||||
max8997_write_reg(info->muic, mdata->init_data[i].addr,
|
||||
mdata->init_data[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit max8997_muic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
|
||||
struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev);
|
||||
struct max8997_muic_info *info;
|
||||
int ret, i;
|
||||
|
||||
@ -412,16 +438,8 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
|
||||
goto err_kfree;
|
||||
}
|
||||
|
||||
if (!pdata->muic_pdata) {
|
||||
dev_err(&pdev->dev, "failed to get platform_data\n");
|
||||
ret = -EINVAL;
|
||||
goto err_pdata;
|
||||
}
|
||||
info->muic_pdata = pdata->muic_pdata;
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->iodev = iodev;
|
||||
info->muic = iodev->muic;
|
||||
info->muic = max8997->muic;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
mutex_init(&info->mutex);
|
||||
@ -444,18 +462,41 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* External connector */
|
||||
info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL);
|
||||
if (!info->edev) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_irq;
|
||||
}
|
||||
info->edev->name = DEV_NAME;
|
||||
info->edev->supported_cable = max8997_extcon_cable;
|
||||
ret = extcon_dev_register(info->edev, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register extcon device\n");
|
||||
goto err_extcon;
|
||||
}
|
||||
|
||||
/* Initialize registers according to platform data */
|
||||
max8997_initialize_device(info);
|
||||
if (pdata->muic_pdata) {
|
||||
struct max8997_muic_platform_data *mdata = info->muic_pdata;
|
||||
|
||||
for (i = 0; i < mdata->num_init_data; i++) {
|
||||
max8997_write_reg(info->muic, mdata->init_data[i].addr,
|
||||
mdata->init_data[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initial device detection */
|
||||
max8997_muic_detect_dev(info);
|
||||
|
||||
return ret;
|
||||
|
||||
err_extcon:
|
||||
kfree(info->edev);
|
||||
err_irq:
|
||||
while (--i >= 0)
|
||||
free_irq(pdata->irq_base + muic_irqs[i].irq, info);
|
||||
err_pdata:
|
||||
kfree(info);
|
||||
err_kfree:
|
||||
return ret;
|
||||
@ -464,14 +505,15 @@ err_kfree:
|
||||
static int __devexit max8997_muic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max8997_muic_info *info = platform_get_drvdata(pdev);
|
||||
struct max8997_platform_data *pdata =
|
||||
dev_get_platdata(info->iodev->dev);
|
||||
struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
|
||||
free_irq(pdata->irq_base + muic_irqs[i].irq, info);
|
||||
free_irq(max8997->irq_base + muic_irqs[i].irq, info);
|
||||
cancel_work_sync(&info->irq_work);
|
||||
|
||||
extcon_dev_unregister(info->edev);
|
||||
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
@ -479,7 +521,7 @@ static int __devexit max8997_muic_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver max8997_muic_driver = {
|
||||
.driver = {
|
||||
.name = "max8997-muic",
|
||||
.name = DEV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max8997_muic_probe,
|
||||
@ -488,6 +530,6 @@ static struct platform_driver max8997_muic_driver = {
|
||||
|
||||
module_platform_driver(max8997_muic_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Maxim MAX8997 MUIC driver");
|
||||
MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver");
|
||||
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
832
drivers/extcon/extcon_class.c
Normal file
832
drivers/extcon/extcon_class.c
Normal file
@ -0,0 +1,832 @@
|
||||
/*
|
||||
* drivers/extcon/extcon_class.c
|
||||
*
|
||||
* External connector (extcon) class driver
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Author: Donggeun Kim <dg77.kim@samsung.com>
|
||||
* Author: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* based on android/drivers/switch/switch_class.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/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* extcon_cable_name suggests the standard cable names for commonly used
|
||||
* cable types.
|
||||
*
|
||||
* However, please do not use extcon_cable_name directly for extcon_dev
|
||||
* struct's supported_cable pointer unless your device really supports
|
||||
* every single port-type of the following cable names. Please choose cable
|
||||
* names that are actually used in your extcon device.
|
||||
*/
|
||||
const char *extcon_cable_name[] = {
|
||||
[EXTCON_USB] = "USB",
|
||||
[EXTCON_USB_HOST] = "USB-Host",
|
||||
[EXTCON_TA] = "TA",
|
||||
[EXTCON_FAST_CHARGER] = "Fast-charger",
|
||||
[EXTCON_SLOW_CHARGER] = "Slow-charger",
|
||||
[EXTCON_CHARGE_DOWNSTREAM] = "Charge-downstream",
|
||||
[EXTCON_HDMI] = "HDMI",
|
||||
[EXTCON_MHL] = "MHL",
|
||||
[EXTCON_DVI] = "DVI",
|
||||
[EXTCON_VGA] = "VGA",
|
||||
[EXTCON_DOCK] = "Dock",
|
||||
[EXTCON_LINE_IN] = "Line-in",
|
||||
[EXTCON_LINE_OUT] = "Line-out",
|
||||
[EXTCON_MIC_IN] = "Microphone",
|
||||
[EXTCON_HEADPHONE_OUT] = "Headphone",
|
||||
[EXTCON_SPDIF_IN] = "SPDIF-in",
|
||||
[EXTCON_SPDIF_OUT] = "SPDIF-out",
|
||||
[EXTCON_VIDEO_IN] = "Video-in",
|
||||
[EXTCON_VIDEO_OUT] = "Video-out",
|
||||
[EXTCON_MECHANICAL] = "Mechanical",
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct class *extcon_class;
|
||||
#if defined(CONFIG_ANDROID)
|
||||
static struct class_compat *switch_class;
|
||||
#endif /* CONFIG_ANDROID */
|
||||
|
||||
static LIST_HEAD(extcon_dev_list);
|
||||
static DEFINE_MUTEX(extcon_dev_list_lock);
|
||||
|
||||
/**
|
||||
* check_mutually_exclusive - Check if new_state violates mutually_exclusive
|
||||
* condition.
|
||||
* @edev: the extcon device
|
||||
* @new_state: new cable attach status for @edev
|
||||
*
|
||||
* Returns 0 if nothing violates. Returns the index + 1 for the first
|
||||
* violated condition.
|
||||
*/
|
||||
static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (!edev->mutually_exclusive)
|
||||
return 0;
|
||||
|
||||
for (i = 0; edev->mutually_exclusive[i]; i++) {
|
||||
int count = 0, j;
|
||||
u32 correspondants = new_state & edev->mutually_exclusive[i];
|
||||
u32 exp = 1;
|
||||
|
||||
for (j = 0; j < 32; j++) {
|
||||
if (exp & correspondants)
|
||||
count++;
|
||||
if (count > 1)
|
||||
return i + 1;
|
||||
exp <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int i, count = 0;
|
||||
struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
|
||||
|
||||
if (edev->print_state) {
|
||||
int ret = edev->print_state(edev, buf);
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
/* Use default if failed */
|
||||
}
|
||||
|
||||
if (edev->max_supported == 0)
|
||||
return sprintf(buf, "%u\n", edev->state);
|
||||
|
||||
for (i = 0; i < SUPPORTED_CABLE_MAX; i++) {
|
||||
if (!edev->supported_cable[i])
|
||||
break;
|
||||
count += sprintf(buf + count, "%s=%d\n",
|
||||
edev->supported_cable[i],
|
||||
!!(edev->state & (1 << i)));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int extcon_set_state(struct extcon_dev *edev, u32 state);
|
||||
static ssize_t state_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
u32 state;
|
||||
ssize_t ret = 0;
|
||||
struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
|
||||
|
||||
ret = sscanf(buf, "0x%x", &state);
|
||||
if (ret == 0)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = extcon_set_state(edev, state);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
|
||||
|
||||
/* Optional callback given by the user */
|
||||
if (edev->print_name) {
|
||||
int ret = edev->print_name(edev, buf);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s\n", dev_name(edev->dev));
|
||||
}
|
||||
|
||||
static ssize_t cable_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct extcon_cable *cable = container_of(attr, struct extcon_cable,
|
||||
attr_name);
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
cable->edev->supported_cable[cable->cable_index]);
|
||||
}
|
||||
|
||||
static ssize_t cable_state_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct extcon_cable *cable = container_of(attr, struct extcon_cable,
|
||||
attr_state);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
extcon_get_cable_state_(cable->edev,
|
||||
cable->cable_index));
|
||||
}
|
||||
|
||||
static ssize_t cable_state_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct extcon_cable *cable = container_of(attr, struct extcon_cable,
|
||||
attr_state);
|
||||
int ret, state;
|
||||
|
||||
ret = sscanf(buf, "%d", &state);
|
||||
if (ret == 0)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = extcon_set_cable_state_(cable->edev, cable->cable_index,
|
||||
state);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* extcon_update_state() - Update the cable attach states of the extcon device
|
||||
* only for the masked bits.
|
||||
* @edev: the extcon device
|
||||
* @mask: the bit mask to designate updated bits.
|
||||
* @state: new cable attach status for @edev
|
||||
*
|
||||
* Changing the state sends uevent with environment variable containing
|
||||
* the name of extcon device (envp[0]) and the state output (envp[1]).
|
||||
* Tizen uses this format for extcon device to get events from ports.
|
||||
* Android uses this format as well.
|
||||
*
|
||||
* Note that the notifier provides which bits are changed in the state
|
||||
* variable with the val parameter (second) to the callback.
|
||||
*/
|
||||
int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
|
||||
{
|
||||
char name_buf[120];
|
||||
char state_buf[120];
|
||||
char *prop_buf;
|
||||
char *envp[3];
|
||||
int env_offset = 0;
|
||||
int length;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&edev->lock, flags);
|
||||
|
||||
if (edev->state != ((edev->state & ~mask) | (state & mask))) {
|
||||
u32 old_state = edev->state;
|
||||
|
||||
if (check_mutually_exclusive(edev, (edev->state & ~mask) |
|
||||
(state & mask))) {
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
edev->state &= ~mask;
|
||||
edev->state |= state & mask;
|
||||
|
||||
raw_notifier_call_chain(&edev->nh, old_state, edev);
|
||||
|
||||
/* This could be in interrupt handler */
|
||||
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
|
||||
if (prop_buf) {
|
||||
length = name_show(edev->dev, NULL, prop_buf);
|
||||
if (length > 0) {
|
||||
if (prop_buf[length - 1] == '\n')
|
||||
prop_buf[length - 1] = 0;
|
||||
snprintf(name_buf, sizeof(name_buf),
|
||||
"NAME=%s", prop_buf);
|
||||
envp[env_offset++] = name_buf;
|
||||
}
|
||||
length = state_show(edev->dev, NULL, prop_buf);
|
||||
if (length > 0) {
|
||||
if (prop_buf[length - 1] == '\n')
|
||||
prop_buf[length - 1] = 0;
|
||||
snprintf(state_buf, sizeof(state_buf),
|
||||
"STATE=%s", prop_buf);
|
||||
envp[env_offset++] = state_buf;
|
||||
}
|
||||
envp[env_offset] = NULL;
|
||||
/* Unlock early before uevent */
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
|
||||
kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
|
||||
free_page((unsigned long)prop_buf);
|
||||
} else {
|
||||
/* Unlock early before uevent */
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
|
||||
dev_err(edev->dev, "out of memory in extcon_set_state\n");
|
||||
kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
|
||||
}
|
||||
} else {
|
||||
/* No changes */
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_update_state);
|
||||
|
||||
/**
|
||||
* extcon_set_state() - Set the cable attach states of the extcon device.
|
||||
* @edev: the extcon device
|
||||
* @state: new cable attach status for @edev
|
||||
*
|
||||
* Note that notifier provides which bits are changed in the state
|
||||
* variable with the val parameter (second) to the callback.
|
||||
*/
|
||||
int extcon_set_state(struct extcon_dev *edev, u32 state)
|
||||
{
|
||||
return extcon_update_state(edev, 0xffffffff, state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_set_state);
|
||||
|
||||
/**
|
||||
* extcon_find_cable_index() - Get the cable index based on the cable name.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @cable_name: cable name to be searched.
|
||||
*
|
||||
* Note that accessing a cable state based on cable_index is faster than
|
||||
* cable_name because using cable_name induces a loop with strncmp().
|
||||
* Thus, when get/set_cable_state is repeatedly used, using cable_index
|
||||
* is recommended.
|
||||
*/
|
||||
int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (edev->supported_cable) {
|
||||
for (i = 0; edev->supported_cable[i]; i++) {
|
||||
if (!strncmp(edev->supported_cable[i],
|
||||
cable_name, CABLE_NAME_MAX))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_find_cable_index);
|
||||
|
||||
/**
|
||||
* extcon_get_cable_state_() - Get the status of a specific cable.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @index: cable index that can be retrieved by extcon_find_cable_index().
|
||||
*/
|
||||
int extcon_get_cable_state_(struct extcon_dev *edev, int index)
|
||||
{
|
||||
if (index < 0 || (edev->max_supported && edev->max_supported <= index))
|
||||
return -EINVAL;
|
||||
|
||||
return !!(edev->state & (1 << index));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
|
||||
|
||||
/**
|
||||
* extcon_get_cable_state() - Get the status of a specific cable.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @cable_name: cable name.
|
||||
*
|
||||
* Note that this is slower than extcon_get_cable_state_.
|
||||
*/
|
||||
int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
|
||||
{
|
||||
return extcon_get_cable_state_(edev, extcon_find_cable_index
|
||||
(edev, cable_name));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_get_cable_state);
|
||||
|
||||
/**
|
||||
* extcon_get_cable_state_() - Set the status of a specific cable.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @index: cable index that can be retrieved by extcon_find_cable_index().
|
||||
* @cable_state: the new cable status. The default semantics is
|
||||
* true: attached / false: detached.
|
||||
*/
|
||||
int extcon_set_cable_state_(struct extcon_dev *edev,
|
||||
int index, bool cable_state)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
if (index < 0 || (edev->max_supported && edev->max_supported <= index))
|
||||
return -EINVAL;
|
||||
|
||||
state = cable_state ? (1 << index) : 0;
|
||||
return extcon_update_state(edev, 1 << index, state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
|
||||
|
||||
/**
|
||||
* extcon_get_cable_state() - Set the status of a specific cable.
|
||||
* @edev: the extcon device that has the cable.
|
||||
* @cable_name: cable name.
|
||||
* @cable_state: the new cable status. The default semantics is
|
||||
* true: attached / false: detached.
|
||||
*
|
||||
* Note that this is slower than extcon_set_cable_state_.
|
||||
*/
|
||||
int extcon_set_cable_state(struct extcon_dev *edev,
|
||||
const char *cable_name, bool cable_state)
|
||||
{
|
||||
return extcon_set_cable_state_(edev, extcon_find_cable_index
|
||||
(edev, cable_name), cable_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_set_cable_state);
|
||||
|
||||
/**
|
||||
* extcon_get_extcon_dev() - Get the extcon device instance from the name
|
||||
* @extcon_name: The extcon name provided with extcon_dev_register()
|
||||
*/
|
||||
struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
|
||||
{
|
||||
struct extcon_dev *sd;
|
||||
|
||||
mutex_lock(&extcon_dev_list_lock);
|
||||
list_for_each_entry(sd, &extcon_dev_list, entry) {
|
||||
if (!strcmp(sd->name, extcon_name))
|
||||
goto out;
|
||||
}
|
||||
sd = NULL;
|
||||
out:
|
||||
mutex_unlock(&extcon_dev_list_lock);
|
||||
return sd;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
|
||||
|
||||
static int _call_per_cable(struct notifier_block *nb, unsigned long val,
|
||||
void *ptr)
|
||||
{
|
||||
struct extcon_specific_cable_nb *obj = container_of(nb,
|
||||
struct extcon_specific_cable_nb, internal_nb);
|
||||
struct extcon_dev *edev = ptr;
|
||||
|
||||
if ((val & (1 << obj->cable_index)) !=
|
||||
(edev->state & (1 << obj->cable_index))) {
|
||||
bool cable_state = true;
|
||||
|
||||
obj->previous_value = val;
|
||||
|
||||
if (val & (1 << obj->cable_index))
|
||||
cable_state = false;
|
||||
|
||||
return obj->user_nb->notifier_call(obj->user_nb,
|
||||
cable_state, ptr);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* extcon_register_interest() - Register a notifier for a state change of a
|
||||
* specific cable, not a entier set of cables of a
|
||||
* extcon device.
|
||||
* @obj: an empty extcon_specific_cable_nb object to be returned.
|
||||
* @extcon_name: the name of extcon device.
|
||||
* @cable_name: the target cable name.
|
||||
* @nb: the notifier block to get notified.
|
||||
*
|
||||
* Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
|
||||
* the struct for you.
|
||||
*
|
||||
* extcon_register_interest is a helper function for those who want to get
|
||||
* notification for a single specific cable's status change. If a user wants
|
||||
* to get notification for any changes of all cables of a extcon device,
|
||||
* he/she should use the general extcon_register_notifier().
|
||||
*
|
||||
* Note that the second parameter given to the callback of nb (val) is
|
||||
* "old_state", not the current state. The current state can be retrieved
|
||||
* by looking at the third pameter (edev pointer)'s state value.
|
||||
*/
|
||||
int extcon_register_interest(struct extcon_specific_cable_nb *obj,
|
||||
const char *extcon_name, const char *cable_name,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
if (!obj || !extcon_name || !cable_name || !nb)
|
||||
return -EINVAL;
|
||||
|
||||
obj->edev = extcon_get_extcon_dev(extcon_name);
|
||||
if (!obj->edev)
|
||||
return -ENODEV;
|
||||
|
||||
obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
|
||||
if (obj->cable_index < 0)
|
||||
return -ENODEV;
|
||||
|
||||
obj->user_nb = nb;
|
||||
|
||||
obj->internal_nb.notifier_call = _call_per_cable;
|
||||
|
||||
return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
|
||||
}
|
||||
|
||||
/**
|
||||
* extcon_unregister_interest() - Unregister the notifier registered by
|
||||
* extcon_register_interest().
|
||||
* @obj: the extcon_specific_cable_nb object returned by
|
||||
* extcon_register_interest().
|
||||
*/
|
||||
int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return -EINVAL;
|
||||
|
||||
return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
|
||||
}
|
||||
|
||||
/**
|
||||
* extcon_register_notifier() - Register a notifee to get notified by
|
||||
* any attach status changes from the extcon.
|
||||
* @edev: the extcon device.
|
||||
* @nb: a notifier block to be registered.
|
||||
*
|
||||
* Note that the second parameter given to the callback of nb (val) is
|
||||
* "old_state", not the current state. The current state can be retrieved
|
||||
* by looking at the third pameter (edev pointer)'s state value.
|
||||
*/
|
||||
int extcon_register_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return raw_notifier_chain_register(&edev->nh, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_register_notifier);
|
||||
|
||||
/**
|
||||
* extcon_unregister_notifier() - Unregister a notifee from the extcon device.
|
||||
* @edev: the extcon device.
|
||||
* @nb: a registered notifier block to be unregistered.
|
||||
*/
|
||||
int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return raw_notifier_chain_unregister(&edev->nh, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
|
||||
|
||||
static struct device_attribute extcon_attrs[] = {
|
||||
__ATTR(state, S_IRUGO | S_IWUSR, state_show, state_store),
|
||||
__ATTR_RO(name),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
static int create_extcon_class(void)
|
||||
{
|
||||
if (!extcon_class) {
|
||||
extcon_class = class_create(THIS_MODULE, "extcon");
|
||||
if (IS_ERR(extcon_class))
|
||||
return PTR_ERR(extcon_class);
|
||||
extcon_class->dev_attrs = extcon_attrs;
|
||||
|
||||
#if defined(CONFIG_ANDROID)
|
||||
switch_class = class_compat_register("switch");
|
||||
if (WARN(!switch_class, "cannot allocate"))
|
||||
return -ENOMEM;
|
||||
#endif /* CONFIG_ANDROID */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void extcon_cleanup(struct extcon_dev *edev, bool skip)
|
||||
{
|
||||
mutex_lock(&extcon_dev_list_lock);
|
||||
list_del(&edev->entry);
|
||||
mutex_unlock(&extcon_dev_list_lock);
|
||||
|
||||
if (!skip && get_device(edev->dev)) {
|
||||
int index;
|
||||
|
||||
if (edev->mutually_exclusive && edev->max_supported) {
|
||||
for (index = 0; edev->mutually_exclusive[index];
|
||||
index++)
|
||||
kfree(edev->d_attrs_muex[index].attr.name);
|
||||
kfree(edev->d_attrs_muex);
|
||||
kfree(edev->attrs_muex);
|
||||
}
|
||||
|
||||
for (index = 0; index < edev->max_supported; index++)
|
||||
kfree(edev->cables[index].attr_g.name);
|
||||
|
||||
if (edev->max_supported) {
|
||||
kfree(edev->extcon_dev_type.groups);
|
||||
kfree(edev->cables);
|
||||
}
|
||||
|
||||
device_unregister(edev->dev);
|
||||
put_device(edev->dev);
|
||||
}
|
||||
|
||||
kfree(edev->dev);
|
||||
}
|
||||
|
||||
static void extcon_dev_release(struct device *dev)
|
||||
{
|
||||
struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
|
||||
|
||||
extcon_cleanup(edev, true);
|
||||
}
|
||||
|
||||
static const char *muex_name = "mutually_exclusive";
|
||||
static void dummy_sysfs_dev_release(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* extcon_dev_register() - Register a new extcon device
|
||||
* @edev : the new extcon device (should be allocated before calling)
|
||||
* @dev : the parent device for this extcon device.
|
||||
*
|
||||
* Among the members of edev struct, please set the "user initializing data"
|
||||
* in any case and set the "optional callbacks" if required. However, please
|
||||
* do not set the values of "internal data", which are initialized by
|
||||
* this function.
|
||||
*/
|
||||
int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
|
||||
{
|
||||
int ret, index = 0;
|
||||
|
||||
if (!extcon_class) {
|
||||
ret = create_extcon_class();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (edev->supported_cable) {
|
||||
/* Get size of array */
|
||||
for (index = 0; edev->supported_cable[index]; index++)
|
||||
;
|
||||
edev->max_supported = index;
|
||||
} else {
|
||||
edev->max_supported = 0;
|
||||
}
|
||||
|
||||
if (index > SUPPORTED_CABLE_MAX) {
|
||||
dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
|
||||
if (!edev->dev)
|
||||
return -ENOMEM;
|
||||
edev->dev->parent = dev;
|
||||
edev->dev->class = extcon_class;
|
||||
edev->dev->release = extcon_dev_release;
|
||||
|
||||
dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
|
||||
|
||||
if (edev->max_supported) {
|
||||
char buf[10];
|
||||
char *str;
|
||||
struct extcon_cable *cable;
|
||||
|
||||
edev->cables = kzalloc(sizeof(struct extcon_cable) *
|
||||
edev->max_supported, GFP_KERNEL);
|
||||
if (!edev->cables) {
|
||||
ret = -ENOMEM;
|
||||
goto err_sysfs_alloc;
|
||||
}
|
||||
for (index = 0; index < edev->max_supported; index++) {
|
||||
cable = &edev->cables[index];
|
||||
|
||||
snprintf(buf, 10, "cable.%d", index);
|
||||
str = kzalloc(sizeof(char) * (strlen(buf) + 1),
|
||||
GFP_KERNEL);
|
||||
if (!str) {
|
||||
for (index--; index >= 0; index--) {
|
||||
cable = &edev->cables[index];
|
||||
kfree(cable->attr_g.name);
|
||||
}
|
||||
ret = -ENOMEM;
|
||||
|
||||
goto err_alloc_cables;
|
||||
}
|
||||
strcpy(str, buf);
|
||||
|
||||
cable->edev = edev;
|
||||
cable->cable_index = index;
|
||||
cable->attrs[0] = &cable->attr_name.attr;
|
||||
cable->attrs[1] = &cable->attr_state.attr;
|
||||
cable->attrs[2] = NULL;
|
||||
cable->attr_g.name = str;
|
||||
cable->attr_g.attrs = cable->attrs;
|
||||
|
||||
cable->attr_name.attr.name = "name";
|
||||
cable->attr_name.attr.mode = 0444;
|
||||
cable->attr_name.show = cable_name_show;
|
||||
|
||||
cable->attr_state.attr.name = "state";
|
||||
cable->attr_state.attr.mode = 0644;
|
||||
cable->attr_state.show = cable_state_show;
|
||||
cable->attr_state.store = cable_state_store;
|
||||
}
|
||||
}
|
||||
|
||||
if (edev->max_supported && edev->mutually_exclusive) {
|
||||
char buf[80];
|
||||
char *name;
|
||||
|
||||
/* Count the size of mutually_exclusive array */
|
||||
for (index = 0; edev->mutually_exclusive[index]; index++)
|
||||
;
|
||||
|
||||
edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
|
||||
(index + 1), GFP_KERNEL);
|
||||
if (!edev->attrs_muex) {
|
||||
ret = -ENOMEM;
|
||||
goto err_muex;
|
||||
}
|
||||
|
||||
edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
|
||||
index, GFP_KERNEL);
|
||||
if (!edev->d_attrs_muex) {
|
||||
ret = -ENOMEM;
|
||||
kfree(edev->attrs_muex);
|
||||
goto err_muex;
|
||||
}
|
||||
|
||||
for (index = 0; edev->mutually_exclusive[index]; index++) {
|
||||
sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
|
||||
name = kzalloc(sizeof(char) * (strlen(buf) + 1),
|
||||
GFP_KERNEL);
|
||||
if (!name) {
|
||||
for (index--; index >= 0; index--) {
|
||||
kfree(edev->d_attrs_muex[index].attr.
|
||||
name);
|
||||
}
|
||||
kfree(edev->d_attrs_muex);
|
||||
kfree(edev->attrs_muex);
|
||||
ret = -ENOMEM;
|
||||
goto err_muex;
|
||||
}
|
||||
strcpy(name, buf);
|
||||
edev->d_attrs_muex[index].attr.name = name;
|
||||
edev->d_attrs_muex[index].attr.mode = 0000;
|
||||
edev->attrs_muex[index] = &edev->d_attrs_muex[index]
|
||||
.attr;
|
||||
}
|
||||
edev->attr_g_muex.name = muex_name;
|
||||
edev->attr_g_muex.attrs = edev->attrs_muex;
|
||||
|
||||
}
|
||||
|
||||
if (edev->max_supported) {
|
||||
edev->extcon_dev_type.groups =
|
||||
kzalloc(sizeof(struct attribute_group *) *
|
||||
(edev->max_supported + 2), GFP_KERNEL);
|
||||
if (!edev->extcon_dev_type.groups) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc_groups;
|
||||
}
|
||||
|
||||
edev->extcon_dev_type.name = dev_name(edev->dev);
|
||||
edev->extcon_dev_type.release = dummy_sysfs_dev_release;
|
||||
|
||||
for (index = 0; index < edev->max_supported; index++)
|
||||
edev->extcon_dev_type.groups[index] =
|
||||
&edev->cables[index].attr_g;
|
||||
if (edev->mutually_exclusive)
|
||||
edev->extcon_dev_type.groups[index] =
|
||||
&edev->attr_g_muex;
|
||||
|
||||
edev->dev->type = &edev->extcon_dev_type;
|
||||
}
|
||||
|
||||
ret = device_register(edev->dev);
|
||||
if (ret) {
|
||||
put_device(edev->dev);
|
||||
goto err_dev;
|
||||
}
|
||||
#if defined(CONFIG_ANDROID)
|
||||
if (switch_class)
|
||||
ret = class_compat_create_link(switch_class, edev->dev,
|
||||
dev);
|
||||
#endif /* CONFIG_ANDROID */
|
||||
|
||||
spin_lock_init(&edev->lock);
|
||||
|
||||
RAW_INIT_NOTIFIER_HEAD(&edev->nh);
|
||||
|
||||
dev_set_drvdata(edev->dev, edev);
|
||||
edev->state = 0;
|
||||
|
||||
mutex_lock(&extcon_dev_list_lock);
|
||||
list_add(&edev->entry, &extcon_dev_list);
|
||||
mutex_unlock(&extcon_dev_list_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dev:
|
||||
if (edev->max_supported)
|
||||
kfree(edev->extcon_dev_type.groups);
|
||||
err_alloc_groups:
|
||||
if (edev->max_supported && edev->mutually_exclusive) {
|
||||
for (index = 0; edev->mutually_exclusive[index]; index++)
|
||||
kfree(edev->d_attrs_muex[index].attr.name);
|
||||
kfree(edev->d_attrs_muex);
|
||||
kfree(edev->attrs_muex);
|
||||
}
|
||||
err_muex:
|
||||
for (index = 0; index < edev->max_supported; index++)
|
||||
kfree(edev->cables[index].attr_g.name);
|
||||
err_alloc_cables:
|
||||
if (edev->max_supported)
|
||||
kfree(edev->cables);
|
||||
err_sysfs_alloc:
|
||||
kfree(edev->dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_dev_register);
|
||||
|
||||
/**
|
||||
* extcon_dev_unregister() - Unregister the extcon device.
|
||||
* @edev: the extcon device instance to be unregitered.
|
||||
*
|
||||
* Note that this does not call kfree(edev) because edev was not allocated
|
||||
* by this class.
|
||||
*/
|
||||
void extcon_dev_unregister(struct extcon_dev *edev)
|
||||
{
|
||||
extcon_cleanup(edev, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
|
||||
|
||||
static int __init extcon_class_init(void)
|
||||
{
|
||||
return create_extcon_class();
|
||||
}
|
||||
module_init(extcon_class_init);
|
||||
|
||||
static void __exit extcon_class_exit(void)
|
||||
{
|
||||
class_destroy(extcon_class);
|
||||
}
|
||||
module_exit(extcon_class_exit);
|
||||
|
||||
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
||||
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
|
||||
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
|
||||
MODULE_DESCRIPTION("External connector (extcon) class driver");
|
||||
MODULE_LICENSE("GPL");
|
169
drivers/extcon/extcon_gpio.c
Normal file
169
drivers/extcon/extcon_gpio.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* drivers/extcon/extcon_gpio.c
|
||||
*
|
||||
* Single-state GPIO extcon driver based on extcon class
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Author: Mike Lockwood <lockwood@android.com>
|
||||
*
|
||||
* Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
|
||||
* (originally switch class is supported)
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/extcon/extcon_gpio.h>
|
||||
|
||||
struct gpio_extcon_data {
|
||||
struct extcon_dev edev;
|
||||
unsigned gpio;
|
||||
const char *state_on;
|
||||
const char *state_off;
|
||||
int irq;
|
||||
struct delayed_work work;
|
||||
unsigned long debounce_jiffies;
|
||||
};
|
||||
|
||||
static void gpio_extcon_work(struct work_struct *work)
|
||||
{
|
||||
int state;
|
||||
struct gpio_extcon_data *data =
|
||||
container_of(to_delayed_work(work), struct gpio_extcon_data,
|
||||
work);
|
||||
|
||||
state = gpio_get_value(data->gpio);
|
||||
extcon_set_state(&data->edev, state);
|
||||
}
|
||||
|
||||
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct gpio_extcon_data *extcon_data = dev_id;
|
||||
|
||||
schedule_delayed_work(&extcon_data->work,
|
||||
extcon_data->debounce_jiffies);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
|
||||
{
|
||||
struct gpio_extcon_data *extcon_data =
|
||||
container_of(edev, struct gpio_extcon_data, edev);
|
||||
const char *state;
|
||||
if (extcon_get_state(edev))
|
||||
state = extcon_data->state_on;
|
||||
else
|
||||
state = extcon_data->state_off;
|
||||
|
||||
if (state)
|
||||
return sprintf(buf, "%s\n", state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __devinit gpio_extcon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_extcon_data *extcon_data;
|
||||
int ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
return -EBUSY;
|
||||
if (!pdata->irq_flags) {
|
||||
dev_err(&pdev->dev, "IRQ flag is not specified.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
|
||||
GFP_KERNEL);
|
||||
if (!extcon_data)
|
||||
return -ENOMEM;
|
||||
|
||||
extcon_data->edev.name = pdata->name;
|
||||
extcon_data->gpio = pdata->gpio;
|
||||
extcon_data->state_on = pdata->state_on;
|
||||
extcon_data->state_off = pdata->state_off;
|
||||
if (pdata->state_on && pdata->state_off)
|
||||
extcon_data->edev.print_state = extcon_gpio_print_state;
|
||||
extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
|
||||
|
||||
ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
|
||||
if (ret < 0)
|
||||
goto err_extcon_dev_register;
|
||||
|
||||
ret = gpio_request_one(extcon_data->gpio, GPIOF_DIR_IN, pdev->name);
|
||||
if (ret < 0)
|
||||
goto err_request_gpio;
|
||||
|
||||
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
|
||||
|
||||
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
|
||||
if (extcon_data->irq < 0) {
|
||||
ret = extcon_data->irq;
|
||||
goto err_detect_irq_num_failed;
|
||||
}
|
||||
|
||||
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
|
||||
pdata->irq_flags, pdev->name,
|
||||
extcon_data);
|
||||
if (ret < 0)
|
||||
goto err_request_irq;
|
||||
|
||||
/* Perform initial detection */
|
||||
gpio_extcon_work(&extcon_data->work.work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_irq:
|
||||
err_detect_irq_num_failed:
|
||||
gpio_free(extcon_data->gpio);
|
||||
err_request_gpio:
|
||||
extcon_dev_unregister(&extcon_data->edev);
|
||||
err_extcon_dev_register:
|
||||
devm_kfree(&pdev->dev, extcon_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit gpio_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&extcon_data->work);
|
||||
gpio_free(extcon_data->gpio);
|
||||
extcon_dev_unregister(&extcon_data->edev);
|
||||
devm_kfree(&pdev->dev, extcon_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver gpio_extcon_driver = {
|
||||
.probe = gpio_extcon_probe,
|
||||
.remove = __devexit_p(gpio_extcon_remove),
|
||||
.driver = {
|
||||
.name = "extcon-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(gpio_extcon_driver);
|
||||
|
||||
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
||||
MODULE_DESCRIPTION("GPIO extcon driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -83,8 +83,7 @@ EXPORT_SYMBOL(devm_gpio_request);
|
||||
void devm_gpio_free(struct device *dev, unsigned int gpio)
|
||||
{
|
||||
|
||||
WARN_ON(devres_destroy(dev, devm_gpio_release, devm_gpio_match,
|
||||
WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match,
|
||||
&gpio));
|
||||
gpio_free(gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(devm_gpio_free);
|
||||
|
@ -46,40 +46,61 @@ struct vmbus_channel_message_table_entry {
|
||||
*
|
||||
* @icmsghdrp is of type &struct icmsg_hdr.
|
||||
* @negop is of type &struct icmsg_negotiate.
|
||||
* Set up and fill in default negotiate response message. This response can
|
||||
* come from both the vmbus driver and the hv_utils driver. The current api
|
||||
* will respond properly to both Windows 2008 and Windows 2008-R2 operating
|
||||
* systems.
|
||||
* Set up and fill in default negotiate response message.
|
||||
*
|
||||
* The max_fw_version specifies the maximum framework version that
|
||||
* we can support and max _srv_version specifies the maximum service
|
||||
* version we can support. A special value MAX_SRV_VER can be
|
||||
* specified to indicate that we can handle the maximum version
|
||||
* exposed by the host.
|
||||
*
|
||||
* Mainly used by Hyper-V drivers.
|
||||
*/
|
||||
void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
|
||||
struct icmsg_negotiate *negop, u8 *buf)
|
||||
struct icmsg_negotiate *negop, u8 *buf,
|
||||
int max_fw_version, int max_srv_version)
|
||||
{
|
||||
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
icmsghdrp->icmsgsize = 0x10;
|
||||
int icframe_vercnt;
|
||||
int icmsg_vercnt;
|
||||
int i;
|
||||
|
||||
negop = (struct icmsg_negotiate *)&buf[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
icmsghdrp->icmsgsize = 0x10;
|
||||
|
||||
if (negop->icframe_vercnt == 2 &&
|
||||
negop->icversion_data[1].major == 3) {
|
||||
negop->icversion_data[0].major = 3;
|
||||
negop->icversion_data[0].minor = 0;
|
||||
negop->icversion_data[1].major = 3;
|
||||
negop->icversion_data[1].minor = 0;
|
||||
} else {
|
||||
negop->icversion_data[0].major = 1;
|
||||
negop->icversion_data[0].minor = 0;
|
||||
negop->icversion_data[1].major = 1;
|
||||
negop->icversion_data[1].minor = 0;
|
||||
}
|
||||
negop = (struct icmsg_negotiate *)&buf[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
|
||||
negop->icframe_vercnt = 1;
|
||||
negop->icmsg_vercnt = 1;
|
||||
icframe_vercnt = negop->icframe_vercnt;
|
||||
icmsg_vercnt = negop->icmsg_vercnt;
|
||||
|
||||
/*
|
||||
* Select the framework version number we will
|
||||
* support.
|
||||
*/
|
||||
|
||||
for (i = 0; i < negop->icframe_vercnt; i++) {
|
||||
if (negop->icversion_data[i].major <= max_fw_version)
|
||||
icframe_vercnt = negop->icversion_data[i].major;
|
||||
}
|
||||
|
||||
for (i = negop->icframe_vercnt;
|
||||
(i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) {
|
||||
if (negop->icversion_data[i].major <= max_srv_version)
|
||||
icmsg_vercnt = negop->icversion_data[i].major;
|
||||
}
|
||||
|
||||
/*
|
||||
* Respond with the maximum framework and service
|
||||
* version numbers we can support.
|
||||
*/
|
||||
negop->icframe_vercnt = 1;
|
||||
negop->icmsg_vercnt = 1;
|
||||
negop->icversion_data[0].major = icframe_vercnt;
|
||||
negop->icversion_data[0].minor = 0;
|
||||
negop->icversion_data[1].major = icmsg_vercnt;
|
||||
negop->icversion_data[1].minor = 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
|
||||
|
||||
/*
|
||||
|
@ -252,7 +252,7 @@ void hv_cleanup(void)
|
||||
*
|
||||
* This involves a hypercall.
|
||||
*/
|
||||
u16 hv_post_message(union hv_connection_id connection_id,
|
||||
int hv_post_message(union hv_connection_id connection_id,
|
||||
enum hv_message_type message_type,
|
||||
void *payload, size_t payload_size)
|
||||
{
|
||||
|
@ -394,7 +394,8 @@ void hv_kvp_onchannelcallback(void *context)
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, negop, recv_buffer);
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, negop,
|
||||
recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
|
||||
} else {
|
||||
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
|
@ -70,7 +70,8 @@ static void shutdown_onchannelcallback(void *context)
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf);
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, negop,
|
||||
shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
|
||||
} else {
|
||||
shutdown_msg =
|
||||
(struct shutdown_msg_data *)&shut_txf_buf[
|
||||
@ -195,7 +196,8 @@ static void timesync_onchannelcallback(void *context)
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf);
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf,
|
||||
MAX_SRV_VER, MAX_SRV_VER);
|
||||
} else {
|
||||
timedatap = (struct ictimesync_data *)&time_txf_buf[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
@ -234,7 +236,8 @@ static void heartbeat_onchannelcallback(void *context)
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf);
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, NULL,
|
||||
hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
|
||||
} else {
|
||||
heartbeat_msg =
|
||||
(struct heartbeat_msg_data *)&hbeat_txf_buf[
|
||||
|
@ -495,7 +495,7 @@ extern int hv_init(void);
|
||||
|
||||
extern void hv_cleanup(void);
|
||||
|
||||
extern u16 hv_post_message(union hv_connection_id connection_id,
|
||||
extern int hv_post_message(union hv_connection_id connection_id,
|
||||
enum hv_message_type message_type,
|
||||
void *payload, size_t payload_size);
|
||||
|
||||
|
43
drivers/memory/Kconfig
Normal file
43
drivers/memory/Kconfig
Normal file
@ -0,0 +1,43 @@
|
||||
#
|
||||
# Memory devices
|
||||
#
|
||||
|
||||
menuconfig MEMORY
|
||||
bool "Memory Controller drivers"
|
||||
|
||||
if MEMORY
|
||||
|
||||
config TI_EMIF
|
||||
tristate "Texas Instruments EMIF driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
select DDR
|
||||
help
|
||||
This driver is for the EMIF module available in Texas Instruments
|
||||
SoCs. EMIF is an SDRAM controller that, based on its revision,
|
||||
supports one or more of DDR2, DDR3, and LPDDR2 SDRAM protocols.
|
||||
This driver takes care of only LPDDR2 memories presently. The
|
||||
functions of the driver includes re-configuring AC timing
|
||||
parameters and other settings during frequency, voltage and
|
||||
temperature changes
|
||||
|
||||
config TEGRA20_MC
|
||||
bool "Tegra20 Memory Controller(MC) driver"
|
||||
default y
|
||||
depends on ARCH_TEGRA_2x_SOC
|
||||
help
|
||||
This driver is for the Memory Controller(MC) module available
|
||||
in Tegra20 SoCs, mainly for a address translation fault
|
||||
analysis, especially for IOMMU/GART(Graphics Address
|
||||
Relocation Table) module.
|
||||
|
||||
config TEGRA30_MC
|
||||
bool "Tegra30 Memory Controller(MC) driver"
|
||||
default y
|
||||
depends on ARCH_TEGRA_3x_SOC
|
||||
help
|
||||
This driver is for the Memory Controller(MC) module available
|
||||
in Tegra30 SoCs, mainly for a address translation fault
|
||||
analysis, especially for IOMMU/SMMU(System Memory Management
|
||||
Unit) module.
|
||||
|
||||
endif
|
7
drivers/memory/Makefile
Normal file
7
drivers/memory/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for memory devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_TI_EMIF) += emif.o
|
||||
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
|
||||
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
|
1670
drivers/memory/emif.c
Normal file
1670
drivers/memory/emif.c
Normal file
File diff suppressed because it is too large
Load Diff
589
drivers/memory/emif.h
Normal file
589
drivers/memory/emif.h
Normal file
@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Defines for the EMIF driver
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments, Inc.
|
||||
*
|
||||
* Benoit Cousson (b-cousson@ti.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 __EMIF_H
|
||||
#define __EMIF_H
|
||||
|
||||
/*
|
||||
* Maximum number of different frequencies supported by EMIF driver
|
||||
* Determines the number of entries in the pointer array for register
|
||||
* cache
|
||||
*/
|
||||
#define EMIF_MAX_NUM_FREQUENCIES 6
|
||||
|
||||
/* State of the core voltage */
|
||||
#define DDR_VOLTAGE_STABLE 0
|
||||
#define DDR_VOLTAGE_RAMPING 1
|
||||
|
||||
/* Defines for timing De-rating */
|
||||
#define EMIF_NORMAL_TIMINGS 0
|
||||
#define EMIF_DERATED_TIMINGS 1
|
||||
|
||||
/* Length of the forced read idle period in terms of cycles */
|
||||
#define EMIF_READ_IDLE_LEN_VAL 5
|
||||
|
||||
/*
|
||||
* forced read idle interval to be used when voltage
|
||||
* is changed as part of DVFS/DPS - 1ms
|
||||
*/
|
||||
#define READ_IDLE_INTERVAL_DVFS (1*1000000)
|
||||
|
||||
/*
|
||||
* Forced read idle interval to be used when voltage is stable
|
||||
* 50us - or maximum value will do
|
||||
*/
|
||||
#define READ_IDLE_INTERVAL_NORMAL (50*1000000)
|
||||
|
||||
/* DLL calibration interval when voltage is NOT stable - 1us */
|
||||
#define DLL_CALIB_INTERVAL_DVFS (1*1000000)
|
||||
|
||||
#define DLL_CALIB_ACK_WAIT_VAL 5
|
||||
|
||||
/* Interval between ZQCS commands - hw team recommended value */
|
||||
#define EMIF_ZQCS_INTERVAL_US (50*1000)
|
||||
/* Enable ZQ Calibration on exiting Self-refresh */
|
||||
#define ZQ_SFEXITEN_ENABLE 1
|
||||
/*
|
||||
* ZQ Calibration simultaneously on both chip-selects:
|
||||
* Needs one calibration resistor per CS
|
||||
*/
|
||||
#define ZQ_DUALCALEN_DISABLE 0
|
||||
#define ZQ_DUALCALEN_ENABLE 1
|
||||
|
||||
#define T_ZQCS_DEFAULT_NS 90
|
||||
#define T_ZQCL_DEFAULT_NS 360
|
||||
#define T_ZQINIT_DEFAULT_NS 1000
|
||||
|
||||
/* DPD_EN */
|
||||
#define DPD_DISABLE 0
|
||||
#define DPD_ENABLE 1
|
||||
|
||||
/*
|
||||
* Default values for the low-power entry to be used if not provided by user.
|
||||
* OMAP4/5 has a hw bug(i735) due to which this value can not be less than 512
|
||||
* Timeout values are in DDR clock 'cycles' and frequency threshold in Hz
|
||||
*/
|
||||
#define EMIF_LP_MODE_TIMEOUT_PERFORMANCE 2048
|
||||
#define EMIF_LP_MODE_TIMEOUT_POWER 512
|
||||
#define EMIF_LP_MODE_FREQ_THRESHOLD 400000000
|
||||
|
||||
/* DDR_PHY_CTRL_1 values for EMIF4D - ATTILA PHY combination */
|
||||
#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY 0x049FF000
|
||||
#define EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY 0x41
|
||||
#define EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY 0x80
|
||||
#define EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY 0xFF
|
||||
|
||||
/* DDR_PHY_CTRL_1 values for EMIF4D5 INTELLIPHY combination */
|
||||
#define EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY 0x0E084200
|
||||
#define EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS 10000
|
||||
|
||||
/* TEMP_ALERT_CONFIG - corresponding to temp gradient 5 C/s */
|
||||
#define TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS 360
|
||||
|
||||
#define EMIF_T_CSTA 3
|
||||
#define EMIF_T_PDLL_UL 128
|
||||
|
||||
/* External PHY control registers magic values */
|
||||
#define EMIF_EXT_PHY_CTRL_1_VAL 0x04020080
|
||||
#define EMIF_EXT_PHY_CTRL_5_VAL 0x04010040
|
||||
#define EMIF_EXT_PHY_CTRL_6_VAL 0x01004010
|
||||
#define EMIF_EXT_PHY_CTRL_7_VAL 0x00001004
|
||||
#define EMIF_EXT_PHY_CTRL_8_VAL 0x04010040
|
||||
#define EMIF_EXT_PHY_CTRL_9_VAL 0x01004010
|
||||
#define EMIF_EXT_PHY_CTRL_10_VAL 0x00001004
|
||||
#define EMIF_EXT_PHY_CTRL_11_VAL 0x00000000
|
||||
#define EMIF_EXT_PHY_CTRL_12_VAL 0x00000000
|
||||
#define EMIF_EXT_PHY_CTRL_13_VAL 0x00000000
|
||||
#define EMIF_EXT_PHY_CTRL_14_VAL 0x80080080
|
||||
#define EMIF_EXT_PHY_CTRL_15_VAL 0x00800800
|
||||
#define EMIF_EXT_PHY_CTRL_16_VAL 0x08102040
|
||||
#define EMIF_EXT_PHY_CTRL_17_VAL 0x00000001
|
||||
#define EMIF_EXT_PHY_CTRL_18_VAL 0x540A8150
|
||||
#define EMIF_EXT_PHY_CTRL_19_VAL 0xA81502A0
|
||||
#define EMIF_EXT_PHY_CTRL_20_VAL 0x002A0540
|
||||
#define EMIF_EXT_PHY_CTRL_21_VAL 0x00000000
|
||||
#define EMIF_EXT_PHY_CTRL_22_VAL 0x00000000
|
||||
#define EMIF_EXT_PHY_CTRL_23_VAL 0x00000000
|
||||
#define EMIF_EXT_PHY_CTRL_24_VAL 0x00000077
|
||||
|
||||
#define EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS 1200
|
||||
|
||||
/* Registers offset */
|
||||
#define EMIF_MODULE_ID_AND_REVISION 0x0000
|
||||
#define EMIF_STATUS 0x0004
|
||||
#define EMIF_SDRAM_CONFIG 0x0008
|
||||
#define EMIF_SDRAM_CONFIG_2 0x000c
|
||||
#define EMIF_SDRAM_REFRESH_CONTROL 0x0010
|
||||
#define EMIF_SDRAM_REFRESH_CTRL_SHDW 0x0014
|
||||
#define EMIF_SDRAM_TIMING_1 0x0018
|
||||
#define EMIF_SDRAM_TIMING_1_SHDW 0x001c
|
||||
#define EMIF_SDRAM_TIMING_2 0x0020
|
||||
#define EMIF_SDRAM_TIMING_2_SHDW 0x0024
|
||||
#define EMIF_SDRAM_TIMING_3 0x0028
|
||||
#define EMIF_SDRAM_TIMING_3_SHDW 0x002c
|
||||
#define EMIF_LPDDR2_NVM_TIMING 0x0030
|
||||
#define EMIF_LPDDR2_NVM_TIMING_SHDW 0x0034
|
||||
#define EMIF_POWER_MANAGEMENT_CONTROL 0x0038
|
||||
#define EMIF_POWER_MANAGEMENT_CTRL_SHDW 0x003c
|
||||
#define EMIF_LPDDR2_MODE_REG_DATA 0x0040
|
||||
#define EMIF_LPDDR2_MODE_REG_CONFIG 0x0050
|
||||
#define EMIF_OCP_CONFIG 0x0054
|
||||
#define EMIF_OCP_CONFIG_VALUE_1 0x0058
|
||||
#define EMIF_OCP_CONFIG_VALUE_2 0x005c
|
||||
#define EMIF_IODFT_TEST_LOGIC_GLOBAL_CONTROL 0x0060
|
||||
#define EMIF_IODFT_TEST_LOGIC_CTRL_MISR_RESULT 0x0064
|
||||
#define EMIF_IODFT_TEST_LOGIC_ADDRESS_MISR_RESULT 0x0068
|
||||
#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_1 0x006c
|
||||
#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_2 0x0070
|
||||
#define EMIF_IODFT_TEST_LOGIC_DATA_MISR_RESULT_3 0x0074
|
||||
#define EMIF_PERFORMANCE_COUNTER_1 0x0080
|
||||
#define EMIF_PERFORMANCE_COUNTER_2 0x0084
|
||||
#define EMIF_PERFORMANCE_COUNTER_CONFIG 0x0088
|
||||
#define EMIF_PERFORMANCE_COUNTER_MASTER_REGION_SELECT 0x008c
|
||||
#define EMIF_PERFORMANCE_COUNTER_TIME 0x0090
|
||||
#define EMIF_MISC_REG 0x0094
|
||||
#define EMIF_DLL_CALIB_CTRL 0x0098
|
||||
#define EMIF_DLL_CALIB_CTRL_SHDW 0x009c
|
||||
#define EMIF_END_OF_INTERRUPT 0x00a0
|
||||
#define EMIF_SYSTEM_OCP_INTERRUPT_RAW_STATUS 0x00a4
|
||||
#define EMIF_LL_OCP_INTERRUPT_RAW_STATUS 0x00a8
|
||||
#define EMIF_SYSTEM_OCP_INTERRUPT_STATUS 0x00ac
|
||||
#define EMIF_LL_OCP_INTERRUPT_STATUS 0x00b0
|
||||
#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET 0x00b4
|
||||
#define EMIF_LL_OCP_INTERRUPT_ENABLE_SET 0x00b8
|
||||
#define EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR 0x00bc
|
||||
#define EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR 0x00c0
|
||||
#define EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG 0x00c8
|
||||
#define EMIF_TEMPERATURE_ALERT_CONFIG 0x00cc
|
||||
#define EMIF_OCP_ERROR_LOG 0x00d0
|
||||
#define EMIF_READ_WRITE_LEVELING_RAMP_WINDOW 0x00d4
|
||||
#define EMIF_READ_WRITE_LEVELING_RAMP_CONTROL 0x00d8
|
||||
#define EMIF_READ_WRITE_LEVELING_CONTROL 0x00dc
|
||||
#define EMIF_DDR_PHY_CTRL_1 0x00e4
|
||||
#define EMIF_DDR_PHY_CTRL_1_SHDW 0x00e8
|
||||
#define EMIF_DDR_PHY_CTRL_2 0x00ec
|
||||
#define EMIF_PRIORITY_TO_CLASS_OF_SERVICE_MAPPING 0x0100
|
||||
#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_1_MAPPING 0x0104
|
||||
#define EMIF_CONNECTION_ID_TO_CLASS_OF_SERVICE_2_MAPPING 0x0108
|
||||
#define EMIF_READ_WRITE_EXECUTION_THRESHOLD 0x0120
|
||||
#define EMIF_COS_CONFIG 0x0124
|
||||
#define EMIF_PHY_STATUS_1 0x0140
|
||||
#define EMIF_PHY_STATUS_2 0x0144
|
||||
#define EMIF_PHY_STATUS_3 0x0148
|
||||
#define EMIF_PHY_STATUS_4 0x014c
|
||||
#define EMIF_PHY_STATUS_5 0x0150
|
||||
#define EMIF_PHY_STATUS_6 0x0154
|
||||
#define EMIF_PHY_STATUS_7 0x0158
|
||||
#define EMIF_PHY_STATUS_8 0x015c
|
||||
#define EMIF_PHY_STATUS_9 0x0160
|
||||
#define EMIF_PHY_STATUS_10 0x0164
|
||||
#define EMIF_PHY_STATUS_11 0x0168
|
||||
#define EMIF_PHY_STATUS_12 0x016c
|
||||
#define EMIF_PHY_STATUS_13 0x0170
|
||||
#define EMIF_PHY_STATUS_14 0x0174
|
||||
#define EMIF_PHY_STATUS_15 0x0178
|
||||
#define EMIF_PHY_STATUS_16 0x017c
|
||||
#define EMIF_PHY_STATUS_17 0x0180
|
||||
#define EMIF_PHY_STATUS_18 0x0184
|
||||
#define EMIF_PHY_STATUS_19 0x0188
|
||||
#define EMIF_PHY_STATUS_20 0x018c
|
||||
#define EMIF_PHY_STATUS_21 0x0190
|
||||
#define EMIF_EXT_PHY_CTRL_1 0x0200
|
||||
#define EMIF_EXT_PHY_CTRL_1_SHDW 0x0204
|
||||
#define EMIF_EXT_PHY_CTRL_2 0x0208
|
||||
#define EMIF_EXT_PHY_CTRL_2_SHDW 0x020c
|
||||
#define EMIF_EXT_PHY_CTRL_3 0x0210
|
||||
#define EMIF_EXT_PHY_CTRL_3_SHDW 0x0214
|
||||
#define EMIF_EXT_PHY_CTRL_4 0x0218
|
||||
#define EMIF_EXT_PHY_CTRL_4_SHDW 0x021c
|
||||
#define EMIF_EXT_PHY_CTRL_5 0x0220
|
||||
#define EMIF_EXT_PHY_CTRL_5_SHDW 0x0224
|
||||
#define EMIF_EXT_PHY_CTRL_6 0x0228
|
||||
#define EMIF_EXT_PHY_CTRL_6_SHDW 0x022c
|
||||
#define EMIF_EXT_PHY_CTRL_7 0x0230
|
||||
#define EMIF_EXT_PHY_CTRL_7_SHDW 0x0234
|
||||
#define EMIF_EXT_PHY_CTRL_8 0x0238
|
||||
#define EMIF_EXT_PHY_CTRL_8_SHDW 0x023c
|
||||
#define EMIF_EXT_PHY_CTRL_9 0x0240
|
||||
#define EMIF_EXT_PHY_CTRL_9_SHDW 0x0244
|
||||
#define EMIF_EXT_PHY_CTRL_10 0x0248
|
||||
#define EMIF_EXT_PHY_CTRL_10_SHDW 0x024c
|
||||
#define EMIF_EXT_PHY_CTRL_11 0x0250
|
||||
#define EMIF_EXT_PHY_CTRL_11_SHDW 0x0254
|
||||
#define EMIF_EXT_PHY_CTRL_12 0x0258
|
||||
#define EMIF_EXT_PHY_CTRL_12_SHDW 0x025c
|
||||
#define EMIF_EXT_PHY_CTRL_13 0x0260
|
||||
#define EMIF_EXT_PHY_CTRL_13_SHDW 0x0264
|
||||
#define EMIF_EXT_PHY_CTRL_14 0x0268
|
||||
#define EMIF_EXT_PHY_CTRL_14_SHDW 0x026c
|
||||
#define EMIF_EXT_PHY_CTRL_15 0x0270
|
||||
#define EMIF_EXT_PHY_CTRL_15_SHDW 0x0274
|
||||
#define EMIF_EXT_PHY_CTRL_16 0x0278
|
||||
#define EMIF_EXT_PHY_CTRL_16_SHDW 0x027c
|
||||
#define EMIF_EXT_PHY_CTRL_17 0x0280
|
||||
#define EMIF_EXT_PHY_CTRL_17_SHDW 0x0284
|
||||
#define EMIF_EXT_PHY_CTRL_18 0x0288
|
||||
#define EMIF_EXT_PHY_CTRL_18_SHDW 0x028c
|
||||
#define EMIF_EXT_PHY_CTRL_19 0x0290
|
||||
#define EMIF_EXT_PHY_CTRL_19_SHDW 0x0294
|
||||
#define EMIF_EXT_PHY_CTRL_20 0x0298
|
||||
#define EMIF_EXT_PHY_CTRL_20_SHDW 0x029c
|
||||
#define EMIF_EXT_PHY_CTRL_21 0x02a0
|
||||
#define EMIF_EXT_PHY_CTRL_21_SHDW 0x02a4
|
||||
#define EMIF_EXT_PHY_CTRL_22 0x02a8
|
||||
#define EMIF_EXT_PHY_CTRL_22_SHDW 0x02ac
|
||||
#define EMIF_EXT_PHY_CTRL_23 0x02b0
|
||||
#define EMIF_EXT_PHY_CTRL_23_SHDW 0x02b4
|
||||
#define EMIF_EXT_PHY_CTRL_24 0x02b8
|
||||
#define EMIF_EXT_PHY_CTRL_24_SHDW 0x02bc
|
||||
#define EMIF_EXT_PHY_CTRL_25 0x02c0
|
||||
#define EMIF_EXT_PHY_CTRL_25_SHDW 0x02c4
|
||||
#define EMIF_EXT_PHY_CTRL_26 0x02c8
|
||||
#define EMIF_EXT_PHY_CTRL_26_SHDW 0x02cc
|
||||
#define EMIF_EXT_PHY_CTRL_27 0x02d0
|
||||
#define EMIF_EXT_PHY_CTRL_27_SHDW 0x02d4
|
||||
#define EMIF_EXT_PHY_CTRL_28 0x02d8
|
||||
#define EMIF_EXT_PHY_CTRL_28_SHDW 0x02dc
|
||||
#define EMIF_EXT_PHY_CTRL_29 0x02e0
|
||||
#define EMIF_EXT_PHY_CTRL_29_SHDW 0x02e4
|
||||
#define EMIF_EXT_PHY_CTRL_30 0x02e8
|
||||
#define EMIF_EXT_PHY_CTRL_30_SHDW 0x02ec
|
||||
|
||||
/* Registers shifts and masks */
|
||||
|
||||
/* EMIF_MODULE_ID_AND_REVISION */
|
||||
#define SCHEME_SHIFT 30
|
||||
#define SCHEME_MASK (0x3 << 30)
|
||||
#define MODULE_ID_SHIFT 16
|
||||
#define MODULE_ID_MASK (0xfff << 16)
|
||||
#define RTL_VERSION_SHIFT 11
|
||||
#define RTL_VERSION_MASK (0x1f << 11)
|
||||
#define MAJOR_REVISION_SHIFT 8
|
||||
#define MAJOR_REVISION_MASK (0x7 << 8)
|
||||
#define MINOR_REVISION_SHIFT 0
|
||||
#define MINOR_REVISION_MASK (0x3f << 0)
|
||||
|
||||
/* STATUS */
|
||||
#define BE_SHIFT 31
|
||||
#define BE_MASK (1 << 31)
|
||||
#define DUAL_CLK_MODE_SHIFT 30
|
||||
#define DUAL_CLK_MODE_MASK (1 << 30)
|
||||
#define FAST_INIT_SHIFT 29
|
||||
#define FAST_INIT_MASK (1 << 29)
|
||||
#define RDLVLGATETO_SHIFT 6
|
||||
#define RDLVLGATETO_MASK (1 << 6)
|
||||
#define RDLVLTO_SHIFT 5
|
||||
#define RDLVLTO_MASK (1 << 5)
|
||||
#define WRLVLTO_SHIFT 4
|
||||
#define WRLVLTO_MASK (1 << 4)
|
||||
#define PHY_DLL_READY_SHIFT 2
|
||||
#define PHY_DLL_READY_MASK (1 << 2)
|
||||
|
||||
/* SDRAM_CONFIG */
|
||||
#define SDRAM_TYPE_SHIFT 29
|
||||
#define SDRAM_TYPE_MASK (0x7 << 29)
|
||||
#define IBANK_POS_SHIFT 27
|
||||
#define IBANK_POS_MASK (0x3 << 27)
|
||||
#define DDR_TERM_SHIFT 24
|
||||
#define DDR_TERM_MASK (0x7 << 24)
|
||||
#define DDR2_DDQS_SHIFT 23
|
||||
#define DDR2_DDQS_MASK (1 << 23)
|
||||
#define DYN_ODT_SHIFT 21
|
||||
#define DYN_ODT_MASK (0x3 << 21)
|
||||
#define DDR_DISABLE_DLL_SHIFT 20
|
||||
#define DDR_DISABLE_DLL_MASK (1 << 20)
|
||||
#define SDRAM_DRIVE_SHIFT 18
|
||||
#define SDRAM_DRIVE_MASK (0x3 << 18)
|
||||
#define CWL_SHIFT 16
|
||||
#define CWL_MASK (0x3 << 16)
|
||||
#define NARROW_MODE_SHIFT 14
|
||||
#define NARROW_MODE_MASK (0x3 << 14)
|
||||
#define CL_SHIFT 10
|
||||
#define CL_MASK (0xf << 10)
|
||||
#define ROWSIZE_SHIFT 7
|
||||
#define ROWSIZE_MASK (0x7 << 7)
|
||||
#define IBANK_SHIFT 4
|
||||
#define IBANK_MASK (0x7 << 4)
|
||||
#define EBANK_SHIFT 3
|
||||
#define EBANK_MASK (1 << 3)
|
||||
#define PAGESIZE_SHIFT 0
|
||||
#define PAGESIZE_MASK (0x7 << 0)
|
||||
|
||||
/* SDRAM_CONFIG_2 */
|
||||
#define CS1NVMEN_SHIFT 30
|
||||
#define CS1NVMEN_MASK (1 << 30)
|
||||
#define EBANK_POS_SHIFT 27
|
||||
#define EBANK_POS_MASK (1 << 27)
|
||||
#define RDBNUM_SHIFT 4
|
||||
#define RDBNUM_MASK (0x3 << 4)
|
||||
#define RDBSIZE_SHIFT 0
|
||||
#define RDBSIZE_MASK (0x7 << 0)
|
||||
|
||||
/* SDRAM_REFRESH_CONTROL */
|
||||
#define INITREF_DIS_SHIFT 31
|
||||
#define INITREF_DIS_MASK (1 << 31)
|
||||
#define SRT_SHIFT 29
|
||||
#define SRT_MASK (1 << 29)
|
||||
#define ASR_SHIFT 28
|
||||
#define ASR_MASK (1 << 28)
|
||||
#define PASR_SHIFT 24
|
||||
#define PASR_MASK (0x7 << 24)
|
||||
#define REFRESH_RATE_SHIFT 0
|
||||
#define REFRESH_RATE_MASK (0xffff << 0)
|
||||
|
||||
/* SDRAM_TIMING_1 */
|
||||
#define T_RTW_SHIFT 29
|
||||
#define T_RTW_MASK (0x7 << 29)
|
||||
#define T_RP_SHIFT 25
|
||||
#define T_RP_MASK (0xf << 25)
|
||||
#define T_RCD_SHIFT 21
|
||||
#define T_RCD_MASK (0xf << 21)
|
||||
#define T_WR_SHIFT 17
|
||||
#define T_WR_MASK (0xf << 17)
|
||||
#define T_RAS_SHIFT 12
|
||||
#define T_RAS_MASK (0x1f << 12)
|
||||
#define T_RC_SHIFT 6
|
||||
#define T_RC_MASK (0x3f << 6)
|
||||
#define T_RRD_SHIFT 3
|
||||
#define T_RRD_MASK (0x7 << 3)
|
||||
#define T_WTR_SHIFT 0
|
||||
#define T_WTR_MASK (0x7 << 0)
|
||||
|
||||
/* SDRAM_TIMING_2 */
|
||||
#define T_XP_SHIFT 28
|
||||
#define T_XP_MASK (0x7 << 28)
|
||||
#define T_ODT_SHIFT 25
|
||||
#define T_ODT_MASK (0x7 << 25)
|
||||
#define T_XSNR_SHIFT 16
|
||||
#define T_XSNR_MASK (0x1ff << 16)
|
||||
#define T_XSRD_SHIFT 6
|
||||
#define T_XSRD_MASK (0x3ff << 6)
|
||||
#define T_RTP_SHIFT 3
|
||||
#define T_RTP_MASK (0x7 << 3)
|
||||
#define T_CKE_SHIFT 0
|
||||
#define T_CKE_MASK (0x7 << 0)
|
||||
|
||||
/* SDRAM_TIMING_3 */
|
||||
#define T_PDLL_UL_SHIFT 28
|
||||
#define T_PDLL_UL_MASK (0xf << 28)
|
||||
#define T_CSTA_SHIFT 24
|
||||
#define T_CSTA_MASK (0xf << 24)
|
||||
#define T_CKESR_SHIFT 21
|
||||
#define T_CKESR_MASK (0x7 << 21)
|
||||
#define ZQ_ZQCS_SHIFT 15
|
||||
#define ZQ_ZQCS_MASK (0x3f << 15)
|
||||
#define T_TDQSCKMAX_SHIFT 13
|
||||
#define T_TDQSCKMAX_MASK (0x3 << 13)
|
||||
#define T_RFC_SHIFT 4
|
||||
#define T_RFC_MASK (0x1ff << 4)
|
||||
#define T_RAS_MAX_SHIFT 0
|
||||
#define T_RAS_MAX_MASK (0xf << 0)
|
||||
|
||||
/* POWER_MANAGEMENT_CONTROL */
|
||||
#define PD_TIM_SHIFT 12
|
||||
#define PD_TIM_MASK (0xf << 12)
|
||||
#define DPD_EN_SHIFT 11
|
||||
#define DPD_EN_MASK (1 << 11)
|
||||
#define LP_MODE_SHIFT 8
|
||||
#define LP_MODE_MASK (0x7 << 8)
|
||||
#define SR_TIM_SHIFT 4
|
||||
#define SR_TIM_MASK (0xf << 4)
|
||||
#define CS_TIM_SHIFT 0
|
||||
#define CS_TIM_MASK (0xf << 0)
|
||||
|
||||
/* LPDDR2_MODE_REG_DATA */
|
||||
#define VALUE_0_SHIFT 0
|
||||
#define VALUE_0_MASK (0x7f << 0)
|
||||
|
||||
/* LPDDR2_MODE_REG_CONFIG */
|
||||
#define CS_SHIFT 31
|
||||
#define CS_MASK (1 << 31)
|
||||
#define REFRESH_EN_SHIFT 30
|
||||
#define REFRESH_EN_MASK (1 << 30)
|
||||
#define ADDRESS_SHIFT 0
|
||||
#define ADDRESS_MASK (0xff << 0)
|
||||
|
||||
/* OCP_CONFIG */
|
||||
#define SYS_THRESH_MAX_SHIFT 24
|
||||
#define SYS_THRESH_MAX_MASK (0xf << 24)
|
||||
#define MPU_THRESH_MAX_SHIFT 20
|
||||
#define MPU_THRESH_MAX_MASK (0xf << 20)
|
||||
#define LL_THRESH_MAX_SHIFT 16
|
||||
#define LL_THRESH_MAX_MASK (0xf << 16)
|
||||
|
||||
/* PERFORMANCE_COUNTER_1 */
|
||||
#define COUNTER1_SHIFT 0
|
||||
#define COUNTER1_MASK (0xffffffff << 0)
|
||||
|
||||
/* PERFORMANCE_COUNTER_2 */
|
||||
#define COUNTER2_SHIFT 0
|
||||
#define COUNTER2_MASK (0xffffffff << 0)
|
||||
|
||||
/* PERFORMANCE_COUNTER_CONFIG */
|
||||
#define CNTR2_MCONNID_EN_SHIFT 31
|
||||
#define CNTR2_MCONNID_EN_MASK (1 << 31)
|
||||
#define CNTR2_REGION_EN_SHIFT 30
|
||||
#define CNTR2_REGION_EN_MASK (1 << 30)
|
||||
#define CNTR2_CFG_SHIFT 16
|
||||
#define CNTR2_CFG_MASK (0xf << 16)
|
||||
#define CNTR1_MCONNID_EN_SHIFT 15
|
||||
#define CNTR1_MCONNID_EN_MASK (1 << 15)
|
||||
#define CNTR1_REGION_EN_SHIFT 14
|
||||
#define CNTR1_REGION_EN_MASK (1 << 14)
|
||||
#define CNTR1_CFG_SHIFT 0
|
||||
#define CNTR1_CFG_MASK (0xf << 0)
|
||||
|
||||
/* PERFORMANCE_COUNTER_MASTER_REGION_SELECT */
|
||||
#define MCONNID2_SHIFT 24
|
||||
#define MCONNID2_MASK (0xff << 24)
|
||||
#define REGION_SEL2_SHIFT 16
|
||||
#define REGION_SEL2_MASK (0x3 << 16)
|
||||
#define MCONNID1_SHIFT 8
|
||||
#define MCONNID1_MASK (0xff << 8)
|
||||
#define REGION_SEL1_SHIFT 0
|
||||
#define REGION_SEL1_MASK (0x3 << 0)
|
||||
|
||||
/* PERFORMANCE_COUNTER_TIME */
|
||||
#define TOTAL_TIME_SHIFT 0
|
||||
#define TOTAL_TIME_MASK (0xffffffff << 0)
|
||||
|
||||
/* DLL_CALIB_CTRL */
|
||||
#define ACK_WAIT_SHIFT 16
|
||||
#define ACK_WAIT_MASK (0xf << 16)
|
||||
#define DLL_CALIB_INTERVAL_SHIFT 0
|
||||
#define DLL_CALIB_INTERVAL_MASK (0x1ff << 0)
|
||||
|
||||
/* END_OF_INTERRUPT */
|
||||
#define EOI_SHIFT 0
|
||||
#define EOI_MASK (1 << 0)
|
||||
|
||||
/* SYSTEM_OCP_INTERRUPT_RAW_STATUS */
|
||||
#define DNV_SYS_SHIFT 2
|
||||
#define DNV_SYS_MASK (1 << 2)
|
||||
#define TA_SYS_SHIFT 1
|
||||
#define TA_SYS_MASK (1 << 1)
|
||||
#define ERR_SYS_SHIFT 0
|
||||
#define ERR_SYS_MASK (1 << 0)
|
||||
|
||||
/* LOW_LATENCY_OCP_INTERRUPT_RAW_STATUS */
|
||||
#define DNV_LL_SHIFT 2
|
||||
#define DNV_LL_MASK (1 << 2)
|
||||
#define TA_LL_SHIFT 1
|
||||
#define TA_LL_MASK (1 << 1)
|
||||
#define ERR_LL_SHIFT 0
|
||||
#define ERR_LL_MASK (1 << 0)
|
||||
|
||||
/* SYSTEM_OCP_INTERRUPT_ENABLE_SET */
|
||||
#define EN_DNV_SYS_SHIFT 2
|
||||
#define EN_DNV_SYS_MASK (1 << 2)
|
||||
#define EN_TA_SYS_SHIFT 1
|
||||
#define EN_TA_SYS_MASK (1 << 1)
|
||||
#define EN_ERR_SYS_SHIFT 0
|
||||
#define EN_ERR_SYS_MASK (1 << 0)
|
||||
|
||||
/* LOW_LATENCY_OCP_INTERRUPT_ENABLE_SET */
|
||||
#define EN_DNV_LL_SHIFT 2
|
||||
#define EN_DNV_LL_MASK (1 << 2)
|
||||
#define EN_TA_LL_SHIFT 1
|
||||
#define EN_TA_LL_MASK (1 << 1)
|
||||
#define EN_ERR_LL_SHIFT 0
|
||||
#define EN_ERR_LL_MASK (1 << 0)
|
||||
|
||||
/* SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG */
|
||||
#define ZQ_CS1EN_SHIFT 31
|
||||
#define ZQ_CS1EN_MASK (1 << 31)
|
||||
#define ZQ_CS0EN_SHIFT 30
|
||||
#define ZQ_CS0EN_MASK (1 << 30)
|
||||
#define ZQ_DUALCALEN_SHIFT 29
|
||||
#define ZQ_DUALCALEN_MASK (1 << 29)
|
||||
#define ZQ_SFEXITEN_SHIFT 28
|
||||
#define ZQ_SFEXITEN_MASK (1 << 28)
|
||||
#define ZQ_ZQINIT_MULT_SHIFT 18
|
||||
#define ZQ_ZQINIT_MULT_MASK (0x3 << 18)
|
||||
#define ZQ_ZQCL_MULT_SHIFT 16
|
||||
#define ZQ_ZQCL_MULT_MASK (0x3 << 16)
|
||||
#define ZQ_REFINTERVAL_SHIFT 0
|
||||
#define ZQ_REFINTERVAL_MASK (0xffff << 0)
|
||||
|
||||
/* TEMPERATURE_ALERT_CONFIG */
|
||||
#define TA_CS1EN_SHIFT 31
|
||||
#define TA_CS1EN_MASK (1 << 31)
|
||||
#define TA_CS0EN_SHIFT 30
|
||||
#define TA_CS0EN_MASK (1 << 30)
|
||||
#define TA_SFEXITEN_SHIFT 28
|
||||
#define TA_SFEXITEN_MASK (1 << 28)
|
||||
#define TA_DEVWDT_SHIFT 26
|
||||
#define TA_DEVWDT_MASK (0x3 << 26)
|
||||
#define TA_DEVCNT_SHIFT 24
|
||||
#define TA_DEVCNT_MASK (0x3 << 24)
|
||||
#define TA_REFINTERVAL_SHIFT 0
|
||||
#define TA_REFINTERVAL_MASK (0x3fffff << 0)
|
||||
|
||||
/* OCP_ERROR_LOG */
|
||||
#define MADDRSPACE_SHIFT 14
|
||||
#define MADDRSPACE_MASK (0x3 << 14)
|
||||
#define MBURSTSEQ_SHIFT 11
|
||||
#define MBURSTSEQ_MASK (0x7 << 11)
|
||||
#define MCMD_SHIFT 8
|
||||
#define MCMD_MASK (0x7 << 8)
|
||||
#define MCONNID_SHIFT 0
|
||||
#define MCONNID_MASK (0xff << 0)
|
||||
|
||||
/* DDR_PHY_CTRL_1 - EMIF4D */
|
||||
#define DLL_SLAVE_DLY_CTRL_SHIFT_4D 4
|
||||
#define DLL_SLAVE_DLY_CTRL_MASK_4D (0xFF << 4)
|
||||
#define READ_LATENCY_SHIFT_4D 0
|
||||
#define READ_LATENCY_MASK_4D (0xf << 0)
|
||||
|
||||
/* DDR_PHY_CTRL_1 - EMIF4D5 */
|
||||
#define DLL_HALF_DELAY_SHIFT_4D5 21
|
||||
#define DLL_HALF_DELAY_MASK_4D5 (1 << 21)
|
||||
#define READ_LATENCY_SHIFT_4D5 0
|
||||
#define READ_LATENCY_MASK_4D5 (0x1f << 0)
|
||||
|
||||
/* DDR_PHY_CTRL_1_SHDW */
|
||||
#define DDR_PHY_CTRL_1_SHDW_SHIFT 5
|
||||
#define DDR_PHY_CTRL_1_SHDW_MASK (0x7ffffff << 5)
|
||||
#define READ_LATENCY_SHDW_SHIFT 0
|
||||
#define READ_LATENCY_SHDW_MASK (0x1f << 0)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* Structure containing shadow of important registers in EMIF
|
||||
* The calculation function fills in this structure to be later used for
|
||||
* initialisation and DVFS
|
||||
*/
|
||||
struct emif_regs {
|
||||
u32 freq;
|
||||
u32 ref_ctrl_shdw;
|
||||
u32 ref_ctrl_shdw_derated;
|
||||
u32 sdram_tim1_shdw;
|
||||
u32 sdram_tim1_shdw_derated;
|
||||
u32 sdram_tim2_shdw;
|
||||
u32 sdram_tim3_shdw;
|
||||
u32 sdram_tim3_shdw_derated;
|
||||
u32 pwr_mgmt_ctrl_shdw;
|
||||
union {
|
||||
u32 read_idle_ctrl_shdw_normal;
|
||||
u32 dll_calib_ctrl_shdw_normal;
|
||||
};
|
||||
union {
|
||||
u32 read_idle_ctrl_shdw_volt_ramp;
|
||||
u32 dll_calib_ctrl_shdw_volt_ramp;
|
||||
};
|
||||
|
||||
u32 phy_ctrl_1_shdw;
|
||||
u32 ext_phy_ctrl_2_shdw;
|
||||
u32 ext_phy_ctrl_3_shdw;
|
||||
u32 ext_phy_ctrl_4_shdw;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __EMIF_H */
|
257
drivers/memory/tegra20-mc.c
Normal file
257
drivers/memory/tegra20-mc.c
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Tegra20 Memory Controller
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "tegra20-mc"
|
||||
|
||||
#define MC_INTSTATUS 0x0
|
||||
#define MC_INTMASK 0x4
|
||||
|
||||
#define MC_INT_ERR_SHIFT 6
|
||||
#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_INVALID_GART_PAGE BIT(MC_INT_ERR_SHIFT + 1)
|
||||
#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
|
||||
#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
|
||||
|
||||
#define MC_GART_ERROR_REQ 0x30
|
||||
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
|
||||
#define MC_SECURITY_VIOLATION_STATUS 0x74
|
||||
|
||||
#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
|
||||
|
||||
#define MC_CLIENT_ID_MASK 0x3f
|
||||
|
||||
#define NUM_MC_REG_BANKS 2
|
||||
|
||||
struct tegra20_mc {
|
||||
void __iomem *regs[NUM_MC_REG_BANKS];
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (offs < 0x24)
|
||||
val = readl(mc->regs[0] + offs);
|
||||
if (offs < 0x400)
|
||||
val = readl(mc->regs[1] + offs - 0x3c);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
|
||||
{
|
||||
if (offs < 0x24) {
|
||||
writel(val, mc->regs[0] + offs);
|
||||
return;
|
||||
}
|
||||
if (offs < 0x400) {
|
||||
writel(val, mc->regs[1] + offs - 0x3c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const tegra20_mc_client[] = {
|
||||
"cbr_display0a",
|
||||
"cbr_display0ab",
|
||||
"cbr_display0b",
|
||||
"cbr_display0bb",
|
||||
"cbr_display0c",
|
||||
"cbr_display0cb",
|
||||
"cbr_display1b",
|
||||
"cbr_display1bb",
|
||||
"cbr_eppup",
|
||||
"cbr_g2pr",
|
||||
"cbr_g2sr",
|
||||
"cbr_mpeunifbr",
|
||||
"cbr_viruv",
|
||||
"csr_avpcarm7r",
|
||||
"csr_displayhc",
|
||||
"csr_displayhcb",
|
||||
"csr_fdcdrd",
|
||||
"csr_g2dr",
|
||||
"csr_host1xdmar",
|
||||
"csr_host1xr",
|
||||
"csr_idxsrd",
|
||||
"csr_mpcorer",
|
||||
"csr_mpe_ipred",
|
||||
"csr_mpeamemrd",
|
||||
"csr_mpecsrd",
|
||||
"csr_ppcsahbdmar",
|
||||
"csr_ppcsahbslvr",
|
||||
"csr_texsrd",
|
||||
"csr_vdebsevr",
|
||||
"csr_vdember",
|
||||
"csr_vdemcer",
|
||||
"csr_vdetper",
|
||||
"cbw_eppu",
|
||||
"cbw_eppv",
|
||||
"cbw_eppy",
|
||||
"cbw_mpeunifbw",
|
||||
"cbw_viwsb",
|
||||
"cbw_viwu",
|
||||
"cbw_viwv",
|
||||
"cbw_viwy",
|
||||
"ccw_g2dw",
|
||||
"csw_avpcarm7w",
|
||||
"csw_fdcdwr",
|
||||
"csw_host1xw",
|
||||
"csw_ispw",
|
||||
"csw_mpcorew",
|
||||
"csw_mpecswr",
|
||||
"csw_ppcsahbdmaw",
|
||||
"csw_ppcsahbslvw",
|
||||
"csw_vdebsevw",
|
||||
"csw_vdembew",
|
||||
"csw_vdetpmw",
|
||||
};
|
||||
|
||||
static void tegra20_mc_decode(struct tegra20_mc *mc, int n)
|
||||
{
|
||||
u32 addr, req;
|
||||
const char *client = "Unknown";
|
||||
int idx, cid;
|
||||
const struct reg_info {
|
||||
u32 offset;
|
||||
u32 write_bit; /* 0=READ, 1=WRITE */
|
||||
int cid_shift;
|
||||
char *message;
|
||||
} reg[] = {
|
||||
{
|
||||
.offset = MC_DECERR_EMEM_OTHERS_STATUS,
|
||||
.write_bit = 31,
|
||||
.message = "MC_DECERR",
|
||||
},
|
||||
{
|
||||
.offset = MC_GART_ERROR_REQ,
|
||||
.cid_shift = 1,
|
||||
.message = "MC_GART_ERR",
|
||||
|
||||
},
|
||||
{
|
||||
.offset = MC_SECURITY_VIOLATION_STATUS,
|
||||
.write_bit = 31,
|
||||
.message = "MC_SECURITY_ERR",
|
||||
},
|
||||
};
|
||||
|
||||
idx = n - MC_INT_ERR_SHIFT;
|
||||
if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) {
|
||||
dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
|
||||
BIT(n));
|
||||
return;
|
||||
}
|
||||
|
||||
req = mc_readl(mc, reg[idx].offset);
|
||||
cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK;
|
||||
if (cid < ARRAY_SIZE(tegra20_mc_client))
|
||||
client = tegra20_mc_client[cid];
|
||||
|
||||
addr = mc_readl(mc, reg[idx].offset + sizeof(u32));
|
||||
|
||||
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s)\n",
|
||||
reg[idx].message, req, addr, client,
|
||||
(req & BIT(reg[idx].write_bit)) ? "write" : "read",
|
||||
(reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ?
|
||||
((req & SECURITY_VIOLATION_TYPE) ?
|
||||
"carveout" : "trustzone") : "");
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra20_mc_of_match[] __devinitconst = {
|
||||
{ .compatible = "nvidia,tegra20-mc", },
|
||||
{},
|
||||
};
|
||||
|
||||
static irqreturn_t tegra20_mc_isr(int irq, void *data)
|
||||
{
|
||||
u32 stat, mask, bit;
|
||||
struct tegra20_mc *mc = data;
|
||||
|
||||
stat = mc_readl(mc, MC_INTSTATUS);
|
||||
mask = mc_readl(mc, MC_INTMASK);
|
||||
mask &= stat;
|
||||
if (!mask)
|
||||
return IRQ_NONE;
|
||||
while ((bit = ffs(mask)) != 0)
|
||||
tegra20_mc_decode(mc, bit - 1);
|
||||
mc_writel(mc, stat, MC_INTSTATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit tegra20_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *irq;
|
||||
struct tegra20_mc *mc;
|
||||
int i, err;
|
||||
u32 intmask;
|
||||
|
||||
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
mc->dev = &pdev->dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!mc->regs[i])
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq)
|
||||
return -ENODEV;
|
||||
err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), mc);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, mc);
|
||||
|
||||
intmask = MC_INT_INVALID_GART_PAGE |
|
||||
MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
|
||||
mc_writel(mc, intmask, MC_INTMASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra20_mc_driver = {
|
||||
.probe = tegra20_mc_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra20_mc_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra20_mc_driver);
|
||||
|
||||
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra20 MC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
382
drivers/memory/tegra30-mc.c
Normal file
382
drivers/memory/tegra30-mc.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Tegra30 Memory Controller
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "tegra30-mc"
|
||||
|
||||
#define MC_INTSTATUS 0x0
|
||||
#define MC_INTMASK 0x4
|
||||
|
||||
#define MC_INT_ERR_SHIFT 6
|
||||
#define MC_INT_ERR_MASK (0x1f << MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_DECERR_EMEM BIT(MC_INT_ERR_SHIFT)
|
||||
#define MC_INT_SECURITY_VIOLATION BIT(MC_INT_ERR_SHIFT + 2)
|
||||
#define MC_INT_ARBITRATION_EMEM BIT(MC_INT_ERR_SHIFT + 3)
|
||||
#define MC_INT_INVALID_SMMU_PAGE BIT(MC_INT_ERR_SHIFT + 4)
|
||||
|
||||
#define MC_ERR_STATUS 0x8
|
||||
#define MC_ERR_ADR 0xc
|
||||
|
||||
#define MC_ERR_TYPE_SHIFT 28
|
||||
#define MC_ERR_TYPE_MASK (7 << MC_ERR_TYPE_SHIFT)
|
||||
#define MC_ERR_TYPE_DECERR_EMEM 2
|
||||
#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3
|
||||
#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
|
||||
#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
|
||||
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_SHIFT 25
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_MASK (7 << MC_ERR_INVALID_SMMU_PAGE_SHIFT)
|
||||
#define MC_ERR_RW_SHIFT 16
|
||||
#define MC_ERR_RW BIT(MC_ERR_RW_SHIFT)
|
||||
#define MC_ERR_SECURITY BIT(MC_ERR_RW_SHIFT + 1)
|
||||
|
||||
#define SECURITY_VIOLATION_TYPE BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
|
||||
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
|
||||
#define MC_EMEM_ARB_TIMING_RCD 0x98
|
||||
#define MC_EMEM_ARB_TIMING_RP 0x9c
|
||||
#define MC_EMEM_ARB_TIMING_RC 0xa0
|
||||
#define MC_EMEM_ARB_TIMING_RAS 0xa4
|
||||
#define MC_EMEM_ARB_TIMING_FAW 0xa8
|
||||
#define MC_EMEM_ARB_TIMING_RRD 0xac
|
||||
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
|
||||
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
|
||||
#define MC_EMEM_ARB_TIMING_R2R 0xb8
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
#define MC_EMEM_ARB_MISC1 0xdc
|
||||
|
||||
#define MC_EMEM_ARB_RING3_THROTTLE 0xe4
|
||||
#define MC_EMEM_ARB_OVERRIDE 0xe8
|
||||
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
|
||||
#define MC_CLIENT_ID_MASK 0x7f
|
||||
|
||||
#define NUM_MC_REG_BANKS 4
|
||||
|
||||
struct tegra30_mc {
|
||||
void __iomem *regs[NUM_MC_REG_BANKS];
|
||||
struct device *dev;
|
||||
u32 ctx[0];
|
||||
};
|
||||
|
||||
static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
if (offs < 0x10)
|
||||
val = readl(mc->regs[0] + offs);
|
||||
if (offs < 0x1f0)
|
||||
val = readl(mc->regs[1] + offs - 0x3c);
|
||||
if (offs < 0x228)
|
||||
val = readl(mc->regs[2] + offs - 0x200);
|
||||
if (offs < 0x400)
|
||||
val = readl(mc->regs[3] + offs - 0x284);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
|
||||
{
|
||||
if (offs < 0x10) {
|
||||
writel(val, mc->regs[0] + offs);
|
||||
return;
|
||||
}
|
||||
if (offs < 0x1f0) {
|
||||
writel(val, mc->regs[1] + offs - 0x3c);
|
||||
return;
|
||||
}
|
||||
if (offs < 0x228) {
|
||||
writel(val, mc->regs[2] + offs - 0x200);
|
||||
return;
|
||||
}
|
||||
if (offs < 0x400) {
|
||||
writel(val, mc->regs[3] + offs - 0x284);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const tegra30_mc_client[] = {
|
||||
"csr_ptcr",
|
||||
"cbr_display0a",
|
||||
"cbr_display0ab",
|
||||
"cbr_display0b",
|
||||
"cbr_display0bb",
|
||||
"cbr_display0c",
|
||||
"cbr_display0cb",
|
||||
"cbr_display1b",
|
||||
"cbr_display1bb",
|
||||
"cbr_eppup",
|
||||
"cbr_g2pr",
|
||||
"cbr_g2sr",
|
||||
"cbr_mpeunifbr",
|
||||
"cbr_viruv",
|
||||
"csr_afir",
|
||||
"csr_avpcarm7r",
|
||||
"csr_displayhc",
|
||||
"csr_displayhcb",
|
||||
"csr_fdcdrd",
|
||||
"csr_fdcdrd2",
|
||||
"csr_g2dr",
|
||||
"csr_hdar",
|
||||
"csr_host1xdmar",
|
||||
"csr_host1xr",
|
||||
"csr_idxsrd",
|
||||
"csr_idxsrd2",
|
||||
"csr_mpe_ipred",
|
||||
"csr_mpeamemrd",
|
||||
"csr_mpecsrd",
|
||||
"csr_ppcsahbdmar",
|
||||
"csr_ppcsahbslvr",
|
||||
"csr_satar",
|
||||
"csr_texsrd",
|
||||
"csr_texsrd2",
|
||||
"csr_vdebsevr",
|
||||
"csr_vdember",
|
||||
"csr_vdemcer",
|
||||
"csr_vdetper",
|
||||
"csr_mpcorelpr",
|
||||
"csr_mpcorer",
|
||||
"cbw_eppu",
|
||||
"cbw_eppv",
|
||||
"cbw_eppy",
|
||||
"cbw_mpeunifbw",
|
||||
"cbw_viwsb",
|
||||
"cbw_viwu",
|
||||
"cbw_viwv",
|
||||
"cbw_viwy",
|
||||
"ccw_g2dw",
|
||||
"csw_afiw",
|
||||
"csw_avpcarm7w",
|
||||
"csw_fdcdwr",
|
||||
"csw_fdcdwr2",
|
||||
"csw_hdaw",
|
||||
"csw_host1xw",
|
||||
"csw_ispw",
|
||||
"csw_mpcorelpw",
|
||||
"csw_mpcorew",
|
||||
"csw_mpecswr",
|
||||
"csw_ppcsahbdmaw",
|
||||
"csw_ppcsahbslvw",
|
||||
"csw_sataw",
|
||||
"csw_vdebsevw",
|
||||
"csw_vdedbgw",
|
||||
"csw_vdembew",
|
||||
"csw_vdetpmw",
|
||||
};
|
||||
|
||||
static void tegra30_mc_decode(struct tegra30_mc *mc, int n)
|
||||
{
|
||||
u32 err, addr;
|
||||
const char * const mc_int_err[] = {
|
||||
"MC_DECERR",
|
||||
"Unknown",
|
||||
"MC_SECURITY_ERR",
|
||||
"MC_ARBITRATION_EMEM",
|
||||
"MC_SMMU_ERR",
|
||||
};
|
||||
const char * const err_type[] = {
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"DECERR_EMEM",
|
||||
"SECURITY_TRUSTZONE",
|
||||
"SECURITY_CARVEOUT",
|
||||
"Unknown",
|
||||
"INVALID_SMMU_PAGE",
|
||||
"Unknown",
|
||||
};
|
||||
char attr[6];
|
||||
int cid, perm, type, idx;
|
||||
const char *client = "Unknown";
|
||||
|
||||
idx = n - MC_INT_ERR_SHIFT;
|
||||
if ((idx < 0) || (idx >= ARRAY_SIZE(mc_int_err)) || (idx == 1)) {
|
||||
dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
|
||||
BIT(n));
|
||||
return;
|
||||
}
|
||||
|
||||
err = readl(mc + MC_ERR_STATUS);
|
||||
|
||||
type = (err & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
|
||||
perm = (err & MC_ERR_INVALID_SMMU_PAGE_MASK) >>
|
||||
MC_ERR_INVALID_SMMU_PAGE_SHIFT;
|
||||
if (type == MC_ERR_TYPE_INVALID_SMMU_PAGE)
|
||||
sprintf(attr, "%c-%c-%c",
|
||||
(perm & BIT(2)) ? 'R' : '-',
|
||||
(perm & BIT(1)) ? 'W' : '-',
|
||||
(perm & BIT(0)) ? 'S' : '-');
|
||||
else
|
||||
attr[0] = '\0';
|
||||
|
||||
cid = err & MC_CLIENT_ID_MASK;
|
||||
if (cid < ARRAY_SIZE(tegra30_mc_client))
|
||||
client = tegra30_mc_client[cid];
|
||||
|
||||
addr = readl(mc + MC_ERR_ADR);
|
||||
|
||||
dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s %s %s)\n",
|
||||
mc_int_err[idx], err, addr, client,
|
||||
(err & MC_ERR_SECURITY) ? "secure" : "non-secure",
|
||||
(err & MC_ERR_RW) ? "write" : "read",
|
||||
err_type[type], attr);
|
||||
}
|
||||
|
||||
static const u32 tegra30_mc_ctx[] = {
|
||||
MC_EMEM_ARB_CFG,
|
||||
MC_EMEM_ARB_OUTSTANDING_REQ,
|
||||
MC_EMEM_ARB_TIMING_RCD,
|
||||
MC_EMEM_ARB_TIMING_RP,
|
||||
MC_EMEM_ARB_TIMING_RC,
|
||||
MC_EMEM_ARB_TIMING_RAS,
|
||||
MC_EMEM_ARB_TIMING_FAW,
|
||||
MC_EMEM_ARB_TIMING_RRD,
|
||||
MC_EMEM_ARB_TIMING_RAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_WAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_R2R,
|
||||
MC_EMEM_ARB_TIMING_W2W,
|
||||
MC_EMEM_ARB_TIMING_R2W,
|
||||
MC_EMEM_ARB_TIMING_W2R,
|
||||
MC_EMEM_ARB_DA_TURNS,
|
||||
MC_EMEM_ARB_DA_COVERS,
|
||||
MC_EMEM_ARB_MISC0,
|
||||
MC_EMEM_ARB_MISC1,
|
||||
MC_EMEM_ARB_RING3_THROTTLE,
|
||||
MC_EMEM_ARB_OVERRIDE,
|
||||
MC_INTMASK,
|
||||
};
|
||||
|
||||
static int tegra30_mc_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra30_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
|
||||
mc->ctx[i] = mc_readl(mc, tegra30_mc_ctx[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra30_mc_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra30_mc *mc = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra30_mc_ctx); i++)
|
||||
mc_writel(mc, mc->ctx[i], tegra30_mc_ctx[i]);
|
||||
|
||||
mc_writel(mc, 1, MC_TIMING_CONTROL);
|
||||
/* Read-back to ensure that write reached */
|
||||
mc_readl(mc, MC_TIMING_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,
|
||||
tegra30_mc_suspend,
|
||||
tegra30_mc_resume, NULL);
|
||||
|
||||
static const struct of_device_id tegra30_mc_of_match[] __devinitconst = {
|
||||
{ .compatible = "nvidia,tegra30-mc", },
|
||||
{},
|
||||
};
|
||||
|
||||
static irqreturn_t tegra30_mc_isr(int irq, void *data)
|
||||
{
|
||||
u32 stat, mask, bit;
|
||||
struct tegra30_mc *mc = data;
|
||||
|
||||
stat = mc_readl(mc, MC_INTSTATUS);
|
||||
mask = mc_readl(mc, MC_INTMASK);
|
||||
mask &= stat;
|
||||
if (!mask)
|
||||
return IRQ_NONE;
|
||||
while ((bit = ffs(mask)) != 0)
|
||||
tegra30_mc_decode(mc, bit - 1);
|
||||
mc_writel(mc, stat, MC_INTSTATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit tegra30_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *irq;
|
||||
struct tegra30_mc *mc;
|
||||
size_t bytes;
|
||||
int err, i;
|
||||
u32 intmask;
|
||||
|
||||
bytes = sizeof(*mc) + sizeof(u32) * ARRAY_SIZE(tegra30_mc_ctx);
|
||||
mc = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
mc->dev = &pdev->dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
mc->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!mc->regs[i])
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq)
|
||||
return -ENODEV;
|
||||
err = devm_request_irq(&pdev->dev, irq->start, tegra30_mc_isr,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), mc);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, mc);
|
||||
|
||||
intmask = MC_INT_INVALID_SMMU_PAGE |
|
||||
MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
|
||||
mc_writel(mc, intmask, MC_INTMASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra30_mc_driver = {
|
||||
.probe = tegra30_mc_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra30_mc_of_match,
|
||||
.pm = &tegra30_mc_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra30_mc_driver);
|
||||
|
||||
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra30 MC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
@ -508,14 +508,6 @@ config USB_SWITCH_FSA9480
|
||||
stereo and mono audio, video, microphone and UART data to use
|
||||
a common connector port.
|
||||
|
||||
config MAX8997_MUIC
|
||||
tristate "MAX8997 MUIC Support"
|
||||
depends on MFD_MAX8997
|
||||
help
|
||||
If you say yes here you get support for the MUIC device of
|
||||
Maxim MAX8997 PMIC.
|
||||
The MAX8997 MUIC is a USB port accessory detector and switch.
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
|
@ -50,4 +50,3 @@ obj-y += lis3lv02d/
|
||||
obj-y += carma/
|
||||
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
|
||||
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
|
||||
obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o
|
||||
|
@ -2075,7 +2075,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
|
||||
|
||||
printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
|
||||
if (p->base_hi && priv->ecr)
|
||||
printk(" (0x%lx)", p->base_hi);
|
||||
printk(KERN_CONT " (0x%lx)", p->base_hi);
|
||||
if (p->irq == PARPORT_IRQ_AUTO) {
|
||||
p->irq = PARPORT_IRQ_NONE;
|
||||
parport_irq_probe(p);
|
||||
@ -2086,7 +2086,7 @@ struct parport *parport_pc_probe_port(unsigned long int base,
|
||||
p->irq = PARPORT_IRQ_NONE;
|
||||
}
|
||||
if (p->irq != PARPORT_IRQ_NONE) {
|
||||
printk(", irq %d", p->irq);
|
||||
printk(KERN_CONT ", irq %d", p->irq);
|
||||
priv->ctr_writable |= 0x10;
|
||||
|
||||
if (p->dma == PARPORT_DMA_AUTO) {
|
||||
@ -2110,21 +2110,21 @@ struct parport *parport_pc_probe_port(unsigned long int base,
|
||||
/* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */
|
||||
#endif /* IEEE 1284 support */
|
||||
if (p->dma != PARPORT_DMA_NONE) {
|
||||
printk(", dma %d", p->dma);
|
||||
printk(KERN_CONT ", dma %d", p->dma);
|
||||
p->modes |= PARPORT_MODE_DMA;
|
||||
} else
|
||||
printk(", using FIFO");
|
||||
printk(KERN_CONT ", using FIFO");
|
||||
} else
|
||||
/* We can't use the DMA channel after all. */
|
||||
p->dma = PARPORT_DMA_NONE;
|
||||
#endif /* Allowed to use FIFO/DMA */
|
||||
|
||||
printk(" [");
|
||||
printk(KERN_CONT " [");
|
||||
|
||||
#define printmode(x) \
|
||||
{\
|
||||
if (p->modes & PARPORT_MODE_##x) {\
|
||||
printk("%s%s", f ? "," : "", #x);\
|
||||
printk(KERN_CONT "%s%s", f ? "," : "", #x);\
|
||||
f++;\
|
||||
} \
|
||||
}
|
||||
@ -2140,9 +2140,9 @@ struct parport *parport_pc_probe_port(unsigned long int base,
|
||||
}
|
||||
#undef printmode
|
||||
#ifndef CONFIG_PARPORT_1284
|
||||
printk("(,...)");
|
||||
printk(KERN_CONT "(,...)");
|
||||
#endif /* CONFIG_PARPORT_1284 */
|
||||
printk("]\n");
|
||||
printk(KERN_CONT "]\n");
|
||||
if (probedirq != PARPORT_IRQ_NONE)
|
||||
printk(KERN_INFO "%s: irq %d detected\n", p->name, probedirq);
|
||||
|
||||
|
@ -53,8 +53,6 @@ config ANDROID_LOW_MEMORY_KILLER
|
||||
---help---
|
||||
Register processes to be killed when memory is low
|
||||
|
||||
source "drivers/staging/android/switch/Kconfig"
|
||||
|
||||
config ANDROID_INTF_ALARM
|
||||
bool "Android alarm driver"
|
||||
depends on RTC_CLASS
|
||||
@ -80,7 +78,6 @@ config ANDROID_ALARM_OLDDRV_COMPAT
|
||||
Provides preprocessor alias to aid compatability with
|
||||
older out-of-tree drivers that use the Android Alarm
|
||||
in-kernel API. This will be removed eventually.
|
||||
|
||||
endif # if ANDROID
|
||||
|
||||
endmenu
|
||||
|
@ -6,6 +6,5 @@ obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
|
||||
obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
|
||||
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
|
||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
||||
obj-$(CONFIG_ANDROID_SWITCH) += switch/
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM) += alarm.o
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
|
||||
|
@ -1,11 +0,0 @@
|
||||
menuconfig ANDROID_SWITCH
|
||||
tristate "Android Switch class support"
|
||||
help
|
||||
Say Y here to enable Android switch class support. This allows
|
||||
monitoring switches by userspace via sysfs and uevent.
|
||||
|
||||
config ANDROID_SWITCH_GPIO
|
||||
tristate "Android GPIO Switch support"
|
||||
depends on GENERIC_GPIO && ANDROID_SWITCH
|
||||
help
|
||||
Say Y here to enable GPIO based switch support.
|
@ -1,4 +0,0 @@
|
||||
# Android Switch Class Driver
|
||||
obj-$(CONFIG_ANDROID_SWITCH) += switch_class.o
|
||||
obj-$(CONFIG_ANDROID_SWITCH_GPIO) += switch_gpio.o
|
||||
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Switch class driver
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SWITCH_H__
|
||||
#define __LINUX_SWITCH_H__
|
||||
|
||||
struct switch_dev {
|
||||
const char *name;
|
||||
struct device *dev;
|
||||
int index;
|
||||
int state;
|
||||
|
||||
ssize_t (*print_name)(struct switch_dev *sdev, char *buf);
|
||||
ssize_t (*print_state)(struct switch_dev *sdev, char *buf);
|
||||
};
|
||||
|
||||
struct gpio_switch_platform_data {
|
||||
const char *name;
|
||||
unsigned gpio;
|
||||
|
||||
/* if NULL, switch_dev.name will be printed */
|
||||
const char *name_on;
|
||||
const char *name_off;
|
||||
/* if NULL, "0" or "1" will be printed */
|
||||
const char *state_on;
|
||||
const char *state_off;
|
||||
};
|
||||
|
||||
extern int switch_dev_register(struct switch_dev *sdev);
|
||||
extern void switch_dev_unregister(struct switch_dev *sdev);
|
||||
|
||||
static inline int switch_get_state(struct switch_dev *sdev)
|
||||
{
|
||||
return sdev->state;
|
||||
}
|
||||
|
||||
extern void switch_set_state(struct switch_dev *sdev, int state);
|
||||
|
||||
#endif /* __LINUX_SWITCH_H__ */
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* switch_class.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/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include "switch.h"
|
||||
|
||||
struct class *switch_class;
|
||||
static atomic_t device_count;
|
||||
|
||||
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct switch_dev *sdev = (struct switch_dev *)
|
||||
dev_get_drvdata(dev);
|
||||
|
||||
if (sdev->print_state) {
|
||||
int ret = sdev->print_state(sdev, buf);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
return sprintf(buf, "%d\n", sdev->state);
|
||||
}
|
||||
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct switch_dev *sdev = (struct switch_dev *)
|
||||
dev_get_drvdata(dev);
|
||||
|
||||
if (sdev->print_name) {
|
||||
int ret = sdev->print_name(sdev, buf);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
return sprintf(buf, "%s\n", sdev->name);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
|
||||
static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
|
||||
|
||||
void switch_set_state(struct switch_dev *sdev, int state)
|
||||
{
|
||||
char name_buf[120];
|
||||
char state_buf[120];
|
||||
char *prop_buf;
|
||||
char *envp[3];
|
||||
int env_offset = 0;
|
||||
int length;
|
||||
|
||||
if (sdev->state != state) {
|
||||
sdev->state = state;
|
||||
|
||||
prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (prop_buf) {
|
||||
length = name_show(sdev->dev, NULL, prop_buf);
|
||||
if (length > 0) {
|
||||
if (prop_buf[length - 1] == '\n')
|
||||
prop_buf[length - 1] = 0;
|
||||
snprintf(name_buf, sizeof(name_buf),
|
||||
"SWITCH_NAME=%s", prop_buf);
|
||||
envp[env_offset++] = name_buf;
|
||||
}
|
||||
length = state_show(sdev->dev, NULL, prop_buf);
|
||||
if (length > 0) {
|
||||
if (prop_buf[length - 1] == '\n')
|
||||
prop_buf[length - 1] = 0;
|
||||
snprintf(state_buf, sizeof(state_buf),
|
||||
"SWITCH_STATE=%s", prop_buf);
|
||||
envp[env_offset++] = state_buf;
|
||||
}
|
||||
envp[env_offset] = NULL;
|
||||
kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
|
||||
free_page((unsigned long)prop_buf);
|
||||
} else {
|
||||
printk(KERN_ERR "out of memory in switch_set_state\n");
|
||||
kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switch_set_state);
|
||||
|
||||
static int create_switch_class(void)
|
||||
{
|
||||
if (!switch_class) {
|
||||
switch_class = class_create(THIS_MODULE, "switch");
|
||||
if (IS_ERR(switch_class))
|
||||
return PTR_ERR(switch_class);
|
||||
atomic_set(&device_count, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int switch_dev_register(struct switch_dev *sdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!switch_class) {
|
||||
ret = create_switch_class();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sdev->index = atomic_inc_return(&device_count);
|
||||
sdev->dev = device_create(switch_class, NULL,
|
||||
MKDEV(0, sdev->index), NULL, sdev->name);
|
||||
if (IS_ERR(sdev->dev))
|
||||
return PTR_ERR(sdev->dev);
|
||||
|
||||
ret = device_create_file(sdev->dev, &dev_attr_state);
|
||||
if (ret < 0)
|
||||
goto err_create_file_1;
|
||||
ret = device_create_file(sdev->dev, &dev_attr_name);
|
||||
if (ret < 0)
|
||||
goto err_create_file_2;
|
||||
|
||||
dev_set_drvdata(sdev->dev, sdev);
|
||||
sdev->state = 0;
|
||||
return 0;
|
||||
|
||||
err_create_file_2:
|
||||
device_remove_file(sdev->dev, &dev_attr_state);
|
||||
err_create_file_1:
|
||||
device_destroy(switch_class, MKDEV(0, sdev->index));
|
||||
printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switch_dev_register);
|
||||
|
||||
void switch_dev_unregister(struct switch_dev *sdev)
|
||||
{
|
||||
device_remove_file(sdev->dev, &dev_attr_name);
|
||||
device_remove_file(sdev->dev, &dev_attr_state);
|
||||
device_destroy(switch_class, MKDEV(0, sdev->index));
|
||||
dev_set_drvdata(sdev->dev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switch_dev_unregister);
|
||||
|
||||
static int __init switch_class_init(void)
|
||||
{
|
||||
return create_switch_class();
|
||||
}
|
||||
|
||||
static void __exit switch_class_exit(void)
|
||||
{
|
||||
class_destroy(switch_class);
|
||||
}
|
||||
|
||||
module_init(switch_class_init);
|
||||
module_exit(switch_class_exit);
|
||||
|
||||
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
||||
MODULE_DESCRIPTION("Switch class driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* switch_gpio.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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/gpio.h>
|
||||
#include "switch.h"
|
||||
|
||||
struct gpio_switch_data {
|
||||
struct switch_dev sdev;
|
||||
unsigned gpio;
|
||||
const char *name_on;
|
||||
const char *name_off;
|
||||
const char *state_on;
|
||||
const char *state_off;
|
||||
int irq;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
static void gpio_switch_work(struct work_struct *work)
|
||||
{
|
||||
int state;
|
||||
struct gpio_switch_data *data =
|
||||
container_of(work, struct gpio_switch_data, work);
|
||||
|
||||
state = gpio_get_value(data->gpio);
|
||||
switch_set_state(&data->sdev, state);
|
||||
}
|
||||
|
||||
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct gpio_switch_data *switch_data =
|
||||
(struct gpio_switch_data *)dev_id;
|
||||
|
||||
schedule_work(&switch_data->work);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf)
|
||||
{
|
||||
struct gpio_switch_data *switch_data =
|
||||
container_of(sdev, struct gpio_switch_data, sdev);
|
||||
const char *state;
|
||||
if (switch_get_state(sdev))
|
||||
state = switch_data->state_on;
|
||||
else
|
||||
state = switch_data->state_off;
|
||||
|
||||
if (state)
|
||||
return sprintf(buf, "%s\n", state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int gpio_switch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_switch_data *switch_data;
|
||||
int ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
return -EBUSY;
|
||||
|
||||
switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
|
||||
if (!switch_data)
|
||||
return -ENOMEM;
|
||||
|
||||
switch_data->sdev.name = pdata->name;
|
||||
switch_data->gpio = pdata->gpio;
|
||||
switch_data->name_on = pdata->name_on;
|
||||
switch_data->name_off = pdata->name_off;
|
||||
switch_data->state_on = pdata->state_on;
|
||||
switch_data->state_off = pdata->state_off;
|
||||
switch_data->sdev.print_state = switch_gpio_print_state;
|
||||
|
||||
ret = switch_dev_register(&switch_data->sdev);
|
||||
if (ret < 0)
|
||||
goto err_switch_dev_register;
|
||||
|
||||
ret = gpio_request(switch_data->gpio, pdev->name);
|
||||
if (ret < 0)
|
||||
goto err_request_gpio;
|
||||
|
||||
ret = gpio_direction_input(switch_data->gpio);
|
||||
if (ret < 0)
|
||||
goto err_set_gpio_input;
|
||||
|
||||
INIT_WORK(&switch_data->work, gpio_switch_work);
|
||||
|
||||
switch_data->irq = gpio_to_irq(switch_data->gpio);
|
||||
if (switch_data->irq < 0) {
|
||||
ret = switch_data->irq;
|
||||
goto err_detect_irq_num_failed;
|
||||
}
|
||||
|
||||
ret = request_irq(switch_data->irq, gpio_irq_handler,
|
||||
IRQF_TRIGGER_LOW, pdev->name, switch_data);
|
||||
if (ret < 0)
|
||||
goto err_request_irq;
|
||||
|
||||
/* Perform initial detection */
|
||||
gpio_switch_work(&switch_data->work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_irq:
|
||||
err_detect_irq_num_failed:
|
||||
err_set_gpio_input:
|
||||
gpio_free(switch_data->gpio);
|
||||
err_request_gpio:
|
||||
switch_dev_unregister(&switch_data->sdev);
|
||||
err_switch_dev_register:
|
||||
kfree(switch_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit gpio_switch_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_switch_data *switch_data = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_work_sync(&switch_data->work);
|
||||
gpio_free(switch_data->gpio);
|
||||
switch_dev_unregister(&switch_data->sdev);
|
||||
kfree(switch_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver gpio_switch_driver = {
|
||||
.probe = gpio_switch_probe,
|
||||
.remove = __devexit_p(gpio_switch_remove),
|
||||
.driver = {
|
||||
.name = "switch-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init gpio_switch_init(void)
|
||||
{
|
||||
return platform_driver_register(&gpio_switch_driver);
|
||||
}
|
||||
|
||||
static void __exit gpio_switch_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&gpio_switch_driver);
|
||||
}
|
||||
|
||||
module_init(gpio_switch_init);
|
||||
module_exit(gpio_switch_exit);
|
||||
|
||||
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
||||
MODULE_DESCRIPTION("GPIO Switch driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -146,6 +146,14 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
|
||||
priv->flags = 0; /* interrupt is enabled to begin with */
|
||||
priv->pdev = pdev;
|
||||
|
||||
if (!uioinfo->irq) {
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get IRQ\n");
|
||||
goto bad0;
|
||||
}
|
||||
uioinfo->irq = ret;
|
||||
}
|
||||
uiomem = &uioinfo->mem[0];
|
||||
|
||||
for (i = 0; i < pdev->num_resources; ++i) {
|
||||
|
@ -73,7 +73,7 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
|
||||
return (value < 0) ? value : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
|
||||
static DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR,
|
||||
show_bConfigurationValue, set_bConfigurationValue);
|
||||
|
||||
/* String fields */
|
||||
@ -595,7 +595,7 @@ static ssize_t usb_dev_authorized_store(struct device *dev,
|
||||
return result < 0? result : size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(authorized, 0644,
|
||||
static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, 0644,
|
||||
usb_dev_authorized_show, usb_dev_authorized_store);
|
||||
|
||||
/* "Safely remove a device" */
|
||||
@ -618,7 +618,7 @@ static ssize_t usb_remove_store(struct device *dev,
|
||||
usb_unlock_device(udev);
|
||||
return rc;
|
||||
}
|
||||
static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store);
|
||||
static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, usb_remove_store);
|
||||
|
||||
|
||||
static struct attribute *dev_attrs[] = {
|
||||
|
@ -332,7 +332,6 @@ static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
|
||||
},
|
||||
.size = 1,
|
||||
.read = w1_f29_read_cond_search_mask,
|
||||
.write = 0,
|
||||
},
|
||||
{
|
||||
.attr = {
|
||||
@ -341,7 +340,6 @@ static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
|
||||
},
|
||||
.size = 1,
|
||||
.read = w1_f29_read_cond_search_polarity,
|
||||
.write = 0,
|
||||
},
|
||||
{
|
||||
.attr = {
|
||||
|
@ -1027,7 +1027,7 @@ static int __init w1_init(void)
|
||||
retval = driver_register(&w1_slave_driver);
|
||||
if (retval) {
|
||||
printk(KERN_ERR
|
||||
"Failed to register master driver. err=%d.\n",
|
||||
"Failed to register slave driver. err=%d.\n",
|
||||
retval);
|
||||
goto err_out_master_unregister;
|
||||
}
|
||||
|
@ -31,6 +31,9 @@
|
||||
static int w1_delay_parm = 1;
|
||||
module_param_named(delay_coef, w1_delay_parm, int, 0);
|
||||
|
||||
static int w1_disable_irqs = 0;
|
||||
module_param_named(disable_irqs, w1_disable_irqs, int, 0);
|
||||
|
||||
static u8 w1_crc8_table[] = {
|
||||
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
|
||||
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
|
||||
@ -79,6 +82,10 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit)
|
||||
*/
|
||||
static void w1_write_bit(struct w1_master *dev, int bit)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
|
||||
if(w1_disable_irqs) local_irq_save(flags);
|
||||
|
||||
if (bit) {
|
||||
dev->bus_master->write_bit(dev->bus_master->data, 0);
|
||||
w1_delay(6);
|
||||
@ -90,6 +97,8 @@ static void w1_write_bit(struct w1_master *dev, int bit)
|
||||
dev->bus_master->write_bit(dev->bus_master->data, 1);
|
||||
w1_delay(10);
|
||||
}
|
||||
|
||||
if(w1_disable_irqs) local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,7 +167,7 @@ EXPORT_SYMBOL_GPL(w1_write_8);
|
||||
static u8 w1_read_bit(struct w1_master *dev)
|
||||
{
|
||||
int result;
|
||||
unsigned long flags;
|
||||
unsigned long flags = 0;
|
||||
|
||||
/* sample timing is critical here */
|
||||
local_irq_save(flags);
|
||||
@ -318,6 +327,9 @@ EXPORT_SYMBOL_GPL(w1_read_block);
|
||||
int w1_reset_bus(struct w1_master *dev)
|
||||
{
|
||||
int result;
|
||||
unsigned long flags = 0;
|
||||
|
||||
if(w1_disable_irqs) local_irq_save(flags);
|
||||
|
||||
if (dev->bus_master->reset_bus)
|
||||
result = dev->bus_master->reset_bus(dev->bus_master->data) & 0x1;
|
||||
@ -330,19 +342,21 @@ int w1_reset_bus(struct w1_master *dev)
|
||||
* cpu for such a short amount of time AND get it back in
|
||||
* the maximum amount of time.
|
||||
*/
|
||||
w1_delay(480);
|
||||
w1_delay(500);
|
||||
dev->bus_master->write_bit(dev->bus_master->data, 1);
|
||||
w1_delay(70);
|
||||
|
||||
result = dev->bus_master->read_bit(dev->bus_master->data) & 0x1;
|
||||
/* minmum 70 (above) + 410 = 480 us
|
||||
/* minmum 70 (above) + 430 = 500 us
|
||||
* There aren't any timing requirements between a reset and
|
||||
* the following transactions. Sleeping is safe here.
|
||||
*/
|
||||
/* w1_delay(410); min required time */
|
||||
/* w1_delay(430); min required time */
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if(w1_disable_irqs) local_irq_restore(flags);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(w1_reset_bus);
|
||||
|
@ -132,6 +132,24 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
|
||||
rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
|
||||
/* Test for attributes that want to ignore lockdep for read-locking */
|
||||
static bool ignore_lockdep(struct sysfs_dirent *sd)
|
||||
{
|
||||
return sysfs_type(sd) == SYSFS_KOBJ_ATTR &&
|
||||
sd->s_attr.attr->ignore_lockdep;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline bool ignore_lockdep(struct sysfs_dirent *sd)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sysfs_get_active - get an active reference to sysfs_dirent
|
||||
* @sd: sysfs_dirent to get an active reference to
|
||||
@ -155,15 +173,17 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
|
||||
return NULL;
|
||||
|
||||
t = atomic_cmpxchg(&sd->s_active, v, v + 1);
|
||||
if (likely(t == v)) {
|
||||
rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
|
||||
return sd;
|
||||
}
|
||||
if (likely(t == v))
|
||||
break;
|
||||
if (t < 0)
|
||||
return NULL;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (likely(!ignore_lockdep(sd)))
|
||||
rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
|
||||
return sd;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,7 +200,8 @@ void sysfs_put_active(struct sysfs_dirent *sd)
|
||||
if (unlikely(!sd))
|
||||
return;
|
||||
|
||||
rwsem_release(&sd->dep_map, 1, _RET_IP_);
|
||||
if (likely(!ignore_lockdep(sd)))
|
||||
rwsem_release(&sd->dep_map, 1, _RET_IP_);
|
||||
v = atomic_dec_return(&sd->s_active);
|
||||
if (likely(v != SD_DEACTIVATED_BIAS))
|
||||
return;
|
||||
@ -858,7 +879,6 @@ int sysfs_rename(struct sysfs_dirent *sd,
|
||||
struct sysfs_dirent *new_parent_sd, const void *new_ns,
|
||||
const char *new_name)
|
||||
{
|
||||
const char *dup_name = NULL;
|
||||
int error;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
@ -875,11 +895,11 @@ int sysfs_rename(struct sysfs_dirent *sd,
|
||||
/* rename sysfs_dirent */
|
||||
if (strcmp(sd->s_name, new_name) != 0) {
|
||||
error = -ENOMEM;
|
||||
new_name = dup_name = kstrdup(new_name, GFP_KERNEL);
|
||||
new_name = kstrdup(new_name, GFP_KERNEL);
|
||||
if (!new_name)
|
||||
goto out;
|
||||
|
||||
dup_name = sd->s_name;
|
||||
kfree(sd->s_name);
|
||||
sd->s_name = new_name;
|
||||
}
|
||||
|
||||
@ -895,7 +915,6 @@ int sysfs_rename(struct sysfs_dirent *sd,
|
||||
error = 0;
|
||||
out:
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
kfree(dup_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <asm/device.h>
|
||||
|
||||
struct device;
|
||||
@ -502,7 +503,10 @@ ssize_t device_store_int(struct device *dev, struct device_attribute *attr,
|
||||
{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
|
||||
#define DEVICE_INT_ATTR(_name, _mode, _var) \
|
||||
struct dev_ext_attribute dev_attr_##_name = \
|
||||
{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
|
||||
{ __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) }
|
||||
#define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \
|
||||
struct device_attribute dev_attr_##_name = \
|
||||
__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
|
||||
|
||||
extern int device_create_file(struct device *device,
|
||||
const struct device_attribute *entry);
|
||||
@ -541,6 +545,8 @@ extern void *devres_remove(struct device *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data);
|
||||
extern int devres_destroy(struct device *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data);
|
||||
extern int devres_release(struct device *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data);
|
||||
|
||||
/* devres group */
|
||||
extern void * __must_check devres_open_group(struct device *dev, void *id,
|
||||
@ -931,6 +937,32 @@ int _dev_info(const struct device *dev, const char *fmt, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#define dev_level_ratelimited(dev_level, dev, fmt, ...) \
|
||||
do { \
|
||||
static DEFINE_RATELIMIT_STATE(_rs, \
|
||||
DEFAULT_RATELIMIT_INTERVAL, \
|
||||
DEFAULT_RATELIMIT_BURST); \
|
||||
if (__ratelimit(&_rs)) \
|
||||
dev_level(dev, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define dev_emerg_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_emerg, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_alert_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_alert, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_crit_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_crit, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_err_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_err, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_warn_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_warn, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_notice_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_info_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__)
|
||||
#define dev_dbg_ratelimited(dev, fmt, ...) \
|
||||
dev_level_ratelimited(dev_dbg, dev, fmt, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Stupid hackaround for existing uses of non-printk uses dev_info
|
||||
*
|
||||
|
@ -17,8 +17,8 @@ struct _ddebug {
|
||||
const char *format;
|
||||
unsigned int lineno:18;
|
||||
/*
|
||||
* The flags field controls the behaviour at the callsite.
|
||||
* The bits here are changed dynamically when the user
|
||||
* The flags field controls the behaviour at the callsite.
|
||||
* The bits here are changed dynamically when the user
|
||||
* writes commands to <debugfs>/dynamic_debug/control
|
||||
*/
|
||||
#define _DPRINTK_FLAGS_NONE 0
|
||||
@ -44,6 +44,9 @@ extern int ddebug_remove_module(const char *mod_name);
|
||||
extern __printf(2, 3)
|
||||
int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...);
|
||||
|
||||
extern int ddebug_dyndbg_module_param_cb(char *param, char *val,
|
||||
const char *modname);
|
||||
|
||||
struct device;
|
||||
|
||||
extern __printf(3, 4)
|
||||
@ -94,11 +97,26 @@ do { \
|
||||
|
||||
#else
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
static inline int ddebug_remove_module(const char *mod)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ddebug_dyndbg_module_param_cb(char *param, char *val,
|
||||
const char *modname)
|
||||
{
|
||||
if (strstr(param, "dyndbg")) {
|
||||
/* avoid pr_warn(), which wants pr_fmt() fully defined */
|
||||
printk(KERN_WARNING "dyndbg param is supported only in "
|
||||
"CONFIG_DYNAMIC_DEBUG builds\n");
|
||||
return 0; /* allow and ignore */
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define dynamic_pr_debug(fmt, ...) \
|
||||
do { if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); } while (0)
|
||||
#define dynamic_dev_dbg(dev, fmt, ...) \
|
||||
|
324
include/linux/extcon.h
Normal file
324
include/linux/extcon.h
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* External connector (extcon) class driver
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Author: Donggeun Kim <dg77.kim@samsung.com>
|
||||
* Author: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* based on switch class driver
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_EXTCON_H__
|
||||
#define __LINUX_EXTCON_H__
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define SUPPORTED_CABLE_MAX 32
|
||||
#define CABLE_NAME_MAX 30
|
||||
|
||||
/*
|
||||
* The standard cable name is to help support general notifier
|
||||
* and notifee device drivers to share the common names.
|
||||
* Please use standard cable names unless your notifier device has
|
||||
* a very unique and abnormal cable or
|
||||
* the cable type is supposed to be used with only one unique
|
||||
* pair of notifier/notifee devices.
|
||||
*
|
||||
* Please add any other "standard" cables used with extcon dev.
|
||||
*
|
||||
* You may add a dot and number to specify version or specification
|
||||
* of the specific cable if it is required. (e.g., "Fast-charger.18"
|
||||
* and "Fast-charger.10" for 1.8A and 1.0A chargers)
|
||||
* However, the notifee and notifier should be able to handle such
|
||||
* string and if the notifee can negotiate the protocol or idenify,
|
||||
* you don't need such convention. This convention is helpful when
|
||||
* notifier can distinguish but notifiee cannot.
|
||||
*/
|
||||
enum extcon_cable_name {
|
||||
EXTCON_USB = 0,
|
||||
EXTCON_USB_HOST,
|
||||
EXTCON_TA, /* Travel Adaptor */
|
||||
EXTCON_FAST_CHARGER,
|
||||
EXTCON_SLOW_CHARGER,
|
||||
EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
|
||||
EXTCON_HDMI,
|
||||
EXTCON_MHL,
|
||||
EXTCON_DVI,
|
||||
EXTCON_VGA,
|
||||
EXTCON_DOCK,
|
||||
EXTCON_LINE_IN,
|
||||
EXTCON_LINE_OUT,
|
||||
EXTCON_MIC_IN,
|
||||
EXTCON_HEADPHONE_OUT,
|
||||
EXTCON_SPDIF_IN,
|
||||
EXTCON_SPDIF_OUT,
|
||||
EXTCON_VIDEO_IN,
|
||||
EXTCON_VIDEO_OUT,
|
||||
EXTCON_MECHANICAL,
|
||||
};
|
||||
extern const char *extcon_cable_name[];
|
||||
|
||||
struct extcon_cable;
|
||||
|
||||
/**
|
||||
* struct extcon_dev - An extcon device represents one external connector.
|
||||
* @name The name of this extcon device. Parent device name is used
|
||||
* if NULL.
|
||||
* @supported_cable Array of supported cable name ending with NULL.
|
||||
* If supported_cable is NULL, cable name related APIs
|
||||
* are disabled.
|
||||
* @mutually_exclusive Array of mutually exclusive set of cables that cannot
|
||||
* be attached simultaneously. The array should be
|
||||
* ending with NULL or be NULL (no mutually exclusive
|
||||
* cables). For example, if it is { 0x7, 0x30, 0}, then,
|
||||
* {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot
|
||||
* be attached simulataneously. {0x7, 0} is equivalent to
|
||||
* {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
|
||||
* can be no simultaneous connections.
|
||||
* @print_name An optional callback to override the method to print the
|
||||
* name of the extcon device.
|
||||
* @print_state An optional callback to override the method to print the
|
||||
* status of the extcon device.
|
||||
* @dev Device of this extcon. Do not provide at register-time.
|
||||
* @state Attach/detach state of this extcon. Do not provide at
|
||||
* register-time
|
||||
* @nh Notifier for the state change events from this extcon
|
||||
* @entry To support list of extcon devices so that uses can search
|
||||
* for extcon devices based on the extcon name.
|
||||
* @lock
|
||||
* @max_supported Internal value to store the number of cables.
|
||||
* @extcon_dev_type Device_type struct to provide attribute_groups
|
||||
* customized for each extcon device.
|
||||
* @cables Sysfs subdirectories. Each represents one cable.
|
||||
*
|
||||
* In most cases, users only need to provide "User initializing data" of
|
||||
* this struct when registering an extcon. In some exceptional cases,
|
||||
* optional callbacks may be needed. However, the values in "internal data"
|
||||
* are overwritten by register function.
|
||||
*/
|
||||
struct extcon_dev {
|
||||
/* --- Optional user initializing data --- */
|
||||
const char *name;
|
||||
const char **supported_cable;
|
||||
const u32 *mutually_exclusive;
|
||||
|
||||
/* --- Optional callbacks to override class functions --- */
|
||||
ssize_t (*print_name)(struct extcon_dev *edev, char *buf);
|
||||
ssize_t (*print_state)(struct extcon_dev *edev, char *buf);
|
||||
|
||||
/* --- Internal data. Please do not set. --- */
|
||||
struct device *dev;
|
||||
u32 state;
|
||||
struct raw_notifier_head nh;
|
||||
struct list_head entry;
|
||||
spinlock_t lock; /* could be called by irq handler */
|
||||
int max_supported;
|
||||
|
||||
/* /sys/class/extcon/.../cable.n/... */
|
||||
struct device_type extcon_dev_type;
|
||||
struct extcon_cable *cables;
|
||||
/* /sys/class/extcon/.../mutually_exclusive/... */
|
||||
struct attribute_group attr_g_muex;
|
||||
struct attribute **attrs_muex;
|
||||
struct device_attribute *d_attrs_muex;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct extcon_cable - An internal data for each cable of extcon device.
|
||||
* @edev The extcon device
|
||||
* @cable_index Index of this cable in the edev
|
||||
* @attr_g Attribute group for the cable
|
||||
* @attr_name "name" sysfs entry
|
||||
* @attr_state "state" sysfs entry
|
||||
* @attrs Array pointing to attr_name and attr_state for attr_g
|
||||
*/
|
||||
struct extcon_cable {
|
||||
struct extcon_dev *edev;
|
||||
int cable_index;
|
||||
|
||||
struct attribute_group attr_g;
|
||||
struct device_attribute attr_name;
|
||||
struct device_attribute attr_state;
|
||||
|
||||
struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct extcon_specific_cable_nb - An internal data for
|
||||
* extcon_register_interest().
|
||||
* @internal_nb a notifier block bridging extcon notifier and cable notifier.
|
||||
* @user_nb user provided notifier block for events from a specific cable.
|
||||
* @cable_index the target cable.
|
||||
* @edev the target extcon device.
|
||||
* @previous_value the saved previous event value.
|
||||
*/
|
||||
struct extcon_specific_cable_nb {
|
||||
struct notifier_block internal_nb;
|
||||
struct notifier_block *user_nb;
|
||||
int cable_index;
|
||||
struct extcon_dev *edev;
|
||||
unsigned long previous_value;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_EXTCON)
|
||||
|
||||
/*
|
||||
* Following APIs are for notifiers or configurations.
|
||||
* Notifiers are the external port and connection devices.
|
||||
*/
|
||||
extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
|
||||
extern void extcon_dev_unregister(struct extcon_dev *edev);
|
||||
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
|
||||
|
||||
/*
|
||||
* get/set/update_state access the 32b encoded state value, which represents
|
||||
* states of all possible cables of the multistate port. For example, if one
|
||||
* calls extcon_set_state(edev, 0x7), it may mean that all the three cables
|
||||
* are attached to the port.
|
||||
*/
|
||||
static inline u32 extcon_get_state(struct extcon_dev *edev)
|
||||
{
|
||||
return edev->state;
|
||||
}
|
||||
|
||||
extern int extcon_set_state(struct extcon_dev *edev, u32 state);
|
||||
extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
|
||||
|
||||
/*
|
||||
* get/set_cable_state access each bit of the 32b encoded state value.
|
||||
* They are used to access the status of each cable based on the cable_name
|
||||
* or cable_index, which is retrived by extcon_find_cable_index
|
||||
*/
|
||||
extern int extcon_find_cable_index(struct extcon_dev *sdev,
|
||||
const char *cable_name);
|
||||
extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index);
|
||||
extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index,
|
||||
bool cable_state);
|
||||
|
||||
extern int extcon_get_cable_state(struct extcon_dev *edev,
|
||||
const char *cable_name);
|
||||
extern int extcon_set_cable_state(struct extcon_dev *edev,
|
||||
const char *cable_name, bool cable_state);
|
||||
|
||||
/*
|
||||
* Following APIs are for notifiees (those who want to be notified)
|
||||
* to register a callback for events from a specific cable of the extcon.
|
||||
* Notifiees are the connected device drivers wanting to get notified by
|
||||
* a specific external port of a connection device.
|
||||
*/
|
||||
extern int extcon_register_interest(struct extcon_specific_cable_nb *obj,
|
||||
const char *extcon_name,
|
||||
const char *cable_name,
|
||||
struct notifier_block *nb);
|
||||
extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
|
||||
|
||||
/*
|
||||
* Following APIs are to monitor every action of a notifier.
|
||||
* Registerer gets notified for every external port of a connection device.
|
||||
* Probably this could be used to debug an action of notifier; however,
|
||||
* we do not recommend to use this at normal 'notifiee' device drivers who
|
||||
* want to be notified by a specific external port of the notifier.
|
||||
*/
|
||||
extern int extcon_register_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb);
|
||||
extern int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb);
|
||||
#else /* CONFIG_EXTCON */
|
||||
static inline int extcon_dev_register(struct extcon_dev *edev,
|
||||
struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
|
||||
|
||||
static inline u32 extcon_get_state(struct extcon_dev *edev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_set_state(struct extcon_dev *edev, u32 state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_update_state(struct extcon_dev *edev, u32 mask,
|
||||
u32 state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_find_cable_index(struct extcon_dev *edev,
|
||||
const char *cable_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_get_cable_state_(struct extcon_dev *edev,
|
||||
int cable_index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_set_cable_state_(struct extcon_dev *edev,
|
||||
int cable_index, bool cable_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_get_cable_state(struct extcon_dev *edev,
|
||||
const char *cable_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_set_cable_state(struct extcon_dev *edev,
|
||||
const char *cable_name, int state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int extcon_register_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj,
|
||||
const char *extcon_name,
|
||||
const char *cable_name,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_unregister_interest(struct extcon_specific_cable_nb
|
||||
*obj)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_EXTCON */
|
||||
#endif /* __LINUX_EXTCON_H__ */
|
52
include/linux/extcon/extcon_gpio.h
Normal file
52
include/linux/extcon/extcon_gpio.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* External connector (extcon) class generic GPIO driver
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Author: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* based on switch class driver
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef __EXTCON_GPIO_H__
|
||||
#define __EXTCON_GPIO_H__ __FILE__
|
||||
|
||||
#include <linux/extcon.h>
|
||||
|
||||
/**
|
||||
* struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device.
|
||||
* @name The name of this GPIO extcon device.
|
||||
* @gpio Corresponding GPIO.
|
||||
* @debounce Debounce time for GPIO IRQ in ms.
|
||||
* @irq_flags IRQ Flags (e.g., IRQF_TRIGGER_LOW).
|
||||
* @state_on print_state is overriden with state_on if attached. If Null,
|
||||
* default method of extcon class is used.
|
||||
* @state_off print_state is overriden with state_on if dettached. If Null,
|
||||
* default method of extcon class is used.
|
||||
*
|
||||
* Note that in order for state_on or state_off to be valid, both state_on
|
||||
* and state_off should be not NULL. If at least one of them is NULL,
|
||||
* the print_state is not overriden.
|
||||
*/
|
||||
struct gpio_extcon_platform_data {
|
||||
const char *name;
|
||||
unsigned gpio;
|
||||
unsigned long debounce;
|
||||
unsigned long irq_flags;
|
||||
|
||||
/* if NULL, "0" or "1" will be printed */
|
||||
const char *state_on;
|
||||
const char *state_off;
|
||||
};
|
||||
|
||||
#endif /* __EXTCON_GPIO_H__ */
|
@ -1062,8 +1062,10 @@ struct hyperv_service_callback {
|
||||
void (*callback) (void *context);
|
||||
};
|
||||
|
||||
#define MAX_SRV_VER 0x7ffffff
|
||||
extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *,
|
||||
struct icmsg_negotiate *, u8 *);
|
||||
struct icmsg_negotiate *, u8 *, int,
|
||||
int);
|
||||
|
||||
int hv_kvp_init(struct hv_util_service *);
|
||||
void hv_kvp_deinit(void);
|
||||
|
@ -99,34 +99,11 @@ struct max8997_muic_reg_data {
|
||||
|
||||
/**
|
||||
* struct max8997_muic_platform_data
|
||||
* @usb_callback: callback function for USB
|
||||
* inform callee of USB type (HOST or DEVICE)
|
||||
* and attached state(true or false)
|
||||
* @charger_callback: callback function for charger
|
||||
* inform callee of charger_type
|
||||
* and attached state(true or false)
|
||||
* @deskdock_callback: callback function for desk dock
|
||||
* inform callee of attached state(true or false)
|
||||
* @cardock_callback: callback function for car dock
|
||||
* inform callee of attached state(true or false)
|
||||
* @mhl_callback: callback function for MHL (Mobile High-definition Link)
|
||||
* inform callee of attached state(true or false)
|
||||
* @uart_callback: callback function for JIG UART
|
||||
* inform callee of attached state(true or false)
|
||||
* @init_data: array of max8997_muic_reg_data
|
||||
* used for initializing registers of MAX8997 MUIC device
|
||||
* @num_init_data: array size of init_data
|
||||
*/
|
||||
struct max8997_muic_platform_data {
|
||||
void (*usb_callback)(enum max8997_muic_usb_type usb_type,
|
||||
bool attached);
|
||||
void (*charger_callback)(bool attached,
|
||||
enum max8997_muic_charger_type charger_type);
|
||||
void (*deskdock_callback) (bool attached);
|
||||
void (*cardock_callback) (bool attached);
|
||||
void (*mhl_callback) (bool attached);
|
||||
void (*uart_callback) (bool attached);
|
||||
|
||||
struct max8997_muic_reg_data *init_data;
|
||||
int num_init_data;
|
||||
};
|
||||
|
@ -320,7 +320,8 @@ extern int parse_args(const char *name,
|
||||
unsigned num,
|
||||
s16 level_min,
|
||||
s16 level_max,
|
||||
int (*unknown)(char *param, char *val));
|
||||
int (*unknown)(char *param, char *val,
|
||||
const char *doing));
|
||||
|
||||
/* Called by module remove. */
|
||||
#ifdef CONFIG_SYSFS
|
||||
|
128
include/linux/platform_data/emif_plat.h
Normal file
128
include/linux/platform_data/emif_plat.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Definitions for TI EMIF device platform data
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments, Inc.
|
||||
*
|
||||
* Aneesh V <aneesh@ti.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 __EMIF_PLAT_H
|
||||
#define __EMIF_PLAT_H
|
||||
|
||||
/* Low power modes - EMIF_PWR_MGMT_CTRL */
|
||||
#define EMIF_LP_MODE_DISABLE 0
|
||||
#define EMIF_LP_MODE_CLOCK_STOP 1
|
||||
#define EMIF_LP_MODE_SELF_REFRESH 2
|
||||
#define EMIF_LP_MODE_PWR_DN 4
|
||||
|
||||
/* Hardware capabilities */
|
||||
#define EMIF_HW_CAPS_LL_INTERFACE 0x00000001
|
||||
|
||||
/*
|
||||
* EMIF IP Revisions
|
||||
* EMIF4D - Used in OMAP4
|
||||
* EMIF4D5 - Used in OMAP5
|
||||
*/
|
||||
#define EMIF_4D 1
|
||||
#define EMIF_4D5 2
|
||||
|
||||
/*
|
||||
* PHY types
|
||||
* ATTILAPHY - Used in OMAP4
|
||||
* INTELLIPHY - Used in OMAP5
|
||||
*/
|
||||
#define EMIF_PHY_TYPE_ATTILAPHY 1
|
||||
#define EMIF_PHY_TYPE_INTELLIPHY 2
|
||||
|
||||
/* Custom config requests */
|
||||
#define EMIF_CUSTOM_CONFIG_LPMODE 0x00000001
|
||||
#define EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL 0x00000002
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/**
|
||||
* struct ddr_device_info - All information about the DDR device except AC
|
||||
* timing parameters
|
||||
* @type: Device type (LPDDR2-S4, LPDDR2-S2 etc)
|
||||
* @density: Device density
|
||||
* @io_width: Bus width
|
||||
* @cs1_used: Whether there is a DDR device attached to the second
|
||||
* chip-select(CS1) of this EMIF instance
|
||||
* @cal_resistors_per_cs: Whether there is one calibration resistor per
|
||||
* chip-select or whether it's a single one for both
|
||||
* @manufacturer: Manufacturer name string
|
||||
*/
|
||||
struct ddr_device_info {
|
||||
u32 type;
|
||||
u32 density;
|
||||
u32 io_width;
|
||||
u32 cs1_used;
|
||||
u32 cal_resistors_per_cs;
|
||||
char manufacturer[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct emif_custom_configs - Custom configuration parameters/policies
|
||||
* passed from the platform layer
|
||||
* @mask: Mask to indicate which configs are requested
|
||||
* @lpmode: LPMODE to be used in PWR_MGMT_CTRL register
|
||||
* @lpmode_timeout_performance: Timeout before LPMODE entry when higher
|
||||
* performance is desired at the cost of power (typically
|
||||
* at higher OPPs)
|
||||
* @lpmode_timeout_power: Timeout before LPMODE entry when better power
|
||||
* savings is desired and performance is not important
|
||||
* (typically at lower loads indicated by lower OPPs)
|
||||
* @lpmode_freq_threshold: The DDR frequency threshold to identify between
|
||||
* the above two cases:
|
||||
* timeout = (freq >= lpmode_freq_threshold) ?
|
||||
* lpmode_timeout_performance :
|
||||
* lpmode_timeout_power;
|
||||
* @temp_alert_poll_interval_ms: LPDDR2 MR4 polling interval at nominal
|
||||
* temperature(in milliseconds). When temperature is high
|
||||
* polling is done 4 times as frequently.
|
||||
*/
|
||||
struct emif_custom_configs {
|
||||
u32 mask;
|
||||
u32 lpmode;
|
||||
u32 lpmode_timeout_performance;
|
||||
u32 lpmode_timeout_power;
|
||||
u32 lpmode_freq_threshold;
|
||||
u32 temp_alert_poll_interval_ms;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct emif_platform_data - Platform data passed on EMIF platform
|
||||
* device creation. Used by the driver.
|
||||
* @hw_caps: Hw capabilities of the EMIF IP in the respective SoC
|
||||
* @device_info: Device info structure containing information such
|
||||
* as type, bus width, density etc
|
||||
* @timings: Timings information from device datasheet passed
|
||||
* as an array of 'struct lpddr2_timings'. Can be NULL
|
||||
* if if default timings are ok
|
||||
* @timings_arr_size: Size of the timings array. Depends on the number
|
||||
* of different frequencies for which timings data
|
||||
* is provided
|
||||
* @min_tck: Minimum value of some timing parameters in terms
|
||||
* of number of cycles. Can be NULL if default values
|
||||
* are ok
|
||||
* @custom_configs: Custom configurations requested by SoC or board
|
||||
* code and the data for them. Can be NULL if default
|
||||
* configurations done by the driver are ok. See
|
||||
* documentation for 'struct emif_custom_configs' for
|
||||
* more details
|
||||
*/
|
||||
struct emif_platform_data {
|
||||
u32 hw_caps;
|
||||
struct ddr_device_info *device_info;
|
||||
const struct lpddr2_timings *timings;
|
||||
u32 timings_arr_size;
|
||||
const struct lpddr2_min_tck *min_tck;
|
||||
struct emif_custom_configs *custom_configs;
|
||||
u32 ip_rev;
|
||||
u32 phy_type;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* __LINUX_EMIF_H */
|
@ -95,8 +95,19 @@ extern int printk_needs_cpu(int cpu);
|
||||
extern void printk_tick(void);
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
asmlinkage __printf(5, 0)
|
||||
int vprintk_emit(int facility, int level,
|
||||
const char *dict, size_t dictlen,
|
||||
const char *fmt, va_list args);
|
||||
|
||||
asmlinkage __printf(1, 0)
|
||||
int vprintk(const char *fmt, va_list args);
|
||||
|
||||
asmlinkage __printf(5, 6) __cold
|
||||
asmlinkage int printk_emit(int facility, int level,
|
||||
const char *dict, size_t dictlen,
|
||||
const char *fmt, ...);
|
||||
|
||||
asmlinkage __printf(1, 2) __cold
|
||||
int printk(const char *fmt, ...);
|
||||
|
||||
@ -289,6 +300,8 @@ extern void dump_stack(void) __cold;
|
||||
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern const struct file_operations kmsg_fops;
|
||||
|
||||
enum {
|
||||
DUMP_PREFIX_NONE,
|
||||
DUMP_PREFIX_ADDRESS,
|
||||
|
@ -27,6 +27,7 @@ struct attribute {
|
||||
const char *name;
|
||||
umode_t mode;
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
bool ignore_lockdep:1;
|
||||
struct lock_class_key *key;
|
||||
struct lock_class_key skey;
|
||||
#endif
|
||||
@ -80,6 +81,17 @@ struct attribute_group {
|
||||
|
||||
#define __ATTR_NULL { .attr = { .name = NULL } }
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
#define __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) { \
|
||||
.attr = {.name = __stringify(_name), .mode = _mode, \
|
||||
.ignore_lockdep = true }, \
|
||||
.show = _show, \
|
||||
.store = _store, \
|
||||
}
|
||||
#else
|
||||
#define __ATTR_IGNORE_LOCKDEP __ATTR
|
||||
#endif
|
||||
|
||||
#define attr_name(_attr) (_attr).attr.name
|
||||
|
||||
struct file;
|
||||
|
175
include/memory/jedec_ddr.h
Normal file
175
include/memory/jedec_ddr.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Definitions for DDR memories based on JEDEC specs
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments, Inc.
|
||||
*
|
||||
* Aneesh V <aneesh@ti.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 __LINUX_JEDEC_DDR_H
|
||||
#define __LINUX_JEDEC_DDR_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* DDR Densities */
|
||||
#define DDR_DENSITY_64Mb 1
|
||||
#define DDR_DENSITY_128Mb 2
|
||||
#define DDR_DENSITY_256Mb 3
|
||||
#define DDR_DENSITY_512Mb 4
|
||||
#define DDR_DENSITY_1Gb 5
|
||||
#define DDR_DENSITY_2Gb 6
|
||||
#define DDR_DENSITY_4Gb 7
|
||||
#define DDR_DENSITY_8Gb 8
|
||||
#define DDR_DENSITY_16Gb 9
|
||||
#define DDR_DENSITY_32Gb 10
|
||||
|
||||
/* DDR type */
|
||||
#define DDR_TYPE_DDR2 1
|
||||
#define DDR_TYPE_DDR3 2
|
||||
#define DDR_TYPE_LPDDR2_S4 3
|
||||
#define DDR_TYPE_LPDDR2_S2 4
|
||||
#define DDR_TYPE_LPDDR2_NVM 5
|
||||
|
||||
/* DDR IO width */
|
||||
#define DDR_IO_WIDTH_4 1
|
||||
#define DDR_IO_WIDTH_8 2
|
||||
#define DDR_IO_WIDTH_16 3
|
||||
#define DDR_IO_WIDTH_32 4
|
||||
|
||||
/* Number of Row bits */
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
#define R11 11
|
||||
#define R12 12
|
||||
#define R13 13
|
||||
#define R14 14
|
||||
#define R15 15
|
||||
#define R16 16
|
||||
|
||||
/* Number of Column bits */
|
||||
#define C7 7
|
||||
#define C8 8
|
||||
#define C9 9
|
||||
#define C10 10
|
||||
#define C11 11
|
||||
#define C12 12
|
||||
|
||||
/* Number of Banks */
|
||||
#define B1 0
|
||||
#define B2 1
|
||||
#define B4 2
|
||||
#define B8 3
|
||||
|
||||
/* Refresh rate in nano-seconds */
|
||||
#define T_REFI_15_6 15600
|
||||
#define T_REFI_7_8 7800
|
||||
#define T_REFI_3_9 3900
|
||||
|
||||
/* tRFC values */
|
||||
#define T_RFC_90 90000
|
||||
#define T_RFC_110 110000
|
||||
#define T_RFC_130 130000
|
||||
#define T_RFC_160 160000
|
||||
#define T_RFC_210 210000
|
||||
#define T_RFC_300 300000
|
||||
#define T_RFC_350 350000
|
||||
|
||||
/* Mode register numbers */
|
||||
#define DDR_MR0 0
|
||||
#define DDR_MR1 1
|
||||
#define DDR_MR2 2
|
||||
#define DDR_MR3 3
|
||||
#define DDR_MR4 4
|
||||
#define DDR_MR5 5
|
||||
#define DDR_MR6 6
|
||||
#define DDR_MR7 7
|
||||
#define DDR_MR8 8
|
||||
#define DDR_MR9 9
|
||||
#define DDR_MR10 10
|
||||
#define DDR_MR11 11
|
||||
#define DDR_MR16 16
|
||||
#define DDR_MR17 17
|
||||
#define DDR_MR18 18
|
||||
|
||||
/*
|
||||
* LPDDR2 related defines
|
||||
*/
|
||||
|
||||
/* MR4 register fields */
|
||||
#define MR4_SDRAM_REF_RATE_SHIFT 0
|
||||
#define MR4_SDRAM_REF_RATE_MASK 7
|
||||
#define MR4_TUF_SHIFT 7
|
||||
#define MR4_TUF_MASK (1 << 7)
|
||||
|
||||
/* MR4 SDRAM Refresh Rate field values */
|
||||
#define SDRAM_TEMP_NOMINAL 0x3
|
||||
#define SDRAM_TEMP_RESERVED_4 0x4
|
||||
#define SDRAM_TEMP_HIGH_DERATE_REFRESH 0x5
|
||||
#define SDRAM_TEMP_HIGH_DERATE_REFRESH_AND_TIMINGS 0x6
|
||||
#define SDRAM_TEMP_VERY_HIGH_SHUTDOWN 0x7
|
||||
|
||||
#define NUM_DDR_ADDR_TABLE_ENTRIES 11
|
||||
#define NUM_DDR_TIMING_TABLE_ENTRIES 4
|
||||
|
||||
/* Structure for DDR addressing info from the JEDEC spec */
|
||||
struct lpddr2_addressing {
|
||||
u32 num_banks;
|
||||
u32 tREFI_ns;
|
||||
u32 tRFCab_ps;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure for timings from the LPDDR2 datasheet
|
||||
* All parameters are in pico seconds(ps) unless explicitly indicated
|
||||
* with a suffix like tRAS_max_ns below
|
||||
*/
|
||||
struct lpddr2_timings {
|
||||
u32 max_freq;
|
||||
u32 min_freq;
|
||||
u32 tRPab;
|
||||
u32 tRCD;
|
||||
u32 tWR;
|
||||
u32 tRAS_min;
|
||||
u32 tRRD;
|
||||
u32 tWTR;
|
||||
u32 tXP;
|
||||
u32 tRTP;
|
||||
u32 tCKESR;
|
||||
u32 tDQSCK_max;
|
||||
u32 tDQSCK_max_derated;
|
||||
u32 tFAW;
|
||||
u32 tZQCS;
|
||||
u32 tZQCL;
|
||||
u32 tZQinit;
|
||||
u32 tRAS_max_ns;
|
||||
};
|
||||
|
||||
/*
|
||||
* Min value for some parameters in terms of number of tCK cycles(nCK)
|
||||
* Please set to zero parameters that are not valid for a given memory
|
||||
* type
|
||||
*/
|
||||
struct lpddr2_min_tck {
|
||||
u32 tRPab;
|
||||
u32 tRCD;
|
||||
u32 tWR;
|
||||
u32 tRASmin;
|
||||
u32 tRRD;
|
||||
u32 tWTR;
|
||||
u32 tXP;
|
||||
u32 tRTP;
|
||||
u32 tCKE;
|
||||
u32 tCKESR;
|
||||
u32 tFAW;
|
||||
};
|
||||
|
||||
extern const struct lpddr2_addressing
|
||||
lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES];
|
||||
extern const struct lpddr2_timings
|
||||
lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES];
|
||||
extern const struct lpddr2_min_tck lpddr2_jedec_min_tck;
|
||||
|
||||
#endif /* __LINUX_JEDEC_DDR_H */
|
33
init/main.c
33
init/main.c
@ -226,7 +226,7 @@ static int __init loglevel(char *str)
|
||||
early_param("loglevel", loglevel);
|
||||
|
||||
/* Change NUL term back to "=", to make "param" the whole string. */
|
||||
static int __init repair_env_string(char *param, char *val)
|
||||
static int __init repair_env_string(char *param, char *val, const char *unused)
|
||||
{
|
||||
if (val) {
|
||||
/* param=val or param="val"? */
|
||||
@ -246,9 +246,9 @@ static int __init repair_env_string(char *param, char *val)
|
||||
* Unknown boot options get handed to init, unless they look like
|
||||
* unused parameters (modprobe will find them in /proc/cmdline).
|
||||
*/
|
||||
static int __init unknown_bootoption(char *param, char *val)
|
||||
static int __init unknown_bootoption(char *param, char *val, const char *unused)
|
||||
{
|
||||
repair_env_string(param, val);
|
||||
repair_env_string(param, val, unused);
|
||||
|
||||
/* Handle obsolete-style parameters */
|
||||
if (obsolete_checksetup(param))
|
||||
@ -385,7 +385,7 @@ static noinline void __init_refok rest_init(void)
|
||||
}
|
||||
|
||||
/* Check for early params. */
|
||||
static int __init do_early_param(char *param, char *val)
|
||||
static int __init do_early_param(char *param, char *val, const char *unused)
|
||||
{
|
||||
const struct obs_kernel_param *p;
|
||||
|
||||
@ -725,14 +725,14 @@ static initcall_t *initcall_levels[] __initdata = {
|
||||
};
|
||||
|
||||
static char *initcall_level_names[] __initdata = {
|
||||
"early parameters",
|
||||
"core parameters",
|
||||
"postcore parameters",
|
||||
"arch parameters",
|
||||
"subsys parameters",
|
||||
"fs parameters",
|
||||
"device parameters",
|
||||
"late parameters",
|
||||
"early",
|
||||
"core",
|
||||
"postcore",
|
||||
"arch",
|
||||
"subsys",
|
||||
"fs",
|
||||
"device",
|
||||
"late",
|
||||
};
|
||||
|
||||
static void __init do_initcall_level(int level)
|
||||
@ -745,7 +745,7 @@ static void __init do_initcall_level(int level)
|
||||
static_command_line, __start___param,
|
||||
__stop___param - __start___param,
|
||||
level, level,
|
||||
repair_env_string);
|
||||
&repair_env_string);
|
||||
|
||||
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
|
||||
do_one_initcall(*fn);
|
||||
@ -755,8 +755,13 @@ static void __init do_initcalls(void)
|
||||
{
|
||||
int level;
|
||||
|
||||
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
|
||||
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {
|
||||
pr_info("initlevel:%d=%s, %d registered initcalls\n",
|
||||
level, initcall_level_names[level],
|
||||
(int) (initcall_levels[level+1]
|
||||
- initcall_levels[level]));
|
||||
do_initcall_level(level);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2953,7 +2953,7 @@ static struct module *load_module(void __user *umod,
|
||||
|
||||
/* Module is ready to execute: parsing args may do that. */
|
||||
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
|
||||
-32768, 32767, NULL);
|
||||
-32768, 32767, &ddebug_dyndbg_module_param_cb);
|
||||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
|
@ -85,11 +85,13 @@ bool parameq(const char *a, const char *b)
|
||||
|
||||
static int parse_one(char *param,
|
||||
char *val,
|
||||
const char *doing,
|
||||
const struct kernel_param *params,
|
||||
unsigned num_params,
|
||||
s16 min_level,
|
||||
s16 max_level,
|
||||
int (*handle_unknown)(char *param, char *val))
|
||||
int (*handle_unknown)(char *param, char *val,
|
||||
const char *doing))
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
@ -104,8 +106,8 @@ static int parse_one(char *param,
|
||||
if (!val && params[i].ops->set != param_set_bool
|
||||
&& params[i].ops->set != param_set_bint)
|
||||
return -EINVAL;
|
||||
pr_debug("They are equal! Calling %p\n",
|
||||
params[i].ops->set);
|
||||
pr_debug("handling %s with %p\n", param,
|
||||
params[i].ops->set);
|
||||
mutex_lock(¶m_lock);
|
||||
err = params[i].ops->set(val, ¶ms[i]);
|
||||
mutex_unlock(¶m_lock);
|
||||
@ -114,11 +116,11 @@ static int parse_one(char *param,
|
||||
}
|
||||
|
||||
if (handle_unknown) {
|
||||
pr_debug("Unknown argument: calling %p\n", handle_unknown);
|
||||
return handle_unknown(param, val);
|
||||
pr_debug("doing %s: %s='%s'\n", doing, param, val);
|
||||
return handle_unknown(param, val, doing);
|
||||
}
|
||||
|
||||
pr_debug("Unknown argument `%s'\n", param);
|
||||
pr_debug("Unknown argument '%s'\n", param);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
@ -175,49 +177,47 @@ static char *next_arg(char *args, char **param, char **val)
|
||||
}
|
||||
|
||||
/* Args looks like "foo=bar,bar2 baz=fuz wiz". */
|
||||
int parse_args(const char *name,
|
||||
int parse_args(const char *doing,
|
||||
char *args,
|
||||
const struct kernel_param *params,
|
||||
unsigned num,
|
||||
s16 min_level,
|
||||
s16 max_level,
|
||||
int (*unknown)(char *param, char *val))
|
||||
int (*unknown)(char *param, char *val, const char *doing))
|
||||
{
|
||||
char *param, *val;
|
||||
|
||||
pr_debug("Parsing ARGS: %s\n", args);
|
||||
|
||||
/* Chew leading spaces */
|
||||
args = skip_spaces(args);
|
||||
|
||||
if (*args)
|
||||
pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);
|
||||
|
||||
while (*args) {
|
||||
int ret;
|
||||
int irq_was_disabled;
|
||||
|
||||
args = next_arg(args, ¶m, &val);
|
||||
irq_was_disabled = irqs_disabled();
|
||||
ret = parse_one(param, val, params, num,
|
||||
ret = parse_one(param, val, doing, params, num,
|
||||
min_level, max_level, unknown);
|
||||
if (irq_was_disabled && !irqs_disabled()) {
|
||||
printk(KERN_WARNING "parse_args(): option '%s' enabled "
|
||||
"irq's!\n", param);
|
||||
}
|
||||
if (irq_was_disabled && !irqs_disabled())
|
||||
pr_warn("%s: option '%s' enabled irq's!\n",
|
||||
doing, param);
|
||||
|
||||
switch (ret) {
|
||||
case -ENOENT:
|
||||
printk(KERN_ERR "%s: Unknown parameter `%s'\n",
|
||||
name, param);
|
||||
pr_err("%s: Unknown parameter `%s'\n", doing, param);
|
||||
return ret;
|
||||
case -ENOSPC:
|
||||
printk(KERN_ERR
|
||||
"%s: `%s' too large for parameter `%s'\n",
|
||||
name, val ?: "", param);
|
||||
pr_err("%s: `%s' too large for parameter `%s'\n",
|
||||
doing, val ?: "", param);
|
||||
return ret;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"%s: `%s' invalid for parameter `%s'\n",
|
||||
name, val ?: "", param);
|
||||
pr_err("%s: `%s' invalid for parameter `%s'\n",
|
||||
doing, val ?: "", param);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -263,8 +263,7 @@ STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", unsigned long, strict_strtoul);
|
||||
int param_set_charp(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (strlen(val) > 1024) {
|
||||
printk(KERN_ERR "%s: string parameter too long\n",
|
||||
kp->name);
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
@ -400,8 +399,7 @@ static int param_array(const char *name,
|
||||
int len;
|
||||
|
||||
if (*num == max) {
|
||||
printk(KERN_ERR "%s: can only take %i arguments\n",
|
||||
name, max);
|
||||
pr_err("%s: can only take %i arguments\n", name, max);
|
||||
return -EINVAL;
|
||||
}
|
||||
len = strcspn(val, ",");
|
||||
@ -420,8 +418,7 @@ static int param_array(const char *name,
|
||||
} while (save == ',');
|
||||
|
||||
if (*num < min) {
|
||||
printk(KERN_ERR "%s: needs at least %i arguments\n",
|
||||
name, min);
|
||||
pr_err("%s: needs at least %i arguments\n", name, min);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
@ -480,7 +477,7 @@ int param_set_copystring(const char *val, const struct kernel_param *kp)
|
||||
const struct kparam_string *kps = kp->str;
|
||||
|
||||
if (strlen(val)+1 > kps->maxlen) {
|
||||
printk(KERN_ERR "%s: string doesn't fit in %u chars.\n",
|
||||
pr_err("%s: string doesn't fit in %u chars.\n",
|
||||
kp->name, kps->maxlen-1);
|
||||
return -ENOSPC;
|
||||
}
|
||||
@ -750,11 +747,8 @@ static struct module_kobject * __init locate_module_kobject(const char *name)
|
||||
#endif
|
||||
if (err) {
|
||||
kobject_put(&mk->kobj);
|
||||
printk(KERN_ERR
|
||||
"Module '%s' failed add to sysfs, error number %d\n",
|
||||
pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n",
|
||||
name, err);
|
||||
printk(KERN_ERR
|
||||
"The system will be unstable now.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
1392
kernel/printk.c
1392
kernel/printk.c
File diff suppressed because it is too large
Load Diff
@ -353,6 +353,14 @@ config CORDIC
|
||||
This option provides an implementation of the CORDIC algorithm;
|
||||
calculations are in fixed point. Module will be called cordic.
|
||||
|
||||
config DDR
|
||||
bool "JEDEC DDR data"
|
||||
help
|
||||
Data from JEDEC specs for DDR SDRAM memories,
|
||||
particularly the AC timing parameters and addressing
|
||||
information. This data is useful for drivers handling
|
||||
DDR SDRAM controllers.
|
||||
|
||||
config MPILIB
|
||||
tristate
|
||||
select CLZ_TAB
|
||||
|
@ -3,12 +3,16 @@ config PRINTK_TIME
|
||||
bool "Show timing information on printks"
|
||||
depends on PRINTK
|
||||
help
|
||||
Selecting this option causes timing information to be
|
||||
included in printk output. This allows you to measure
|
||||
the interval between kernel operations, including bootup
|
||||
operations. This is useful for identifying long delays
|
||||
in kernel startup. Or add printk.time=1 at boot-time.
|
||||
See Documentation/kernel-parameters.txt
|
||||
Selecting this option causes time stamps of the printk()
|
||||
messages to be added to the output of the syslog() system
|
||||
call and at the console.
|
||||
|
||||
The timestamp is always recorded internally, and exported
|
||||
to /dev/kmsg. This flag just specifies if the timestamp should
|
||||
be included, not that the timestamp is recorded.
|
||||
|
||||
The behavior is also controlled by the kernel command line
|
||||
parameter printk.time=1. See Documentation/kernel-parameters.txt
|
||||
|
||||
config DEFAULT_MESSAGE_LOGLEVEL
|
||||
int "Default message log level (1-7)"
|
||||
@ -1205,8 +1209,13 @@ config DYNAMIC_DEBUG
|
||||
otherwise be available at runtime. These messages can then be
|
||||
enabled/disabled based on various levels of scope - per source file,
|
||||
function, module, format string, and line number. This mechanism
|
||||
implicitly enables all pr_debug() and dev_dbg() calls. The impact of
|
||||
this compile option is a larger kernel text size of about 2%.
|
||||
implicitly compiles in all pr_debug() and dev_dbg() calls, which
|
||||
enlarges the kernel text size by about 2%.
|
||||
|
||||
If a source file is compiled with DEBUG flag set, any
|
||||
pr_debug() calls in it are enabled by default, but can be
|
||||
disabled at runtime as below. Note that DEBUG flag is
|
||||
turned on by many CONFIG_*DEBUG* options.
|
||||
|
||||
Usage:
|
||||
|
||||
@ -1223,16 +1232,16 @@ config DYNAMIC_DEBUG
|
||||
lineno : line number of the debug statement
|
||||
module : module that contains the debug statement
|
||||
function : function that contains the debug statement
|
||||
flags : 'p' means the line is turned 'on' for printing
|
||||
flags : '=p' means the line is turned 'on' for printing
|
||||
format : the format used for the debug statement
|
||||
|
||||
From a live system:
|
||||
|
||||
nullarbor:~ # cat <debugfs>/dynamic_debug/control
|
||||
# filename:lineno [module]function flags format
|
||||
fs/aio.c:222 [aio]__put_ioctx - "__put_ioctx:\040freeing\040%p\012"
|
||||
fs/aio.c:248 [aio]ioctx_alloc - "ENOMEM:\040nr_events\040too\040high\012"
|
||||
fs/aio.c:1770 [aio]sys_io_cancel - "calling\040cancel\012"
|
||||
fs/aio.c:222 [aio]__put_ioctx =_ "__put_ioctx:\040freeing\040%p\012"
|
||||
fs/aio.c:248 [aio]ioctx_alloc =_ "ENOMEM:\040nr_events\040too\040high\012"
|
||||
fs/aio.c:1770 [aio]sys_io_cancel =_ "calling\040cancel\012"
|
||||
|
||||
Example usage:
|
||||
|
||||
|
@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o
|
||||
|
||||
obj-$(CONFIG_CLZ_TAB) += clz_tab.o
|
||||
|
||||
obj-$(CONFIG_DDR) += jedec_ddr_data.o
|
||||
|
||||
hostprogs-y := gen_crc32table
|
||||
clean-files := crc32table.h
|
||||
|
||||
|
@ -107,20 +107,22 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf,
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define vpr_info_dq(q, msg) \
|
||||
do { \
|
||||
if (verbose) \
|
||||
/* trim last char off format print */ \
|
||||
pr_info("%s: func=\"%s\" file=\"%s\" " \
|
||||
"module=\"%s\" format=\"%.*s\" " \
|
||||
"lineno=%u-%u", \
|
||||
msg, \
|
||||
q->function ? q->function : "", \
|
||||
q->filename ? q->filename : "", \
|
||||
q->module ? q->module : "", \
|
||||
(int)(q->format ? strlen(q->format) - 1 : 0), \
|
||||
q->format ? q->format : "", \
|
||||
q->first_lineno, q->last_lineno); \
|
||||
#define vpr_info(fmt, ...) \
|
||||
if (verbose) do { pr_info(fmt, ##__VA_ARGS__); } while (0)
|
||||
|
||||
#define vpr_info_dq(q, msg) \
|
||||
do { \
|
||||
/* trim last char off format print */ \
|
||||
vpr_info("%s: func=\"%s\" file=\"%s\" " \
|
||||
"module=\"%s\" format=\"%.*s\" " \
|
||||
"lineno=%u-%u", \
|
||||
msg, \
|
||||
q->function ? q->function : "", \
|
||||
q->filename ? q->filename : "", \
|
||||
q->module ? q->module : "", \
|
||||
(int)(q->format ? strlen(q->format) - 1 : 0), \
|
||||
q->format ? q->format : "", \
|
||||
q->first_lineno, q->last_lineno); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
@ -180,12 +182,11 @@ static int ddebug_change(const struct ddebug_query *query,
|
||||
if (newflags == dp->flags)
|
||||
continue;
|
||||
dp->flags = newflags;
|
||||
if (verbose)
|
||||
pr_info("changed %s:%d [%s]%s =%s\n",
|
||||
trim_prefix(dp->filename), dp->lineno,
|
||||
dt->mod_name, dp->function,
|
||||
ddebug_describe_flags(dp, flagbuf,
|
||||
sizeof(flagbuf)));
|
||||
vpr_info("changed %s:%d [%s]%s =%s\n",
|
||||
trim_prefix(dp->filename), dp->lineno,
|
||||
dt->mod_name, dp->function,
|
||||
ddebug_describe_flags(dp, flagbuf,
|
||||
sizeof(flagbuf)));
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ddebug_lock);
|
||||
@ -337,7 +338,7 @@ static int check_set(const char **dest, char *src, char *name)
|
||||
* Returns 0 on success, <0 on error.
|
||||
*/
|
||||
static int ddebug_parse_query(char *words[], int nwords,
|
||||
struct ddebug_query *query)
|
||||
struct ddebug_query *query, const char *modname)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
@ -347,6 +348,10 @@ static int ddebug_parse_query(char *words[], int nwords,
|
||||
return -EINVAL;
|
||||
memset(query, 0, sizeof(*query));
|
||||
|
||||
if (modname)
|
||||
/* support $modname.dyndbg=<multiple queries> */
|
||||
query->module = modname;
|
||||
|
||||
for (i = 0 ; i < nwords ; i += 2) {
|
||||
if (!strcmp(words[i], "func"))
|
||||
rc = check_set(&query->function, words[i+1], "func");
|
||||
@ -410,8 +415,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (verbose)
|
||||
pr_info("op='%c'\n", op);
|
||||
vpr_info("op='%c'\n", op);
|
||||
|
||||
for ( ; *str ; ++str) {
|
||||
for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
|
||||
@ -423,8 +427,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (verbose)
|
||||
pr_info("flags=0x%x\n", flags);
|
||||
vpr_info("flags=0x%x\n", flags);
|
||||
|
||||
/* calculate final *flagsp, *maskp according to mask and op */
|
||||
switch (op) {
|
||||
@ -441,12 +444,11 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
|
||||
*flagsp = 0;
|
||||
break;
|
||||
}
|
||||
if (verbose)
|
||||
pr_info("*flagsp=0x%x *maskp=0x%x\n", *flagsp, *maskp);
|
||||
vpr_info("*flagsp=0x%x *maskp=0x%x\n", *flagsp, *maskp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ddebug_exec_query(char *query_string)
|
||||
static int ddebug_exec_query(char *query_string, const char *modname)
|
||||
{
|
||||
unsigned int flags = 0, mask = 0;
|
||||
struct ddebug_query query;
|
||||
@ -457,7 +459,7 @@ static int ddebug_exec_query(char *query_string)
|
||||
nwords = ddebug_tokenize(query_string, words, MAXWORDS);
|
||||
if (nwords <= 0)
|
||||
return -EINVAL;
|
||||
if (ddebug_parse_query(words, nwords-1, &query))
|
||||
if (ddebug_parse_query(words, nwords-1, &query, modname))
|
||||
return -EINVAL;
|
||||
if (ddebug_parse_flags(words[nwords-1], &flags, &mask))
|
||||
return -EINVAL;
|
||||
@ -473,7 +475,7 @@ static int ddebug_exec_query(char *query_string)
|
||||
last error or number of matching callsites. Module name is either
|
||||
in param (for boot arg) or perhaps in query string.
|
||||
*/
|
||||
static int ddebug_exec_queries(char *query)
|
||||
static int ddebug_exec_queries(char *query, const char *modname)
|
||||
{
|
||||
char *split;
|
||||
int i, errs = 0, exitcode = 0, rc, nfound = 0;
|
||||
@ -487,10 +489,9 @@ static int ddebug_exec_queries(char *query)
|
||||
if (!query || !*query || *query == '#')
|
||||
continue;
|
||||
|
||||
if (verbose)
|
||||
pr_info("query %d: \"%s\"\n", i, query);
|
||||
vpr_info("query %d: \"%s\"\n", i, query);
|
||||
|
||||
rc = ddebug_exec_query(query);
|
||||
rc = ddebug_exec_query(query, modname);
|
||||
if (rc < 0) {
|
||||
errs++;
|
||||
exitcode = rc;
|
||||
@ -498,7 +499,7 @@ static int ddebug_exec_queries(char *query)
|
||||
nfound += rc;
|
||||
i++;
|
||||
}
|
||||
pr_info("processed %d queries, with %d matches, %d errs\n",
|
||||
vpr_info("processed %d queries, with %d matches, %d errs\n",
|
||||
i, nfound, errs);
|
||||
|
||||
if (exitcode)
|
||||
@ -653,10 +654,9 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
|
||||
return -EFAULT;
|
||||
}
|
||||
tmpbuf[len] = '\0';
|
||||
if (verbose)
|
||||
pr_info("read %d bytes from userspace\n", (int)len);
|
||||
vpr_info("read %d bytes from userspace\n", (int)len);
|
||||
|
||||
ret = ddebug_exec_queries(tmpbuf);
|
||||
ret = ddebug_exec_queries(tmpbuf, NULL);
|
||||
kfree(tmpbuf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -717,8 +717,7 @@ static void *ddebug_proc_start(struct seq_file *m, loff_t *pos)
|
||||
struct _ddebug *dp;
|
||||
int n = *pos;
|
||||
|
||||
if (verbose)
|
||||
pr_info("called m=%p *pos=%lld\n", m, (unsigned long long)*pos);
|
||||
vpr_info("called m=%p *pos=%lld\n", m, (unsigned long long)*pos);
|
||||
|
||||
mutex_lock(&ddebug_lock);
|
||||
|
||||
@ -742,9 +741,8 @@ static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos)
|
||||
struct ddebug_iter *iter = m->private;
|
||||
struct _ddebug *dp;
|
||||
|
||||
if (verbose)
|
||||
pr_info("called m=%p p=%p *pos=%lld\n",
|
||||
m, p, (unsigned long long)*pos);
|
||||
vpr_info("called m=%p p=%p *pos=%lld\n",
|
||||
m, p, (unsigned long long)*pos);
|
||||
|
||||
if (p == SEQ_START_TOKEN)
|
||||
dp = ddebug_iter_first(iter);
|
||||
@ -766,8 +764,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
|
||||
struct _ddebug *dp = p;
|
||||
char flagsbuf[10];
|
||||
|
||||
if (verbose)
|
||||
pr_info("called m=%p p=%p\n", m, p);
|
||||
vpr_info("called m=%p p=%p\n", m, p);
|
||||
|
||||
if (p == SEQ_START_TOKEN) {
|
||||
seq_puts(m,
|
||||
@ -791,8 +788,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
|
||||
*/
|
||||
static void ddebug_proc_stop(struct seq_file *m, void *p)
|
||||
{
|
||||
if (verbose)
|
||||
pr_info("called m=%p p=%p\n", m, p);
|
||||
vpr_info("called m=%p p=%p\n", m, p);
|
||||
mutex_unlock(&ddebug_lock);
|
||||
}
|
||||
|
||||
@ -815,8 +811,7 @@ static int ddebug_proc_open(struct inode *inode, struct file *file)
|
||||
struct ddebug_iter *iter;
|
||||
int err;
|
||||
|
||||
if (verbose)
|
||||
pr_info("called\n");
|
||||
vpr_info("called\n");
|
||||
|
||||
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
|
||||
if (iter == NULL)
|
||||
@ -866,12 +861,51 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n,
|
||||
list_add_tail(&dt->link, &ddebug_tables);
|
||||
mutex_unlock(&ddebug_lock);
|
||||
|
||||
if (verbose)
|
||||
pr_info("%u debug prints in module %s\n", n, dt->mod_name);
|
||||
vpr_info("%u debug prints in module %s\n", n, dt->mod_name);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ddebug_add_module);
|
||||
|
||||
/* helper for ddebug_dyndbg_(boot|module)_param_cb */
|
||||
static int ddebug_dyndbg_param_cb(char *param, char *val,
|
||||
const char *modname, int on_err)
|
||||
{
|
||||
char *sep;
|
||||
|
||||
sep = strchr(param, '.');
|
||||
if (sep) {
|
||||
/* needed only for ddebug_dyndbg_boot_param_cb */
|
||||
*sep = '\0';
|
||||
modname = param;
|
||||
param = sep + 1;
|
||||
}
|
||||
if (strcmp(param, "dyndbg"))
|
||||
return on_err; /* determined by caller */
|
||||
|
||||
ddebug_exec_queries((val ? val : "+p"), modname);
|
||||
|
||||
return 0; /* query failure shouldnt stop module load */
|
||||
}
|
||||
|
||||
/* handle both dyndbg and $module.dyndbg params at boot */
|
||||
static int ddebug_dyndbg_boot_param_cb(char *param, char *val,
|
||||
const char *unused)
|
||||
{
|
||||
vpr_info("%s=\"%s\"\n", param, val);
|
||||
return ddebug_dyndbg_param_cb(param, val, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* modprobe foo finds foo.params in boot-args, strips "foo.", and
|
||||
* passes them to load_module(). This callback gets unknown params,
|
||||
* processes dyndbg params, rejects others.
|
||||
*/
|
||||
int ddebug_dyndbg_module_param_cb(char *param, char *val, const char *module)
|
||||
{
|
||||
vpr_info("module: %s %s=\"%s\"\n", module, param, val);
|
||||
return ddebug_dyndbg_param_cb(param, val, module, -ENOENT);
|
||||
}
|
||||
|
||||
static void ddebug_table_free(struct ddebug_table *dt)
|
||||
{
|
||||
list_del_init(&dt->link);
|
||||
@ -888,8 +922,7 @@ int ddebug_remove_module(const char *mod_name)
|
||||
struct ddebug_table *dt, *nextdt;
|
||||
int ret = -ENOENT;
|
||||
|
||||
if (verbose)
|
||||
pr_info("removing module \"%s\"\n", mod_name);
|
||||
vpr_info("removing module \"%s\"\n", mod_name);
|
||||
|
||||
mutex_lock(&ddebug_lock);
|
||||
list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) {
|
||||
@ -940,8 +973,10 @@ static int __init dynamic_debug_init(void)
|
||||
{
|
||||
struct _ddebug *iter, *iter_start;
|
||||
const char *modname = NULL;
|
||||
char *cmdline;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
int n = 0, entries = 0, modct = 0;
|
||||
int verbose_bytes = 0;
|
||||
|
||||
if (__start___verbose == __stop___verbose) {
|
||||
pr_warn("_ddebug table is empty in a "
|
||||
@ -952,10 +987,15 @@ static int __init dynamic_debug_init(void)
|
||||
modname = iter->modname;
|
||||
iter_start = iter;
|
||||
for (; iter < __stop___verbose; iter++) {
|
||||
entries++;
|
||||
verbose_bytes += strlen(iter->modname) + strlen(iter->function)
|
||||
+ strlen(iter->filename) + strlen(iter->format);
|
||||
|
||||
if (strcmp(modname, iter->modname)) {
|
||||
modct++;
|
||||
ret = ddebug_add_module(iter_start, n, modname);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
goto out_err;
|
||||
n = 0;
|
||||
modname = iter->modname;
|
||||
iter_start = iter;
|
||||
@ -964,29 +1004,45 @@ static int __init dynamic_debug_init(void)
|
||||
}
|
||||
ret = ddebug_add_module(iter_start, n, modname);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
goto out_err;
|
||||
|
||||
/* ddebug_query boot param got passed -> set it up */
|
||||
ddebug_init_success = 1;
|
||||
vpr_info("%d modules, %d entries and %d bytes in ddebug tables,"
|
||||
" %d bytes in (readonly) verbose section\n",
|
||||
modct, entries, (int)( modct * sizeof(struct ddebug_table)),
|
||||
verbose_bytes + (int)(__stop___verbose - __start___verbose));
|
||||
|
||||
/* apply ddebug_query boot param, dont unload tables on err */
|
||||
if (ddebug_setup_string[0] != '\0') {
|
||||
ret = ddebug_exec_queries(ddebug_setup_string);
|
||||
pr_warn("ddebug_query param name is deprecated,"
|
||||
" change it to dyndbg\n");
|
||||
ret = ddebug_exec_queries(ddebug_setup_string, NULL);
|
||||
if (ret < 0)
|
||||
pr_warn("Invalid ddebug boot param %s",
|
||||
ddebug_setup_string);
|
||||
else
|
||||
pr_info("%d changes by ddebug_query\n", ret);
|
||||
|
||||
/* keep tables even on ddebug_query parse error */
|
||||
ret = 0;
|
||||
}
|
||||
/* now that ddebug tables are loaded, process all boot args
|
||||
* again to find and activate queries given in dyndbg params.
|
||||
* While this has already been done for known boot params, it
|
||||
* ignored the unknown ones (dyndbg in particular). Reusing
|
||||
* parse_args avoids ad-hoc parsing. This will also attempt
|
||||
* to activate queries for not-yet-loaded modules, which is
|
||||
* slightly noisy if verbose, but harmless.
|
||||
*/
|
||||
cmdline = kstrdup(saved_command_line, GFP_KERNEL);
|
||||
parse_args("dyndbg params", cmdline, NULL,
|
||||
0, 0, 0, &ddebug_dyndbg_boot_param_cb);
|
||||
kfree(cmdline);
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
if (ret)
|
||||
ddebug_remove_all_tables();
|
||||
else
|
||||
ddebug_init_success = 1;
|
||||
out_err:
|
||||
ddebug_remove_all_tables();
|
||||
return 0;
|
||||
}
|
||||
/* Allow early initialization for boot messages via boot param */
|
||||
arch_initcall(dynamic_debug_init);
|
||||
early_initcall(dynamic_debug_init);
|
||||
|
||||
/* Debugfs setup must be done later */
|
||||
module_init(dynamic_debug_init_debugfs);
|
||||
fs_initcall(dynamic_debug_init_debugfs);
|
||||
|
135
lib/jedec_ddr_data.c
Normal file
135
lib/jedec_ddr_data.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* DDR addressing details and AC timing parameters from JEDEC specs
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments, Inc.
|
||||
*
|
||||
* Aneesh V <aneesh@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <memory/jedec_ddr.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/* LPDDR2 addressing details from JESD209-2 section 2.4 */
|
||||
const struct lpddr2_addressing
|
||||
lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES] = {
|
||||
{B4, T_REFI_15_6, T_RFC_90}, /* 64M */
|
||||
{B4, T_REFI_15_6, T_RFC_90}, /* 128M */
|
||||
{B4, T_REFI_7_8, T_RFC_90}, /* 256M */
|
||||
{B4, T_REFI_7_8, T_RFC_90}, /* 512M */
|
||||
{B8, T_REFI_7_8, T_RFC_130}, /* 1GS4 */
|
||||
{B8, T_REFI_3_9, T_RFC_130}, /* 2GS4 */
|
||||
{B8, T_REFI_3_9, T_RFC_130}, /* 4G */
|
||||
{B8, T_REFI_3_9, T_RFC_210}, /* 8G */
|
||||
{B4, T_REFI_7_8, T_RFC_130}, /* 1GS2 */
|
||||
{B4, T_REFI_3_9, T_RFC_130}, /* 2GS2 */
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(lpddr2_jedec_addressing_table);
|
||||
|
||||
/* LPDDR2 AC timing parameters from JESD209-2 section 12 */
|
||||
const struct lpddr2_timings
|
||||
lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES] = {
|
||||
/* Speed bin 400(200 MHz) */
|
||||
[0] = {
|
||||
.max_freq = 200000000,
|
||||
.min_freq = 10000000,
|
||||
.tRPab = 21000,
|
||||
.tRCD = 18000,
|
||||
.tWR = 15000,
|
||||
.tRAS_min = 42000,
|
||||
.tRRD = 10000,
|
||||
.tWTR = 10000,
|
||||
.tXP = 7500,
|
||||
.tRTP = 7500,
|
||||
.tCKESR = 15000,
|
||||
.tDQSCK_max = 5500,
|
||||
.tFAW = 50000,
|
||||
.tZQCS = 90000,
|
||||
.tZQCL = 360000,
|
||||
.tZQinit = 1000000,
|
||||
.tRAS_max_ns = 70000,
|
||||
.tDQSCK_max_derated = 6000,
|
||||
},
|
||||
/* Speed bin 533(266 MHz) */
|
||||
[1] = {
|
||||
.max_freq = 266666666,
|
||||
.min_freq = 10000000,
|
||||
.tRPab = 21000,
|
||||
.tRCD = 18000,
|
||||
.tWR = 15000,
|
||||
.tRAS_min = 42000,
|
||||
.tRRD = 10000,
|
||||
.tWTR = 7500,
|
||||
.tXP = 7500,
|
||||
.tRTP = 7500,
|
||||
.tCKESR = 15000,
|
||||
.tDQSCK_max = 5500,
|
||||
.tFAW = 50000,
|
||||
.tZQCS = 90000,
|
||||
.tZQCL = 360000,
|
||||
.tZQinit = 1000000,
|
||||
.tRAS_max_ns = 70000,
|
||||
.tDQSCK_max_derated = 6000,
|
||||
},
|
||||
/* Speed bin 800(400 MHz) */
|
||||
[2] = {
|
||||
.max_freq = 400000000,
|
||||
.min_freq = 10000000,
|
||||
.tRPab = 21000,
|
||||
.tRCD = 18000,
|
||||
.tWR = 15000,
|
||||
.tRAS_min = 42000,
|
||||
.tRRD = 10000,
|
||||
.tWTR = 7500,
|
||||
.tXP = 7500,
|
||||
.tRTP = 7500,
|
||||
.tCKESR = 15000,
|
||||
.tDQSCK_max = 5500,
|
||||
.tFAW = 50000,
|
||||
.tZQCS = 90000,
|
||||
.tZQCL = 360000,
|
||||
.tZQinit = 1000000,
|
||||
.tRAS_max_ns = 70000,
|
||||
.tDQSCK_max_derated = 6000,
|
||||
},
|
||||
/* Speed bin 1066(533 MHz) */
|
||||
[3] = {
|
||||
.max_freq = 533333333,
|
||||
.min_freq = 10000000,
|
||||
.tRPab = 21000,
|
||||
.tRCD = 18000,
|
||||
.tWR = 15000,
|
||||
.tRAS_min = 42000,
|
||||
.tRRD = 10000,
|
||||
.tWTR = 7500,
|
||||
.tXP = 7500,
|
||||
.tRTP = 7500,
|
||||
.tCKESR = 15000,
|
||||
.tDQSCK_max = 5500,
|
||||
.tFAW = 50000,
|
||||
.tZQCS = 90000,
|
||||
.tZQCL = 360000,
|
||||
.tZQinit = 1000000,
|
||||
.tRAS_max_ns = 70000,
|
||||
.tDQSCK_max_derated = 5620,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(lpddr2_jedec_timings);
|
||||
|
||||
const struct lpddr2_min_tck lpddr2_jedec_min_tck = {
|
||||
.tRPab = 3,
|
||||
.tRCD = 3,
|
||||
.tWR = 3,
|
||||
.tRASmin = 3,
|
||||
.tRRD = 2,
|
||||
.tWTR = 2,
|
||||
.tXP = 2,
|
||||
.tRTP = 2,
|
||||
.tCKE = 3,
|
||||
.tCKESR = 3,
|
||||
.tFAW = 8
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck);
|
@ -47,13 +47,11 @@ static int populate_dir(struct kobject *kobj)
|
||||
static int create_dir(struct kobject *kobj)
|
||||
{
|
||||
int error = 0;
|
||||
if (kobject_name(kobj)) {
|
||||
error = sysfs_create_dir(kobj);
|
||||
if (!error) {
|
||||
error = populate_dir(kobj);
|
||||
if (error)
|
||||
sysfs_remove_dir(kobj);
|
||||
}
|
||||
error = sysfs_create_dir(kobj);
|
||||
if (!error) {
|
||||
error = populate_dir(kobj);
|
||||
if (error)
|
||||
sysfs_remove_dir(kobj);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@ -634,7 +632,7 @@ struct kobject *kobject_create(void)
|
||||
/**
|
||||
* kobject_create_and_add - create a struct kobject dynamically and register it with sysfs
|
||||
*
|
||||
* @name: the name for the kset
|
||||
* @name: the name for the kobject
|
||||
* @parent: the parent kobject of this kobject, if any.
|
||||
*
|
||||
* This function creates a kobject structure dynamically and registers it
|
||||
|
@ -4763,12 +4763,12 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)
|
||||
for (i = 0; i < MAX_NR_ZONES; i++) {
|
||||
if (i == ZONE_MOVABLE)
|
||||
continue;
|
||||
printk(" %-8s ", zone_names[i]);
|
||||
printk(KERN_CONT " %-8s ", zone_names[i]);
|
||||
if (arch_zone_lowest_possible_pfn[i] ==
|
||||
arch_zone_highest_possible_pfn[i])
|
||||
printk("empty\n");
|
||||
printk(KERN_CONT "empty\n");
|
||||
else
|
||||
printk("%0#10lx -> %0#10lx\n",
|
||||
printk(KERN_CONT "%0#10lx -> %0#10lx\n",
|
||||
arch_zone_lowest_possible_pfn[i],
|
||||
arch_zone_highest_possible_pfn[i]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user