diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 16:02:13 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 16:02:13 -0700 |
commit | 5d4e2d08e7fdf7339f84a1c670d296a77e02f881 (patch) | |
tree | 1c419660defa56191091dfdf50fdb57a72009173 | |
parent | fb2123fad3b499f0898835b19dbb93b18d27ee98 (diff) | |
parent | 94ca629e40eb7e997be21d8065c25e4f3797b03f (diff) | |
download | op-kernel-dev-5d4e2d08e7fdf7339f84a1c670d296a77e02f881.zip op-kernel-dev-5d4e2d08e7fdf7339f84a1c670d296a77e02f881.tar.gz |
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
...
79 files changed, 6981 insertions, 1310 deletions
diff --git a/Documentation/ABI/testing/dev-kmsg b/Documentation/ABI/testing/dev-kmsg new file mode 100644 index 0000000..281ecc5 --- /dev/null +++ b/Documentation/ABI/testing/dev-kmsg @@ -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 diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon new file mode 100644 index 0000000..20ab361 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-extcon @@ -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. diff --git a/Documentation/HOWTO b/Documentation/HOWTO index f7ade3b..59c080f 100644 --- a/Documentation/HOWTO +++ b/Documentation/HOWTO @@ -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 diff --git a/Documentation/devices.txt b/Documentation/devices.txt index 0038318..5941f51 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -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. diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt new file mode 100644 index 0000000..c25a0a5 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-mc.txt @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra30-mc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra30-mc.txt new file mode 100644 index 0000000..e47e73f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra30-mc.txt @@ -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>; + }; diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 74e6c77..6e16849 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -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) + +For display, the flags are preceded by '=' +(mnemonic: what the flags are currently equal to). -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". +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" diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class new file mode 100644 index 0000000..eb0fa5f --- /dev/null +++ b/Documentation/extcon/porting-android-switch-class @@ -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. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index e4b5775..e9abede 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -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> --------------------------- diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e275432..62aba89 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -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] diff --git a/Documentation/memory-devices/ti-emif.txt b/Documentation/memory-devices/ti-emif.txt new file mode 100644 index 0000000..f4ad9a7 --- /dev/null +++ b/Documentation/memory-devices/ti-emif.txt @@ -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. diff --git a/Documentation/zh_CN/magic-number.txt b/Documentation/zh_CN/magic-number.txt index f606ba8..4263022 100644 --- a/Documentation/zh_CN/magic-number.txt +++ b/Documentation/zh_CN/magic-number.txt @@ -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来获取他们完整的信息。 diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index fb05b12..1a6de0a 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -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; diff --git a/drivers/Kconfig b/drivers/Kconfig index d236aef..63b8182 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -140,4 +140,8 @@ source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" +source "drivers/extcon/Kconfig" + +source "drivers/memory/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 95952c8..265b506 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -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/ diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 4a29763..a128082 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -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); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index eb6fd23..06527c5 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -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. diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 26a06b8..2bcef65 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -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) diff --git a/drivers/base/core.c b/drivers/base/core.c index e28ce98..346be8b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -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); diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 524bf96..2360adb 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -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) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 8493536..765c3a2 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -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> diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 07cbbc6..05c64c1 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -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. * diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 3ec3896..207c27d 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -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, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index d6e9d08..67c3371 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -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 diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig new file mode 100644 index 0000000..29c5cf8 --- /dev/null +++ b/drivers/extcon/Kconfig @@ -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 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile new file mode 100644 index 0000000..86020bd --- /dev/null +++ b/drivers/extcon/Makefile @@ -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 diff --git a/drivers/misc/max8997-muic.c b/drivers/extcon/extcon-max8997.c index 2e7df9c..23416e4 100644 --- a/drivers/misc/max8997-muic.c +++ b/drivers/extcon/extcon-max8997.c @@ -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"); diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c new file mode 100644 index 0000000..f598a70 --- /dev/null +++ b/drivers/extcon/extcon_class.c @@ -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"); diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c new file mode 100644 index 0000000..fe7a07b --- /dev/null +++ b/drivers/extcon/extcon_gpio.c @@ -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"); diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 3dd2939..8950f62 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -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); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 9ffbfc5..2b8b8d4 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -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; - - negop = (struct icmsg_negotiate *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - 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; - } + int icframe_vercnt; + int icmsg_vercnt; + int i; + + icmsghdrp->icmsgsize = 0x10; + + negop = (struct icmsg_negotiate *)&buf[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + 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; + } - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; + 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); /* diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 15956bd..86f8885 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -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) { diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 6186025..0012eed 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -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) + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index dbb8b8e..d3ac6a4 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -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[ diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 699f0d8..b9426a6 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -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); diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig new file mode 100644 index 0000000..067f311 --- /dev/null +++ b/drivers/memory/Kconfig @@ -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 diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile new file mode 100644 index 0000000..42b3ce9 --- /dev/null +++ b/drivers/memory/Makefile @@ -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 diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c new file mode 100644 index 0000000..33a4396 --- /dev/null +++ b/drivers/memory/emif.c @@ -0,0 +1,1670 @@ +/* + * EMIF driver + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Aneesh V <aneesh@ti.com> + * Santosh Shilimkar <santosh.shilimkar@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 <linux/kernel.h> +#include <linux/reboot.h> +#include <linux/platform_data/emif_plat.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <memory/jedec_ddr.h> +#include "emif.h" + +/** + * struct emif_data - Per device static data for driver's use + * @duplicate: Whether the DDR devices attached to this EMIF + * instance are exactly same as that on EMIF1. In + * this case we can save some memory and processing + * @temperature_level: Maximum temperature of LPDDR2 devices attached + * to this EMIF - read from MR4 register. If there + * are two devices attached to this EMIF, this + * value is the maximum of the two temperature + * levels. + * @node: node in the device list + * @base: base address of memory-mapped IO registers. + * @dev: device pointer. + * @addressing table with addressing information from the spec + * @regs_cache: An array of 'struct emif_regs' that stores + * calculated register values for different + * frequencies, to avoid re-calculating them on + * each DVFS transition. + * @curr_regs: The set of register values used in the last + * frequency change (i.e. corresponding to the + * frequency in effect at the moment) + * @plat_data: Pointer to saved platform data. + * @debugfs_root: dentry to the root folder for EMIF in debugfs + */ +struct emif_data { + u8 duplicate; + u8 temperature_level; + u8 lpmode; + struct list_head node; + unsigned long irq_state; + void __iomem *base; + struct device *dev; + const struct lpddr2_addressing *addressing; + struct emif_regs *regs_cache[EMIF_MAX_NUM_FREQUENCIES]; + struct emif_regs *curr_regs; + struct emif_platform_data *plat_data; + struct dentry *debugfs_root; +}; + +static struct emif_data *emif1; +static spinlock_t emif_lock; +static unsigned long irq_state; +static u32 t_ck; /* DDR clock period in ps */ +static LIST_HEAD(device_list); + +static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif, + struct emif_regs *regs) +{ + u32 type = emif->plat_data->device_info->type; + u32 ip_rev = emif->plat_data->ip_rev; + + seq_printf(s, "EMIF register cache dump for %dMHz\n", + regs->freq/1000000); + + seq_printf(s, "ref_ctrl_shdw\t: 0x%08x\n", regs->ref_ctrl_shdw); + seq_printf(s, "sdram_tim1_shdw\t: 0x%08x\n", regs->sdram_tim1_shdw); + seq_printf(s, "sdram_tim2_shdw\t: 0x%08x\n", regs->sdram_tim2_shdw); + seq_printf(s, "sdram_tim3_shdw\t: 0x%08x\n", regs->sdram_tim3_shdw); + + if (ip_rev == EMIF_4D) { + seq_printf(s, "read_idle_ctrl_shdw_normal\t: 0x%08x\n", + regs->read_idle_ctrl_shdw_normal); + seq_printf(s, "read_idle_ctrl_shdw_volt_ramp\t: 0x%08x\n", + regs->read_idle_ctrl_shdw_volt_ramp); + } else if (ip_rev == EMIF_4D5) { + seq_printf(s, "dll_calib_ctrl_shdw_normal\t: 0x%08x\n", + regs->dll_calib_ctrl_shdw_normal); + seq_printf(s, "dll_calib_ctrl_shdw_volt_ramp\t: 0x%08x\n", + regs->dll_calib_ctrl_shdw_volt_ramp); + } + + if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4) { + seq_printf(s, "ref_ctrl_shdw_derated\t: 0x%08x\n", + regs->ref_ctrl_shdw_derated); + seq_printf(s, "sdram_tim1_shdw_derated\t: 0x%08x\n", + regs->sdram_tim1_shdw_derated); + seq_printf(s, "sdram_tim3_shdw_derated\t: 0x%08x\n", + regs->sdram_tim3_shdw_derated); + } +} + +static int emif_regdump_show(struct seq_file *s, void *unused) +{ + struct emif_data *emif = s->private; + struct emif_regs **regs_cache; + int i; + + if (emif->duplicate) + regs_cache = emif1->regs_cache; + else + regs_cache = emif->regs_cache; + + for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++) { + do_emif_regdump_show(s, emif, regs_cache[i]); + seq_printf(s, "\n"); + } + + return 0; +} + +static int emif_regdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, emif_regdump_show, inode->i_private); +} + +static const struct file_operations emif_regdump_fops = { + .open = emif_regdump_open, + .read = seq_read, + .release = single_release, +}; + +static int emif_mr4_show(struct seq_file *s, void *unused) +{ + struct emif_data *emif = s->private; + + seq_printf(s, "MR4=%d\n", emif->temperature_level); + return 0; +} + +static int emif_mr4_open(struct inode *inode, struct file *file) +{ + return single_open(file, emif_mr4_show, inode->i_private); +} + +static const struct file_operations emif_mr4_fops = { + .open = emif_mr4_open, + .read = seq_read, + .release = single_release, +}; + +static int __init_or_module emif_debugfs_init(struct emif_data *emif) +{ + struct dentry *dentry; + int ret; + + dentry = debugfs_create_dir(dev_name(emif->dev), NULL); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto err0; + } + emif->debugfs_root = dentry; + + dentry = debugfs_create_file("regcache_dump", S_IRUGO, + emif->debugfs_root, emif, &emif_regdump_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto err1; + } + + dentry = debugfs_create_file("mr4", S_IRUGO, + emif->debugfs_root, emif, &emif_mr4_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto err1; + } + + return 0; +err1: + debugfs_remove_recursive(emif->debugfs_root); +err0: + return ret; +} + +static void __exit emif_debugfs_exit(struct emif_data *emif) +{ + debugfs_remove_recursive(emif->debugfs_root); + emif->debugfs_root = NULL; +} + +/* + * Calculate the period of DDR clock from frequency value + */ +static void set_ddr_clk_period(u32 freq) +{ + /* Divide 10^12 by frequency to get period in ps */ + t_ck = (u32)DIV_ROUND_UP_ULL(1000000000000ull, freq); +} + +/* + * Get bus width used by EMIF. Note that this may be different from the + * bus width of the DDR devices used. For instance two 16-bit DDR devices + * may be connected to a given CS of EMIF. In this case bus width as far + * as EMIF is concerned is 32, where as the DDR bus width is 16 bits. + */ +static u32 get_emif_bus_width(struct emif_data *emif) +{ + u32 width; + void __iomem *base = emif->base; + + width = (readl(base + EMIF_SDRAM_CONFIG) & NARROW_MODE_MASK) + >> NARROW_MODE_SHIFT; + width = width == 0 ? 32 : 16; + + return width; +} + +/* + * Get the CL from SDRAM_CONFIG register + */ +static u32 get_cl(struct emif_data *emif) +{ + u32 cl; + void __iomem *base = emif->base; + + cl = (readl(base + EMIF_SDRAM_CONFIG) & CL_MASK) >> CL_SHIFT; + + return cl; +} + +static void set_lpmode(struct emif_data *emif, u8 lpmode) +{ + u32 temp; + void __iomem *base = emif->base; + + temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL); + temp &= ~LP_MODE_MASK; + temp |= (lpmode << LP_MODE_SHIFT); + writel(temp, base + EMIF_POWER_MANAGEMENT_CONTROL); +} + +static void do_freq_update(void) +{ + struct emif_data *emif; + + /* + * Workaround for errata i728: Disable LPMODE during FREQ_UPDATE + * + * i728 DESCRIPTION: + * The EMIF automatically puts the SDRAM into self-refresh mode + * after the EMIF has not performed accesses during + * EMIF_PWR_MGMT_CTRL[7:4] REG_SR_TIM number of DDR clock cycles + * and the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set + * to 0x2. If during a small window the following three events + * occur: + * - The SR_TIMING counter expires + * - And frequency change is requested + * - And OCP access is requested + * Then it causes instable clock on the DDR interface. + * + * WORKAROUND + * To avoid the occurrence of the three events, the workaround + * is to disable the self-refresh when requesting a frequency + * change. Before requesting a frequency change the software must + * program EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x0. When the + * frequency change has been done, the software can reprogram + * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x2 + */ + list_for_each_entry(emif, &device_list, node) { + if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH) + set_lpmode(emif, EMIF_LP_MODE_DISABLE); + } + + /* + * TODO: Do FREQ_UPDATE here when an API + * is available for this as part of the new + * clock framework + */ + + list_for_each_entry(emif, &device_list, node) { + if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH) + set_lpmode(emif, EMIF_LP_MODE_SELF_REFRESH); + } +} + +/* Find addressing table entry based on the device's type and density */ +static const struct lpddr2_addressing *get_addressing_table( + const struct ddr_device_info *device_info) +{ + u32 index, type, density; + + type = device_info->type; + density = device_info->density; + + switch (type) { + case DDR_TYPE_LPDDR2_S4: + index = density - 1; + break; + case DDR_TYPE_LPDDR2_S2: + switch (density) { + case DDR_DENSITY_1Gb: + case DDR_DENSITY_2Gb: + index = density + 3; + break; + default: + index = density - 1; + } + break; + default: + return NULL; + } + + return &lpddr2_jedec_addressing_table[index]; +} + +/* + * Find the the right timing table from the array of timing + * tables of the device using DDR clock frequency + */ +static const struct lpddr2_timings *get_timings_table(struct emif_data *emif, + u32 freq) +{ + u32 i, min, max, freq_nearest; + const struct lpddr2_timings *timings = NULL; + const struct lpddr2_timings *timings_arr = emif->plat_data->timings; + struct device *dev = emif->dev; + + /* Start with a very high frequency - 1GHz */ + freq_nearest = 1000000000; + + /* + * Find the timings table such that: + * 1. the frequency range covers the required frequency(safe) AND + * 2. the max_freq is closest to the required frequency(optimal) + */ + for (i = 0; i < emif->plat_data->timings_arr_size; i++) { + max = timings_arr[i].max_freq; + min = timings_arr[i].min_freq; + if ((freq >= min) && (freq <= max) && (max < freq_nearest)) { + freq_nearest = max; + timings = &timings_arr[i]; + } + } + + if (!timings) + dev_err(dev, "%s: couldn't find timings for - %dHz\n", + __func__, freq); + + dev_dbg(dev, "%s: timings table: freq %d, speed bin freq %d\n", + __func__, freq, freq_nearest); + + return timings; +} + +static u32 get_sdram_ref_ctrl_shdw(u32 freq, + const struct lpddr2_addressing *addressing) +{ + u32 ref_ctrl_shdw = 0, val = 0, freq_khz, t_refi; + + /* Scale down frequency and t_refi to avoid overflow */ + freq_khz = freq / 1000; + t_refi = addressing->tREFI_ns / 100; + + /* + * refresh rate to be set is 'tREFI(in us) * freq in MHz + * division by 10000 to account for change in units + */ + val = t_refi * freq_khz / 10000; + ref_ctrl_shdw |= val << REFRESH_RATE_SHIFT; + + return ref_ctrl_shdw; +} + +static u32 get_sdram_tim_1_shdw(const struct lpddr2_timings *timings, + const struct lpddr2_min_tck *min_tck, + const struct lpddr2_addressing *addressing) +{ + u32 tim1 = 0, val = 0; + + val = max(min_tck->tWTR, DIV_ROUND_UP(timings->tWTR, t_ck)) - 1; + tim1 |= val << T_WTR_SHIFT; + + if (addressing->num_banks == B8) + val = DIV_ROUND_UP(timings->tFAW, t_ck*4); + else + val = max(min_tck->tRRD, DIV_ROUND_UP(timings->tRRD, t_ck)); + tim1 |= (val - 1) << T_RRD_SHIFT; + + val = DIV_ROUND_UP(timings->tRAS_min + timings->tRPab, t_ck) - 1; + tim1 |= val << T_RC_SHIFT; + + val = max(min_tck->tRASmin, DIV_ROUND_UP(timings->tRAS_min, t_ck)); + tim1 |= (val - 1) << T_RAS_SHIFT; + + val = max(min_tck->tWR, DIV_ROUND_UP(timings->tWR, t_ck)) - 1; + tim1 |= val << T_WR_SHIFT; + + val = max(min_tck->tRCD, DIV_ROUND_UP(timings->tRCD, t_ck)) - 1; + tim1 |= val << T_RCD_SHIFT; + + val = max(min_tck->tRPab, DIV_ROUND_UP(timings->tRPab, t_ck)) - 1; + tim1 |= val << T_RP_SHIFT; + + return tim1; +} + +static u32 get_sdram_tim_1_shdw_derated(const struct lpddr2_timings *timings, + const struct lpddr2_min_tck *min_tck, + const struct lpddr2_addressing *addressing) +{ + u32 tim1 = 0, val = 0; + + val = max(min_tck->tWTR, DIV_ROUND_UP(timings->tWTR, t_ck)) - 1; + tim1 = val << T_WTR_SHIFT; + + /* + * tFAW is approximately 4 times tRRD. So add 1875*4 = 7500ps + * to tFAW for de-rating + */ + if (addressing->num_banks == B8) { + val = DIV_ROUND_UP(timings->tFAW + 7500, 4 * t_ck) - 1; + } else { + val = DIV_ROUND_UP(timings->tRRD + 1875, t_ck); + val = max(min_tck->tRRD, val) - 1; + } + tim1 |= val << T_RRD_SHIFT; + + val = DIV_ROUND_UP(timings->tRAS_min + timings->tRPab + 1875, t_ck); + tim1 |= (val - 1) << T_RC_SHIFT; + + val = DIV_ROUND_UP(timings->tRAS_min + 1875, t_ck); + val = max(min_tck->tRASmin, val) - 1; + tim1 |= val << T_RAS_SHIFT; + + val = max(min_tck->tWR, DIV_ROUND_UP(timings->tWR, t_ck)) - 1; + tim1 |= val << T_WR_SHIFT; + + val = max(min_tck->tRCD, DIV_ROUND_UP(timings->tRCD + 1875, t_ck)); + tim1 |= (val - 1) << T_RCD_SHIFT; + + val = max(min_tck->tRPab, DIV_ROUND_UP(timings->tRPab + 1875, t_ck)); + tim1 |= (val - 1) << T_RP_SHIFT; + + return tim1; +} + +static u32 get_sdram_tim_2_shdw(const struct lpddr2_timings *timings, + const struct lpddr2_min_tck *min_tck, + const struct lpddr2_addressing *addressing, + u32 type) +{ + u32 tim2 = 0, val = 0; + + val = min_tck->tCKE - 1; + tim2 |= val << T_CKE_SHIFT; + + val = max(min_tck->tRTP, DIV_ROUND_UP(timings->tRTP, t_ck)) - 1; + tim2 |= val << T_RTP_SHIFT; + + /* tXSNR = tRFCab_ps + 10 ns(tRFCab_ps for LPDDR2). */ + val = DIV_ROUND_UP(addressing->tRFCab_ps + 10000, t_ck) - 1; + tim2 |= val << T_XSNR_SHIFT; + + /* XSRD same as XSNR for LPDDR2 */ + tim2 |= val << T_XSRD_SHIFT; + + val = max(min_tck->tXP, DIV_ROUND_UP(timings->tXP, t_ck)) - 1; + tim2 |= val << T_XP_SHIFT; + + return tim2; +} + +static u32 get_sdram_tim_3_shdw(const struct lpddr2_timings *timings, + const struct lpddr2_min_tck *min_tck, + const struct lpddr2_addressing *addressing, + u32 type, u32 ip_rev, u32 derated) +{ + u32 tim3 = 0, val = 0, t_dqsck; + + val = timings->tRAS_max_ns / addressing->tREFI_ns - 1; + val = val > 0xF ? 0xF : val; + tim3 |= val << T_RAS_MAX_SHIFT; + + val = DIV_ROUND_UP(addressing->tRFCab_ps, t_ck) - 1; + tim3 |= val << T_RFC_SHIFT; + + t_dqsck = (derated == EMIF_DERATED_TIMINGS) ? + timings->tDQSCK_max_derated : timings->tDQSCK_max; + if (ip_rev == EMIF_4D5) + val = DIV_ROUND_UP(t_dqsck + 1000, t_ck) - 1; + else + val = DIV_ROUND_UP(t_dqsck, t_ck) - 1; + + tim3 |= val << T_TDQSCKMAX_SHIFT; + + val = DIV_ROUND_UP(timings->tZQCS, t_ck) - 1; + tim3 |= val << ZQ_ZQCS_SHIFT; + + val = DIV_ROUND_UP(timings->tCKESR, t_ck); + val = max(min_tck->tCKESR, val) - 1; + tim3 |= val << T_CKESR_SHIFT; + + if (ip_rev == EMIF_4D5) { + tim3 |= (EMIF_T_CSTA - 1) << T_CSTA_SHIFT; + + val = DIV_ROUND_UP(EMIF_T_PDLL_UL, 128) - 1; + tim3 |= val << T_PDLL_UL_SHIFT; + } + + return tim3; +} + +static u32 get_zq_config_reg(const struct lpddr2_addressing *addressing, + bool cs1_used, bool cal_resistors_per_cs) +{ + u32 zq = 0, val = 0; + + val = EMIF_ZQCS_INTERVAL_US * 1000 / addressing->tREFI_ns; + zq |= val << ZQ_REFINTERVAL_SHIFT; + + val = DIV_ROUND_UP(T_ZQCL_DEFAULT_NS, T_ZQCS_DEFAULT_NS) - 1; + zq |= val << ZQ_ZQCL_MULT_SHIFT; + + val = DIV_ROUND_UP(T_ZQINIT_DEFAULT_NS, T_ZQCL_DEFAULT_NS) - 1; + zq |= val << ZQ_ZQINIT_MULT_SHIFT; + + zq |= ZQ_SFEXITEN_ENABLE << ZQ_SFEXITEN_SHIFT; + + if (cal_resistors_per_cs) + zq |= ZQ_DUALCALEN_ENABLE << ZQ_DUALCALEN_SHIFT; + else + zq |= ZQ_DUALCALEN_DISABLE << ZQ_DUALCALEN_SHIFT; + + zq |= ZQ_CS0EN_MASK; /* CS0 is used for sure */ + + val = cs1_used ? 1 : 0; + zq |= val << ZQ_CS1EN_SHIFT; + + return zq; +} + +static u32 get_temp_alert_config(const struct lpddr2_addressing *addressing, + const struct emif_custom_configs *custom_configs, bool cs1_used, + u32 sdram_io_width, u32 emif_bus_width) +{ + u32 alert = 0, interval, devcnt; + + if (custom_configs && (custom_configs->mask & + EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL)) + interval = custom_configs->temp_alert_poll_interval_ms; + else + interval = TEMP_ALERT_POLL_INTERVAL_DEFAULT_MS; + + interval *= 1000000; /* Convert to ns */ + interval /= addressing->tREFI_ns; /* Convert to refresh cycles */ + alert |= (interval << TA_REFINTERVAL_SHIFT); + + /* + * sdram_io_width is in 'log2(x) - 1' form. Convert emif_bus_width + * also to this form and subtract to get TA_DEVCNT, which is + * in log2(x) form. + */ + emif_bus_width = __fls(emif_bus_width) - 1; + devcnt = emif_bus_width - sdram_io_width; + alert |= devcnt << TA_DEVCNT_SHIFT; + + /* DEVWDT is in 'log2(x) - 3' form */ + alert |= (sdram_io_width - 2) << TA_DEVWDT_SHIFT; + + alert |= 1 << TA_SFEXITEN_SHIFT; + alert |= 1 << TA_CS0EN_SHIFT; + alert |= (cs1_used ? 1 : 0) << TA_CS1EN_SHIFT; + + return alert; +} + +static u32 get_read_idle_ctrl_shdw(u8 volt_ramp) +{ + u32 idle = 0, val = 0; + + /* + * Maximum value in normal conditions and increased frequency + * when voltage is ramping + */ + if (volt_ramp) + val = READ_IDLE_INTERVAL_DVFS / t_ck / 64 - 1; + else + val = 0x1FF; + + /* + * READ_IDLE_CTRL register in EMIF4D has same offset and fields + * as DLL_CALIB_CTRL in EMIF4D5, so use the same shifts + */ + idle |= val << DLL_CALIB_INTERVAL_SHIFT; + idle |= EMIF_READ_IDLE_LEN_VAL << ACK_WAIT_SHIFT; + + return idle; +} + +static u32 get_dll_calib_ctrl_shdw(u8 volt_ramp) +{ + u32 calib = 0, val = 0; + + if (volt_ramp == DDR_VOLTAGE_RAMPING) + val = DLL_CALIB_INTERVAL_DVFS / t_ck / 16 - 1; + else + val = 0; /* Disabled when voltage is stable */ + + calib |= val << DLL_CALIB_INTERVAL_SHIFT; + calib |= DLL_CALIB_ACK_WAIT_VAL << ACK_WAIT_SHIFT; + + return calib; +} + +static u32 get_ddr_phy_ctrl_1_attilaphy_4d(const struct lpddr2_timings *timings, + u32 freq, u8 RL) +{ + u32 phy = EMIF_DDR_PHY_CTRL_1_BASE_VAL_ATTILAPHY, val = 0; + + val = RL + DIV_ROUND_UP(timings->tDQSCK_max, t_ck) - 1; + phy |= val << READ_LATENCY_SHIFT_4D; + + if (freq <= 100000000) + val = EMIF_DLL_SLAVE_DLY_CTRL_100_MHZ_AND_LESS_ATTILAPHY; + else if (freq <= 200000000) + val = EMIF_DLL_SLAVE_DLY_CTRL_200_MHZ_ATTILAPHY; + else + val = EMIF_DLL_SLAVE_DLY_CTRL_400_MHZ_ATTILAPHY; + + phy |= val << DLL_SLAVE_DLY_CTRL_SHIFT_4D; + + return phy; +} + +static u32 get_phy_ctrl_1_intelliphy_4d5(u32 freq, u8 cl) +{ + u32 phy = EMIF_DDR_PHY_CTRL_1_BASE_VAL_INTELLIPHY, half_delay; + + /* + * DLL operates at 266 MHz. If DDR frequency is near 266 MHz, + * half-delay is not needed else set half-delay + */ + if (freq >= 265000000 && freq < 267000000) + half_delay = 0; + else + half_delay = 1; + + phy |= half_delay << DLL_HALF_DELAY_SHIFT_4D5; + phy |= ((cl + DIV_ROUND_UP(EMIF_PHY_TOTAL_READ_LATENCY_INTELLIPHY_PS, + t_ck) - 1) << READ_LATENCY_SHIFT_4D5); + + return phy; +} + +static u32 get_ext_phy_ctrl_2_intelliphy_4d5(void) +{ + u32 fifo_we_slave_ratio; + + fifo_we_slave_ratio = DIV_ROUND_CLOSEST( + EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck); + + return fifo_we_slave_ratio | fifo_we_slave_ratio << 11 | + fifo_we_slave_ratio << 22; +} + +static u32 get_ext_phy_ctrl_3_intelliphy_4d5(void) +{ + u32 fifo_we_slave_ratio; + + fifo_we_slave_ratio = DIV_ROUND_CLOSEST( + EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck); + + return fifo_we_slave_ratio >> 10 | fifo_we_slave_ratio << 1 | + fifo_we_slave_ratio << 12 | fifo_we_slave_ratio << 23; +} + +static u32 get_ext_phy_ctrl_4_intelliphy_4d5(void) +{ + u32 fifo_we_slave_ratio; + + fifo_we_slave_ratio = DIV_ROUND_CLOSEST( + EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck); + + return fifo_we_slave_ratio >> 9 | fifo_we_slave_ratio << 2 | + fifo_we_slave_ratio << 13; +} + +static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev) +{ + u32 pwr_mgmt_ctrl = 0, timeout; + u32 lpmode = EMIF_LP_MODE_SELF_REFRESH; + u32 timeout_perf = EMIF_LP_MODE_TIMEOUT_PERFORMANCE; + u32 timeout_pwr = EMIF_LP_MODE_TIMEOUT_POWER; + u32 freq_threshold = EMIF_LP_MODE_FREQ_THRESHOLD; + + struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs; + + if (cust_cfgs && (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE)) { + lpmode = cust_cfgs->lpmode; + timeout_perf = cust_cfgs->lpmode_timeout_performance; + timeout_pwr = cust_cfgs->lpmode_timeout_power; + freq_threshold = cust_cfgs->lpmode_freq_threshold; + } + + /* Timeout based on DDR frequency */ + timeout = freq >= freq_threshold ? timeout_perf : timeout_pwr; + + /* The value to be set in register is "log2(timeout) - 3" */ + if (timeout < 16) { + timeout = 0; + } else { + timeout = __fls(timeout) - 3; + if (timeout & (timeout - 1)) + timeout++; + } + + switch (lpmode) { + case EMIF_LP_MODE_CLOCK_STOP: + pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) | + SR_TIM_MASK | PD_TIM_MASK; + break; + case EMIF_LP_MODE_SELF_REFRESH: + /* Workaround for errata i735 */ + if (timeout < 6) + timeout = 6; + + pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) | + CS_TIM_MASK | PD_TIM_MASK; + break; + case EMIF_LP_MODE_PWR_DN: + pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) | + CS_TIM_MASK | SR_TIM_MASK; + break; + case EMIF_LP_MODE_DISABLE: + default: + pwr_mgmt_ctrl = CS_TIM_MASK | + PD_TIM_MASK | SR_TIM_MASK; + } + + /* No CS_TIM in EMIF_4D5 */ + if (ip_rev == EMIF_4D5) + pwr_mgmt_ctrl &= ~CS_TIM_MASK; + + pwr_mgmt_ctrl |= lpmode << LP_MODE_SHIFT; + + return pwr_mgmt_ctrl; +} + +/* + * Get the temperature level of the EMIF instance: + * Reads the MR4 register of attached SDRAM parts to find out the temperature + * level. If there are two parts attached(one on each CS), then the temperature + * level for the EMIF instance is the higher of the two temperatures. + */ +static void get_temperature_level(struct emif_data *emif) +{ + u32 temp, temperature_level; + void __iomem *base; + + base = emif->base; + + /* Read mode register 4 */ + writel(DDR_MR4, base + EMIF_LPDDR2_MODE_REG_CONFIG); + temperature_level = readl(base + EMIF_LPDDR2_MODE_REG_DATA); + temperature_level = (temperature_level & MR4_SDRAM_REF_RATE_MASK) >> + MR4_SDRAM_REF_RATE_SHIFT; + + if (emif->plat_data->device_info->cs1_used) { + writel(DDR_MR4 | CS_MASK, base + EMIF_LPDDR2_MODE_REG_CONFIG); + temp = readl(base + EMIF_LPDDR2_MODE_REG_DATA); + temp = (temp & MR4_SDRAM_REF_RATE_MASK) + >> MR4_SDRAM_REF_RATE_SHIFT; + temperature_level = max(temp, temperature_level); + } + + /* treat everything less than nominal(3) in MR4 as nominal */ + if (unlikely(temperature_level < SDRAM_TEMP_NOMINAL)) + temperature_level = SDRAM_TEMP_NOMINAL; + + /* if we get reserved value in MR4 persist with the existing value */ + if (likely(temperature_level != SDRAM_TEMP_RESERVED_4)) + emif->temperature_level = temperature_level; +} + +/* + * Program EMIF shadow registers that are not dependent on temperature + * or voltage + */ +static void setup_registers(struct emif_data *emif, struct emif_regs *regs) +{ + void __iomem *base = emif->base; + + writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW); + writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW); + + /* Settings specific for EMIF4D5 */ + if (emif->plat_data->ip_rev != EMIF_4D5) + return; + writel(regs->ext_phy_ctrl_2_shdw, base + EMIF_EXT_PHY_CTRL_2_SHDW); + writel(regs->ext_phy_ctrl_3_shdw, base + EMIF_EXT_PHY_CTRL_3_SHDW); + writel(regs->ext_phy_ctrl_4_shdw, base + EMIF_EXT_PHY_CTRL_4_SHDW); +} + +/* + * When voltage ramps dll calibration and forced read idle should + * happen more often + */ +static void setup_volt_sensitive_regs(struct emif_data *emif, + struct emif_regs *regs, u32 volt_state) +{ + u32 calib_ctrl; + void __iomem *base = emif->base; + + /* + * EMIF_READ_IDLE_CTRL in EMIF4D refers to the same register as + * EMIF_DLL_CALIB_CTRL in EMIF4D5 and dll_calib_ctrl_shadow_* + * is an alias of the respective read_idle_ctrl_shdw_* (members of + * a union). So, the below code takes care of both cases + */ + if (volt_state == DDR_VOLTAGE_RAMPING) + calib_ctrl = regs->dll_calib_ctrl_shdw_volt_ramp; + else + calib_ctrl = regs->dll_calib_ctrl_shdw_normal; + + writel(calib_ctrl, base + EMIF_DLL_CALIB_CTRL_SHDW); +} + +/* + * setup_temperature_sensitive_regs() - set the timings for temperature + * sensitive registers. This happens once at initialisation time based + * on the temperature at boot time and subsequently based on the temperature + * alert interrupt. Temperature alert can happen when the temperature + * increases or drops. So this function can have the effect of either + * derating the timings or going back to nominal values. + */ +static void setup_temperature_sensitive_regs(struct emif_data *emif, + struct emif_regs *regs) +{ + u32 tim1, tim3, ref_ctrl, type; + void __iomem *base = emif->base; + u32 temperature; + + type = emif->plat_data->device_info->type; + + tim1 = regs->sdram_tim1_shdw; + tim3 = regs->sdram_tim3_shdw; + ref_ctrl = regs->ref_ctrl_shdw; + + /* No de-rating for non-lpddr2 devices */ + if (type != DDR_TYPE_LPDDR2_S2 && type != DDR_TYPE_LPDDR2_S4) + goto out; + + temperature = emif->temperature_level; + if (temperature == SDRAM_TEMP_HIGH_DERATE_REFRESH) { + ref_ctrl = regs->ref_ctrl_shdw_derated; + } else if (temperature == SDRAM_TEMP_HIGH_DERATE_REFRESH_AND_TIMINGS) { + tim1 = regs->sdram_tim1_shdw_derated; + tim3 = regs->sdram_tim3_shdw_derated; + ref_ctrl = regs->ref_ctrl_shdw_derated; + } + +out: + writel(tim1, base + EMIF_SDRAM_TIMING_1_SHDW); + writel(tim3, base + EMIF_SDRAM_TIMING_3_SHDW); + writel(ref_ctrl, base + EMIF_SDRAM_REFRESH_CTRL_SHDW); +} + +static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif) +{ + u32 old_temp_level; + irqreturn_t ret = IRQ_HANDLED; + + spin_lock_irqsave(&emif_lock, irq_state); + old_temp_level = emif->temperature_level; + get_temperature_level(emif); + + if (unlikely(emif->temperature_level == old_temp_level)) { + goto out; + } else if (!emif->curr_regs) { + dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n"); + goto out; + } + + if (emif->temperature_level < old_temp_level || + emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) { + /* + * Temperature coming down - defer handling to thread OR + * Temperature far too high - do kernel_power_off() from + * thread context + */ + ret = IRQ_WAKE_THREAD; + } else { + /* Temperature is going up - handle immediately */ + setup_temperature_sensitive_regs(emif, emif->curr_regs); + do_freq_update(); + } + +out: + spin_unlock_irqrestore(&emif_lock, irq_state); + return ret; +} + +static irqreturn_t emif_interrupt_handler(int irq, void *dev_id) +{ + u32 interrupts; + struct emif_data *emif = dev_id; + void __iomem *base = emif->base; + struct device *dev = emif->dev; + irqreturn_t ret = IRQ_HANDLED; + + /* Save the status and clear it */ + interrupts = readl(base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS); + writel(interrupts, base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS); + + /* + * Handle temperature alert + * Temperature alert should be same for all ports + * So, it's enough to process it only for one of the ports + */ + if (interrupts & TA_SYS_MASK) + ret = handle_temp_alert(base, emif); + + if (interrupts & ERR_SYS_MASK) + dev_err(dev, "Access error from SYS port - %x\n", interrupts); + + if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) { + /* Save the status and clear it */ + interrupts = readl(base + EMIF_LL_OCP_INTERRUPT_STATUS); + writel(interrupts, base + EMIF_LL_OCP_INTERRUPT_STATUS); + + if (interrupts & ERR_LL_MASK) + dev_err(dev, "Access error from LL port - %x\n", + interrupts); + } + + return ret; +} + +static irqreturn_t emif_threaded_isr(int irq, void *dev_id) +{ + struct emif_data *emif = dev_id; + + if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) { + dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n"); + kernel_power_off(); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&emif_lock, irq_state); + + if (emif->curr_regs) { + setup_temperature_sensitive_regs(emif, emif->curr_regs); + do_freq_update(); + } else { + dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n"); + } + + spin_unlock_irqrestore(&emif_lock, irq_state); + + return IRQ_HANDLED; +} + +static void clear_all_interrupts(struct emif_data *emif) +{ + void __iomem *base = emif->base; + + writel(readl(base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS), + base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS); + if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) + writel(readl(base + EMIF_LL_OCP_INTERRUPT_STATUS), + base + EMIF_LL_OCP_INTERRUPT_STATUS); +} + +static void disable_and_clear_all_interrupts(struct emif_data *emif) +{ + void __iomem *base = emif->base; + + /* Disable all interrupts */ + writel(readl(base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET), + base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR); + if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) + writel(readl(base + EMIF_LL_OCP_INTERRUPT_ENABLE_SET), + base + EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR); + + /* Clear all interrupts */ + clear_all_interrupts(emif); +} + +static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq) +{ + u32 interrupts, type; + void __iomem *base = emif->base; + + type = emif->plat_data->device_info->type; + + clear_all_interrupts(emif); + + /* Enable interrupts for SYS interface */ + interrupts = EN_ERR_SYS_MASK; + if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4) + interrupts |= EN_TA_SYS_MASK; + writel(interrupts, base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET); + + /* Enable interrupts for LL interface */ + if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) { + /* TA need not be enabled for LL */ + interrupts = EN_ERR_LL_MASK; + writel(interrupts, base + EMIF_LL_OCP_INTERRUPT_ENABLE_SET); + } + + /* setup IRQ handlers */ + return devm_request_threaded_irq(emif->dev, irq, + emif_interrupt_handler, + emif_threaded_isr, + 0, dev_name(emif->dev), + emif); + +} + +static void __init_or_module emif_onetime_settings(struct emif_data *emif) +{ + u32 pwr_mgmt_ctrl, zq, temp_alert_cfg; + void __iomem *base = emif->base; + const struct lpddr2_addressing *addressing; + const struct ddr_device_info *device_info; + + device_info = emif->plat_data->device_info; + addressing = get_addressing_table(device_info); + + /* + * Init power management settings + * We don't know the frequency yet. Use a high frequency + * value for a conservative timeout setting + */ + pwr_mgmt_ctrl = get_pwr_mgmt_ctrl(1000000000, emif, + emif->plat_data->ip_rev); + emif->lpmode = (pwr_mgmt_ctrl & LP_MODE_MASK) >> LP_MODE_SHIFT; + writel(pwr_mgmt_ctrl, base + EMIF_POWER_MANAGEMENT_CONTROL); + + /* Init ZQ calibration settings */ + zq = get_zq_config_reg(addressing, device_info->cs1_used, + device_info->cal_resistors_per_cs); + writel(zq, base + EMIF_SDRAM_OUTPUT_IMPEDANCE_CALIBRATION_CONFIG); + + /* Check temperature level temperature level*/ + get_temperature_level(emif); + if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) + dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n"); + + /* Init temperature polling */ + temp_alert_cfg = get_temp_alert_config(addressing, + emif->plat_data->custom_configs, device_info->cs1_used, + device_info->io_width, get_emif_bus_width(emif)); + writel(temp_alert_cfg, base + EMIF_TEMPERATURE_ALERT_CONFIG); + + /* + * Program external PHY control registers that are not frequency + * dependent + */ + if (emif->plat_data->phy_type != EMIF_PHY_TYPE_INTELLIPHY) + return; + writel(EMIF_EXT_PHY_CTRL_1_VAL, base + EMIF_EXT_PHY_CTRL_1_SHDW); + writel(EMIF_EXT_PHY_CTRL_5_VAL, base + EMIF_EXT_PHY_CTRL_5_SHDW); + writel(EMIF_EXT_PHY_CTRL_6_VAL, base + EMIF_EXT_PHY_CTRL_6_SHDW); + writel(EMIF_EXT_PHY_CTRL_7_VAL, base + EMIF_EXT_PHY_CTRL_7_SHDW); + writel(EMIF_EXT_PHY_CTRL_8_VAL, base + EMIF_EXT_PHY_CTRL_8_SHDW); + writel(EMIF_EXT_PHY_CTRL_9_VAL, base + EMIF_EXT_PHY_CTRL_9_SHDW); + writel(EMIF_EXT_PHY_CTRL_10_VAL, base + EMIF_EXT_PHY_CTRL_10_SHDW); + writel(EMIF_EXT_PHY_CTRL_11_VAL, base + EMIF_EXT_PHY_CTRL_11_SHDW); + writel(EMIF_EXT_PHY_CTRL_12_VAL, base + EMIF_EXT_PHY_CTRL_12_SHDW); + writel(EMIF_EXT_PHY_CTRL_13_VAL, base + EMIF_EXT_PHY_CTRL_13_SHDW); + writel(EMIF_EXT_PHY_CTRL_14_VAL, base + EMIF_EXT_PHY_CTRL_14_SHDW); + writel(EMIF_EXT_PHY_CTRL_15_VAL, base + EMIF_EXT_PHY_CTRL_15_SHDW); + writel(EMIF_EXT_PHY_CTRL_16_VAL, base + EMIF_EXT_PHY_CTRL_16_SHDW); + writel(EMIF_EXT_PHY_CTRL_17_VAL, base + EMIF_EXT_PHY_CTRL_17_SHDW); + writel(EMIF_EXT_PHY_CTRL_18_VAL, base + EMIF_EXT_PHY_CTRL_18_SHDW); + writel(EMIF_EXT_PHY_CTRL_19_VAL, base + EMIF_EXT_PHY_CTRL_19_SHDW); + writel(EMIF_EXT_PHY_CTRL_20_VAL, base + EMIF_EXT_PHY_CTRL_20_SHDW); + writel(EMIF_EXT_PHY_CTRL_21_VAL, base + EMIF_EXT_PHY_CTRL_21_SHDW); + writel(EMIF_EXT_PHY_CTRL_22_VAL, base + EMIF_EXT_PHY_CTRL_22_SHDW); + writel(EMIF_EXT_PHY_CTRL_23_VAL, base + EMIF_EXT_PHY_CTRL_23_SHDW); + writel(EMIF_EXT_PHY_CTRL_24_VAL, base + EMIF_EXT_PHY_CTRL_24_SHDW); +} + +static void get_default_timings(struct emif_data *emif) +{ + struct emif_platform_data *pd = emif->plat_data; + + pd->timings = lpddr2_jedec_timings; + pd->timings_arr_size = ARRAY_SIZE(lpddr2_jedec_timings); + + dev_warn(emif->dev, "%s: using default timings\n", __func__); +} + +static int is_dev_data_valid(u32 type, u32 density, u32 io_width, u32 phy_type, + u32 ip_rev, struct device *dev) +{ + int valid; + + valid = (type == DDR_TYPE_LPDDR2_S4 || + type == DDR_TYPE_LPDDR2_S2) + && (density >= DDR_DENSITY_64Mb + && density <= DDR_DENSITY_8Gb) + && (io_width >= DDR_IO_WIDTH_8 + && io_width <= DDR_IO_WIDTH_32); + + /* Combinations of EMIF and PHY revisions that we support today */ + switch (ip_rev) { + case EMIF_4D: + valid = valid && (phy_type == EMIF_PHY_TYPE_ATTILAPHY); + break; + case EMIF_4D5: + valid = valid && (phy_type == EMIF_PHY_TYPE_INTELLIPHY); + break; + default: + valid = 0; + } + + if (!valid) + dev_err(dev, "%s: invalid DDR details\n", __func__); + return valid; +} + +static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs, + struct device *dev) +{ + int valid = 1; + + if ((cust_cfgs->mask & EMIF_CUSTOM_CONFIG_LPMODE) && + (cust_cfgs->lpmode != EMIF_LP_MODE_DISABLE)) + valid = cust_cfgs->lpmode_freq_threshold && + cust_cfgs->lpmode_timeout_performance && + cust_cfgs->lpmode_timeout_power; + + if (cust_cfgs->mask & EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL) + valid = valid && cust_cfgs->temp_alert_poll_interval_ms; + + if (!valid) + dev_warn(dev, "%s: invalid custom configs\n", __func__); + + return valid; +} + +static struct emif_data *__init_or_module get_device_details( + struct platform_device *pdev) +{ + u32 size; + struct emif_data *emif = NULL; + struct ddr_device_info *dev_info; + struct emif_custom_configs *cust_cfgs; + struct emif_platform_data *pd; + struct device *dev; + void *temp; + + pd = pdev->dev.platform_data; + dev = &pdev->dev; + + if (!(pd && pd->device_info && is_dev_data_valid(pd->device_info->type, + pd->device_info->density, pd->device_info->io_width, + pd->phy_type, pd->ip_rev, dev))) { + dev_err(dev, "%s: invalid device data\n", __func__); + goto error; + } + + emif = devm_kzalloc(dev, sizeof(*emif), GFP_KERNEL); + temp = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL); + + if (!emif || !pd || !dev_info) { + dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__); + goto error; + } + + memcpy(temp, pd, sizeof(*pd)); + pd = temp; + memcpy(dev_info, pd->device_info, sizeof(*dev_info)); + + pd->device_info = dev_info; + emif->plat_data = pd; + emif->dev = dev; + emif->temperature_level = SDRAM_TEMP_NOMINAL; + + /* + * For EMIF instances other than EMIF1 see if the devices connected + * are exactly same as on EMIF1(which is typically the case). If so, + * mark it as a duplicate of EMIF1 and skip copying timings data. + * This will save some memory and some computation later. + */ + emif->duplicate = emif1 && (memcmp(dev_info, + emif1->plat_data->device_info, + sizeof(struct ddr_device_info)) == 0); + + if (emif->duplicate) { + pd->timings = NULL; + pd->min_tck = NULL; + goto out; + } else if (emif1) { + dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n", + __func__); + } + + /* + * Copy custom configs - ignore allocation error, if any, as + * custom_configs is not very critical + */ + cust_cfgs = pd->custom_configs; + if (cust_cfgs && is_custom_config_valid(cust_cfgs, dev)) { + temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL); + if (temp) + memcpy(temp, cust_cfgs, sizeof(*cust_cfgs)); + else + dev_warn(dev, "%s:%d: allocation error\n", __func__, + __LINE__); + pd->custom_configs = temp; + } + + /* + * Copy timings and min-tck values from platform data. If it is not + * available or if memory allocation fails, use JEDEC defaults + */ + size = sizeof(struct lpddr2_timings) * pd->timings_arr_size; + if (pd->timings) { + temp = devm_kzalloc(dev, size, GFP_KERNEL); + if (temp) { + memcpy(temp, pd->timings, sizeof(*pd->timings)); + pd->timings = temp; + } else { + dev_warn(dev, "%s:%d: allocation error\n", __func__, + __LINE__); + get_default_timings(emif); + } + } else { + get_default_timings(emif); + } + + if (pd->min_tck) { + temp = devm_kzalloc(dev, sizeof(*pd->min_tck), GFP_KERNEL); + if (temp) { + memcpy(temp, pd->min_tck, sizeof(*pd->min_tck)); + pd->min_tck = temp; + } else { + dev_warn(dev, "%s:%d: allocation error\n", __func__, + __LINE__); + pd->min_tck = &lpddr2_jedec_min_tck; + } + } else { + pd->min_tck = &lpddr2_jedec_min_tck; + } + +out: + return emif; + +error: + return NULL; +} + +static int __init_or_module emif_probe(struct platform_device *pdev) +{ + struct emif_data *emif; + struct resource *res; + int irq; + + emif = get_device_details(pdev); + if (!emif) { + pr_err("%s: error getting device data\n", __func__); + goto error; + } + + list_add(&emif->node, &device_list); + emif->addressing = get_addressing_table(emif->plat_data->device_info); + + /* Save pointers to each other in emif and device structures */ + emif->dev = &pdev->dev; + platform_set_drvdata(pdev, emif); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(emif->dev, "%s: error getting memory resource\n", + __func__); + goto error; + } + + emif->base = devm_request_and_ioremap(emif->dev, res); + if (!emif->base) { + dev_err(emif->dev, "%s: devm_request_and_ioremap() failed\n", + __func__); + goto error; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(emif->dev, "%s: error getting IRQ resource - %d\n", + __func__, irq); + goto error; + } + + emif_onetime_settings(emif); + emif_debugfs_init(emif); + disable_and_clear_all_interrupts(emif); + setup_interrupts(emif, irq); + + /* One-time actions taken on probing the first device */ + if (!emif1) { + emif1 = emif; + spin_lock_init(&emif_lock); + + /* + * TODO: register notifiers for frequency and voltage + * change here once the respective frameworks are + * available + */ + } + + dev_info(&pdev->dev, "%s: device configured with addr = %p and IRQ%d\n", + __func__, emif->base, irq); + + return 0; +error: + return -ENODEV; +} + +static int __exit emif_remove(struct platform_device *pdev) +{ + struct emif_data *emif = platform_get_drvdata(pdev); + + emif_debugfs_exit(emif); + + return 0; +} + +static void emif_shutdown(struct platform_device *pdev) +{ + struct emif_data *emif = platform_get_drvdata(pdev); + + disable_and_clear_all_interrupts(emif); +} + +static int get_emif_reg_values(struct emif_data *emif, u32 freq, + struct emif_regs *regs) +{ + u32 cs1_used, ip_rev, phy_type; + u32 cl, type; + const struct lpddr2_timings *timings; + const struct lpddr2_min_tck *min_tck; + const struct ddr_device_info *device_info; + const struct lpddr2_addressing *addressing; + struct emif_data *emif_for_calc; + struct device *dev; + const struct emif_custom_configs *custom_configs; + + dev = emif->dev; + /* + * If the devices on this EMIF instance is duplicate of EMIF1, + * use EMIF1 details for the calculation + */ + emif_for_calc = emif->duplicate ? emif1 : emif; + timings = get_timings_table(emif_for_calc, freq); + addressing = emif_for_calc->addressing; + if (!timings || !addressing) { + dev_err(dev, "%s: not enough data available for %dHz", + __func__, freq); + return -1; + } + + device_info = emif_for_calc->plat_data->device_info; + type = device_info->type; + cs1_used = device_info->cs1_used; + ip_rev = emif_for_calc->plat_data->ip_rev; + phy_type = emif_for_calc->plat_data->phy_type; + + min_tck = emif_for_calc->plat_data->min_tck; + custom_configs = emif_for_calc->plat_data->custom_configs; + + set_ddr_clk_period(freq); + + regs->ref_ctrl_shdw = get_sdram_ref_ctrl_shdw(freq, addressing); + regs->sdram_tim1_shdw = get_sdram_tim_1_shdw(timings, min_tck, + addressing); + regs->sdram_tim2_shdw = get_sdram_tim_2_shdw(timings, min_tck, + addressing, type); + regs->sdram_tim3_shdw = get_sdram_tim_3_shdw(timings, min_tck, + addressing, type, ip_rev, EMIF_NORMAL_TIMINGS); + + cl = get_cl(emif); + + if (phy_type == EMIF_PHY_TYPE_ATTILAPHY && ip_rev == EMIF_4D) { + regs->phy_ctrl_1_shdw = get_ddr_phy_ctrl_1_attilaphy_4d( + timings, freq, cl); + } else if (phy_type == EMIF_PHY_TYPE_INTELLIPHY && ip_rev == EMIF_4D5) { + regs->phy_ctrl_1_shdw = get_phy_ctrl_1_intelliphy_4d5(freq, cl); + regs->ext_phy_ctrl_2_shdw = get_ext_phy_ctrl_2_intelliphy_4d5(); + regs->ext_phy_ctrl_3_shdw = get_ext_phy_ctrl_3_intelliphy_4d5(); + regs->ext_phy_ctrl_4_shdw = get_ext_phy_ctrl_4_intelliphy_4d5(); + } else { + return -1; + } + + /* Only timeout values in pwr_mgmt_ctrl_shdw register */ + regs->pwr_mgmt_ctrl_shdw = + get_pwr_mgmt_ctrl(freq, emif_for_calc, ip_rev) & + (CS_TIM_MASK | SR_TIM_MASK | PD_TIM_MASK); + + if (ip_rev & EMIF_4D) { + regs->read_idle_ctrl_shdw_normal = + get_read_idle_ctrl_shdw(DDR_VOLTAGE_STABLE); + + regs->read_idle_ctrl_shdw_volt_ramp = + get_read_idle_ctrl_shdw(DDR_VOLTAGE_RAMPING); + } else if (ip_rev & EMIF_4D5) { + regs->dll_calib_ctrl_shdw_normal = + get_dll_calib_ctrl_shdw(DDR_VOLTAGE_STABLE); + + regs->dll_calib_ctrl_shdw_volt_ramp = + get_dll_calib_ctrl_shdw(DDR_VOLTAGE_RAMPING); + } + + if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4) { + regs->ref_ctrl_shdw_derated = get_sdram_ref_ctrl_shdw(freq / 4, + addressing); + + regs->sdram_tim1_shdw_derated = + get_sdram_tim_1_shdw_derated(timings, min_tck, + addressing); + + regs->sdram_tim3_shdw_derated = get_sdram_tim_3_shdw(timings, + min_tck, addressing, type, ip_rev, + EMIF_DERATED_TIMINGS); + } + + regs->freq = freq; + + return 0; +} + +/* + * get_regs() - gets the cached emif_regs structure for a given EMIF instance + * given frequency(freq): + * + * As an optimisation, every EMIF instance other than EMIF1 shares the + * register cache with EMIF1 if the devices connected on this instance + * are same as that on EMIF1(indicated by the duplicate flag) + * + * If we do not have an entry corresponding to the frequency given, we + * allocate a new entry and calculate the values + * + * Upon finding the right reg dump, save it in curr_regs. It can be + * directly used for thermal de-rating and voltage ramping changes. + */ +static struct emif_regs *get_regs(struct emif_data *emif, u32 freq) +{ + int i; + struct emif_regs **regs_cache; + struct emif_regs *regs = NULL; + struct device *dev; + + dev = emif->dev; + if (emif->curr_regs && emif->curr_regs->freq == freq) { + dev_dbg(dev, "%s: using curr_regs - %u Hz", __func__, freq); + return emif->curr_regs; + } + + if (emif->duplicate) + regs_cache = emif1->regs_cache; + else + regs_cache = emif->regs_cache; + + for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++) { + if (regs_cache[i]->freq == freq) { + regs = regs_cache[i]; + dev_dbg(dev, + "%s: reg dump found in reg cache for %u Hz\n", + __func__, freq); + break; + } + } + + /* + * If we don't have an entry for this frequency in the cache create one + * and calculate the values + */ + if (!regs) { + regs = devm_kzalloc(emif->dev, sizeof(*regs), GFP_ATOMIC); + if (!regs) + return NULL; + + if (get_emif_reg_values(emif, freq, regs)) { + devm_kfree(emif->dev, regs); + return NULL; + } + + /* + * Now look for an un-used entry in the cache and save the + * newly created struct. If there are no free entries + * over-write the last entry + */ + for (i = 0; i < EMIF_MAX_NUM_FREQUENCIES && regs_cache[i]; i++) + ; + + if (i >= EMIF_MAX_NUM_FREQUENCIES) { + dev_warn(dev, "%s: regs_cache full - reusing a slot!!\n", + __func__); + i = EMIF_MAX_NUM_FREQUENCIES - 1; + devm_kfree(emif->dev, regs_cache[i]); + } + regs_cache[i] = regs; + } + + return regs; +} + +static void do_volt_notify_handling(struct emif_data *emif, u32 volt_state) +{ + dev_dbg(emif->dev, "%s: voltage notification : %d", __func__, + volt_state); + + if (!emif->curr_regs) { + dev_err(emif->dev, + "%s: volt-notify before registers are ready: %d\n", + __func__, volt_state); + return; + } + + setup_volt_sensitive_regs(emif, emif->curr_regs, volt_state); +} + +/* + * TODO: voltage notify handling should be hooked up to + * regulator framework as soon as the necessary support + * is available in mainline kernel. This function is un-used + * right now. + */ +static void __attribute__((unused)) volt_notify_handling(u32 volt_state) +{ + struct emif_data *emif; + + spin_lock_irqsave(&emif_lock, irq_state); + + list_for_each_entry(emif, &device_list, node) + do_volt_notify_handling(emif, volt_state); + do_freq_update(); + + spin_unlock_irqrestore(&emif_lock, irq_state); +} + +static void do_freq_pre_notify_handling(struct emif_data *emif, u32 new_freq) +{ + struct emif_regs *regs; + + regs = get_regs(emif, new_freq); + if (!regs) + return; + + emif->curr_regs = regs; + + /* + * Update the shadow registers: + * Temperature and voltage-ramp sensitive settings are also configured + * in terms of DDR cycles. So, we need to update them too when there + * is a freq change + */ + dev_dbg(emif->dev, "%s: setting up shadow registers for %uHz", + __func__, new_freq); + setup_registers(emif, regs); + setup_temperature_sensitive_regs(emif, regs); + setup_volt_sensitive_regs(emif, regs, DDR_VOLTAGE_STABLE); + + /* + * Part of workaround for errata i728. See do_freq_update() + * for more details + */ + if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH) + set_lpmode(emif, EMIF_LP_MODE_DISABLE); +} + +/* + * TODO: frequency notify handling should be hooked up to + * clock framework as soon as the necessary support is + * available in mainline kernel. This function is un-used + * right now. + */ +static void __attribute__((unused)) freq_pre_notify_handling(u32 new_freq) +{ + struct emif_data *emif; + + /* + * NOTE: we are taking the spin-lock here and releases it + * only in post-notifier. This doesn't look good and + * Sparse complains about it, but this seems to be + * un-avoidable. We need to lock a sequence of events + * that is split between EMIF and clock framework. + * + * 1. EMIF driver updates EMIF timings in shadow registers in the + * frequency pre-notify callback from clock framework + * 2. clock framework sets up the registers for the new frequency + * 3. clock framework initiates a hw-sequence that updates + * the frequency EMIF timings synchronously. + * + * All these 3 steps should be performed as an atomic operation + * vis-a-vis similar sequence in the EMIF interrupt handler + * for temperature events. Otherwise, there could be race + * conditions that could result in incorrect EMIF timings for + * a given frequency + */ + spin_lock_irqsave(&emif_lock, irq_state); + + list_for_each_entry(emif, &device_list, node) + do_freq_pre_notify_handling(emif, new_freq); +} + +static void do_freq_post_notify_handling(struct emif_data *emif) +{ + /* + * Part of workaround for errata i728. See do_freq_update() + * for more details + */ + if (emif->lpmode == EMIF_LP_MODE_SELF_REFRESH) + set_lpmode(emif, EMIF_LP_MODE_SELF_REFRESH); +} + +/* + * TODO: frequency notify handling should be hooked up to + * clock framework as soon as the necessary support is + * available in mainline kernel. This function is un-used + * right now. + */ +static void __attribute__((unused)) freq_post_notify_handling(void) +{ + struct emif_data *emif; + + list_for_each_entry(emif, &device_list, node) + do_freq_post_notify_handling(emif); + + /* + * Lock is done in pre-notify handler. See freq_pre_notify_handling() + * for more details + */ + spin_unlock_irqrestore(&emif_lock, irq_state); +} + +static struct platform_driver emif_driver = { + .remove = __exit_p(emif_remove), + .shutdown = emif_shutdown, + .driver = { + .name = "emif", + }, +}; + +static int __init_or_module emif_register(void) +{ + return platform_driver_probe(&emif_driver, emif_probe); +} + +static void __exit emif_unregister(void) +{ + platform_driver_unregister(&emif_driver); +} + +module_init(emif_register); +module_exit(emif_unregister); +MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:emif"); +MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h new file mode 100644 index 0000000..bfe08ba --- /dev/null +++ b/drivers/memory/emif.h @@ -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 */ diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c new file mode 100644 index 0000000..3ed49c1 --- /dev/null +++ b/drivers/memory/tegra20-mc.c @@ -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); diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c new file mode 100644 index 0000000..e56ff04 --- /dev/null +++ b/drivers/memory/tegra30-mc.c @@ -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); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9684695..757fbd0 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -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" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 509d056..1628617 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -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 diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 4029563..5abffe5 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -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); diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index eb1dee2..c706635 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -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 diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 9b6c9ed..045d17b 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -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 diff --git a/drivers/staging/android/switch/Kconfig b/drivers/staging/android/switch/Kconfig deleted file mode 100644 index 36846f6..0000000 --- a/drivers/staging/android/switch/Kconfig +++ /dev/null @@ -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. diff --git a/drivers/staging/android/switch/Makefile b/drivers/staging/android/switch/Makefile deleted file mode 100644 index d76bfdc..0000000 --- a/drivers/staging/android/switch/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# Android Switch Class Driver -obj-$(CONFIG_ANDROID_SWITCH) += switch_class.o -obj-$(CONFIG_ANDROID_SWITCH_GPIO) += switch_gpio.o - diff --git a/drivers/staging/android/switch/switch.h b/drivers/staging/android/switch/switch.h deleted file mode 100644 index 4fcb310..0000000 --- a/drivers/staging/android/switch/switch.h +++ /dev/null @@ -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__ */ diff --git a/drivers/staging/android/switch/switch_class.c b/drivers/staging/android/switch/switch_class.c deleted file mode 100644 index 7468044..0000000 --- a/drivers/staging/android/switch/switch_class.c +++ /dev/null @@ -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"); diff --git a/drivers/staging/android/switch/switch_gpio.c b/drivers/staging/android/switch/switch_gpio.c deleted file mode 100644 index 38b2c2f..0000000 --- a/drivers/staging/android/switch/switch_gpio.c +++ /dev/null @@ -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"); diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index b98371d..42202cd 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -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) { diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 566d9f9..9a56e3a 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -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[] = { diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 7c8cdb8..8e813ee 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -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 = { diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 9761950..2f2e894 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -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; } diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 3135b2c..e10acc2 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -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); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 35a36d3..e6bb9b2 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -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; } diff --git a/include/linux/device.h b/include/linux/device.h index 5ad17cc..e04f577 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -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 * diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 7e3c53a..c18257b 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -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, ...) \ diff --git a/include/linux/extcon.h b/include/linux/extcon.h new file mode 100644 index 0000000..cdd4014 --- /dev/null +++ b/include/linux/extcon.h @@ -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__ */ diff --git a/include/linux/extcon/extcon_gpio.h b/include/linux/extcon/extcon_gpio.h new file mode 100644 index 0000000..a2129b7 --- /dev/null +++ b/include/linux/extcon/extcon_gpio.h @@ -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__ */ diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 6af8738..68ed7f7 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.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); diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 28726dd..b40c08c 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -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; }; diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index ea36486..1b14d25 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -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 diff --git a/include/linux/platform_data/emif_plat.h b/include/linux/platform_data/emif_plat.h new file mode 100644 index 0000000..03378ca --- /dev/null +++ b/include/linux/platform_data/emif_plat.h @@ -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 */ diff --git a/include/linux/printk.h b/include/linux/printk.h index 0525927..1bec2f7 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.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, diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 0010009..381f06d 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -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; diff --git a/include/memory/jedec_ddr.h b/include/memory/jedec_ddr.h new file mode 100644 index 0000000..ddad0f8 --- /dev/null +++ b/include/memory/jedec_ddr.h @@ -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 */ diff --git a/init/main.c b/init/main.c index cb54cd3..1ca6b32 100644 --- a/init/main.c +++ b/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); + } } /* diff --git a/kernel/module.c b/kernel/module.c index 78ac6ec..a4e6097 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -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; diff --git a/kernel/params.c b/kernel/params.c index f37d826..ed35345 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -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; } diff --git a/kernel/printk.c b/kernel/printk.c index b663c2c..32462d2 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -41,6 +41,7 @@ #include <linux/cpu.h> #include <linux/notifier.h> #include <linux/rculist.h> +#include <linux/poll.h> #include <asm/uaccess.h> @@ -54,8 +55,6 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) { } -#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) - /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL @@ -99,24 +98,6 @@ EXPORT_SYMBOL_GPL(console_drivers); static int console_locked, console_suspended; /* - * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars - * It is also used in interesting ways to provide interlocking in - * console_unlock();. - */ -static DEFINE_RAW_SPINLOCK(logbuf_lock); - -#define LOG_BUF_MASK (log_buf_len-1) -#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK]) - -/* - * The indices into log_buf are not constrained to log_buf_len - they - * must be masked before subscripting - */ -static unsigned log_start; /* Index into log_buf: next char to be read by syslog() */ -static unsigned con_start; /* Index into log_buf: next char to be sent to consoles */ -static unsigned log_end; /* Index into log_buf: most-recently-written-char + 1 */ - -/* * If exclusive_console is non-NULL then only this console is to be printed to. */ static struct console *exclusive_console; @@ -145,13 +126,491 @@ EXPORT_SYMBOL(console_set_on_cmdline); /* Flag: console code may call schedule() */ static int console_may_schedule; +/* + * The printk log buffer consists of a chain of concatenated variable + * length records. Every record starts with a record header, containing + * the overall length of the record. + * + * The heads to the first and last entry in the buffer, as well as the + * sequence numbers of these both entries are maintained when messages + * are stored.. + * + * If the heads indicate available messages, the length in the header + * tells the start next message. A length == 0 for the next message + * indicates a wrap-around to the beginning of the buffer. + * + * Every record carries the monotonic timestamp in microseconds, as well as + * the standard userspace syslog level and syslog facility. The usual + * kernel messages use LOG_KERN; userspace-injected messages always carry + * a matching syslog facility, by default LOG_USER. The origin of every + * message can be reliably determined that way. + * + * The human readable log message directly follows the message header. The + * length of the message text is stored in the header, the stored message + * is not terminated. + * + * Optionally, a message can carry a dictionary of properties (key/value pairs), + * to provide userspace with a machine-readable message context. + * + * Examples for well-defined, commonly used property names are: + * DEVICE=b12:8 device identifier + * b12:8 block dev_t + * c127:3 char dev_t + * n8 netdev ifindex + * +sound:card0 subsystem:devname + * SUBSYSTEM=pci driver-core subsystem name + * + * Valid characters in property names are [a-zA-Z0-9.-_]. The plain text value + * follows directly after a '=' character. Every property is terminated by + * a '\0' character. The last property is not terminated. + * + * Example of a message structure: + * 0000 ff 8f 00 00 00 00 00 00 monotonic time in nsec + * 0008 34 00 record is 52 bytes long + * 000a 0b 00 text is 11 bytes long + * 000c 1f 00 dictionary is 23 bytes long + * 000e 03 00 LOG_KERN (facility) LOG_ERR (level) + * 0010 69 74 27 73 20 61 20 6c "it's a l" + * 69 6e 65 "ine" + * 001b 44 45 56 49 43 "DEVIC" + * 45 3d 62 38 3a 32 00 44 "E=b8:2\0D" + * 52 49 56 45 52 3d 62 75 "RIVER=bu" + * 67 "g" + * 0032 00 00 00 padding to next message header + * + * The 'struct log' buffer header must never be directly exported to + * userspace, it is a kernel-private implementation detail that might + * need to be changed in the future, when the requirements change. + * + * /dev/kmsg exports the structured data in the following line format: + * "level,sequnum,timestamp;<message text>\n" + * + * The optional key/value pairs are attached as continuation lines starting + * with a space character and terminated by a newline. All possible + * non-prinatable characters are escaped in the "\xff" notation. + * + * Users of the export format should ignore possible additional values + * separated by ',', and find the message after the ';' character. + */ + +struct log { + u64 ts_nsec; /* timestamp in nanoseconds */ + u16 len; /* length of entire record */ + u16 text_len; /* length of text buffer */ + u16 dict_len; /* length of dictionary buffer */ + u16 level; /* syslog level + facility */ +}; + +/* + * The logbuf_lock protects kmsg buffer, indices, counters. It is also + * used in interesting ways to provide interlocking in console_unlock(); + */ +static DEFINE_RAW_SPINLOCK(logbuf_lock); + +/* the next printk record to read by syslog(READ) or /proc/kmsg */ +static u64 syslog_seq; +static u32 syslog_idx; + +/* index and sequence number of the first record stored in the buffer */ +static u64 log_first_seq; +static u32 log_first_idx; + +/* index and sequence number of the next record to store in the buffer */ +static u64 log_next_seq; #ifdef CONFIG_PRINTK +static u32 log_next_idx; + +/* the next printk record to read after the last 'clear' command */ +static u64 clear_seq; +static u32 clear_idx; + +#define LOG_LINE_MAX 1024 -static char __log_buf[__LOG_BUF_LEN]; +/* record buffer */ +#if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) +#define LOG_ALIGN 4 +#else +#define LOG_ALIGN 8 +#endif +#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) +static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); static char *log_buf = __log_buf; -static int log_buf_len = __LOG_BUF_LEN; -static unsigned logged_chars; /* Number of chars produced since last read+clear operation */ -static int saved_console_loglevel = -1; +static u32 log_buf_len = __LOG_BUF_LEN; + +/* cpu currently holding logbuf_lock */ +static volatile unsigned int logbuf_cpu = UINT_MAX; + +/* human readable text of the record */ +static char *log_text(const struct log *msg) +{ + return (char *)msg + sizeof(struct log); +} + +/* optional key/value pair dictionary attached to the record */ +static char *log_dict(const struct log *msg) +{ + return (char *)msg + sizeof(struct log) + msg->text_len; +} + +/* get record by index; idx must point to valid msg */ +static struct log *log_from_idx(u32 idx) +{ + struct log *msg = (struct log *)(log_buf + idx); + + /* + * A length == 0 record is the end of buffer marker. Wrap around and + * read the message at the start of the buffer. + */ + if (!msg->len) + return (struct log *)log_buf; + return msg; +} + +/* get next record; idx must point to valid msg */ +static u32 log_next(u32 idx) +{ + struct log *msg = (struct log *)(log_buf + idx); + + /* length == 0 indicates the end of the buffer; wrap */ + /* + * A length == 0 record is the end of buffer marker. Wrap around and + * read the message at the start of the buffer as *this* one, and + * return the one after that. + */ + if (!msg->len) { + msg = (struct log *)log_buf; + return msg->len; + } + return idx + msg->len; +} + +/* insert record into the buffer, discard old ones, update heads */ +static void log_store(int facility, int level, + const char *dict, u16 dict_len, + const char *text, u16 text_len) +{ + struct log *msg; + u32 size, pad_len; + + /* number of '\0' padding bytes to next message */ + size = sizeof(struct log) + text_len + dict_len; + pad_len = (-size) & (LOG_ALIGN - 1); + size += pad_len; + + while (log_first_seq < log_next_seq) { + u32 free; + + if (log_next_idx > log_first_idx) + free = max(log_buf_len - log_next_idx, log_first_idx); + else + free = log_first_idx - log_next_idx; + + if (free > size + sizeof(struct log)) + break; + + /* drop old messages until we have enough contiuous space */ + log_first_idx = log_next(log_first_idx); + log_first_seq++; + } + + if (log_next_idx + size + sizeof(struct log) >= log_buf_len) { + /* + * This message + an additional empty header does not fit + * at the end of the buffer. Add an empty header with len == 0 + * to signify a wrap around. + */ + memset(log_buf + log_next_idx, 0, sizeof(struct log)); + log_next_idx = 0; + } + + /* fill message */ + msg = (struct log *)(log_buf + log_next_idx); + memcpy(log_text(msg), text, text_len); + msg->text_len = text_len; + memcpy(log_dict(msg), dict, dict_len); + msg->dict_len = dict_len; + msg->level = (facility << 3) | (level & 7); + msg->ts_nsec = local_clock(); + memset(log_dict(msg) + dict_len, 0, pad_len); + msg->len = sizeof(struct log) + text_len + dict_len + pad_len; + + /* insert message */ + log_next_idx += msg->len; + log_next_seq++; +} + +/* /dev/kmsg - userspace message inject/listen interface */ +struct devkmsg_user { + u64 seq; + u32 idx; + struct mutex lock; + char buf[8192]; +}; + +static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv, + unsigned long count, loff_t pos) +{ + char *buf, *line; + int i; + int level = default_message_loglevel; + int facility = 1; /* LOG_USER */ + size_t len = iov_length(iv, count); + ssize_t ret = len; + + if (len > LOG_LINE_MAX) + return -EINVAL; + buf = kmalloc(len+1, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + line = buf; + for (i = 0; i < count; i++) { + if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len)) + goto out; + line += iv[i].iov_len; + } + + /* + * Extract and skip the syslog prefix <[0-9]*>. Coming from userspace + * the decimal value represents 32bit, the lower 3 bit are the log + * level, the rest are the log facility. + * + * If no prefix or no userspace facility is specified, we + * enforce LOG_USER, to be able to reliably distinguish + * kernel-generated messages from userspace-injected ones. + */ + line = buf; + if (line[0] == '<') { + char *endp = NULL; + + i = simple_strtoul(line+1, &endp, 10); + if (endp && endp[0] == '>') { + level = i & 7; + if (i >> 3) + facility = i >> 3; + endp++; + len -= endp - line; + line = endp; + } + } + line[len] = '\0'; + + printk_emit(facility, level, NULL, 0, "%s", line); +out: + kfree(buf); + return ret; +} + +static ssize_t devkmsg_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct devkmsg_user *user = file->private_data; + struct log *msg; + u64 ts_usec; + size_t i; + size_t len; + ssize_t ret; + + if (!user) + return -EBADF; + + mutex_lock(&user->lock); + raw_spin_lock(&logbuf_lock); + while (user->seq == log_next_seq) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + raw_spin_unlock(&logbuf_lock); + goto out; + } + + raw_spin_unlock(&logbuf_lock); + ret = wait_event_interruptible(log_wait, + user->seq != log_next_seq); + if (ret) + goto out; + raw_spin_lock(&logbuf_lock); + } + + if (user->seq < log_first_seq) { + /* our last seen message is gone, return error and reset */ + user->idx = log_first_idx; + user->seq = log_first_seq; + ret = -EPIPE; + raw_spin_unlock(&logbuf_lock); + goto out; + } + + msg = log_from_idx(user->idx); + ts_usec = msg->ts_nsec; + do_div(ts_usec, 1000); + len = sprintf(user->buf, "%u,%llu,%llu;", + msg->level, user->seq, ts_usec); + + /* escape non-printable characters */ + for (i = 0; i < msg->text_len; i++) { + unsigned char c = log_text(msg)[i]; + + if (c < ' ' || c >= 128) + len += sprintf(user->buf + len, "\\x%02x", c); + else + user->buf[len++] = c; + } + user->buf[len++] = '\n'; + + if (msg->dict_len) { + bool line = true; + + for (i = 0; i < msg->dict_len; i++) { + unsigned char c = log_dict(msg)[i]; + + if (line) { + user->buf[len++] = ' '; + line = false; + } + + if (c == '\0') { + user->buf[len++] = '\n'; + line = true; + continue; + } + + if (c < ' ' || c >= 128) { + len += sprintf(user->buf + len, "\\x%02x", c); + continue; + } + + user->buf[len++] = c; + } + user->buf[len++] = '\n'; + } + + user->idx = log_next(user->idx); + user->seq++; + raw_spin_unlock(&logbuf_lock); + + if (len > count) { + ret = -EINVAL; + goto out; + } + + if (copy_to_user(buf, user->buf, len)) { + ret = -EFAULT; + goto out; + } + ret = len; +out: + mutex_unlock(&user->lock); + return ret; +} + +static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence) +{ + struct devkmsg_user *user = file->private_data; + loff_t ret = 0; + + if (!user) + return -EBADF; + if (offset) + return -ESPIPE; + + raw_spin_lock(&logbuf_lock); + switch (whence) { + case SEEK_SET: + /* the first record */ + user->idx = log_first_idx; + user->seq = log_first_seq; + break; + case SEEK_DATA: + /* + * The first record after the last SYSLOG_ACTION_CLEAR, + * like issued by 'dmesg -c'. Reading /dev/kmsg itself + * changes no global state, and does not clear anything. + */ + user->idx = clear_idx; + user->seq = clear_seq; + break; + case SEEK_END: + /* after the last record */ + user->idx = log_next_idx; + user->seq = log_next_seq; + break; + default: + ret = -EINVAL; + } + raw_spin_unlock(&logbuf_lock); + return ret; +} + +static unsigned int devkmsg_poll(struct file *file, poll_table *wait) +{ + struct devkmsg_user *user = file->private_data; + int ret = 0; + + if (!user) + return POLLERR|POLLNVAL; + + poll_wait(file, &log_wait, wait); + + raw_spin_lock(&logbuf_lock); + if (user->seq < log_next_seq) { + /* return error when data has vanished underneath us */ + if (user->seq < log_first_seq) + ret = POLLIN|POLLRDNORM|POLLERR|POLLPRI; + ret = POLLIN|POLLRDNORM; + } + raw_spin_unlock(&logbuf_lock); + + return ret; +} + +static int devkmsg_open(struct inode *inode, struct file *file) +{ + struct devkmsg_user *user; + int err; + + /* write-only does not need any file context */ + if ((file->f_flags & O_ACCMODE) == O_WRONLY) + return 0; + + err = security_syslog(SYSLOG_ACTION_READ_ALL); + if (err) + return err; + + user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL); + if (!user) + return -ENOMEM; + + mutex_init(&user->lock); + + raw_spin_lock(&logbuf_lock); + user->idx = log_first_idx; + user->seq = log_first_seq; + raw_spin_unlock(&logbuf_lock); + + file->private_data = user; + return 0; +} + +static int devkmsg_release(struct inode *inode, struct file *file) +{ + struct devkmsg_user *user = file->private_data; + + if (!user) + return 0; + + mutex_destroy(&user->lock); + kfree(user); + return 0; +} + +const struct file_operations kmsg_fops = { + .open = devkmsg_open, + .read = devkmsg_read, + .aio_write = devkmsg_writev, + .llseek = devkmsg_llseek, + .poll = devkmsg_poll, + .release = devkmsg_release, +}; #ifdef CONFIG_KEXEC /* @@ -165,9 +624,9 @@ static int saved_console_loglevel = -1; void log_buf_kexec_setup(void) { VMCOREINFO_SYMBOL(log_buf); - VMCOREINFO_SYMBOL(log_end); VMCOREINFO_SYMBOL(log_buf_len); - VMCOREINFO_SYMBOL(logged_chars); + VMCOREINFO_SYMBOL(log_first_idx); + VMCOREINFO_SYMBOL(log_next_idx); } #endif @@ -191,7 +650,6 @@ early_param("log_buf_len", log_buf_len_setup); void __init setup_log_buf(int early) { unsigned long flags; - unsigned start, dest_idx, offset; char *new_log_buf; int free; @@ -219,20 +677,8 @@ void __init setup_log_buf(int early) log_buf_len = new_log_buf_len; log_buf = new_log_buf; new_log_buf_len = 0; - free = __LOG_BUF_LEN - log_end; - - offset = start = min(con_start, log_start); - dest_idx = 0; - while (start != log_end) { - unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1); - - log_buf[dest_idx] = __log_buf[log_idx_mask]; - start++; - dest_idx++; - } - log_start -= offset; - con_start -= offset; - log_end -= offset; + free = __LOG_BUF_LEN - log_next_idx; + memcpy(log_buf, __log_buf, __LOG_BUF_LEN); raw_spin_unlock_irqrestore(&logbuf_lock, flags); pr_info("log_buf_len: %d\n", log_buf_len); @@ -332,11 +778,202 @@ static int check_syslog_permissions(int type, bool from_file) return 0; } +#if defined(CONFIG_PRINTK_TIME) +static bool printk_time = 1; +#else +static bool printk_time; +#endif +module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR); + +static size_t print_prefix(const struct log *msg, bool syslog, char *buf) +{ + size_t len = 0; + + if (syslog) { + if (buf) { + len += sprintf(buf, "<%u>", msg->level); + } else { + len += 3; + if (msg->level > 9) + len++; + if (msg->level > 99) + len++; + } + } + + if (printk_time) { + if (buf) { + unsigned long long ts = msg->ts_nsec; + unsigned long rem_nsec = do_div(ts, 1000000000); + + len += sprintf(buf + len, "[%5lu.%06lu] ", + (unsigned long) ts, rem_nsec / 1000); + } else { + len += 15; + } + } + + return len; +} + +static size_t msg_print_text(const struct log *msg, bool syslog, + char *buf, size_t size) +{ + const char *text = log_text(msg); + size_t text_size = msg->text_len; + size_t len = 0; + + do { + const char *next = memchr(text, '\n', text_size); + size_t text_len; + + if (next) { + text_len = next - text; + next++; + text_size -= next - text; + } else { + text_len = text_size; + } + + if (buf) { + if (print_prefix(msg, syslog, NULL) + + text_len + 1>= size - len) + break; + + len += print_prefix(msg, syslog, buf + len); + memcpy(buf + len, text, text_len); + len += text_len; + buf[len++] = '\n'; + } else { + /* SYSLOG_ACTION_* buffer size only calculation */ + len += print_prefix(msg, syslog, NULL); + len += text_len + 1; + } + + text = next; + } while (text); + + return len; +} + +static int syslog_print(char __user *buf, int size) +{ + char *text; + struct log *msg; + int len; + + text = kmalloc(LOG_LINE_MAX, GFP_KERNEL); + if (!text) + return -ENOMEM; + + raw_spin_lock_irq(&logbuf_lock); + if (syslog_seq < log_first_seq) { + /* messages are gone, move to first one */ + syslog_seq = log_first_seq; + syslog_idx = log_first_idx; + } + msg = log_from_idx(syslog_idx); + len = msg_print_text(msg, true, text, LOG_LINE_MAX); + syslog_idx = log_next(syslog_idx); + syslog_seq++; + raw_spin_unlock_irq(&logbuf_lock); + + if (len > 0 && copy_to_user(buf, text, len)) + len = -EFAULT; + + kfree(text); + return len; +} + +static int syslog_print_all(char __user *buf, int size, bool clear) +{ + char *text; + int len = 0; + + text = kmalloc(LOG_LINE_MAX, GFP_KERNEL); + if (!text) + return -ENOMEM; + + raw_spin_lock_irq(&logbuf_lock); + if (buf) { + u64 next_seq; + u64 seq; + u32 idx; + + if (clear_seq < log_first_seq) { + /* messages are gone, move to first available one */ + clear_seq = log_first_seq; + clear_idx = log_first_idx; + } + + /* + * Find first record that fits, including all following records, + * into the user-provided buffer for this dump. + */ + seq = clear_seq; + idx = clear_idx; + while (seq < log_next_seq) { + struct log *msg = log_from_idx(idx); + + len += msg_print_text(msg, true, NULL, 0); + idx = log_next(idx); + seq++; + } + seq = clear_seq; + idx = clear_idx; + while (len > size && seq < log_next_seq) { + struct log *msg = log_from_idx(idx); + + len -= msg_print_text(msg, true, NULL, 0); + idx = log_next(idx); + seq++; + } + + /* last message in this dump */ + next_seq = log_next_seq; + + len = 0; + while (len >= 0 && seq < next_seq) { + struct log *msg = log_from_idx(idx); + int textlen; + + textlen = msg_print_text(msg, true, text, LOG_LINE_MAX); + if (textlen < 0) { + len = textlen; + break; + } + idx = log_next(idx); + seq++; + + raw_spin_unlock_irq(&logbuf_lock); + if (copy_to_user(buf + len, text, textlen)) + len = -EFAULT; + else + len += textlen; + raw_spin_lock_irq(&logbuf_lock); + + if (seq < log_first_seq) { + /* messages are gone, move to next one */ + seq = log_first_seq; + idx = log_first_idx; + } + } + } + + if (clear) { + clear_seq = log_next_seq; + clear_idx = log_next_idx; + } + raw_spin_unlock_irq(&logbuf_lock); + + kfree(text); + return len; +} + int do_syslog(int type, char __user *buf, int len, bool from_file) { - unsigned i, j, limit, count; - int do_clear = 0; - char c; + bool clear = false; + static int saved_console_loglevel = -1; int error; error = check_syslog_permissions(type, from_file); @@ -364,28 +1001,14 @@ int do_syslog(int type, char __user *buf, int len, bool from_file) goto out; } error = wait_event_interruptible(log_wait, - (log_start - log_end)); + syslog_seq != log_next_seq); if (error) goto out; - i = 0; - raw_spin_lock_irq(&logbuf_lock); - while (!error && (log_start != log_end) && i < len) { - c = LOG_BUF(log_start); - log_start++; - raw_spin_unlock_irq(&logbuf_lock); - error = __put_user(c,buf); - buf++; - i++; - cond_resched(); - raw_spin_lock_irq(&logbuf_lock); - } - raw_spin_unlock_irq(&logbuf_lock); - if (!error) - error = i; + error = syslog_print(buf, len); break; /* Read/clear last kernel messages */ case SYSLOG_ACTION_READ_CLEAR: - do_clear = 1; + clear = true; /* FALL THRU */ /* Read last kernel messages */ case SYSLOG_ACTION_READ_ALL: @@ -399,52 +1022,11 @@ int do_syslog(int type, char __user *buf, int len, bool from_file) error = -EFAULT; goto out; } - count = len; - if (count > log_buf_len) - count = log_buf_len; - raw_spin_lock_irq(&logbuf_lock); - if (count > logged_chars) - count = logged_chars; - if (do_clear) - logged_chars = 0; - limit = log_end; - /* - * __put_user() could sleep, and while we sleep - * printk() could overwrite the messages - * we try to copy to user space. Therefore - * the messages are copied in reverse. <manfreds> - */ - for (i = 0; i < count && !error; i++) { - j = limit-1-i; - if (j + log_buf_len < log_end) - break; - c = LOG_BUF(j); - raw_spin_unlock_irq(&logbuf_lock); - error = __put_user(c,&buf[count-1-i]); - cond_resched(); - raw_spin_lock_irq(&logbuf_lock); - } - raw_spin_unlock_irq(&logbuf_lock); - if (error) - break; - error = i; - if (i != count) { - int offset = count-error; - /* buffer overflow during copy, correct user buffer. */ - for (i = 0; i < error; i++) { - if (__get_user(c,&buf[i+offset]) || - __put_user(c,&buf[i])) { - error = -EFAULT; - break; - } - cond_resched(); - } - } + error = syslog_print_all(buf, len, clear); break; /* Clear ring buffer */ case SYSLOG_ACTION_CLEAR: - logged_chars = 0; - break; + syslog_print_all(NULL, 0, true); /* Disable logging to console */ case SYSLOG_ACTION_CONSOLE_OFF: if (saved_console_loglevel == -1) @@ -472,7 +1054,35 @@ int do_syslog(int type, char __user *buf, int len, bool from_file) break; /* Number of chars in the log buffer */ case SYSLOG_ACTION_SIZE_UNREAD: - error = log_end - log_start; + raw_spin_lock_irq(&logbuf_lock); + if (syslog_seq < log_first_seq) { + /* messages are gone, move to first one */ + syslog_seq = log_first_seq; + syslog_idx = log_first_idx; + } + if (from_file) { + /* + * Short-cut for poll(/"proc/kmsg") which simply checks + * for pending data, not the size; return the count of + * records, not the length. + */ + error = log_next_idx - syslog_idx; + } else { + u64 seq; + u32 idx; + + error = 0; + seq = syslog_seq; + idx = syslog_idx; + while (seq < log_next_seq) { + struct log *msg = log_from_idx(idx); + + error += msg_print_text(msg, true, NULL, 0); + idx = log_next(idx); + seq++; + } + } + raw_spin_unlock_irq(&logbuf_lock); break; /* Size of the log buffer */ case SYSLOG_ACTION_SIZE_BUFFER: @@ -501,29 +1111,11 @@ void kdb_syslog_data(char *syslog_data[4]) { syslog_data[0] = log_buf; syslog_data[1] = log_buf + log_buf_len; - syslog_data[2] = log_buf + log_end - - (logged_chars < log_buf_len ? logged_chars : log_buf_len); - syslog_data[3] = log_buf + log_end; + syslog_data[2] = log_buf + log_first_idx; + syslog_data[3] = log_buf + log_next_idx; } #endif /* CONFIG_KGDB_KDB */ -/* - * Call the console drivers on a range of log_buf - */ -static void __call_console_drivers(unsigned start, unsigned end) -{ - struct console *con; - - for_each_console(con) { - if (exclusive_console && con != exclusive_console) - continue; - if ((con->flags & CON_ENABLED) && con->write && - (cpu_online(smp_processor_id()) || - (con->flags & CON_ANYTIME))) - con->write(con, &LOG_BUF(start), end - start); - } -} - static bool __read_mostly ignore_loglevel; static int __init ignore_loglevel_setup(char *str) @@ -540,142 +1132,33 @@ MODULE_PARM_DESC(ignore_loglevel, "ignore loglevel setting, to" "print all kernel messages to the console."); /* - * Write out chars from start to end - 1 inclusive - */ -static void _call_console_drivers(unsigned start, - unsigned end, int msg_log_level) -{ - trace_console(&LOG_BUF(0), start, end, log_buf_len); - - if ((msg_log_level < console_loglevel || ignore_loglevel) && - console_drivers && start != end) { - if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) { - /* wrapped write */ - __call_console_drivers(start & LOG_BUF_MASK, - log_buf_len); - __call_console_drivers(0, end & LOG_BUF_MASK); - } else { - __call_console_drivers(start, end); - } - } -} - -/* - * Parse the syslog header <[0-9]*>. The decimal value represents 32bit, the - * lower 3 bit are the log level, the rest are the log facility. In case - * userspace passes usual userspace syslog messages to /dev/kmsg or - * /dev/ttyprintk, the log prefix might contain the facility. Printk needs - * to extract the correct log level for in-kernel processing, and not mangle - * the original value. - * - * If a prefix is found, the length of the prefix is returned. If 'level' is - * passed, it will be filled in with the log level without a possible facility - * value. If 'special' is passed, the special printk prefix chars are accepted - * and returned. If no valid header is found, 0 is returned and the passed - * variables are not touched. - */ -static size_t log_prefix(const char *p, unsigned int *level, char *special) -{ - unsigned int lev = 0; - char sp = '\0'; - size_t len; - - if (p[0] != '<' || !p[1]) - return 0; - if (p[2] == '>') { - /* usual single digit level number or special char */ - switch (p[1]) { - case '0' ... '7': - lev = p[1] - '0'; - break; - case 'c': /* KERN_CONT */ - case 'd': /* KERN_DEFAULT */ - sp = p[1]; - break; - default: - return 0; - } - len = 3; - } else { - /* multi digit including the level and facility number */ - char *endp = NULL; - - lev = (simple_strtoul(&p[1], &endp, 10) & 7); - if (endp == NULL || endp[0] != '>') - return 0; - len = (endp + 1) - p; - } - - /* do not accept special char if not asked for */ - if (sp && !special) - return 0; - - if (special) { - *special = sp; - /* return special char, do not touch level */ - if (sp) - return len; - } - - if (level) - *level = lev; - return len; -} - -/* * Call the console drivers, asking them to write out * log_buf[start] to log_buf[end - 1]. * The console_lock must be held. */ -static void call_console_drivers(unsigned start, unsigned end) +static void call_console_drivers(int level, const char *text, size_t len) { - unsigned cur_index, start_print; - static int msg_level = -1; + struct console *con; - BUG_ON(((int)(start - end)) > 0); + trace_console(text, 0, len, len); - cur_index = start; - start_print = start; - while (cur_index != end) { - if (msg_level < 0 && ((end - cur_index) > 2)) { - /* strip log prefix */ - cur_index += log_prefix(&LOG_BUF(cur_index), &msg_level, NULL); - start_print = cur_index; - } - while (cur_index != end) { - char c = LOG_BUF(cur_index); - - cur_index++; - if (c == '\n') { - if (msg_level < 0) { - /* - * printk() has already given us loglevel tags in - * the buffer. This code is here in case the - * log buffer has wrapped right round and scribbled - * on those tags - */ - msg_level = default_message_loglevel; - } - _call_console_drivers(start_print, cur_index, msg_level); - msg_level = -1; - start_print = cur_index; - break; - } - } - } - _call_console_drivers(start_print, end, msg_level); -} + if (level >= console_loglevel && !ignore_loglevel) + return; + if (!console_drivers) + return; -static void emit_log_char(char c) -{ - LOG_BUF(log_end) = c; - log_end++; - if (log_end - log_start > log_buf_len) - log_start = log_end - log_buf_len; - if (log_end - con_start > log_buf_len) - con_start = log_end - log_buf_len; - if (logged_chars < log_buf_len) - logged_chars++; + for_each_console(con) { + if (exclusive_console && con != exclusive_console) + continue; + if (!(con->flags & CON_ENABLED)) + continue; + if (!con->write) + continue; + if (!cpu_online(smp_processor_id()) && + !(con->flags & CON_ANYTIME)) + continue; + con->write(con, text, len); + } } /* @@ -700,16 +1183,6 @@ static void zap_locks(void) sema_init(&console_sem, 1); } -#if defined(CONFIG_PRINTK_TIME) -static bool printk_time = 1; -#else -static bool printk_time = 0; -#endif -module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR); - -static bool always_kmsg_dump; -module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR); - /* Check if we have any console registered that can be called early in boot. */ static int have_callable_console(void) { @@ -722,51 +1195,6 @@ static int have_callable_console(void) return 0; } -/** - * printk - print a kernel message - * @fmt: format string - * - * This is printk(). It can be called from any context. We want it to work. - * - * We try to grab the console_lock. If we succeed, it's easy - we log the output and - * call the console drivers. If we fail to get the semaphore we place the output - * into the log buffer and return. The current holder of the console_sem will - * notice the new output in console_unlock(); and will send it to the - * consoles before releasing the lock. - * - * One effect of this deferred printing is that code which calls printk() and - * then changes console_loglevel may break. This is because console_loglevel - * is inspected when the actual printing occurs. - * - * See also: - * printf(3) - * - * See the vsnprintf() documentation for format string extensions over C99. - */ - -asmlinkage int printk(const char *fmt, ...) -{ - va_list args; - int r; - -#ifdef CONFIG_KGDB_KDB - if (unlikely(kdb_trap_printk)) { - va_start(args, fmt); - r = vkdb_printf(fmt, args); - va_end(args); - return r; - } -#endif - va_start(args, fmt); - r = vprintk(fmt, args); - va_end(args); - - return r; -} - -/* cpu currently holding logbuf_lock */ -static volatile unsigned int printk_cpu = UINT_MAX; - /* * Can we actually use the console at this time on this cpu? * @@ -810,17 +1238,12 @@ static int console_trylock_for_printk(unsigned int cpu) retval = 0; } } - printk_cpu = UINT_MAX; + logbuf_cpu = UINT_MAX; if (wake) up(&console_sem); raw_spin_unlock(&logbuf_lock); return retval; } -static const char recursion_bug_msg [] = - KERN_CRIT "BUG: recent printk recursion!\n"; -static int recursion_bug; -static int new_text_line = 1; -static char printk_buf[1024]; int printk_delay_msec __read_mostly; @@ -836,15 +1259,23 @@ static inline void printk_delay(void) } } -asmlinkage int vprintk(const char *fmt, va_list args) +asmlinkage int vprintk_emit(int facility, int level, + const char *dict, size_t dictlen, + const char *fmt, va_list args) { - int printed_len = 0; - int current_log_level = default_message_loglevel; + static int recursion_bug; + static char cont_buf[LOG_LINE_MAX]; + static size_t cont_len; + static int cont_level; + static struct task_struct *cont_task; + static char textbuf[LOG_LINE_MAX]; + char *text = textbuf; + size_t text_len; unsigned long flags; int this_cpu; - char *p; - size_t plen; - char special; + bool newline = false; + bool prefix = false; + int printed_len = 0; boot_delay_msec(); printk_delay(); @@ -856,7 +1287,7 @@ asmlinkage int vprintk(const char *fmt, va_list args) /* * Ouch, printk recursed into itself! */ - if (unlikely(printk_cpu == this_cpu)) { + if (unlikely(logbuf_cpu == this_cpu)) { /* * If a crash is occurring during printk() on this CPU, * then try to get the crash message out but make sure @@ -873,97 +1304,110 @@ asmlinkage int vprintk(const char *fmt, va_list args) lockdep_off(); raw_spin_lock(&logbuf_lock); - printk_cpu = this_cpu; + logbuf_cpu = this_cpu; if (recursion_bug) { + static const char recursion_msg[] = + "BUG: recent printk recursion!"; + recursion_bug = 0; - strcpy(printk_buf, recursion_bug_msg); - printed_len = strlen(recursion_bug_msg); + printed_len += strlen(recursion_msg); + /* emit KERN_CRIT message */ + log_store(0, 2, NULL, 0, recursion_msg, printed_len); } - /* Emit the output into the temporary buffer */ - printed_len += vscnprintf(printk_buf + printed_len, - sizeof(printk_buf) - printed_len, fmt, args); - p = printk_buf; + /* + * The printf needs to come first; we need the syslog + * prefix which might be passed-in as a parameter. + */ + text_len = vscnprintf(text, sizeof(textbuf), fmt, args); - /* Read log level and handle special printk prefix */ - plen = log_prefix(p, ¤t_log_level, &special); - if (plen) { - p += plen; + /* mark and strip a trailing newline */ + if (text_len && text[text_len-1] == '\n') { + text_len--; + newline = true; + } - switch (special) { - case 'c': /* Strip <c> KERN_CONT, continue line */ - plen = 0; - break; - case 'd': /* Strip <d> KERN_DEFAULT, start new line */ - plen = 0; - default: - if (!new_text_line) { - emit_log_char('\n'); - new_text_line = 1; - } + /* strip syslog prefix and extract log level or control flags */ + if (text[0] == '<' && text[1] && text[2] == '>') { + switch (text[1]) { + case '0' ... '7': + if (level == -1) + level = text[1] - '0'; + case 'd': /* KERN_DEFAULT */ + prefix = true; + case 'c': /* KERN_CONT */ + text += 3; + text_len -= 3; } } - /* - * Copy the output into log_buf. If the caller didn't provide - * the appropriate log prefix, we insert them here - */ - for (; *p; p++) { - if (new_text_line) { - new_text_line = 0; - - if (plen) { - /* Copy original log prefix */ - int i; - - for (i = 0; i < plen; i++) - emit_log_char(printk_buf[i]); - printed_len += plen; - } else { - /* Add log prefix */ - emit_log_char('<'); - emit_log_char(current_log_level + '0'); - emit_log_char('>'); - printed_len += 3; - } + if (level == -1) + level = default_message_loglevel; - if (printk_time) { - /* Add the current time stamp */ - char tbuf[50], *tp; - unsigned tlen; - unsigned long long t; - unsigned long nanosec_rem; - - t = cpu_clock(printk_cpu); - nanosec_rem = do_div(t, 1000000000); - tlen = sprintf(tbuf, "[%5lu.%06lu] ", - (unsigned long) t, - nanosec_rem / 1000); - - for (tp = tbuf; tp < tbuf + tlen; tp++) - emit_log_char(*tp); - printed_len += tlen; - } + if (dict) { + prefix = true; + newline = true; + } - if (!*p) - break; + if (!newline) { + if (cont_len && (prefix || cont_task != current)) { + /* + * Flush earlier buffer, which is either from a + * different thread, or when we got a new prefix. + */ + log_store(facility, cont_level, NULL, 0, cont_buf, cont_len); + cont_len = 0; } - emit_log_char(*p); - if (*p == '\n') - new_text_line = 1; + if (!cont_len) { + cont_level = level; + cont_task = current; + } + + /* buffer or append to earlier buffer from the same thread */ + if (cont_len + text_len > sizeof(cont_buf)) + text_len = sizeof(cont_buf) - cont_len; + memcpy(cont_buf + cont_len, text, text_len); + cont_len += text_len; + } else { + if (cont_len && cont_task == current) { + if (prefix) { + /* + * New prefix from the same thread; flush. We + * either got no earlier newline, or we race + * with an interrupt. + */ + log_store(facility, cont_level, + NULL, 0, cont_buf, cont_len); + cont_len = 0; + } + + /* append to the earlier buffer and flush */ + if (cont_len + text_len > sizeof(cont_buf)) + text_len = sizeof(cont_buf) - cont_len; + memcpy(cont_buf + cont_len, text, text_len); + cont_len += text_len; + log_store(facility, cont_level, + NULL, 0, cont_buf, cont_len); + cont_len = 0; + cont_task = NULL; + printed_len = cont_len; + } else { + /* ordinary single and terminated line */ + log_store(facility, level, + dict, dictlen, text, text_len); + printed_len = text_len; + } } /* - * Try to acquire and then immediately release the - * console semaphore. The release will do all the - * actual magic (print out buffers, wake up klogd, - * etc). + * Try to acquire and then immediately release the console semaphore. + * The release will print out buffers and wake up /dev/kmsg and syslog() + * users. * - * The console_trylock_for_printk() function - * will release 'logbuf_lock' regardless of whether it - * actually gets the semaphore or not. + * The console_trylock_for_printk() function will release 'logbuf_lock' + * regardless of whether it actually gets the console semaphore or not. */ if (console_trylock_for_printk(this_cpu)) console_unlock(); @@ -974,16 +1418,81 @@ out_restore_irqs: return printed_len; } -EXPORT_SYMBOL(printk); -EXPORT_SYMBOL(vprintk); +EXPORT_SYMBOL(vprintk_emit); -#else +asmlinkage int vprintk(const char *fmt, va_list args) +{ + return vprintk_emit(0, -1, NULL, 0, fmt, args); +} +EXPORT_SYMBOL(vprintk); -static void call_console_drivers(unsigned start, unsigned end) +asmlinkage int printk_emit(int facility, int level, + const char *dict, size_t dictlen, + const char *fmt, ...) { + va_list args; + int r; + + va_start(args, fmt); + r = vprintk_emit(facility, level, dict, dictlen, fmt, args); + va_end(args); + + return r; } +EXPORT_SYMBOL(printk_emit); +/** + * printk - print a kernel message + * @fmt: format string + * + * This is printk(). It can be called from any context. We want it to work. + * + * We try to grab the console_lock. If we succeed, it's easy - we log the + * output and call the console drivers. If we fail to get the semaphore, we + * place the output into the log buffer and return. The current holder of + * the console_sem will notice the new output in console_unlock(); and will + * send it to the consoles before releasing the lock. + * + * One effect of this deferred printing is that code which calls printk() and + * then changes console_loglevel may break. This is because console_loglevel + * is inspected when the actual printing occurs. + * + * See also: + * printf(3) + * + * See the vsnprintf() documentation for format string extensions over C99. + */ +asmlinkage int printk(const char *fmt, ...) +{ + va_list args; + int r; + +#ifdef CONFIG_KGDB_KDB + if (unlikely(kdb_trap_printk)) { + va_start(args, fmt); + r = vkdb_printf(fmt, args); + va_end(args); + return r; + } #endif + va_start(args, fmt); + r = vprintk_emit(0, -1, NULL, 0, fmt, args); + va_end(args); + + return r; +} +EXPORT_SYMBOL(printk); + +#else + +#define LOG_LINE_MAX 0 +static struct log *log_from_idx(u32 idx) { return NULL; } +static u32 log_next(u32 idx) { return 0; } +static void call_console_drivers(int level, const char *text, size_t len) {} +static size_t msg_print_text(const struct log *msg, bool syslog, + char *buf, size_t size) { return 0; } + +#endif /* CONFIG_PRINTK */ static int __add_preferred_console(char *name, int idx, char *options, char *brl_options) @@ -1217,7 +1726,7 @@ int is_console_locked(void) } /* - * Delayed printk facility, for scheduler-internal messages: + * Delayed printk version, for scheduler-internal messages: */ #define PRINTK_BUF_SIZE 512 @@ -1253,6 +1762,10 @@ void wake_up_klogd(void) this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP); } +/* the next printk record to write to the console */ +static u64 console_seq; +static u32 console_idx; + /** * console_unlock - unlock the console system * @@ -1263,15 +1776,16 @@ void wake_up_klogd(void) * by printk(). If this is the case, console_unlock(); emits * the output prior to releasing the lock. * - * If there is output waiting for klogd, we wake it up. + * If there is output waiting, we wake /dev/kmsg and syslog() users. * * console_unlock(); may be called from any context. */ void console_unlock(void) { + static u64 seen_seq; unsigned long flags; - unsigned _con_start, _log_end; - unsigned wake_klogd = 0, retry = 0; + bool wake_klogd = false; + bool retry; if (console_suspended) { up(&console_sem); @@ -1281,17 +1795,38 @@ void console_unlock(void) console_may_schedule = 0; again: - for ( ; ; ) { + for (;;) { + struct log *msg; + static char text[LOG_LINE_MAX]; + size_t len; + int level; + raw_spin_lock_irqsave(&logbuf_lock, flags); - wake_klogd |= log_start - log_end; - if (con_start == log_end) - break; /* Nothing to print */ - _con_start = con_start; - _log_end = log_end; - con_start = log_end; /* Flush */ + if (seen_seq != log_next_seq) { + wake_klogd = true; + seen_seq = log_next_seq; + } + + if (console_seq < log_first_seq) { + /* messages are gone, move to first one */ + console_seq = log_first_seq; + console_idx = log_first_idx; + } + + if (console_seq == log_next_seq) + break; + + msg = log_from_idx(console_idx); + level = msg->level & 7; + + len = msg_print_text(msg, false, text, sizeof(text)); + + console_idx = log_next(console_idx); + console_seq++; raw_spin_unlock(&logbuf_lock); + stop_critical_timings(); /* don't trace print latency */ - call_console_drivers(_con_start, _log_end); + call_console_drivers(level, text, len); start_critical_timings(); local_irq_restore(flags); } @@ -1312,8 +1847,7 @@ again: * flush, no worries. */ raw_spin_lock(&logbuf_lock); - if (con_start != log_end) - retry = 1; + retry = console_seq != log_next_seq; raw_spin_unlock_irqrestore(&logbuf_lock, flags); if (retry && console_trylock()) @@ -1549,7 +2083,8 @@ void register_console(struct console *newcon) * for us. */ raw_spin_lock_irqsave(&logbuf_lock, flags); - con_start = log_start; + console_seq = syslog_seq; + console_idx = syslog_idx; raw_spin_unlock_irqrestore(&logbuf_lock, flags); /* * We're about to replay the log buffer. Only do this to the @@ -1758,6 +2293,9 @@ int kmsg_dump_unregister(struct kmsg_dumper *dumper) } EXPORT_SYMBOL_GPL(kmsg_dump_unregister); +static bool always_kmsg_dump; +module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR); + /** * kmsg_dump - dump kernel log to kernel message dumpers. * @reason: the reason (oops, panic etc) for dumping @@ -1767,8 +2305,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister); */ void kmsg_dump(enum kmsg_dump_reason reason) { - unsigned long end; - unsigned chars; + u64 idx; struct kmsg_dumper *dumper; const char *s1, *s2; unsigned long l1, l2; @@ -1780,24 +2317,27 @@ void kmsg_dump(enum kmsg_dump_reason reason) /* Theoretically, the log could move on after we do this, but there's not a lot we can do about that. The new messages will overwrite the start of what we dump. */ + raw_spin_lock_irqsave(&logbuf_lock, flags); - end = log_end & LOG_BUF_MASK; - chars = logged_chars; - raw_spin_unlock_irqrestore(&logbuf_lock, flags); + if (syslog_seq < log_first_seq) + idx = syslog_idx; + else + idx = log_first_idx; - if (chars > end) { - s1 = log_buf + log_buf_len - chars + end; - l1 = chars - end; + if (idx > log_next_idx) { + s1 = log_buf; + l1 = log_next_idx; - s2 = log_buf; - l2 = end; + s2 = log_buf + idx; + l2 = log_buf_len - idx; } else { s1 = ""; l1 = 0; - s2 = log_buf + end - chars; - l2 = chars; + s2 = log_buf + idx; + l2 = log_next_idx - idx; } + raw_spin_unlock_irqrestore(&logbuf_lock, flags); rcu_read_lock(); list_for_each_entry_rcu(dumper, &dump_list, list) diff --git a/lib/Kconfig b/lib/Kconfig index 4a8aba2..0e25c03 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -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 diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6777153..e119341 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -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: diff --git a/lib/Makefile b/lib/Makefile index 18515f0..74290c9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 310c753..7ca29a0 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -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_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)); - /* ddebug_query boot param got passed -> set it up */ + /* 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); diff --git a/lib/jedec_ddr_data.c b/lib/jedec_ddr_data.c new file mode 100644 index 0000000..6d2cbf1 --- /dev/null +++ b/lib/jedec_ddr_data.c @@ -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); diff --git a/lib/kobject.c b/lib/kobject.c index aeefa8b..e07ee1f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -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 diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 918330f..9f389e5 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -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]); } |